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.

687 lines
19 KiB

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