mqtt The Bartender - Automatisk drikke smaker bedre
Når man har automatisert lys, varme, dørlåser, garasjeporter, sengetepper, og så videre, så er selvfølgelig spørsmålet: "Hvordan kan jeg nå gjøre livet enklere for meg selv?" Svaret er åpenbart: Den tiden det tar å helde drikke fra en beholder til et glass er jo helt bortkastet, så det må jo automatiseres!
Og så er det jo selvfølgelig kulere.
Dermed ble The Bartender™ født. En liten 5V slangepumpe styrt av en ESP8266, et par slangelengder, litt tålmodighet, og voila!
Den kan selvfølgelig gjøre mer enn det:
Deler
- En NodeMCU eller ESP32 (som denne: https://www.aliexpress.com/item/32665100123.html)
- En slangepumpe (som denne: https://www.aliexpress.com/item/4000974680479.html)
- Et 5V relé brett (som dette: https://www.aliexpress.com/item/4000125547605.html)
- En 5V strømforsyning (f.eks. fra Clas Ohlson)
- Et knippe prosjekt-ledninger (som dette: https://www.aliexpress.com/item/33060775595.html)
- Strømtilkoblinger (som disse: https://www.aliexpress.com/item/4000097450967.html)
- En passende plankebit, jeg hadde en foring liggende
- Slanger (jeg fant på Biltema)
- Hullbånd
- Kantbånd for stryking
Oppkobling
Koble 3V og GND på NodeMCUen til VCC og GND på reléet. Koble D1 på NodeMCUen til Vin på reléet.
Utgangene på releet kobles i serie på strømforsyningen.
Kode
Jeg styrer den via MQTT. Det var enkleste måten å få inn og ut data fra den uten for mye styr. Den har følgende topics.
- /motor/get - sender status på motoren, verdi: 1 eller 0
- /motor/set - setter motoren på eller av, verdi: 1 eller 0
- /runtime - heltall på antall sekunder motoren skal kjøre
- /runtimestatus - rapporterter fortløpende hvor lenge i prosent motoren har kjørt av sekunder satt med /runtime (verdi: 0-100)
Alt sendes til "bartender/1", det siste /1 i tilfelle jeg skulle finne på å lage noen til…
Du trenger Arduio Studio og ha installert PubSubClient (https://pubsubclient.knolleary.net)
Bartender.ino:
#include <ESP8266WiFi.h> #include <PubSubClient.h> #define RelayPin 5 //D1 #define BUILTIN_LED 2 //D0 const char *ssid = "WIFI-SSID"; const char *password = "WIFI-PASSORD"; const char *mqtt_server = "IP_TIL_BROKER"; String clientId = "bartender1"; const char *topic = "bartender/1"; const char *motor_topic = "bartender/1/motor/set"; const char *motor_status_topic = "bartender/1/motor/get"; const char *runtime_topic = "bartender/1/runtime"; const char *runtime_status_topic = "bartender/1/runtimestatus"; unsigned long lastMotorSet; unsigned long lastRuntimeSet; unsigned long lastRuntimeUpdate; float runtimeOriginal; float runtime; int motorStatus; bool ignoreNextMotorStatusMessage; WiFiClient espClient; PubSubClient client(espClient); void setup() { //Set pins at output pinMode(BUILTIN_LED, OUTPUT); pinMode(RelayPin, OUTPUT); //Turn off motor digitalWrite(BUILTIN_LED, HIGH); digitalWrite(RelayPin, HIGH); //Connect to wifi and MQTT broker Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); } void setup_wifi() { delay(10); // Connect to wifi Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); //Wait until connected... while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } randomSeed(micros()); Serial.println(""); Serial.println("WiFi connected"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } //Handle reconnect if necessary void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect(clientId.c_str())) { Serial.println("connected"); client.subscribe(motor_topic); client.subscribe(runtime_topic); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 2 seconds"); // Wait 5 seconds before retrying delay(2000); } } } //Handle incomming MQTT callbacks void callback(char *topic, byte *payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); String stringPayload = ""; for (int i = 0; i < length; i++) { //Serial.print((char)payload[i]); stringPayload = stringPayload + (char)payload[i]; } //Serial.println(); Serial.println(stringPayload); if (strcmp(runtime_topic, topic) == 0) { Serial.println("Detected runtime_topic"); lastRuntimeSet = millis(); lastRuntimeUpdate = millis(); runtime = stringPayload.toFloat() * 1000; runtimeOriginal = runtime; } if (strcmp(motor_topic, topic) == 0) { // if (ignoreNextMotorStatusMessage == false) // { Serial.println("Detected motor_topic"); lastMotorSet = millis(); if (stringPayload == "1") if (motorStatus == 0) MotorOn(); if (stringPayload == "0") if (motorStatus == 1) MotorOff(); } } unsigned long lastReadTime; void loop() { if (!client.connected()) { reconnect(); } client.loop(); delay(1); if (runtime > 0) { runtime = runtime - (millis() - lastRuntimeUpdate); lastRuntimeUpdate = millis(); } if (runtime < 0) runtime = 0; if (lastRuntimeSet > lastMotorSet) { if (runtime > 0) { if (motorStatus == 0) { //ignoreNextMotorStatusMessage = true; MotorOn(); } } else { if (motorStatus == 1) { //ignoreNextMotorStatusMessage = true; MotorOff(); } } } if (millis() - lastReadTime > 250) { if (motorStatus == 1 && runtime > 0) { float percent = (runtimeOriginal - runtime) / runtimeOriginal * 100; String percenString = String(percent, 0); client.publish(runtime_status_topic, percenString.c_str()); Serial.print(percent); Serial.print("% -- "); } Serial.println(runtime); lastReadTime = millis(); } } void MotorOn() { motorStatus = 1; digitalWrite(RelayPin, LOW); digitalWrite(BUILTIN_LED, LOW); client.publish(motor_status_topic, "1"); client.publish(runtime_status_topic, "0"); Serial.println("Turning on..."); } void MotorOff() { motorStatus = 0; digitalWrite(BUILTIN_LED, HIGH); digitalWrite(RelayPin, HIGH); client.publish(motor_status_topic, "0"); client.publish(runtime_status_topic, "100"); Serial.println("Turning off..."); }
I HomeSeer ser det slik ut:
… som er satt opp i mscMQTT slik:
Dette kan man selvsagt enkelt legge til andre systemer, som Home Assistant, Node-Red eller Homey hvis man ønsker.
Kalibrering
En typisk drink er visstnok 40 ml. Jeg hadde et målebeger og kjørte pumpa til det nådde opp til 0,4 dl. Det tok 19,5 sekunder som jeg runder opp til 20. Dermed får vi sammenhengen
Kjøretid = Drink_størrelse * (20 sek /40 ml) = ca. drinkstørrelse * 0,5.
Men det kan være annerledes for din pumpe og strømforsyning.
Alexa
Den øverste devicen (merket 4052) er egentlig bare på/av som er lagt inn i Alexa. Der har jeg også bare laget en rutine som skrur på device 4052 når Alexa hører "pour me a drink". Når devicen blir skrudd på har jeg et event som sender "20" til topic "/runtime", og skrur seg selv av (dvs. til "inactive" etter 20 sekunder).
Så nå står den her på kjøkkenbenken. Tror jeg må få meg et barskap den kan passe inn i...
- 2
- 2
2 Comments
Recommended Comments