Installing Project Code

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 MagTag_CovidTracking/ 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 drive should now look similar to the following image:

# SPDX-FileCopyrightText: 2020 ladyada, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
import time
import alarm
import supervisor
import alarm
from adafruit_magtag.magtag import MagTag

# Change this to the hour you want to check the data at, for us its 8pm
# local time (eastern), which is 20:00 hrs
DAILY_UPDATE_HOUR = 20

# Set up where we'll be fetching data from
DATA_SOURCE = "https://api.covidtracking.com/v1/us/current.json"
DATE_LOCATION = [0, 'dateChecked']
NEWPOS_LOCATION = [0, 'positiveIncrease']
CURRHOSP_LOCATION = [0, 'hospitalizedCurrently']
NEWHOSP_LOCATION = [0, 'hospitalizedIncrease']
ALLDEATH_LOCATION = [0, 'death']
NEWDEATH_LOCATION = [0, 'deathIncrease']

magtag = MagTag(
    url=DATA_SOURCE,
    json_path=(DATE_LOCATION, NEWPOS_LOCATION,
               CURRHOSP_LOCATION, NEWHOSP_LOCATION,
               ALLDEATH_LOCATION, NEWDEATH_LOCATION),
)


# Date stamp of info
magtag.add_text(
    text_font="/fonts/Arial-Bold-12.pcf",
    text_position=(10, 15),
    text_transform=lambda x: "Date: {}".format(x[0:10]),
)
# Positive increase
magtag.add_text(
    text_font="/fonts/Arial-Bold-12.pcf",
    text_position=(10, 35),
    text_transform=lambda x: "New positive:   {:,}".format(x),
)
# Curr hospitalized
magtag.add_text(
    text_font="/fonts/Arial-Bold-12.pcf",
    text_position=(10, 55),
    text_transform=lambda x: "Current Hospital:   {:,}".format(x),
)
# Change in hospitalized
magtag.add_text(
    text_font="/fonts/Arial-Bold-12.pcf",
    text_position=(10, 75),
    text_transform=lambda x: "Change in Hospital:   {:,}".format(x),
)
# All deaths
magtag.add_text(
    text_font="/fonts/Arial-Bold-12.pcf",
    text_position=(10, 95),
    text_transform=lambda x: "Total deaths:   {:,}".format(x),
)
# new deaths
magtag.add_text(
    text_font="/fonts/Arial-Bold-12.pcf",
    text_position=(10, 115),
    text_transform=lambda x: "New deaths:   {:,}".format(x),
)

# updated time
magtag.add_text(
    text_font="/fonts/Arial-Bold-12.pcf",
    text_position=(245, 30),
    line_spacing=0.75,
    is_data=False
)

magtag.graphics.qrcode(b"https://covidtracking.com/data",
                       qr_size=2, x=240, y=70)

magtag.peripherals.neopixels.brightness = 0.1
magtag.peripherals.neopixel_disable = False # turn on lights
magtag.peripherals.neopixels.fill(0x0F0000) # red!

magtag.get_local_time()
try:
    now = time.localtime()
    print("Now: ", now)

    # display the current time since its the last-update
    updated_at = "%d/%d\n%d:%02d" % now[1:5]
    magtag.set_text(updated_at, 6, False)

    # get data from the Covid Tracking Project
    value = magtag.fetch()
    print("Response is", value)

    # OK we're done!
    magtag.peripherals.neopixels.fill(0x000F00) # greten
except (ValueError, RuntimeError, ConnectionError, OSError) as e:
    print("Some error occured, trying again later -", e)

time.sleep(2) # let screen finish updating

# we only wanna wake up once a day, around the event update time:
event_time = time.struct_time((now[0], now[1], now[2],
                               DAILY_UPDATE_HOUR, 0, 0,
                               -1, -1, now[8]))
# how long is that from now?
remaining = time.mktime(event_time) - time.mktime(now)
if remaining < 0:             # ah its aready happened today...
    remaining += 24 * 60 * 60 # wrap around to the next day
remaining_hrs = remaining // 3660
remaining_min = (remaining % 3600) // 60
print("Gonna zzz for %d hours, %d minutes" % (remaining_hrs, remaining_min))

# Turn it all off and go to bed till the next update time
magtag.exit_and_deep_sleep(remaining)

Code Walkthrough

Thankfully, the Covid Tracking Project folks made a free, easy-to-use JSON API endpoint that you can easily query with the MagTag to get the current stats at https://api.covidtracking.com/v1/us/current.json You can click on it right from your browser to see the current data and you'll get something like this

[
  {
    "date": 20201210,
    "states": 56,
    "positive": 15360841,
    "negative": 167187901,
    "pending": 12409,
    "hospitalizedCurrently": 107258,
    "hospitalizedCumulative": 603554,
    "inIcuCurrently": 21023,
    "inIcuCumulative": 32919,
    "onVentilatorCurrently": 7442,
    "onVentilatorCumulative": 3394,
    "recovered": 5985047,
    "dateChecked": "2020-12-10T24:00:00Z",
    "death": 283555,
    "hospitalized": 603554,
    "totalTestResults": 213015816,
    "lastModified": "2020-12-10T24:00:00Z",
    "total": 0,
    "posNeg": 0,
    "deathIncrease": 3067,
    "hospitalizedIncrease": 4335,
    "negativeIncrease": 1339749,
    "positiveIncrease": 215669,
    "totalTestResultsIncrease": 1954686,
    "hash": "27226d54c5463327b7303d241b4085e06d976068"
  }
]

These are pretty self-explanatory, note that date is not the current date, but the date at which the data was collected. We'll be displaying positiveIncrease, hospitalizedCurrently, hospitalizedIncrease, death and deathIncrease but of course the display can be customized.

Daily Update Time

The report from CTP only gets updated once a day, so its a good project for MagTag to deep-sleep between reads. That way it can last many weeks on a charge. Data is updated at around 7:30pm eastern, which is our local time. To account for any delay in reporting, we will check every night at 8pm eastern. 8pm eastern in 24-hour-time numbers is 20:00, so we put in 20 as the daily-check-hour. If you're in another timezone, figure out what local time that translates to if you'd like to catch the report right after its posted.

# Change this to the hour you want to check the data at, for us its 8pm
# local time (eastern), which is 20:00 hrs
DAILY_UPDATE_HOUR = 20

Text Transforms with lambdas

The numbers we get from CTP need to have some text before them to explain what they are, for example "New Positive" before the number we extract from the json API as "negativeIncrease": 1339749

To do that we use an 'anonymous function' (a.k.a. a lambda) which we can code in-line that will take the string extracted "1339749" and preface it with the text "New positive:" as well as putting commas in between the digit groupings.

Thats what this line does:

text_transform=lambda x: "New positive: {:,}".format(x)

lambda x: is basically the same as making a new function with:

def anonymous_function(x):
     return "New positive: {:,}".format(x)

except it doesn't even have a name!

Adding a QR Code

For more info, you can visit the CTP website, we insert a QR code which will show on the E-Ink display very nicely and can be scanned to get the full report

magtag.graphics.qrcode(b"https://covidtracking.com/data", qr_size=2, x=240, y=70)

The 'qr_size' indicates the scaling, e.g. how big the pixel squares are. X and Y coordinates puts it over on the bottom right

Connect and Update

The main chunk of code turns on the NeoPixels red for an indicator that we're connecting to the Internet, gets the local time (we need this later), updates a text box in the top-right with the timestamp of the current date/time so you know when it was last running, then calls magtag.fetch() to update all the text boxes.

magtag.peripherals.neopixels.brightness = 0.1
magtag.peripherals.neopixel_disable = False # turn on lights
magtag.peripherals.neopixels.fill(0x0F0000) # red!

magtag.get_local_time()
try:
    now = time.localtime()
    print("Now: ", now)

    # display the current time since its the last-update
    updated_at = "%d/%d\n%d:%02d" % now[1:5]
    magtag.set_text(updated_at, 6, False)

    # get data from the Covid Tracking Project
    value = magtag.fetch()
    print("Response is", value)

    # OK we're done!
    magtag.peripherals.neopixels.fill(0x000F00) # greten
except (ValueError, RuntimeError) as e:
    print("Some error occured, trying again later -", e)

time.sleep(2) # let screen finish updating

After the display has updated, its time to go to sleep. For this project we want to wake up at exactly 8pm, which might be later today or it might be tomorrow. Since we don't know what day it will be, we first assume that we will be waking up later today by creating a new time structure with the same year, month, day as today but the hour, min, sec is DAILY_UPDATE_HOUR, 0, 0:

event_time = time.struct_time((now[0], now[1], now[2], DAILY_UPDATE_HOUR, 0, 0, -1, -1, now[8]))

Then we convert the current time, and the next-wakeup time to seconds using time.mktime() and subtract the difference. That is, what is the number of seconds between these two time points

remaining = time.mktime(event_time) - time.mktime(now)

If the amount of time is negative, that mean's that the "wake up today" time already happened, so we add 24-hours-worth of seconds to the time difference, to get to the event time one day later (tomorrow!)

if remaining < 0: # ah its aready happened today...
    remaining += 24 * 60 * 60 # wrap around to the next day

We then display that time in hours and minutes, just for us to debug as humans

remaining_hrs = remaining // 3660
remaining_min = (remaining % 3600) // 60
print("Gonna zzz for %d hours, %d minutes" % (remaining_hrs, remaining_min))

And finally, we go into deep sleep mode until that time comes up

# Turn it all off and go to bed till the next update time
magtag.exit_and_deep_sleep(remaining)

This guide was first published on Dec 11, 2020. It was last updated on Mar 28, 2024.

This page (Project Code) was last updated on Mar 27, 2024.

Text editor powered by tinymce.