New repository for old PyCoffee Machine.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

690 lines
19 KiB

5 days ago
5 days ago
5 days ago
  1. #include <Arduino.h>
  2. #include <ESP8266WiFi.h>
  3. #include <ESPAsyncTCP.h>
  4. #include <ESPAsyncWebServer.h>
  5. #include <AsyncElegantOTA.h>
  6. #include <Wire.h>
  7. #include <LiquidCrystal.h>
  8. #include <EEPROM.h> // to store number of coffees made and pumped water
  9. AsyncWebServer server(80);
  10. // coffee times (in seconds) and temperature (in C) settings:
  11. // Warning! these are currently hard-coded in the arduino nano
  12. //int rist = 15;
  13. //int espr = 20;
  14. //int lung = 25;
  15. //int amer = 60;
  16. //int desiredTemp = 70;
  17. // support variables used by checktraywater() to interact with EEPROM
  18. int mem0 = 0;
  19. int mem1 = 0;
  20. // This next variable is used to get current "time" in ms and break out of while cycles that need a time limit safeguard
  21. unsigned long startTime;
  22. // WiFi settings
  23. const char* ssid = "LILiK_WiFi"; //replace with your SSID
  24. const char* password = "pippopippo"; //password
  25. String hostname = "PyCoffee";
  26. int wifiStatus = 0; // variable to store status (connected/disconnected, used by LCDprint)
  27. // string to store coffee style from HTML
  28. String input_field = ""; // string for coffee type
  29. String input_field2 = ""; // string for serial commands
  30. // HTML WEB interface
  31. const char index_html[] PROGMEM = R"rawliteral(
  32. <!DOCTYPE HTML><html><head>
  33. <title>PyCoffee HTML interface</title>
  34. <meta name="viewport" content="width=device-width, initial-scale=1">
  35. <style>
  36. html {display: inline-block; text-align: center;}
  37. </style>
  38. </head><body>
  39. <a>Ciao! Sono la macchina del caffe' della 117bis!</a><br>
  40. <a>Seleziona il tipo di caffe' e conferma</a><br><br>
  41. <form action="/get">
  42. <select name="coffee_input">
  43. <option value="Ristretto">Ristretto</option>
  44. <option value="Espresso">Espresso</option>
  45. <option value="Lungo">Lungo</option>
  46. <option value="Americano">Americano</option>
  47. </select><br><br>
  48. <input type="submit" value="Conferma">
  49. </form>
  50. <br><br>
  51. <a>Puoi mandare comandi a Arduino via seriale</a><br>
  52. <a>usa 'exit' per uscire dall'ultimo comando seriale</a><br><br>
  53. <form action="/serial">
  54. <select name="serial_input">
  55. <option value="exit">Exit Command</option>
  56. <option value="dryr">PerformDryRun</option>
  57. <option value="noco">NoCoffeeRun</option>
  58. <option value="clea">Self-Clean</option>
  59. <option value="make">Make Coffee</option>
  60. <option value="pump">pumpWater</option>
  61. <option value="pres">Press</option>
  62. <option value="unpr">un-Press</option>
  63. <option value="f-pr">Force-Press</option>
  64. <option value="f-un">Force-unpress</option>
  65. <option value="heat">Heat Boiler</option>
  66. <option value="grin">GrindPowder</option>
  67. <option value="drop">Drop Powder</option>
  68. <option value="rist">setRistretto-15s</option>
  69. <option value="espr">setEspresso-20s</option>
  70. <option value="long">setLungo-25s</option>
  71. <option value="amer">setAmericano-60s</option>
  72. </select><br><br>
  73. <input type="submit" value="Invia Comando">
  74. </form>
  75. <br><br>
  76. <a>Consulta la pagina Gitea per maggiori informazioni</a><br><br>
  77. <a>Aggiornamento firmware:</a> <br>
  78. <a href='/update'>&#11014;&#65039; OTA Firmware_Update &#11014;&#65039;</a><br><br>
  79. <a>Codice sorgente (su LILiK projects):</a> <br>
  80. <a href='https://projects.lilik.it/LILiK/PyCoffee_ESP8266'>&#9749;Gitea&#9749;</a><br><br>
  81. </body></html>)rawliteral";
  82. // input parameter for html server
  83. const char* input_paramter1 = "coffee_input";
  84. const char* input_paramter2 = "serial_input";
  85. // enable http coffee making on server request
  86. int httpCoffee = 0;
  87. int serialCommand = 0;
  88. // button pins
  89. const int sel = 0, enter = 2;
  90. // Global string, allows serial in
  91. String readString;
  92. // coffee type variable
  93. String type = "Espresso";
  94. // number of coffees made and seconds of pumped water since last tray empty/water refill
  95. // used by checkTrayWater() function
  96. int coffeesDone = 0;
  97. int pumpedS = 0;
  98. const int MaxCoffees = 10;
  99. const int MaxPumped = 500;
  100. // Support string to store stuff to be sent over serial
  101. String serialOUTbuffer = "none";
  102. // Variables to store lines to be printed on LCD:
  103. String line1 = "";
  104. String line2 = "";
  105. // only refresh LCD when needed
  106. int printed = 0;
  107. // LCD pins <--> ESP8266 LOLIN D1 pins
  108. const int e_RS = 4, e_EN = 5, e_D6 = 12, e_D7 = 13, e_D5 = 14, e_D8 = 15;
  109. LiquidCrystal lcd(e_RS, e_EN, e_D6, e_D7, e_D5, e_D8);
  110. // Custom character matrix (coffee cup left side)
  111. byte cupL[8] = {0b00001,0b00010,0b00010,0b00001,0b00001,0b00000,0b00111,0b00100};
  112. // Custom character matrix (coffee cup center)
  113. byte cupC[8] = {0b00100,0b00101,0b01001,0b01000,0b00100,0b00000,0b11111,0b00000};
  114. // Custom character matrix (coffee cup right side)
  115. byte cupR[8] = {0b10000,0b00000,0b00000,0b10000,0b10000,0b00000,0b11000,0b01110};
  116. // Custom character matrix (coffee cup left side line 2)
  117. byte cupL1[8] = {0b00100,0b00100,0b00100,0b00010,0b01111,0b01000,0b00111,0b00000};
  118. // Custom character matrix (coffee cup center line 2)
  119. byte cupC1[8] = {0b00000,0b00000,0b00000,0b00000,0b11111,0b00000,0b11111,0b00000};
  120. // Custom character matrix (coffee cup right side line 2)
  121. byte cupR1[8] = {0b01010,0b01010,0b01110,0b10000,0b11100,0b00100,0b11000,0b00000};
  122. // Custom character matrix (WiFi connected/disconnected)
  123. byte wifiok[8] = {0b00000,0b01110,0b10001,0b00100,0b01010,0b00000,0b00000,0b00100};
  124. byte wifino[8] = {0b00000,0b10001,0b01010,0b00100,0b01010,0b10001,0b00000,0b00100};
  125. void notFound(AsyncWebServerRequest *request) {
  126. request->send(404, "text/plain", "Not found");
  127. }
  128. //_____________________________________________________________________________
  129. // the setup function runs once when you press reset or power the board
  130. void setup(void) {
  131. // check how many coffees were done and seconds of pumped water from EEPROM
  132. mem0 = (EEPROM.read(0));
  133. mem1 = (EEPROM.read(1));
  134. // protect against under/over-flow due to errors or memory location never having been used
  135. if (mem0 < 0 or mem0 > MaxCoffees + 1) {
  136. EEPROM.write(0, 0);
  137. mem0 = 0;
  138. }
  139. if (mem1 < 0 or mem1 > MaxPumped + 50) {
  140. EEPROM.write(1, 0);
  141. mem1 = 0;
  142. }
  143. coffeesDone = mem0;
  144. pumpedS = mem1;
  145. // Set buttons as inputs with internal pull-ups
  146. pinMode(enter, INPUT_PULLUP);
  147. pinMode(sel, INPUT_PULLUP);
  148. // initialize serial:
  149. Serial.begin(9600);
  150. // Make custom characters available to LCD to paint a cute coffee cup
  151. lcd.createChar(0, cupL);
  152. lcd.createChar(1, cupC);
  153. lcd.createChar(2, cupR);
  154. lcd.createChar(3, cupL1);
  155. lcd.createChar(4, cupC1);
  156. lcd.createChar(5, cupR1);
  157. lcd.createChar(6, wifiok);
  158. lcd.createChar(7, wifino);
  159. lcd.begin(16, 2); // set up number of columns and rows
  160. lcd.clear(); // Clear LCD
  161. // Show boot animation on LCD
  162. bootAnimation();
  163. WiFi.mode(WIFI_STA);
  164. // WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
  165. WiFi.setHostname(hostname.c_str()); //define hostname
  166. WiFi.begin(ssid, password);
  167. connectedTo();
  168. // Send web page with input fields to client
  169. server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
  170. request->send(200, "text/html", index_html);
  171. });
  172. // Get data from menu
  173. // coffee
  174. server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
  175. if (request->hasParam(input_paramter1)) {
  176. type = request->getParam(input_paramter1)->value();
  177. httpCoffee = 1;
  178. }
  179. request->send_P(200, "text/html", index_html); // Re-send main page, with data!
  180. });
  181. // serial commands
  182. server.on("/serial", HTTP_GET, [] (AsyncWebServerRequest *request) {
  183. if (request->hasParam(input_paramter2)) {
  184. if (serialOUTbuffer != "") {
  185. serialOUTbuffer = request->getParam(input_paramter2)->value();
  186. serialCommand = 1;
  187. httpCoffee = 0;
  188. }
  189. }
  190. request->send_P(200, "text/html", index_html); // Re-send main page, with data!
  191. });
  192. // in case of error:
  193. server.onNotFound(notFound);
  194. AsyncElegantOTA.begin(&server); // Start ElegantOTA
  195. server.begin();
  196. Serial.println("HTTP server started");
  197. // Print selection screen
  198. printed = 1;
  199. // tell the Arduino we're ready
  200. serialOUTbuffer = "read";
  201. delay(100);
  202. writeSerial();
  203. type = "Espresso";
  204. line1 = "Selection";
  205. line2 = type;
  206. LCDprint();
  207. delay(500);
  208. }
  209. //_____________________________________________________________________________
  210. void loop() {
  211. // the loop function runs over and over again forever
  212. delay(10);
  213. serialFromHTTP();
  214. // select type of brew by pressing the sel button
  215. if (digitalRead(sel) == LOW && type == "Espresso") {
  216. type = "Lungo";
  217. line1 = "Selection";
  218. line2 = type;
  219. LCDprint();
  220. delay(500);
  221. }
  222. else if (digitalRead(sel) == LOW && type == "Lungo") {
  223. type = "Americano";
  224. line1 = "Selection";
  225. line2 = type;
  226. LCDprint();
  227. delay(500);
  228. }
  229. else if (digitalRead(sel) == LOW && type == "Americano") {
  230. type = "Ristretto";
  231. line1 = "Selection";
  232. line2 = type;
  233. LCDprint();
  234. delay(500);
  235. }
  236. else if (digitalRead(sel) == LOW && type == "Ristretto") {
  237. type = "Espresso";
  238. line1 = "Selection";
  239. line2 = type;
  240. LCDprint();
  241. delay(500);
  242. }
  243. else if (Serial.available() > 0) {
  244. // read the incoming data: (we only care about the first few characters, I've chosen 4)
  245. readString = "";
  246. serialRead();
  247. String incomingData = readString.substring(0,4);
  248. // are we heating the boiler to vapor temperature?
  249. if (incomingData == "Vapo") {
  250. line1 = "Heating";
  251. line2 = "Vapor";
  252. LCDprint();
  253. delay(1000);
  254. }
  255. if (incomingData == "reac") {
  256. line1 = "Vapor";
  257. line2 = "ok";
  258. LCDprint();
  259. delay(1000);
  260. printed = 1;
  261. delay(10000);
  262. serialOUTbuffer = "unpr";
  263. writeSerial();
  264. delay(100);
  265. }
  266. }
  267. refreshMenu();
  268. delay(100);
  269. // check if data has been sent on serial from Arduino,
  270. // send commands to arduino and display corresponding messages on LCD
  271. // this is the main function dedicated to making coffee and reporting errors.
  272. serialCoffeeCommandLCDInterface();
  273. }
  274. //_____________________________________________________________________________
  275. // this is how we read the input
  276. void serialRead() {
  277. while (Serial.available()) {
  278. delay(1);
  279. if (Serial.available() > 0) {
  280. char c = Serial.read();
  281. readString += c;}
  282. }
  283. }
  284. //_____________________________________________________________________________
  285. void bootAnimation() {
  286. delay(1000);
  287. line1 = "ESP_Coffee";
  288. line2 = "Release 1.1e";
  289. LCDprint();
  290. delay(1000);
  291. line1 = "JIMBO";
  292. line2 = "";
  293. LCDprint();
  294. delay(1000);
  295. lcd.setCursor(11,0); //print coffee cup top using uint8 method
  296. lcd.write((uint8_t)0);
  297. delay(10);
  298. lcd.write((uint8_t)1);
  299. delay(10);
  300. lcd.write((uint8_t)2);
  301. delay(10);
  302. lcd.setCursor(11,1); //print coffee cup bottom using uint8 method
  303. lcd.write((uint8_t)3);
  304. delay(10);
  305. lcd.write((uint8_t)4);
  306. delay(10);
  307. lcd.write((uint8_t)5);
  308. delay(2000);
  309. }
  310. //_____________________________________________________________________________
  311. void connectedTo() {
  312. startTime = millis();
  313. while (WiFi.status() != WL_CONNECTED) {
  314. delay(500);
  315. Serial.print(".");
  316. if (millis() - startTime > 60000) {
  317. Serial.println("WiFi connection timeout");
  318. line1 = "Timeout at SSID";
  319. line2 = ssid;
  320. LCDprint();
  321. delay(10000);
  322. break;
  323. }
  324. else {
  325. wifiStatus = 1;
  326. }
  327. }
  328. Serial.println("");
  329. Serial.println("WiFi connected");
  330. Serial.println("IP address: ");
  331. Serial.println(WiFi.localIP()); //You can get IP address assigned to ESP
  332. line1 = "SSID";
  333. line2 = ssid;
  334. LCDprint();
  335. delay(2000);
  336. line1 = "Connected to IP:";
  337. line2 = "";
  338. LCDprint();
  339. delay(10);
  340. lcd.setCursor(0, 1); // move cursor to (0, 1)
  341. lcd.print(WiFi.localIP()); // re-print line2 or IP won't print
  342. delay(2000);
  343. }
  344. //_____________________________________________________________________________
  345. void refreshMenu() {
  346. if (line1 == "Selection" && printed == 1) {
  347. lcd.clear();
  348. lcd.setCursor(0, 0); // move cursor to (0, 0)
  349. lcd.print(line1); // print line1
  350. delay(10);
  351. lcd.setCursor(15, 0); // move cursor to (14, 0)
  352. // print wifi logo depending on connection starus
  353. if (wifiStatus == 1) {
  354. lcd.write((uint8_t)6);
  355. delay(10);
  356. }
  357. else if (wifiStatus == 0) {
  358. lcd.write((uint8_t)7);
  359. delay(10);
  360. }
  361. lcd.setCursor(0, 1); // move cursor to (0, 1)
  362. lcd.print(line2); // print line2
  363. delay(10);
  364. printed = 0;
  365. }
  366. }
  367. //_____________________________________________________________________________
  368. void LCDprint() {
  369. lcd.clear();
  370. lcd.setCursor(0, 0); // move cursor to (0, 0)
  371. lcd.print(line1); // print line1
  372. delay(10);
  373. lcd.setCursor(15, 0); // move cursor to (14, 0)
  374. // print wifi logo depending on connection starus
  375. if (wifiStatus == 1) {
  376. lcd.write((uint8_t)6);
  377. delay(10);
  378. }
  379. else if (wifiStatus == 0) {
  380. lcd.write((uint8_t)7);
  381. delay(10);
  382. }
  383. lcd.setCursor(0, 1); // move cursor to (0, 1)
  384. lcd.print(line2); // print line2
  385. delay(10);
  386. }
  387. //_____________________________________________________________________________
  388. void writeSerial() {
  389. Serial.println(serialOUTbuffer);
  390. }
  391. //_____________________________________________________________________________
  392. void serialFromHTTP() {
  393. if (serialCommand == 1){
  394. serialCommand = 0;
  395. line1 = "serialCom";
  396. line2 = serialOUTbuffer;
  397. LCDprint();
  398. delay(1000);
  399. writeSerial();
  400. while (serialOUTbuffer != "exit" && digitalRead(sel) == HIGH ) {
  401. if (Serial.available() > 0) {
  402. // read the incoming data: (we only care about the first few characters, I've chosen 4)
  403. readString = "";
  404. serialRead();
  405. String incomingData = readString.substring(0,16);
  406. Serial.print("serial put from Arduino:\n");
  407. Serial.print(incomingData);
  408. Serial.print("\n");
  409. if (serialOUTbuffer != "none") {
  410. line1 = serialOUTbuffer;
  411. }
  412. line2 = incomingData;
  413. LCDprint();
  414. delay(100);
  415. serialOUTbuffer = "none";
  416. writeSerial();
  417. }
  418. }
  419. line1 = "Selection";
  420. line2 = type;
  421. LCDprint();
  422. delay(500);
  423. }
  424. }
  425. //_____________________________________________________________________________
  426. void checkTrayWater() {
  427. // section dedicated to empty tray and refill water messages
  428. if (coffeesDone == MaxCoffees) {
  429. lcd.clear();
  430. while (digitalRead(enter) == HIGH) {
  431. line1 = "Empty Tray";
  432. line2 = "Then hold Make";
  433. delay(500);
  434. LCDprint();
  435. }
  436. coffeesDone = 0;
  437. EEPROM.write(0, 0);
  438. line1 = "Tray emptied";
  439. line2 = "continuing...";
  440. delay(1000);
  441. LCDprint();
  442. }
  443. if (pumpedS > MaxPumped) {
  444. lcd.clear();
  445. while (digitalRead(enter) == HIGH) {
  446. line1 = "Refill Water";
  447. line1 = "Then hold Make";
  448. delay(500);
  449. LCDprint();
  450. }
  451. pumpedS = 0;
  452. EEPROM.write(1, 0);
  453. line1 = "Water Refilled";
  454. line2 = "continuing...";
  455. delay(1000);
  456. LCDprint();
  457. }
  458. delay(500);
  459. printed = 1;
  460. EEPROM.commit();
  461. line1 = "Selection";
  462. line2 = type;
  463. }
  464. //_____________________________________________________________________________
  465. void serialCoffeeCommandLCDInterface() {
  466. if (digitalRead(enter) == LOW or httpCoffee == 1) {
  467. httpCoffee = 0;
  468. if (type == "Espresso") {
  469. serialOUTbuffer = "espr";
  470. pumpedS = pumpedS + 20;
  471. }
  472. else if (type == "Lungo") {
  473. serialOUTbuffer = "long";
  474. pumpedS = pumpedS + 25;
  475. }
  476. else if (type == "Ristretto") {
  477. serialOUTbuffer = "rist";
  478. pumpedS = pumpedS + 15;
  479. }
  480. else if (type == "Americano") {
  481. serialOUTbuffer = "amer";
  482. pumpedS = pumpedS + 60;
  483. }
  484. writeSerial();
  485. delay(500);
  486. serialOUTbuffer = "make";
  487. writeSerial();
  488. }
  489. if (Serial.available() > 0) {
  490. // read the incoming data: (we only care about the first few characters, I've chosen 4)
  491. readString = "";
  492. serialRead();
  493. String incomingData = readString.substring(0,4);
  494. Serial.print(incomingData);
  495. Serial.print("\n");
  496. if (incomingData == "Maki") {
  497. // Display making coffee message
  498. line1 = "Making";
  499. line2 = type;
  500. LCDprint();
  501. delay(10);
  502. lcd.setCursor(11,0); //print coffee cup top using uint8 method
  503. lcd.write((uint8_t)0);
  504. delay(10);
  505. lcd.write((uint8_t)1);
  506. delay(10);
  507. lcd.write((uint8_t)2);
  508. delay(10);
  509. lcd.setCursor(11,1); //print coffee cup bottom using uint8 method
  510. lcd.write((uint8_t)3);
  511. delay(10);
  512. lcd.write((uint8_t)4);
  513. delay(10);
  514. lcd.write((uint8_t)5);
  515. delay(10);
  516. }
  517. if (incomingData == "u-Th") {
  518. Serial.print("Thermocouple: unplugged or failed\n");
  519. while (true) {
  520. // Unrecoverable error has occurred during heating, lock the machine
  521. line1 = "ThermoCouple err";
  522. line2 = "Unplugged/failed";
  523. LCDprint();
  524. delay(10000);
  525. }
  526. }
  527. if (incomingData == "p-Th") {
  528. Serial.print("Thermocouple: positioning or relay fault\n");
  529. while (true) {
  530. // Unrecoverable error has occurred during heating, lock the machine
  531. line1 = "ThermoCouple err";
  532. line2 = "Fallen/relayFail";
  533. LCDprint();
  534. delay(10000);
  535. }
  536. }
  537. if (incomingData == "Erro") {
  538. Serial.print("Unrecoverable error in Arduino subsystem. Please restart\n");
  539. while (true) {
  540. // Unrecoverable error has occurred during heating, lock the machine
  541. line1 = "Enrecoverable";
  542. line2 = "Error. Restart.";
  543. LCDprint();
  544. delay(10000);
  545. }
  546. }
  547. if (incomingData == "h-ta") {
  548. Serial.print("took too long to warm boiler, this is just a warning. Everything should be fine\n");
  549. line1 = "Heating too long";
  550. line2 = "Continuing...";
  551. LCDprint();
  552. }
  553. if (incomingData == "p-en") {
  554. Serial.print("Unable to Press. Please restart\n");
  555. while (true) {
  556. // Unrecoverable error has occurred while pressing, lock the machine
  557. line1 = "Press failed";
  558. line2 = "Error. Restart.";
  559. LCDprint();
  560. delay(10000);
  561. }
  562. }
  563. if (incomingData == "u-en") {
  564. Serial.print("Unable to unPress. Please restart\n");
  565. while (true) {
  566. // Unrecoverable error has occurred while unpressing, lock the machine
  567. line1 = "unPress failed";
  568. line2 = "Error. Restart.";
  569. LCDprint();
  570. delay(10000);
  571. }
  572. }
  573. if (incomingData == "refi") {
  574. Serial.print("refill coffee and send cont command\n");
  575. line1 = "Refill Coffee";
  576. line2 = "then tap make";
  577. LCDprint();
  578. while (true) {
  579. delay(10);
  580. if (digitalRead(enter) == LOW) {
  581. line1 = "Refilled";
  582. line2 = "continuing";
  583. LCDprint();
  584. serialOUTbuffer = "cont";
  585. writeSerial();
  586. break;
  587. }
  588. }
  589. }
  590. if (incomingData == "g-fa") {
  591. Serial.print("g-failure: second grinding failure, check grinder!\n");
  592. while (true) {
  593. // Unrecoverable error has occurred during grinding, lock the machine
  594. line1 = "Grinding failed";
  595. line2 = "Check Machine";
  596. LCDprint();
  597. delay(10000);
  598. }
  599. }
  600. if (incomingData == "Comp") {
  601. coffeesDone++;
  602. Serial.print("Made coffee\n");
  603. line1 = "Coffee Done :)";
  604. line2 = "Enjoy!";
  605. LCDprint();
  606. delay(5000);
  607. line1 = "Selection";
  608. delay(500);
  609. }
  610. }
  611. }