librescoot-pm (pm-service)¶
Description¶
The power manager (pm-service) controls system power states including suspend, hibernation, and reboot. It monitors service activity via a Unix socket inhibitor interface, tracks busy services via Redis, implements delays before power transitions, and manages a hibernation timer. The service uses a finite state machine (FSM) backed by librefsm to drive all power state transitions.
Command-Line Options¶
Usage of pm-service:
-default-state string
Default power state (run, suspend, hibernate, hibernate-manual, hibernate-timer, reboot) (default "suspend")
-dry-run
Dry run state (don't actually issue power state changes)
-hibernation-timer duration
Duration of the hibernation timer (default 120h0m0s) [5 days]
-inhibitor-duration duration
Duration the system is held active after suspend is issued (default 500ms)
-pre-suspend-delay duration
Delay between standby and low-power-state-imminent state (default 1m0s)
-redis-host string
Redis host (default "localhost")
-redis-port int
Redis port (default 6379)
-socket-path string
Path for the Unix domain socket for inhibitor connections (default "/tmp/suspend_inhibitor")
-suspend-imminent-delay duration
Duration for which low-power-state-imminent state is held (default 5s)
Note: The hibernation timer default is 120 hours (5 days). The systemd unit file typically overrides the default state to run to prevent automatic suspends during development.
Redis Operations¶
Hash: power-manager¶
Fields written:
- state - Power manager state (see States section below)
- wakeup-source - IRQ number of wakeup source (e.g., "45" for RTC)
Fields read:
- None (hibernation timer settings in settings hash)
Published channel: power-manager
- state - Published when power state changes
- wakeup-source - Published when system wakes from suspend
Hash: power-manager:busy-services¶
Fields written:
- <who> <why> <what> = <type> — inhibitor entry
- Example: "pm-service delay default delay" = "delay" (pm-service's own delay inhibitor)
- Example: "connection-based connection-based connection-based" = "block" (active socket connection)
This hash tracks all active inhibitors. Socket-based connections always use "connection-based" for who, why, and what. Redis-based inhibitors use values from the power:inhibits hash entry.
Published channel: power-manager:busy-services
- Updated atomically (DEL + HMSET + PUBLISH) when inhibitors change
Hash: power:inhibits¶
Fields written/read:
- <inhibit-id> — JSON data with inhibit request details
- Format: {"id": "...", "who": "...", "what": "...", "why": "...", "type": "block|delay|suspend-only", "duration": <unix_ns>, "created": <unix_ns>}
Used for programmatic inhibitor management (downloading, installing, etc.)
pm-service subscribes to the power:inhibits channel and syncs entries into its inhibitor manager as manual inhibitors, so update-service power inhibits (e.g. during DBC boot partition writes) actually block suspend/hibernate/poweroff.
Hash: settings¶
Fields read:
- hibernation-timer - Hibernation timer duration in seconds
Subscribed channel: settings
- hibernation-timer - Notification when hibernation timer setting changes
Hash: system¶
Fields written:
- cpu:governor - CPU governor setting (ondemand, powersave, performance)
Published channel: system
- cpu:governor - Published when CPU governor changes
Lists consumed (BRPOP via HandleRequests)¶
scooter:power- Power commandsrun- Set target state to running (highest priority)suspend- Request suspendhibernate- Request hibernationhibernate-manual- Manual hibernation (user-initiated)hibernate-timer- Timer-based hibernation-
reboot- System reboot -
scooter:governor- CPU governor commands ondemand- On-demand frequency scalingpowersave- Power-saving modeperformance- Performance mode
Lists published (LPUSH)¶
scooter:modem- Commands sent to modem servicedisable- Request modem shutdown before hibernation
Hash subscriptions (field updates)¶
vehicle→state- Vehicle state monitoring (stand-by, parked, etc.)battery:0→state- Battery slot 0 state monitoring (idle, active, charging)battery:1→state- Battery slot 1 state monitoring (idle, active, charging)
Power Manager States¶
The FSM has these internal states:
running- Normal operationpre-suspend- Waiting out the pre-suspend delay (natural suspend path only); publishes<target>-pendingto Redis (e.g.suspending-pending)suspend-imminent- Suspend imminent timer running; publishessuspending-imminenthibernate-imminent- Hibernate/reboot imminent timer runningwaiting-inhibitors- Waiting for blocking inhibitors to clearissuing-low-power- Power command issued to systemdsuspended- System has returned from suspend (transient)
Redis-published power-manager state values:
| Condition | Redis value |
|---|---|
| running | running |
| suspend target, pre-suspend phase | suspending-pending |
| suspend-imminent | suspending-imminent |
| hibernate-imminent | hibernating-imminent |
| hibernate-manual-imminent | hibernating-manual-imminent |
| hibernate-timer-imminent | hibernating-timer-imminent |
| reboot-imminent | reboot-imminent |
| issuing suspend | suspending |
| issuing hibernate | hibernating |
| issuing hibernate-manual | hibernating-manual |
| issuing hibernate-timer | hibernating-timer |
| issuing reboot | reboot |
See States Documentation for complete state machine.
Hardware Interfaces¶
nRF52840 Communication¶
The power manager interacts with nRF hardware indirectly via Redis:
- Commands hibernation via scooter:power list (handled by vehicle-service)
- nRF firmware handles actual hardware power control
Hibernation Levels¶
When hibernating, the nRF firmware selects between two levels based on CB battery charge: - L1: CB battery >5%, periodic wakeup checks - L2: CB battery ≤5%, switches to AUX battery, minimal power
See nRF Power Management for details.
Configuration¶
Systemd Unit¶
- Unit file:
librescoot-pm.service(installed to systemd system directory) - Binary location:
/usr/bin/pm-service - Started by: systemd at boot (requires redis.service)
- Restart policy: on-failure with 5 second delay
- User/Group: root (required for systemd power operations)
Dry-Run Mode¶
When started with --dry-run, the service:
- Operates normally
- Does NOT actually suspend/hibernate the system
- Logs all power state transitions
- For suspend dry-run, simulates an immediate wakeup event
Delay Configuration¶
Command-line flags control timing:
- Pre-suspend delay (-pre-suspend-delay): Time after stand-by before entering imminent state on the natural suspend path (default: 1m)
- Suspend imminent delay (-suspend-imminent-delay): Minimum duration in imminent state (default: 5s)
- Inhibitor duration (-inhibitor-duration): How long system stays active after suspend request (default: 500ms)
These delays allow services to complete operations before power down.
Default Power State¶
The -default-state option sets the target power state at startup:
- run - No automatic power transitions
- suspend - Allow suspend when vehicle is in stand-by (default)
- hibernate - Target hibernation when conditions met
- hibernate-manual - Manual hibernation mode
- hibernate-timer - Timer-based hibernation
- reboot - Reboot when conditions met
Hibernation Timer¶
- Configuration: Via
-hibernation-timeroption (duration) orsettings hibernation-timerRedis field (seconds) - Default: 5 days (120 hours)
- Purpose: Auto-hibernate after extended inactivity
- Activation: Timer starts when vehicle leaves
ready-to-drive; stops when vehicle re-entersready-to-drive - Dynamic updates: Can be changed at runtime via Redis settings; set to 0 to disable
Observable Behavior¶
Startup Sequence¶
- Reads initial vehicle and battery states from Redis (
vehicle state,battery:0 state,battery:1 state) - Creates inhibitor manager and opens Unix domain socket (default:
/tmp/suspend_inhibitor) - Creates hibernation timer
- Enables wakeup sources on serial ports (ttymxc0, ttymxc1)
- Builds and starts the FSM
- Publishes initial power state to Redis
- Initializes hibernation timer if vehicle is not in
ready-to-drive - Starts Redis hash watchers for
vehicle,battery:0,battery:1,settings - Starts command listeners for
scooter:powerandscooter:governor - Starts Redis inhibitor listener for
power:inhibits
Runtime Behavior¶
Suspend Trigger¶
When vehicle enters stand-by state with target=suspend:
1. FSM transitions to pre-suspend; publishes suspending-pending
2. Pre-suspend delay timer starts (default 1m)
3. After pre-suspend delay, FSM transitions to suspend-imminent; publishes suspending-imminent
4. Suspend-imminent timer starts (default 5s)
5. After imminent timer, FSM enters waiting-inhibitors
6. Once no blocking inhibitors remain, FSM enters issuing-low-power; publishes suspending
7. Sends suspend command to systemd
Explicit suspend commands (LPUSH scooter:power suspend) skip the pre-suspend delay and go directly to suspend-imminent.
Hibernation Trigger¶
Explicit hibernate command:
- LPUSH scooter:power hibernate (or hibernate-manual, hibernate-timer)
- Skips pre-suspend delay; FSM goes directly to hibernate-imminent
Hibernation timer:
- After timer expires (default 5 days outside ready-to-drive)
- FSM goes to hibernate-imminent with timer target
Inhibitor System¶
The pm-service implements two inhibitor mechanisms:
1. Unix Socket Inhibitors
- Services connect to Unix domain socket (default: /tmp/suspend_inhibitor)
- Connection-based: inhibitor active while socket connection open
- All socket connections are block type
- Service receives acknowledgment byte (0x00) upon connection
- Automatically released when socket closes
2. Redis-based Inhibitors
- Stored in power:inhibits hash as JSON
- Synced into the inhibitor manager on startup and on channel notifications
- Three types: block (blocks everything), delay (short delay), suspend-only (blocks suspend but not hibernate/poweroff/reboot)
Example socket inhibitor connection:
# Connect and hold connection to block suspend
nc -U /tmp/suspend_inhibitor
# Service is now blocking - close connection to release
Busy Services Tracking¶
The power-manager:busy-services hash shows active inhibitors:
redis-cli HGETALL power-manager:busy-services
# Key format: "<who> <why> <what>" = "<type>"
# Socket connections: "connection-based connection-based connection-based" = "block"
The hash is replaced atomically on every change.
Hibernation Procedure¶
- Power manager receives hibernation command (via
scooter:powerlist) - FSM transitions directly to
hibernate-imminent(no pre-suspend delay) - Publishes imminent state to Redis (e.g.
hibernating-imminent) - Suspend-imminent timer runs (5 seconds)
- FSM enters
waiting-inhibitors - If only modem has a blocking inhibitor, sends
LPUSH scooter:modem disable - Waits for all blocking inhibitors to clear
- Issues systemd
poweroffcommand - System powers down
Wakeup from Suspend¶
- Wakeup source triggers (serial port, RTC, etc.)
- Kernel resumes; systemd suspend call returns
- Reads wakeup IRQ from
/sys/power/pm_wakeup_irq - Publishes wakeup source to Redis (
power-manager wakeup-source) - Routes FSM based on wakeup type:
- RTC wakeup (IRQ 45): goes directly to
suspend-imminent(fast path, skips pre-suspend) - Other wakeups with target=suspend: goes to
pre-suspend - Otherwise: returns to
running - Publishes
runningstate to Redis
Log Output¶
The service logs to journald. Common log patterns:
Startup:
- Starting power management service <version>
- Enabled wakeup on ttymxc0 / ttymxc1
- Initial vehicle state: stand-by
- Initial battery:0 state: idle
Power state changes:
- Received power command: hibernate
- FSM state transition: running -> hibernate-imminent
- Entering hibernate-imminent state
- Issuing poweroff command
Inhibitors:
- Redis inhibitor added: update-service (block) by update-service — downloading
- New inhibitor connected: @
- Inhibitor disconnected: @
Wakeup:
- Wakeup detected with reason: 45
- RTC wakeup detected, using fast path
- Publishing wakeup source: 45
CPU governor:
- Received governor command: powersave
- Setting CPU governor to: powersave
- Successfully set CPU governor to powersave
Use journalctl -u librescoot-pm.service to view logs.
Dependencies¶
- systemd - For power state commands (suspend, poweroff, reboot)
- D-Bus - For systemd communication
- Redis server - For state coordination and service communication
- vehicle-service - Monitors
vehicle statefor power transition triggers - battery-service - Monitors
battery:0andbattery:1state - modem-service (optional) - Coordinated shutdown before hibernation
- settings-service (optional) - For hibernation timer configuration
Architecture Details¶
Key Components¶
FSM (internal/fsm/definition.go, internal/fsm/types.go)
- Defines all states, events, and transitions using librefsm
- States: running, pre-suspend, suspend-imminent, hibernate-imminent, waiting-inhibitors, issuing-low-power, suspended
- Priority-based transition guards
Inhibitor Manager (internal/inhibitor/inhibitor.go)
- Manages Unix socket for connection-based inhibitors (always block type)
- Tracks programmatically-added inhibitors
- Calls onChange callback on every change; FSM receives EvInhibitorsChanged
Redis Inhibitor Listener (internal/inhibitor/redis.go)
- Syncs power:inhibits hash entries into the inhibitor manager
- Supports block, delay, and suspend-only types from JSON type field
Hibernation Timer (internal/hibernation/timer.go)
- Tracks whether vehicle is in an idle state (not ready-to-drive)
- Configurable timer duration (default 5 days); set to 0 to disable
- Fires EvHibernationTimerExpired into the FSM on expiry
Service Coordinator (internal/service/service.go)
- Implements the FSM Actions interface
- Coordinates all components (FSM, inhibitors, hibernation timer)
- Handles Redis subscriptions and command lists
- Processes vehicle and battery state changes
Power State Priority System¶
Power state transitions follow strict priority rules (highest to lowest):
- run - Cancels all lower priority states
- hibernate-manual - User-initiated hibernation (brake + keycard)
- hibernate - Low battery or explicit hibernate command
- hibernate-timer - Timer-based auto-hibernation
- suspend/reboot - Normal suspend or reboot operations
When a higher priority state is requested, lower priority requests are ignored. To change from high to low priority, first request run to reset, then request the desired state.
CPU Governor Management¶
The service manages CPU frequency scaling via /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor:
# Set CPU governor
redis-cli LPUSH scooter:governor powersave
# Current governor published to Redis
redis-cli HGET system cpu:governor
Supported governors: ondemand, powersave, performance.
Wakeup Source Management¶
The service enables wakeup on serial ports at startup:
- /sys/class/tty/ttymxc0/power/wakeup → enabled
- /sys/class/tty/ttymxc1/power/wakeup → enabled
After wakeup, reads IRQ from /sys/power/pm_wakeup_irq and publishes to Redis.
Building and Testing¶
# Build for ARM target (armv7eabi)
make build
# Build for local architecture
make build-local
# Install to /usr/bin
sudo make install
# Run with dry-run mode (no actual power transitions)
./pm-service -dry-run
# Monitor power state changes
redis-cli SUBSCRIBE power-manager
# Check busy services
redis-cli HGETALL power-manager:busy-services
# Request hibernation
redis-cli LPUSH scooter:power hibernate
# Change CPU governor
redis-cli LPUSH scooter:governor powersave
Integration Points¶
With vehicle-service:
- Monitors vehicle state for stand-by conditions
- Triggers power transitions based on vehicle state changes
With battery-service:
- Monitors battery:0 and battery:1 state
- Derived aggregate: if either slot is active, suspend is blocked
With modem-service:
- Sends disable command before hibernation when modem is the only blocker
- Waits for modem to clear its blocking inhibitor
With settings-service:
- Reads settings hibernation-timer for timer duration in seconds
- Subscribes to settings changes for dynamic updates
With update-service:
- Respects inhibitors written to power:inhibits hash
- Type suspend-only delays suspend only; block blocks all low-power transitions
Related Documentation¶
- Power Management States - Power manager state machine
- nRF Power Management - Hibernation details
- Redis Operations - Power manager hash fields
- Vehicle States - How vehicle state affects power management
- LibreScoot Services - Service overview