This project is coded using CircuitPython which makes it super simple to edit. There are a couple of customisations you can update to make this project fit your needs. If you also want to change the style and type of LED animations, there is a guide on using the CircuitPython LED Animation library that covers all the features. This guide does not go into customising the animations.

This page will show you how to customise a couple of things in the code. However, before the code will run, you need to load the LED Animation library onto your CIRCUITPY drive.

If you're having difficulty running this example, it could be because your MagTag CircuitPython firmware or library needs to be upgraded! Please be sure to follow https://learn.adafruit.com/adafruit-magtag/circuitpython to install the latest CircuitPython firmware and then also replace/update ALL the MagTag-specific libraries mentioned here https://learn.adafruit.com/adafruit-magtag/circuitpython-libraries-2

Install Code and Libraries

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_Cheerlights_LED_Animations/ 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:

CIRCUITPY

Code

# SPDX-FileCopyrightText: 2020 Kattni Rembor, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
import time
import board
import neopixel
from adafruit_magtag.magtag import MagTag

from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.sparkle import Sparkle
from adafruit_led_animation.animation.chase import Chase
from adafruit_led_animation.animation.blink import Blink
from adafruit_led_animation.animation.pulse import Pulse
from adafruit_led_animation.sequence import AnimationSequence
from adafruit_led_animation.group import AnimationGroup
from adafruit_led_animation.color import RED, GREEN, BLUE, CYAN, WHITE,\
    OLD_LACE, PURPLE, MAGENTA, YELLOW, ORANGE, PINK

# =============== CUSTOMISATIONS ================
# The strip LED brightness, where 0.0 is 0% (off) and 1.0 is 100% brightness, e.g. 0.3 is 30%
strip_pixel_brightness = 1
# The MagTag LED brightness, where 0.0 is 0% (off) and 1.0 is 100% brightness, e.g. 0.3 is 30%.
magtag_pixel_brightness = 0.5

# The rate interval in seconds at which the Cheerlights data is fetched.
# Defaults to one minute (60 seconds).
refresh_rate = 60
# ===============================================

# Set up where we'll be fetching data from
DATA_SOURCE = "http://api.thingspeak.com/channels/1417/field/1/last.json"
COLOR_LOCATION = ['field1']
DATE_LOCATION = ['created_at']

magtag = MagTag(
    url=DATA_SOURCE,
    json_path=(COLOR_LOCATION, DATE_LOCATION),
)
magtag.network.connect()

strip_pixels = neopixel.NeoPixel(board.D10, 30, brightness=strip_pixel_brightness)
magtag_pixels = magtag.peripherals.neopixels
magtag_pixels.brightness = magtag_pixel_brightness

# Cheerlights color
magtag.add_text(
    text_scale=2,
    text_position=(10, 15),
    text_transform=lambda x: "New Color: {}".format(x),  # pylint: disable=unnecessary-lambda
)
# datestamp
magtag.add_text(
    text_scale=2,
    text_position=(10, 65),
    text_transform=lambda x: "Updated on:\n{}".format(x),  # pylint: disable=unnecessary-lambda
)

timestamp = None
while True:
    if not timestamp or ((time.monotonic() - timestamp) > refresh_rate):  # Refresh rate in seconds
        try:
            # Turn on the MagTag NeoPixels.
            magtag.peripherals.neopixel_disable = False

            # Fetch the color and datetime, and print it to the serial console.
            cheerlights_update_info = magtag.fetch()
            print("Response is", cheerlights_update_info)

            # Get the color from the fetched info for use below.
            cheerlights_color = cheerlights_update_info[0]

            # If red, blue or pink, do a comet animation in the specified color.
            if cheerlights_color in ('red', 'blue', 'pink'):
                if cheerlights_color == 'red':
                    comet_color = RED
                elif cheerlights_color == 'blue':
                    comet_color = BLUE
                elif cheerlights_color == 'pink':
                    comet_color = PINK
                animations = AnimationSequence(
                    AnimationGroup(
                        Comet(magtag_pixels, 0.3, color=comet_color, tail_length=3),
                        Comet(strip_pixels, 0.05, color=comet_color, tail_length=15),
                    )
                )

            # If green or orange, do a pulse animation in the specified color.
            if cheerlights_color in ('green', 'orange'):
                if cheerlights_color == 'green':
                    pulse_color = GREEN
                elif cheerlights_color == 'orange':
                    pulse_color = ORANGE
                animations = AnimationSequence(
                    AnimationGroup(
                        Pulse(magtag_pixels, speed=0.1, color=pulse_color, period=3),
                        Pulse(strip_pixels, speed=0.1, color=pulse_color, period=3),
                    )
                )

            # If oldlace, warmmwhite or magenta, do a sparkle animation in the specified color.
            if cheerlights_color in ('oldlace', 'warmwhite', 'magenta'):
                if cheerlights_color in ('oldlace', 'warmwhite'):
                    sparkle_color = OLD_LACE
                elif cheerlights_color == 'magenta':
                    sparkle_color = MAGENTA
                animations = AnimationSequence(
                    AnimationGroup(
                        Sparkle(magtag_pixels, speed=0.1, color=sparkle_color, num_sparkles=1),
                        Sparkle(strip_pixels, speed=0.01, color=sparkle_color, num_sparkles=15),
                    )
                )

            # If cyan or purple, do a blink animation in the specified color.
            if cheerlights_color in ('cyan', 'purple'):
                if cheerlights_color == 'cyan':
                    blink_color = CYAN
                elif cheerlights_color == 'purple':
                    blink_color = PURPLE
                animations = AnimationSequence(
                    AnimationGroup(
                        Blink(magtag_pixels, speed=0.5, color=blink_color),
                        Blink(strip_pixels, speed=0.5, color=blink_color),
                    )
                )

            # If white or yellow, do a chase animation in the specified color.
            if cheerlights_color in ('white', 'yellow'):
                if cheerlights_color == 'white':
                    chase_color = WHITE
                elif cheerlights_color == 'yellow':
                    chase_color = YELLOW
                animations = AnimationSequence(
                    AnimationGroup(
                        Chase(magtag_pixels, speed=0.1, size=2, spacing=1, color=chase_color),
                        Chase(strip_pixels, speed=0.1, size=3, spacing=2, color=chase_color),
                    )
                )

            timestamp = time.monotonic()
        except (ValueError, RuntimeError, ConnectionError, OSError) as e:
            # Catch any random errors so the code will continue running.
            print("Some error occured, retrying! -", e)
    try:
        # This runs the animations.
        animations.animate()
    except NameError:
        # If Cheerlights adds a color not included above, the code would fail. This allows it to
        # continue to retry until a valid color comes up.
        print("There may be a Cheerlights color not included above.")

Each time the MagTag fetches the current Cheerlights color, the current animation will pause. Once the information is fetched, the new animation will begin and the display will refresh. This is expected behavior. The following shows a transition from orange to white.

Customisations

There are a couple of things you can easily change in this code.

LED Brightness

You can set the brightness of both the MagTag LEDs and the strip separately. Depending on how you mounted the strip, you may want to decrease the brightness.

strip_pixel_brightness = 1
magtag_pixel_brightness = 0.5

NeoPixels can get really bright. The code defaults to 0.5, or 50% brightness, for the MagTag LEDs, and 1, or 100% brightness, for the strip. The strip cannot get any brighter than 100%, however you can increase the MagTag LED brightness number to make them brighter. You can decrease either of the numbers to make them dimmer, however, be aware that some of the colors look different when the LEDs are very dim.

Refresh Rate

You can also set the rate in seconds at which the MagTag fetches the Cheerlights data. Bear in mind that the animations pause during the data retrieval, so if you set the rate too low, your animations won't run for long between fetches. As well, it is not good for the display to refresh too frequently.

refresh_rate = 60

The refresh_rate defaults to 60 seconds, or one minute. You can increase or decrease this to fit your needs.

Walkthrough

Let's take a look at the code.

First, we set up where we'll be fetching the data from.

Cheerlights has three feeds available - one that provides the hex value of the latest color and the update time, one that provides the name of the latest color and the update time, and one that contains the entire feed of hex and color names and update times. This project uses the feed that provides the name of the latest color and update time because it does not require you to know hex colors, and makes the code easier to read.

DATA_SOURCE = "http://api.thingspeak.com/channels/1417/field/1/last.json"
COLOR_LOCATION = ['field1']
DATE_LOCATION = ['created_at']

Then we initialise the MagTag library, including the information needed to fetch the Cheerlights data, and we tell the board to connect to the network.

magtag = MagTag(
    url=DATA_SOURCE,
    json_path=(COLOR_LOCATION, DATE_LOCATION),
)
magtag.network.connect()

Next, we set up the external NeoPixel strip, and set the brightness specified at the beginning of the code on both the strip NeoPixels and the MagTag NeoPixels.

strip_pixels = neopixel.NeoPixel(board.D10, 30, brightness=strip_pixel_brightness)
magtag_pixels = magtag.peripherals.neopixels
magtag_pixels.brightness = magtag_pixel_brightness

Then we set up the text that will be display on the screen. By using the text_transform option, we can set up the display text in the beginning, and update it each time we fetch new data in the loop.

magtag.add_text(
    text_scale=2,
    text_position=(10, 15),
    text_transform=lambda x: "New Color: {}".format(x),
)
magtag.add_text(
    text_scale=2,
    text_position=(10, 65),
    text_transform=lambda x: "Updated on:\n{}".format(x),
)

The first thing inside the loop is checking whether it's the first time the loop has started or if the refresh_rate seconds (set in the beginning of the code - defaults to 60 seconds) has passed.

if not timestamp or ((time.monotonic() - timestamp) > refresh_rate):

The next block of code is wrapped in a try/except to avoid the code stopping if it experiences any errors with regards to fetching the data.

[...]
        try:
            [...]  # Main block of code is here.
        except (ValueError, RuntimeError) as e:
            print("Some error occured, retrying! -", e)

Inside, we first enable the NeoPixels by turning off magtag.neopixel_disable.

magtag.peripherals.neopixel_disable = False

Then we fetch the Cheerlights data, which returns a two-member list that includes the latest color, and the current date and time. For example, it appears as something like the following:

['white', '2020-12-02T22:52:45Z'].

We assign it to the cheerlights_update_info variable, and print that information to the serial console.

cheerlights_update_info = magtag.fetch()
print("Response is", cheerlights_update_info)

For the animations, we need only the color from the data we fetch. So, to make it easy to use the color, we assign the first member of the list (using [0]) to a variable called cheerlights_color.

cheerlights_color = cheerlights_update_info[0]

Then we begin using the colors to display animations. There are eleven colors available for Cheerlights, and five basic animations, so each animation applies to multiple colors. Each animation checks the cheerlights_color variable to see what the latest color is, and displays the matching animation. The same general format is used for every animation - therefore this will only cover the first animation.

This section of the code could be more succinct if the hex value feed was used. However, not everyone is familiar with hex colors, so this section is slightly longer-form to enhance simplicity and ease of understanding.

First, the code checks if the latest color is one that applies to that particular animation, in this case, is it red, blue or pink.

Then, it verifies the specific Cheerlights color, and sets the animation color to the appropriate color. In this case, for example, if the Cheerlights color is red, it sets comet_color = RED.

Last, it creates the animation, in this case the comet animation, along with any necessary animation settings. For more details on the animations or their settings, take a look at the CircuitPython LED Animation guide.

if cheerlights_color in ('red', 'blue', 'pink'):
    if cheerlights_color == 'red':
        comet_color = RED
    elif cheerlights_color == 'blue':
        comet_color = BLUE
    elif cheerlights_color == 'pink':
        comet_color = PINK
    animations = AnimationSequence(
        AnimationGroup(
            Comet(magtag_pixels, 0.3, color=comet_color, tail_length=3),
            Comet(strip_pixels, 0.05, color=comet_color, tail_length=15),
          )
        )

Finally, the code runs the animations. This code is also wrapped in a try/except. In the event that Cheerlights adds a new color, the code would fail - this ensures your code will continue and display the next time one of the current colors is fetched.

try:
    animations.animate()
except NameError:
    print("There may be a Cheerlights color not included above.")

That's all there is to displaying LED animations based on Cheerlights data!

This guide was first published on Dec 07, 2020. It was last updated on Dec 07, 2020.

This page (Code) was last updated on May 22, 2023.

Text editor powered by tinymce.