Architecture
The Study Buddy is the smartwatch's five-mode program with three additions: a mode list that lives in a file, two small services that stay loaded in every mode, and a Library for downloading new things.
Two Kinds of Downloads
| Pack | Mode | |
|---|---|---|
| What it is | Data: quiz decks, quotes, events | A Python module (mode_*.py) |
| Format | JSON (see Pack Formats) | Python source |
| What it can do | Nothing. It only gets read. | Anything. MicroPython has no sandbox, so a mode can read secrets.py, rewrite files, or use the network. |
| Who writes one | Anyone, including students | The teacher (or an instructor-approved author) |
| Who publishes it | The teacher adds it to the class Library | The teacher adds it to the class Library |
| Who installs it | Students and teachers, from the Library | Students and teachers, from the Library |
| Install check | JSON parses and has the right kind |
SHA-256 matches the manifest, and the person confirms |
Both students and instructors use the Library, so a student never needs to copy a file or open Thonny to get a new deck or mode. What keeps that safe is that students choose from what the teacher has published, and cannot point the Library at an address of their own on the device.
Downloaded modes are trusted code
There is no sandbox on a Pico. The only protection is that modes come from a teacher-controlled HTTPS address, and that the manifest lists each file's SHA-256 hash. A student can still write their own packs freely. Never add a Library address you do not control.
The settings that limit what a student can install live in
config.py, which a student can also edit. They are guardrails for
honest mistakes, not security. A student who wants to run their own
code on their own Pico can always do so.
Files on the Pico
1 2 3 4 5 6 7 8 9 10 11 12 | |
modes.json replaces the hard-coded MODES tuple in the template:
1 2 3 4 5 6 7 8 9 10 | |
Changes to the Main Template
The template's contract does not change. A mode still has
start(display, up, down, saved), update(now), on_mode(kind), and
stop(). What changes is the program around the modes:
- Read the mode list from
modes.json. If the file is missing or broken, fall back to the five built-in modes, so a bad download can never brick the device. - Dots show only pinned modes. MODE steps through the pinned modes only, and the dots draw one per pinned mode, with a maximum of 8. Modes that are not pinned are reached from the Library. The dot row is currently 16 px apart, so 8 dots fit in 112 px.
- Import
soundandremindersbeforeloaded_beforeis taken. The template deletes every module a mode imported that was not loaded before it. Ifsound.pywere imported by a mode, switching modes would delete it and tear down the I2S object mid-chime. Importing both at the top of the template keeps them resident. - Add a reminder check to the main loop (see below).
- Use only modes whose file exists. A mode named in
modes.jsonbut missing from flash is skipped, not fatal. - Handle the mute chord. UP and DOWN held together for 1 second toggles the sound in every mode (see Audio).
- Honor a mode's optional
busyflag. It is a module-level variable a mode sets toTruewhile it must not be interrupted. A mode that does not have one is never busy.
The reminder check
wake_at cannot be used for reminders. It is a time.ticks_ms() value,
and on this port ticks_ms() wraps after about 12 days (verify), with
ticks_diff() only reliable for about half that. A quiz next Friday can
be farther away than the tick counter can say.
Reminders use the wall clock instead. Since wifi_time.sync_time()
sets the RTC to local time, time.mktime(time.localtime()) is a plain
integer that can be compared with an event's local time. Nothing in the
timer's wake_at mechanism changes.
The resident reminders.py loads user/events.json once, works out the
next time anything should fire, and keeps just that one integer. The
main loop does one integer comparison per pass:
1 2 3 4 | |
When a reminder fires, the template switches straight to mode_reminder
(the same trick the countdown timer uses for its alarm), whatever mode is
showing. Switching modes from a reminder never interrupts an active quiz
question or a timer being set: those modes return True from on_mode()
and also set busy = True, and the template waits, up to 30 seconds,
before switching.
The Library Mode
mode_library.py is the only part that downloads anything. It is built
for an 11-year-old to use alone, so the screens use plain words. Hashes,
manifests, and byte counts never appear on screen.
Two tabs: Browse and Installed
The Library keeps MODE for itself, as the quiz does, so a short press confirms. The tabs switch with a hold of MODE, the same gesture other modes use to leave a task or change the view:
| Tab | What it shows | UP / DOWN | Hold UP | Hold DOWN |
|---|---|---|---|---|
| Browse | What the Library offers, featured items first | Move between items | Install the item | (nothing) |
| Installed | What is on this device, with free space ("1.2 MB free") | Move between items | Pin or unpin a mode | Remove the item |
Built-in modes cannot be removed. Removing a deck also removes that deck's saved progress after a "Remove it?" confirm that takes a second press, so a slip of the finger does not lose a month of practice.
Browse screen
Each item gets one screen:
1 2 3 4 5 | |
- A title, a one-line
description, a type (DECK for a pack, MODE for code), a size, and GOT IT if already installed. - A mode's confirm screen says, in plain words, "This adds a new mode from your teacher's library. Hold UP again to add it." That is all.
- Items needing a module the device lacks show "Needs: sound" and cannot be installed.
- Items the teacher marks
featuredappear first, with a star.
Installing
- Connect to WiFi and fetch the manifest. With one library in
LIBRARIES(see below) the Browse tab opens straight away. With more than one, a short first screen lists them byname, and UP, DOWN, and MODE choose. - Download to a temporary file, check the byte count and SHA-256, then rename it into place. A failed download never replaces a working file. On failure the screen says "Didn't work. Try again.", with the reason (WiFi, download, or damaged file) in the Thonny shell only.
- For a mode, add it to
modes.jsonas not pinned. The Installed tab lets the student pin or unpin it. Only up to 8 can be pinned. - Play the
rightjingle, and show "Got it!"
More than one library
config.py lists the libraries the device may use:
1 2 3 4 5 6 | |
| Field | Meaning |
|---|---|
name |
Shown when choosing between libraries. |
url |
The manifest's address. HTTPS. |
modes |
True to show modes from this library. False hides every item of type mode, so a library of student-made decks can never put code on a device. |
The student-facing screens never ask for an address. Only a person
editing config.py can add a library, and the setup guide tells them not
to add one they do not control. A teacher who wants younger students to
get decks only sets "modes": False for every library, and installs
modes on the devices ahead of time.
What the teacher does
| Task | How |
|---|---|
| Publish a deck or mode | Put the file in a folder and run the manifest script on the computer. It writes manifest.json with each file's size and hash. Nobody types a hash. |
| Review student decks | Students hand in a deck file. The teacher checks it, including each quote's source, then adds it to the class folder. |
| Feature an item | Set "featured": true in the item's entry, or pass --featured to the script. |
| Retire an item | Remove it from the folder and run the script again. Devices that already have it keep it. |
| Pre-install for a class | Put the files on the Pico with upload-code.sh, so a student's first run needs no WiFi. |
The manifest script is tools/make_manifest.py in the kit's folder
(written in Lab 09).
hashlib.sha256 is available in MicroPython's rp2 builds (verify on the
firmware version the kit ships). Downloads use HTTPS. Whether the Pico
2 W's TLS stack can reach GitHub Pages reliably is a verify item; if
not, the fallback is a teacher-run web server on the school network.
The manifest
See Pack Formats for the exact
fields. The manifest is a flat list, so a school can publish its own and
point a device at it with one entry in LIBRARIES.
What a Mode Must Follow
All the watch kit's rules still apply, plus three new ones:
- Do not draw in the dot strip at the bottom.
- Do not clear the screen except in
start(). - Do not make a full-screen fill (131 ms) while a sound is playing. See Audio.
- Set
busy = Truewhile the student is in the middle of something that must not be interrupted by a reminder. - Read packs through the helper in
packs.py, which enforces size limits, instead of opening files directly.