GEMMA M0 boards can run CircuitPython — a different approach to programming compared to Arduino sketches. In fact, CircuitPython comes factory pre-loaded on GEMMA M0. If you’ve overwritten it with an Arduino sketch, or just want to learn the basics of setting up and using CircuitPython, this is explained in the Adafruit GEMMA M0 guide.
Below is CircuitPython code that works similarly (though not exactly the same) as the Arduino sketch shown on a prior page. To use this, plug the GEMMA M0 into USB…it should show up on your computer as a small flash drive…then edit the file “main.py” with your text editor of choice. Select and copy the code below and paste it into that file, entirely replacing its contents (don’t mix it in with lingering bits of old code). When you save the file, the code should start running almost immediately (if not, see notes at the bottom of this page).
If GEMMA M0 doesn’t show up as a drive, follow the GEMMA M0 guide link above to prepare the board for CircuitPython.
# SPDX-FileCopyrightText: 2018 Phillip Burgess for Adafruit Industries # # SPDX-License-Identifier: MIT # Gemma "Firewalker Lite" sneakers # - Uses the following Adafruit parts (X2 for two shoes): # * Gemma M0 3V microcontroller (#3501) # * 150 mAh LiPoly battery (#1317) or larger # * Medium vibration sensor switch (#2384) # * 60/m NeoPixel RGB LED strip (#1138 or #1461) # * LiPoly charger such as #1304 # # - originally written by Phil Burgess for Gemma using Arduino # * https://learn.adafruit.com/gemma-led-sneakers import board import digitalio import neopixel try: import urandom as random except ImportError: import random # Declare a NeoPixel object on led_pin with num_leds as pixels # No auto-write. led_pin = board.D1 # Which pin your pixels are connected to num_leds = 40 # How many LEDs you have circumference = 40 # Shoe circumference, in pixels, may be > NUM_LEDS frames_per_second = 50 # Animation frames per second brightness = 0 # Current wave height strip = neopixel.NeoPixel(led_pin, num_leds, brightness=1, auto_write=False) offset = 0 # vibration sensor motion_pin = board.D0 # Pin where vibration switch is connected pin = digitalio.DigitalInOut(motion_pin) pin.direction = digitalio.Direction.INPUT pin.pull = digitalio.Pull.UP ramping_up = False center = 0 # Center point of wave in fixed-point space (0 - 255) speed = 1 # Distance to move between frames (-128 - +127) width = 2 # Width from peak to bottom of triangle wave (0 - 128) hue = 3 # Current wave hue (color) see comments later hue_target = 4 # Final hue we're aiming for red = 5 # LED RGB color calculated from hue green = 6 # LED RGB color calculated from hue blue = 7 # LED RGB color calculated from hue y = 0 brightness = 0 count = 0 # Gemma can animate 3 of these on 40 LEDs at 50 FPS # More LEDs and/or more waves will need lower wave = [0] * 8, [0] * 8, [0] * 8 # Note that the speeds of each wave are different prime numbers. # This avoids repetition as the waves move around the # perimeter...if they were even numbers or multiples of each # other, there'd be obvious repetition in the pattern of motion... # beat frequencies. n_waves = len(wave) # 90 distinct hues (0-89) around color wheel hue_table = [255, 255, 255, 255, 255, 255, 255, 255, 237, 203, 169, 135, 101, 67, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 52, 86, 120, 154, 188, 222, 255, 255, 255, 255, 255, 255, 255, 255] # Gamma-correction table gammas = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114, 115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175, 177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, 215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255 ] def h2rgb(colour_hue): colour_hue %= 90 h = hue_table[colour_hue >> 1] if colour_hue & 1: ret = h & 15 else: ret = (h >> 4) return ret * 17 # pylint: disable=global-statement def wave_setup(): global wave wave = [[0, 3, 60, 0, 0, 0, 0, 0], [0, -5, 45, 0, 0, 0, 0, 0], [0, 7, 30, 0, 0, 0, 0, 0]] # assign random starting colors to waves for wave_index in range(n_waves): current_wave = wave[wave_index] random_offset = random.randint(0, 90) current_wave[hue] = current_wave[hue_target] = 90 + random_offset current_wave[red] = h2rgb(current_wave[hue] - 30) current_wave[green] = h2rgb(current_wave[hue]) current_wave[blue] = h2rgb(current_wave[hue] + 30) def vibration_detector(): while True: if not pin.value: return True while True: # wait for vibration sensor to trigger if not ramping_up: ramping_up = vibration_detector() wave_setup() # But it's not just a straight shot that it ramps up. # This is a low-pass filter...it makes the brightness # value decelerate as it approaches a target (200 in # this case). 207 is used here because integers round # down on division and we'd never reach the target; # it's an ersatz ceil() function: ((199*7)+200+7)/8 = 200; brightness = int(((brightness * 7) + 207) / 8) count += 1 if count == (circumference + num_leds + 5): ramping_up = False count = 0 # Wave positions and colors are updated... for w in range(n_waves): # Move wave; wraps around ends, is OK! wave[w][center] += wave[w][speed] # Hue not currently changing? if wave[w][hue] == wave[w][hue_target]: # There's a tiny random chance of picking a new hue... if not random.randint(frames_per_second * 4, 255): # Within 1/3 color wheel wave[w][hue_target] = random.randint( wave[w][hue] - 30, wave[w][hue] + 30) # This wave's hue is currently shifting... else: if wave[w][hue] < wave[w][hue_target]: wave[w][hue] += 1 # Move up or else: wave[w][hue] -= 1 # down as needed # Reached destination? if wave[w][hue] == wave[w][hue_target]: wave[w][hue] = 90 + wave[w][hue] % 90 # Clamp to 90-180 range wave[w][hue_target] = wave[w][hue] # Copy to target wave[w][red] = h2rgb(wave[w][hue] - 30) wave[w][green] = h2rgb(wave[w][hue]) wave[w][blue] = h2rgb(wave[w][hue] + 30) # Now render the LED strip using the current # brightness & wave states. # Each LED in strip is visited just once... for i in range(num_leds): # Transform 'i' (LED number in pixel space) to the # equivalent point in 8-bit fixed-point space (0-255) # "* 256" because that would be # the start of the (N+1)th pixel # "+ 127" to get pixel center. x = (i * 256 + 127) / circumference # LED assumed off, but wave colors will add up here r = g = b = 0 # For each item in wave[] array... for w_index in range(n_waves): # Calculate distance from pixel center to wave # center point, using both signed and unsigned # 8-bit integers... d1 = int(abs(x - wave[w_index][center])) d2 = int(abs(x - wave[w_index][center])) # Then take the lesser of the two, resulting in # a distance (0-128) # that 'wraps around' the ends of the strip as # necessary...it's a contiguous ring, and waves # can move smoothly across the gap. if d2 < d1: d1 = d2 # d1 is pixel-to-wave-center distance # d2 distance, relative to wave width, is then # proportional to the wave's brightness at this # pixel (basic linear y=mx+b stuff). # Is distance within wave's influence? # d2 is opposite; distance to wave's end if d1 < wave[w_index][width]: d2 = wave[w_index][width] - d1 y = int(brightness * d2 / wave[w_index][width]) # 0 to 200 # y is a brightness scale value -- # proportional to, but not exactly equal # to, the resulting RGB value. if y < 128: # Fade black to RGB color # In HSV colorspace, this would be # tweaking 'value' n = int(y * 2 + 1) # 1-256 r += (wave[w_index][red] * n) >> 8 # More fixed-point math # Wave color is scaled by 'n' g += (wave[w_index][green] * n) >> 8 b += (wave[w_index][blue] * n) >> 8 # >>8 is equiv to /256 else: # Fade RGB color to white # In HSV colorspace, this tweaks 'saturation' n = int((y - 128) * 2) # 0-255 affects white level m = 256 * n n = 256 - n # 1-256 affects RGB level r += (m + wave[w_index][red] * n) >> 8 g += (m + wave[w_index][green] * n) >> 8 b += (m + wave[w_index][blue] * n) >> 8 # r,g,b are 16-bit types that accumulate brightness # from all waves that affect this pixel; may exceed # 255. Now clip to 0-255 range: if r > 255: r = 255 if g > 255: g = 255 if b > 255: b = 255 # Store resulting RGB value and we're done with # this pixel! strip[i] = (r, g, b) # Once rendering is complete, a second pass is made # through pixel data applying gamma correction, for # more perceptually linear colors. # https://learn.adafruit.com/led-tricks-gamma-correction for j in range(num_leds): (red_gamma, green_gamma, blue_gamma) = strip[j] red_gamma = gammas[red_gamma] green_gamma = gammas[green_gamma] blue_gamma = gammas[blue_gamma] strip[j] = (red_gamma, green_gamma, blue_gamma) strip.show()
This code requires the neopixel.py library. A factory-fresh board will have this already installed. If you’ve just reloaded the board with CircuitPython, create the “lib” directory and then download neopixel.py from Github.
Text editor powered by tinymce.