Skip to content

Raspbian trixie - #1106

Draft
SteveMicroNova wants to merge 76 commits into
mainfrom
RaspbianTrixie
Draft

SteveMicroNova wants to merge 76 commits into
mainfrom
RaspbianTrixie

Conversation

@SteveMicroNova

@SteveMicroNova SteveMicroNova commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What does this change intend to accomplish?

This is a reworking of AmpliPi to function in a 64 bit raspbian trixie A:B tryboot setup with the goal of having a more robust system overall and a cleaner update flow with full image updates

Keep an eye on the docs/a_b_update_design.md file for a good rundown of the high level design and purpose of the new components within this PR

A few things that need to be worked on yet:

  • Ensure that LMS config and plugins persist on slot-swap
  • Label the partitions something other than p1-7
  • Where should image files be hosted?

Checklist

  • Have you tested your changes and ensured they work?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?
  • If applicable, have you updated the documentation/manual?
  • If applicable, have you updated the CHANGELOG?
  • Does your submission pass linting & tests? You can test on localhost using ./scripts/test
  • Have you written new tests for your core features/changes, as applicable?
  • If this is a UI change, have you tested it across multiple browser platforms on both desktop and mobile?

@SteveMicroNova

SteveMicroNova commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

This is a WIP that will take a while to finish

This will change the baseline partition scheme of amplipi from basic boot+root to a 7 partition schema:

  • p1: Autoboot (raspberry pi A:B boot necessity), mounted as /boot/firmware
  • p2: Boot partition A
  • p3: Boot partition B
  • p4: Extended partition containing partitions 5, 6, and 7
  • p5: Root partition A
  • p6: Root partition B
  • p7: Persistent data storage for things that need to remain during flash, such as the user's config. Mounted at /data

Currently, I have the ability to update from images via the updater/asgi.py /update/flash endpoint that takes an update package: a folder containing a root image, an optional boot image, and a manifest.json file that describes the update package with a bytesize and sha256 checksum of the images.
This endpoint uses that update package to first verify the checksum of the image(s) before flashing, and then flashes the inactive directory. After flashing is complete, it also writes an "update-pending" file to p1 that signals to the update finalization service that things are freshly flashed and need to be checked on reboot.
Once that's all done, it also runs sudo reboot "0 tryboot" to move to the other slot

Once in the other boot+root slot, the update verification service checks to see if that /boot/firmware/update-pending file exists and if yes, runs a preflight check before allowing amplipi to start up - verifying if all the apt and python packages are properly in place. Once that's done and amplipi runs, it also edits /boot/firmware/autoboot.txt to change the newly flashed boot slot to be the new default slot so that the user doesn't revert to the previous slot on next reboot

Comment thread amplipi/display/check_pass
Comment thread amplipi/display/common.py
the stored default AmpliPi password."""

# Password config location
PASS_DIR = os.path.join(os.path.expanduser('~'), '.config', 'amplipi')

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole .config folder and a few services got moved to /data to ensure they persist post-update, many lines like this had to be changed as a result

Comment thread config/autoboot.txt Outdated
Comment on lines +1 to +13
[all]
tryboot_a_b=1
boot_partition=2

[tryboot]
boot_partition=3

if exist mmc 0:1 /boot_partition.txt then
load mmc 0:1 ${loadaddr} /boot_partition.txt
env import -t ${loadaddr} ${filesize}
fi

boot mmc 0:${boot_partition}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the default autoboot.txt, which is what the update verification service edits when an update is successful. Specifically what is changed is swapping the [all] boot_partition value with the [tryboot] boot_partition value, that's all it takes to swap the default boot slot

Comment thread docs/imaging_etcher.md
* A micro USB cable
* Your AmpliPi

## Optional Step: Preserve your config

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file likely needs more reworking as realistically you shouldn't need to flash ever again, though also I'd say that makes more sense to leave until this code is in the hands of users and has a few support cases under its belt to verify if that's necessary

Comment on lines +33 to +41
# Run device-specific customization scripts from p7.
# These survive OTA updates and are re-applied on each new slot's first boot.
if [ -d "${SCRIPTS_DIR}" ]; then
for script in "${SCRIPTS_DIR}"/*.sh; do
[ -f "${script}" ] || continue
log "Running ${script}..."
bash "${script}" || log "Warning: ${script} exited non-zero"
done
fi

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some users have specific setups on their devices that we don't want to destroy on a full-image update, so I've given them a directory that they can fill with bash scripts to set up their services automatically post-update

I need to test what happens if a user provides scripts that fail for those cases

Comment thread scripts/dev/bootstrap_pi
@@ -9,10 +9,10 @@ Bootstrap a Raspberry Pi Compute Module for first-time AmpliPi setup.
--hostname NAME: The hostname to set on the pi, so once this script
successfully completes connect via {hostname}.local.
Default is amplipi
--cli-only: Use the lite version of Raspberry Pi OS which does not have

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point, CLI only became a default case for our units. I decided to flip the default case in this script to reflect that

Comment thread scripts/dev/bootstrap_pi

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file either needs full reworking or deletion. The new 7 partition setup is complex enough that updating this is a bit of a lift when we hardly need it and can also just distribute a full 7 partition image for those who need that sort of thing

@SteveMicroNova SteveMicroNova Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The answer is reworking. It won't be a simple portion, but creating a clean image that isn't mucked up with any dev happenings is an important goal and this script is likely one of the better ways to achieve that

Comment thread scripts/configure.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike bootstrap.py, I'd say configure.py is still very useful despite not being our update path anymore. manual deploys with the deploy+configure script are likely to be the primary ways we configure the root partitions that later get snapshotted into an image we distribute as an update

Comment thread amplipi/ctrl.py
vol, vol_f, vol_f_delta, vol_min, or vol_max.
"""
# Field precedence: vol (db) > vol_delta > vol (float)
# vol (db) is first in precedence yet last in the stack to cover the default case of no volume change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a rebase error. This branch is long lived and had some merge conflicts on rebase and so may need to be combed for reversions like this before merging

Comment thread amplipi/rt.py
Comment on lines +286 to +289
try:
self.bus.close()
except Exception:
pass

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an additional fix for the issue fixed by #1098 which was merged in #1091

My unit actually got into a bad state during testing, which led to me having the ability to test those changes and add a little more to the fix

My own issue was downstream of some configuration file providing an environment that the I2C didn't like

Comment thread scripts/dev/bootstrap_pi
Comment on lines +309 to 336
5. (Optional) Run the cleanup script to make the system 'factory fresh' with a randomgen ssh password and empty logs
"


echo "
NOTE: a modern AmpliPi setup is more complicated than simple bash scripting will allow, to match what a normal Pi would contain please follow the above directions, and then:
1. use `sudo dd if=/dev/sdx1 of=amplipi-boot.img status=progress bs=4MiB` to make an image of your boot directory, changing sdx into whatever sd your device mounted to
2. use `sudo dd if=/dev/sdx2 of=amplipi-root.img status=progress bs=4MiB` to make an image of your root directory, changing sdx into whatever sd your device mounted to
3. use parted or gparted to wipe all partitions and make the following partitions:

p1: FAT32, 516 MB, flash with amplipi-boot.img
p2: FAT32, 516 MB, flash with amplipi-boot.img
p3: FAT32, 516 MB, flash with amplipi-boot.img
p4: Extended partition containing p5-7 containing remainder of disk
p5: EXT4, 10939 MB, flash with amplipi-root.img
p6: EXT4, 10939 MB, flash with amplipi-root.img
p7: EXT4, sized to fill the remaining space

4. Edit the contents of the partitions as so:

p1: add autoboot.txt as seen in /config/autoboot.txt
p2: adjust cmdline.txt to point to partition 5
p3: adjust cmdline.txt to point to partition 6

p5: Add p7 to fstab as /data
p6: Add p7 to fstab as /data
p7: create a .config folder
"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is useful documentation for someone who might be trying to get their bearings on this PR

@SteveMicroNova SteveMicroNova Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also worth noting that this section isn't useful as a guide to get a unit up and running anymore

As I've continued working I've found more specific details that simply will not be captured here due to being things like "go deep into this directory and change this line in a random config file that I don't remember anymore"

Comment thread config/autoboot.txt

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just considered that this file needs a greater explanation

The official docs are a bit sparse (or were when I first read them months ago), so I'll also explain myself:

tryboot_a_b=1 activates the tryboot mechanism, something supported in the raspbian kernel that allows for partition swapping using the command sudo reboot "0 tryboot". Using specifically that "0 tryboot" arg is how you boot into the partition listed under the [tryboot] boot_partition section.

That "0 tryboot" arg simply loads the boot partition listed under the [tryboot] header, it does not toggle anything in this file to make that partition the new default (which is handled by the amplipi-postflash service I've made), so doing a simple sudo reboot from the tryboot partition will send you back to whatever partition is listed under the [all] header

All the actual lifting is done by the individual boot partitions, which have their own kernels and mappings to their own root partitions

Comment thread config/autoboot.txt Outdated
Comment on lines +280 to +298
<!--
Progress bar, dynamically sized depending on if the update needs to be downloaded first as well as if the boot image is present
this translates to there being a minimum of two segments if the update is already downloaded and is only for root, or 5 if it isn't downloaded and contains boot
-->
<div class="p-3 pb-0">
<div class="progress" style="position: relative;">
<div id="update-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-info" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
</div>
<div class="d-flex text-center mt-1">
<small class="update-progress-label d-none" data-label="Downloading root image" style="width: 0%;">Download Root</small>
<small class="update-progress-label d-none" data-label="Downloading boot image" style="width: 0%;">Download Boot</small>
<small class="update-progress-label d-none" data-label="Verifying root image" style="width: 0%;">Verify Root</small>
<small class="update-progress-label d-none" data-label="Verifying boot image" style="width: 0%;">Verify Boot</small>
<small class="update-progress-label d-none" data-label="Flashing root" style="width: 0%;">Flash Root</small>
<small class="update-progress-label d-none" data-label="Flashing boot" style="width: 0%;">Flash Boot</small>
<small class="update-progress-label d-none" data-label="Downloading manifest" style="width: 0%;">Manifest</small>
<small class="update-progress-label d-none" data-label="Downloading release" style="width: 0%;">Download</small>
<small class="update-progress-label d-none" data-label="Applying update" style="width: 0%;">Apply</small>
</div>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a weighted progress bar during these changes to help make the updater feel more responsive and less like a black box of sorts

This is possible now that we're dealing with files of known size, and the system knows how far along the flashing process it is.

Here's an example of what it looks like:

Image

Comment thread amplipi/updater/asgi.py
Comment on lines +76 to +81
class SSEChannel:
""" Bundles the queue/in-progress-flag/latest-status trio a long-running background job reports
its progress through, plus the polling generator that serves it as SSE to a native EventSource.
/update/flash and /update/download/images each run their own job in a background thread,
separate from the SSE connection itself, so a dropped/reconnecting browser can't interrupt or
duplicate the actual work - progress is watched via a paired GET .../progress endpoint instead. """

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to ensure that there could only be one update running at a time (a problem with our current updater, but even more important to handle here) and thus I had to make a progress tracking channel.
While doing so, I also had the idea of having a visible progress bar on the frontend that this now also feeds

Comment thread docs/a_b_design.md

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably going to be the most useful file during PR review, keeping this up to date will prove easier than keeping the actual PR description up to date especially since I can easily link to specific files from this

@SteveMicroNova
SteveMicroNova force-pushed the RaspbianTrixie branch 2 times, most recently from 3bbb544 to e4d9a70 Compare September 17, 2026 18:08
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 2.25564% with 520 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.06%. Comparing base (7499989) to head (39248d1).
⚠️ Report is 104 commits behind head on main.

Files with missing lines Patch % Lines
amplipi/updater/asgi.py 0.00% 484 Missing ⚠️
amplipi/rt.py 0.00% 15 Missing ⚠️
amplipi/display/common.py 0.00% 6 Missing ⚠️
amplipi/streams/lms.py 0.00% 6 Missing ⚠️
amplipi/streams/bluetooth.py 0.00% 5 Missing ⚠️
amplipi/app.py 71.42% 2 Missing ⚠️
amplipi/utils.py 60.00% 2 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1106      +/-   ##
==========================================
- Coverage   50.67%   48.06%   -2.61%     
==========================================
  Files          40       41       +1     
  Lines        7154     8004     +850     
==========================================
+ Hits         3625     3847     +222     
- Misses       3529     4157     +628     
Flag Coverage Δ
unittests 48.06% <2.25%> (-2.61%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@SteveMicroNova SteveMicroNova linked an issue Sep 18, 2026 that may be closed by this pull request
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Robust version-switching script Update updater to flash full images Migrate to 64-bit Raspberry Pi OS

3 participants