Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

IoT leicht gemacht mit UNO, ESP-01, ThingSpeak und MIT App Erfinder

Komponenten und Verbrauchsmaterialien

Arduino UNO
× 1
Espressif ESP8266 ESP-01
× 1
DHT22-Temperatursensor
× 1
DS18B20 digitaler 1-Draht-Temperatursensor für den Einsatz im Boden
× 1
LDR - Lichtabhängiger Widerstand
× 1
2-Kanal-DC-5-V-Relaismodul
× 1

Apps und Onlinedienste

Arduino-IDE
ThingSpeak API
MIT App Inventor

Über dieses Projekt

Unser Ziel wird es sein, grundsätzlich Informationen von einer lokalen Einheit zu sammeln und an das Internet zu senden. Ein Benutzer überall auf der Welt, der diese Informationen betrachtet, trifft Entscheidungen, indem er Fernbefehle an die Aktoren sendet, die sich ebenfalls in dieser lokalen Einheit befinden. Jeder Sensor oder Aktor kann verwendet werden.

Der Großteil meiner Arbeit in der IoT-Arena war die Verwendung von NodeMCU und in jüngerer Zeit ESP32. Aber ich denke, es ist wichtig, meine frühen Schritte nicht zu vergessen, vor ein paar Jahren, als ich mit dem Erlernen des IoT mit einem Arduino UNO und dem alten und guten ESP8266-01 begann.

Also beschließe ich hier, in diese Zeit zurückzukehren (mit etwas mehr Erfahrung jetzt) ​​und diese großartigen Geräte erneut zu erkunden, indem ich sie über den ThingSpeak.com-Webservice mit der Cloud verbinde. Wir werden auch versuchen, Dinge aus der Ferne zu steuern, indem wir eine Android-App verwenden, die mit dem MIT AppInventor entwickelt wurde.

Das "Zentrum unseres IoT-Projekts" wird ThingSpeak.com sein. Die lokale Einheit (UNO/ESP-01) erfasst Daten von Sensoren und Aktorstatus, senden sie ins Internet, "schreiben" auf einem bestimmten ThingSpeak.com Statuskanal . Die örtliche Einheit erhält auch Daten aus dem Internet, "Lesen" von bestimmten ThingSpeak Actuator Channels .

Eine Android-App wird auch lesen diese Daten aus dem ThingSpeak.com Status Channel und deren Anzeige für den Benutzer. Auf die gleiche Weise kann der Benutzer basierend auf diesen Statusinformationen Befehle an Aktoren senden, schreibend sie auf ThingSpeak-Aktuatorkanälen (Siehe obiges Blockdiagramm, um den Datenfluss besser zu verstehen).

Also, was werden wir tun? Das im nächsten Schritt gezeigte Blockschaltbild gibt uns einen Überblick über das endgültige Projekt:

Schritt 1:Einführung

Mit gängigen Sensoren wird unser Projekt mehrere Daten erfassen und an die Cloud senden, wo jeder sie über das Internet sehen kann. Um diese Daten zu verarbeiten, verwenden wir den Service von ThingSpeak.com, einer offenen IoT-Plattform, die es uns ermöglicht, diese Daten zu sammeln, zu analysieren und darauf zu reagieren.

Die von Sensoren zu sammelnden Daten wird sein:

  • Lufttemperatur und relative Luftfeuchtigkeit
  • Bodentemperatur und -feuchtigkeit
  • Leuchtkraft

Das Projekt wird 2 Aktoren haben :

  • Elektrische Pumpe
  • Elektrische Lampe

Der Status dieser Aktoren ("ON/OFF") sollte ebenfalls an die Cloud gesendet werden.

Die Idee besteht also darin, diese Daten von den Sensoren, beispielsweise einer Plantage, zu erfassen und in die Cloud zu senden. Basierend auf diesen Daten sollte ein Benutzer die Entscheidung basierend auf diesen Aussagen treffen:

  • Schalten Sie die Pumpe EIN, wenn die Bodenfeuchtigkeit zu niedrig ist
  • Schalten Sie die Lampe EIN, wenn die Bodentemperatur zu niedrig ist

Um unsere Aktoren aus der Ferne zu steuern, verwenden wir eine Android-App.

Schritt 2:Stückliste - Stückliste

Einige der wichtigsten hier aufgeführten Komponenten sind mit einem Link und einem indikativen Preis in USD verbunden. Diese Links dienen nur zur Information.

  • Arduino UNO (Mikrocontroller) - $10,00
  • ESP8266-01 (Kommunikationsmodul) - 3,50 $
  • DHT22 (Sensor für Luft und relative Luftfeuchtigkeit) - 9,00 $
  • DS18B20 (1-Draht-Digital-Temperatursensor für den Einsatz auf dem Boden) - 6,00 USD
  • YL-69 + LM393 (Bodenfeuchtesensor) - 2,00 $
  • LDR (Helligkeitssensor) - 0,20 €
  • 2 x LEDs (Rot und Grün)
  • 1 x 2-Kanal-DC-5-V-Relaismodul mit Optokoppler-Low-Level-Trigger - 7,00 USD
  • 5-V-Gleichstrompumpe - 3,00 USD
  • 220-V-Lampe
  • 2 x 330 Ohm Widerstand (zur Verwendung mit LEDs)
  • 2 x 10K Ohm Widerstand (zur Verwendung mit DHT22 und LDR)
  • 1 x 4K7 Ohm Widerstand (zur Verwendung mit DS18B20
  • Protoboard
  • Pullover
  • Externe 5V DC Stromversorgung für Relais

Schritt 3:Die Hardware

Lassen Sie uns die Projekt-HW zusammenbauen. Das Ideal ist, unser Projekt nach Teilen zu installieren und zu testen. Als Vorschlag können wir die Schritte befolgen:

  • Installieren und testen Sie alle Sensoren lokal
  • Installieren und konfigurieren Sie das ESP-01 (BareMinimum)
  • Ändern Sie die ESP-01-Installation für ihre endgültige Konfiguration und testen Sie sie
  • Konfigurieren Sie den ThingsPeak-Statuskanal
  • Installieren Sie ThingsPeak-Code in Ihrem Arduino und überprüfen Sie den Sensorstatus in der Cloud
  • Entwickeln Sie die erste Version der Android-App, um Status und Nachrichten anzuzeigen
  • Aktoren (LEDs und Relais) installieren
  • ThingSpeak Actuators-Kanäle konfigurieren
  • Arduino-Code mit Aktoren installieren und testen
  • Entwickeln Sie die endgültige Android-App-Version

Schritt 4:Sensoren anschließen

Wir müssen einige Bibliotheken auf unserer IDE installiert haben, um die Sensoren richtig zu lesen. Überprüfen Sie, ob alle Bibliotheken installiert sind. Ihre anfängliche Konfiguration sollte sein:

// DS18B20#include #include #define ONE_WIRE_BUS 5 // DS18B20 an Pin D5 OneWire oneWire(ONE_WIRE_BUS);DallasTemperature DS18B20(&oneWire);int groundTemp =0; //DHT#include "DHT.h"#include int pinoDHT =11;int tipoDHT =DHT22;DHT dht(pinoDHT, tipoDHT); int airTemp =0;int airHum =0;// LDR (Light)#define ldrPIN 1int light =0;// Bodenfeuchtigkeit#define groundHumPIN 0int groundHum =0; 

Bei Setup und Schleife schreiben wir:

void setup () { Serial.begin (9600); DS18B20.begin(); dht.begin();}void loop(){readSensors(); DisplaySensoren(); Verzögerung (10000);} 

Und zuletzt schreiben wir zwei spezifische Funktionen, eine zum Lesen unserer Sensoren und eine andere zum Anzeigen ihrer Werte auf dem Serial Monitor:

/********* Sensorwert lesen *************/void readSensors(void) { airTemp =dht.readTemperature(); airHum =dht.readHumidity(); DS18B20.requestTemperatures(); BodenTemp =DS18B20.getTempCByIndex(0); // Sensor 0 wird die Bodentemperatur in Celsius erfassen. groundHum =map (analogRead (soilHumPIN), 1023, 0, 0, 100); light =map(analogRead(ldrPIN), 1023, 0, 0, 100); //LDRDark:0 ==> Licht 100%}/********* Sensorwert anzeigen ************/void displaySensors(void) {Serial.print (" Lufttemp (oC):"); Serial.println (airTemp); Serial.print ("airHum (%):"); Serial.println (airHum); Serial.print ("soilTemp (oC):"); Serial.println (soilTemp); Serial.print ("soilHum (%):"); Serial.println (soilHum); Serial.print ("light (%):"); Serial.println (hell); Serial.println ("");} 

Das untere Bild des Serial Monitors zeigt uns die Sensorwerte.

Der Code kann von meinem GITHUB heruntergeladen werden:Sens ors_Test.ino

Schritt 5:​ESP8266-01 Erstkonfiguration

Das ESP-01 wird als serielle Brücke verwendet, was bedeutet, dass wir es mit "AT-Befehlen" programmieren. Stellen Sie zunächst sicher, dass Ihr ESP-01 die richtige Übertragungsgeschwindigkeit der Baudrate hat. In unserem Fall 9.600 Baud. Normalerweise ist das ESP-01 ab Werk mit 115.200 Baud programmiert und wir müssen es auf 9.600 Baud ändern.

Zuerst müssen Sie das ESP-01 wie oben gezeigt anschließen.

Verbinden Sie dann das Arduino mit Ihrem Computer, öffnen Sie die IDE und laden Sie das Beispiel, das sich in Datei> Beispiele> 01.Basics> BareMinimum befindet. Dies ist ein leerer Code, um sicherzustellen, dass es keinen Kommunikationskonflikt zwischen Arduino und ESP gibt.

Wir haben diesen Code auf das Arduino übertragen, bevor wir es mit dem ESP-01S verbinden, um sicherzustellen, dass das Arduino keine serielle Kommunikation (TX und RX) verwendet. Dies ist wichtig, damit ESP richtig kommunizieren kann.

Öffnen Sie Ihren IDE Serial Monitor und ändern Sie die Geschwindigkeit auf 115.200 Baud Beginnen Sie mit dem Senden eines "AT"-Befehls auf Ihrem IDE Serial Monitor. Das ESP-01 sollte "OK" zurückgeben

Als nächstes ändern wir die Geschwindigkeit. Dazu können Sie den Befehl verwenden:

AT + CIOBAUD =9600 

Beachten Sie, dass das ESP-01 möglicherweise auf die Werksprogrammierung zurückkehrt (ich weiß nicht, ob dies an der FW-Version liegt). Zumindest in meinem Fall musste ich einen anderen Befehl verwenden, um die BaudRate endgültig zu ändern:

AT+ UART_DEF=,,,, 

Zum Beispiel:9600 Baud / 8 Datenbits / 1 Stoppbits und keine Parität und Flusskontrolle:

AT + UART_DEF =9600,8,1,0,0 

Ändern Sie im Auswahlfeld am unteren Rand Ihres Serial Monitors die Geschwindigkeit auf "9600 Baud". Testen Sie die Kommunikation:Geben Sie auf der oberen Seite des Fensters AT ein und sehen Sie die Antwort OK. Jetzt müssen Sie das Modul im Stationsmodus konfigurieren als Kunde fungieren Ihres WLAN-Netzwerks. Verwenden Sie den Befehl:

 AT + CWMODE =1 

Jetzt müssen wir das Modul mit Ihrem Wi-Fi-Netzwerk verbinden.

Verwenden Sie dazu die folgenden Befehle und ersetzen Sie "network_name" durch den Namen Ihres Wi-Fi-Netzwerks und "network_name" durch Ihr Passwort. Behalten Sie die Anführungszeichen bei.

AT + CWJAP ="network_name", "network_name"  

Wenn Sie die folgende Antwort sehen, wurde Ihre Verbindung korrekt hergestellt:

WIFI VERBUNDENES WIFI GOT IP 

Um die IP zu finden, führen Sie den Befehl aus:

AT + CIFSR  

Und notieren Sie sich die IP-Adresse, die in Ihrem Serial Monitor angezeigt wird. Sie können es in Zukunft brauchen.

Schritt 6:Testen des ESP-01

Sobald wir das ESP-01 konfiguriert haben, müssen wir es in seinem letzten Stromkreis installieren. Dazu müssen wir die zuvor vorgenommene Verkabelung ÄNDERN und das ESP-01 wie folgt an unsere UNO anschließen:

  • ESP-01 RX (Gelb) an UNO Pin D7
  • ESP-01 TX (Orange) an UNO Pin D6
  • ESP-01 Ch-Pd (Braun) zu Vcc (3.3V)
  • ESP-01 Reset (Blau) auf UNO Pin D8
  • ESP-01 Vcc (Rot) bis 3,3 V
  • ESP-01 Gnd (Schwarz) an UNO GND

Lassen Sie uns einen einfachen Test durchführen, um zu überprüfen, ob unser ESP-01 korrekt installiert und getestet wurde. Geben Sie den folgenden Code ein:

#include  SoftwareSerial esp8266(6,7); // Rx ==> Pin 6; TX ==> Pin7 #define speed8266 9600 void setup() { esp8266.begin (speed8266); Serial.begin (speed8266); Serial.println ("ESP8266 Setup-Test - AT-Coomands verwenden");}void loop () { Während (esp8266.available ()) { Serial.write (esp8266.read ()); aufrechtzuerhalten. Während (Serial.available ()) { esp8266.write (Serial.read ()); }} 

Versuchen Sie nun einige AT-Befehle und sehen Sie sich das Ergebnis auf Ihrem seriellen Monitor an:

* AT =====> ESP8266 gibt OK* AT+RST =====> ESP8266 Neustart und gibt OK* zurück AT+GMR =====> ESP8266 gibt AT Version zurück; SDK-Version; Ich würde; OK* AT+CWMODE? => ESP8266 gibt Modustyp zurück* AT+CWLAP ===> ESP8266 gibt Zugangspunkte schließen zurück* AT+CIFSR ===> ESP8266 gibt Designided IP zurück 

Der Code kann von meinem GITHUB heruntergeladen werden:ESP_AT_Config.ino

Wenn Sie jedes Mal, wenn ein Reset auftritt (oder Ihr Arduino aus- / eingeschaltet wird) und Ihre Zugangsdaten eingeben, eine Verbindung zum WiFi-Netzwerk herstellen möchten, fügen Sie einen Anruf zum connectWiFi () . hinzu Funktion am Ende des Setups () Funktion:

setup(){ ... connectWiFi(); } 

Das connectWiFi() Die Funktion sollte am Ende Ihres Hauptcodes .ino stehen:

void connectWiFi(void){ sendData("AT+RST\r\n", 2000, 0); // sendData zurücksetzen("AT+CWJAP=\"IHR BENUTZERNAME\",\"IHR PASSWORT\"\r\n", 2000, 0); // Netzwerkverzögerung verbinden (3000); sendData("AT+CWMODE=1\r\n", 1000, 0); sendData("AT+CIFSR\r\n", 1000, 0); // IP-Adresse anzeigen Serial.println("8266 Connected");} 

Beachten Sie, dass die obige Funktion ein weiteres sendData (data) aufruft Funktion, die sich auch in Ihrem Code befinden sollte:

String sendData(String-Befehl, const int timeout, boolean debug){ String response =""; EspSerial.print (Befehl); lange int-Zeit =millis(); while ( (time + timeout)> millis ()) { while (EspSerial.available ()) { // Der ESP hat Daten, also zeige seine Ausgabe im seriellen Fenster an char c =EspSerial.read (); // Lesen Sie das nächste Zeichen. Antwort +=c; }} Wenn (Debug) {Serial.print (Antwort); } Antwort zurückgeben;}  

Schritt 7:Anschließen von Sensoren und ESP-01

Sobald wir alle Sensoren installiert und getestet haben und auch unser ESP-01 ordnungsgemäß funktioniert, sehen wir uns alle zusammen an und bereiten uns darauf vor, Daten ins Internet zu senden.

Schritt 8:Das ThingSpeak

Einer der wichtigsten Teile unseres Projekts ist ThingSpeak, eine offene IoT-Plattform, die es uns ermöglicht, gesammelte Daten zu sammeln, zu analysieren und darauf zu reagieren. Wenn Sie noch kein Konto haben, gehen Sie bitte zu ThingSpeak registrieren und erstellen Sie Ihr Konto.

Als nächstes erstellen Sie einen neuen Kanal, in dem wir unsere 2 Aktoren, 5 Sensoren und einen freien Feldstatus haben:

  • Feld 1:Aktuator 1
  • Feld 2:Aktuator 2
  • Feld 3:Lufttemperatur in oC
  • Datei 4:Relative Luftfeuchtigkeit in %
  • Feld 5:Bodentemperatur in oC
  • Feld 6:Bodenfeuchte in %
  • Feld 7:Helligkeit in %
  • Feld 8:Ersatz

Feld 8 wird als Reserve für zukünftige Erweiterungen oder für Debug-Zwecke übrig bleiben. Zum Beispiel werde ich es als "Zähler" für jeden Kommunikationsfehler verwenden, der während des Arduino/ESP-01-Handshakes mit ThingSpeak.com auftritt.

Sobald Sie Ihren Kanal erstellt haben (in diesem Fall unser Statuskanal), ist es wichtig, sich Ihre Schlüssel wie unten gezeigt zu notieren.

Schritt 9:Status an die Cloud senden

Zu diesem Zeitpunkt haben wir unseren Cloud-Service zur Verfügung und unsere Sensoren erfassen Daten lokal. Nehmen wir diese Werte und senden sie an ThingSpeak.com. Wir werden auf dem ThingSpeak-Kanal SCHREIBEN und dafür müssen wir einen GET-String senden. Wir werden in 3 Teilen tun:

Wir senden ein "Start cmd":

AT+CIPSTART="TCP","184.106.153.149",80 

Nach der "Länge" des Befehls:

AT+CIPSEND=116 

Und der GET-String selbst, der in die entsprechenden Felder im Statuskanal schreibt:

GET /update?api_key=YOUR_WRITE_KEY_HERE&field1=pump&fieldlamp=0&field3=airTemp&field4=airHum&field5=soilTemp&field6=soilHum&field7=light&field8=spare

Der folgende Code wird die Arbeit für uns erledigen und der obige PrintScreen zeigt das Ergebnis auf Serial Monitor:

// Thingspeak String statusChWriteKey ="6SRPQQKIE6AJVQE6"; // Statuskanal-ID:385184#include SoftwareSerial EspSerial(6, 7); // Rx, Tx#define HARDWARE_RESET 8// DS18B20#include #include #define ONE_WIRE_BUS 5 // DS18B20 auf Pin D5 OneWire oneWire(ONE_WIRE_BUS);DallasTemperature DS18B20(&Tempone .Wire);int Boden =0;//DHT#include "DHT.h"#include int pinoDHT =11;int tipoDHT =DHT22;DHT dht(pinoDHT, tipoDHT); int airTemp =0;int airHum =0;// LDR (Light)#define ldrPIN 1int light =0;// Bodenfeuchtigkeit#define groundHumPIN 0int groundHum =0;// Variablen die mit Timerslong writeTimingSeconds =17 verwendet werden sollen; // ==> Definiere die Abtastzeit in Sekunden zum Senden von datalong startWriteTiming =0;long elapsedWriteTime =0;// Variablen, die mit Actuators verwendet werden sollen boolesche Pumpe =0; Boolesche Lampe =0; int spare =0;boolescher Fehler;void setup () { Serial.begin (9600); pinMode (HARDWARE_RESET, AUSGANG); digitalWrite (HARDWARE_RESET, HOCH); DS18B20.begin(); dht.begin(); EspSerial.begin(9600); // Comunicacao com Modulo WiFi EspHardwareReset(); // Reset tun Modulo WiFi startWriteTiming =millis (); // Starten der "Programmuhr"}void loop () { start:// Label error =0; elapsedWriteTime =millis()-startWriteTiming; if (elapsedWriteTime> (writeTimingSeconds*1000)) {readSensors(); writeThingSpeak(); startWriteTiming =millis(); } if (error ==1) // erneut senden, wenn die Übertragung nicht abgeschlossen ist { Serial.println ( " <<<>>>"); Verzögerung (2000); gehe zum Start; // gehe zum Label "Start"}}/********* Sensorwert lesen *************/void readSensors(void) {airTemp =dht.readTemperature(); airHum =dht.readHumidity(); DS18B20.requestTemperatures(); BodenTemp =DS18B20.getTempCByIndex(0); // Sensor 0 erfasst die Bodentemperatur bei Celcius-Licht =map (analogRead (ldrPIN), 1023, 0, 0, 100); //LDRDark:0 ==> Licht 100% BodenHum =map(analogRead(soilHumPIN), 1023, 0, 0, 100); }/********* Conexao com TCP com Thingspeak ******/void writeThingSpeak(void){ startThingSpeakCmd(); // String vorbereiten GET String getStr ="GET /update?api_key="; getStr +=statusChWriteKey; getStr +="&field1="; getStr +=String(Pumpe); getStr +="&field2="; getStr +=String(Lampe); getStr +="&field3="; getStr +=String(airTemp); getStr +="&field4="; getStr +=String(airHum); getStr +="&field5="; getStr +=String(bodenTemp); getStr +="&field6="; getStr +=String(soilHum); getStr +="&field7="; getStr +=String (Licht); getStr +="&field8="; getStr +=String(Ersatz); getStr +="\r\n\r\n"; sendThingSpeakGetCmd(getStr); }/********* ESP zurücksetzen *************/void EspHardwareReset (void) {Serial.println ("Reseting ......."); digitalWrite (HARDWARE_RESET, NIEDRIG); Verzögerung (500); digitalWrite (HARDWARE_RESET, HOCH); Verzögerung (8000); // Tempo notwendig für Começar ein ler Serial.println ("RESET"); }/********* Kommunikation mit ThingSpeak starten*************/void startThingSpeakCmd(void){ EspSerial.flush();//limpa o buffer antes de começar a Gravar String cmd ="AT+CIPSTART=\"TCP\",\""; cmd +="184.106.153.149"; // Endereco IP de api.thingspeak.com cmd +="\",80"; EspSerial.println(cmd); Serial.print("enviado ==> Starte cmd:"); Serial.println (cmd); if(EspSerial.find("Fehler")) {Serial.println("AT+CIPSTART Fehler"); Rückkehr; }}/********* sende ein GET cmd an ThingSpeak *************/String sendThingSpeakGetCmd(String getStr){ String cmd ="AT+CIPSEND="; cmd +=String(getStr.length()); EspSerial.println(cmd); Serial.print("enviado ==> Länge cmd:"); Serial.println (cmd); if(EspSerial.find((char *)">")) { EspSerial.print(getStr); Serial.print("enviado ==> getStr:"); Serial.println (getStr); delay(500);//Tempo für Prozessor o GET, semest delay apresenta busy no próximo comando String messageBody =""; while (EspSerial.available()) { String line =EspSerial.readStringUntil('\n'); if (line.length() ==1) { //der eigentliche Inhalt beginnt nach einer leeren Zeile (die Länge 1 hat) messageBody =EspSerial.readStringUntil('\n'); } } Serial.print ( "MessageBody erhalten:"); Serial.println (messageBody); Nachricht zurückgeben; } else { EspSerial.println("AT+CIPCLOSE"); // Benutzer alarmieren Serial.println ( "ESP8266 CIPSEND ERROR:RESENDING"); //Erneut senden... Ersatz =Ersatz + 1; Fehler=1; "Fehler" zurückgeben; } }  

Der Code kann von meinem GITHUB heruntergeladen werden:SendingStatusTS_EXT.ino

Schritt 10:Der erste Teil der Android-App - Statusüberwachung

Lassen Sie uns unseren ersten Teil der Android-App erstellen.

Zunächst gestalten wir die Benutzeroberfläche. Der obige Druckbildschirm oben zeigt die wichtigsten sichtbaren und nicht sichtbaren Elemente. Danach müssen wir die Blöcke entwerfen (die folgenden Zahlen entsprechen den obigen Abbildungen):

Alle 2 Sekunden (definiert durch Clock1) rufen wir eine Prozedur namens "readArduino" auf.

  • Die Rückgabe einer solchen Prozedur ist der Wert für jede der Statusvariablen, die auf dem Bildschirm angezeigt werden soll.
  • Beachten Sie, dass wir zum besseren Verständnis die Werte "0" und "1" aus dem Aktorstatus für "AUS" und "EIN" "umwandeln".
  • Diese Werte ("Status") werden auf entsprechenden "Labels" angezeigt
  • Die Statusvariablen müssen als Global deklariert werden.
  • Die Prozedur "readArduino" liest tatsächlich den Status Channel bei ThingSpeak. Wir müssen also die URL definieren, die an Thingspeak gesendet werden soll. Dazu müssen 3 globale Variablen deklariert und verknüpft werden, um die an TS zu sendende URL zu erstellen. Ein GET sollte an die Webkomponente namens "ArduFarmBotStatusCh" gesendet werden
  • Der vom vorherigen Befehl erhaltene Text wird im JSon-Format ankommen. Dieser Text muss verarbeitet werden, indem jedes Feld gelesen und in der entsprechenden globalen Variablen gespeichert wird.
  • Das letzte, was getan werden muss, ist die "Alarm"-Prozedur aufzurufen, die den Status von zwei Bodensensoren analysiert. Wenn die Temperatur zu niedrig ist (in unserem Fall 10oC), muss eine Meldung angezeigt werden. Gleiches gilt für die Luftfeuchtigkeit, wenn sie unter 60 % liegt. Beachten Sie, dass wir einen weiteren Timer (Clock2) definiert haben, der so programmiert ist, dass er jede Sekunde ausgelöst wird. Dies dient nur zum "Umschalten" der Farbe des Nachrichtentextes (von weiß auf rot). Dadurch "blinkt" die Nachricht.

Das letzte Foto oben zeigt die endgültige Funktion der App.

Der App-Code kann von meinem GITHUB heruntergeladen werden:ArduFarmBot_Status_App_EXT.aia

Schritt 11:Aktoren (LEDs und Relais) installieren

Vervollständigen wir unser HW.

Dafür müssen wir unsere Aktuatoren installieren. Wie Sie sich erinnern, erhalten wir aus der Ferne Befehle zum Ein- und Ausschalten einer Pumpe und einer Lampe. Der Arduino-Ausgang aktiviert ein Relais (und eine LED), um diese Aktionen zu erhalten.

Wir verwenden ein Relaismodul mit einem Optokoppler-Low-Level-Trigger. Außerdem liefern wir 5 V über einen separaten Pin, sodass wir nicht den erforderlichen Strom am Eingangspin bereitstellen müssen. Das erledigt das Modul für uns.

Die obige Abbildung zeigt, wie die Aktoren angeschlossen werden müssen. Beachten Sie, dass der Realy GND NICHT mit Arduino GND verbunden ist. Dies trägt dazu bei, dass kein Rauschen entsteht, wenn das Relais arbeitet.

Der Einfachheit halber habe ich aus dem Diagramm die Sensoren herausgenommen. Sie können jedoch die Aktorenschaltung zu Ihrem Projekt hinzufügen, ohne die bereits installierten und getesteten Sensoren herauszunehmen.

Schritt 12:Konfigurieren der ThingSpeak-Aktuatorkanäle

Auf die gleiche Weise wie für den Status werden wir 2 neue Kanäle erstellen, einen für jeden Aktor. Notieren Sie sich von jedem von ihnen die Kanal-ID, die Lese- und Schreibtasten. Wir schreiben nur auf Feld 1 von jedem dieser Kanäle. Also in meinem Fall zum Beispiel:

Kanal-ID 375598 ==> LED Rot (Pumpe)

  • Feld1 =0 ==> Pumpe AUS
  • Feld1 =1 ==> Pumpe EIN

Kanal-ID 375599 ==> LED Grün (Lampe)

  • Feld1 =0 ==> Lampe AUS
  • Feld1 =1 ==> Lampe EIN

Schritt 13:Arduino-Code mit Aktoren installieren und testen

Als wir Daten an das Web gesendet haben, haben wir auf einen ThingSpeak-Kanal (Kanalstatus) geschrieben, um Daten zu "senden" (hochzuladen). Jetzt sollten wir von einem ThingSpeak-Kanal (Aktuator-Kanal) LESEN, um Daten zu "empfangen" (herunterzuladen).

Wir werden von einem ThingSpeak-Kanal LESEN und dafür müssen wir einen GET-String senden. Wir werden in 3 Teilen tun:

Wir senden ein "Start cmd":

AT+CIPSTART="TCP","184.106.153.149",80 

Nach der "Länge" des Befehls:

AT+CIPSEND=36 

Und der GET-String selbst, der aus Feld 1 auf jedem der Aktuatorkanäle gelesen wird:

GET /channels/375598/fields/1/last 

Wir werden in Abständen von 10 Sekunden aus ThingSpeak-Kanälen lesen. Nachdem wir den obigen GET-Befehl gesendet haben, der den "LAST VALUE STORED ON FIELD 1" fordert, erhalten wir eine Antwort von ThingSpeak, die an einer bestimmten Position der Antwort "1" oder "0" sein sollte angekommen, müssen wir es ignorieren.

Der Hauptunterschied zwischen diesem Teil des Codes und dem vorherigen (zum Senden von Statusdaten) ist die Funktion:

readThingSpeak(String channelID) 

Der folgende Code wird die Arbeit für uns erledigen und der obige PrintScreen zeigt das Ergebnis auf Serial Monitor:

// Thingspeak String canalID1 ="375598"; //Aktor1String canalID2 ="375599"; //Actuator2#include SoftwareSerial EspSerial(6, 7); // Rx, Tx#define HARDWARE_RESET 8// Zu verwendende Variablen mit Timerslong readTimingSeconds =10; // ==> Definiere die Abtastzeit in Sekunden zum Empfangen von datalong startReadTiming =0;long elapsedReadTime =0;//Relays#define ACTUATOR1 10 // ROTE LED ==> Pump#define ACTUATOR2 12 // GRÜNE LED ==> Lampboolesche Pumpe =0; Boolesche Lampe =0; int spare =0;boolescher Fehler;void setup () { Serial.begin (9600); pinMode (AKTUATOR1, AUSGANG); PinMode (AKTUATOR2, AUSGANG); pinMode (HARDWARE_RESET, AUSGANG); digitalWrite (AKTUATOR1, HOCH); //o módulo relé é ativo em LOW digitalWrite (ACTUATOR2, HIGH); //o módulo relé é ativo em LOW digitalWrite (HARDWARE_RESET, HIGH); EspSerial.begin(9600); // Comunicacao com Modulo WiFi EspHardwareReset(); // Reset tun Modulo WiFi startReadTiming =millis (); // Starten der "Programmuhr"}void loop () { start:// Label error =0; elapsedReadTime =millis()-startReadTiming; if (elapsedReadTime> (readTimingSeconds*1000)) { int command =readThingSpeak (canalID1); if (Befehl !=9) Pumpe =Befehl; Verzögerung (5000); Befehl =readThingSpeak (canalID2); if (Befehl !=9) Lampe =Befehl; TakeActions(); startReadTiming =millis(); } if (error ==1) // erneut senden, wenn die Übertragung nicht abgeschlossen ist { Serial.println ( " <<<>>>"); Verzögerung (2000); gehe zum Start; // gehe zum Label "start"}}/********* Ergreife Aktionen basierend auf ThingSpeak-Befehlen *************/void takeActions (void) {Serial.print ( "Pumpe:"); Serial.println (Pumpe); Serial.print("Lampe:"); Serial.println (Lampe); if (Pumpe ==1) digitalWrite (ACTUATOR1, LOW); sonst digitalWrite (ACTUATOR1, HIGH); if (Lampe ==1) digitalWrite (ACTUATOR2, LOW); else digitalWrite(ACTUATOR2, HIGH);}/********* Aktuatoren-Befehl von ThingSpeak lesen *************/int readThingSpeak(String channelID){ startThingSpeakCmd(); int-Befehl; // String vorbereiten GET String getStr ="GET /channels/"; getStr +=Kanal-ID; getStr +="/fields/1/last"; getStr +="\r\n"; String messageDown =sendThingSpeakGetCmd(getStr); if (messageDown[5] ==49) {Befehl =messageDown[7]-48; Serial.print ( "Befehl erhalten:"); Serial.println (Befehl); } sonst Befehl =9; Befehl zurückgeben;}/********* ESP zurücksetzen *************/void EspHardwareReset(void){ Serial.println("Reseting......." ); digitalWrite (HARDWARE_RESET, NIEDRIG); Verzögerung (500); digitalWrite (HARDWARE_RESET, HOCH); Verzögerung (8000); // Tempo notwendig für Começar ein ler Serial.println ("RESET"); }/********* Kommunikation mit ThingSpeak starten*************/void startThingSpeakCmd(void){ EspSerial.flush();//limpa o buffer antes de começar a Gravar String cmd ="AT+CIPSTART=\"TCP\",\""; cmd +="184.106.153.149"; // Endereco IP de api.thingspeak.com cmd +="\",80"; EspSerial.println(cmd); Serial.print("enviado ==> Starte cmd:"); Serial.println (cmd); if(EspSerial.find("Fehler")) {Serial.println("AT+CIPSTART Fehler"); Rückkehr; }}/********* sende ein GET cmd an ThingSpeak *************/String sendThingSpeakGetCmd(String getStr){ String cmd ="AT+CIPSEND="; cmd +=String(getStr.length()); EspSerial.println(cmd); Serial.print("enviado ==> Länge cmd:"); Serial.println (cmd); if(EspSerial.find((char *)">")) { EspSerial.print(getStr); Serial.print("enviado ==> getStr:"); Serial.println (getStr); delay(500);//Tempo für Prozessor o GET, semest delay apresenta busy no próximo comando String messageBody =""; while (EspSerial.available()) { String line =EspSerial.readStringUntil('\n'); if (line.length() ==1) { //actual content starts after empty line (that has length 1) messageBody =EspSerial.readStringUntil('\n'); } } Serial.print("MessageBody received:"); Serial.println(messageBody); return messageBody; } else { EspSerial.println("AT+CIPCLOSE"); // alert user Serial.println("ESP8266 CIPSEND ERROR:RESENDING"); //Resend... spare =spare + 1; error=1; return "error"; } } 

The code can be downloaded from my GITHUB:ReadingCommandTS_EXT.ino

Step 14:Sending Commands to Actuators

At this point, we have the actuators channels configured on ThingSpeak and changing the value of Field 1 on each channel, we must see the actuators changing accordingly. On our final project we will do this task, using the Android App, but for testing proposes we can also do it using a normal browser. Let's do it.

The commands are:

Turn ON Pump (RED LED):

https://api.thingspeak.com/update?api_key=ACT1_WRITE_KEY&field1=1 

Turn OFF Pump (RED LED):

https://api.thingspeak.com/update?api_key=ACT1_WRITE_KEY&field1=0 

Turn ON Lamp (GREEN LED):

https://api.thingspeak.com/update?api_key=ACT2_WRITE_KEY&field1=1 

Turn OFF Lamp (GREEN LED):

https://api.thingspeak.com/update?api_key=ACT2_WRITE_KEY&field1=0 

Above you can see a print screen of a command to TurnOn the Pump sent from a browser and how it will appear at Serial Monitor. Obviously, the LED Red and relay will be also be turned on.

Step 15:Completing the Android APP

Let's complete the APP. Previously we have developed a simple App that gets the status from ThingSpeak (READ from Staus Channel). Now we must WRITE on Actuator channels, so those commands could be read by Arduino and act on Pump and Lamp accordingly.

For a user to pass the commands to the App, we will use "buttons". A pair of buttons (ON and OFF) for each one of the Actuators.

When a button is pressed, the color of its text changes.

  • If ON ==> Blue
  • if OFF ==> Red

Above you can see the set of blocks for each one of the pairs of buttons.

Test the App, sending commands to turn ON and OFF the actuators. Check on Serial Monitor, the messages exchanged between ESP-01 and ThingSpeak.

The complete App code can be downloaded from my GITHUB:ArduFarmBot_V1_EXT.aia

Step 16:Putting All Together

Perfekt! At this point, you have a full Android APP, a complete HW but you still do not have a code that will continuously read and write on ThingSpeak. Let's combine all that we have developed previously.

On the final code, you will find additional portions to verify for example if the ESP-01 is not freezing. We will do it, sending an AT command to it before any read or write. As we saw at the very beginning of this tutorial, sending an AT command should return from ESP-01 an OK. If this does not happen, we will proceed with an HW reset, commanded by SW (as we do once during setup phase).

The complete code for our project can be downloaded from my GITHUB:ArduFarmBot_Light_EXT.ino

Step 17:Conclusion

There is a lot to be explored in IoT arena with those great little devices, the Arduino Uno, and the ESP8266-01. We will return soon with new tutorials! Keep following MJRoBot tutorials!

As always, I hope this project can help others find their way in the exciting world of electronics, robotics, and IoT!

Please visit my GitHub for updated files:ArduFarmBot_Light

For more projects, please visit my blog:MJRoBot.org

Saludos from the south of the world!

See you at my next tutorial!

Thank you,

Marcelo

Code

  • Code-Snippet Nr. 1
  • Code snippet #2
  • Code-Snippet #3
  • Code snippet #11
  • Code snippet #12
  • Code snippet #16
  • Code snippet #20
Code-Snippet Nr. 1Nur-Text
// DS18B20#include #include #define ONE_WIRE_BUS 5 // DS18B20 on pin D5 OneWire oneWire(ONE_WIRE_BUS);DallasTemperature DS18B20(&oneWire);int soilTemp =0;//DHT#include "DHT.h"#include int pinoDHT =11;int tipoDHT =DHT22;DHT dht(pinoDHT, tipoDHT); int airTemp =0;int airHum =0;// LDR (Light)#define ldrPIN 1int light =0;// Soil humidity#define soilHumPIN 0int soilHum =0;
Code snippet #2Plain text
void setup(){ Serial.begin(9600); DS18B20.begin(); dht.begin();}void loop(){ readSensors(); displaySensors(); delay (10000);}
Code-Snippet #3Kurztext
/********* Read Sensors value *************/void readSensors(void){ airTemp =dht.readTemperature(); airHum =dht.readHumidity(); DS18B20.requestTemperatures(); soilTemp =DS18B20.getTempCByIndex(0); // Sensor 0 will capture Soil Temp in Celcius soilHum =map(analogRead(soilHumPIN), 1023, 0, 0, 100); light =map(analogRead(ldrPIN), 1023, 0, 0, 100); //LDRDark:0 ==> light 100% }/********* Display Sensors value *************/void displaySensors(void){ Serial.print ("airTemp (oC):"); Serial.println (airTemp); Serial.print ("airHum (%):"); Serial.println (airHum); Serial.print ("soilTemp (oC):"); Serial.println (soilTemp); Serial.print ("soilHum (%):"); Serial.println (soilHum); Serial.print ("light (%):"); Serial.println (light); Serial.println ("");}
Code snippet #11Plain text
#include  SoftwareSerial esp8266(6,7); //Rx ==> Pin 6; TX ==> Pin7 #define speed8266 9600 void setup() { esp8266.begin (speed8266); Serial.begin(speed8266); Serial.println("ESP8266 Setup test - use AT coomands");}void loop() { while(esp8266.available()) { Serial.write(esp8266.read()); } while(Serial.available()) { esp8266.write(Serial.read()); }}
Code snippet #12Plain text
* AT =====> ESP8266 returns OK* AT+RST =====> ESP8266 restart and returns OK* AT+GMR =====> ESP8266 returns AT Version; SDK version; id; OK* AT+CWMODE? => ESP8266 returns mode type* AT+CWLAP ===> ESP8266 returs close access points* AT+CIFSR ===> ESP8266 returs designided IP
Code snippet #16Plain text
// Thingspeak String statusChWriteKey ="6SRPQQKIE6AJVQE6"; // Status Channel id:385184#include SoftwareSerial EspSerial(6, 7); // Rx, Tx#define HARDWARE_RESET 8// DS18B20#include #include #define ONE_WIRE_BUS 5 // DS18B20 on pin D5 OneWire oneWire(ONE_WIRE_BUS);DallasTemperature DS18B20(&oneWire);int soilTemp =0;//DHT#include "DHT.h"#include int pinoDHT =11;int tipoDHT =DHT22;DHT dht(pinoDHT, tipoDHT); int airTemp =0;int airHum =0;// LDR (Light)#define ldrPIN 1int light =0;// Soil humidity#define soilHumPIN 0int soilHum =0;// Variables to be used with timerslong writeTimingSeconds =17; // ==> Define Sample time in seconds to send datalong startWriteTiming =0;long elapsedWriteTime =0;// Variables to be used with Actuatorsboolean pump =0; boolean lamp =0; int spare =0;boolean error;void setup(){ Serial.begin(9600); pinMode(HARDWARE_RESET,OUTPUT); digitalWrite(HARDWARE_RESET, HIGH); DS18B20.begin(); dht.begin(); EspSerial.begin(9600); // Comunicacao com Modulo WiFi EspHardwareReset(); //Reset do Modulo WiFi startWriteTiming =millis(); // starting the "program clock"}void loop(){ start://label error=0; elapsedWriteTime =millis()-startWriteTiming; if (elapsedWriteTime> (writeTimingSeconds*1000)) { readSensors(); writeThingSpeak(); startWriteTiming =millis(); } if (error==1) //Resend if transmission is not completed { Serial.println(" <<<>>>"); delay (2000); goto start; //go to label "start" }}/********* Read Sensors value *************/void readSensors(void){ airTemp =dht.readTemperature(); airHum =dht.readHumidity(); DS18B20.requestTemperatures(); soilTemp =DS18B20.getTempCByIndex(0); // Sensor 0 will capture Soil Temp in Celcius light =map(analogRead(ldrPIN), 1023, 0, 0, 100); //LDRDark:0 ==> light 100% soilHum =map(analogRead(soilHumPIN), 1023, 0, 0, 100); }/********* Conexao com TCP com Thingspeak *******/void writeThingSpeak(void){ startThingSpeakCmd(); // preparacao da string GET String getStr ="GET /update?api_key="; getStr +=statusChWriteKey; getStr +="&field1="; getStr +=String(pump); getStr +="&field2="; getStr +=String(lamp); getStr +="&field3="; getStr +=String(airTemp); getStr +="&field4="; getStr +=String(airHum); getStr +="&field5="; getStr +=String(soilTemp); getStr +="&field6="; getStr +=String(soilHum); getStr +="&field7="; getStr +=String(light); getStr +="&field8="; getStr +=String(spare); getStr +="\r\n\r\n"; sendThingSpeakGetCmd(getStr); }/********* Reset ESP *************/void EspHardwareReset(void){ Serial.println("Reseting......."); digitalWrite(HARDWARE_RESET, LOW); Verzögerung (500); digitalWrite(HARDWARE_RESET, HIGH); delay(8000);//Tempo necessário para começar a ler Serial.println("RESET"); }/********* Start communication with ThingSpeak*************/void startThingSpeakCmd(void){ EspSerial.flush();//limpa o buffer antes de começar a gravar String cmd ="AT+CIPSTART=\"TCP\",\""; cmd +="184.106.153.149"; // Endereco IP de api.thingspeak.com cmd +="\",80"; EspSerial.println(cmd); Serial.print("enviado ==> Start cmd:"); Serial.println(cmd); if(EspSerial.find("Error")) { Serial.println("AT+CIPSTART error"); Rückkehr; }}/********* send a GET cmd to ThingSpeak *************/String sendThingSpeakGetCmd(String getStr){ String cmd ="AT+CIPSEND="; cmd +=String(getStr.length()); EspSerial.println(cmd); Serial.print("enviado ==> lenght cmd:"); Serial.println(cmd); if(EspSerial.find((char *)">")) { EspSerial.print(getStr); Serial.print("enviado ==> getStr:"); Serial.println(getStr); delay(500);//tempo para processar o GET, sem este delay apresenta busy no próximo comando String messageBody =""; while (EspSerial.available()) { String line =EspSerial.readStringUntil('\n'); if (line.length() ==1) { //actual content starts after empty line (that has length 1) messageBody =EspSerial.readStringUntil('\n'); } } Serial.print("MessageBody received:"); Serial.println(messageBody); return messageBody; } else { EspSerial.println("AT+CIPCLOSE"); // alert user Serial.println("ESP8266 CIPSEND ERROR:RESENDING"); //Resend... spare =spare + 1; error=1; return "error"; } }
Code snippet #20Plain text
// Thingspeak String canalID1 ="375598"; //Actuator1String canalID2 ="375599"; //Actuator2#include SoftwareSerial EspSerial(6, 7); // Rx, Tx#define HARDWARE_RESET 8// Variables to be used with timerslong readTimingSeconds =10; // ==> Define Sample time in seconds to receive datalong startReadTiming =0;long elapsedReadTime =0;//Relays#define ACTUATOR1 10 // RED LED ==> Pump#define ACTUATOR2 12 // GREEN LED ==> Lampboolean pump =0; boolean lamp =0; int spare =0;boolean error;void setup(){ Serial.begin(9600); pinMode(ACTUATOR1,OUTPUT); pinMode(ACTUATOR2,OUTPUT); pinMode(HARDWARE_RESET,OUTPUT); digitalWrite(ACTUATOR1, HIGH); //o módulo relé é ativo em LOW digitalWrite(ACTUATOR2, HIGH); //o módulo relé é ativo em LOW digitalWrite(HARDWARE_RESET, HIGH); EspSerial.begin(9600); // Comunicacao com Modulo WiFi EspHardwareReset(); //Reset do Modulo WiFi startReadTiming =millis(); // starting the "program clock"}void loop(){ start://label error=0; elapsedReadTime =millis()-startReadTiming; if (elapsedReadTime> (readTimingSeconds*1000)) { int command =readThingSpeak(canalID1); if (command !=9) pump =command; delay (5000); command =readThingSpeak(canalID2); if (command !=9) lamp =command; takeActions(); startReadTiming =millis(); } if (error==1) //Resend if transmission is not completed { Serial.println(" <<<>>>"); delay (2000); goto start; //go to label "start" }}/********* Take actions based on ThingSpeak Commands *************/void takeActions(void){ Serial.print("Pump:"); Serial.println(pump); Serial.print("Lamp:"); Serial.println(lamp); if (pump ==1) digitalWrite(ACTUATOR1, LOW); else digitalWrite(ACTUATOR1, HIGH); if (lamp ==1) digitalWrite(ACTUATOR2, LOW); else digitalWrite(ACTUATOR2, HIGH);}/********* Read Actuators command from ThingSpeak *************/int readThingSpeak(String channelID){ startThingSpeakCmd(); int command; // preparacao da string GET String getStr ="GET /channels/"; getStr +=channelID; getStr +="/fields/1/last"; getStr +="\r\n"; String messageDown =sendThingSpeakGetCmd(getStr); if (messageDown[5] ==49) { command =messageDown[7]-48; Serial.print("Command received:"); Serial.println(command); } else command =9; return command;}/********* Reset ESP *************/void EspHardwareReset(void){ Serial.println("Reseting......."); digitalWrite(HARDWARE_RESET, LOW); Verzögerung (500); digitalWrite(HARDWARE_RESET, HIGH); delay(8000);//Tempo necessário para começar a ler Serial.println("RESET"); }/********* Start communication with ThingSpeak*************/void startThingSpeakCmd(void){ EspSerial.flush();//limpa o buffer antes de começar a gravar String cmd ="AT+CIPSTART=\"TCP\",\""; cmd +="184.106.153.149"; // Endereco IP de api.thingspeak.com cmd +="\",80"; EspSerial.println(cmd); Serial.print("enviado ==> Start cmd:"); Serial.println(cmd); if(EspSerial.find("Error")) { Serial.println("AT+CIPSTART error"); Rückkehr; }}/********* send a GET cmd to ThingSpeak *************/String sendThingSpeakGetCmd(String getStr){ String cmd ="AT+CIPSEND="; cmd +=String(getStr.length()); EspSerial.println(cmd); Serial.print("enviado ==> lenght cmd:"); Serial.println(cmd); if(EspSerial.find((char *)">")) { EspSerial.print(getStr); Serial.print("enviado ==> getStr:"); Serial.println(getStr); delay(500);//tempo para processar o GET, sem este delay apresenta busy no próximo comando String messageBody =""; while (EspSerial.available()) { String line =EspSerial.readStringUntil('\n'); if (line.length() ==1) { //actual content starts after empty line (that has length 1) messageBody =EspSerial.readStringUntil('\n'); } } Serial.print("MessageBody received:"); Serial.println(messageBody); return messageBody; } else { EspSerial.println("AT+CIPCLOSE"); // alert user Serial.println("ESP8266 CIPSEND ERROR:RESENDING"); //Resend... spare =spare + 1; error=1; return "error"; } }
Github
https://github.com/Mjrovai/ArduFarmBot_Light

Schaltpläne

Electrical diagram
https://github.com/Mjrovai/ArduFarmBot_Light/blob/master/ArduFarmBot_Light/ArduFarmBot%20Light.fzz

Herstellungsprozess

  1. Parallel Computing auf Raspberry Pi 4B+ IoT-Boards leicht gemacht
  2. Herzfrequenzmesser mit IoT
  3. WebServerBlink mit Arduino Uno WiFi
  4. Einfacher UNO-Rechner
  5. Beharrlichkeit der Vision
  6. Kontaktloses Temperaturüberwachungstor
  7. Arduino - Temperatur über eine serielle Schnittstelle an das Web senden
  8. ThingSpeak Arduino Wetterstation
  9. Wie einfach ist es, einen Thermistor zu verwenden?!
  10. Azure IoT Swimming Pool