You can send track all Mastodon posts with a particular hashtag using CircuitPython and your Raspberry Pi Pico W! This example allows you to customise which hashtag you'd like to follow, and then works with the Mastodon API to retrieve toots with that hashtag.

This example will also run on any CircuitPython-compatible microcontroller board with native WiFi. No changes necessary!

Load the Example and Library

This example requires one external library. Luckily you can load the code and the necessary library together. Click the blue Download Project Bundle button above the code below to download the necessary library and the code.py file in a zip file.

Connect the Raspberry Pi Pico W to your computer via a known good data+power USB A to micro B cable. The Pico W should show up in your File Explorer or Finder (depending on Operating System) as a thumb drive named CIRCUITPY. Extract the contents of the zip file, and copy the entire lib folder, and the code.py file to your CIRCUITPY drive.

Your CIRCUITPY/lib folder should contain the following file:

  • adafruit_requests.mpy

Your contents of your CIRCUITPY drive should resemble the following:

CIRCUITPY

Example Code

# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT

import os
import re
import time
import ssl
import wifi
import socketpool
import microcontroller
import adafruit_requests

#  enter the hashtag that you want to follow
hashtag = "CircuitPython"

#  connect to SSID
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))

#  add your mastodon token as 'mastodon_token' to your settings.toml file
headers = {'Authorization': 'Bearer ' + os.getenv('mastodon_token') + 'read:statuses'}

pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())

#  initial request, gets most recent matching hashtag post
#  add your mastodon instance (mastodon.social, tech.lgbt, etc)
#  to your settings.toml file as mastodon_host
r = requests.get("https://%s/api/v1/timelines/tag/%s?limit=1" % (os.getenv('mastodon_host'), hashtag), headers=headers) # pylint: disable=line-too-long
json_data = r.json()
post_id = str(json_data[0]['id'])
print("A new #%s post from @%s:" % (hashtag, str(json_data[0]['account']['acct'])))
print(re.sub('<[^>]+>', '', json_data[0]['content']))
print()

while True:
    try:
        time.sleep(360)
        # compares post_id to see if a new post to the hashtag has been found
        r = requests.get("https://%s/api/v1/timelines/tag/%s?since_id=%s" % (os.getenv('mastodon_host'), hashtag, post_id), headers=headers) # pylint: disable=line-too-long
        json_data = r.json()
        json_length = len(json_data)
        # if the id's match, then the json array is empty (length of 0)
        # otherwise there is a new post
        if json_length > 0:
            post_id = str(json_data[0]['id'])
            print("A new #%s post from @%s:" % (hashtag, str(json_data[0]['account']['acct'])))
            print(re.sub('<[^>]+>', '', json_data[0]['content']))
            print()
        else:
            print("no new #%s posts" % hashtag)
            print(json_length)
            print()

    except Exception as e:  # pylint: disable=broad-except
        print("Error:\n", str(e))
        print("Resetting microcontroller in 10 seconds")
        time.sleep(10)
        microcontroller.reset()

Add Your settings.toml File

Remember to add your settings.toml file as described in the Create Your settings.toml File page earlier in the guide. You'll need to include your CIRCUITPY_WIFI_SSID, CIRCUITPY_WIFI_PASSWORD, mastodon_host and mastodon_token in the file.

The mastodon_host variable should be assigned only the text part of the URL to your Mastodon instance. For example, "mastodon.social" or "fosstodon.org" without the https:// or a trailing /. 

CIRCUITPY_WIFI_SSID = "your-wifi-ssid-here"
CIRCUITPY_WIFI_PASSWORD = "your-wifi-password-here"

# Your host should be only the text part of the URL to your Mastodon instance.
# For example, "mastodon.social" or "fosstodon.org" without the https://. 
mastodon_host = "your-mastodon-instance-here"
mastodon_token = "your-mastodon-token-here"

Tracking a Hashtag

Once everything is saved to CIRCUITPY, you'll want to connect to the serial console. After a moment, you'll see the latest post that included the #CircuitPython hashtag.

Once the latest post is grabbed, the code will continue to check the hashtag for new posts. If there are no new posts, you'll see the following in the serial console.

When it finds a new post, it will print it to the serial console as it did with the first one.

That's all there is to tracking a hashtag on Mastodon using CircuitPython and the Raspberry Pi Pico W!

Code Walkthrough

Here are some details of what's happening behind the scenes in this example.

Hashtag

At the top of code, you can enter the hashtag that you want to follow as a string. This will be included in your API request URL.

#  enter the hashtag that you want to follow
hashtag = "CircuitPython"

Headers

Your authentication token is packed into headers via your settings.toml file. This is included in your API POST request to gain access to your Mastodon account. url is your API request URL. It passes your Mastodon instance via your settings.toml file.

#  add your mastodon token as 'mastodon_token' to your settings.toml file
headers = {'Authorization': 'Bearer ' + os.getenv('mastodon_token') + 'read:statuses'}

Initial Request

Before the loop, an initial request is made to the API to access the most recent post with the hashtag that you are tracking. In the URL, your Mastodon instance is passed via your settings.toml file.

The response from the request, is packed into a JSON. From the JSON, the post ID number is tracked as post_id. This will be used in the loop to see if a new hashtag post has been tooted.

In the REPL, you'll see the hashtag post printed with the account name and the post content. re.sub() is used to remove extraneous HTML (< and >) tags that are included in the raw post text.

#  initial request, gets most recent matching hashtag post
#  add your mastodon instance (mastodon.social, tech.lgbt, etc) to your settings.toml file as mastodon_host
r = requests.get("https://%s/api/v1/timelines/tag/%s?limit=1" % (os.getenv('mastodon_host'), hashtag), headers=headers) # pylint: disable=line-too-long
json_data = r.json()
post_id = str(json_data[0]['id'])
print("A new #%s post from @%s:" % (hashtag, str(json_data[0]['account']['acct'])))
print(re.sub('<[^>]+>', '', json_data[0]['content']))
print()

The Loop

In the loop, a request is made via the API approximately every 5 minutes to see if a new post with the hashtag has been tooted. post_id is passed to the ?since_id= object in the URL. This allows you to see if there is a new post via the API rather than using logic in CircuitPython.

If there a new post has not been tooted, then the request returns an empty JSON. This is checked using the len() function. If the JSON isn't empty, then the newly tooted post with the hashtag is printed to the REPL and post_id is updated with the new post ID number.

time.sleep(360)
# compares post_id to see if a new post to the hashtag has been found
r = requests.get("https://%s/api/v1/timelines/tag/%s?since_id=%s" % (os.getenv('mastodon_host'), hashtag, post_id), headers=headers) # pylint: disable=line-too-long
json_data = r.json()
json_length = len(json_data)
# if the id's match, then the json array is empty (length of 0)
# otherwise there is a new post
if json_length > 0:
  post_id = str(json_data[0]['id'])
  print("A new #%s post from @%s:" % (hashtag, str(json_data[0]['account']['acct'])))
  print(re.sub('<[^>]+>', '', json_data[0]['content']))
  print()
else:
  print("no new #%s posts" % hashtag)
  print(json_length)
  print()

This guide was first published on Dec 06, 2022. It was last updated on Dec 21, 2022.

This page (Track a Hashtag) was last updated on Mar 27, 2023.

Text editor powered by tinymce.