Adafruit IO gives you the option to disconnect your microcontroller from your computer and run it off of USB power or a battery, and still be able to see the data. It also allows you to send data to your microcontroller, such as NeoPixel colors. This example shows how to both send data to and receive data from Adafruit IO. It pulls from a "random" number generator and sends the "random" number to Adafruit IO, while simultaneously listening for NeoPixel color data from Adafruit IO.

NeoPixel Location

The NeoPixel LED (highlighted in green) is located next to the ESP32-S2/S3 microcontroller, and is labeled Neo on the silk.

Adafruit IO Feeds and Dashboard

The first thing you'll need to do, is head over to Adafruit IO and make sure your account is set up.

Then, you need to create two feeds called neopixel and random. These are case sensitive!

Next, you'll create a dashboard for the NeoPixel Color Picker. You can name the dashboard whatever you like.

Once the dashboard is created, you'll want to add a color picker block. The color picker block is highlighted by a red arrow in the image below.

Once you choose the color picker block, you'll need to connect a feed to it. Check the box next to neopixel.

Finally, a Block Settings page will come up. You can add an optional block title here. Then you press Create Block.

The dashboard should look something like the following.

Now that things are set up on the Adafruit IO end, you can continue on to the code on your microcontroller!

Adafruit IO Example Secrets

This example requires you to provide your Wi-Fi credentials, and your Adafruit IO username and key. To do this, you'll want to create a secrets.py file on your CIRCUITPY drive.

To obtain your Adafruit IO key, follow the initial steps on this page.

For information on how to structure your secrets.py file, and what information to add to it, check out the Secrets File section on the CircuitPython Internet Test page.

Adafruit IO Example Code

To run this example, you need to first install the NeoPixel, Adafruit IO, and Adafruit MiniMQTT libraries into the lib folder on your CIRCUITPY drive. Then you need to update code.py with the example script.

Thankfully, we can do this in one go. In the example below, click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, and copy the entire lib folder and the code.py file to your CIRCUITPY drive.

# SPDX-FileCopyrightText: 2021 Ladyada for Adafruit Industries
# SPDX-FileCopyrightText: 2022 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
import ssl
from random import randint
import microcontroller
import socketpool
import wifi
import board
import neopixel
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT

try:
    from secrets import secrets
except ImportError:
    print("WiFi and Adafruit IO credentials are kept in secrets.py - please add them there!")
    raise

# Add your Adafruit IO Username and Key to secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need to obtain your Adafruit IO key.)
aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]

# WiFi
try:
    print("Connecting to %s" % secrets["ssid"])
    wifi.radio.connect(secrets["ssid"], secrets["password"])
    print("Connected to %s!" % secrets["ssid"])
# Wi-Fi connectivity fails with error messages, not specific errors, so this except is broad.
except Exception as e:  # pylint: disable=broad-except
    print("Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds.")
    time.sleep(30)
    microcontroller.reset()

# Initialise NeoPixel
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3)


# Define callback functions which will be called when certain events happen.
def connected(client):
    print("Connected to Adafruit IO!  Listening for NeoPixel changes...")
    # Subscribe to Adafruit IO feed called "neopixel"
    client.subscribe("neopixel")


def message(client, feed_id, payload):  # pylint: disable=unused-argument
    print("Feed {0} received new value: {1}".format(feed_id, payload))
    if feed_id == "neopixel":
        pixel.fill(int(payload[1:], 16))


# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)

# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
    broker="io.adafruit.com",
    username=secrets["aio_username"],
    password=secrets["aio_key"],
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

# Initialize Adafruit IO MQTT "helper"
io = IO_MQTT(mqtt_client)

# Set up the callback methods above
io.on_connect = connected
io.on_message = message

timestamp = 0
while True:
    try:
        # If Adafruit IO is not connected...
        if not io.is_connected:
            # Connect the client to the MQTT broker.
            print("Connecting to Adafruit IO...")
            io.connect()

        # Explicitly pump the message loop.
        io.loop()
        # Obtain the "random" value, print it and publish it to Adafruit IO every 10 seconds.
        if (time.monotonic() - timestamp) >= 10:
            random_number = "{}".format(randint(0, 255))
            print("Current 'random' number: {}".format(random_number))
            io.publish("random", random_number)
            timestamp = time.monotonic()

    # Adafruit IO fails with internal error types and WiFi fails with specific messages.
    # This except is broad to handle any possible failure.
    except Exception as e:  # pylint: disable=broad-except
        print("Failed to get or send data, or connect. Error:", e,
              "\nBoard will hard reset in 30 seconds.")
        time.sleep(30)
        microcontroller.reset()

Your CIRCUITPY/lib folder should contain at least the following folders and files:

  • adafruit_io/
  • adafrruit_minimqtt/
  • neopixel.mpy
CIRCUITPY

If you like, you can connect to the serial console to see the connection info and current readings printed out.

NeoPixel Color Change

To change the color of the NeoPixel, go to the NeoPixel Adafruit IO dashboard you created at the beginning, and click on the colored circle in the ColorPicker block. It will bring up the following.

You can move the dot in the box around, and the slider line across the gradient to choose the perfect color. Choose a new color and click SAVE.

The NeoPixel color will update, and you will see the new value printed to the serial console, as shown below.

Code Walkthrough

This example contains three try/except blocks. These are included where the code is likely to fail due to WiFi or Adafruit IO connection failures. WiFi can be finicky, and without these code blocks, if the connection was lost, the code would crash. Instead, it is designed to reset the board and start the code over again to reestablish the connection, regardless of the cause. This ensures your code will continue running. The details of these blocks are explained below.

First you import all of the necessary modules and libraries. This includes importing the data from your secrets.py file.

import time
import ssl
from random import randint
import socketpool
import wifi
import board
import neopixel
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT

try:
    from secrets import secrets
except ImportError:
    print("WiFi and Adafruit IO credentials are kept in secrets.py - please add them there!")
    raise
Note that if a secrets.py file is not present on your CIRCUITPY drive, the code will fail to run, and you will receive an error in the serial console. Add a secrets.py file to your CIRCUITPY drive to resolve this error.

The code pulls your Adafruit IO username and key from secrets.py.

aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]

The WiFi attempts to connect, and prints the status to the serial console. If it connects successfully, the code continues onto the NeoPixel set up.

try:
    print("Connecting to %s" % secrets["ssid"])
    wifi.radio.connect(secrets["ssid"], secrets["password"])
    print("Connected to %s!" % secrets["ssid"])

If the WiFi connection is not successful, the error will be printed to the serial console, and the board will hard reset after 30 seconds.

except Exception as e:  # pylint: disable=broad-except
    print("Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds.")
    time.sleep(30)
    microcontroller.reset()

Once the WiFi successfully connects, the NeoPixel object is initiated.

pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3)

Following that are two callback methods. For more details, check out this guide. The connected method subscribes to the neopixel feed on Adafruit IO. The message callback checks for updates to the neopixel feed, and turns the pixel the color from the feed.

def connected(client):
    print("Connected to Adafruit IO!  Listening for NeoPixel changes...")
    # Subscribe to Adafruit IO feed called "neopixel"
    client.subscribe("neopixel")


# pylint: disable=unused-argument
def message(client, feed_id, payload):
    print("Feed {0} received new value: {1}".format(feed_id, payload))
    if feed_id == "neopixel":
        pixel.fill(int(payload[1:], 16))

You create a socket pool, use that to initialise the new MQTT Client object, and use that to initialise the Adafruit IO MQTT "helper".

pool = socketpool.SocketPool(wifi.radio)

mqtt_client = MQTT.MQTT(
    broker="io.adafruit.com",
    username=secrets["aio_username"],
    password=secrets["aio_key"],
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

io = IO_MQTT(mqtt_client)

You set up the callback methods mentioned above.

io.on_connect = connected
io.on_message = message

Next, you attempt to connect the client to the MQTT broker. If connection is successful, the code continues on to the timestamp.

try:
    io.connect()

If the MQTT broker connection is not successful, the error is printed to the serial console, and the board will hard reset after 30 seconds.

except Exception as e:
    print("Failed to connect to Adafruit IO. Error:", e, "\nBoard will hard reset in 30 seconds.")
    time.sleep(30)
    microcontroller.reset()

Once the broker is connected, you set the timestamp to 0 immediately before the loop.

timestamp = 0

Inside the loop, you attempt to do two things. You first explicitly poll the message loop. Check out this guide for more details on that.

while True:
    try:
        io.loop()

Second, you have a block of code that runs every 10 seconds. Inside, you obtain a "random" value between 0-255 inclusive, print it to the serial console, and publish it to an Adafruit IO feed. Finally, you reset timestamp so the block of code knows when another 10 seconds has passed, and runs again.

[...]
        if (time.monotonic() - timestamp) >= 10:
            random_number = "{}".format(randint(0, 255))
            print("Current 'random' number: {}".format(random_number))
            io.publish("random", random_number)
            timestamp = time.monotonic()

If at any time WiFi or Adafruit IO disconnects, the code will print the error to the serial console, and the board will hard reset after 30 seconds.

[...]
    except Exception as e:
        print("Failed to get or send data, or connect. Error:", e,
              "\nBoard will hard reset in 30 seconds.")
        time.sleep(30)
        microcontroller.reset()

That's all there is to using CircuitPython and Adafruit IO to send data to Adafruit IO, and receive data from it!

This guide was first published on Apr 20, 2022. It was last updated on 2022-04-20 14:41:15 -0400.

This page (Adafruit IO: Send and Receive Data) was last updated on May 28, 2022.

Text editor powered by tinymce.