MQTT

From Fixme.ch
Jump to: navigation, search

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


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).

Sonoff.png

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);
   */
  }
}