@ -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; | |||||
} | |||||
} |