MQTT-SN client for Loxone.
This is a fork from sebastienfouss/loxone_mqttsn with improved subscriber code robustness and wildcard topic subscription implementation.
MQTT-SN specifications available here.
- I am not affiliated with Loxone Electronics GmbH
- Use this code at your own risk
Loxone is a great platform, but for there is no possibility to integrate it natively with a MQTT broker. Solutions exist (for example, a Node Red gateway between MQTT broker and Loxone), but I wanted a solution that does not imply an intermediate layer between Loxone and the broker.
In order to implement this, a couple of limitations have to be considered:
- Virtual Output: the source port of an UDP connection is unknown to the user, making it impossible to monitor the replies from the server in a bidirectional communication
- Virtual Input: can only be a UDP connection
So using only Virtual Inputs and Outputs is not enough to manage a MQTT connection. How can we proceed ?
A couple of ways:
- Run a pair of socat, for example on your router:
- Socat 1 accept UDP messages on Port A, and redirect them to MQTT broker on Port B with source port X
- Socat 2 accept UDP messages back from broker on port X, and redirect them to Loxone on Port C
Pretty easy, it does work, but it requires to setup socat somewhere.
- Run picoC program, more below.
- PicoC is mono threaded only, that is, each picoC block will run in its own thread, but you can't spawn threads within a block. This implies, for bidirectional communication as it is the case with a MQTT server, that we have to create two applications: a publisher and a listener.
- UDP sockets cannot be reused between two applications Note: PicoC can open a TCP connection, so therotically we could use TCP instead of UDP, at least to communicate with the broker, but this opens another level of complexity: PicoC is not really user friendly for debugging.
- Create 2 listeners on the MQTT-SN gateway (1 for Loxone publisher, 1 for Loxone subscriber)
- On Loxone, create a Virtual Input "MQTT-SN Publisher" with port UDP X, and 2 Virtual Outputs ("MQTT-SN Publisher" with target port UDP Y, and "MQTT-SN Subscriber" with target port UDP Z)
- On Publisher application, workflow will be:
- Open an ingress UDP connection on port Z
- Open an egress UDP stream to MQTT-SN gateway
- Monitor port Z: as soon as data is available (that is, data user is willing to publish to MQTT), parse it and forward it to MQTT-SN gateway
- Keepalive is also managed from the Publisher application
- On Subscriber application, workflow will be:
- Open an ingress UDP connection on port Y
- Open an egress UDP stream to MQTT-SN gateway
- Open an egress UDP stream to Loxone on port X
- On every subscription request, parse it and forward it to MQTT-SN gateway
- Monitor MQTT Broker: as soon as data available (that is, data published on a monitored MQTT topic, parse it and forward it to Loxone on port X
Next question is: what broker to chose, or more precisely: what gateway to choose to interact with a MQTT broker, considering that we will have to use UDP and MQTT protocol is TCP base ? Couple of options here, the main ones:
- MQTT-SN: a lightweight, UDP-base, MQTT protocol variant
- COAP: another MQTT-like protocol. This project is implemented using MQTT-SN protocol. COAP is a bit too complex to manage it in the limited environment of a PicoC application.
This brings us to the main topic: which broker ? Considering that a MQTT-SN "broker" is in essence a gateway to an actual MQTT broker, I decided to use EMQX (emqx.io) that embeds in a single server a MQTT broker, as well as a MQTT-SN (and COAP, etc) gateway.
Install EMQX Broker.
On Cluster Settings > Gateways, enable MQTT-SN gateway.
Make sure to add a second (UDP) listener, as per the picture below.
Add a Virtual Input, name it for example MQTT-SN IN, and set the port (for example, 9903).
Add 2 Virtual Outputs:
- MQTT-SN OUT PUBLISH
- As address, enter /dev/udp/127.0.0.1/9904
- Make sure to enable "Close connection after sending"
- Leave the 2 remaining fields (separator, connection command) empty
- MQTT-SN OUT SUBSCRIBE
- As address, enter /dev/udp/127.0.0.1/9902
- Make sure to enable "Close connection after sending"
- Leave the 2 remaining fields (separator, connection command) empty
Add on an existing or a new page, two picoC blocks:
- First one named "Subscribe"
- Second one named "Publish
The output O13 from the "Subscribe" program shall be linked to a Front Detector, then linked to your subscriptions (see below).
In the "Subscribe" block, copy and paste the content from the file subscriber.c. In the "Publish" block, copy and paste the content from the file publish.c.
Make sure to modify the URL address of your MQTT-SN gateway, and to adapt the ports if needed.
End result should be similar to this:
In Virtual Output "MQTT-SN OUT PUBLISH", add a Virtual Output Command.
In the Virtual Output Command, set "Command with ON" as a typical MQTT publication, ie topic/payload. You can use etc for payload, as usual for a Virtual Output.
In Virtual Output "MQTT-SN OUT SUBSCRIBE", add a Virtual Output Command.
In the Virtual Output Command, fill "Command with ON" with the requested MQTT topic.
Finally, make sure to connect the Virtual Output Command to the Front Detection linked to Publisher bloc.
In Virtual Input "MQTT-SN IN", add a Virtual Input Command. Fill the command detection as usual, for example: