0 XP
Lv 1
🌍 Ελληνικά
For coders age 11+ πŸš€

CLUE Quest!

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 β–Ά
0123VGND HI! AB
Level 0

πŸ”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:

πŸ“ΊColor screen1.3" square, 240 Γ— 240 pixels
πŸ…°οΈπŸ…±οΈButtons A & BLeft and right of the screen
🌈NeoPixelAn LED that can be any color
πŸ”΄βšͺRed LED + white LEDsA status light and two mini flashlights
πŸ”ŠSpeakerBeeps, tones and tunes
🎀MicrophoneHears how loud it is
πŸ‘†Touch pads 0, 1, 2Gold pads on the bottom edge. Screen facing you: 0, 1, 2, 3V, GND from left to right
🎒Accelerometer + gyroFeels tilt, shakes and spins
🧭MagnetometerSenses magnets (a compass!)
βœ‹Proximity, gesture & colorThe little black sensor at the top
🌑️Temp + humidityHow hot and how sticky the air is
⛰️Air pressureWeather and altitude

By the end of this quest you'll have used every one of these. You also need:

Level 1

🐍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:

BlockPython
foreverwhile True:
repeat 10for i in range(10):
if <> then / elseif ...:
else:
set score to 0score = 0
change score by 1score += 1
say Hello / show stringprint("Hello")
wait 1 second / pause 1000 mstime.sleep(1)
make a block / functiondef my_block():
list / arraythings = [1, 2, 3]
add thing to listthings.append(4)
item 1 of listthings[0] (Python starts at 0!)
length of listlen(things)
pick random 1 to 6random.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!

πŸ† Challenge
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).
🧠 Remember
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.
Level 2

πŸ› οΈ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

  1. Go to circuitpython.org/board/clue_nrf52840_express and download the latest stable .UF2 file (not an "Absolute Newest" or beta one).
  2. Plug the CLUE into your computer with your data USB cable.
  3. Double-click the little Reset button (quick: click-click!). The NeoPixel turns green and a new drive called CLUEBOOT appears on your computer.
  4. Drag the .UF2 file onto CLUEBOOT. The light flashes, CLUEBOOT disappears, and a new drive called CIRCUITPY shows up. πŸŽ‰
😬 No CLUEBOOT drive?
β‘  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.

  1. Go to circuitpython.org/libraries and download the Bundle whose first number matches. For example, if boot_out.txt says CircuitPython 10.3.1, pick the bundle for 10.x.
  2. Unzip it. Open the lib folder inside the bundle. It's huge, but we only need a few things.
  3. Copy these into the lib folder on CIRCUITPY (some are folders, some are files ending in .mpy):
Copy thisIt powers…
adafruit_clue.mpyThe easy CLUE commands ⭐
adafruit_apds9960 πŸ“Proximity, gesture & color sensor
adafruit_bmp280.mpyAir pressure & altitude
adafruit_sht31d.mpyHumidity (and temperature)
adafruit_lsm6ds πŸ“Accelerometer & gyro
adafruit_lis3mdl.mpyMagnetometer
adafruit_display_text πŸ“Words on the screen
neopixel.mpyThe color LED
adafruit_bus_device πŸ“Helper that talks to sensors
adafruit_register πŸ“Another sensor helper
πŸ’‘ Error says 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

  1. Open code.circuitpython.org in Chrome or Edge.
  2. A box pops up asking how to connect. Click USB.
  3. Step 1: click Connect to Device. Chrome shows a list: pick CLUE nRF52840 Express - CircuitPython CDC control (COM…) and click Connect.
  4. 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.
  5. A Device Info box shows your board and version. If it says "Update available … alpha", ignore it (alpha = unfinished test version) and click Close.
  6. The editor starts with an empty [New Document]. Click Open and double-click code.py. This is the file the CLUE runs automatically.
  7. 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!).
πŸͺ„ The golden rule
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. Type 2 + 2 and press Enter! Try the Python lessons above here. Press Ctrl + D to go back to running code.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.
Mission 1

πŸ“ΊHello, Screen!

+100 XP
πŸ—ΊοΈ The Missions
Each 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.

πŸ€” Why the 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.

πŸ† Challenge
Make a name badge with your name, your favorite food and your favorite game, each in a different color.
πŸ“Ί Want more screen power?
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!
Mission 2

🌈Light Show

+100 XP

The 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
😎 Careful
Don't stare into the white LEDs, they're really bright!
πŸ† Challenge
Make a police siren: flash red and blue on the NeoPixel, and blink the white LEDs every time it turns blue.
Mission 3

πŸ…°οΈButton Mash

+100 XP

clue.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()
πŸ† Challenge
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).
Mission 4

🎡Beep Boop Band

+100 XP

clue.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.

πŸ† Challenge
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).
Mission 5

🎹Touch Piano

+100 XP

The 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.

🍌 Fun hack
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.)
πŸ† Challenge
Add two more notes using button A and button B, so you have a 5-note piano.
Mission 6

🎱Shake & Tilt

+150 XP

The 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.

πŸ“ˆ Graph it!
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)
πŸ† Challenge
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.
Mission 7

πŸŒ€Spin Detector

+100 XP

The 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!

πŸ† Challenge
Play a higher note the faster you spin (hint: clue.start_tone(200 + int(abs(z)))).
Mission 8

🧭Magnet Hunter & Compass

+150 XP

The 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.

πŸ”§ Real-world alert
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.
πŸ† Challenge
Show "N", "E", "S" or "W" instead of a number.
Mission 9

βœ‹Jedi Hand Powers

+150 XP

The 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()
πŸ† Challenge
Swipe left/right to change the NeoPixel color through a list, like flipping pages.
Mission 10

🎨Color Copycat

+150 XP

The 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.)

πŸ† Challenge
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.
Mission 11

β›…Weather Station

+150 XP

Four air sensors in one program:

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)
πŸ”¬ Experiments
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.
🌑️ Heads up
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.
πŸ† Challenge
Turn the NeoPixel blue when it's cold, green when comfy, and red when hot. Use the corrected temp_c.
Mission 12

πŸ‘Clap Detector

+150 XP

The 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.

πŸ† Challenge
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.
βš”οΈ Boss Level

⚑Reaction Race

+300 XP

Time 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".

πŸ† Boss Challenges
β‘  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.
πŸ“œ Cheat Sheet

Every CLUE power on one page

Always start with from adafruit_clue import clue.

GadgetCodeWhat 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 = groupYour own displayio drawing
NeoPixelclue.pixel.fill((r, g, b))
clue.pixel.brightness = 0.2
Any color, 0 to 255 each
Red LEDclue.red_led = TrueOn / off
White LEDsclue.white_leds = TrueOn / off
Buttonsclue.button_a, clue.button_bTrue while pressed
Touch padsclue.touch_0, clue.touch_1, clue.touch_2True while touched
Speakerclue.play_tone(440, 0.5)
clue.start_tone(440) / clue.stop_tone()
Beeps and notes
Microphoneclue.sound_level
clue.loud_sound(sound_threshold=500)
Loudness number / True if loud
Accelerometerx, y, z = clue.accelerationm/sΒ² (gravity β‰ˆ 9.8)
Shakeclue.shake(shake_threshold=20)True if shaken (30 is the default)
Gyroscopex, y, z = clue.gyroSpin speed, degrees/second
Magnetometerx, y, z = clue.magneticMicroteslas
Proximityclue.proximity0 (far) to 255 (close)
Gestureclue.gesture0 none, 1 up, 2 down, 3 left, 4 right
Colorr, g, b, c = clue.colorRed, green, blue, clear light
Temperatureclue.temperatureΒ°C
Humidityclue.humidity% relative humidity
Pressureclue.pressurehPa
Altitudeclue.sea_level_pressure = 1013.25
clue.altitude
Meters

More adventures