Files
battery-monitor/CLAUDE.md
denq a4bc66a745 Implement unified uConsole Smart Power Regulator with AC detection
Major overhaul of power management system to address voltage-induced 4G
module hangs through intelligent, event-driven power regulation.

Key Features:
- Unified power regulation based on AC power + 4G modem state
- Event-driven AC detection via udev (power_supply subsystem)
- Three optimized power modes for different scenarios
- Automatic voltage monitoring with multi-method alerts

Power Modes:
- AC Connected: 2.4GHz (ondemand) - full performance
- Battery + 4G: 1.8GHz (powersave) - voltage monitoring enabled
- Battery Only: 2.0GHz (ondemand) - balanced performance

Technical Improvements:
- AC power state detection via udev events (not polling)
- Edge case handling (service starts with AC connected)
- Unified logging to /var/log/uconsole-power-regulator.log
- Upgrade path from old 4G Power Manager
- State tracking to avoid redundant regulator triggers

Components:
- uconsole-power-regulator.sh: Main power orchestrator
- uconsole-power-daemon.sh: Background state monitoring (5s interval)
- voltage-monitor.sh: Voltage checker (Battery + 4G only)
- voltage-alert-notify.sh: Multi-method alerts (desktop/audio/log/LED)
- voltage-monitor-control.sh: Monitor lifecycle controller
- 99-uconsole-power-regulator.rules: udev event triggers
- install-uconsole-power-regulator.sh: Installation + upgrade script

Documentation:
- Updated README.md with new system overview
- Created README_CN.md (Chinese translation)
- Updated TOOL-GUIDE.md with new architecture details
- Updated CLAUDE.md with unified system documentation

Breaking Changes:
- Replaces 4G Power Manager with unified regulator
- New service name: uconsole-power-regulator.service
- New log file: /var/log/uconsole-power-regulator.log
- Use upgrade script to migrate from old system

Installation:
- Fresh: sudo ./install-uconsole-power-regulator.sh install
- Upgrade: sudo ./install-uconsole-power-regulator.sh upgrade
- Uninstall: sudo ./install-uconsole-power-regulator.sh uninstall
2025-11-06 17:00:13 +08:00

13 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Battery Monitor is a Next.js 16 web application designed to run on Raspberry Pi uConsole CM5 hardware. It monitors and visualizes battery metrics in real-time by reading from Linux sysfs power supply interfaces specific to AXP20x/AXP22x power management chips commonly found in embedded systems.

Key Features

  • Real-time battery monitoring with interactive charts
  • Two Monitoring Modes: Live mode (real-time charts) and Background mode (battery-saving)
  • Historical data storage and session management (SQLite)
  • CSV export functionality
  • Incomplete session detection and repair
  • 4G Power Management: Automatic CPU frequency scaling to prevent system hangs when 4G module is active on battery

Development Commands

# Install dependencies
npm install

# Run development server (http://localhost:3000)
npm run dev

# Build for production
npm build

# Run production server
npm start

# Run linter
npm run lint

Hardware-Specific Context

This application is designed to run on Raspberry Pi or similar embedded Linux systems with AXP20x battery management hardware. The API reads from these sysfs paths:

  • Battery data: /sys/class/power_supply/axp20x-battery/*
  • AC adapter: /sys/class/power_supply/axp22x-ac/online

Important: The application will fail to read battery data on systems without these hardware interfaces. When testing on non-Pi hardware, the API will return 500 errors.

Architecture

Frontend (Client-Side)

  • Main Component: src/components/BatteryMonitor.tsx is a client component that polls the battery API every 2 seconds when monitoring is active
  • State Management: Uses React hooks to maintain current battery data and historical readings (last 100 data points)
  • Visualization: Uses Recharts for real-time line and area charts showing battery percentage, power consumption, voltage, and current trends
  • Data Export: CSV export functionality for historical battery data

Backend (Server-Side)

  • API Route: src/app/api/battery/route.ts provides a GET endpoint at /api/battery
    • Add ?save=true query parameter to save readings to database
  • Hardware Interface: Reads directly from Linux sysfs using Node.js fs.readFileSync()
  • Data Transformation: Converts raw values from microvolts (µV) and microamps (µA) to volts and amps
  • Database: SQLite database (battery-data.db) stores historical readings
    • src/lib/db.ts contains database utilities and schema
    • /api/battery/history - Query historical data by time range
    • /api/battery/sessions - Get monitoring session summaries

UI Components

Located in src/components/ui/, these are shadcn/ui components customized for this project:

  • Card, Button, Badge, Progress, Table components
  • Chart components for Recharts integration
  • All use the cn() utility from src/lib/utils.ts for Tailwind class merging

Path Aliases

The project uses @/* to reference src/*:

import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';

Key Dependencies

  • Next.js 16: App Router architecture with React 19
  • Recharts: Charts and data visualization
  • shadcn/ui: UI component library (Radix UI primitives)
  • Tailwind CSS 4: Styling with PostCSS
  • date-fns: Date formatting
  • lucide-react: Icons
  • better-sqlite3: SQLite database for persistent data storage

TypeScript Configuration

  • Strict mode enabled
  • JSX mode: react-jsx (React 19 automatic runtime)
  • Module resolution: bundler
  • Path alias @/* maps to ./src/*

Data Flow

Monitoring vs Recording

The application separates monitoring (display only) from recording (database writes), and offers two display modes for power optimization:

Monitoring Mode (No Database Writes):

  1. User clicks "Start Monitoring" button
  2. BatteryMonitor component sets up 2-second polling interval
  3. Each poll hits /api/battery endpoint (NO database save)
  4. API reads raw battery values from sysfs
  5. API converts and calculates derived values (voltage, current, power)
  6. Frontend updates state, appends to historicalData (max 100 points in memory)
  7. Data is also buffered in monitoringDataBufferRef (max 1000 points)
  8. Charts automatically re-render with new data
  9. User can start recording at any point

Recording Mode (With Database Writes):

  1. While monitoring is active, user selects recording start point:
    • "Record from now": New data only
    • "Record from monitoring start": Saves buffered data since monitoring began
  2. User clicks "Start Recording"
  3. Creates new session in database with selected start time
  4. If "from monitoring start", sends buffered data via /api/battery/save endpoint
  5. Each subsequent poll hits /api/battery?save=true&sessionId=X
  6. Readings are saved to database immediately with session association
  7. User can stop recording (monitoring continues) or stop both
  8. Session end_time and reading_count updated on stop

Power Failure Resilience:

  • All readings are saved to SQLite database immediately (synchronous commits)
  • If power dies during recording, all data up to that point IS preserved
  • Session metadata may be incomplete: end_time equals start_time, reading_count may be 0
  • UI automatically detects incomplete sessions (yellow warning badge)
  • "Repair All" button updates incomplete sessions to correct end_time and reading_count
  • Repair endpoint: /api/battery/sessions/repair (POST with repairAll: true or specific sessionId)

Display Modes (Battery Optimization):

Live Mode (Default):

  • Full real-time chart rendering with animations
  • All visualizations active (percentage, power, voltage, current charts)
  • Higher power consumption due to continuous SVG rendering
  • Best for active monitoring and data analysis

Background Mode (Battery Saving):

  • Charts hidden completely (no rendering overhead)
  • Only current stats displayed in the status card
  • Same 2-second polling interval (consistent data quality)
  • Full data granularity maintained - historicalData array still updated
  • Database recording continues normally
  • Saves ~20-40% power through multiple optimizations:
    • No chart SVG rendering (biggest win)
    • Disabled chart animations globally
    • Removed CartesianGrid (reduces SVG elements)
    • Lazy-loaded sessions list (Intersection Observer)
  • Best for long recording sessions where visualization isn't needed
  • Safe for power failure testing - no data loss

Toggle between modes with the "Live Mode" / "Background Mode" button when monitoring is active.

Power Optimizations Applied:

  1. Charts hidden in background mode (eliminates 4 Recharts re-renders every 2s)
  2. Chart animations disabled (isAnimationActive={false} on all Line/Area components)
  3. CartesianGrid removed from all charts (reduces SVG complexity by ~30%)
  4. Sessions list lazy-loaded only when scrolled into view (Intersection Observer)
  5. Data granularity preserved - no polling changes, historicalData always updated

Historical Data Mode

  1. User selects start/end date/time in "Export Custom Time Range" card
  2. Frontend queries /api/battery/history?start=<ISO>&end=<ISO>
  3. Database returns all readings within the time range
  4. User can view data in charts or export directly to CSV
  5. User can click "Back to Live View" to resume real-time monitoring

Per-Session Export

  1. User clicks download button on specific session
  2. Frontend fetches /api/battery/sessions/:id
  3. All readings for that session are retrieved
  4. Data is exported to CSV with session name in filename

Working with Battery Data

The BatteryData interface is shared between API and frontend:

interface BatteryData {
  timestamp: string;      // ISO 8601 format
  percentage: number;     // 0-100
  voltage: number;        // volts (converted from µV)
  current: number;        // amps (converted from µA, positive=charging, negative=discharging)
  power: number;          // watts (voltage * current, positive=charging, negative=discharging)
  status: string;         // e.g., "Charging", "Discharging", "Full"
  health: string;         // e.g., "Good", "Unknown"
  acConnected: boolean;   // true if AC adapter is connected
}

Database Schema

The SQLite database (battery-data.db) contains two tables:

monitoring_sessions table - Tracks monitoring sessions:

  • id: Auto-incrementing primary key
  • name: Custom session name (nullable, editable by user)
  • start_time: ISO 8601 timestamp when monitoring started
  • end_time: ISO 8601 timestamp when monitoring stopped
  • reading_count: Number of readings in this session
  • created_at: Database insertion timestamp

battery_readings table - Stores individual battery readings:

  • id: Auto-incrementing primary key
  • session_id: Foreign key to monitoring_sessions (nullable)
  • timestamp: ISO 8601 timestamp from battery reading
  • percentage, voltage, current, power, status, health: Battery metrics
  • ac_connected: Boolean (stored as 0/1)
  • created_at: Database insertion timestamp

Indexes on timestamp, created_at, and session_id for efficient queries.

Session Management

  • When "Start Monitoring" is clicked, a new session is created
  • All readings during monitoring are linked to that session via session_id
  • When "Stop Monitoring" is clicked, the session's end_time and reading_count are updated
  • Users can select sessions from a dropdown or edit session names inline for easier identification
  • Default session name: Session [start timestamp] (can be customized)
  • Users can delete sessions with a confirmation dialog - this removes the session and all associated readings

Error Handling

  • If sysfs files cannot be read, API returns 500 error with { error: 'Unable to read battery data' }
  • Frontend displays error state in a red error card
  • Missing or null values from sysfs are handled gracefully in getBatteryData()

uConsole Smart Power Regulator

Located in scripts/ directory. See STORY.md and TOOL-GUIDE.md for full documentation.

Problem

The 4G modem (SimTech SIM7600G-H) requires minimum 3.45V to operate reliably. When battery voltage drops below this threshold, the modem can hang the entire system, requiring physical battery removal to restart. The issue is voltage-related, not power budget.

Solution: Unified Event-Driven Power Regulation

Key Components:

  1. uconsole-power-regulator.sh - Main orchestrator

    • Determines power state based on AC and 4G status
    • Applies appropriate CPU settings for each state
    • Controls voltage monitoring
    • Logs to /var/log/uconsole-power-regulator.log
  2. uconsole-power-daemon.sh - Background daemon

    • Monitors AC power and 4G modem state every 5 seconds
    • Triggers regulator only when state changes
    • Handles edge cases (e.g., service starts with AC already connected)
  3. Voltage Monitoring System:

    • voltage-monitor.sh - Checks voltage every 5 seconds when Battery + 4G active
    • voltage-alert-notify.sh - Multi-method alerts (desktop notification, audio, log, LED)
    • voltage-monitor-control.sh - Start/stop controller
    • Alerts when voltage < 3.45V, rate-limited to every 30 seconds
  4. udev Rules: scripts/99-uconsole-power-regulator.rules

    • Triggers on AC power connect/disconnect (power_supply subsystem)
    • Triggers on 4G modem USB device changes (vendor:product = 1e0e:9001)
    • Monitors wwan0 interface state changes
    • Responds to ttyUSB and cdc-wdm device events
  5. Power Modes:

    State AC 4G Governor Max Freq Voltage Monitoring
    AC_POWER Connected Any ondemand 2.4GHz Off
    BATTERY_4G Disconnected Active powersave 1.8GHz On
    BATTERY_ONLY Disconnected Inactive ondemand 2.0GHz Off
  6. Installation: scripts/install-uconsole-power-regulator.sh

    • Fresh install: sudo ./install-uconsole-power-regulator.sh install
    • Upgrade from old system: sudo ./install-uconsole-power-regulator.sh upgrade
    • Uninstall: sudo ./install-uconsole-power-regulator.sh uninstall

Hardware Context

  • AXP228 PMIC: Has IPS™ (Intelligent Power Select) that manages USB, AC, and battery inputs
  • Power Limits: Battery discharge path limited to ~18-20W sustained
  • Battery Config: Two 18650 cells in parallel (not series, no switching)
  • USB Supplementation: When on USB power, can combine USB + battery for ~25W total
  • 4G Modem: SimTech SIM7600G-H (Qualcomm), creates ttyUSB0-4 and wwan0 interface

Integration with Battery Monitor

The battery monitor app can display real-time power consumption, helping users:

  • Monitor if power stays below 18-20W threshold
  • Verify 4G power manager is working (power should drop when 4G activates)
  • Track battery voltage under load (should stay above 3.3V)

Tuning

Edit scripts/4g-power-manager.sh configuration:

MAX_FREQ_POWERSAVE="1800000"  # Lower for more power saving (try 1500000)
POWERSAVE_GOVERNOR="powersave" # Or try "conservative"

Commit Messages

  • Do not mention Claude Code or AI assistance in commit messages
  • Focus on technical changes and benefits to users
  • do not mention claude in git commit