User Tools

Site Tools


notes:mqtt

This is an old revision of the document!


Written by Jacob Haip on April 17, 2024

I am interested in integrating more physical hardware and sensors into Folk Computer to help expand the scope to the full room and to take advantage of the affordances of hardware that the projected AR system alone can't do. Things like sensing the noise level of the environment, motors that spin a fan and create wind, and the physical feel of a knob that clicks when turning off.

WiFi-connected microcontrollers are the primary tool I have been using to integrate sensors and actuators with Folk. Boards I have used include the Adafruit Feather ESP32, RPi Pico W, and the Particle Photon. They are either programmed using Arduino or MicroPython. I have also integrated microcontrollers using BLE or a wired serial connection but I tend to like WiFi for long-lived stable devices embedded in my space.

Folk has the ability to create arbitrary HTTP endpoints and a Websocket connection that proxies through Folk Statements, but neither are currently bidirectional and both are heavy protocols for microcontrollers to send or get data from Folk.

Lately I have stated using MQTT as the protocol for microcontrollers to talk to the Folk System. It is a lighter weight Pub/Sub messaging protocol that is primarily done on top of a TCP connection. Microcontrollers talk to a MQTT Broker running alongside Folk and then a Folk program talks to the broker as well.

I am using the Mosquitto MQTT Broker which runs on port 1883.

sudo apt install mosquitto mosquitto-clients -y
sudo systemctl status mosquitto
sudo systemctl start mosquitto
sudo systemctl enable mosquitto
mosquitto_sub -h address -t topic
mosquitto_pub -h address -t topic -m “message”

The bridge from the MQTT broker to Folk is the mqtt.folk program which adds support for wishes like Wish MQTT publish $message on topic $feed at timestamp $timestamp and When MQTT claims message /message/ topic /topic/ timestamp /t/ { ... }.

mqtt.folk
# This program depends on the https://chiselapp.com/user/schelte/repository/mqtt/home MQT library being installed at the /home/folk/mqtt directory
Start process mqtt {
    Wish $::thisProcess receives statements like \
        [list /someone/ wishes MQTT publish /...anything/]
    Wish $::thisProcess receives statements like \
        [list $::thisNode has step count /...anything/]
    Wish $::thisProcess shares statements like \
        [list MQTT claims message /message/ topic /topic/ timestamp /timestamp/]
 
    ::tcl::tm::path add /home/folk/mqtt
    package require mqtt
 
    set client [mqtt new]
    set ::mqttclient $client
 
    proc cb {topic content status} {
        set message [encoding convertfrom utf-8 $content]
        puts "MQTT CALLBACK: $topic: $message"
        set now [clock milliseconds]
        Assert MQTT claims message $message topic $topic timestamp $now

        # Retract all events that are more than 5 seconds old.
        set events [Statements::findMatches [list MQTT claims message /message/ topic /topic/ timestamp /timestamp/]]
        foreach event $events {
            dict with event {
                if {$now - $timestamp > 5000} {
                    Retract MQTT claims message $message topic $topic timestamp $timestamp
                }
            }
        }
    }
    $client connect test-client localhost 1883
    $client subscribe "#" cb
 
    When /someone/ wishes MQTT publish /m/ on topic /t/ at timestamp /ts/ {
        # puts "publishing $m on $t at $ts"
        $::mqttclient publish "$t" "$m"
    }
 
    while true {
        # Step for folk, update for TCL and MQTT
        Step
        update
    }
}

Example code using the Adafruit MagTag E-Ink Microcontroller

Adafruit MagTag MicroPython Code that displays texts messages received on the MQTT /magtag/message topic, and publishes a heartbeat message on the MQTT /feeds/test topic.

magtag-micropython.py
import os
import time
import ssl
import socketpool
import wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_magtag.magtag import MagTag
 
magtag = MagTag()
magtag.add_text(
    text_position=(
        50,
        (magtag.graphics.display.height // 2) - 1,
    ),
    text_scale=3,
)
magtag.set_text("Hello World")
 
print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_SSID')}")
print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_PASSWORD')}")
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}!")
 
sending_feed = "/feeds/test"
message_feed = "/magtag/message"
 
def connected(client, userdata, flags, rc):
    client.subscribe(message_feed)
 
def disconnected(client, userdata, rc):
    print("Disconnected")
 
def message(client, topic, message):
    print(f"New message on topic {topic}: {message}")
    magtag.set_text(message)
 
pool = socketpool.SocketPool(wifi.radio)
ssl_context = ssl.create_default_context()
mqtt_client = MQTT.MQTT(
    broker="folk-haip.local",
    port=1883,
    socket_pool=pool,
    ssl_context=ssl_context,
)
 
# Setup the callback methods above
mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message
 
# Connect the client to the MQTT broker.
print("Connecting...")
mqtt_client.connect()
 
test_val = 0
while True:
    # Poll the message queue
    mqtt_client.loop(timeout=1)
 
    # Send a new message
    print(f"Sending value: {test_val}...")
    mqtt_client.publish(sending_feed, test_val)
    print("Sent!")
    test_val += 1

A folk program to make a crude clock on the MagTag display:

crude-clock.folk
When /node/ has step count /c/ {
  if {[expr {int($c) % 5000}] == 0} {
    set ts [clock milliseconds]
    Wish MQTT publish $ts on topic jhaip/feeds/onoff at timestamp $ts
  }
}

A folk program that shows the latest published message from the MagTag device:

show-message.folk
When MQTT claims message /message/ topic "/feeds/test" timestamp /t/ {
   Wish $this is labelled "MagTag value: $message"
}
notes/mqtt.1713404561.txt.gz · Last modified: 2024/04/18 01:42 by discord

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki