M.2 UPS PMH AUTOMATIC LINUX INSTALLER V02
22 October 2026

TARGET
Raspberry Pi 5 Model B with a clean Raspberry Pi OS Bookworm or Trixie card
(Debian 12/13; 32-bit or 64-bit Python 3). Standard and Advanced M.2 UPS PMH.
The bundled daemon v1.1, monitor and updater are copied from the V88 package.
Hardware validation of this installer on a clean card is still to be completed.
Use the already validated application/bootloader V88 on the M.2 board.

BEFORE STARTING
1. Assemble the Pi and M.2 board with all power disconnected. Connect the
   I2C header and Pi-button control link as described in the manual. Check the
   battery polarity and chemistry; use one supported backup source.
2. Prepare Raspberry Pi OS with Raspberry Pi Imager. Configure your user,
   SSH, network and Wi-Fi country if applicable. Boot and connect by SSH.
   Keep a stable cable supply connected throughout installation/reboot/testing.
3. Copy M2_UPS_PMH_Installer_V02.zip to the Pi user's home directory.
   Internet access and functioning apt repositories are needed on first install.
   The initial package does not contain or program MCU firmware.

INSTALL (commands run on the Raspberry Pi)
cd ~
unzip M2_UPS_PMH_Installer_V02.zip
cd M2_UPS_PMH_Installer_V02
sudo python3 install_M2_UPS_PMH.py

Read the result. If installation succeeded and asks for reboot:
sudo reboot

SSH will disconnect. Allow Linux to boot, reconnect, then:
cd ~/M2_UPS_PMH_Installer_V02
sudo python3 install_M2_UPS_PMH.py --check

CHECK PASSED means that command-bank I2C reads, firmware identification,
M.2 RTC registration, UART device (unless excluded), current service state and
recent successful heartbeat were found. NTP may still be pending; this does
not cause failure. Time writes wait for confirmed NTP in the existing daemon.
The check does not send UART data, program firmware, acknowledge a shutdown
request or force a clock write. It reads the application's command/status banks.
If a shutdown request is pending, the normal daemon may shut Linux down.

Collect check output for the first SD-card test:
sudo python3 install_M2_UPS_PMH.py --check 2>&1 | tee M2_install_check.txt
sudo journalctl -u M2_PMH_i2c.service -b -n 25 --no-pager

WHAT IS INSTALLED
- apt packages: python3-smbus, python3-serial, i2c-tools and util-linux.
- /usr/local/sbin/M2_PMH_i2c.py (existing tested daemon v1.1).
- /etc/systemd/system/M2_PMH_i2c.service; enabled for each boot.
- /opt/M2_UPS_PMH/M2_PMH_status.py (optional read-only monitor).
- /opt/M2_UPS_PMH/M2_PM_UPS_BL01D.py (updater; installed, never executed).
- /etc/modules-load.d/M2_UPS_PMH.conf loads i2c-dev at boot.
- One marked block in the actual boot config.txt: I2C on; DS1307-compatible
  M.2 RTC overlay; uart0-pi5 on GPIO14/15 by default.
- Linux serial console/getty on /dev/ttyAMA0 is removed/masked so the updater
  can use it. An alias console is changed only if that alias resolves to
  ttyAMA0. The separate Pi 5 debug UART ttyAMA10 is left alone.

The 40-pin header UART is /dev/ttyAMA0. /dev/serial0 on Pi 5 normally points to
its separate debug UART, so it is not a substitute for this wiring. The installer
uses the official uart0-pi5 overlay without RTS/CTS. Normal installation does not
transmit UART commands. Bootloader baud 38400 is selected when running the
updater separately; application diagnostic UART baud is a separate setting.

RTC numbering is discovered by name rtc-ds1307 1-0068, not hard-coded as rtc1.
The daemon supplies 250ms heartbeat, shutdown handling and NTP-gated hourly
Linux-to-M.2 RTC synchronization. Existing service overrides, custom service
units, conflicting RTC/UART settings or another detected UPS daemon cause a
clear stop instead of silent replacement. Included config files are checked too.

The installer does NOT change Raspberry Pi EEPROM, SSD/PCIe settings, network,
Wi-Fi, user accounts, boot order, battery chemistry/current, runtime, ON/OFF mode,
or MCU firmware/bootloader. No automated power-loss test or automatic reboot.

OPTIONS
sudo python3 install_M2_UPS_PMH.py --dry-run
  Inspect and print the plan. No apt downloads, config writes or service changes.

sudo python3 install_M2_UPS_PMH.py --no-apt
  Install with dependencies already present; suitable for prepared offline cards.

sudo python3 install_M2_UPS_PMH.py --no-uart
  Configure Linux UPS protection/RTC but leave serial interfaces/consoles alone.
  Keep this same choice when rerunning; uninstall first if changing that choice.

sudo python3 install_M2_UPS_PMH.py --uninstall
  Restore backed-up files and the original service/getty states. Does not remove
  apt packages or backups. Reboot afterwards. The installer refuses restoration
  if a managed file has since been edited; preserve that edit and inspect the
  backup before proceeding. Do not uninstall while relying on battery protection.

REPEATED INSTALLS AND RECOVERY
Run the same install command again to verify/reapply this package. A normal
rerun keeps the original backup set and does not duplicate its config block or
restart an unchanged daemon. Boot changes take effect only after reboot.
Backups: /var/backups/M2_UPS_PMH/<UTC timestamp>/
State and file hashes: /var/lib/M2_UPS_PMH/installer.json
Ordinary caught failures restore this invocation's changed files and attempt to
restore the previous service state. A power cut or SIGKILL cannot run rollback;
retain cable power, inspect state/backups, and use --uninstall before a new
install. If a pending write is ambiguous, the guard may require manual recovery.
Package installation by apt is not rolled back.

COMMON FIRST-BOOT RESULTS
- Missing /dev/i2c-1, RTC or ttyAMA0 before reboot: reboot, then run --check.
- RTC probe failure after reboot: retain output and provide kernel RTC messages:
  sudo journalctl -k -b --no-pager | rg -i 'rtc|i2c|sda'
  (If rg is not installed use grep -Ei 'rtc|i2c|sda'.)
- M.2 is in bootloader mode: Linux I2C checks will fail until the application
  returns. Finish the separate firmware/recovery procedure first.
- apt/network failure: fix networking and retry; boot configuration is not yet
  changed at this stage. No full OS upgrade is requested by the installer.
- Service override/custom config: review it; the installer deliberately does
  not remove a previous site's settings on its own.

VALIDATION
20 host-side tests use a temporary Linux filesystem and mocked system commands:
clean install/reinstall/reboot/check/uninstall; original file restoration;
configuration include conflicts; custom unit/override refusal; apt/start failure;
modified-file guards; UART console aliases; I2C timeout/failed checks; payload
integrity; no firmware/EEPROM/programmer/reboot command execution.
These tests do not replace the first clean-card hardware trial.
Development tests are not included in this customer package.

CONFIGURATION REFERENCES
https://www.raspberrypi.com/documentation/computers/configuration.html
https://github.com/raspberrypi/firmware/blob/master/boot/overlays/README

V02 RTC BACKUP CONFIGURATION
The installer always appends dtparam=rtc_bbat_vchg=0 in its final [all] block.
This disables Raspberry Pi RTC charging for the regulated 3.3 V M.2 backup cable.
Earlier settings, including settings in included files, are overridden without
editing those included files. A reboot is required; no live RTC write is made.
Do not use a separate rechargeable RTC cell with this installation policy.
--check verifies the final configuration and, when available, the onboard
rpi-rtc charging_voltage sysfs value. It reports missing runtime verification.
V02 upgrades an unchanged V01 installation and keeps the original backup.
Uninstall restores the original boot configuration, including its RTC setting.
The new RTC change passed host tests; its hardware test remains to be done.
