Text Editor
Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.
Alternatively, you can use any text editor that saves files.
Installing Project Code
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 CLUE_Egg_Drop/ 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.
CIRCUITPY

# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries # # SPDX-License-Identifier: MIT import time import math import board from digitalio import DigitalInOut, Direction, Pull import pwmio from adafruit_lsm6ds.lsm6ds33 import LSM6DS33 from adafruit_lsm6ds import AccelRange, AccelHPF, Rate from adafruit_display_text import label import displayio import terminalio button_a = DigitalInOut(board.BUTTON_A) button_a.direction = Direction.INPUT button_a.pull = Pull.UP splash = displayio.Group() # bad egg BAD_EGG_FILENAME = "broken_egg.bmp" # CircuitPython 6 & 7 compatible begg_file = open(BAD_EGG_FILENAME, "rb") begg_bmp = displayio.OnDiskBitmap(begg_file) begg_sprite = displayio.TileGrid( begg_bmp, pixel_shader=getattr(begg_bmp, 'pixel_shader', displayio.ColorConverter()) ) # # CircuitPython 7+ compatible # begg_bmp = displayio.OnDiskBitmap(BAD_EGG_FILENAME) # begg_sprite = displayio.TileGrid(begg_bmp, pixel_shader=begg_bmp.pixel_shader) # good egg GOOD_EGG_FILENAME = "good_egg.bmp" # CircuitPython 6 & 7 compatible gegg_file = open(GOOD_EGG_FILENAME, "rb") gegg_bmp = displayio.OnDiskBitmap(gegg_file) gegg_sprite = displayio.TileGrid( gegg_bmp, pixel_shader=getattr(gegg_bmp, 'pixel_shader', displayio.ColorConverter()) ) # # CircuitPython 7+ compatible # gegg_bmp = displayio.OnDiskBitmap(GOOD_EGG_FILENAME) # gegg_sprite = displayio.TileGrid(gegg_bmp, pixel_shader=gegg_bmp.pixel_shader) # draw the bad egg! splash.append(begg_sprite) # draw the good egg on top splash.append(gegg_sprite) # Draw a label text_group = displayio.Group(scale=2, x=10, y=220) text = "Current & Max Acceleration" text_area = label.Label(terminalio.FONT, text=text, color=0x0000FF) text_group.append(text_area) # Subgroup for text scaling splash.append(text_group) # display everything so far board.DISPLAY.root_group = splash # connect to the accelerometer i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller sensor = LSM6DS33(i2c) # highest range for impacts! sensor.accelerometer_range = AccelRange.RANGE_16G # we'll read at about 1KHz sensor.accelerometer_rate = Rate.RATE_1_66K_HZ # Instead of raw accelerometer data, we'll read the *change* in acceleration (shock) sensor.high_pass_filter = AccelHPF.SLOPE sensor.high_pass_filter_enabled = True # and make a lil buzzer buzzer = pwmio.PWMOut(board.SPEAKER, variable_frequency=True) buzzer.frequency = 1000 max_slope = 0 egg_ok = True while True: # This isn't the acceleration but the SLOPE (change!) in acceleration x, y, z = sensor.acceleration # take the vector length by squaring, adding, taking root slope_g = math.sqrt(x*x + y*y + z*z) / 9.8 # take vector, convert to g # we'll track the max delta g if max_slope < slope_g: max_slope = slope_g print(slope_g) text_area.text = "Max Slope %0.1fg" % max_slope if max_slope >= 9 and egg_ok: gegg_sprite.x = 300 time.sleep(0.1) egg_ok = False buzzer.duty_cycle = 2**15 time.sleep(2) buzzer.duty_cycle = 0 continue if button_a.value is False and egg_ok is False: print("Reset") time.sleep(0.1) # debounce max_slope = 0 gegg_sprite.x = 0 egg_ok = True
How It Works
Library Import
First, the code imports libraries: time
, math
, board
, digitalio
(in this case for using the A button), pulseio
for sounding the buzzer, the accelerometer adafruit_lsm6
library, and, so we can display text and graphics on screen, adafruit_display_text
, displayio
and terminalio
.
import time import math import board from digitalio import DigitalInOut, Direction, Pull import pulseio from adafruit_lsm6ds import LSM6DS33, AccelRange, AccelHPF, Rate from adafruit_display_text import label import displayio import terminalio
button_a = DigitalInOut(board.BUTTON_A) button_a.direction = Direction.INPUT button_a.pull = Pull.UP
Display
To prepare the screen for bitmap display, we'll create a displayio.Group()
called splash
, and then create tiles for the two egg .bmp image files saved on the CLUE.
splash = displayio.Group() # bad egg BAD_EGG_FILENAME = "broken_egg.bmp" # CircuitPython 6 & 7 compatible begg_file = open(BAD_EGG_FILENAME, "rb") begg_bmp = displayio.OnDiskBitmap(begg_file) begg_sprite = displayio.TileGrid( begg_bmp, pixel_shader=getattr(begg_bmp, 'pixel_shader', displayio.ColorConverter()) ) # # CircuitPython 7+ compatible # begg_bmp = displayio.OnDiskBitmap(BAD_EGG_FILENAME) # begg_sprite = displayio.TileGrid(begg_bmp, pixel_shader=begg_bmp.pixel_shader) # good egg GOOD_EGG_FILENAME = "good_egg.bmp" # CircuitPython 6 & 7 compatible gegg_file = open(GOOD_EGG_FILENAME, "rb") gegg_bmp = displayio.OnDiskBitmap(gegg_file) gegg_sprite = displayio.TileGrid( gegg_bmp, pixel_shader=getattr(gegg_bmp, 'pixel_shader', displayio.ColorConverter()) ) # # CircuitPython 7+ compatible # gegg_bmp = displayio.OnDiskBitmap(GOOD_EGG_FILENAME) # gegg_sprite = displayio.TileGrid(gegg_bmp, pixel_shader=gegg_bmp.pixel_shader) # draw the bad egg! splash.append(begg_sprite) # draw the good egg on top splash.append(gegg_sprite)
Label
We'll also prepare the text display by creating a text group with the text "Current & Max Acceleration".
All of these elements will be drawn to the screen with the command board.DISPLAY.root_group = splash
# Draw a label text_group = displayio.Group(scale=2, x=10, y=220) text = "Current & Max Acceleration" text_area = label.Label(terminalio.FONT, text=text, color=0x0000FF) text_group.append(text_area) # Subgroup for text scaling splash.append(text_group) # display everything so far board.DISPLAY.root_group = splash
Accelerometer & Buzzer
The accelerometer object will be instantiated as sensor
connected over the I2C bus to the LSM6DS33 package. The range is set to 16G so it can read large impacts (poor egg!) read at a rate of 1.66KHz.
To filter out noise, the accelerometer's high pass filter is enabled.
We'll also set up the buzzer using pulseio with a frequency of 1000Hz.
# connect to the accelerometer sensor = LSM6DS33(board.I2C()) # highest range for impacts! sensor.accelerometer_range = AccelRange.RANGE_16G # we'll read at about 1KHz sensor.accelerometer_rate = Rate.RATE_1_66K_HZ # Instead of raw accelerometer data, we'll read the *change* in acceleration (shock) sensor.high_pass_filter = AccelHPF.SLOPE sensor.high_pass_filter_enabled = True # and make a lil buzzer buzzer = pulseio.PWMOut(board.SPEAKER, variable_frequency=True) buzzer.frequency = 1000
The max_slope
variable is created with an initial value of 0 -- this is used to measure the slope change in acceleration.
The egg_ok
variable is set to True and will be used to reset the board after a large impact beyond the maximum slope value of 9.
max_slope = 0 egg_ok = True
Main Loop
In the main loop of the program the accelerometer is read, and a slope_g
is determined by squaring, adding and taking the root of the accelerometer value.
When the egg drop CLUE is in freefall, we'll begin checking the max_slope
value acceleration -- if it is greater than 9, the simulated egg has probably cracked!
In this case, we move the good egg sprite off screen by 300 pixels, revealing the bad egg image. The buzzer will also sound for two seconds.
# This isn't the acceleration but the SLOPE (change!) in acceleration x, y, z = sensor.acceleration # take the vector length by squaring, adding, taking root slope_g = math.sqrt(x*x + y*y + z*z) / 9.8 # take vector, convert to g # we'll track the max delta g if max_slope < slope_g: max_slope = slope_g print(slope_g) text_area.text = "Max Slope %0.1fg" % max_slope if max_slope >= 9 and egg_ok: gegg_sprite.x = 300 time.sleep(0.1) egg_ok = False buzzer.duty_cycle = 2**15 time.sleep(2) buzzer.duty_cycle = 0 continue
Button Reset
Now, we can check the button_a.value
to see if it changes after the egg_ok
variable has been set to False
. Since the button is on a pull up resistor, it is normally True
until pressed. When pressed, the button value will go to False
and we can then reset the max_slope
value, and return the good egg image, and reset the egg_ok
to True
!
if button_a.value is False and egg_ok is False: print("Reset") time.sleep(0.1) # debounce max_slope = 0 gegg_sprite.x = 0 egg_ok = True
At this point, we're ready for another try!
Next, we'll build a landing vehicle and take it for a spin (drop?)!
Page last edited February 24, 2025
Text editor powered by tinymce.