The software for this project is written in CircuitPython so it's easy to run and modify, just write the code straight to the board's USB drive!  If you're new to CircuitPython and Gemma M0 first check out the Gemma M0 CircuitPython guide pages.  These will explain what CircuitPython is and how to load it onto your Gemma M0 board.

Before you continue make sure your Gemma M0 is running the latest version of CircuitPython by updating its UF2 firmware.  Also be sure you can see the board's CIRCUITPY drive when you connect it to your computer with a USB cable.  Once you see the CIRCUITPY drive you're all set and ready to load the project code.

Now you need to install two dependencies from the CircuitPython bundle.  Like the bundle install page mentions for non-express boards like Gemma M0 this means you have to download the bundle zip file, open it on your computer, and copy the following files and folders to your board's CIRCUITPY drive:

  • adafruit_dotstar.mpy
  • adafruit_fancyled

Be sure you've copied both these files and folders (and all the files inside the folders) to your CIRCUITPY drive before continuing!

# Cyber Flower: Digital Valentine
#
# 'Roses are red,
#  Violets are blue,
#  This flower changes color,
#  To show its love for you.'
#
# Load this on a Gemma M0 running CircuitPython and it will smoothly animate
# the DotStar LED between different color hues.  Touch the D0 pad and it will
# cause the pixel to pulse like a heart beat.  You might need to also attach a
# wire to the ground pin to ensure capacitive touch sensing can work on battery
# power.  For example strip the insulation from a wire and solder it to ground,
# then solder a wire (with the insulation still attached) to D0, and wrap
# both wires around the stem of a flower like a double-helix.  When you touch
# the wires you'll ground yourself (touching the bare ground wire) and cause
# enough capacitance in the D0 wire (even though it's still insulated) to
# trigger the heartbeat.  Or just leave D0 unconnected to have a nicely
# animated lit-up flower!
#
# Note that on power-up the flower will wait about 5 seconds before turning on
# the LED.  During this time the board's red LED will flash and this is an
# indication that it's waiting to power on.  Place the flower down so nothing
# is touching it and then pick it up again after the DotStar LED starts
# animating.  This will ensure the capacitive touch sensing isn't accidentally
# calibrated with your body touching it (making it less accurate).
#
# Also note this depends on two external modules to be loaded on the Gemma M0:
#  - Adafruit CircuitPython DotStar:
# https://github.com/adafruit/Adafruit_CircuitPython_DotStar
#  - Adafruit CircuitPython FancyLED:
# https://github.com/adafruit/Adafruit_CircuitPython_FancyLED
#
# You _must_ have both adafruit_dotstar.mpy and the adafruit_fancyled folder
# and files within it on your board for this code to work!  If you run into
# trouble or can't get the dependencies see the main_simple.py code as an
# alternative that has no dependencies but slightly more complex code.
#
# Author: Tony DiCola
# License: MIT License
import math
import time

import adafruit_dotstar
import adafruit_fancyled.adafruit_fancyled as fancy
import board
import digitalio
import touchio

# Variables that control the code.  Try changing these to modify speed, color,
# etc.
START_DELAY = 5.0  # How many seconds to wait after power up before
# jumping into the animation and initializing the
# touch input.  This gives you time to take move your
# fingers off the flower so the capacitive touch
# sensing is better calibrated.  During the delay
# the small red LED on the board will flash.

TOUCH_PIN = board.D0  # The board pin to listen for touches and trigger the
# heart beat animation.  You can change this to any
# other pin like board.D2 or board.D1.  Make sure not
# to touch this pin as the board powers on or the
# capacitive sensing will get confused (just reset
# the board and try again).

BRIGHTNESS = 1.0  # The brightness of the colors.  Set this to a value
# anywhere within 0 and 1.0, where 1.0 is full bright.
# For example 0.5 would be half brightness.

RAINBOW_PERIOD_S = 18.0  # How many seconds it takes for the default rainbow
# cycle animation to perform a full cycle.  Increase
# this to slow down the animation or decrease to speed
# it up.

HEARTBEAT_BPM = 60.0  # Heartbeat animation beats per minute.  Increase to
# speed up the heartbeat, and decrease to slow down.

HEARTBEAT_HUE = 300.0  # The color hue to use when animating the heartbeat
# animation.  Pick a value in the range of 0 to 359
# degrees, see the hue spectrum here:
#   https://en.wikipedia.org/wiki/Hue
# A value of 300 is a nice pink color.

# First initialize the DotStar LED and turn it off.
dotstar = adafruit_dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1)
dotstar[0] = (0, 0, 0)

# Also make sure the on-board red LED is turned off.
red_led = digitalio.DigitalInOut(board.L)
red_led.switch_to_output(value=False)

# Wait the startup delay period while flashing the red LED.  This gives time
# to move your hand away from the flower/stem so the capacitive touch sensing
# is initialized and calibrated with a good non-touch starting state.
start = time.monotonic()
while time.monotonic() - start <= START_DELAY:
    # Blink the red LED on and off every half second.
    red_led.value = True
    time.sleep(0.5)
    red_led.value = False
    time.sleep(0.5)

# Setup the touch input.
touch = touchio.TouchIn(TOUCH_PIN)

# Convert periods to frequencies that are used later in animations.
rainbow_freq = 1.0 / RAINBOW_PERIOD_S

# Calculcate periods and values used by the heartbeat animation.
beat_period = 60.0 / HEARTBEAT_BPM
beat_quarter_period = beat_period / 4.0  # Quarter period controls the speed of
# the heartbeat drop-off (using an
# exponential decay function).
beat_phase = beat_period / 5.0  # Phase controls how long in-between


# the two parts of the heart beat
# (the 'ba-boom' of the beat).

# Handy function for linear interpolation of a value.  Pass in a value
# x that's within the range x0...x1 and a range y0...y1 to get an output value
# y that's proportionally within y0...y1 based on x within x0...x1.  Handy for
# transforming a value in one range to a value in another (like Arduino's map
# function).

# pylint: disable=redefined-outer-name
def lerp(x, x0, x1, y0, y1):
    return y0 + (x - x0) * ((y1 - y0) / (x1 - x0))


# Main loop below will run forever:
while True:
    # Get the current time at the start of the animation update.
    current = time.monotonic()
    # Now check if the touch input is being touched and choose a different
    # animation to run, either a rainbow cycle or heartbeat.
    if touch.value:
        # The touch input is being touched, so figure out the color using
        # a heartbeat animation.
        # This works using exponential decay of the color value (brightness)
        # over time:
        #   https://en.wikipedia.org/wiki/Exponential_decay
        # A heart beat is made of two sub-beats (the 'ba-boom') so two decay
        # functions are calculated using the same fall-off period but slightly
        # out of phase so one occurs a little bit after the other.
        t0 = current % beat_period
        t1 = (current + beat_phase) % beat_period
        x0 = math.pow(math.e, -t0 / beat_quarter_period)
        x1 = math.pow(math.e, -t1 / beat_quarter_period)
        # After calculating both exponential decay values pick the biggest one
        # as the secondary one will occur after the first.  Scale each by
        # the global brightness and then convert to RGB color using the fixed
        # hue but modulating the color value (brightness).  Luckily the result
        # of the exponential decay is a value that goes from 1.0 to 0.0 just
        # like we expect for full bright to zero brightness with HSV color
        # (i.e. no interpolation is necessary).
        val = max(x0, x1) * BRIGHTNESS
        color = fancy.gamma_adjust(fancy.CHSV(HEARTBEAT_HUE / 359.0, 1.0, val))
        dotstar[0] = color.pack()
    else:
        # The touch input is not being touched (touch.value is False) so
        # compute the hue with a smooth cycle over time.
        # First use the sine function to smoothly generate a value that goes
        # from -1.0 to 1.0 at a certain frequency to match the rainbow period.
        x = math.sin(2.0 * math.pi * rainbow_freq * current)
        # Then compute the hue by converting the sine wave value from something
        # that goes from -1.0 to 1.0 to instead go from 0 to 1.0 hue.
        hue = lerp(x, -1.0, 1.0, 0.0, 1.0)
        # Finally update the DotStar LED by converting the HSV color at the
        # specified hue to a RGB color the LED understands.
        color = fancy.gamma_adjust(fancy.CHSV(hue, 1.0, BRIGHTNESS))
        dotstar[0] = color.pack()

Copy this code.py to your Gemma M0's CIRCUITPY drive.  Make sure to overwrite any main.py that's already on the board!

At this point be sure your CIRCUITPY drive has the cyber flower code.py, adafruit_dotstar.mpy, and adafruit_fancyled files and folder on it.  You can delete any other files and folders on the drive (boot_out.txt is created by the board with debug data, you can ignore it):

CIRCUITPY

The flower code might start running as soon as you copy the file to your board.  However just to be sure you can eject the CIRCUITPY drive from your computer, then press the board's reset button and the flower code should start running.

When the flower code first starts it will wait 5 seconds and flash the Gemma M0's tiny red LED on and off.  This is a short delay that gives you time to stop touching the board and the D0 and ground wires.  For the capacitive touch sensing to work you want the board to be 'calibrated' with nothing touching it, so during the initial startup delay put the flower down so nothing is touching it.  Then after 5 seconds the red LED will stop flashing and the DotStar LED will turn on to animate different colors.  At this point the flower code is running--try picking up the flower or touching the D0 wire to see the heart beat animation!

Note that if the flower is only powered by a battery (i.e. not connected to your computer) you might need to touch both the ground wire and D0 wire to trigger the heart beat animation.  This is why assembling the flower with a bare / uninsulated wire connected to ground is best, it allows someone to touch the flower and simultaneously ground themselves and trigger the capacitive sensing.

Also be aware if you have trouble getting all the dependencies copied to your board and working, there's a variant that does not have any dependencies called main_simple.py  You can download main_simple.py and rename it to code.py, then copy to your Gemma M0's CIRCUITPY drive.  There are no other dependencies needed to use this version of the code (but it's a little harder to read and follow compared to the main code above).

Modify The Flower Code

Out of the box the flower code should work great without any modifications.  However since it's all simple Python code you can easily change it with any text editor.  The code is written with a few variables at the top that you can change to modify how the flower works.  First make sure you're familiar with editing and running code on a CircuitPython board.

If you open the flower code.py on the CIRCUITPY drive, notice these values at the top:

# Variables that control the code.  Try changing these to modify speed, color,
# etc.
START_DELAY = 5.0       # How many seconds to wait after power up before
                        # jumping into the animation and initializing the
                        # touch input.  This gives you time to take move your
                        # fingers off the flower so the capacitive touch
                        # sensing is better calibrated.  During the delay
                        # the small red LED on the board will flash.

TOUCH_PIN = board.D0    # The board pin to listen for touches and trigger the
                        # heart beat animation.  You can change this to any
                        # other pin like board.D2 or board.D1.  Make sure not
                        # to touch this pin as the board powers on or the
                        # capacitive sensing will get confused (just reset
                        # the board and try again).

BRIGHTNESS = 1.0        # The brightness of the colors.  Set this to a value
                        # anywhere within 0 and 1.0, where 1.0 is full bright.
                        # For example 0.5 would be half brightness.

RAINBOW_PERIOD_S = 18.0 # How many seconds it takes for the default rainbow
                        # cycle animation to perform a full cycle.  Increase
                        # this to slow down the animation or decrease to speed
                        # it up.

HEARTBEAT_BPM = 60.0    # Heartbeat animation beats per minute.  Increase to
                        # speed up the heartbeat, and decrease to slow down.

HEARTBEAT_HUE = 300.0   # The color hue to use when animating the heartbeat
                        # animation.  Pick a value in the range of 0 to 359
                        # degrees, see the hue spectrum here:
                        #   https://en.wikipedia.org/wiki/Hue

On the right you can see comments (text after a # character) which describe the values.  Try changing one of these, like the HEARTBEAT_HUE value at the bottom.  If you change HEARTBEAT_HUE from 300.0 to 180.0 it will change the color of the heart beat animation to a cyan/blue color.  Modify the value and save the code.py file on your CIRCUITPY drive.  The board should see the changed code and start running it again (remember it has a 5 second startup delay where it blinks the red LED).  If you touch the D0 pad now the heart beat should be cyan/blue--cool!

Experiment with changing other values like HEARTBEAT_BPM to change how fast the heart beat animation runs, or RAINBOW_PERIOD_S to change how long it takes for the DotStar to cycle through a rainbow of colors.  Each time you change a value just save the code.py on the board again and it should start running with the new code!

That's all there is to the cyber flower digital valentine project!

This guide was first published on Feb 05, 2018. It was last updated on Feb 05, 2018.

This page (Software) was last updated on Aug 13, 2021.

Text editor powered by tinymce.