Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3bbbbea
Switch f469-disco to fork with upgraded MicroPython
miketlk May 27, 2025
b939993
Update Makefile for Micropython v1.25 (Specter adaptation)
miketlk Jun 8, 2025
edb46e9
Update f469-disco pointer
miketlk Jun 8, 2025
13144da
Update f469-disco
miketlk Jun 10, 2025
d9504a6
Update f469-disco
miketlk Jun 10, 2025
2bd37a0
Update f469-disco
miketlk Jun 24, 2025
3b655db
Fix missing inclusion of custom MicroPython config in Unix build
miketlk Jun 24, 2025
29b0190
Update f469-disco
miketlk Aug 14, 2025
995511f
fix Makefile issues with clean and unix targets
miketlk Aug 25, 2025
f91921c
Update f469-disco
miketlk Aug 25, 2025
592edb1
Update f469-disco
miketlk Sep 6, 2025
6cc2ac5
Update macOS build instructions (SDL2 notice)
miketlk Sep 11, 2025
78a21e6
Fix build doc for macOS: move SDL2 to the Simulator section
miketlk Sep 11, 2025
f6cb902
Add VcXsrv to build doc and improve structure
miketlk Sep 12, 2025
4694287
Update f469-disco
miketlk Sep 16, 2025
b9f3813
Update build instructions: avoid conflicts with Homebrew's includes
miketlk Sep 16, 2025
64860ca
Upgrade Docker container to Arm GNU Toolchain v14.3.rel1 and Python v…
miketlk Sep 16, 2025
705e962
Update f469-disco
miketlk Oct 13, 2025
7b0afa3
feat: disco helper tool and requirements
k9ert Dec 19, 2025
00edc4a
chore: dev config (nix, envrc, claude)
k9ert Dec 19, 2025
5107ca1
docs: architecture, prd, debugging guides
k9ert Dec 19, 2025
ad09b46
build: Makefile debug/disco targets
k9ert Dec 19, 2025
e49bf03
fix: LVGL 9.x migration for core GUI components an QR flows
k9ert Dec 19, 2025
816e7d2
fix: scanner
k9ert Dec 19, 2025
a2993dd
fix: more python fixes for lvgl
k9ert Dec 20, 2025
61f70bc
fix: complete LVGL 9.x migration for simulator
k9ert Jan 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ release
.idea
.vscode
.DS_Store
.claude
.augment
.bmad-core
.direnv
fwbox
.beads
.history
3 changes: 2 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[submodule "f469-disco"]
path = f469-disco
url = https://github.com/diybitcoinhardware/f469-disco
url = https://github.com/miketlk/f469-disco.git
branch = micropython-upgrade
[submodule "bootloader"]
path = bootloader
url = https://github.com/cryptoadvance/specter-bootloader
79 changes: 79 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Beads Workflow Context

> **Context Recovery**: Run `bd prime` after compaction, clear, or new session
> Hooks auto-call this in Claude Code when .beads/ detected

# 🚨 SESSION CLOSE PROTOCOL 🚨

**CRITICAL**: Before saying "done" or "complete", you MUST run this checklist:

```
[ ] 1. git status (check what changed)
[ ] 2. git add <files> (stage code changes)
[ ] 3. bd sync (commit beads changes)
[ ] 4. git commit -m "..." (commit code)
[ ] 5. bd sync (commit any new beads changes)
[ ] 6. git push (push to remote)
```

**NEVER skip this.** Work is not done until pushed.

## Core Rules
- Track ALL work in beads (no TodoWrite tool, no markdown TODOs)
- Use `bd create` to create issues, not TodoWrite tool
- Git workflow: hooks auto-sync, run `bd sync` at session end
- Session management: check `bd ready` for available work

## Essential Commands

### Finding Work
- `bd ready` - Show issues ready to work (no blockers)
- `bd list --status=open` - All open issues
- `bd list --status=in_progress` - Your active work
- `bd show <id>` - Detailed issue view with dependencies

### Creating & Updating
- `bd create --title="..." --type=task|bug|feature --priority=2` - New issue
- Priority: 0-4 or P0-P4 (0=critical, 2=medium, 4=backlog). NOT "high"/"medium"/"low"
- `bd update <id> --status=in_progress` - Claim work
- `bd update <id> --assignee=username` - Assign to someone
- `bd close <id>` - Mark complete
- `bd close <id1> <id2> ...` - Close multiple issues at once (more efficient)
- `bd close <id> --reason="explanation"` - Close with reason
- **Tip**: When creating multiple issues/tasks/epics, use parallel subagents for efficiency

### Dependencies & Blocking
- `bd dep add <issue> <depends-on>` - Add dependency (issue depends on depends-on)
- `bd blocked` - Show all blocked issues
- `bd show <id>` - See what's blocking/blocked by this issue

### Sync & Collaboration
- `bd sync` - Sync with git remote (run at session end)
- `bd sync --status` - Check sync status without syncing

### Project Health
- `bd stats` - Project statistics (open/closed/blocked counts)
- `bd doctor` - Check for issues (sync problems, missing hooks)

## Common Workflows

**Starting work:**
```bash
bd ready # Find available work
bd show <id> # Review issue details
bd update <id> --status=in_progress # Claim it
```

**Completing work:**
```bash
bd close <id1> <id2> ... # Close all completed issues at once
bd sync # Push to remote
```

**Creating dependent work:**
```bash
# Run bd create commands in parallel (use subagents for many items)
bd create --title="Implement feature X" --type=feature
bd create --title="Write tests for X" --type=task
bd dep add beads-yyy beads-xxx # Tests depend on Feature (Feature blocks tests)
```
24 changes: 24 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Please read
* all the files in docs/architecture
* all files in docs/prd
* docs/debugging.md

Run `scripts/disco --help` to understand your options with the board.

Run `scripts/disco` commands to see whether there is a board connected and in which status it is.

## Serial Device Safety

**Prefer** `scripts/disco` for board/serial interaction - it handles timeouts properly.

If you must access serial devices directly, **always wrap with `timeout`**:

```bash
# DANGEROUS - can freeze session indefinitely:
cat /dev/cu.usbmodem*
echo "test" > /dev/cu.usbmodem*
stty -f /dev/cu.usbmodem* ...

# SAFE - with timeout:
timeout 3 cat /dev/cu.usbmodem*
```
55 changes: 41 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,50 @@
FROM python:3.9.15@sha256:b5f024fa682187ef9305a2b5d2c4bb583bef83356259669fc80273bb2222f5ed
ENV LANG C.UTF-8
# syntax=docker/dockerfile:1.6
FROM python:3.9.23-bookworm@sha256:dc01447eea126f97459cbcb0e52a5863fcc84ff53462650ae5a28277c175f49d

ENV LANG=C.UTF-8
ARG DEBIAN_FRONTEND=noninteractive
ARG TOOLCHAIN_VER=14.3.rel1
ARG TARGETARCH
ARG TARGET_DIR="/opt/arm-toolchain"

# ARM Embedded Toolchain
# Integrity is checked using the MD5 checksum provided by ARM at https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
RUN curl -sSfL -o arm-toolchain.tar.bz2 "https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz2?revision=05382cca-1721-44e1-ae19-1e7c3dc96118&rev=05382cca172144e1ae191e7c3dc96118&hash=3ACFE672E449EBA7A21773EE284A88BC7DFA5044" && \
echo 2b9eeccc33470f9d3cda26983b9d2dc6 arm-toolchain.tar.bz2 > /tmp/arm-toolchain.md5 && \
md5sum --check /tmp/arm-toolchain.md5 && rm /tmp/arm-toolchain.md5 && \
tar xf arm-toolchain.tar.bz2 -C /opt && \
rm arm-toolchain.tar.bz2
# Minimal deps for download/verify/extract
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl xz-utils grep coreutils \
&& rm -rf /var/lib/apt/lists/*

# Adding GCC to PATH and defining rustup/cargo home directories
ENV PATH=/opt/gcc-arm-none-eabi-9-2020-q2-update/bin:$PATH
# Default to Arm's blob storage mirror
ARG TOOLCHAIN_MIRROR=armkeil.blob.core.windows.net/developer/Files/downloads/gnu

# Installing python requirements
# Pin Arm GNU Toolchain per-arch with SHA256 sums
ENV TOOLCHAIN_SHA256_AMD64=8f6903f8ceb084d9227b9ef991490413014d991874a1e34074443c2a72b14dbd
ENV TOOLCHAIN_SHA256_ARM64=2d465847eb1d05f876270494f51034de9ace9abe87a4222d079f3360240184d3

# Arm GNU Toolchain (arm-none-eabi), host-aware, SHA-256 verified, cached download
RUN --mount=type=cache,target=/root/.cache \
set -eux; \
case "$TARGETARCH" in \
amd64) host="x86_64"; TOOLCHAIN_SHA256="$TOOLCHAIN_SHA256_AMD64" ;; \
arm64) host="aarch64"; TOOLCHAIN_SHA256="$TOOLCHAIN_SHA256_ARM64" ;; \
*) echo "Unsupported arch: $TARGETARCH" && exit 1 ;; \
esac; \
file="arm-gnu-toolchain-${TOOLCHAIN_VER}-${host}-arm-none-eabi.tar.xz"; \
url="https://${TOOLCHAIN_MIRROR}/${TOOLCHAIN_VER}/binrel/${file}"; \
dest="/root/.cache/$file"; \
if [ ! -f "$dest" ]; then \
echo "Downloading $url"; \
curl -fSL --retry 5 --retry-all-errors -C - -o "$dest" "$url"; \
else \
echo "Using cached $dest"; \
fi; \
echo "${TOOLCHAIN_SHA256} $dest" | sha256sum -c -; \
tar -xJf "$dest" -C /opt; \
ln -s /opt/arm-gnu-toolchain-${TOOLCHAIN_VER}-${host}-arm-none-eabi ${TARGET_DIR}

ENV PATH="${TARGET_DIR}/bin:${PATH}"

# Python deps
COPY bootloader/tools/requirements.txt .
RUN pip3 install -r requirements.txt
RUN python -m pip install --no-cache-dir -r requirements.txt

WORKDIR /app

CMD ["/usr/bin/env", "bash", "./build_firmware.sh"]
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ mpy-cross: $(TARGET_DIR) $(MPY_DIR)/mpy-cross/Makefile
@echo Building cross-compiler
make -C $(MPY_DIR)/mpy-cross \
DEBUG=$(DEBUG) && \
cp $(MPY_DIR)/mpy-cross/mpy-cross $(TARGET_DIR)
cp $(MPY_DIR)/mpy-cross/build/mpy-cross $(TARGET_DIR)

# disco board with bitcoin library
disco: $(TARGET_DIR) mpy-cross $(MPY_DIR)/ports/stm32
@echo Building firmware
@rm -rf $(MPY_DIR)/ports/stm32/build-$(BOARD)/frozen_mpy $(MPY_DIR)/ports/stm32/build-$(BOARD)/frozen_content.c
make -C $(MPY_DIR)/ports/stm32 \
BOARD=$(BOARD) \
FLAVOR=$(FLAVOR) \
USE_DBOOT=$(USE_DBOOT) \
USER_C_MODULES=$(USER_C_MODULES) \
FROZEN_MANIFEST=$(FROZEN_MANIFEST_DISCO) \
CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_specter.h>"' \
DEBUG=$(DEBUG) && \
arm-none-eabi-objcopy -O binary \
$(MPY_DIR)/ports/stm32/build-STM32F469DISC/firmware.elf \
Expand All @@ -42,12 +44,14 @@ disco: $(TARGET_DIR) mpy-cross $(MPY_DIR)/ports/stm32
# disco board with bitcoin library
debug: $(TARGET_DIR) mpy-cross $(MPY_DIR)/ports/stm32
@echo Building firmware
@rm -rf $(MPY_DIR)/ports/stm32/build-$(BOARD)/frozen_mpy $(MPY_DIR)/ports/stm32/build-$(BOARD)/frozen_content.c
make -C $(MPY_DIR)/ports/stm32 \
BOARD=$(BOARD) \
FLAVOR=$(FLAVOR) \
USE_DBOOT=$(USE_DBOOT) \
USER_C_MODULES=$(USER_C_MODULES) \
FROZEN_MANIFEST=$(FROZEN_MANIFEST_DEBUG) \
CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_specter.h>"' \
DEBUG=$(DEBUG) && \
arm-none-eabi-objcopy -O binary \
$(MPY_DIR)/ports/stm32/build-STM32F469DISC/firmware.elf \
Expand All @@ -59,10 +63,12 @@ debug: $(TARGET_DIR) mpy-cross $(MPY_DIR)/ports/stm32
# unixport (simulator)
unix: $(TARGET_DIR) mpy-cross $(MPY_DIR)/ports/unix
@echo Building binary with frozen files
@rm -rf $(MPY_DIR)/ports/unix/build-standard/frozen_mpy $(MPY_DIR)/ports/unix/build-standard/frozen_content.c
make -C $(MPY_DIR)/ports/unix \
USER_C_MODULES=$(USER_C_MODULES) \
FROZEN_MANIFEST=$(FROZEN_MANIFEST_UNIX) && \
cp $(MPY_DIR)/ports/unix/micropython $(TARGET_DIR)/micropython_unix
FROZEN_MANIFEST=$(FROZEN_MANIFEST_UNIX) \
CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_specter.h>"' && \
cp $(MPY_DIR)/ports/unix/build-standard/micropython $(TARGET_DIR)/micropython_unix

simulate: unix
$(TARGET_DIR)/micropython_unix simulate.py
Expand All @@ -75,6 +81,7 @@ all: mpy-cross disco unix
clean:
rm -rf $(TARGET_DIR)
make -C $(MPY_DIR)/mpy-cross clean
rm -rf $(MPY_DIR)/mpy-cross/build
make -C $(MPY_DIR)/ports/unix \
USER_C_MODULES=$(USER_C_MODULES) \
FROZEN_MANIFEST=$(FROZEN_MANIFEST_UNIX) clean
Expand Down
6 changes: 5 additions & 1 deletion boot/debug/hardwaretest.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ async def main(self):
await self.qr.enable()
s = await self.qr.get_data()
if s:
data = s.read().decode()
raw = s.read()
try:
data = raw.decode()
except:
data = repr(raw)
await self.gui.alert("Here's what we scanned:", data)
else:
conn = get_connection()
Expand Down
Loading