Difference between revisions of "MQTT"
m (→Publier) |
(Ajout second exemple PubSubClient) |
||
(3 intermediate revisions by the same user not shown) | |||
Line 23: | Line 23: | ||
Les PIN exposées du dispositif permettent de le reflasher pour commander le relais en souscrivant à un service n/off. (une pin-GPIO14) reste disponible pour y connecter un capteur et donc publier cette information. | Les PIN exposées du dispositif permettent de le reflasher pour commander le relais en souscrivant à un service n/off. (une pin-GPIO14) reste disponible pour y connecter un capteur et donc publier cette information. | ||
+ | === Raccordement pour flashage === | ||
+ | Pour le raccorder, il faut un petit dispositif UART<->USB celui du FabLab (permet de travailler en 3.3v ce qui est pratique pour cet usage. | ||
+ | Pour le flasher il faut appuyer sur le long bouton reset avant la mise sous tension (par l'USB). | ||
+ | |||
+ | [[File:Sonoff.png | 400 px]] | ||
=== Le code en service === | === Le code en service === | ||
+ | J'ai utilisé la librairie d'Adafruit que j'ai testée aussi en SSL/TLS, mais je vais essayer PubSubClient de Nick O'Leary qui a bonne réputation. | ||
<nowiki> | <nowiki> | ||
/*************************************************** | /*************************************************** | ||
Line 184: | Line 190: | ||
} | } | ||
Serial.println("MQTT Connected!"); | Serial.println("MQTT Connected!"); | ||
+ | } | ||
+ | </nowiki> | ||
+ | === Le code avec PubSubClient === | ||
+ | <nowiki> | ||
+ | /* | ||
+ | Basic ESP8266 MQTT example | ||
+ | |||
+ | This sketch demonstrates the capabilities of the pubsub library in combination | ||
+ | with the ESP8266 board/library. | ||
+ | |||
+ | It connects to an MQTT server then: | ||
+ | - publishes "hello world" to the topic "outTopic" every two seconds | ||
+ | - subscribes to the topic "inTopic", printing out any messages | ||
+ | it receives. NB - it assumes the received payloads are strings not binary | ||
+ | - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led, | ||
+ | else switch it off | ||
+ | |||
+ | It will reconnect to the server if the connection is lost using a blocking | ||
+ | reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to | ||
+ | achieve the same result without blocking the main loop. | ||
+ | |||
+ | To install the ESP8266 board, (using Arduino 1.6.4+): | ||
+ | - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": | ||
+ | http://arduino.esp8266.com/stable/package_esp8266com_index.json | ||
+ | - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" | ||
+ | - Select your ESP8266 in "Tools -> Board" | ||
+ | |||
+ | */ | ||
+ | |||
+ | #include <WiFi.h> | ||
+ | #include <PubSubClient.h> | ||
+ | |||
+ | // Update these with values suitable for your network. | ||
+ | |||
+ | const char* ssid = "FIXME-NAT"; | ||
+ | const char* password = "XXXXXXXXXXX"; | ||
+ | const char* mqtt_server = "62.220.135.221"; | ||
+ | const char* broker_user = "fixme" | ||
+ | const char* broker_passwd = "YYYYYYYYYYY" | ||
+ | const char* client_key_uniq = "Achanger1234" // Doit différer d'un client à l'autre | ||
+ | |||
+ | WiFiClient espClient; | ||
+ | PubSubClient client(espClient); | ||
+ | long lastMsg = 0; | ||
+ | char msg[50]; | ||
+ | int value = 0; | ||
+ | |||
+ | void setup() { | ||
+ | pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output | ||
+ | Serial.begin(115200); | ||
+ | setup_wifi(); | ||
+ | client.setServer(mqtt_server, 1883); | ||
+ | client.setCallback(callback); | ||
+ | } | ||
+ | |||
+ | void setup_wifi() { | ||
+ | |||
+ | delay(10); | ||
+ | // We start by connecting to a WiFi network | ||
+ | Serial.println(); | ||
+ | Serial.print("Connecting to "); | ||
+ | Serial.println(ssid); | ||
+ | |||
+ | WiFi.begin(ssid, password); | ||
+ | |||
+ | while (WiFi.status() != WL_CONNECTED) { | ||
+ | delay(500); | ||
+ | Serial.print("."); | ||
+ | } | ||
+ | |||
+ | Serial.println(""); | ||
+ | Serial.println("WiFi connected"); | ||
+ | Serial.println("IP address: "); | ||
+ | Serial.println(WiFi.localIP()); | ||
+ | } | ||
+ | |||
+ | void callback(char* topic, byte* payload, unsigned int length) { | ||
+ | Serial.print("Message arrived ["); | ||
+ | Serial.print(topic); | ||
+ | Serial.print("] "); | ||
+ | for (int i = 0; i < length; i++) { | ||
+ | Serial.print((char)payload[i]); | ||
+ | } | ||
+ | Serial.println(); | ||
+ | |||
+ | // Switch on the LED if an 1 was received as first character | ||
+ | if ((char)payload[0] == '1') { | ||
+ | digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level | ||
+ | // but actually the LED is on; this is because | ||
+ | // it is acive low on the ESP-01) | ||
+ | } else { | ||
+ | digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | void reconnect() { | ||
+ | // Loop until we're reconnected | ||
+ | while (!client.connected()) { | ||
+ | Serial.print("Attempting MQTT connection..."); | ||
+ | // Attempt to connect | ||
+ | if (client.connect(client_key_uniq,broker_user,broker_passwd)) { | ||
+ | Serial.println("connected"); | ||
+ | // Once connected, publish an announcement... | ||
+ | //client.publish("outTopic", "hello world"); | ||
+ | // ... and resubscribe | ||
+ | client.subscribe("inTopic"); | ||
+ | } else { | ||
+ | Serial.print("failed, rc="); | ||
+ | Serial.print(client.state()); | ||
+ | Serial.println(" try again in 5 seconds"); | ||
+ | // Wait 5 seconds before retrying | ||
+ | delay(5000); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | void loop() { | ||
+ | |||
+ | if (!client.connected()) { | ||
+ | reconnect(); | ||
+ | } | ||
+ | client.loop(); | ||
+ | /* | ||
+ | long now = millis(); | ||
+ | if (now - lastMsg > 2000) { | ||
+ | lastMsg = now; | ||
+ | ++value; | ||
+ | snprintf (msg, 75, "hello world #%ld", value); | ||
+ | Serial.print("Publish message: "); | ||
+ | Serial.println(msg); | ||
+ | // client.publish("outTopic", msg); | ||
+ | */ | ||
+ | } | ||
} | } | ||
</nowiki> | </nowiki> |
Latest revision as of 22:20, 29 October 2018
We're running mosquitto as MQTT broker running on Bellatrix and here's how to use it:
- Host: 62.220.135.221
- Port: 1883
- User: fixme
- Password: ask User:Francois
- No guest access allowed
Contents
MQTT un protocole fabuleux
MQTT est un protocole né à la fin des années 1990 permettant de transmettre des messages entre différents nœuds d'un réseau TCP/IP. Les objets situés sur les nœuds souscrivent à des services pour recevoir des données et/ou se déclarent pour en publier. Un broker situé à un point du réseau met en relation les souscripteurs (consommateurs de données) aux publieurs (fournisseurs de données).
Ce protocole simple et robuste était utilisé à l'origine pour gérer un grand nombre de capteurs/actionneurs sur des pipelines... Il est toujours utilisé en informatique industriel plus simple à mettre en œuvre et à déployer que les WebSockets.
Souscrire
Notre sonoff va s'instancier sur le réseau en contactant le broker dont nous lui avons donné l'adresse IP en souscrivant au service (topic) /sonoff. Quand un objet publiera une donnée 1 sur le topic /sonoff la lampe s'allumera et s'étendra à la réception d'un 0
Publier
Sur un smartphone Android -par exemple - on pourra charger l'application MQTT Dash, se raccorder au Broker mis en place par François et créer un service de bouton on/off qui publiera sur le topic /sonoff et enverra des ordres d'allumage (1) ou d'extinction (0). Cette application permet de publier et de souscrire à tout type de services.
Un exemple d'utilisation avec un SonOff en commande d'éclairage
Les Sonoff sont de petits interrupteurs construits autour d'un ESP8266 et d'un relais 10A. On peut facilement hacker ces objets qui ne coutent que 6 CHF pour les insérer dans notre architecture selon nos besoins. Les PIN exposées du dispositif permettent de le reflasher pour commander le relais en souscrivant à un service n/off. (une pin-GPIO14) reste disponible pour y connecter un capteur et donc publier cette information.
Raccordement pour flashage
Pour le raccorder, il faut un petit dispositif UART<->USB celui du FabLab (permet de travailler en 3.3v ce qui est pratique pour cet usage. Pour le flasher il faut appuyer sur le long bouton reset avant la mise sous tension (par l'USB).
Le code en service
J'ai utilisé la librairie d'Adafruit que j'ai testée aussi en SSL/TLS, mais je vais essayer PubSubClient de Nick O'Leary qui a bonne réputation.
/*************************************************** Adafruit MQTT Library ESP8266 Example Must use ESP8266 Arduino from: https://github.com/esp8266/Arduino Works great with Adafruit's Huzzah ESP board & Feather ----> https://www.adafruit.com/product/2471 ----> https://www.adafruit.com/products/2821 Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Tony DiCola for Adafruit Industries. MIT license, all text above must be included in any redistribution ****************************************************/ #include <ESP8266WiFi.h> //#include <WiFi.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" /************************* WiFi Access Point *********************************/ #define WLAN_SSID "FIXME-NAT" #define WLAN_PASS "xxxxxxxxx" /************************* Adafruit.io Setup *********************************/ #define AIO_SERVER "62.220.135.221" #define AIO_SERVERPORT 1883 // use 8883 for SSL #define AIO_USERNAME "fixme" #define AIO_KEY "yyyyyyyyyy" #define LED_PIN 13 #define RELAY_PIN 12 /************ Global State (you don't need to change this!) ******************/ // Create an ESP8266 WiFiClient class to connect to the MQTT server. WiFiClient client; // or... use WiFiFlientSecure for SSL //WiFiClientSecure client; // Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); //Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT); /****************************** Feeds ***************************************/ // Setup a feed called 'photocell' for publishing. // Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname> //Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell"); //Adafruit_MQTT_Publish halleffect = Adafruit_MQTT_Publish(&mqtt, "/effethall"); //Adafruit_MQTT_Publish lipocharge = Adafruit_MQTT_Publish(&mqtt, "/chargelipo"); // Setup a feed called 'onoff' for subscribing to changes. Adafruit_MQTT_Subscribe sonoff = Adafruit_MQTT_Subscribe(&mqtt, "/sonoff"); /*************************** Sketch Code ************************************/ //Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, "/onoffbutton"); // Bug workaround for Arduino 1.6.6, it seems to need a function declaration // for some reason (only affects ESP8266, likely an arduino-builder bug). void MQTT_connect(); void setup() { Serial.begin(115200); delay(10); pinMode(LED_PIN,OUTPUT); pinMode(RELAY_PIN,OUTPUT); Serial.println(F("Adafruit MQTT Sonoff demo")); //ledcSetup(1, 50, TIMER_WIDTH); // channel 1, 50 Hz, 16-bit width //ledcAttachPin(21, 1); // GPIO 22 assigned to channel 1 // Connect to WiFi access point. Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(WLAN_SSID); WiFi.begin(WLAN_SSID, WLAN_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); // Setup MQTT subscription for onoff feed. mqtt.subscribe(&sonoff); } void loop() { // Ensure the connection to the MQTT server is alive (this will make the first // connection and automatically reconnect when disconnected). See the MQTT_connect // function definition further below. MQTT_connect(); // this is our 'wait for incoming subscription packets' busy subloop // try to spend your time here Adafruit_MQTT_Subscribe *subscription; while ((subscription = mqtt.readSubscription(1000))) { if (subscription == &sonoff) { Serial.print(F("Got: ")); String A = (char *)sonoff.lastread ; Serial.println(A); if (A == "1") { digitalWrite(RELAY_PIN, 255); digitalWrite(LED_PIN, 0); } if(A == "0") { digitalWrite(RELAY_PIN, 0); digitalWrite(LED_PIN, 255); } } } // ping the server to keep the mqtt connection alive // NOT required if you are publishing once every KEEPALIVE seconds /* if(! mqtt.ping()) { mqtt.disconnect(); } */ } // 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... "); uint8_t retries = 3; 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 retries--; if (retries == 0) { // basically die and wait for WDT to reset me while (1); } } Serial.println("MQTT Connected!"); }
Le code avec PubSubClient
/* Basic ESP8266 MQTT example This sketch demonstrates the capabilities of the pubsub library in combination with the ESP8266 board/library. It connects to an MQTT server then: - publishes "hello world" to the topic "outTopic" every two seconds - subscribes to the topic "inTopic", printing out any messages it receives. NB - it assumes the received payloads are strings not binary - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led, else switch it off It will reconnect to the server if the connection is lost using a blocking reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to achieve the same result without blocking the main loop. To install the ESP8266 board, (using Arduino 1.6.4+): - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": http://arduino.esp8266.com/stable/package_esp8266com_index.json - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" - Select your ESP8266 in "Tools -> Board" */ #include <WiFi.h> #include <PubSubClient.h> // Update these with values suitable for your network. const char* ssid = "FIXME-NAT"; const char* password = "XXXXXXXXXXX"; const char* mqtt_server = "62.220.135.221"; const char* broker_user = "fixme" const char* broker_passwd = "YYYYYYYYYYY" const char* client_key_uniq = "Achanger1234" // Doit différer d'un client à l'autre WiFiClient espClient; PubSubClient client(espClient); long lastMsg = 0; char msg[50]; int value = 0; void setup() { pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); } void setup_wifi() { delay(10); // We start by connecting to a WiFi network Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); // Switch on the LED if an 1 was received as first character if ((char)payload[0] == '1') { digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level // but actually the LED is on; this is because // it is acive low on the ESP-01) } else { digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH } } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect(client_key_uniq,broker_user,broker_passwd)) { Serial.println("connected"); // Once connected, publish an announcement... //client.publish("outTopic", "hello world"); // ... and resubscribe client.subscribe("inTopic"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void loop() { if (!client.connected()) { reconnect(); } client.loop(); /* long now = millis(); if (now - lastMsg > 2000) { lastMsg = now; ++value; snprintf (msg, 75, "hello world #%ld", value); Serial.print("Publish message: "); Serial.println(msg); // client.publish("outTopic", msg); */ } }