Contrôler les modules Yoctopuce depuis MQTT

Contrôler les modules Yoctopuce depuis MQTT

Les YoctoHubs sont capables de publier automatiquement les valeurs de senseurs sur un Broker MQTT, nous avons du reste fait un article sur ce sujet. Cependant, pour des raisons de scalabilité et de sécurité, les YoctoHubs ne peuvent pas contrôler les modules connectés en fonction des messages MQTT. Il y a pourtant des situations où on désire intégrer des modules de commande Yoctopuce dans une architecture existante MQTT. Dans ce cas, il suffit d'utiliser une passerelle entre le protocole MQTT et les modules Yoctopuce. C'est ce que nous allons faire cette semaine.

Pour représenter une infrastructure MQTT existante, nous avons utilisé un Rapberry Pi 3 sur lequel nous avons installé le broker MQTT mosquitto. Pour envoyer des message MQTT, nous avons utilisé IoT MQTT Dashboard qui est installé sur un téléphone Android. A défaut d'être réaliste, cette configuration est très facile à installer et permet d'illustrer le rôle de chaque composant.

L'objectif est d'ajouter à cette architecture un capteur de température, un relais qui contrôle un ventilateur et deux LED de signalisation. Pour cela, nous allons utiliser un Yocto-Temperature, un Yocto-PowerRelay et un Yocto-Color-V2, le tout branché sur un YoctoHub-Ethernet.

L'architecture de notre système
L'architecture de notre système



On veut publier la température ambiante sur MQTT sous le topic "temperature". Le ventilateur doit souscrire au topic "fan_control" qui peut prendre les valeurs "on" ou "off". Quant aux LEDs, elles doivent sourcrire au topic "leds_control", et la valeur passée est la valeur RGB au format #000000.

Une passerelle MQTT en Java


Nous allons écrire notre passerelle en Java car cela permet à fois d'être utilisé comme une application et comme un callback WebSocket.

L'application Java doit transmettre au broker la température mesurée par le Yocto-Temperature, mais aussi écouter les topics "fan_control" et "leds_control" et envoyer les bonnes commandes au Yocto-Relay et au Yocto-Color-V2. Cette passerelle Java doit uniquement "traduire" les messages MQTT en commandes Yoctopuce. Le contrôle du système et l'affichage de la température sont gérés par l'application Android.

Pour commencer, il faut ajouter la librairie Java Paho à notre projet. Cette librairie étant disponible sous Maven, il suffit d'ajouter le code suivant au fichier pom.xml.

<dependency>
  <groupId>org.eclipse.paho</groupId>
  <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
  <version>1.1.0</version>
</dependency>



Il faut aussi ajouter la librairie Yoctopuce comme décrit dans ce précédent article.

Etablir les connnexions



Pour commencer, il faut établir la connexion avec le YoctoHub:

private YAPIContext _yctx;
private String _broker_url;
private MqttClient _sampleClient;
private YRelay _yrelay;
private ArrayList&gt;YColorLed&lt; _yleds = new ArrayList&gt;&lt;();


  // Constructor used for traditional application
  public MQTTBridge(String hub_url, String broker_url) throws YAPI_Exception
  {
    _yctx = new YAPIContext();
    _yctx.RegisterHub(hub_url);
    _broker_url = broker_url;
  }



Puis établir la connexion avec le Broker MQTT en utilisant la classe MqttClient de la librairie Paho.

  public void run()
  {
    ...
    MemoryPersistence persistence = new MemoryPersistence();
    _sampleClient = new MqttClient(_broker_url, "MQTTBridge", persistence);
    MqttConnectOptions connOpts = new MqttConnectOptions();
    connOpts.setCleanSession(true);
    System.out.println("Connecting to broker: " + _broker_url);
    _sampleClient.connect(connOpts);
    System.out.println("Connected");



Souscrire aux topics "fan_control" et "leds_control"



Il faut souscrire aux topics "fan_control" et "leds_control" et stocker les références sur les objets YRelay et YColorLeds qui permettent de contrôler le relais et les LEDs.

    _sampleClient.setCallback(this);
    // subscribes to events from MQTT
    _sampleClient.subscribe("fan_control");
    _sampleClient.subscribe("leds_control");

    _yctx.UpdateDeviceList()
    _yrelay = YRelay.FirstRelayInContext(_yctx);
    YColorLed colorLed = YColorLed.FirstColorLedInContext(_yctx);
    while (colorLed != null) {
        _yleds.add(colorLed);
        colorLed = colorLed.nextColorLed();
    }



Il faut ensuite traiter les messages MQTT entrant dans la méthode messsageArrived. Pour le topic "fan_control", on change la position du Yocto-PowerRelay à l'aide de la méthode set_output(). Pour le topic "leds_control", on décode la couleur et on l'applique à toutes les LEDs avec la méthode set_rgbColor().

  @Override
  public void messageArrived(String topic, MqttMessage mqttMessage)
  {
    byte[] payload = mqttMessage.getPayload();
    String message = new String(payload);
    try {
      switch (topic) {
        case "fan_control":
          int output;
          if ("on".equalsIgnoreCase(message)) {
              output = YRelay.OUTPUT_ON;
          } else {
              output = YRelay.OUTPUT_OFF;
          }
          _yrelay.set_output(output);
          break;
        case "led_test":
          int color = Integer.parseInt(message.replaceFirst("#", ""), 16);
          for (YColorLed colorLed  : _leds){
              colorLed.set_rgbColor(color);
          }
          break;
      }
   } catch (YAPI_Exception e) {
      e.printStackTrace();
    }
  }



Publier la temperature



Il est tentant de faire une boucle sans fin et d'appeler la méthode get_currentValue() du capteur de température, mais il est beaucoup plus efficace d’enregistrer un callback qui est appelé dès que la valeur du capteur change.

    YTemperature temperature = YTemperature.FirstTemperatureInContext(_yctx);
    temperature.registerValueCallback(this);
    while (true) {
        _yctx.Sleep(1000);
    }




Le callback reçoit directement la nouvelle température sous forme textuelle et il faut simplement publier cette nouvelle valeur pour le topic "temperature".

 @Override
    public void yNewValue(YTemperature temperature, String currentValue)
    {
        MqttMessage message = new MqttMessage(currentValue.getBytes());
        try {
            _sampleClient.publish("temperature", message);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }



Réseau local vs cloud


Pour pouvoir utiliser cette passerelle, il faut simplement la lancer sur une machine qui peut accéder à la fois au broker MQTT et au YoctoHub. L'endroit le plus simple est de le placer sur la même machine qui héberge le broker MQTT, à savoir le Raspberry Pi.

Cette solution fonctionne car dans notre notre exemple le broker MQTT et le YoctoHub sont branchés sur le même réseau local, cependant si le broker MQTT est dans le cloud et que le YoctoHub est derrière un routeur ADSL, ou sur un réseau cellulaire, cette solution ne fonctionne plus. La machine qui héberge le broker MQTT ne peut pas se connecter directement au YoctoHub, car le routeur ou le réseau cellulaire bloque les connexions entrantes.

Il y a des moyens de contourner ce problème, comme ouvrir un port sur le routeur ADSL, utiliser un VPN ou encore héberger la passerelle sur une autre machine qui se situe sur le même réseau que le YoctoHub. Mais il existe une autre solution: convertir notre application Java en callback WebSocket.

La passerelle en callback WebSocket


L'utilisation du callback WebSocket résout notre problèmes car la connexion est établie par le YoctoHub et non plus la passerelle Java. Et si votre YoctoHub peut se connecter à Internet il peut aussi se connecter à la passerelle.

Les callbacks fonctionne même derrière un filtre NAT
Les callbacks fonctionne même derrière un filtre NAT



Si vous n'avez pas de serveur d'application Java, le plus gros travail consiste à installer un serveur d'application comme Tomcat ou GlassFish. Une fois cette étape réalisée, il suffit de créer un endpoint WebSocket qui instancie la passerelle et lance la boucle principale dans un Thread.

@ServerEndpoint("/mqtt_bridge")
public class YServerWebsocket
{

    @OnOpen
    public void onOpen(Session session)
    {
        // registers the YoctoHub/VirtualHub that starts the connection
        try {
          MQTTBridge bridge = new MQTTBridge(session, "tcp://www.example.com:1883");
        } catch (YAPI_Exception e) {
          e.printStackTrace();
          return;
        }
        Thread thread = new Thread(bridge);
        thread.start();
    }


}



La seule modification à apporter à notre code Java est l'ajout d'un constructeur qui prend en paramètre la session WebSocket créée par le serveur d'application au lieu de l'URL du YoctoHub. L'appel à la méthode RegisterHub est donc remplacé par un appel à la méthode PreregisterHubWebSocketCallback() qui prend en paramètre la session Websocket. Le reste du code est inchangé.

  public MQTTBridge(Session session, String broker_url) throws YAPI_Exception
  {

    _yctx = new YAPIContext();
    _yctx.PreregisterHubWebSocketCallback(session);
  _broker_url = broker_url;
  }



Il ne reste plus qu'à configurer le YoctoHub pour se connecter à notre endpoint WebSocket.

Conclusion


Comme on peut le voir, contrôler des modules Yoctopuce depuis une architecture MQTT nécessite d'écrire une passerelle, mais le code est assez simple et court. Si vous n’êtes pas très à l'aise avec Java, vous pouvez toujours partir de cet exemple et le modifier pour qu'il corresponde à vos besoins.

Le code complet de ces deux exemples est disponible sur notre compte GitHub: https://github.com/yoctopuce-examples/mqtt_bridge

Nous avons aussi réalisé une petite vidéo de démo:

  


Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.