Demo Code

This is the code that ships with the Raspberry Breadstick. It uses the gyroscope to change the LEDs when rotated about its z-axis.

Required Libraries

To run this code, some additional libraries that aren't baked into the Circuit Python distribution need to be copied to your Breadstick's lib folder.

Code

"""
Raspberry Breadstick Demo
Breadstick Innovations
March 16, 2024

This code reads the X,Y,Z accelerometer and gyrocope data from the LSM6DS over I2C 30 times per second.
The rotation about the Z-Axis is used to update the colour of the SK9822 LEDs 30 times per second.
The X,Y,Z accelerometer and gyrocope data is printed over serial 10 times per second.
Because it's printed as a tuple (acc_x,acc_y,acc_z,gyro_x,gyro_y,gyro_z) you can view live data in Mu's Plotter!
A full list of all global dictionary values are printed once per second.

Multitasking with asyncio:
Think of the processor as a server in a restaraunt and async-tasks like tables.
The server walks from table to table, helping with whatever task they have.
When the task is complete, the table says to the server, "We're good for at least 5 minutes."
If other tables are waiting to be helped, the server goes and helps them; if there are no other tables, it waits.
So long as it doesn't get busy doing other things, the server will make it back to help that first table in 5 minutes.
You won't get perfect timing, it's not interrupt based, but it's a pretty good solution for juggling multiple tasks.

async def is used for making async functions.
Using frequency as an input lets you decide how many times per second its internal loop runs.
These functions can have additional inputs, like pins for inputs and outputs.
You can create many async tasks from the same async def funtion.
Think of 3 LEDs on 3 separate I/O pins blinking at different frequencies, you'd only write 1 function but make 3 tasks from it.

Each of these tasks is basically a self-contained loop.
Sometimes you want to use information from one to affect something in another.
We do this by giving them all access to a shared global dictionary that they can all read and write from.
This way the LED loop can see what the latest gyroscope measurment was and use it to change the LED colour.
"""


"""
Import Libraries & Modules
"""
# Import pin names so you can use them in your code
# The * imports everything so you can just type D4 instead of board.D4 each time
from board import *

# The multitasking library
# Not as scary as it seems
import asyncio

# Lets you easily map raw sensor values like 0-65535 to useful ranges like 0-10
from adafruit_simplemath import map_range

# DotStar LED Libraries
from adafruit_dotstar import DotStar
from adafruit_fancyled.adafruit_fancyled import CRGB, CHSV

# I2C Acceleromter and Gyroscope
import busio
from adafruit_lsm6ds.lsm6ds3trc import LSM6DS3TRC as LSM6DS
from adafruit_lsm6ds import Rate, AccelRange, GyroRange


"""
Define Functions To Be Called By Your Code
"""


async def status_update(frequency):
    """Prints current values in the global shared dictionary."""

    """
    Initial Setup
    """
    period = 1 / frequency
    global shared



    """
    Loop
    """
    while True:
        try:
            print()
            for key in shared.keys():
                print(key, ":", shared[key])
            await asyncio.sleep(period)
        except:
            print("Status Update Error")
            await asyncio.sleep(period)


async def IMU_plotter_update(frequency):
    """Prints plotter values as tuple for plotting."""

    """
    Initial Setup
    """
    period = 1 / frequency
    global shared

    scale = 5 #Only Makes Gyro Values Bigger On Plotter

    """
    Loop
    """
    while True:
        try:
            print((shared['acc_x'],shared['acc_y'],shared['acc_z'],shared['gyro_x']*scale,shared['gyro_y']*scale,shared['gyro_z']*scale))
            await asyncio.sleep(period)
        except:
            print("Plotter Update Error")
            await asyncio.sleep(period)


async def led_update(frequency):
    """Updates LED Strip."""

    """
    Initial Setup
    """
    period = 1 / frequency
    global shared
    leds = DotStar(DOTSTAR_CLOCK, DOTSTAR_DATA, 24, brightness=0.25, auto_write=False)
    leds.fill((0, 0, 0))
    offset = 1

    """
    Loop
    """
    while True:
        try:
            offset += map_range(shared["gyro_z"], -10, 10, -1, 1)

            for i in range(24):
                c = CRGB(CHSV(i / 24 / 5 + offset, 1.0, 0.1))
                leds[i] = c.pack()
            leds.show()
            await asyncio.sleep(period)
        except:
            print("LED Error")
            await asyncio.sleep(period)


async def imu_update(frequency):
    """Reads latest X,Y,Z values from the Gyroscope and Accelerometer."""

    """
    Initial Setup
    """
    period = 1 / frequency

    global shared
    shared["acc_x"] = 0
    shared["acc_y"] = 0
    shared["acc_z"] = 0
    shared["gyro_x"] = 0
    shared["gyro_y"] = 0
    shared["gyro_z"] = 0

    # Setup I2C Accelerometer and Gyroscope
    i2c = busio.I2C(IMU_SCL, IMU_SDA)
    IMU = LSM6DS(i2c)
    IMU.accelerometer_range = AccelRange.RANGE_4G
    print("Accelerometer range set to: %d G" % AccelRange.string[IMU.accelerometer_range])
    IMU.gyro_range = GyroRange.RANGE_1000_DPS
    print("Gyro range set to: %d DPS" % GyroRange.string[IMU.gyro_range])
    IMU.accelerometer_data_rate = Rate.RATE_1_66K_HZ
    print("Accelerometer rate set to: %d HZ" % Rate.string[IMU.accelerometer_data_rate])
    IMU.gyro_data_rate = Rate.RATE_1_66K_HZ
    print("Gyro rate set to: %d HZ" % Rate.string[IMU.gyro_data_rate])

    """
    Loop
    """
    while True:
        try:
            shared["acc_x"], shared["acc_y"], shared["acc_z"] = IMU.acceleration
            shared["gyro_x"], shared["gyro_y"], shared["gyro_z"] = IMU.gyro
            await asyncio.sleep(period)
        except:
            print("IMU Update Error")
            await asyncio.sleep(period)


async def main():
    """Main Program Loop.  Gets called below by asyncio.run(main())."""
    imu_task = asyncio.create_task(imu_update(30))
    led_task = asyncio.create_task(led_update(30))
    plotter_task = asyncio.create_task(IMU_plotter_update(10))
    status_task = asyncio.create_task(status_update(1))

    await imu_task
    await led_task
    await plotter_task
    await status_task



"""
This Is Where Code Execution Actually Begins!
"""

# Dictionary of global values accessible to async functions
shared = {}

# Run main loop
asyncio.run(main())

Last updated