Turn a tiny circuit board into a game console, a weather station, a piano, a compass and more, using CircuitPython.
Never coded before? No problem. Know some Python? Skip to the missions. Every mission you finish earns you XP (look at the bar at the top!).
Start the quest βΆπMeet the CLUE
The Adafruit CLUE is a small computer (a microcontroller) about the size of a credit card. It's crammed with gadgets. Here's what's on board:
By the end of this quest you'll have used every one of these. You also need:
- A USB cable that can carry data (some cables only charge, and those won't work!)
- A computer with Chrome or Edge (a Chromebook is fine)
πPython in 20 minutes
CircuitPython is Python made for tiny boards. If you already know Python, skim this and move on. If not, these are the only ideas you need for the whole quest. After Setup, paste them into the REPL (see Troubleshooting) or into code.py.
π§± Coming from Scratch or MakeCode?
You already know how to code! Python just uses typed words instead of blocks. Here's the translation:
| Block | Python |
|---|---|
| forever | while True: |
| repeat 10 | for i in range(10): |
| if <> then / else | if ...: |
| set score to 0 | score = 0 |
| change score by 1 | score += 1 |
| say Hello / show string | print("Hello") |
| wait 1 second / pause 1000 ms | time.sleep(1) |
| make a block / function | def my_block(): |
| list / array | things = [1, 2, 3] |
| add thing to list | things.append(4) |
| item 1 of list | things[0] (Python starts at 0!) |
| length of list | len(things) |
| pick random 1 to 6 | random.randint(1, 6) |
1. Comments: notes for humans
Anything after # is ignored by the computer.
# This is a comment. The CLUE skips it.
2. print(): send a message
Shows a message in the Serial console (and on the CLUE's own screen until your program draws something else).
print("Hello!")
print(2 + 3) # prints 5
3. Variables: labeled boxes
score = 0 # a number
name = "Robo" # text (a "string") goes in quotes
score = score + 10 # now score is 10
score += 5 # shortcut for score = score + 5, now 15
print(name, "has", score, "points")
4. Math: Python is a calculator
print(7 + 2) # 9 add
print(7 - 2) # 5 subtract
print(7 * 2) # 14 multiply (a star, not x)
print(7 / 2) # 3.5 divide
print(7 // 2) # 3 divide and throw away the leftover
print(7 % 2) # 1 ONLY the leftover (remainder)
print(2 ** 3) # 8 power: 2 x 2 x 2
print(round(3.7)) # 4
% is super handy: n % 2 == 0 means "n is even", and angle % 360 keeps an angle between 0 and 359.
5. True / False and if
Indentation matters! The lines pushed in by 4 spaces belong to the if.
temperature = 30
if temperature > 25:
print("Hot!")
elif temperature > 15:
print("Nice.")
else:
print("Brrr!")
Comparisons: > bigger, < smaller, >= bigger or equal, == equal (two equals signs!), != not equal. Combine tests with and, or, not (for example if a > 0 and b > 0:).
6. Loops: do it again
for i in range(3): # runs 3 times: i = 0, 1, 2
print("Hip hip hooray", i)
for i in range(10, 0, -2): # start 10, stop before 0, step -2
print(i) # 10 8 6 4 2
count = 0
while count < 3: # repeat WHILE this is True
count += 1
print("counted to", count)
while True: # runs FOREVER...
print("I never stop!")
break # ...unless you break out of it
Almost every CLUE program has a while True: loop, so it keeps checking the buttons and sensors forever.
7. Lists (arrays!): many things in one box
In Scratch they're called lists, in MakeCode arrays, and in Python lists. A list keeps things in order inside square brackets [ ].
pets = ["cat", "dog", "hamster"]
print(pets[0]) # cat (counting starts at 0!)
print(pets[2]) # hamster
print(pets[-1]) # hamster (-1 = the LAST one)
print(len(pets)) # 3 (how many things)
pets[1] = "parrot" # change an item
pets.append("fish") # add to the end
pets.insert(0, "frog") # add at position 0
pets.remove("cat") # remove by value
last = pets.pop() # take the last one out (and keep it)
print(pets, last) # ['frog', 'parrot', 'hamster'] fish
if "frog" in pets: # is it in the list?
print("Ribbit!")
Looping through a list
scores = [12, 45, 7, 30]
for s in scores: # s becomes each item in turn
print("score:", s)
for i in range(len(scores)): # when you also need the position
print("player", i, "got", scores[i])
print(sum(scores), max(scores), min(scores)) # 94 45 7
print(sorted(scores)) # [7, 12, 30, 45]
print(scores[1:3]) # [45, 7] a "slice": items 1 and 2
Building a list bit by bit
evens = [] # start with an empty list
for n in range(1, 11):
if n % 2 == 0:
evens.append(n)
print(evens) # [2, 4, 6, 8, 10]
Lists inside lists: a grid
Great for game boards and mazes. grid[row][column]:
grid = [
[0, 1, 0],
[1, 1, 1],
[0, 1, 0],
]
print(grid[1][2]) # row 1, column 2 -> 1
grid[0][0] = 9
for row in grid:
print(row)
π§© Try it: list puzzles (answers inside)
1. nums = [5, 10, 15, 20]. What is nums[-2]? 2. What does len([]) give? 3. After a = [1, 2] and a.append(a[0] + a[1]), what is a?
Answers: 1. 15 2. 0 (an empty list) 3. [1, 2, 3]
8. Tuples: lists that never change
Round brackets ( ) instead of square ones. You can read them but not change them. The CLUE uses them a lot: colors and sensor readings are tuples.
purple = (255, 0, 255) # red, green, blue amounts
print(purple[0]) # 255
x, y, z = (1, 2, 3) # "unpack" a tuple into 3 variables
# purple[0] = 100 # ERROR! tuples can't be changed
9. Strings: playing with text
word = "clue"
print(word.upper()) # CLUE
print(len(word)) # 4 letters
print(word[0]) # c (strings work like lists of letters!)
print("Hi " + word + "!") # Hi clue! (+ glues text together)
print("ha" * 3) # hahaha
age = 12
print("I am " + str(age)) # str() turns a number into text
print(int("7") + 1) # int() turns text into a number: 8
10. import: borrow superpowers
import time # clock tools
time.sleep(0.5) # wait half a second
import random # dice-rolling tools
print(random.randint(1, 6)) # a random whole number 1 to 6
print(random.choice(["rock", "paper", "scissors"])) # pick from a list
from adafruit_clue import clue # borrow ALL the CLUE powers
11. Functions: your own commands
def cheer(name):
print("Go", name, "go!")
cheer("Robo") # Go Robo go!
def double(n):
return n * 2 # return hands an answer back
print(double(4)) # 8
def average(numbers): # functions + lists = power!
return sum(numbers) / len(numbers)
print(average([10, 20, 30])) # 20.0
print shows something; return gives the answer back to the code that called the function.
12. f-strings: mix text and numbers
temp = 21.5678
print(f"Temp: {temp:.1f} C") # Temp: 21.6 C (.1f = 1 decimal place)
13. Bonus: dictionaries, lists with name tags
A list finds things by position. A dictionary finds them by name (a "key"), using curly brackets { }.
colors = {"red": (255, 0, 0), "green": (0, 255, 0), "blue": (0, 0, 255)}
print(colors["green"]) # (0, 255, 0)
colors["pink"] = (255, 0, 128) # add a new key
for name in colors:
print(name, colors[name]) # careful: on the CLUE the order can come out mixed up!
Unlike a list, a dictionary on the CLUE doesn't promise to keep its order. Use a list when order matters.
π¬ Mini project: a list of real sensor readings
Once your CLUE is set up (next level), try this. It collects 10 temperature readings into a list, then does math on them.
import time
from adafruit_clue import clue
readings = []
for i in range(10):
readings.append(clue.temperature)
time.sleep(0.2)
print("all:", [round(t, 1) for t in readings])
print(f"lowest {min(readings):.1f} highest {max(readings):.1f}")
print(f"average {sum(readings) / len(readings):.2f}")
The line with [round(t, 1) for t in readings] is a fancy shortcut that makes a new list with every reading rounded. You don't need to write those yourself yet!
Change it to collect
clue.sound_level readings instead, and print how many of them were above 20 (hint: a counter and an if inside a for loop).Python is picky: capital letters matter (
True not true), colons go at the end of if/for/while/def lines, and indent with 4 spaces.π οΈSetup: power up your CLUE
We only have to do this once. There are 3 parts: (A) install CircuitPython, (B) add the libraries, (C) connect the code editor.
Part A: Install CircuitPython
- Go to circuitpython.org/board/clue_nrf52840_express and download the latest stable .UF2 file (not an "Absolute Newest" or beta one).
- Plug the CLUE into your computer with your data USB cable.
- Double-click the little Reset button (quick: click-click!). The NeoPixel turns green and a new drive called CLUEBOOT appears on your computer.
- Drag the .UF2 file onto CLUEBOOT. The light flashes, CLUEBOOT disappears, and a new drive called CIRCUITPY shows up. π
β Try a different USB cable, since it's probably a charge-only cable. β‘ Double-click faster (or slower!). β’ Try another USB port. If you see CIRCUITPY instead, CircuitPython is already installed, so skip to Part B.
Look inside CIRCUITPY. You'll see code.py (your program), a lib folder (for libraries) and boot_out.txt. Open boot_out.txt: the first line tells you your CircuitPython version, for example Adafruit CircuitPython 10.0.0. Remember that first number!
Part B: Add the libraries
A library is code someone else wrote that you can borrow. The adafruit_clue library gives us easy commands like clue.temperature. It needs some helper libraries too.
- Go to circuitpython.org/libraries and download the Bundle whose first number matches. For example, if
boot_out.txtsaysCircuitPython 10.3.1, pick the bundle for 10.x. - Unzip it. Open the
libfolder inside the bundle. It's huge, but we only need a few things. - Copy these into the
libfolder on CIRCUITPY (some are folders, some are files ending in.mpy):
| Copy this | It powers⦠|
|---|---|
adafruit_clue.mpy | The easy CLUE commands β |
adafruit_apds9960 π | Proximity, gesture & color sensor |
adafruit_bmp280.mpy | Air pressure & altitude |
adafruit_sht31d.mpy | Humidity (and temperature) |
adafruit_lsm6ds π | Accelerometer & gyro |
adafruit_lis3mdl.mpy | Magnetometer |
adafruit_display_text π | Words on the screen |
neopixel.mpy | The color LED |
adafruit_bus_device π | Helper that talks to sensors |
adafruit_register π | Another sensor helper |
ImportError: no module named 'β¦'?That means a library is missing. Find the name it says in the bundle's
lib folder and copy it over too.π§βπ« For teachers: the faster way with circup
If Python 3 is installed on the computer, circup installs a library and all its helpers automatically, matched to the board's version. With the CLUE plugged in:
pip install circup
circup install adafruit_clue
Handy for setting up a whole class set of boards.
Part C: Connect to the code editor
- Open code.circuitpython.org in Chrome or Edge.
- A box pops up asking how to connect. Click USB.
- Step 1: click Connect to Device. Chrome shows a list: pick CLUE nRF52840 Express - CircuitPython CDC control (COMβ¦) and click Connect.
- Step 2: click Select New Folder, choose the CIRCUITPY drive and click Select Folder. Chrome asks "Allow this site to edit files?": click Allow, then click OK.
- A Device Info box shows your board and version. If it says "Update available β¦ alpha", ignore it (alpha = unfinished test version) and click Close.
- The editor starts with an empty [New Document]. Click Open and double-click
code.py. This is the file the CLUE runs automatically. - Click Serial (bottom left) to open the console on the right. This is where
print()messages show up. It has buttons for Halt (stop your program), Restart, Clear and Plotter (live graphs!).
Edit
code.py β click Save + Run β the CLUE restarts and runs your code instantly. That's it!When a program ends, the console says "Code done running." That's normal, not an error.
Your first program
Delete everything in code.py, type this in, and click Save + Run:
from adafruit_clue import clue
print("Hello from my CLUE!")
print("It is", round(clue.temperature, 1), "degrees C in here (a bit high, see Mission 11)")
Look in the Serial console. Did it print the temperature? You just read a sensor! π
π Troubleshooting & the REPL
- Red error text? Read the last line and look at the line number. Most bugs are typos, a missing
:, or wrong indentation. - Errors also show on the CLUE's screen, which is handy!
- REPL = a live Python playground. Click the Halt button (or click in the Serial console and press Ctrl + C), then press any key. You get a
>>>prompt. Type2 + 2and press Enter! Try the Python lessons above here. Press Ctrl + D to go back to runningcode.py. - Editor can't see the board? Unplug, plug back in, refresh the page, and connect again.
- The editor also supports Bluetooth for the CLUE, but USB is the easiest way to start.
πΊHello, Screen!
+100 XPEach mission unlocks a new CLUE power. Type the code (typing it beats copy-paste for learning!), run it, then try the challenge.
Let's put words on the display. simple_text_display gives you a title plus lines numbered 0, 1, 2β¦
from adafruit_clue import clue
screen = clue.simple_text_display(title="CLUE QUEST", text_scale=2,
colors=((255, 0, 255), (0, 255, 255), (255, 255, 0)))
screen[0].text = "Hello, world!"
screen[1].text = "My name is"
screen[2].text = "Sam"
screen.show() # nothing appears until you call show()!
while True: # keep the program running so the text stays up
pass # "pass" means "do nothing"
Colors are (red, green, blue), each from 0 to 255. (255, 0, 255) = full red + full blue = magenta. The title is always white: colors= only changes the lines. Use title_color=(255, 255, 0) to change it.
while True?When a program ends, the CLUE shows "Code done running" and your text vanishes. The endless loop keeps it on screen.
β Bonus: GIANT text anywhere on the screen
For full control, use displayio (the screen's drawing system) and a Label:
import displayio
import terminalio
from adafruit_display_text import label
from adafruit_clue import clue
group = displayio.Group() # a "layer" to draw on
# background: a 240x240 bitmap filled with one color
bg = displayio.Bitmap(240, 240, 1)
palette = displayio.Palette(1)
palette[0] = 0x220044 # dark purple (hex color)
group.append(displayio.TileGrid(bg, pixel_shader=palette))
big = label.Label(terminalio.FONT, text="WOW", color=0x22D3EE, scale=6, x=65, y=120)
group.append(big)
clue.display.root_group = group # show our layer
while True:
pass
x and y are pixel positions: (0, 0) is the top-left corner, (239, 239) the bottom-right.
Make a name badge with your name, your favorite food and your favorite game, each in a different color.
The Screen Lab explains how the screen really works: plotting pixels, drawing shapes, animation and colors. The Picture Maker turns any photo or GIF into CLUE code!
πLight Show
+100 XPThe CLUE has three kinds of lights: the NeoPixel (any color), the red LED (on/off) and two white LEDs (on/off, super bright).
import time
from adafruit_clue import clue
clue.pixel.brightness = 0.2 # 0.0 = off, 1.0 = BLINDING
rainbow = [(255, 0, 0), (255, 128, 0), (255, 255, 0),
(0, 255, 0), (0, 0, 255), (128, 0, 255)]
while True:
for color in rainbow:
clue.pixel.fill(color) # set the NeoPixel color
clue.red_led = True # red LED on
time.sleep(0.3)
clue.red_led = False # red LED off
time.sleep(0.1)
And the white LEDs are one line:
from adafruit_clue import clue
clue.white_leds = True # flashlight ON (False turns it off)
while True:
pass
Don't stare into the white LEDs, they're really bright!
Make a police siren: flash red and blue on the NeoPixel, and blink the white LEDs every time it turns blue.
π °οΈButton Mash
+100 XPclue.button_a is True while A is held down, False otherwise. Same for B.
from adafruit_clue import clue
while True:
if clue.button_a and clue.button_b:
clue.pixel.fill((0, 255, 0)) # both = green
elif clue.button_a:
clue.pixel.fill((255, 0, 0)) # A = red
elif clue.button_b:
clue.pixel.fill((0, 0, 255)) # B = blue
else:
clue.pixel.fill((0, 0, 0)) # nothing = off
Counting presses
If we just did count = count + 1 while A is held, it would count hundreds of times per press! Instead we remember whether the button was already down, and only count the moment it goes down.
from adafruit_clue import clue
screen = clue.simple_text_display(title="Clicker", text_scale=3)
count = 0
was_down = False
while True:
down = clue.button_a
if down and not was_down: # it JUST got pressed
count = count + 1
was_down = down
if clue.button_b: # B resets
count = 0
screen[0].text = str(count) # str() turns a number into text
screen.show()
Make B toggle the white LED flashlight on and off (hint: use the same "just pressed" trick and
clue.white_leds = not clue.white_leds).π΅Beep Boop Band
+100 XPclue.play_tone(frequency, seconds) plays a note. A higher frequency means a higher pitch. 262 is middle C.
import time
from adafruit_clue import clue
C, D, E, F, G, A = 262, 294, 330, 349, 392, 440
# Twinkle Twinkle Little Star: (note, how long)
song = [(C, 0.4), (C, 0.4), (G, 0.4), (G, 0.4), (A, 0.4), (A, 0.4), (G, 0.8),
(F, 0.4), (F, 0.4), (E, 0.4), (E, 0.4), (D, 0.4), (D, 0.4), (C, 0.8)]
for note, length in song: # unpack each (note, length) pair
clue.play_tone(note, length)
time.sleep(0.05) # tiny gap between notes
There's also clue.start_tone(freq) and clue.stop_tone() for notes that last as long as you want. You'll use those next mission. play_tone waits until the note has finished before the next line runs; start_tone starts a note and moves on at once.
Play a happy "level up" sound when A is pressed (notes going UP) and a sad "game over" sound when B is pressed (notes going DOWN).
πΉTouch Piano
+100 XPThe gold pads labeled 0, 1 and 2 on the bottom edge are touch sensors. Your body carries a tiny electric charge, and the CLUE can feel it!
from adafruit_clue import clue
clue.pixel.brightness = 0.2
while True:
if clue.touch_0:
clue.start_tone(262) # C
clue.pixel.fill((255, 0, 0))
elif clue.touch_1:
clue.start_tone(330) # E
clue.pixel.fill((0, 255, 0))
elif clue.touch_2:
clue.start_tone(392) # G
clue.pixel.fill((0, 0, 255))
else:
clue.stop_tone()
clue.pixel.fill((0, 0, 0))
Hands off while it starts! The CLUE learns each pad's "normal" level when the program starts. If a pad seems stuck, restart the program without touching anything.
Clip an alligator clip from a pad to a banana, apple or foil. Now the fruit is a piano key! (Only use fruit and clips, nothing plugged into the wall.)
Add two more notes using button A and button B, so you have a 5-note piano.
π±Shake & Tilt
+150 XPThe accelerometer measures pushes and pulls, including gravity! clue.acceleration gives 3 numbers (x, y, z). Lying flat and face up, z is about -9.5 to -9.8 (a little different on each board): that's gravity pulling down. Flip it over and it becomes +9.8!
import time
from adafruit_clue import clue
while True:
x, y, z = clue.acceleration
print(f"x={x:.1f} y={y:.1f} z={z:.1f}")
time.sleep(0.2)
Tilt the board and watch the numbers change. Tip it left and right: that's x. Tip it forward and back: that's y.
If you print numbers inside parentheses like
print((x, y, z)), the code editor's Plotter can draw them as live lines.Magic 8-Ball
clue.shake() is True when you shake the board. The default shake_threshold is 30 (a hard shake); we use 20 so a gentle shake works. Raise it if it triggers too easily, lower it if you have to shake too hard, but don't go below 10.
import random
from adafruit_clue import clue
answers = ["Yes!", "No way", "Maybe...", "Ask again", "Definitely", "Nope"]
screen = clue.simple_text_display(title="Magic 8-Ball", text_scale=3)
screen[1].text = "Shake me!"
screen.show()
while True:
if clue.shake(shake_threshold=20):
screen[1].text = random.choice(answers)
screen.show()
clue.play_tone(880, 0.1)
Build a "spirit level": turn the NeoPixel green when the board is flat (x and y both close to 0). First print x and y on a flat table: your board may read 0.5 instead of 0, so pick limits a bit wider, like -1.5 to 1.5, and red when it's tilted.
πSpin Detector
+100 XPThe gyroscope measures how fast the board is spinning, in degrees per second. Sitting still, it reads close to 0.
from adafruit_clue import clue
screen = clue.simple_text_display(title="Spin-o-meter", text_scale=2)
while True:
x, y, z = clue.gyro
screen[0].text = f"x: {x:6.0f}"
screen[1].text = f"y: {y:6.0f}"
screen[2].text = f"z: {z:6.0f}"
if abs(z) > 200: # abs() ignores the minus sign
screen[4].text = "WHEEE!"
else:
screen[4].text = ""
screen.show()
:6.0f means no decimals, at least 6 characters wide, so the numbers line up. You can use lines 3, 4, 5β¦ too: each line is 13 Γ text_scale pixels lower, so big text runs out of screen sooner.
Lay the CLUE flat on the table and spin it like a record. That's the z axis!
Play a higher note the faster you spin (hint:
clue.start_tone(200 + int(abs(z)))).π§Magnet Hunter & Compass
+150 XPThe magnetometer senses magnetic fields (in microteslas). Earth is a giant magnet, and a fridge magnet is a tiny one.
import math
from adafruit_clue import clue
screen = clue.simple_text_display(title="Magnet Hunter", text_scale=2)
while True:
x, y, z = clue.magnetic
strength = math.sqrt(x*x + y*y + z*z) # total magnet power
screen[0].text = f"Power: {strength:.0f}"
if strength > 150:
screen[2].text = "MAGNET NEARBY!"
clue.pixel.fill((255, 0, 0))
else:
screen[2].text = ""
clue.pixel.fill((0, 0, 0))
screen.show()
Bring a magnet close and watch the power jump. Earth's field alone is usually around 25 to 65.
Rough compass
With the board lying flat, atan2(y, x) turns the x and y readings into an angle, and % 360 keeps it between 0 and 359.
import math
from adafruit_clue import clue
screen = clue.simple_text_display(title="Compass", text_scale=3)
while True:
x, y, z = clue.magnetic
heading = math.degrees(math.atan2(y, x)) % 360
screen[1].text = f"{heading:.0f} deg"
screen.show()
The angle is measured from the board's x axis, not from the screen. Turn the board until it reads about 0: now its x axis points to magnetic north. Check which way makes the number go up before you trust N/E/S/W.
Every magnetometer is a little bit off, and metal or electronics nearby confuse it. If your compass is wonky, that's normal! Real engineers calibrate: slowly turn the board all the way around, note the smallest and biggest x and y, then subtract the middle value (
(min + max) / 2) from each reading.Show "N", "E", "S" or "W" instead of a number.
βJedi Hand Powers
+150 XPThe little black sensor at the top of the board shines invisible infrared light and watches it bounce back. That gives us proximity (0 = far, 255 = touching) and gestures (hand swipes!).
Proximity theremin
from adafruit_clue import clue
while True:
p = clue.proximity
if p > 10:
clue.start_tone(200 + p * 5) # closer hand = higher note
else:
clue.stop_tone()
Swipe detector
clue.gesture gives 0 = nothing, 1 = up, 2 = down, 3 = left, 4 = right. Wave your hand about 5 to 10 cm above the sensor.
from adafruit_clue import clue
names = ["", "UP", "DOWN", "LEFT", "RIGHT"]
screen = clue.simple_text_display(title="Swipe!", text_scale=4)
while True:
g = clue.gesture
if g != 0:
screen[1].text = names[g] # use the number to pick from the list
screen.show()
Swipe left/right to change the NeoPixel color through a list, like flipping pages.
π¨Color Copycat
+150 XPThe same sensor can see color! clue.color gives 4 numbers: red, green, blue, clear (clear = total brightness). Turn on the white LEDs so it can see better.
import time
from adafruit_clue import clue
clue.white_leds = True
clue.pixel.brightness = 0.3
screen = clue.simple_text_display(title="Color Copycat", text_scale=2)
while True:
r, g, b, c = clue.color
screen[0].text = f"R {r}"
screen[1].text = f"G {g}"
screen[2].text = f"B {b}"
screen.show()
biggest = max(r, g, b, 1) # the 1 stops us dividing by zero
# scale so the strongest color becomes 255
clue.pixel.fill((r * 255 // biggest, g * 255 // biggest, b * 255 // biggest))
time.sleep(0.1)
Hold a colorful LEGO brick, sticky note or marker cap right on top of the sensor. The NeoPixel copies it!
The first reading after the white LEDs go on is 0 (the sensor needs about half a second for its first "photo"; in a program that reads only once, read, time.sleep(0.5), then read again), and the numbers can be hundreds or thousands, not just 0 to 255. That's fine: only which one is biggest matters. (// divides and throws away the decimals.)
Make a "fruit sorter": show "RED!", "GREEN!" or "BLUE!" on screen depending on which number is biggest. When all three are 0 (the very first reading), show
"?" instead of guessing: add a last else.β Weather Station
+150 XPFour air sensors in one program:
clue.temperature: degrees Celsiusclue.humidity: % of water in the airclue.pressure: air pressure in hPa (falling pressure often means rain is coming!)clue.altitude: height in meters, worked out from pressure
import time
from adafruit_clue import clue
# Look up today's sea-level pressure for your town on a weather site
# and put it here to make altitude more accurate.
clue.sea_level_pressure = 1013.25
TEMP_OFFSET = 5 # measure yours: CLUE reading minus real thermometer
screen = clue.simple_text_display(title="Weather Station", text_scale=2,
colors=((255, 80, 80), (80, 180, 255), (255, 255, 0), (80, 255, 120)))
while True:
temp_c = clue.temperature - TEMP_OFFSET
temp_f = temp_c * 9 / 5 + 32
screen[0].text = f"{temp_c:.1f}C / {temp_f:.0f}F"
screen[1].text = f"Humid {clue.humidity:.0f}%"
screen[2].text = f"{clue.pressure:.0f} hPa"
screen[3].text = f"Alt {clue.altitude:.0f} m"
screen.show()
time.sleep(1)
Breathe on the board: humidity shoots up! Carry it up the stairs: altitude should climb by about 3 m per floor. Hold it in your hand: temperature rises.
The board warms itself, so it reads several degrees too high (ours says 30 C in a comfortable room), and even more if the LEDs have been on. Put a real thermometer next to it, work out the difference and put it in
TEMP_OFFSET: that's calibration again.Turn the NeoPixel blue when it's cold, green when comfy, and red when hot. Use the corrected
temp_c.πClap Detector
+150 XPThe microphone can't record words here, but it measures how loud it is. clue.sound_level gives a number, and clue.loud_sound() is True when it's louder than a threshold.
Noise meter
import time
from adafruit_clue import clue
while True:
level = clue.sound_level
print((level,)) # the comma makes it a tuple, so the Plotter can graph it!
time.sleep(0.05)
Clap-on light
import time
from adafruit_clue import clue
light_on = False
while True:
if clue.loud_sound(sound_threshold=500):
light_on = not light_on # flip True β False
clue.white_leds = light_on
time.sleep(0.5) # ignore the echo of the same clap
Too jumpy? Make sound_threshold bigger. Not hearing you? Make it smaller. In a quiet room the number is usually 5 to 30. Talking is higher and a clap is much higher. Run the noise meter, clap, and pick a threshold between your loudest normal noise and your clap.
Make a "library monitor": the screen shows a happy message like "Nice and quiet" when it's quiet (the CLUE's font can't draw emoji) and "SHHH!" when it gets too loud. Each letter is 6 Γ text_scale pixels wide and the screen is 240 wide, so use
text_scale=2 for the long message.β‘Reaction Race
+300 XPTime to combine powers: screen + NeoPixel + buttons + speaker + time + random. Wait for green, then hit A as fast as you can. Press too early and you lose!
import time
import random
from adafruit_clue import clue
clue.pixel.brightness = 0.3
screen = clue.simple_text_display(title="REACTION RACE", text_scale=2)
best = None # None = "no score yet"
def show(line0, line1="", line2=""):
screen[0].text = line0
screen[1].text = line1
screen[2].text = line2
screen.show()
while True:
best_text = "Best: --" if best is None else f"Best: {best} ms"
show("Press A", "to start", best_text)
while not clue.button_a: # wait for A
pass
while clue.button_a: # wait for A to be let go
pass
show("Get ready...", "wait for GREEN")
clue.pixel.fill((255, 0, 0))
wait = random.uniform(1.5, 4.0)
start_wait = time.monotonic()
cheated = False
while time.monotonic() - start_wait < wait:
if clue.button_a: # pressed too early!
cheated = True
break
if cheated:
clue.pixel.fill((255, 0, 255))
clue.play_tone(150, 0.6)
show("TOO EARLY!", "Try again")
time.sleep(2)
continue # jump back to the top of the loop
clue.pixel.fill((0, 255, 0))
clue.play_tone(1000, 0.05)
go = time.monotonic()
while not clue.button_a:
pass
ms = int((time.monotonic() - go) * 1000)
clue.pixel.fill((0, 0, 0))
if best is None or ms < best:
best = ms
clue.play_tone(523, 0.1)
clue.play_tone(784, 0.2)
show(f"{ms} ms", "NEW RECORD!")
else:
show(f"{ms} ms", "Nice try!")
time.sleep(2)
while clue.button_a:
pass
New things here: time.monotonic() is a stopwatch that counts seconds since the board started, break jumps out of a loop, continue skips to the next round, and None means "nothing yet".
β Make it 2-player: A vs. B, and whoever presses first after green wins.
β‘ Use
clue.shake() instead of a button: "shake when green!" Heads up: clue.shake() takes about half a second to decide, so times are less precise than with a button.β’ Invent your own game. Some ideas: a step counter (shake = step), a tilt maze on the NeoPixel, a pet that gets hungry and needs you to press buttons, or a data logger that tracks temperature all day.
CLUE MASTER!
You used every gadget on the CLUE: screen, NeoPixel, LEDs, buttons, speaker, touch pads, accelerometer, gyro, magnetometer, proximity, gesture, color, temperature, humidity, pressure, altitude and microphone. That's real embedded programming. Go build something awesome!
Every CLUE power on one page
Always start with from adafruit_clue import clue.
| Gadget | Code | What you get |
|---|---|---|
| Screen (easy) | s = clue.simple_text_display(title="Hi", text_scale=2)s[0].text = "..." s.show() | Lines of text |
| Screen (full) | clue.display.root_group = group | Your own displayio drawing |
| NeoPixel | clue.pixel.fill((r, g, b))clue.pixel.brightness = 0.2 | Any color, 0 to 255 each |
| Red LED | clue.red_led = True | On / off |
| White LEDs | clue.white_leds = True | On / off |
| Buttons | clue.button_a, clue.button_b | True while pressed |
| Touch pads | clue.touch_0, clue.touch_1, clue.touch_2 | True while touched |
| Speaker | clue.play_tone(440, 0.5)clue.start_tone(440) / clue.stop_tone() | Beeps and notes |
| Microphone | clue.sound_levelclue.loud_sound(sound_threshold=500) | Loudness number / True if loud |
| Accelerometer | x, y, z = clue.acceleration | m/sΒ² (gravity β 9.8) |
| Shake | clue.shake(shake_threshold=20) | True if shaken (30 is the default) |
| Gyroscope | x, y, z = clue.gyro | Spin speed, degrees/second |
| Magnetometer | x, y, z = clue.magnetic | Microteslas |
| Proximity | clue.proximity | 0 (far) to 255 (close) |
| Gesture | clue.gesture | 0 none, 1 up, 2 down, 3 left, 4 right |
| Color | r, g, b, c = clue.color | Red, green, blue, clear light |
| Temperature | clue.temperature | Β°C |
| Humidity | clue.humidity | % relative humidity |
| Pressure | clue.pressure | hPa |
| Altitude | clue.sea_level_pressure = 1013.25clue.altitude | Meters |
More adventures
- Screen Lab: how the screen works, shapes and animation
- Picture Maker: photos and GIFs on your CLUE
- Adafruit CLUE guide: the official manual
- adafruit_clue library docs: every command explained
- CLUE projects on Adafruit Learn: tons of project ideas
- CircuitPython on GitHub: the source code of CircuitPython itself