Before anything else, plug the battery into the MagTag. The jack is right next to the Adafruit logo on the back of the board. If you would like to use a double-sided adhesive, put it where you want the battery to go, press down firmly, then remove the piece of paper covering the exposed side and stick the battery to it.
To use with CircuitPython, you need to first install a few 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, open the directory oshwa_magtag_display/ and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your CIRCUITPY drive.
Your CIRCUITPY
You'll need to get an OSHWA API key and paste it in on line 34 of code.py.
# SPDX-FileCopyrightText: 2021 Eva Herrada for Adafruit Industries # # SPDX-License-Identifier: MIT import random import ssl import gc import wifi import socketpool import adafruit_requests from adafruit_magtag.magtag import MagTag # Get wifi details and more from a secrets.py file try: from secrets import secrets except ImportError: print("WiFi secrets are kept in secrets.py, please add them there!") raise # Initialize magtag object magtag = MagTag() magtag.set_background("bmps/oshwa_full.bmp") # Set up WiFi wifi.radio.connect(secrets["ssid"], secrets["password"]) print(f"Connected to {secrets['ssid']}!") print("My IP address is", wifi.radio.ipv4_address) socket = socketpool.SocketPool(wifi.radio) https = adafruit_requests.Session(socket, ssl.create_default_context()) # Paste your API token below TOKEN = "YOUR_API_TOKEN" def font_width_to_dict(font): # Reads the font file to determine how wide each character is # Used to avoid bad wrapping breaking the QR code chars = {} with open(font, "r") as file: for line in file: if "FONTBOUNDINGBOX" in line: size = int(line.split(" ")[1]) if "ENCODING" in line and "_ENCODING" not in line: character = chr(int(line.split(" ")[1][:-1])) chars[character] = None if "SWIDTH" in line: swidth = (int(line.split(" ")[1]) / 1000) * size if "DWIDTH" in line: chars[character] = int(int(line.split(" ")[1]) + swidth) return chars def wrap(text, max_width, max_lines, font): # Used to wrap the title and description to avoid breaking the QR code lines = [] ellipsis = 3 * font["."] line = "" line_width = 0 for word in text.split(" "): for character in word: line_width += font[character] if ( len(lines) + 1 != max_lines or sum(font[i] for i in word) + line_width <= max_width ): if line_width > max_width: print(str(line_width) + line) line_width = sum(font[i] for i in word) lines.append(line.strip()) line = word + " " break else: for char_1 in word: if line_width + ellipsis + font[char_1] > max_width: line = line + "..." print(str(line_width) + line) lines.append(line) return "\n".join(lines[:max_lines]) line = line + char_1 line_width += font[char_1] else: line = line + word + " " lines.append(line.strip()) return "\n".join(lines[:max_lines]) # Get first 300 items, saving only the OSHWA UIDs. The first 300 are also used to find the # number of requests that will need to be made. # This was done this way since if the items themselves were all asked for and stored, the MagTag # would run out of memory. If we just got the number of total projects and chose a random number, # that also wouldn't work as you can only get individual projects with an OSHWA UID and these UIDs # are prefixed by the country they were registered in, thus making getting it with a simple number # in-between 1 and the total number of registered projects impossible. URL = "https://certificationapi.oshwa.org/api/projects?limit=300" print(URL) payload = {} headers = {"Content-Type": "application/json", "Authorization": f"Bearer {TOKEN}"} oshwaID = [] print("Getting number of projects and first set of 300 projects") with https.get(URL, headers=headers, data=payload) as response: R_JSON = response.json() total = int(R_JSON["total"]) print(f"{total} Projects") for i in R_JSON["items"]: oshwaID.append(i["oshwaUid"]) R_JSON.clear() R_JSON = None gc.collect() # Gets the rest of the OSHWA UIDs print(len(oshwaID)) for i in range(int(total / 300)): print(f"Getting request {i+2}") url = ( f"https://certificationapi.oshwa.org/api/projects?limit=300&offset={3*(i+1)}00" ) with https.get(url, headers=headers, data=payload) as response: R_JSON = response.json() for item in R_JSON["items"]: oshwaID.append(item["oshwaUid"]) R_JSON.clear() R_JSON = None gc.collect() print(f"{len(oshwaID)} IDs gathered") # Select the UID that will be displayed selected = random.choice(oshwaID) # Get the project that will be displayed url = f"https://certificationapi.oshwa.org/api/projects/{selected}" response = https.get(url, headers=headers, data=payload) selected = response.json()[0] # Filters out characters that the API or the MagTag itself isn't handling correctly for char in range(1, 32): selected["projectDescription"].replace(chr(char), "") selected["projectDescription"] = ( selected["projectDescription"] .replace("'", "'") .replace("&#x27;", "'") .replace("/", "/") .replace(""", '"') .replace("’", "'") ) # Add the two text fields magtag.add_text( text_font="fonts/Arial-Bold-12.bdf", text_position=(5, 0), text_scale=1, line_spacing=0.7, text_anchor_point=(0, 0), ) magtag.add_text( text_font="fonts/ArialMT-9.bdf", text_position=(5, 38), text_scale=1, line_spacing=0.6, text_anchor_point=(0, 0), ) # Create the QR code url = f"https://certification.oshwa.org/{selected['oshwaUid'].lower()}.html" magtag.graphics.qrcode(url, qr_size=4, x=173, y=3) # Prepare to wrap the text correctly by getting the width of each character for every font arial_12 = font_width_to_dict("fonts/Arial-Bold-12.bdf") arial_9 = font_width_to_dict("fonts/ArialMT-9.bdf") # Set the text. On some characters, this fails. If so, run the whole file again in 5 seconds try: magtag.set_text(wrap(selected["projectName"], 545, 2, arial_12), 0, False) magtag.set_text(wrap(selected["projectDescription"], 530, 19, arial_9), 1) magtag.exit_and_deep_sleep(3600) except Exception: # pylint: disable=broad-except print("Could not set title or description: unsupported glyphs.") print("Trying again in 10 seconds.") magtag.exit_and_deep_sleep(10)
First, the code imports all the required libraries.
import random import ssl import gc import wifi import socketpool import adafruit_requests as requests from adafruit_magtag.magtag import MagTag
Next, the code gets the WiFi details and Adafruit IO API token from the secrets.py file.
try: from secrets import secrets except ImportError: print("WiFi secrets are kept in secrets.py, please add them there!") raise
Now, the MagTag
object is initialized.
magtag = MagTag() magtag.set_background("bmps/oshwa_full.bmp")
After that, the WiFi is set up and connected to.
# Set up WiFi wifi.radio.connect(secrets["ssid"], secrets["password"]) print(f"Connected to {secrets['ssid']}!") print("My IP address is", wifi.radio.ipv4_address) socket = socketpool.SocketPool(wifi.radio) https = requests.Session(socket, ssl.create_default_context())
This is the part where you'll paste your API token. If you haven't already done so, you can get it here.
# Paste your API token below TOKEN = "YOUR_API_TOKEN"
The next two functions are used to wrap the description text by pixels instead of characters. This is because when it was being done by characters, the number of lines would vary greatly and the lines would sometimes go into the QR code, making it unreadable. This first function opens the font file and gets the width of every character.
def font_width_to_dict(font): # Reads the font file to determine how wide each character is # Used to avoid bad wrapping breaking the QR code chars = {} with open(font, "r") as file: for line in file: if "FONTBOUNDINGBOX" in line: size = int(line.split(" ")[1]) if "ENCODING" in line and "_ENCODING" not in line: character = chr(int(line.split(" ")[1][:-1])) chars[character] = None if "SWIDTH" in line: swidth = (int(line.split(" ")[1]) / 1000) * size if "DWIDTH" in line: chars[character] = int(int(line.split(" ")[1]) + swidth) return chars
The second function does the actual wrapping of the text. It takes the text to format, the max width of the line, the max number of lines, as well as the dictionary that the previous function returns.
def wrap(text, max_width, max_lines, font): # Used to wrap the title and description to avoid breaking the QR code lines = [] ellipsis = 3 * font["."] line = "" line_width = 0 for word in text.split(" "): for character in word: line_width += font[character] if ( len(lines) + 1 != max_lines or sum(font[i] for i in word) + line_width <= max_width ): if line_width > max_width: print(str(line_width) + line) line_width = sum(font[i] for i in word) lines.append(line.strip()) line = word + " " break else: for char_1 in word: if line_width + ellipsis + font[char_1] > max_width: line = line + "..." print(str(line_width) + line) lines.append(line) return "\n".join(lines[:max_lines]) line = line + char_1 line_width += font[char_1] else: line = line + word + " " lines.append(line.strip()) return "\n".join(lines[:max_lines])
This next bit of code gets first 300 items, saving only the OSHWA UIDs. The first 300 are also used to find the number of requests that will need to be made. This was done since if the items themselves were all stored at once, the MagTag would run out of memory. If we just got the number of total projects and chose a random number, that also wouldn't work as you can only get individual projects with an OSHWA UID and these UIDs are prefixed by the country they were registered in, thus making getting it with a simple number in-between 1 and the total number of registered projects impossible unless you only wanted to get projects registered in one country.
URL = "https://certificationapi.oshwa.org/api/projects?limit=300" print(URL) payload = {} headers = {"Content-Type": "application/json", "Authorization": f"Bearer {TOKEN}"} oshwaID = [] print("Getting number of projects and first set of 300 projects") with https.get(URL, headers=headers, data=payload) as response: R_JSON = response.json() total = int(R_JSON["total"]) print(f"{total} Projects") for i in R_JSON["items"]: oshwaID.append(i["oshwaUid"]) R_JSON.clear() R_JSON = None gc.collect()
This loop gets the rest of the OSHWA UIDs in a way that won't fill up all the memory of the MagTag.
print(len(oshwaID)) for i in range(int(total / 300)): print(f"Getting request {i+2}") url = ( f"https://certificationapi.oshwa.org/api/projects?limit=300&offset={3*(i+1)}00" ) with https.get(url, headers=headers, data=payload) as response: R_JSON = response.json() for item in R_JSON["items"]: oshwaID.append(item["oshwaUid"]) R_JSON.clear() R_JSON = None gc.collect() print(f"{len(oshwaID)} IDs gathered")
Next, a random UID from the list of OSHWA UIDs is chosen, and a request is sent over the API for that specific project.
selected = random.choice(oshwaID) url = f"https://certificationapi.oshwa.org/api/projects/{selected}" response = https.get(url, headers=headers, data=payload) selected = response.json()[0]
The combination of the OSHWA API not handling all non-alphanumeric characters correctly and the fonts used on the MagTag only having a subset of all characters that could have been used results in the need to remove all the special characters and replace all the punctuation marks that aren't being handled correctly.
# Filters out characters that the API or the MagTag itself isn't handling correctly for char in range(1, 32): selected["projectDescription"].replace(chr(char), "") selected["projectDescription"] = ( selected["projectDescription"] .replace("'", "'") .replace("&#x27;", "'") .replace("/", "/") .replace(""", '"') .replace("’", "'") )
Now, the code creates the two text fields. The first one is for the title, and the second one is for the description.
# Add the two text fields magtag.add_text( text_font="fonts/Arial-12.bdf", text_position=(5, -2), text_scale=1, line_spacing=0.6, text_anchor_point=(0, 0), ) magtag.add_text( text_font="fonts/ArialMT-9.bdf", text_position=(5, 30), text_scale=1, line_spacing=0.6, text_anchor_point=(0, 0), )
After that, the QR code is created and added to the display.
# Create the QR code url = f"https://certification.oshwa.org/{selected['oshwaUid'].lower()}.html" magtag.graphics.qrcode(url, qr_size=4, x=173, y=3)
Before adding the text, the code prepares to wrap it correctly by getting the width of each character for the two fonts that will be used.
arial_12 = font_width_to_dict("fonts/Arial-12.bdf") arial_9 = font_width_to_dict("fonts/ArialMT-9.bdf")
Finally, the code sets the text. On some characters which aren't included in the fonts for memory reasons, this fails. If this occurs, the code runs again in 5 seconds.
try: magtag.set_text(wrap(selected["projectName"], 530, 2, arial_12), 0, False) magtag.set_text(wrap(selected["projectDescription"], 530, 10, arial_9), 1) magtag.exit_and_deep_sleep(3600) except Exception: # pylint: disable=broad-except print("Could not set title or description: unsupported glyphs.") print("Trying again in 10 seconds.") magtag.exit_and_deep_sleep(10)
Text editor powered by tinymce.