DIY- IoT Projekt: Bauen Sie einen kostengünstigen Smart Planter
Vor ein paar Wochen habe ich mich entschieden, eine Sukkulente für meinen Schreibtisch zu kaufen, einfach wegen der vielen Vorteile, die sie für die Umwelt mit sich bringen. Falls Sie es noch nicht wussten: Sukkulenten und Kakteen haben viele positive Eigenschaften. Sie helfen, die Luft zu reinigen, die Luftfeuchtigkeit zu erhöhen und frischen Sauerstoff zuzuführen
Nachdem Lana (ja, ich habe sie Lana genannt 💕) ein paar Tage auf meinem Schreibtisch stand, merkte ich, dass etwas fehlte. Als leidenschaftliche Bastlerin und IoT Developer Advocate bei Ubidots Smart Planter zu entwickeln . Nach einigen Tagen harter Arbeit und vielen Druckproblemen konnte ich endlich dieses tolle Ergebnis präsentieren und freue mich darauf, euch meinen Prozess zu zeigen 🙆♀️💚:
Dieses Projekt wurde von einem früheren Projekt von Paul a Maker 👨💻 (3D-Druck, Elektronik, Software) , dem ich seit einigen Monaten folge. Ich habe schon viele Tipps und Tricks von ihm gelernt. Wenn ihr Inspiration für eure Elektronikprojekte sucht, solltet ihr ihm unbedingt folgen – glaubt mir!
Ich persönlich lasse mich sehr gerne von Projekten anderer inspirieren. Ich finde aber immer einen Weg, meinen eigenen Stil einzubringen, die Urheber zu nennen und natürlich das Endergebnis zu teilen, damit auch andere sich inspirieren lassen können. 🤓 🤓
Dieser Beitrag enthält eine Schritt-für-Schritt-Anleitung für mein Projekt sowie alle Ressourcen, die zum Nachbau des „ Smart Planter “ benötigt werden.
Anforderungen
- Eine Sukkulente oder ein Kaktus
- NodeMCU ESP8266
- DHT11 Temperatur-/Feuchtigkeitssensor
- Bodenfeuchtesensor
- RGB-Ring
- (5) Weiße LEDs
- 220 Ω Widerstand
- Drähte
- Steckplatine
- rechtwinklige Stecker
- 3D-Drucker
- Lötkolben
- Lötdraht
Schritt für Schritt
- 3D-Druck
- Verdrahtung
- Gehäuse
- Programmierung von NodeMCU ESP8266
- Dashboard – Steuerung & Überwachung
- Zusammenfassung
1. 3D-Druck
Meine Fähigkeiten im 3D-Modellieren sind noch etwas begrenzt (ich arbeite daran, mehr Projekte mit dem 3D-Drucker zu realisieren), aber es gibt eine offene Community namens Thingiverse , die uns das Leben erleichtert. Es ist eine lebendige Design-Community zum Entdecken, Erstellen und Teilen von 3D-druckbaren Objekten. Nachdem ich mir viele Pflanzgefäß-Designs angesehen hatte, entschied ich mich für das folgende:
Klicken Sie hier, um das Design herunterzuladen – thing:2857938
Mir gefiel dieses Design sehr gut, da es sich bereits um einen IoT Pflanzkübel handelte und die Lampe ein schönes und cooles Accessoire war. Im Grunde entsprach dieses Modell perfekt meinen ursprünglichen Vorstellungen:
- Lampe mit Fernbedienung.
- Platz zur Unterbringung von Elektronikkomponenten und zum Hinzufügen einiger Sensoren.
Mir war jedoch klar, dass der Platz für die Elektronik nicht für alle gewünschten Funktionen ausreichte. Um dieses Problem zu lösen, habe ich das Unterteil zweimal gedruckt:
- Weißes Teil : Zur Unterbringung des NodeMCU ESP8266, der Sensoren und der Kabel.
- Transparentes Teil : Zum Platzieren des NeoPixel-Rings.
Das Ergebnis ist folgendes Meisterwerk:
Hinweis: Der NeoPixel-Ring ist für dieses Projekt nicht zwingend erforderlich; er dient lediglich als dekoratives Element. Sie können auch nur ein einzelnes Stück verwenden.
2. Verkabelung
Beachten Sie gemäß der in diesem Leitfaden bereitgestellten Firmware das untenstehende Diagramm und die Tabelle, um eine korrekte Verbindung zwischen den verwendeten Komponenten herzustellen.
3. Gehäuse
Nachdem die korrekten Drahtverbindungen hergestellt sind, platzieren Sie alle Komponenten wie unten abgebildet im Pflanzgefäß:
Wie Sie sehen, habe ich den NodeMCU auf einem Steckbrett mit 10 rechtwinkligen Stiftleisten platziert. Die Idee war, die Verbindung zu den Sensoren herzustellen und gleichzeitig Platz für die Elektronik zu sparen. Zugegeben, die Kabelführung hätte besser sein können. Der Plan war jedoch, eine maßgeschneiderte Platine für den Pflanzkasten zu erstellen, in die später alles integriert werden sollte. Außerdem habe ich das weiße Teil durchbohrt, um die Kabel des NeoPixel-Rings hindurchzuführen. Zum Schluss habe ich den NeoPixel-Ring mit der Öffnung nach unten auf die Unterseite geklebt und anschließend das transparente Teil darauf geklebt, um das Licht zu streuen. Das Endergebnis ist wirklich beeindruckend:
4. Programmierung des NodeMCU ESP8266
ESP8266 nutzen zu können , müssen Sie diese mithilfe des vorkonfigurierten Arduino Board Managers . Falls Sie mit dem Hinzufügen eines Boards in der Arduino IDE nicht vertraut sind, finden Sie in diesem Artikel weitere Informationen .
2. Nachdem die ESP8266-Plattform installiert ist, wählen Sie das ESP8266-Gerät aus, mit dem Sie arbeiten. In diesem Beispiel verwenden wir ein „ NodeMCU 1.0 “. Um Ihr Board in der Arduino IDE auszuwählen, wählen Sie „Werkzeuge“ > „Board“ > „Generisches ESP8266-Modul“ .
Ubidots MQTTESP8266 herunter und installieren Sie sie . Eine detaillierte Anleitung zur Installation von Bibliotheken mit der Arduino IDE finden Sie in diesem Leitfaden .
4. Fügen Sie den unten stehenden Code in die Arduino IDE ein. Weisen Sie anschließend Ihr eindeutiges Ubidots Token , die SSID (WLAN-Name ) und das Passwort des verfügbaren Netzwerks zu.
Hinweis: Denken Sie daran, die richtigen Pins für die verwendeten Komponenten zuzuweisen, falls Sie eine andere Verbindung als die oben angegebene verwenden.
/* RGB Smart Planter integriert mit Ubidots für Überwachung und Steuerung. Dieser Code funktioniert für: 1) Auslesen von zwei Sensoren: DHT11 und Bodenfeuchtigkeit. 2) Veröffentlichen der Sensormesswerte an Ubidots. 3) Abonnieren mehrerer Variablen für die Fernsteuerung. Benötigte Bibliotheken: - Ubidots ESP8266 MQTT - (ubidots) -ubidotsNeoPixel - (https://github.com/adafruit/Adafruit_NeoPixel) - DHT - (https://github.com/adafruit/DHT-sensor-library) Erstellt von: Maria Hernández - IoT Developer Advocate @ Ubidots Überarbeitung: José García - Development & Support Manager @ Ubidots /**************************************** * Einbinden von Bibliotheken ****************************************/ #include<Adafruit_NeoPixel.h> #enthalten<stdio.h> #enthalten<map> #include "DHT.h" #include "UbidotsESPMQTT.h" /**************************************** * Pins definieren ****************************************/ #define LIGHTPIN D1 // Digitaler Pin für LED-Lampe. #define DHTPIN D5 // Digitaler Pin für DHT-Sensor. #define NEOPIXELSPIN D6 // Digitaler Pin für NeoPixel-Ring. #define MOISTUREPIN A0 // Analoger Pin für Feuchtigkeitssensor. /**************************************** * Konstanten definieren ****************************************/ #define TOKEN "BBFF-xxxxxxxxxx" // Ubidots -Token zuweisen. #define WIFINAME "xxxxxxxxxx" // SSID zuweisen. #define WIFIPASS "xxxxxxxxxx" // WLAN-Passwort zuweisen. #define DEVICE "planter" // Ubidots -Gerätebezeichnung. #define VAR_PUB_1 "temperature" // Ubidots -Variablenbezeichnung für die Veröffentlichung von Daten. #define VAR_PUB_2 "humidity" #define VAR_PUB_3 "soil-moisture" #define VAR_PUB_4 "heat-index" #define VAR_SUB_1 "light-1" // Ubidots -Variablenbezeichnung für das Abonnieren von Daten; \ // Diese Variablen müssen in Ubidotserstellt werden. #define VAR_SUB_2 "light-2" #define NUMPIXELS 12 // 12-Bit-NeoPixel-Ring // Entfernen Sie die Kommentarzeichen vor dem jeweiligen Typ, den Sie verwenden #define DHTTYPE DHT11 // DHT 11 //#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 //#define DHTTYPE DHT21 // DHT 21 (AM2301) typedef enum { red, green, blue, yellow, white, black } NeoPixelColor; // R, G, B uint8_t myColors[][6] = {{250, 0, 0}, // Rot. {0, 255, 0}, // Grün. {0, 0, 255}, // Blau. {255, 255, 0}, // Gelb. {255, 255, 255}, // Weiß. {0, 0, 0}}; // Schwarz. const uint8_t numberOfVariables = 2; // Anzahl der Variablen für das Abonnement. char *variableLabels[numberOfVariables] = {VAR_SUB_1, VAR_SUB_2}; // Variablenbezeichnungen für das Abonnement. float value; // Speichert den eingehenden Wert. int lastValue; bool bottomLight; // Flag zur Steuerung der Bedingungen für die untere Beleuchtung. unsigned long initTime; // Speichert die Initialisierungszeit. const long SECONDS_TO_RECONNECT = 180000; // Zeitraum für die Wiederherstellung der MQTT-Verbindung. // Vergleichsfunktor für Map-Funktionen. struct cmp_str { bool operator()(char const *a, char const *b) const { return strcmp(a, b) < 0; } }; // Deklaration der Map-Funktion. typedef std::function<void()> Funktionstyp; typedef std::map<const char *, FunctionType, cmp_str> mapTopicSubscription; /**************************************** * Instanzen definieren ****************************************/ Ubidots client(TOKEN); Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXELSPIN, NEO_GRB + NEO_KHZ800); DHT dht(DHTPIN, DHTTYPE); mapTopicSubscription ubiSubTopic; /**************************************** * Hauptfunktionen ****************************************/ void setup() { initTime = millis(); // Initialisierungszeit speichern Serial.begin(115200); pinMode(LIGHTPIN, OUTPUT); // Pin-Modus deklarieren // Definiert die zugeordneten Funktionen zur Behandlung des Abonnementereignisses. ubiSubTopic[VAR_SUB_1] = &subscriptionHandler1; ubiSubTopic[VAR_SUB_2] = &subscriptionHandler2; client.ubidotsSetBroker("ubidots"); // Legt den Broker für das // Geschäftskonto korrekt fest. client.setDebug(true); // Übergibt einen booleschen Wert (true oder false), um Debug-Meldungen zu aktivieren. client.wifiConnection(WIFINAME, WIFIPASS); // Stellt eine WLAN-Verbindung her. client.begin(callback); dht.begin(); // Initialisiert den DHT-Sensor. pixels.begin(); // Initialisiert den NeoPixel-Ring. pixels.clear(); // Setzt alle Pixelfarben auf 'aus'. // Stellt ein Abonnement mit den definierten Variablen her.ubidotsSubscribe(DEVICE, VAR_SUB_1);ubidotsSubscribe(DEVICE, VAR_SUB_2); } void loop() { // Stellt das Abonnement mit den definierten Variablen wieder her, wenn die Verbindung unterbrochen wird oder alle 3 Minuten. if (!client.connected() || abs(millis() - initTime) > SECONDS_TO_RECONNECT) { initTime = millis(); client.reconnect();ubidotsSubscribe(DEVICE, VAR_SUB_1);ubidotsSubscribe(DEVICE, VAR_SUB_2); } client.reconnect(); // Temperatur-, Luftfeuchtigkeits- und Bodenfeuchtewerte lesen. float humidity = dht.readHumidity(); float temperature = dht.readTemperature(); int soilMoisture = analogRead(MOISTUREPIN); // Hitzeindex in Celsius berechnen (isFahreheit = false). float heatIndexC = dht.computeHeatIndex(temperature, humidity, false); // Prüfen, ob Lesefehler aufgetreten sind, und gegebenenfalls vorzeitig beenden (um es erneut zu versuchen). if (isnan(humidity) || isnan(temperature)) { Serial.println(F("Fehler beim Lesen vom DHT-Sensor!")); } // Steuert die Farben des NeoPixels basierend auf den Temperaturwerten. if (bottomLight) { if (inRange(temperature, 0, 16)) colorWipe(blue, 50); if (inRange(temperature, 16, 21)) colorWipe(green, 50); if (inRange(temperature, 21, 26)) colorWipe(yellow, 50); if (inRange(temperature, 26, 40)) colorWipe(red, 50); } // Fügt Variablen hinzu, die in Ubidotsveröffentlicht werden sollen. client.add(VAR_PUB_1, temperature); client.add(VAR_PUB_2, humidity); client.add(VAR_PUB_3, soilMoisture); client.add(VAR_PUB_4, heatIndexC); // Veröffentlicht alle hinzugefügten Variablen für das definierte Gerät.ubidotsPublish(DEVICE); client.loop(); delay(1000); } /**************************************** * Abonnementfunktionen ****************************************/ // Funktion, die ausgeführt wird, wenn sich der Status von var_sub_1 ändert. void subscriptionHandler1() { if (value == 1) { Serial.println("Pflanzenlampe eingeschaltet."); digitalWrite(LIGHTPIN, HIGH); } else { Serial.println("Pflanzenlampe ausgeschaltet."); digitalWrite(LIGHTPIN, LOW); } }; // Funktion, die ausgeführt wird, wenn sich der Status von var_sub_2 ändert. void subscriptionHandler2() { if (value != lastValue) { if (value == 1) { Serial.println("Unterlicht des Pflanzgefäßes eingeschaltet."); for (int i = 0; i < 3; i++) { colorWipe(red, 50); colorWipe(green, 50); colorWipe(blue, 50); }; colorWipe(white, 200); bottomLight = true; } else { Serial.println("Die untere Beleuchtung des Pflanzgefäßes ist ausgeschaltet."); colorWipe(white, 50); colorWipe(black, 200); bottomLight = false; } } lastValue = value; }; /**************************************** * Hilfsfunktionen ****************************************/ // Gibt einen Integer mit der Länge eines Zeichens zurück int strLen(char *s) { int l = 0; while (*s != '\0') { s++; l++; } return (l); } // Callback zur Behandlung von Abonnements void callback(char *topic, byte *payload, unsigned int length) { char *variableLabel = (char *)malloc(sizeof(char) * 30); getVariableLabelTopic(topic, variableLabel); // Speichert die Variablenbezeichnung. value = btof(payload, length); // Speichert den Wert der abonnierten Variable. executeCases(variableLabel); // Führt den Funktionshandler für die // abonnierte Variable aus. free(variableLabel); // Speicher freigeben. } // Das empfangene Topic parsen, um die Variablenbezeichnung zu extrahieren. void getVariableLabelTopic(char *topic, char *variableLabel) { sprintf(variableLabel, ""); for (int i = 0; i < numberOfVariables; i++) { char *resultLv = strstr(topic, variableLabels[i]); if (resultLv != NULL) { uint8_t len = strlen(resultLv); char result[100]; uint8_t i = 0; for (i = 0; i < len - 3; i++) { result[i] = resultLv[i]; } result[i] = '\0'; snprintf(variableLabel, strLen(result) + 1, "%s", result); break; } } } // Umwandlung eines Zeichenarrays in einen Gleitkommawert. float btof(byte *payload, unsigned int length) { char *demo_ = (char *)malloc(sizeof(char) * 10); for (int i = 0; i < length; i++) { demo_[i] = payload[i]; } return atof(demo_); } // Führt die entsprechende "Subscription Function" basierend auf dem empfangenen Wert aus. void executeCases(char *variableLabel) { if (ubiSubTopic.find(variableLabel) != ubiSubTopic.end()) { mapTopicSubscription::iterator i = ubiSubTopic.find(variableLabel); (i->second)(); } } // Füllt die NeoPixel-Ringpixel nacheinander mit Farbe. void colorWipe(NeoPixelColor color, int wait) { int r, g, b; r = myColors[color][0]; g = myColors[color][1]; b = myColors[color][2]; for (int i = 0; i < pixels.numPixels(); i++) { pixels.setPixelColor(i, r, g, b); pixels.show(); delay(wait); } } // Überprüft, ob der empfangene Wert im erwarteten Bereich liegt bool inRange(float x, int low, int high) { return ((x - low) > 0 && (high - x) >= 0); }
5. Überprüfen Sie Ihren Code in der Arduino IDE. Klicken Sie dazu in der oberen linken Ecke der Arduino IDE auf das Häkchen- Symbol, um Ihren Code zu überprüfen.
6. Laden Sie den Code in Ihren „NodeMCU 1.0“ . Wählen Sie dazu das Symbol mit dem „ Pfeil nach rechts“ neben dem Symbol mit dem „ Häkchen “.
7. Um die Konnektivität des Geräts und die gesendeten Daten zu überprüfen, öffnen Sie den seriellen Monitor , indem Sie das Lupensymbol in der oberen rechten Ecke der Arduino IDE auswählen, um die Verbindungsprotokolle logs die Antworten des Ubidots -Servers anzuzeigen.
8. Nach wenigen Sekunden Initialisierung erstellt der bereitgestellte Code automatisch ein neues Gerät in Ihrem Ubidots Ubidots Konto zum Gerätebereich ; dort sollten die automatisch erstellten Geräte angezeigt werden.
Geben Sie das Gerät „ Planter “ ein und sehen Sie sich die konfigurierten Variablen an, die die Datenübertragung steuern:
9. Erstellen Sie anschließend zwei neue Rohvariablen, um das Abonnement einzurichten und die Lampen fernzusteuern. Klicken Sie dazu auf „Variable hinzufügen > Rohvariable “ und weisen Sie die Variablenbezeichnung aus dem Code zu. Im bereitgestellten Beispielcode lauten die Variablenbezeichnungen „light-1“ und „light-2“ .
Nachdem die Variablen erfolgreich erstellt wurden, sollte folgendes Ergebnis angezeigt werden:
5. Dashboard – Steuerung & Überwachung
Nachdem nun alles integriert ist, können die Gerätedaten in einem Dashboard . Über das dashboard lässt sich außerdem der Status beider Lampen überwachen.
1. Um Ihr erstes dashboard , gehen Sie zum Dashboard auf den Tab (Daten → Dashboards ) . Wählen Sie dann das Pluszeichen (+) oben rechts und anschließend den gewünschten Widget-Typ aus. Sie sollten nun dashboards wie das untenstehende erstellen können:
Mehr zu Ubidots Dashboards : Anwendungsbranding: Benutzerdefinierte Stile für Ihre dashboards und Widgets
Ubidots ist sehr intuitiv zu bedienen und ermöglicht es jedem Benutzer, seine Daten nach Belieben anzupassen. Falls Sie mehr erfahren möchten, empfehle ich Ihnen die folgenden Anleitungen:
- Ubidots Grundlagen: Geräte, Variablen, Dashboardsund Benachrichtigungen
- Ubidots -Grundlagen: Anwendungen, Organisationen und Benutzer erklärt
2. Ubidots unterstützt bereits integrierte Ereignisse, mit denen Sie Ereignisse, Warnungen und Benachrichtigungen auf Basis Ihrer Sensor- und Aktordaten senden können.
- E-Mail-Benachrichtigungen
- SMS-Benachrichtigungen
- Webhook-Ereignisse – mehr erfahren
- Telegram-Benachrichtigungen
- Slack-Benachrichtigungen – mehr erfahren
- Sprachanrufbenachrichtigungen – mehr erfahren
- Benachrichtigung „Zurück zum Normalzustand“ – mehr erfahren
- Geofencing-Benachrichtigungen – mehr erfahren
6. Zusammenfassung
Nach ein paar Stunden Basteln und Programmieren (und vielen Tassen Kaffee) habe ich diesem intelligenten Pflanzgefäß Leben eingehaucht:
Wie ich eingangs erwähnte, habe ich das nicht nur aus dem Wunsch nach einer neuen Schreibtischdekoration entwickelt, sondern auch, weil ich das Gefühl hatte, dass Lana etwas fehlte. Ich wollte eine eigene Möglichkeit finden, über IoT und Daten mit ihr zu kommunizieren. Falls ihr nach anderen Möglichkeiten sucht, mit euren Pflanzen zu kommunizieren, könnt ihr sie mit dem MIDI Sprout . Ist das nicht cool? 🤩
Dies ist lediglich die V0 des Smart Planter, und es gibt einige Dinge, die ich verbessern möchte, wie zum Beispiel das Design, die Elektronikorganisation mit einer kundenspezifischen Leiterplatte und die Integration mit anderen Diensten wie Google Assistant, Alexa und Twitter.
Ich hoffe, es hat euch genauso viel Spaß gemacht wie mir. Falls ihr Anmerkungen oder Fragen zum Projekt habt, meldet euch einfach! Konstruktives Feedback ist mir sehr wichtig, um solche Projekte weiter zu verbessern und sie mit euch zu teilen. 😁💚
Weitere nützliche IoT Projekte: