@ -0,0 +1,31 @@ | |||
generalConfig = | |||
serialDevice: '/dev/coffee' | |||
logFilename: 'coffee.log' | |||
tempTimeout: 50 | |||
pumpCoefficient: 1.35 | |||
hardwareConfig = | |||
coffeeTemp: 0.758 | |||
coffeeTemp2: 0.758 + 0.1 | |||
grinderPin: 9 | |||
pumpPin: 8 | |||
boilerPin: 6 | |||
powderPin: 7 | |||
wheelPin: 10 | |||
invertWheelPin: 11 | |||
powderSensorPin: 2 | |||
vaporSensorPin: 5 | |||
tempSensorPin: 7 | |||
wheelStartSensorPin: 4 | |||
wheelEndSensorPin: 3 | |||
accountingConfig = | |||
coffeePrice: 20 | |||
exports.general = generalConfig | |||
exports.hardware = hardwareConfig | |||
exports.accounting = accountingConfig | |||
@ -0,0 +1,420 @@ | |||
#include <Arduino.h> | |||
#include <ESP8266WiFi.h> | |||
#include <ESPAsyncTCP.h> | |||
#include <ESPAsyncWebServer.h> | |||
#include <AsyncElegantOTA.h> | |||
#include <Wire.h> | |||
#include <LiquidCrystal.h> | |||
AsyncWebServer server(80); | |||
// This next variable is used to get current "time" in ms and break out of while cycles that need a time limit safeguard | |||
unsigned long startTime; | |||
// WiFi settings | |||
const char* ssid = "LILiK_WiFi"; //replace with your SSID | |||
const char* password = "pippopippo"; //password | |||
//const char* ssid = "AulaStud"; //replace with your SSID | |||
//const char* password = "cinimerda"; //password | |||
String hostname = "PyCoffee"; | |||
// string to store coffee style from HTML | |||
String input_field = "espr"; | |||
// HTML WEB interface | |||
const char index_html[] PROGMEM = R"rawliteral( | |||
<!DOCTYPE HTML><html><head> | |||
<title>PyCoffee HTML interface</title> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<style> | |||
html {display: inline-block; text-align: center;} | |||
</style> | |||
</head><body> | |||
<a>Ciao! Sono la macchina del caffe' della 117bis!</a><br> | |||
<a>Seleziona il tipo di caffe' e conferma</a><br><br> | |||
<form action="/get"> | |||
<select name="coffee_input"> | |||
<option value="Ristretto">Ristretto</option> | |||
<option value="Espresso">Espresso</option> | |||
<option value="Lungo">Lungo</option> | |||
</select><br><br> | |||
<input type="submit" value="Conferma"> | |||
</form> | |||
<br><br> | |||
<a>Clicca qui sotto per aggiornare il firmware:</a> <br> | |||
<a href='/update'>⬆️ OTA Firmware_Update ⬆️</a><br><br> | |||
<a>Codice sorgente (su LILiK projects):</a> <br> | |||
<a href='https://projects.lilik.it/LILiK/PyCoffee_ESP8266'>☕Gitea☕</a><br><br> | |||
</body></html>)rawliteral"; | |||
// input parameter for html server | |||
const char* input_paramter1 = "coffee_input"; | |||
// enable http coffee making on server request | |||
int httpCoffee = 0; | |||
// button pins | |||
const int sel = 0, enter = 2; | |||
// Global string | |||
String readString; | |||
// coffee type variable | |||
String type = "Espresso"; | |||
// Support string to store stuff to be sent over serial | |||
String serialOUTbuffer = "none"; | |||
// Variables to store lines to be printed on LCD: | |||
String line1 = ""; | |||
String line2 = ""; | |||
// LCD pins <--> ESP8266 LOLIN D1 pins | |||
const int e_RS = 4, e_EN = 5, e_D6 = 12, e_D7 = 13, e_D5 = 14, e_D8 = 15; | |||
LiquidCrystal lcd(e_RS, e_EN, e_D6, e_D7, e_D5, e_D8); | |||
// Custom character matrix (coffee cup left side) | |||
byte cupL[8] = {0b00001,0b00010,0b00010,0b00001,0b00001,0b00000,0b00111,0b00100}; | |||
// Custom character matrix (coffee cup center) | |||
byte cupC[8] = {0b00100,0b00101,0b01001,0b01000,0b00100,0b00000,0b11111,0b00000}; | |||
// Custom character matrix (coffee cup right side) | |||
byte cupR[8] = {0b10000,0b00000,0b00000,0b10000,0b10000,0b00000,0b11000,0b01110}; | |||
// Custom character matrix (coffee cup left side line 2) | |||
byte cupL1[8] = {0b00100,0b00100,0b00100,0b00010,0b01111,0b01000,0b00111,0b00000}; | |||
// Custom character matrix (coffee cup center line 2) | |||
byte cupC1[8] = {0b00000,0b00000,0b00000,0b00000,0b11111,0b00000,0b11111,0b00000}; | |||
// Custom character matrix (coffee cup right side line 2) | |||
byte cupR1[8] = {0b01010,0b01010,0b01110,0b10000,0b11100,0b00100,0b11000,0b00000}; | |||
void notFound(AsyncWebServerRequest *request) { | |||
request->send(404, "text/plain", "Not found"); | |||
} | |||
//_____________________________________________________________________________ | |||
// the setup function runs once when you press reset or power the board | |||
void setup(void) { | |||
// Set buttons as inputs with internal pull-ups | |||
pinMode(enter, INPUT_PULLUP); | |||
pinMode(sel, INPUT_PULLUP); | |||
// initialize serial: | |||
Serial.begin(9600); | |||
// Make custom characters available to LCD to paint a cute coffee cup | |||
lcd.createChar(0, cupL); | |||
lcd.createChar(1, cupC); | |||
lcd.createChar(2, cupR); | |||
lcd.createChar(3, cupL1); | |||
lcd.createChar(4, cupC1); | |||
lcd.createChar(5, cupR1); | |||
lcd.begin(16, 2); // set up number of columns and rows | |||
lcd.clear(); // Clear LCD | |||
// Show boot animation on LCD | |||
bootAnimation(); | |||
WiFi.mode(WIFI_STA); | |||
// WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); | |||
WiFi.setHostname(hostname.c_str()); //define hostname | |||
WiFi.begin(ssid, password); | |||
connectedTo(); | |||
// Send web page with input fields to client | |||
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { | |||
request->send(200, "text/html", index_html); | |||
}); | |||
// Get data from menu | |||
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { | |||
if (request->hasParam(input_paramter1)) { | |||
type = request->getParam(input_paramter1)->value(); | |||
httpCoffee = 1; | |||
} | |||
request->send_P(200, "text/html", index_html); // Re-send main page, with data! | |||
}); | |||
// in case of error: | |||
server.onNotFound(notFound); | |||
AsyncElegantOTA.begin(&server); // Start ElegantOTA | |||
server.begin(); | |||
Serial.println("HTTP server started"); | |||
// Print selection screen | |||
line1 = "Selection"; | |||
line2 = type; | |||
LCDprint(); | |||
// tell the Arduino we're ready | |||
serialOUTbuffer = "read"; | |||
delay(100); | |||
writeSerial(); | |||
} | |||
//_____________________________________________________________________________ | |||
void loop() { | |||
// the loop function runs over and over again forever | |||
delay(10); | |||
// select type of brew by pressing the sel button | |||
if (digitalRead(sel) == LOW && type == "Espresso") { | |||
type = "Lungo"; | |||
line1 = "Selection"; | |||
line2 = type; | |||
LCDprint(); | |||
delay(500); | |||
} | |||
else if (digitalRead(sel) == LOW && type == "Lungo") { | |||
type = "Ristretto"; | |||
line1 = "Selection"; | |||
line2 = type; | |||
LCDprint(); | |||
delay(500); | |||
} | |||
else if (digitalRead(sel) == LOW && type == "Ristretto") { | |||
type = "Espresso"; | |||
line1 = "Selection"; | |||
line2 = type; | |||
LCDprint(); | |||
delay(500); | |||
} | |||
// check if data has been sent on serial from Arduino, | |||
// send commands to arduino and display corresponding messages on LCD | |||
// this is the main function dedicated to making coffee and reporting errors. | |||
serialCoffeeCommandLCDInterface(); | |||
} | |||
//_____________________________________________________________________________ | |||
// this is how we read the input | |||
void serialRead() { | |||
while (Serial.available()) { | |||
delay(1); | |||
if (Serial.available() > 0) { | |||
char c = Serial.read(); | |||
readString += c;} | |||
} | |||
} | |||
//_____________________________________________________________________________ | |||
void bootAnimation() { | |||
delay(1000); | |||
line1 = "ESP_Coffee"; | |||
line2 = "Beta 1"; | |||
LCDprint(); | |||
delay(1000); | |||
lcd.setCursor(12,0); //print coffee cup top using uint8 method | |||
lcd.write((uint8_t)0); | |||
lcd.write((uint8_t)1); | |||
lcd.write((uint8_t)2); | |||
lcd.setCursor(12,1); //print coffee cup bottom using uint8 method | |||
lcd.write((uint8_t)3); | |||
lcd.write((uint8_t)4); | |||
lcd.write((uint8_t)5); | |||
delay(2000); | |||
} | |||
//_____________________________________________________________________________ | |||
void connectedTo() { | |||
startTime = millis(); | |||
while (WiFi.status() != WL_CONNECTED) { | |||
delay(500); | |||
Serial.print("."); | |||
if (millis() - startTime > 60000) { | |||
Serial.println("WiFi connection timeout"); | |||
line1 = "Timeout at SSID"; | |||
line2 = ssid; | |||
LCDprint(); | |||
break; | |||
} | |||
delay(10000); | |||
} | |||
Serial.println(""); | |||
Serial.println("WiFi connected"); | |||
Serial.println("IP address: "); | |||
Serial.println(WiFi.localIP()); //You can get IP address assigned to ESP | |||
line1 = "SSID"; | |||
line2 = ssid; | |||
LCDprint(); | |||
delay(2000); | |||
line1 = "Connected to IP:"; | |||
line2 = ""; | |||
LCDprint(); | |||
lcd.setCursor(0, 1); // move cursor to (0, 0) | |||
lcd.print(WiFi.localIP()); // re-print line2 or IP won't print | |||
delay(2000); | |||
} | |||
//_____________________________________________________________________________ | |||
void LCDprint() { | |||
lcd.clear(); | |||
lcd.setCursor(0, 0); // move cursor to (0, 0) | |||
lcd.print(line1); // print line1 | |||
lcd.setCursor(0, 1); // move cursor to (0, 0) | |||
lcd.print(line2); // print line2 | |||
} | |||
//_____________________________________________________________________________ | |||
void writeSerial() { | |||
Serial.println(serialOUTbuffer); | |||
} | |||
//_____________________________________________________________________________ | |||
void serialCoffeeCommandLCDInterface() { | |||
if (digitalRead(enter) == LOW or httpCoffee == 1) { | |||
httpCoffee = 0; | |||
if (type == "Espresso") { | |||
serialOUTbuffer = "espr"; | |||
} | |||
else if (type == "Lungo") { | |||
serialOUTbuffer = "long"; | |||
} | |||
else if (type == "Ristretto") { | |||
serialOUTbuffer = "rist"; | |||
} | |||
writeSerial(); | |||
delay(500); | |||
serialOUTbuffer = "make"; | |||
writeSerial(); | |||
} | |||
if (Serial.available() > 0) { | |||
// read the incoming data: (we only care about the first few characters, I've chosen 4) | |||
readString = ""; | |||
serialRead(); | |||
String incomingData = readString.substring(0,4); | |||
Serial.print(incomingData); | |||
Serial.print("\n"); | |||
if (incomingData == "Maki") { | |||
// Display making coffee message | |||
line1 = "Making"; | |||
line2 = type; | |||
LCDprint(); | |||
lcd.setCursor(12,0); //print coffee cup top using uint8 method | |||
lcd.write((uint8_t)0); | |||
lcd.write((uint8_t)1); | |||
lcd.write((uint8_t)2); | |||
lcd.setCursor(12,1); //print coffee cup bottom using uint8 method | |||
lcd.write((uint8_t)3); | |||
lcd.write((uint8_t)4); | |||
lcd.write((uint8_t)5); | |||
} | |||
if (incomingData == "u-Th") { | |||
Serial.print("Thermocouple: unplugged or failed\n"); | |||
while (true) { | |||
// Unrecoverable error has occurred during heating, lock the machine | |||
line1 = "ThermoCouple err"; | |||
line2 = "Unplugged/failed"; | |||
LCDprint(); | |||
delay(10000); | |||
} | |||
} | |||
if (incomingData == "p-Th") { | |||
Serial.print("Thermocouple: positioning or relay fault\n"); | |||
while (true) { | |||
// Unrecoverable error has occurred during heating, lock the machine | |||
line1 = "ThermoCouple err"; | |||
line2 = "Fallen/relayFail"; | |||
LCDprint(); | |||
delay(10000); | |||
} | |||
} | |||
if (incomingData == "Erro") { | |||
Serial.print("Unrecoverable error in Arduino subsystem. Please restart\n"); | |||
while (true) { | |||
// Unrecoverable error has occurred during heating, lock the machine | |||
line1 = "Enrecoverable"; | |||
line2 = "Error. Restart."; | |||
LCDprint(); | |||
delay(10000); | |||
} | |||
} | |||
if (incomingData == "h-ta") { | |||
Serial.print("took too long to warm boiler, this is just a warning. Everything should be fine\n"); | |||
line1 = "Heating too long"; | |||
line2 = "Continuing..."; | |||
LCDprint(); | |||
} | |||
if (incomingData == "p-en") { | |||
Serial.print("Unable to Press. Please restart\n"); | |||
while (true) { | |||
// Unrecoverable error has occurred while pressing, lock the machine | |||
line1 = "Press failed"; | |||
line2 = "Error. Restart."; | |||
LCDprint(); | |||
delay(10000); | |||
} | |||
} | |||
if (incomingData == "u-en") { | |||
Serial.print("Unable to unPress. Please restart\n"); | |||
while (true) { | |||
// Unrecoverable error has occurred while unpressing, lock the machine | |||
line1 = "unPress failed"; | |||
line2 = "Error. Restart."; | |||
LCDprint(); | |||
delay(10000); | |||
} | |||
} | |||
if (incomingData == "refi") { | |||
Serial.print("refill coffee and send cont command\n"); | |||
line1 = "Refill Coffee"; | |||
line2 = "then tap make"; | |||
LCDprint(); | |||
while (true) { | |||
delay(10); | |||
if (digitalRead(enter) == LOW) { | |||
line1 = "Refilled"; | |||
line2 = "continuing"; | |||
LCDprint(); | |||
serialOUTbuffer = "cont"; | |||
writeSerial(); | |||
break; | |||
} | |||
} | |||
} | |||
if (incomingData == "g-fa") { | |||
Serial.print("g-failure: second grinding failure, check grinder!\n"); | |||
while (true) { | |||
// Unrecoverable error has occurred during grinding, lock the machine | |||
line1 = "Grinding failed"; | |||
line2 = "Check Machine"; | |||
LCDprint(); | |||
delay(10000); | |||
} | |||
} | |||
if (incomingData == "Comp") { | |||
Serial.print("Made coffee\n"); | |||
line1 = "Coffee Done :)"; | |||
line2 = "Enjoy!"; | |||
LCDprint(); | |||
delay(5000); | |||
line1 = "Selection"; | |||
line2 = type; | |||
LCDprint(); | |||
delay(500); | |||
} | |||
} | |||
} |
@ -0,0 +1,443 @@ | |||
// Pinout configuration | |||
const int grinderPin = 9, pumpPin = 8, boilerPin = 6, powderPin = 7, wheelPin = 10, invertWheelPin = 11, powderSensorPin = 2, vaporSensorPin = 5, tempSensorPin = A7, wheelStartSensorPin = 4, wheelEndSensorPin = 3, redLED = 13, greenLED = 12; | |||
// Global string | |||
String readString; | |||
// Thermistor config | |||
// WARNING! while the old coffee machine used a PTC thermistor, this was replaced with an NTC one as it was more readily available | |||
// Code must be adjusted accordingly if a PTC resistor is used once more! | |||
int Vo; | |||
float R1 = 10000; // <-- change this value to the resistance of the fixed resistor (so don't change PLS!) | |||
float logR2, R2, T, Tc, Told; | |||
float c1 = 7.9e-04, c2 = 1.85e-04, c3 = 2e-07; // Here's where you can perform actual calibration | |||
// Variable to store desired Max boiler Temp | |||
int desiredTemp = 70; | |||
// Variables to Store errors: | |||
int unrecoverableErr = 0; | |||
int warning = 0; | |||
// Variables for cleaning and other maintenance | |||
int dry = 0; | |||
int noCoffee = 0; | |||
// Milliseconds to delay between each cycle | |||
const int milliseconds = 10; | |||
int timeratio = 1; | |||
// pumpRatio is = seconds to pump water for and will be updated over serial | |||
int pumpRatio = 15; | |||
// This next variable is used to get current "time" in ms and break out of while cycles that need a time limit safeguard | |||
unsigned long startTime; | |||
void setup() { | |||
// put your setup code here, to run once: | |||
// first we set pins as I/O and initialize outputs LOW | |||
pinMode(boilerPin, OUTPUT); pinMode(pumpPin, OUTPUT); pinMode(grinderPin, OUTPUT); pinMode(powderPin, OUTPUT); pinMode(wheelPin, OUTPUT); pinMode(invertWheelPin, OUTPUT); pinMode (redLED, OUTPUT); pinMode (greenLED, OUTPUT); | |||
pinMode(powderSensorPin, INPUT); pinMode(vaporSensorPin, INPUT); pinMode(tempSensorPin, INPUT); pinMode(wheelStartSensorPin, INPUT); pinMode(wheelEndSensorPin, INPUT); | |||
digitalWrite(grinderPin, LOW); digitalWrite(pumpPin, LOW); digitalWrite(boilerPin, LOW); digitalWrite(powderPin, LOW); digitalWrite(wheelPin, LOW); digitalWrite(invertWheelPin, LOW); digitalWrite(redLED, LOW); digitalWrite(greenLED, LOW); | |||
// timeratio easily allows to determine how many cycles are required to make 1s pass (ms * ratio = 1s) | |||
timeratio = 1000/milliseconds; | |||
// initialize serial: | |||
Serial.begin(9600); | |||
delay(100); | |||
Serial.write("Arduino running :)\n"); | |||
} | |||
void loop() { | |||
// put your main code here, to run repeatedly: | |||
// Check if unrecoverable error has occurred: | |||
if (unrecoverableErr == 1) { | |||
delay(100); | |||
Serial.write("Error has occurred.\n"); delay(100); Serial.write("Check machine and restart\n"); | |||
digitalWrite(greenLED, LOW); | |||
while (true){ | |||
digitalWrite(redLED, HIGH); | |||
delay(1000); | |||
digitalWrite(redLED, LOW); | |||
delay(1000); | |||
} | |||
} | |||
// check if data has been sent on serial from ESP or PC: | |||
if (Serial.available() > 0) { | |||
// read the incoming data: (we only care about the first few characters, I've chosen 4) | |||
readString = ""; | |||
serialRead(); | |||
String incomingData = readString.substring(0,4); | |||
if (incomingData == "read") { | |||
digitalWrite(greenLED, HIGH); | |||
} | |||
if (incomingData == "rist") { | |||
// set parameters for ristretto: | |||
delay(100); | |||
Serial.write("Ristretto\n"); | |||
pumpRatio = 12; | |||
} | |||
if (incomingData == "espr") { | |||
// set parameters for espresso: | |||
delay(100); | |||
Serial.write("Espresso\n"); | |||
pumpRatio = 15; | |||
} | |||
if (incomingData == "long") { | |||
// set parameters for lungo: | |||
delay(100); | |||
Serial.write("Lungo\n"); | |||
pumpRatio = 18; | |||
} | |||
// check if the incoming data is "make": | |||
if (incomingData == "make") { | |||
// run the code to make coffee: | |||
delay(100); | |||
Serial.write("Making some coffee!\n"); | |||
makeCoffee(); | |||
} | |||
if (incomingData == "dryr") { | |||
// run the code to make a dry run (no powder, no water): | |||
delay(100); | |||
Serial.write("DryRun\n"); | |||
dry = 1; | |||
makeCoffee(); | |||
} | |||
if (incomingData == "noco") { | |||
// run the code to make a dry run (no powder, nor water): | |||
delay(100); | |||
Serial.write("NoCo\n"); | |||
pumpRatio = 12; | |||
noCoffee = 1; | |||
makeCoffee(); | |||
} | |||
if (incomingData == "pump") { | |||
// run the code to just pump water: | |||
pumpRatio = 12; | |||
Pump(); | |||
} | |||
if (incomingData == "pres") { | |||
// only press | |||
Press(); | |||
} | |||
if (incomingData == "unpr") { | |||
// only unpress | |||
unPress(); | |||
} | |||
if (incomingData == "heat") { | |||
// only heat | |||
desiredTemp = 85; | |||
Heat(); | |||
} | |||
if (incomingData == "clea") { | |||
// clean machine | |||
delay(100); | |||
Serial.write("s-clean\n"); | |||
dry = 1; | |||
makeCoffee(); | |||
noCoffee = 1; | |||
makeCoffee(); | |||
delay(100); | |||
Serial.write("S-cleaning done.\n"); | |||
} | |||
} | |||
delay(milliseconds); | |||
} | |||
// this is how we read the input | |||
void serialRead() { | |||
while (Serial.available()) { | |||
delay(10); | |||
if (Serial.available() > 0) { | |||
char c = Serial.read(); | |||
readString += c;} | |||
} | |||
} | |||
void Grind() { | |||
digitalWrite(greenLED, LOW); | |||
delay(100); | |||
Serial.write("grinding...\n"); | |||
digitalWrite(grinderPin, HIGH); | |||
startTime = millis(); | |||
while (true) { | |||
delay(milliseconds); | |||
if (digitalRead(powderSensorPin) == HIGH) { | |||
digitalWrite(grinderPin, LOW); | |||
delay(100); | |||
Serial.write("Grinding Done\n"); | |||
break; | |||
} | |||
if (millis() - startTime > 30000) { | |||
delay(100); | |||
Serial.write("Warning, grinding took too long!\n"); | |||
delay(100); | |||
Serial.write("Out of Coffee?\n"); | |||
warning = 1; | |||
break; | |||
} | |||
} | |||
digitalWrite(greenLED, HIGH); | |||
} | |||
void Drop() { | |||
digitalWrite(greenLED, LOW); | |||
delay(100); | |||
Serial.write("dropping...\n"); | |||
digitalWrite(powderPin, HIGH); | |||
delay(1000); | |||
digitalWrite(powderPin, LOW); | |||
delay(100); | |||
Serial.write("Dropped\n"); | |||
digitalWrite(greenLED, HIGH); | |||
} | |||
void Heat() { | |||
digitalWrite(greenLED, LOW); | |||
digitalWrite(redLED, HIGH); | |||
startTime = millis(); | |||
delay(100); | |||
Serial.write("Heating to "); | |||
delay(100); | |||
Serial.print(desiredTemp); | |||
delay(100); | |||
Serial.write(" C\n"); | |||
Tc = 0; | |||
Told = -100; | |||
// monitor temperature and adjust boilerPin as needed: | |||
while (true) { | |||
// read temperature from tempSensorPin: | |||
Vo = analogRead(tempSensorPin); | |||
R2 = R1 * (1023.0 / (float)Vo - 1.0); | |||
logR2 = log(R2); | |||
T = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2)); | |||
Tc = (T - 273.15); | |||
if (millis() - startTime > 1000) { | |||
Told = Tc; // support variable to store temp at beginning, so that we can be sure it's increasing | |||
} | |||
// check if temperature is within the acceptable range and break out of the loop without error if done heating: | |||
if (Tc > desiredTemp) { | |||
// temperature is within range, so break out of the loop: | |||
digitalWrite(boilerPin, LOW); | |||
delay(100); | |||
Serial.write("reached desired temp\n"); | |||
delay(100); | |||
Serial.print(Tc); | |||
delay(100); | |||
Serial.write(" C\n"); | |||
digitalWrite(redLED, LOW); | |||
break; | |||
} | |||
if (Tc < -100) { | |||
delay(100); | |||
Serial.write("u-Thermocouple: unplugged or failed\n"); | |||
digitalWrite(boilerPin, LOW); | |||
unrecoverableErr = 1; | |||
break; | |||
} | |||
if (millis() - startTime > 20000 && Tc - Told < 5) { | |||
delay(100); | |||
Serial.write("p-Thermocouple: positioning or relay fault\n"); | |||
digitalWrite(boilerPin, LOW); | |||
unrecoverableErr = 1; | |||
break; | |||
} | |||
if (millis() - startTime > 30000) { | |||
delay(100); | |||
Serial.write("h-taking too long, continuing...\n"); | |||
digitalWrite(boilerPin, LOW); | |||
digitalWrite(redLED, LOW); | |||
break; | |||
} | |||
// We do this at the end so that the relay is not uselessly cycled | |||
digitalWrite(boilerPin, HIGH); | |||
delay(milliseconds); | |||
} | |||
digitalWrite(greenLED, HIGH); | |||
} | |||
void Press() { | |||
digitalWrite(greenLED, LOW); | |||
delay(100); | |||
Serial.write("pressing...\n"); | |||
// Reset Press before every coffee | |||
digitalWrite(invertWheelPin, HIGH); | |||
delay(1000); | |||
digitalWrite(invertWheelPin, LOW); | |||
digitalWrite(wheelPin, HIGH); | |||
startTime = millis(); | |||
while (true) { | |||
delay(milliseconds); | |||
if (digitalRead(wheelEndSensorPin) == HIGH) { | |||
digitalWrite(wheelPin, LOW); | |||
delay(100); | |||
Serial.write("Pressed\n"); | |||
break; | |||
} | |||
if (millis() - startTime > 15000) { | |||
delay(100); | |||
Serial.write("p-end of pressing not detected\n"); | |||
digitalWrite(wheelPin, LOW); | |||
unrecoverableErr = 1; | |||
break; | |||
} | |||
} | |||
digitalWrite(greenLED, HIGH); | |||
} | |||
void unPress() { | |||
digitalWrite(greenLED, LOW); | |||
delay(100); | |||
Serial.write("unPressing...\n"); | |||
// Reset Press before every coffee | |||
digitalWrite(wheelPin, HIGH); | |||
delay(1000); | |||
digitalWrite(wheelPin, LOW); | |||
digitalWrite(invertWheelPin, HIGH); | |||
startTime = millis(); | |||
while (true) { | |||
delay(milliseconds); | |||
if (digitalRead(wheelStartSensorPin) == HIGH) { | |||
delay(1500); | |||
digitalWrite(invertWheelPin, LOW); | |||
delay(100); | |||
Serial.write("UnPressed\n"); | |||
break; | |||
} | |||
if (millis() - startTime > 15000) { | |||
delay(100); | |||
Serial.write("u-end of unPressing not detected\n"); | |||
digitalWrite(invertWheelPin, LOW); | |||
unrecoverableErr = 1; | |||
break; | |||
} | |||
} | |||
digitalWrite(greenLED, HIGH); | |||
} | |||
void Pump() { | |||
digitalWrite(greenLED, LOW); | |||
delay(100); | |||
Serial.write("pumping Water...\n"); | |||
digitalWrite(pumpPin, HIGH); | |||
delay(1000*pumpRatio); | |||
digitalWrite(pumpPin, LOW); | |||
delay(100); | |||
Serial.write("Pumping water done\n"); | |||
digitalWrite(greenLED, HIGH); | |||
} | |||
void makeCoffee() { | |||
// code to make coffee goes here... | |||
// It would be much smarter to break down complicated functions into subroutines: press/unpress etc... | |||
// Hence that's what we'll have done by the time the first release goes public ;) | |||
// makeCoffee() can still run all the same, but clean() can use the same press/unpress code! | |||
// care must be taken to place these functions earlier in the code, or the compiler will rightfully freak out | |||
while (true) { | |||
desiredTemp = 75; | |||
Heat(); // First we pre-heat the boiler | |||
if (unrecoverableErr == 1) { | |||
break; | |||
} | |||
if (dry == 0 && noCoffee == 0) { | |||
Grind(); // Grinding some nice roasted coffee beans! | |||
} | |||
// If grinder won't stop, coffee probably need to be refilled. | |||
if (warning == 1) { | |||
delay(100); | |||
Serial.write("refill coffee and send cont command\n"); | |||
while (true) { | |||
// check if data has been sent on serial from ESP or PC: | |||
if (Serial.available() > 0) { | |||
// read the incoming data: (we only care about the first few characters, I've chosen 4) | |||
readString = ""; | |||
serialRead(); | |||
String incomingData = readString.substring(0,4); | |||
if (incomingData == "cont") { | |||
// coffee refilled, making is allowed to continue | |||
Serial.write("Refilled\n"); | |||
warning = 0; | |||
Grind(); // Grinding some nice roasted coffee beans! | |||
if (warning == 1) { | |||
unrecoverableErr = 1; | |||
Serial.write("g-failure: second grinding failure, check grinder!\n"); | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
// Stop if grinding failed a second time | |||
if (unrecoverableErr == 1) { | |||
break; | |||
} | |||
delay(1000); // this delay is mandatory to prevent powder spillage caused by grinder inertia. | |||
unPress(); // unpressing the press to reset the machine | |||
if (unrecoverableErr == 1) { | |||
break; | |||
} | |||
Drop(); // Dropping the powder in the press | |||
delay(1000); | |||
Press(); // Self explainatory | |||
if (unrecoverableErr == 1) { | |||
break; | |||
} | |||
if (dry == 0) { | |||
digitalWrite(boilerPin, HIGH); | |||
delay(1000); | |||
Pump(); // Pump ratio (in seconds) will be read from serial input in final release | |||
digitalWrite(boilerPin, LOW); | |||
} | |||
delay(3000); // Allow pressure to wane | |||
unPress(); // unpressing the press to reset the machine | |||
if (unrecoverableErr == 1) { | |||
break; | |||
} | |||
delay(100); | |||
Serial.write("Complete!\n"); | |||
dry = 0; | |||
noCoffee = 0; | |||
break; | |||
} | |||
} |