From 111ae65ebcc88c03ea255bdabec19c9f99f3e8c0 Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Tue, 11 Nov 2025 21:55:05 +0100 Subject: [PATCH 1/3] cleanup: Remove unused python files from src Remove python files that are not referenced directly or indirectly by main.py. Signed-off-by: Matthias Blankertz --- software/src/led_test.py | 58 ------------------ software/src/mfrc522_test.py | 73 ---------------------- software/src/test.py | 113 ----------------------------------- 3 files changed, 244 deletions(-) delete mode 100644 software/src/led_test.py delete mode 100644 software/src/mfrc522_test.py delete mode 100644 software/src/test.py diff --git a/software/src/led_test.py b/software/src/led_test.py deleted file mode 100644 index 0674460..0000000 --- a/software/src/led_test.py +++ /dev/null @@ -1,58 +0,0 @@ -# SPDX-License-Identifier: MIT -# Copyright (c) 2024 Matthias Blankertz - -# Run with mpremote.py run src/led_test.py - -from machine import Pin -from math import pi, sin, pow -from micropython import const -from rp2_neopixel import NeoPixel -from time import sleep, ticks_ms -import asyncio - -pin = Pin.board.GP16 -leds = const(10) -brightness = 0.5 - -np = NeoPixel(pin, leds) - -# test fill and write - -print("LEDs should now turn red") -np.fill((255, 0, 0)) -np.write() -sleep(1) - -print("LEDs should now turn green") -np.fill((0, 255, 0)) -np.write() -sleep(1) - -print("LEDs should now turn blue") -np.fill((0, 0, 255)) -np.write() -sleep(1) - - -# test async -def gamma(value, X=2.2): - return min(max(int(brightness * pow(value / 255.0, X) * 255.0 + 0.5), 0), 255) - - -async def rainbow(np, period=10): - count = 0.0 - while True: - for i in range(leds): - ofs = (count + i) % leds - np[i] = (gamma((sin(ofs / leds * 2 * pi) + 1) * 127), - gamma((sin(ofs / leds * 2 * pi + 2/3*pi) + 1) * 127), - gamma((sin(ofs / leds * 2 * pi + 4/3*pi) + 1) * 127)) - count += 0.2 - before = ticks_ms() - await np.async_write() - now = ticks_ms() - if before + 20 > now: - await asyncio.sleep_ms(20 - (now - before)) - -print("LEDs should now start rainbowing") -asyncio.run(rainbow(np)) diff --git a/software/src/mfrc522_test.py b/software/src/mfrc522_test.py deleted file mode 100644 index 2144183..0000000 --- a/software/src/mfrc522_test.py +++ /dev/null @@ -1,73 +0,0 @@ -from mfrc522 import MFRC522 -import asyncio -import time - -delay_sum = 0 -delay_count = 0 -max_delay = 0 - - -async def latency_test(): - global delay_sum - global delay_count - global max_delay - global min_delay - min_delay = 0xffffffff - await asyncio.sleep_ms(1) - while True: - for _ in range(2000): - before = time.ticks_us() - await asyncio.sleep(0) - after = time.ticks_us() - delay = after - before - delay_sum += delay - delay_count += 1 - if delay > max_delay: - max_delay = delay - if delay < min_delay: - min_delay = delay - await asyncio.sleep_ms(1) - print(f"delay (min / max / avg) [µs]: ({min_delay} / {max_delay} / {delay/delay_sum})") - - -def uid_to_string(uid: list): - return '0x' + ''.join(f'{i:02x}' for i in uid) - - -async def get_tag_uid(reader: MFRC522, poll_interval_ms: int = 50) -> list: - ''' - The maximum measured delay with poll_interval_ms=50 and a reader with tocard_retries=5 is - 15.9 ms: - delay (min / max / avg) [µs]: (360 / 15945 / 1.892923e-06) - - The maximum measured delay dropped to 11.6 ms by setting tocard_retries=1: - delay (min / max / avg) [µs]: (368 / 11696 / 6.204211e-06) - ''' - while True: - reader.init() - - # For now we omit the tag type - (stat, _) = reader.request(reader.REQIDL) - if stat == reader.OK: - (stat, uid) = reader.SelectTagSN() - if stat == reader.OK: - print(f"uid={uid_to_string(uid)}") - - await asyncio.sleep_ms(poll_interval_ms) - - -def main(): - reader = MFRC522(spi_id=1, sck=10, miso=12, mosi=11, cs=13, rst=9, tocard_retries=1) - - print("") - print("Please place card on reader") - print("") - - asyncio.create_task(get_tag_uid(reader)) - asyncio.create_task(latency_test()) - - asyncio.get_event_loop().run_forever() - - -if __name__ == "__main__": - main() diff --git a/software/src/test.py b/software/src/test.py deleted file mode 100644 index 7b09b82..0000000 --- a/software/src/test.py +++ /dev/null @@ -1,113 +0,0 @@ -# SPDX-License-Identifier: MIT -# Copyright (c) 2024-2025 Matthias Blankertz - -import aiorepl -import asyncio -import machine -import micropython -import os -import time -from machine import Pin -from math import pi, sin, pow -from micropython import const - -# Own modules -from audiocore import Audiocore -from mp3player import MP3Player -from rp2_neopixel import NeoPixel -from rp2_sd import SDCard - -micropython.alloc_emergency_exception_buf(100) - -leds = const(10) -brightness = 0.5 - - -def gamma(value, X=2.2): - return min(max(int(brightness * pow(value / 255.0, X) * 255.0 + 0.5), 0), 255) - - -async def rainbow(np, period=10): - count = 0.0 - while True: - for i in range(leds): - ofs = (count + i) % leds - np[i] = (gamma((sin(ofs / leds * 2 * pi) + 1) * 127), - gamma((sin(ofs / leds * 2 * pi + 2/3*pi) + 1) * 127), - gamma((sin(ofs / leds * 2 * pi + 4/3*pi) + 1) * 127)) - count += 0.2 - before = time.ticks_ms() - await np.async_write() - now = time.ticks_ms() - if before + 20 > now: - await asyncio.sleep_ms(20 - (now - before)) - - -# Set 8 mA drive strength and fast slew rate -machine.mem32[0x4001c004 + 6*4] = 0x67 -machine.mem32[0x4001c004 + 7*4] = 0x67 -machine.mem32[0x4001c004 + 8*4] = 0x67 - - -def list_sd(): - try: - sd = SDCard(mosi=Pin(3), miso=Pin(4), sck=Pin(2), ss=Pin(5), baudrate=15000000) - except OSError: - for i in range(leds): - np[i] = (255, 0, 0) - np.write() - return - try: - os.mount(sd, '/sd') - print(os.listdir(b'/sd')) - except OSError as ex: - print(f"{ex}") - - -delay_sum = 0 -delay_count = 0 -max_delay = 0 - - -async def latency_test(): - global delay_sum - global delay_count - global max_delay - await asyncio.sleep_ms(1) - while True: - for _ in range(2000): - before = time.ticks_us() - await asyncio.sleep(0) - after = time.ticks_us() - delay = after - before - delay_sum += delay - delay_count += 1 - if delay > max_delay: - max_delay = delay - await asyncio.sleep_ms(1) - print(f"Max delay {max_delay} us, average {delay/delay_sum} us") - -pin = Pin.board.GP16 -np = NeoPixel(pin, leds) - -# Test SD card -list_sd() - -# Test NeoPixel -asyncio.create_task(rainbow(np)) - -# Test audio -audioctx = Audiocore(Pin(8), Pin(6)) - -player = MP3Player(audioctx) - -# high prio for proc 1 -machine.mem32[0x40030000 + 0x00] = 0x10 - -testfiles = [b'/sd/' + name for name in os.listdir(b'/sd') if name.endswith(b'mp3')] -player.set_playlist(testfiles) -asyncio.create_task(player.task()) - - -asyncio.create_task(aiorepl.task({'player': player})) -asyncio.get_event_loop().run_forever() From 3c23fc1446fbe1fea2682530816bc4e91aa1b752 Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Tue, 11 Nov 2025 22:01:15 +0100 Subject: [PATCH 2/3] feat: freeze all python code, restructure build Add all python code run on the device to the manifest.py to freeze it. This has two benefits: - It precompiles the bytecode, so it is smaller and faster - All code (C and python) is now in the firmware image, leaving the littlefs filesystem on the flash free for user settings. This requires changing the way the hardware variants are handled. There is now only one _filesystem_ image, but instead there are different _firmware_ images for each variant. Signed-off-by: Matthias Blankertz --- software/boards/RPI_PICO_W/manifest-Rev1.py | 3 ++ .../boards/RPI_PICO_W/manifest-breadboard.py | 3 ++ software/boards/RPI_PICO_W/manifest.py | 7 +++ software/build.sh | 50 +++++++++++-------- software/flash.sh | 6 ++- .../hwconfig.py} | 0 .../hwconfig.py} | 0 7 files changed, 45 insertions(+), 24 deletions(-) create mode 100644 software/boards/RPI_PICO_W/manifest-Rev1.py create mode 100644 software/boards/RPI_PICO_W/manifest-breadboard.py rename software/src/{hwconfig_Rev1.py => hwconfig_Rev1/hwconfig.py} (100%) rename software/src/{hwconfig_breadboard.py => hwconfig_breadboard/hwconfig.py} (100%) diff --git a/software/boards/RPI_PICO_W/manifest-Rev1.py b/software/boards/RPI_PICO_W/manifest-Rev1.py new file mode 100644 index 0000000..277458b --- /dev/null +++ b/software/boards/RPI_PICO_W/manifest-Rev1.py @@ -0,0 +1,3 @@ +include("manifest.py") + +module("hwconfig.py", "../../src/hwconfig_Rev1") diff --git a/software/boards/RPI_PICO_W/manifest-breadboard.py b/software/boards/RPI_PICO_W/manifest-breadboard.py new file mode 100644 index 0000000..6551a41 --- /dev/null +++ b/software/boards/RPI_PICO_W/manifest-breadboard.py @@ -0,0 +1,3 @@ +include("manifest.py") + +module("hwconfig.py", "../../src/hwconfig_breadboard") diff --git a/software/boards/RPI_PICO_W/manifest.py b/software/boards/RPI_PICO_W/manifest.py index db382dd..f8cb12a 100644 --- a/software/boards/RPI_PICO_W/manifest.py +++ b/software/boards/RPI_PICO_W/manifest.py @@ -15,3 +15,10 @@ module("microdot.py", "../../lib/microdot/src/microdot/") # TonberryPico modules module("audiocore.py", "../../modules/audiocore") module("rp2_neopixel.py", "../../modules") + +module("main.py", "../../src") +module("app.py", "../../src") +module("mp3player.py", "../../src") +module("webserver.py", "../../src") +package("utils", base_path="../../src") +package("nfc", base_path="../../src") diff --git a/software/build.sh b/software/build.sh index b373a8e..6fd759a 100755 --- a/software/build.sh +++ b/software/build.sh @@ -6,9 +6,6 @@ set -eu ( cd lib/micropython make -C mpy-cross -j "$(nproc)" - make -C ports/rp2 BOARD=TONBERRY_RPI_PICO_W BOARD_DIR="$TOPDIR"/boards/RPI_PICO_W clean - make -C ports/rp2 BOARD=TONBERRY_RPI_PICO_W BOARD_DIR="$TOPDIR"/boards/RPI_PICO_W \ - USER_C_MODULES="$TOPDIR"/modules/micropython.cmake -j "$(nproc)" # build tonberry specific unix port of micropython make -C ports/unix VARIANT_DIR="$TOPDIR"/boards/tonberry_unix clean @@ -19,31 +16,40 @@ set -eu make -j "$(nproc)" ) -PICOTOOL=picotool -if ! command -v $PICOTOOL >/dev/null 2>&1; then - echo "system picotool not found, checking SDK build dir" - PICOTOOL=lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/_deps/picotool-build/picotool - if ! command -v $PICOTOOL >/dev/null 2>&1; then - echo "No picotool found, exiting" - exit 1 - fi -fi BUILDDIR=lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/ +OUTDIR=$(pwd)/build +mkdir -p "$OUTDIR" FS_STAGE_DIR=$(mktemp -d) +mkdir "$FS_STAGE_DIR"/fs trap 'rm -rf $FS_STAGE_DIR' EXIT -for hwconfig in src/hwconfig_*.py; do +tools/mklittlefs/mklittlefs -p 256 -s 868352 -c "$FS_STAGE_DIR"/fs "$FS_STAGE_DIR"/filesystem.bin + +for hwconfig in boards/RPI_PICO_W/manifest-*.py; do hwconfig_base=$(basename "$hwconfig") - hwname=${hwconfig_base##hwconfig_} + hwname=${hwconfig_base##manifest-} hwname=${hwname%%.py} - find src/ -iname '*.py' \! -iname 'hwconfig_*.py' | cpio -pdm "$FS_STAGE_DIR" - cp "$hwconfig" "$FS_STAGE_DIR"/src/hwconfig.py - tools/mklittlefs/mklittlefs -p 256 -s 868352 -c "$FS_STAGE_DIR"/src $BUILDDIR/filesystem.bin + hwconfig_abs=$(realpath "$hwconfig") + ( cd lib/micropython + make -C ports/rp2 BOARD=TONBERRY_RPI_PICO_W BOARD_DIR="$TOPDIR"/boards/RPI_PICO_W clean + make -C ports/rp2 BOARD=TONBERRY_RPI_PICO_W BOARD_DIR="$TOPDIR"/boards/RPI_PICO_W \ + USER_C_MODULES="$TOPDIR"/modules/micropython.cmake \ + FROZEN_MANIFEST="$hwconfig_abs" -j "$(nproc)" + ) + PICOTOOL=picotool + if ! command -v $PICOTOOL >/dev/null 2>&1; then + echo "system picotool not found, checking SDK build dir" + PICOTOOL=lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/_deps/picotool-build/picotool + if ! command -v $PICOTOOL >/dev/null 2>&1; then + echo "No picotool found, exiting" + exit 1 + fi + fi truncate -s 2M $BUILDDIR/firmware-filesystem.bin dd if=$BUILDDIR/firmware.bin of=$BUILDDIR/firmware-filesystem.bin bs=1k - dd if=$BUILDDIR/filesystem.bin of=$BUILDDIR/firmware-filesystem.bin bs=1k seek=1200 - $PICOTOOL uf2 convert $BUILDDIR/firmware-filesystem.bin $BUILDDIR/firmware-filesystem-"$hwname".uf2 - rm -r "${FS_STAGE_DIR:?}"/* + dd if="$FS_STAGE_DIR"/filesystem.bin of=$BUILDDIR/firmware-filesystem.bin bs=1k seek=1200 + cp $BUILDDIR/firmware.uf2 "$OUTDIR"/firmware-"$hwname".uf2 + $PICOTOOL uf2 convert $BUILDDIR/firmware-filesystem.bin "$OUTDIR"/firmware-filesystem-"$hwname".uf2 done -echo "Output in $BUILDDIR/firmware.uf2" -echo "Images with filesystem in" ${BUILDDIR}firmware-filesystem-*.uf2 +echo "Output in" "${OUTDIR}"/firmware-*.uf2 +echo "Images with filesystem in" "${OUTDIR}"/firmware-filesystem-*.uf2 diff --git a/software/flash.sh b/software/flash.sh index 459db12..bea671a 100755 --- a/software/flash.sh +++ b/software/flash.sh @@ -2,6 +2,8 @@ set -eu +TOPDIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + check_command() { name=$1 @@ -16,7 +18,7 @@ check_command lsusb check_command picotool DEVICEPATH=/dev/disk/by-label/RPI-RP2 -IMAGEPATH=lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/ +IMAGEPATH=${TOPDIR}/build REVISION=Rev1 flash_via_mountpoint() @@ -83,7 +85,7 @@ if [ $# -gt 0 ]; then usage fi -IMAGEFILE="$IMAGEPATH"/firmware-filesystem-$REVISION.uf2 +IMAGEFILE="$IMAGEPATH"/firmware-$REVISION.uf2 if [ "$FLASH_VIA_MOUNTPOINT" -eq 0 ]; then flash_via_picotool diff --git a/software/src/hwconfig_Rev1.py b/software/src/hwconfig_Rev1/hwconfig.py similarity index 100% rename from software/src/hwconfig_Rev1.py rename to software/src/hwconfig_Rev1/hwconfig.py diff --git a/software/src/hwconfig_breadboard.py b/software/src/hwconfig_breadboard/hwconfig.py similarity index 100% rename from software/src/hwconfig_breadboard.py rename to software/src/hwconfig_breadboard/hwconfig.py From 19afb2f936816676670b8f6a3bef15d60bb324e6 Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Tue, 11 Nov 2025 22:14:13 +0100 Subject: [PATCH 3/3] ci: Fix build.yaml for changed output dir Signed-off-by: Matthias Blankertz --- .gitea/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index a33e09a..2a614db 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -17,9 +17,9 @@ jobs: uses: actions/upload-artifact@v3 with: name: firmware-RPi-Pico-W - path: software/lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/firmware.uf2 + path: software/build/firmware-*.uf2 - name: Upload firmware w/ filesystem uses: actions/upload-artifact@v3 with: name: firmware-RPi-Pico-W-with-fs - path: software/lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/firmware-filesystem-*.uf2 + path: software/build/firmware-filesystem-*.uf2