Was hat ein kühles Bier mit IoT zu tun
Seit einiger Zeit beschäftige ich mich intensiv mit dem Internet-of-Things (IoT) und frage mich dabei immer wieder: Wozu braucht man denn so was. Aber zugegebenermaßen – in Verbindung mit Mikrocontrollern wie dem Arduino ist es natürlich sehr reizvoll vom Smartphone Steckdosen zu schalten oder die Temperatur und Feuchte seines Kellers zu messen und vom Smartphone aus zu kontrolieren, und das egal wo ich mich gerade befinde.
Ein aktuelles Problem aus dem wahren Leben brachte mich diesem Thema gerade jetzt näher und ich möchte meine Erfahrungen mit euch teilen.
Die Aufgabe
Für ein kleines Fest sollten natürlich ausreichend kalte Getränke, insbesonders leckeres Kölsch zur Verfügung stehen. Der Kühlschrank in der Küche war schon mit leckerem Essen gefüllt und die Temperatur im Keller lag bei ca. 21°C während die Außentemperatur auf knapp 30°C tagsüber anstieg.
Die Idee
Was also tun? Im Keller steht seit einiger Zeit ein ungenutzter Gefrierschrank. Den kann man doch hervorragend als Kühlschrank nutzen, wenn man verhindert, dass er die Getränke einfriert. Also einfach, so die Idee, die Kühlung bei einer Temperatur von ca 5°C ausschalten und bei 6°C wieder einschalten.
Die Realisierung
Alles, was ich dazu brauche habe ich in meiner Bastelkiste:
a) zum Messen der Temperatur
– einen Arduino
– einen DS18B20 Temperatursensor
– einen 4k7 Widerstand
b) zum Schalten der Steckdose
– einen 433MHz Sender- eine Funksteckdose
c) zusätzlich
– ein Protoshield
Der DS18B20 wurde an ein Flachbandkabel gelötet und mit Acryldichtmasse wasserdicht gekapselt. Das Flachbandkabel wurde durch die Türdichtung des Gefrierschranks geführt und der Sensor wurde mit Klebeband im Innern befestigt. Das Programm wurde schnell aus den im Netz vorhandenen Bausteinen zusammengesetzt. Zum Messen der Temperatur wurde die OneWire Lib genutzt, wobei das Programm auf die bekannte Adresse des genutzten Sensors reduziert wurde. Jedem, der mit Netzspannung in seinen Projekten arbeitet, kann ich nur raten, diese immer mit einer Funksteckdose zu schalten. Netzspannung ist lebensgefährlich und mit Nutzung einer Funksteckdose kann fast nichts mehr passieren. Für die Ansteuerung der Funksteckdose benutze ich die RCSwitch Lib. Ich habe den Vorteil Steckdosen vom TypA mit einem Hauscode zu besitzen, damit ist das Schalten sehr simpel.
#include <OneWire.h> OneWire ds(6); // on pin x (a 4.7K pullup resistor is necessary) #include <RCSwitch.h> RCSwitch mySwitch = RCSwitch(); boolean refri_stat; float fptemp = 5.5; // initial set float delta = 0.5; // adjustment range /*************************** Sketch Code ************************************/ void setup(void) { Serial.begin(115200); Serial.println(F("Program started ...")); // Transmitter is connected to Arduino Pin #xx mySwitch.enableTransmit(7); // Switch off refrigerator mySwitch.switchOff("01111", "01000"); refri_stat = false; } void loop(void) { float temp_c = (measure_t()); // call the subroutine to measure if (!refri_stat && temp_c > fptemp + delta) { // decide to switch on/off with hysteresis mySwitch.switchOn("01111", "01000"); refri_stat = true; } if (refri_stat && temp_c < fptemp - delta) { mySwitch.switchOff("01111", "01000"); refri_stat = false; } Serial.print(" on/off state = "); Serial.println(refri_stat); } float measure_t() { byte i; byte present = 0; byte type_s; byte data[12]; byte addr[8] = { 0x28, 0x62, 0x21, 0x80, 0x04, 0x00, 0x00, 0xC1 }; // adress of the sensor ds.reset(); ds.select(addr); ds.write(0x44, 1); // start conversion, with parasite power on at the end delay(1000); // maybe 750ms is enough, maybe not // we might do a ds.depower() here, but the reset will take care of it. present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } int16_t raw = (data[1] << 8) | data[0]; float celsius = (float)raw / 16.0; Serial.print("Sensor present = "); Serial.print(present, HEX); Serial.print(" Temperature[C] = "); Serial.print(celsius); Serial.print(" "); return celsius; }
Die Ausgabe sieht dann so aus, gemessen wird ca. im Sekundenrhythmus, hier noch bei Raumtemperatur:
Program started ... Sensor present = 1 Temperature[C] = 24.94 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 24.94 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.00 on/off state = 1 Sensor present = 1 Temperature[C] = 25.06 on/off state = 1 Sensor present = 1 Temperature[C] = 25.06 on/off state = 1
…. und nun zum Thema IoT
Ein gewisses ungutes Gefühl blieb bestehen. Was ist mit der Temperatur? Schaltet der Arduino zuverlässig? Stürzt das Programm nicht ab? Ich hatte verständlicherweise keine Lust ständig in den Keller zu laufen, also musste eine andere Lösung her. Die Daten müssen in die Cloud! Während ich in meinem letzten Blogbeitrag den Service von data.sparkfun.com genutzt habe, werde ich hier den Service von Adafruit nutzen. Adafruit bietet nicht nur die Speicherung der Daten, sondern auch ein Dashboard, um seine Daten zu sehen und Eingaben zu tätigen. Die Basis der Kommunikation setzt hier auf das MQTT Protokoll auf. Das einzige zusätzliche Material, was zum Einsatz kam, war ein Ethernet Shield. Über ein Powerline Netzwerk wurde vom Router das Ethernet bis in den Keller verlängert.
Mit Hilfe der Tutorials auf Adafruit war mein Account schnell erstellt. Auf meinem Dashboard wollte ich nicht nur die Temperatur und den Zustand der Funksteckdose anzeigen lassen, sondern ich wollte auch den Sollwert der Temperatur verändern können. Ich brauchte also 3 Feeds:
1. die Temperatur – schreibend (publish) vom Arduino auf den Broker (so bezeichnet man den Server in einer MQTT Kommunikation)
2. den Zustand der Funksteckdose (an/aus) und damit die Funktion des Gefrierschranks (kühlen) – schreibend vom Arduino auf den Broker
3. die Einstellung des Soll-Temperaturwertes: am Dashboard einzustellen, der Arduino greift lesend (subscribe) auf den Broker zu
Hier die Sicht auf das Dashboard knapp 3 Stunden nach dem Einschalten. Die Temperatur schwingt zunächst noch über den Zielwert um sich dann später zwischen 13°C und 14°C einzupendeln. Der Status ist momentan Kühlung = aus. Die Einschaltdauer liegt jeweils bei ca. 1 min, aus bei ca. 30 min.
Das oben gelistete Programm wurde durch die notwendigen Befehle der Adafruit IO Library ergänzt. Ich habe versucht das durch zahlreiche Kommentare verständlich zu machen.
#include <OneWire.h> OneWire ds(6); // on pin x (a 4.7K pullup resistor is necessary) #include <RCSwitch.h> RCSwitch mySwitch = RCSwitch(); boolean refri_stat; /*************************************************** Adafruit MQTT Library Ethernet Example Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Alec Moore Derived from the code written by Limor Fried/Ladyada for Adafruit Industries. MIT license, all text above must be included in any redistribution ****************************************************/ #include <SPI.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include <Ethernet.h> #include <EthernetClient.h> #include <Dns.h> #include <Dhcp.h> /************************* Ethernet Client Setup *****************************/ byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; //Uncomment the following, and set to a valid ip if you don't have dhcp available. IPAddress iotIP (192, 168, 2, 99); //Uncomment the following, and set to your preference if you don't have automatic dns. //IPAddress dnsIP (8, 8, 8, 8); //If you uncommented either of the above lines, make sure to change "Ethernet.begin(mac)" to "Ethernet.begin(mac, iotIP)" or "Ethernet.begin(mac, iotIP, dnsIP)" /************************* Adafruit.io Setup *********************************/ #define AIO_SERVER "io.adafruit.com" #define AIO_SERVERPORT 1883 #define AIO_USERNAME "<your username here>" #define AIO_KEY "<your AIO key here>" /************ Global State (you don't need to change this!) ******************/ //Set up the ethernet client EthernetClient client; // Store the MQTT server, client ID, username, and password in flash memory. // This is required for using the Adafruit MQTT library. const char MQTT_SERVER[] PROGMEM = AIO_SERVER; // Set a unique MQTT client ID using the AIO key + the date and time the sketch // was compiled (so this should be unique across multiple devices for a user, // alternatively you can manually set this to a GUID or other random value). const char MQTT_CLIENTID[] PROGMEM = __TIME__ AIO_USERNAME; const char MQTT_USERNAME[] PROGMEM = AIO_USERNAME; const char MQTT_PASSWORD[] PROGMEM = AIO_KEY; Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, AIO_SERVERPORT, MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD); /****************************** Feeds ***************************************/ // Setup a feed called 'biertemp' for publishing. // Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname> const char BIERMON_FEED[] PROGMEM = AIO_USERNAME "/feeds/biertemp"; Adafruit_MQTT_Publish biermon = Adafruit_MQTT_Publish(&mqtt, BIERMON_FEED); const char ONOFF_FEED[] PROGMEM = AIO_USERNAME "/feeds/onoff"; Adafruit_MQTT_Publish onoffbutton = Adafruit_MQTT_Publish(&mqtt, ONOFF_FEED); const char SETTEMP_FEED[] PROGMEM = AIO_USERNAME "/feeds/settemp"; // definition of feedname Adafruit_MQTT_Subscribe settemp = Adafruit_MQTT_Subscribe(&mqtt, SETTEMP_FEED); // subscription float stat = 0; float fptemp = 25; // initial set float delta = 0.1; // adjustment range unsigned long lastmillis; /*************************** Sketch Code ************************************/ void setup(void) { Serial.begin(115200); Serial.println(F("Program started ...")); // Transmitter is connected to Arduino Pin #xx mySwitch.enableTransmit(7); // Switch off refrigerator mySwitch.switchOff("01111", "01000"); refri_stat = false; Ethernet.begin(mac, iotIP); delay(1000); //give the ethernet a second to initialize Serial.print("My IP address: "); Serial.println(Ethernet.localIP()); mqtt.subscribe(&settemp); // activate subscription to feed settemp } void loop(void) { MQTT_connect(); Adafruit_MQTT_Subscribe *subscription; // check subscription while ((subscription = mqtt.readSubscription(500))) { // Check if its the settemp feed if (subscription == &settemp) { Serial.print(F("Got new Temp: ")); Serial.println((char *)settemp.lastread); fptemp = atof((char *)settemp.lastread); // set to new temperature Serial.println(fptemp); } else { Serial.print(F("nothing to read ")); } } float temp_c = (measure_t()); if (!refri_stat && temp_c > fptemp + delta) { mySwitch.switchOn("01111", "01000"); refri_stat = true; } if (refri_stat && temp_c < fptemp - delta) { mySwitch.switchOff("01111", "01000"); refri_stat = false; } Serial.print(" on/off state = "); Serial.println(refri_stat); // Now we can publish stuff! if (stat != float(refri_stat)) { // only when changed Serial.print(F("\nSending changed on/off state ")); Serial.print(refri_stat); Serial.println("..."); stat = float(refri_stat); // published value has to be float (don't know why) if (! onoffbutton.publish(stat)) { Serial.println(F("Failed")); } else { Serial.println(F("OK!")); } } if (millis() > lastmillis + 15000) { // read / write broker every xx milliseconds MQTT_connect(); // Now we can publish stuff! Serial.print(F("\nSending temp val ")); Serial.print(temp_c); Serial.println("..."); if (! biermon.publish(temp_c)) { Serial.println(F("Failed")); } else { Serial.println(F("OK!")); } // ping the server to keep the mqtt connection alive if (! mqtt.ping()) { mqtt.disconnect(); } lastmillis = millis(); } } float measure_t() { byte i; byte present = 0; byte type_s; byte data[12]; byte addr[8] = { 0x28, 0x62, 0x21, 0x80, 0x04, 0x00, 0x00, 0xC1 }; //byte addr[8] = { 0x28, 0x42, 0xCC, 0x7F, 0x04, 0x00, 0x00, 0xAA }; ds.reset(); ds.select(addr); ds.write(0x44, 1); // start conversion, with parasite power on at the end delay(1000); // maybe 750ms is enough, maybe not // we might do a ds.depower() here, but the reset will take care of it. present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } int16_t raw = (data[1] << 8) | data[0]; float celsius = (float)raw / 16.0; Serial.print("Sensor present = "); Serial.print(present, HEX); Serial.print(" Temperature[C] = "); Serial.print(celsius); Serial.print(" "); return celsius; } // Function to connect and reconnect as necessary to the MQTT server. // Should be called in the loop function and it will take care if connecting. void MQTT_connect() { int8_t ret; // Stop if already connected. if (mqtt.connected()) { return; } Serial.print("Connecting to MQTT... "); while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected Serial.println(mqtt.connectErrorString(ret)); Serial.println("Retrying MQTT connection in 5 seconds..."); mqtt.disconnect(); delay(5000); // wait 5 seconds } Serial.println("MQTT Connected!"); }
Video
Veröffentlicht am 6. August 2016, in Arduino, IoT, Tutorial, Uncategorized. Setze ein Lesezeichen auf den Permalink. Hinterlasse einen Kommentar.
Hinterlasse einen Kommentar
Kommentare 0