Freedom for Microcontrollers

We also provide an experimental library to connect your CircuitPython- or MicroPython-enabled microcontroller or IOT device to the Freedom network and stream data to the Freedom interface. You can use this for data logging, remote data viewing, statistics, smart alerts, and more.

Supported devices

At the moment we provide support for CircuitPython and MicroPython. A couple of microcontrollers you can use include the FeatherS2 (CircuitPython) and the TinyPICO (MicroPython).

We may also provide a C++ interface in the future.

Using MicroLink

In order to use MicroLink, there are 5 simple steps you need to follow:

0. Copy the MicroLink library

Copy from the Freedom MicroLink repository onto the root directory of your microcontroller device.

1. Get device credentials

Login to your Freedom Robotics account, create a new device, and fetch credentials for the device. This can be done by modifying the installer command, deleting | python and replacing it with:

curl -sSf " | tr "\n" " " | egrep -o "CREDENTIALS = {[^}]*}" | sed -e "s/CREDENTIALS = //" |  sed -e 's/[A-Z_]\+,/"",/g' | tr "\n" " " | sed -s "s/\(.*\),/\\1/" | python -m json.tool > credentials.json

Transfer the resulting credentials.json file onto your microcontroller as well.

2. Add your Wi-Fi credentials

Create a wifi.json file with your Wi-Fi credentials:

    "ssid": "YOURSSIDHERE",
    "password": "YOURPASSWORDHERE"

3. Set up networking

Setting up networking on your microcontroller. In order to use the MicroLink you need to have an interface that emulates the Python requests library. CircuitPython provides adafruit_requests and MicroPython provides urequests; both of these will work. Here we provide examples of how to instantiate these interfaces and abstract that into a file called

CircuitPython version

import adafruit_requests
import ipaddress
import socketpool
import ssl
import wifi
config_wifi = json.loads(
print("mac address:", [hex(i) for i in])
print("connecting to %s ..." % config_wifi["ssid"])["ssid"], config_wifi["password"])
print("ip address:",
pool = socketpool.SocketPool(
requests = adafruit_requests.Session(pool, ssl.create_default_context())

MicroPython version

import codey
import urequests as requests
import time

config_wifi = json.loads(
print("connecting to %s" % config_wifi["ssid"])

codey.wifi.start(config_wifi["ssid"], config_wifi["password"])
while not codey.wifi.is_connected():

4. Start publishing data!

You're ready to go! You can now use the Freedom MicroLink in your code. Here is a simple "Hello World" number that publishes incrementing integers to Freedom every 1 second:

import time
from net import requests # microcontroller-specific requests implementation
from freedomrobotics import MicroLink

freedom = MicroLink(requests)

i = 0
freedom.log("info", "Initializing")
while True:
    freedom.message("/count", "std_msgs/Int32", { "data": i })
    i += 1

MicroLink reference

class MicroLink(requests = None, account = None, device = None, token = None, secret = None, auto_time_sync = True, auto_sync = True, debug = False, max_queue_size = 127)


  • requests (class) (required). You must pass an implementation of the requests interface, such as an instantiation of adafruit_requests or urequests.
  • max_queue_size (int) (optional): Maximum size of message queue. The message queue is cleared when a sync is successful. If the queue becomes full and more messages are passed, the oldest messages will be deleted.
  • debug (bool) (optional): Set to true to print debug information to the console. Defaults to false.
  • account, device, token, secret: (optional) Credentials for a device can be passed directly to this class. Each is a string. If not provided, credentials will be searched for in /credentials.json on the device.
  • auto_time_sync (bool) (optional): Automatically synchronize the RTC clock over the Internet at startup and then every 10 minutes. Defaults to true. If set to false, you will need to implement your own time synchronizing logic or you may encounter SSL errors.
  • auto_sync (bool) (optional): Automatically upload data to Freedom after every call to message() or log(). Defaults to true. If set to false, call the sync() method periodically to synchronize.


message(topic, topic_type, message)

Queues a message for streaming to Freedom. It will be automatically uploaded unless the class was initialized with auto_sync = False.


  • topic (str): The topic to publish data under, analogous to a ROS topic, e.g. "/humidity" or "/temperature".
  • topic_type (str): The topic type. You can use any of the ROS standard types or a custom type.
  • message (dict): A message with fields specified as a dict. For example a message with type "std_msgs/Int32" could be {"data": 42}.

log(level, message)

Sends a log message to Freedom. It will be automatically uploaded unless the class was initialized with auto_sync = False.


  • level (str): One of "debug", "info", "warn", "error", or "fatal".
  • message (str): Log message text.


Synchronizes all queued message to Freedom. Only needs to be called if initialized with auto_sync = False.