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.

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