M.2 UPS PMH - Bootloader and update instructions
Manual V14, 22 October 2026
Applies to application V88 (0x0088) and protected bootloader V88 at 38400 bit/s.
Standard and Advanced. No KEY jumper, HCM menu or button-selected factory defaults.

INITIAL INSTALLATION OR LOADER REPLACEMENT
Program the combined firmware/M2_PMH.hex through ICSP. Python application
uploads do NOT replace the protected loader, configuration or activation page.
Compile loader first, then application, using firmware/BUILD_V88.cmd if rebuilding.
The separate bootloader/ folder is for maintenance. Its standalone HEX is not
the complete application. Record the loader installation and file hashes.
The PCB/BL identity byte 0x40 alone does not identify UART baud or loader revision.

PREPARE ON RASPBERRY PI
Keep stable cable power connected. Close terminals using the loader UART and
stop the optional monitor. The tested UART is /dev/ttyAMA0; confirm actual wiring.
The normal M2_PMH_i2c.service may remain running; its I2C errors during BL are
expected. Check successful heartbeat resumes after the application returns.

sudo apt install -y python3-serial python3-smbus
cd ~/M2_UPS_PMH_Manual_V91/firmware
python3 ../updater/M2_PM_UPS_BL01D.py -G -f M2_PMH.hex

ENTRY AND UPDATE AUTOMATICALLY FROM SSH
The application must be running and cable power accepted. Omit -j to request BL:
sudo python3 ../updater/M2_PM_UPS_BL01D.py -v -i -k -p /dev/ttyAMA0 -b 38400 -f M2_PMH.hex

BUTTON ENTRY
Hold M.2 RESET, hold FSSD, release RESET while keeping FSSD pressed, then
release FSSD after about 1 second. No second press is needed. Keep cable power.
Do not hold FSSD indefinitely: a key not released within the startup release
window cancels the request. Normal entry does not deliberately press Pi's button.

ENTER BL ALONE THROUGH I2C
sudo i2cset -y 1 0x6b 0x00 0xbb b

UPLOAD AFTER BUTTONS, AFTER THE SEPARATE I2C COMMAND, OR DURING DIRTY RECOVERY
sudo python3 ../updater/M2_PM_UPS_BL01D.py -v -i -j -k -p /dev/ttyAMA0 -b 38400 -f M2_PMH.hex
-i skips application version read; -j skips I2C BL entry; -k skips extra I2C
reset after the serial update. These flags retain Flash CRC32 verification.
The updater default is 9600, so explicitly use -b 38400 for this loader.

WINDOW AND INDICATION
15 seconds to START the first accepted application write. Probes/noise do not
extend it. No write -> intact application resumes. Once writing starts, a durable
marker disables idle exit and prevents execution of an incomplete application.
Walking LEDs advance once per TWO HEX records. Record counts/progress are in
the terminal. The tested 6356-record, 76245-byte transfer took about 2 minutes.
Do not interrupt because progress is printed only roughly every 5 percent.
Success is:
Transfer completed; programmed application Flash CRC32 verified

RECOVERY AFTER INTERRUPTION, MCU RESET OR COMPLETE POWER LOSS
If writing had begun, no new button/I2C entry is needed. Keep/restore cable power
and resend the complete HEX from record one using -i -j -k and -b 38400.
A new image may be selected; its actual Flash contents must pass verification.
If no write began, the normal idle timeout still applies; enter BL again.
V88 cold/brownout recovery with the incomplete-write marker starts Pi once:
release 500ms, press 6000ms, release 2250ms, press 500ms, release 100ms.
Allow the ~9.35s sequence plus 60-90s for Linux/SSH. Recovery waits indefinitely.
MCU RESET alone and ordinary hardware/software BL entry do not run that sequence.
An already booting Pi can restart during cold recovery to establish a known state.
If the protected bootloader itself is damaged, use ICSP.

AFTER VERIFIED UPDATE
Confirm expected startup version, application return and successful I2C heartbeat.
If manually stopped, start the service after the application returns:
sudo systemctl start M2_PMH_i2c.service
sudo journalctl -u M2_PMH_i2c.service -b -n 15 --no-pager
uptime
Check mode/runtime, RTC, charging and a normal FSSD/cable-return cycle.
