Mein Weg in das IoT (24): Dinge aus der Ferne steuern mit dem ESP32 Pico Kit
10. April 2018
über
über
In der letzten Folge haben wir mit dem ESP32 Pico Kit, einer RGB-LED und einem Fotowiderstand ein kleines Gerät gebaut, das periodisch Helligkeitswerte erfasst und zur Cloudplattform AllThingsTalk sendet. Dort werden die Sensorwerte auf einer eigenen Webseite angezeigt, die dem Gerät (Device) zugeordnet ist. Die Seite kann der Nutzer selbst konfigurieren und natürlich von überall auf der Welt aus abrufen. Und Sie erinnern sich sicher auch noch daran, dass wir uns um Verbindungsabbrüche und das automatische neue Einloggen in ein WLAN sowie ein erneutes Verbinden mit dem AllThingsTalk-MQTT-Broker gekümmert haben.
In Folge 18 haben wir ein AllThingsMaker-Device namens „MyJourneyIoT_Sensor“ angelegt, mit einem Asset (einer Unterfunktion), die einem Sensor „temperature“ entspricht. Später haben wir dann noch ein Asset für den Sensor „light“ hinzugefügt. Um das Steuern von der Cloud aus zu testen, habe ich für dasselbe Device noch ein Asset „lamp“ angelegt, diesmal aber statt der Variante „Sensor“ nun „Actuator“ angeklickt. Als Typ habe ich „Number“ gewählt, um Zahlenwerte verschicken zu können (siehe Screenshot):
In der Asset-Übersicht stehen nun drei Einträge untereinander:
Nach einem Klick auf die drei Punkte rechts kann man mit dem Menüeintrag „Send Command“ in ein Fenster gelangen, in der man die Payload der zu verschickenden MQTT-Nachricht direkt editieren und diese absenden kann. Sie erinnern sich vielleicht noch von den Sensoren her, dass Zahlenwerte im JSON-Format notiert werden. Ein Kommando zum Setzen des Wertes 15 beim Aktor lautet beispielsweise:
Es gibt aber noch einen komfortableren Weg der Steuerung. Unter dem Hauptmenüpunkt „Pinboards“ kann man sich eine eigene, auf die Anwendung zugeschnittene Kontroll-Oberfläche stricken. Dies macht man, in dem man ein neues Pinboard anlegt und dann auf den Bleistift rechts oben klickt, um dieses editieren zu können. In der Entwurfsansicht kann man über die Schaltfläche „New pin“ nun neue Kontrollelemente anlegen, die Sensoren, aber auch Aktoren zugeordnet werden können. Der Screenshot zeigt, wie ich das für meinen Aktor „lamp“ gemacht habe:
Im nächsten Fenster habe ich festgelegt, dass die Lampe über einen Slider (Schieberegler) gesteuert werden soll. Das fertige Pinboard sieht nun so aus wie im Screenshot:
Bewegt man den Schieberegler an eine neue Stelle, so wird ein Kommando mit dem neuen Wert über MQTT abgesetzt.
Zunächst erweiterte ich meine Funktion ConnectToATT() noch um folgendes Codestück, das ich mir natürlich aus dem Sketch einer früheren Folge herausgezogen und dann abgewandelt habe:
In der Funktion verbindet sich der ESP32 als MQTT-Client nun nicht mehr nur mit dem Broker, sondern abonniert sich auch auf die Nachrichten aus der Cloud, welche die Lampe steuern sollen. In den ersten Zeilen sehen Sie, wie das Topic zusammengesetzt werden muss.
In der Loop-Schleife müssen wir nun freilich noch regelmäßig nachschauen, ob Nachrichten empfangen wurden. Diese Nachrichten erhält der ESP32 als MQTT-Client (beziehungsweise TCP-Client) vom MQTT-Broker (beziehungsweise TCP-Server) über die offene MQTT/TCP-Verbindung.
Mein erster Ansatz war noch nicht optimal. Mit
ließ ich das Programm regelmäßig nachschauen, ob eine MQTT-Nachricht empfangen wurde; in diesem Fall wird die Variable PayloadCountReceivedMessage größer 0 und die Nutzlast der Nachricht landet im Array attMQTTClient.PayloadBuffer (das innerhalb meiner MQTTClient-Library vereinbart ist). Die .Get-Funktion erwies sich aber recht schnell als sehr blockierend. Diese Funktion der MQTTClient-Library griff nämlich auf die .Receive-Funktion der TCPClient-Library zu. Und bei dieser war ein Timeout von 1 Sekunde eingestellt, falls keine Zeichen kommen sollten. Das war praktisch, um beispielsweise auf eine Antwort von einem Webserver zu warten. In diesem Fall weiß man ja, dass auch Zeichen zurückkommen. Falls man aber nur nachschauen möchte, ob etwas da ist, muss man anders vorgehen.
Ich habe die TCPClient-Library daher noch um eine Funktion .ReceiveIfAvailable(…) erweitert, welche die Funktion .available() der Arduino-ESP32-WifiClient-Library nutzt. Diese gibt zurück, wie viele Zeichen über TCP/IP eingegangen sind. Ist nichts angekommen, wird .ReceiveIfAvailable ganz schnell beendet. Nun musste ich nur noch in der MQTT-Lib ändern, dass die .Get-Funktion auf diese Funktion zugreift. Bei dieser Gelegenheit sei erwähnt, dass auch das Pingen noch nicht besonders zeitsparend arbeitet, doch habe ich die Optimierung aus Zeitgründen auf eine spätere Folge verschoben.
Das sah vielversprechend aus, den eigentlichen Sliderwert konnte ich zwischen den Zeichen „:“ und „}“ finden. Im Download unten finden Sie den fertigen Sketch mit einer kleinen Demo: Über den Schieberegler lässt sich der Blau-Wert der regelmäßig heruntergedimmtem RGB-LED fernsteuern. Natürlich sind im Downloadordner auch die geänderten Bibliotheken enthalten, die wie immer im Ordner „libraries“ der Arduino-IDE untergebracht werden müssen.
Ich habe festgestellt, dass fast immer MQTT-Kommandos empfangen werden, wenn ich den Slider bewege; doch 100 % zuverlässig ist das Ganze nicht. In einem realen Steuerungsszenario müsste man also unbedingt noch eine Rückmeldung einbauen, ob die Lampe auch auf den richtigen Wert geschaltet wurde.
Befehle verschicken mit AllThingsTalk
Die Cloud-Plattform ermöglicht es aber auch, von der Device-Webseite Befehle zum Gerät zu schicken, ebenfalls über MQTT. Man kann damit LEDs, Motoren und viele weitere Aktoren steuern.In Folge 18 haben wir ein AllThingsMaker-Device namens „MyJourneyIoT_Sensor“ angelegt, mit einem Asset (einer Unterfunktion), die einem Sensor „temperature“ entspricht. Später haben wir dann noch ein Asset für den Sensor „light“ hinzugefügt. Um das Steuern von der Cloud aus zu testen, habe ich für dasselbe Device noch ein Asset „lamp“ angelegt, diesmal aber statt der Variante „Sensor“ nun „Actuator“ angeklickt. Als Typ habe ich „Number“ gewählt, um Zahlenwerte verschicken zu können (siehe Screenshot):
In der Asset-Übersicht stehen nun drei Einträge untereinander:
Nach einem Klick auf die drei Punkte rechts kann man mit dem Menüeintrag „Send Command“ in ein Fenster gelangen, in der man die Payload der zu verschickenden MQTT-Nachricht direkt editieren und diese absenden kann. Sie erinnern sich vielleicht noch von den Sensoren her, dass Zahlenwerte im JSON-Format notiert werden. Ein Kommando zum Setzen des Wertes 15 beim Aktor lautet beispielsweise:
{
"value": 15
}
"value": 15
}
Es gibt aber noch einen komfortableren Weg der Steuerung. Unter dem Hauptmenüpunkt „Pinboards“ kann man sich eine eigene, auf die Anwendung zugeschnittene Kontroll-Oberfläche stricken. Dies macht man, in dem man ein neues Pinboard anlegt und dann auf den Bleistift rechts oben klickt, um dieses editieren zu können. In der Entwurfsansicht kann man über die Schaltfläche „New pin“ nun neue Kontrollelemente anlegen, die Sensoren, aber auch Aktoren zugeordnet werden können. Der Screenshot zeigt, wie ich das für meinen Aktor „lamp“ gemacht habe:
Im nächsten Fenster habe ich festgelegt, dass die Lampe über einen Slider (Schieberegler) gesteuert werden soll. Das fertige Pinboard sieht nun so aus wie im Screenshot:
Bewegt man den Schieberegler an eine neue Stelle, so wird ein Kommando mit dem neuen Wert über MQTT abgesetzt.
Warten auf die MQTT-Message?
Nun musste ich meinen ESP32 noch dazu bringen, auf eingehende Nachrichten zu lauschen. Ich habe den Sketch aus der letzten Folge als Grundlage benutzt, diesen können Sie hier herunterladen und mit dem neuen Sketch vergleichen, den Sie unten downloaden können.Zunächst erweiterte ich meine Funktion ConnectToATT() noch um folgendes Codestück, das ich mir natürlich aus dem Sketch einer früheren Folge herausgezogen und dann abgewandelt habe:
String ActuatorName = "lamp";
String ATT_ActuatorTopic = "device/" + strDeviceID + "/asset/" + ActuatorName + "/command";
int MQTT_Subscribe_Result = attMQTTClient.Subscribe(ATT_ActuatorTopic); // Attempt to subscribe to topic
if (MQTT_Subscribe_Result == 1) // successful
{
BreadboardRGBLED.SwitchRGBLED(LED_GREEN);
NodeState = NODESTATE_OK;
Serial.println("MQTT subscribed.");
}
else
{
BreadboardRGBLED.SwitchRGBLED(LED_RED);
BreadboardRGBLED.setRGB(255, 0, 0); // set the default color to red for dimming
NodeState = NODESTATE_NOMQTTSUBSCRIPTION;
Serial.println("MQTT not subscribed.");
}
String ATT_ActuatorTopic = "device/" + strDeviceID + "/asset/" + ActuatorName + "/command";
int MQTT_Subscribe_Result = attMQTTClient.Subscribe(ATT_ActuatorTopic); // Attempt to subscribe to topic
if (MQTT_Subscribe_Result == 1) // successful
{
BreadboardRGBLED.SwitchRGBLED(LED_GREEN);
NodeState = NODESTATE_OK;
Serial.println("MQTT subscribed.");
}
else
{
BreadboardRGBLED.SwitchRGBLED(LED_RED);
BreadboardRGBLED.setRGB(255, 0, 0); // set the default color to red for dimming
NodeState = NODESTATE_NOMQTTSUBSCRIPTION;
Serial.println("MQTT not subscribed.");
}
In der Funktion verbindet sich der ESP32 als MQTT-Client nun nicht mehr nur mit dem Broker, sondern abonniert sich auch auf die Nachrichten aus der Cloud, welche die Lampe steuern sollen. In den ersten Zeilen sehen Sie, wie das Topic zusammengesetzt werden muss.
In der Loop-Schleife müssen wir nun freilich noch regelmäßig nachschauen, ob Nachrichten empfangen wurden. Diese Nachrichten erhält der ESP32 als MQTT-Client (beziehungsweise TCP-Client) vom MQTT-Broker (beziehungsweise TCP-Server) über die offene MQTT/TCP-Verbindung.
Mein erster Ansatz war noch nicht optimal. Mit
byte PayloadCountReceivedMessage = attMQTTClient.Get(attMQTTClient.PayloadBuffer);
ließ ich das Programm regelmäßig nachschauen, ob eine MQTT-Nachricht empfangen wurde; in diesem Fall wird die Variable PayloadCountReceivedMessage größer 0 und die Nutzlast der Nachricht landet im Array attMQTTClient.PayloadBuffer (das innerhalb meiner MQTTClient-Library vereinbart ist). Die .Get-Funktion erwies sich aber recht schnell als sehr blockierend. Diese Funktion der MQTTClient-Library griff nämlich auf die .Receive-Funktion der TCPClient-Library zu. Und bei dieser war ein Timeout von 1 Sekunde eingestellt, falls keine Zeichen kommen sollten. Das war praktisch, um beispielsweise auf eine Antwort von einem Webserver zu warten. In diesem Fall weiß man ja, dass auch Zeichen zurückkommen. Falls man aber nur nachschauen möchte, ob etwas da ist, muss man anders vorgehen.
Ich habe die TCPClient-Library daher noch um eine Funktion .ReceiveIfAvailable(…) erweitert, welche die Funktion .available() der Arduino-ESP32-WifiClient-Library nutzt. Diese gibt zurück, wie viele Zeichen über TCP/IP eingegangen sind. Ist nichts angekommen, wird .ReceiveIfAvailable ganz schnell beendet. Nun musste ich nur noch in der MQTT-Lib ändern, dass die .Get-Funktion auf diese Funktion zugreift. Bei dieser Gelegenheit sei erwähnt, dass auch das Pingen noch nicht besonders zeitsparend arbeitet, doch habe ich die Optimierung aus Zeitgründen auf eine spätere Folge verschoben.
ESP32 empfängt Befehle
Jetzt konnte der ESP32 MQTT-Nachrichten empfangen. Zunächst ließ ich mir nur über die serielle Schnittstelle ausgeben, welche Nutzlast beim ESP32 ankam, wenn ich den Slider auf dem Pinboard bewegte. Es waren immer Zeichenketten wie zum Beispiel die folgende:{"at":"2018-04-09 …… ","value":46}
Das sah vielversprechend aus, den eigentlichen Sliderwert konnte ich zwischen den Zeichen „:“ und „}“ finden. Im Download unten finden Sie den fertigen Sketch mit einer kleinen Demo: Über den Schieberegler lässt sich der Blau-Wert der regelmäßig heruntergedimmtem RGB-LED fernsteuern. Natürlich sind im Downloadordner auch die geänderten Bibliotheken enthalten, die wie immer im Ordner „libraries“ der Arduino-IDE untergebracht werden müssen.
Ich habe festgestellt, dass fast immer MQTT-Kommandos empfangen werden, wenn ich den Slider bewege; doch 100 % zuverlässig ist das Ganze nicht. In einem realen Steuerungsszenario müsste man also unbedingt noch eine Rückmeldung einbauen, ob die Lampe auch auf den richtigen Wert geschaltet wurde.
Mehr anzeigen
Weniger anzeigen
Diskussion (0 Kommentare)