This project reimplements the HALPI2 power monitoring and watchdog daemon (halpid) in Rust. The current Python implementation provides robust functionality but has opportunities for improvement in performance, reliability, and resource utilization. This Rust reimplementation maintains complete API compatibility while delivering:
- Lower memory footprint - No Python interpreter overhead
- Faster startup time - Native binary execution
- Improved reliability - Rust's type safety and memory safety guarantees
- Better resource utilization - More efficient system resource usage
-
API Compatibility: Maintain 100% backward compatibility with existing interfaces:
- I2C register map and communication protocol
- HTTP REST API (Unix socket endpoints)
- CLI commands and arguments
- Configuration file format (YAML)
-
Behavioral Equivalence: Preserve existing system behavior:
- Power management state machine logic
- Watchdog feeding patterns
- Blackout detection and shutdown orchestration
- Firmware update (DFU) protocol
-
Performance Improvements:
- Reduce memory footprint from ~50MB (Python + dependencies) to <10MB
- Improve startup time from ~2s to <100ms
- Maintain responsive I2C communication and HTTP API
-
Reliability:
- Leverage Rust's type safety to eliminate entire classes of runtime errors
- Ensure safe concurrent access to shared resources
- Graceful error handling with proper recovery
- Maintainability: Clear, idiomatic Rust code following best practices
- Testing: Comprehensive unit and integration tests
- Documentation: Well-documented code and user-facing documentation
- Packaging: Debian package (.deb) for easy deployment
Purpose: Communicate with RP2040 firmware over I2C bus
Requirements:
- I2C bus 1, device address 0x6D (configurable)
- Support for all existing I2C registers (0x03-0x45)
- Atomic read/write operations using Linux I2C device interface
- Proper error handling and retry logic for transient I2C errors
Register Categories:
- Version and identification (hardware version, firmware version, device ID)
- Power control and status (output enable, watchdog, thresholds, state)
- Analog measurements (voltages, current, temperatures)
- Shutdown commands
- Firmware update (DFU) protocol
- USB port control
Data Encoding:
- Multi-byte values: Big-endian encoding
- Analog values: 16-bit scaled (value = raw / 65536.0 * scale)
- Temperatures: Kelvin (displayed as Celsius in CLI)
- Firmware version detection affects read methods (byte vs word in v1 vs v2+)
Purpose: Provide IPC interface for CLI and external tools
Requirements:
- HTTP server on Unix domain socket
- Default path:
/var/run/halpid.sock(root) or~/.halpid.sock(non-root) - Socket permissions: 0660, group ownership configurable (default:
adm) - JSON request/response format
- Async I/O for concurrent request handling
Endpoints (must match exactly):
GET /- Health checkGET /version- Daemon versionPOST /shutdown- Initiate system shutdownPOST /standby- Enter standby mode with RTC wakeupGET /config- Get all configurationGET /config/{key}- Get specific config valuePUT /config/{key}- Set config valueGET /values- Get all measurements and stateGET /values/{key}- Get specific valueGET /usb- Get all USB port statesGET /usb/{port}- Get specific USB port statePUT /usb- Set multiple USB portsPUT /usb/{port}- Set specific USB portPOST /flash- Upload firmware (multipart form data)
Purpose: User-friendly interface for system management
Requirements:
- Binary name:
halpi(user-facing CLI tool) - Communicates with daemon via Unix socket HTTP API
- Pretty-printed output using tables and formatting
- Exit codes: 0 (success), 1 (error)
Commands:
halpi status- Show all measurements and statehalpi version- Show CLI versionhalpi get <key>- Get specific valuehalpi config- Show all confighalpi config get <key>- Get config valuehalpi config set <key> <value>- Set config valuehalpi shutdown- Normal shutdownhalpi shutdown --standby --time <time>- Standby with wakeuphalpi usb- Show USB port stateshalpi usb enable <0-3|all>- Enable USB port(s)halpi usb disable <0-3|all>- Disable USB port(s)halpi flash <file>- Upload firmware
Standby Time Parsing:
- The Rust CLI should support common time formats (integer seconds, ISO 8601 datetime)
- Full compatibility with Python's
dateparserlibrary is not required - Implementation can use simpler, more predictable parsing (e.g.,
humantimecrate for durations, standard datetime parsing) - Document supported formats clearly in help text
Output Format:
- Human-readable tables and formatting (can differ from Python implementation)
- Machine-parseable output modes (consider adding --json flag)
- Colored output when TTY detected
Purpose: Monitor power state and orchestrate graceful shutdown
Requirements:
- Four-state FSM: START → OK → BLACKOUT → SHUTDOWN → DEAD
- Poll interval: 0.1 seconds
- Blackout detection:
V_in < blackout_voltage_limit(default 9.0V) - Shutdown trigger: Blackout duration exceeds
blackout_time_limit(default 5.0s) - Watchdog initialization: Set 10-second timeout on startup
- Graceful shutdown sequence:
- Call I2C shutdown command (register 0x30)
- Execute poweroff command (default
/sbin/poweroff)
State Transitions:
START → OK: After watchdog initializationOK → BLACKOUT: When V_in drops below thresholdBLACKOUT → OK: When V_in recovers above thresholdBLACKOUT → SHUTDOWN: After timeout expiresSHUTDOWN → DEAD: After poweroff command execution
Purpose: Update RP2040 firmware via I2C
Requirements:
- 4KB block size (matches flash sector)
- CRC32 validation per block
- State machine: IDLE → PREPARING → UPDATING → READY_TO_COMMIT
- Error states: QUEUE_FULL, CRC_ERROR, DATA_LENGTH_ERROR, WRITE_ERROR, PROTOCOL_ERROR
- Retry logic with timeout for QUEUE_FULL
- Progress reporting
- Abort capability
Protocol:
- Start update (0x40): Send total firmware size
- Upload blocks (0x43): [CRC32][block_num][block_len][data]
- Poll status (0x41): Check DFU state
- Commit (0x44): Finalize update
- Abort (0x45): Cancel on error
Purpose: Support flexible configuration from file and CLI
Requirements:
- Config file format: YAML
- Default location:
/etc/halpid/halpid.conf - Command-line override support for all options
- Key name normalization: dashes to underscores
Configuration Options:
i2c-bus(int): I2C bus number (default: 1)i2c-addr(hex): I2C device address (default: 0x6d)blackout-time-limit(float): Seconds before shutdown (default: 5.0)blackout-voltage-limit(float): Voltage threshold in volts (default: 9.0)socket(path): Unix socket path (default:/run/halpid.sock)socket-group(string): Socket group ownership (default:adm)poweroff(string): Shutdown command (default:/sbin/poweroff)
Precedence: CLI args > Config file > Built-in defaults
Purpose: Run as system service with proper lifecycle management
Requirements:
- Binary name:
halpid(daemon) - Run as root (required for I2C access and shutdown)
- Three concurrent async tasks:
- State machine loop (1 second interval)
- HTTP server (event-driven)
- Signal handler (SIGINT, SIGTERM)
- Graceful shutdown:
- Disable watchdog on exit
- Remove socket file
- Clean up resources
- Systemd integration:
- Service type:
simple - Auto-restart on failure (10s delay)
- Unbuffered output for logging
- Service type:
- Rust Edition: 2024
- MSRV (Minimum Supported Rust Version): 1.91+ (or latest stable)
- tokio - Async runtime with full features (rt-multi-thread, macros, fs, signal)
- axum - HTTP framework for Unix socket server
- serde / serde_json / serde_yaml - Serialization for config and API
- clap - CLI argument parsing (derive feature)
- linux-embedded-hal or i2cdev - I2C device access
- anyhow - Error handling
- tracing / tracing-subscriber - Structured logging
- tokio-util - Unix socket utilities
- cargo-deb - Debian package generation
- cross - Cross-compilation tool for building on x86_64 or ARM64 development machines
- Build target:
aarch64-unknown-linux-musl(static binary for universal ARM64 Linux compatibility) - Cross-compilation: Required from initial phase for development workflow
- Package name:
halpid(version 5.0.0 to signal major rewrite)
- cargo-watch - Development file watching
- cargo-nextest - Modern test runner
- mockall - Mocking for unit tests (if needed)
- criterion - Benchmarking (optional)
-
API Compatibility: External interfaces must remain unchanged
- I2C register addresses and protocols
- HTTP endpoint paths and JSON schemas
- CLI command structure and arguments
- Configuration file format and keys
-
Behavioral Compatibility: System behavior must match
- State machine timing and transitions
- Watchdog feeding patterns
- Shutdown orchestration sequence
- Firmware update protocol
-
Platform Requirements:
- Linux kernel with I2C device support (
/dev/i2c-*) - Unix domain socket support
- Systemd for service management
- Root privileges for I2C access and shutdown
- Linux kernel with I2C device support (
- Deployment: Target is Raspberry Pi CM5 running Debian/Trixie ARM64
- I2C Hardware: RP2040 firmware implements expected protocol (HALPI2-firmware)
- Permissions: Daemon runs as root; CLI can run as any user in socket group
- Filesystem: Standard Linux filesystem layout (
/etc,/var/run,/sbin) - RTC:
rtcwakeutility available for standby mode support
- Memory footprint: <10MB RSS during normal operation
- Startup time: <100ms from exec to ready
- I2C latency: <1ms per register read/write
- HTTP response time: <5ms for simple queries
- State machine responsiveness: 0.1 second polling interval
- Crash resistance: No panics in normal operation; graceful error handling
- Recovery: Automatic retry for transient I2C errors
- Data integrity: CRC validation for firmware uploads
- Watchdog safety: Always disable watchdog before daemon exit
- Privilege separation: CLI communicates via socket (no root required)
- Socket permissions: Restrictive (0660) with configurable group
- Input validation: Sanitize all external inputs (CLI args, HTTP requests, config)
- Dependency audit: Regular security audits with
cargo audit
- Code style: Follow Rust conventions (
rustfmt,clippy) - Documentation: Rustdoc for all public APIs
- Testing: >70% code coverage target
- Error messages: Clear, actionable error messages
The following are explicitly not included in the initial implementation:
- New features - Only reimplementation of existing functionality
- Prometheus/metrics export - Could be added in future versions
- systemd socket activation - Not required for current use case
- Configuration hot-reload - Requires daemon restart for config changes
- IPv4/IPv6 HTTP API - Unix socket only (security)
- Multi-instance support - Single daemon per system
If bugs are discovered in the Python implementation:
- Document the bug and current behavior
- Discuss with maintainer before fixing
- Preserve buggy behavior if external tools depend on it (add compatibility flag)
- Fix if clearly incorrect and unlikely to break downstream users
- All I2C registers readable/writable with correct encoding
- All HTTP API endpoints functional with matching JSON schemas
- All CLI commands produce equivalent output (format can differ aesthetically)
- State machine transitions match Python implementation
- Firmware update completes successfully
- Configuration loading from YAML and CLI overrides works
- Systemd service starts, runs, and stops cleanly
- Unit tests for all core modules (>70% coverage)
- Integration tests for HTTP API endpoints
- I2C hardware tests (run on actual HALPI2 hardware)
- Memory usage <10MB RSS
- No
clippy::pedanticwarnings - Documentation complete (README, rustdoc, man pages)
- Debian package builds successfully
- Package installs and upgrades from Python version cleanly
- Systemd service runs without errors
- CLI accessible to users in
admgroup - Migration guide documented
- New package version: 5.0.0 (major version bump)
- Package name:
halpid(same as Python version) - Binary names:
halpid(daemon),halpi(CLI) - Conflicts: Debian package conflicts with old
halpid(<5.0.0) - Upgrade path:
apt upgradereplaces Python with Rust version - Rollback: Downgrade via
apt install halpid=4.x.x
Before release:
- Test on HALPI2 hardware with all firmware versions (v2.x, v3.x)
- Verify existing scripts using CLI continue to work
- Test HTTP API with external tools (if any)
- Validate configuration migration (old YAML files still work)
- Python implementation:
halpidrepository (HALPI2-daemon) - Firmware specification:
HALPI2-firmwarerepository - I2C protocol: Defined in firmware source (
src/i2c_regs.rs) - Product documentation: https://docs.hatlabs.fi/halpi2