🌐Week 14: Embedded Networking and Communications

Project Hero

Our milled xiao board sending data to a pico via MQTT:

Group Assignment

In this weeks group assignment we connected 2 boards VIA UART, my board was the Pico breakout board we made in week8 which takes clusters of gpio pins and conveniently groups them to plug in motors and encoders, and now as well as breaking out tx, rx and ground for UART communications. I learnt that UART is such a simple protocol to use and its extremely universal. We also played aorund with having multiple recieving pins connected to 1 TX and found it had no issues. I think this is a valuable piece of knowledge for future sitatuations when you are streapped for UART channels.

Reading A Button with the Xiao

Wireless communications is one of my weaker points as a maker, I have extensively used wired protocols to get boards talking to each other, but wireless not so much. So this week I wanted to give MQTT a go and get 2 boards talking to each other - a Pico and the board we milled in week4 (but with an ESP32 variant of the xiao). For this week I followed along with this tutorial.

Starting off I wrote some simple code for the xiao to get it to turn the SMD LED on when ever the SMD button was pressed. The final goal for this week will to have an led light up on the pico the same way this one is, but being told to do so through MQTT.

from machine import Pin, ADC
import time

led = Pin(7, Pin.OUT)
button = Pin(8, Pin.IN, Pin.PULL_DOWN)

while True:
        state = button.value()

        if state == 1:
            led.value(1)
        else:
            led.value(0)

        time.sleep(3)

Setting up a Broker

To use MQTT we will need a broker which handles and manages all the interactions of our boards. While we can set one up locally on a computer, I opted to use Adafruit IO which is a free option and provides a nice dashboard. I created an account and generated my authentication key with the big yellow key button.

Now we need to create a feed, this is the MQTT topic that we will be publishing and receiving messaged on. We can then find the MQTT topic name which we will be using later.

And thats it! We now have everything we need get the Pico connected to it.

Transmitting from the Xiao

In thonny I imported this helpful library which will handle a lot of the MQTT side of things.

We will then import the library and the network library, and then connect the Xiao to a local wireless network with:

import network
from umqtt.simple import MQTTClient

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("ssid", "password")
while wlan.isconnected() == False:
    print('Waiting for connection...')
    time.sleep(1)
print("Connected to WiFi")

Then we will enter the MQTT broker we are using, the name and key we generated, and the topic that this device will be on.

mqtt_host = "io.adafruit.com"
mqtt_username = "JarydGiesen"
mqtt_password = "insert the key here"
mqtt_publish_topic = "JarydGiesen/feeds/moisture"

And then we will enter a unique name for this client, it needs to be globally unique, something no one else uses. Then we initialise the pico as a client and connect to the mqtt server.

mqtt_client_id = "thisIsJarydsReallyRandomClientName"

mqtt_client = MQTTClient(
        client_id=mqtt_client_id,
        server=mqtt_host,
        user=mqtt_username,
        password=mqtt_password)

mqtt_client.connect()

Then in our while True loop, we can now send what ever data we want. I will just be sending the button value and we will do this at a 3 second polling rate (adafruit.io's free tier has data limitations).

        mqtt_client.publish(mqtt_publish_topic, str(reading))

And we can see on adafruit.io that we are recieving this data.

Recieving it on the Pico

The second Pico will have the same setup code (full code at the bottom). But when we get to recieving part olf the code, we need to define a callback function. I found that the message published on the topic came through as a byte string which gave me a whole heap of issues, but I turned it back into a python string with .decode(). It also needs to be an int so we can process it. Then we apply the same logic to turn the LED off and on, connect the client, and then tell it to start waiting to recieve a messge on that topic.

def mqtt_subscription_callback(topic, message):
    print (f'Topic {topic} received message {message}')  # Debug print out of what was received over MQTT
    if message == b'1':
        print("LED ON")
        led.value(1)
    elif message == b'0':
        print("LED OFF")
        led.value(0)


mqtt_client.set_callback(mqtt_subscription_callback)
mqtt_client.connect()

mqtt_client.subscribe(mqtt_receive_topic)

try:
    while True:
        mqtt_client.wait_msg()
except Exception as e:
    print(f'Failed to wait for MQTT messages: {e}')
finally:
    mqtt_client.disconnect()

Now our system is complete! If I run both of them, we now have 2 nodes wirelessly connected through MQTT and a broker server I presume somewhere in America, how awesome! If I press the button, both the LEDS turn on after a short delay. I chose this for this weeks assignment as I want to get into MQTT and actually set up my own wireless plant monitoring system.

Final Code

Sending Xiao Code:

from machine import Pin
import network
import time
from math import sin
from umqtt.simple import MQTTClient

led = Pin(7, Pin.OUT)
button = Pin(8, Pin.IN, Pin.PULL_DOWN)

# Fill in your WiFi network name (ssid) and password here:
wifi_ssid = "my-wifi"
wifi_password = "Password1"

# Connect to WiFi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(wifi_ssid, wifi_password)
while wlan.isconnected() == False:
    print('Waiting for connection...')
    time.sleep(1)
print("Connected to WiFi")

# Fill in your Adafruit IO Authentication and Feed MQTT Topic details
mqtt_host = "io.adafruit.com"
mqtt_username = "Jaryd_Giesen"  # Your Adafruit IO username
mqtt_password = "aio_kPJC27iD34Pvt9Q27346NIdV6imQ"  # Adafruit IO Key
mqtt_publish_topic = "Jaryd_Giesen/feeds/test"  # The MQTT topic for your Adafruit IO Feed

# Enter a random ID for this MQTT Client
# It needs to be globally unique across all of Adafruit IO.
mqtt_client_id = "ufgneruvnberibwouvndouncjdcissjsjawubadcu122ws"

# Initialize our MQTTClient and connect to the MQTT server
mqtt_client = MQTTClient(
        client_id=mqtt_client_id,
        server=mqtt_host,
        user=mqtt_username,
        password=mqtt_password)

mqtt_client.connect()

# Publish a data point to the Adafruit IO MQTT server every 3 seconds
# Note: Adafruit IO has rate limits in place, every 3 seconds is frequent
#  enough to see data in realtime without exceeding the rate limit.
counter = 0
try:
    while True:
        state = button.value()

        if state == 1:
            led.value(1)
        else:
            led.value(0)

        # Publish the data to the topic!
        print(f'Publish {state}')
        mqtt_client.publish(mqtt_publish_topic, str(state))

        # Delay a bit to avoid hitting the rate limit
        time.sleep(3)
except Exception as e:
    print(f'Failed to publish message: {e}')
finally:
    mqtt_client.disconnect()    

Recieving Pico Code:

import time
import network
from machine import Pin
from umqtt.simple import MQTTClient

# Setup the onboard LED so we can turn it on/off
led = Pin("LED", Pin.OUT)

# Fill in your WiFi network name (ssid) and password here:
wifi_ssid = "my-wifi"
wifi_password = "Password1"

# Connect to WiFi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(wifi_ssid, wifi_password)
while wlan.isconnected() == False:
    print('Waiting for connection...')
    time.sleep(1)
print("Connected to WiFi")

# Fill in your Adafruit IO Authentication and Feed MQTT Topic details
mqtt_host = "io.adafruit.com"
mqtt_username = "Jaryd_Giesen"  # Your Adafruit IO username
mqtt_password = "aio_kPJC27iD34Pvt9Q27346NIdV6imQ"  # Adafruit IO Key
mqtt_receive_topic = "Jaryd_Giesen/feeds/test"  # The MQTT topic for your Adafruit IO Feed

# Enter a random ID for this MQTT Client
# It needs to be globally unique across all of Adafruit IO.
mqtt_client_id = "somethingreaasfsdgerhefbsdvwefa222wssdv"

# Initialize our MQTTClient and connect to the MQTT server
mqtt_client = MQTTClient(
        client_id=mqtt_client_id,
        server=mqtt_host,
        user=mqtt_username,
        password=mqtt_password)


# So that we can respond to messages on an MQTT topic, we need a callback
# function that will handle the messages.
def mqtt_subscription_callback(topic, message):
    print (f'Topic {topic} received message {message}')  # Debug print out of what was received over MQTT
    if message == b'1':
        print("LED ON")
        led.value(1)
    elif message == b'0':
        print("LED OFF")
        led.value(0)

# Before connecting, tell the MQTT client to use the callback
mqtt_client.set_callback(mqtt_subscription_callback)
mqtt_client.connect()

# Once connected, subscribe to the MQTT topic
mqtt_client.subscribe(mqtt_receive_topic)
print("Connected and subscribed")

try:
    while True:
        # Infinitely wait for messages on the topic.
        # Note wait_msg() is a blocking call, if you're doing multiple things
        # on the Pico you may want to look at putting this on another thread.
        print(f'Waiting for messages on {mqtt_receive_topic}')
        mqtt_client.wait_msg()
except Exception as e:
    print(f'Failed to wait for MQTT messages: {e}')
finally:
    mqtt_client.disconnect()