- Update battery_low_pct from 15% to 20% for auto-dock trigger (Issue #410 requirement)
- Add social mood publisher for charging animation display
- Trigger face charging animation (happy mood) when entering CHARGING state
- ArUco marker ID 42 detection via RealSense D435i (already configured)
- Approach sequence: detect → align → slow → contact → verify charging
- 360° search then expand capability (DETECTING state handles search)
- Safety abort timeouts already implemented in FSM state machine
- IR beacon fallback detection also available
- ROS2 node for balance mode PID parameter management via pyvesc UART
- Tilt safety kill switch: ±45° pitch > 500ms triggers motor cutoff
- Startup ramp: gradual acceleration from 0 to full output over configurable duration
- IMU integration: subscribe to /imu/data for pitch/roll angle computation
- State publishing: /saltybot/balance_state with tilt angles, PID values, motor telemetry
- Data logging: /saltybot/balance_log publishes CSV-formatted IMU + motor data
- Configurable parameters: PID gains, tilt thresholds, ramp duration, control frequency
- Test suite: quaternion to Euler conversion, tilt safety checks, startup ramp
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- ROS2 node for balance mode PID parameter management via pyvesc UART
- Tilt safety kill switch: ±45° pitch > 500ms triggers motor cutoff
- Startup ramp: gradual acceleration from 0 to full output over configurable duration
- IMU integration: subscribe to /imu/data for pitch/roll angle computation
- State publishing: /saltybot/balance_state with tilt angles, PID values, motor telemetry
- Data logging: /saltybot/balance_log publishes CSV-formatted IMU + motor data
- Configurable parameters: PID gains, tilt thresholds, ramp duration, control frequency
- Test suite: quaternion to Euler conversion, tilt safety checks, startup ramp
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Implement circular buffer bag recorder with:
- Configurable topics recording
- Last N minutes circular buffer (default 30min)
- Manual save trigger via /saltybot/save_bag service
- Auto-save on crash with signal handlers
- Storage management (7-day TTL, 50GB quota)
- Compression via zstd
- Optional rsync to NAS for backup
- Periodic maintenance (cleanup expired, enforce quota)
Saves to /home/seb/rosbags/ by default.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Implement EncounterSyncService ROS2 node for managing offline-first
encounter data syncing. Features:
- Monitors /home/seb/encounter-queue/ for JSON files
- Uploads to configurable cloud API when connectivity detected
- Exponential backoff retry with max 5 attempts
- Moves synced files to /home/seb/encounter-queue/synced/
- Publishes status on /social/encounter_sync_status topic
- Connectivity check via HTTP ping (configurable URL)
- Handles offline operation gracefully
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Implement EncounterSyncService ROS2 node for managing offline-first
encounter data syncing. Features:
- Monitors /home/seb/encounter-queue/ for JSON files
- Uploads to configurable cloud API when connectivity detected
- Exponential backoff retry with max 5 attempts
- Moves synced files to /home/seb/encounter-queue/synced/
- Publishes status on /social/encounter_sync_status topic
- Connectivity check via HTTP ping (configurable URL)
- Handles offline operation gracefully
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Implement state machine for detecting and enrolling unknown persons.
Manages workflow: DETECT → GREET → ASK_NAME → SMALL_TALK → ENROLL → FAREWELL
Features:
- Subscribes to /saltybot/person_tracker for unknown face detection
- Unknown person threshold configurable (default: 30% confidence)
- State machine with Piper TTS triggers for each state
- Captures STT responses for name and conversation context
- Publishes /social/orchestrator/state for coordination with other nodes
- Handles person interruptions gracefully (walks away)
- Auto-enrolls person to face gallery (configurable)
- Stores encounter data as JSON in /home/seb/encounter-queue/
- Tracks duration, responses, interests, and enrollment success
Encounter data structure:
{
person_id, timestamp, state, name, context, greeting_response,
interests[], enrollment_success, duration_sec, notes
}
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Creates log-mel spectrogram template for 'hey salty' wake word detection
using synthetic speech generation. Template generated from 5 synthetic
audio samples with varying pitch to improve robustness.
- generate_wake_word_template.py: Script to synthesize and generate template
- hey_salty.npy: 40-band log-mel template (40, 61) shape
- wake_word_params.yaml: Updated template_path
- README.md: Documentation for template usage and retraining procedures
The template is used by wake_word_node.py via cosine similarity matching
against incoming audio. Configurable sensitivity via match_threshold.
Future work: Collect real training recordings to improve accuracy.
- Detect if MCU was reset by IWDG watchdog timeout at startup
- Log watchdog reset events to debug terminal (USB CDC)
- Store watchdog reset flag for status reporting to Jetson
- Watchdog timer configured with 2-second timeout in safety_init()
- Main loop calls safety_refresh() to kick the watchdog every iteration
The IWDG (Independent Watchdog) resets the MCU if the main loop
hangs and fails to call safety_refresh() within the timeout window.
This provides hardware-enforced detection of software failures.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Adds #include "bno055.h" to src/main.c to resolve implicit declaration
warnings for bno055_read(), bno055_calib_status(), and bno055_temperature().
Functions were properly implemented but header was missing from includes.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Implement SCS/STS serial protocol driver for Waveshare ST3215 servos
at 1Mbps daisy-chain configuration. Pan and tilt servos on single UART.
Features:
- SCSServoBus class: Low-level protocol handler with packet construction
- Position write commands with configurable speed (0-1000)
- Position and temperature readback from servos
- PanTiltNode: ROS2 node with target tracking control loop
- Subscribes to /saltybot/target_track for centroid position
- Proportional control to keep target centered in D435i FOV
- Publishes /pan_tilt/state with angles and temperatures
- Publishes /pan_tilt/command for servo position monitoring
- 30 Hz control loop, 1 Hz telemetry loop
- Configurable servo limits and speeds
Servos: 0.24° resolution, 0-4095 position range
Camera: 87° × 58° field of view
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Add coulomb counter for accurate SoC estimation independent of load:
- New coulomb_counter module: integrate current over time to track Ah consumed
* coulomb_counter_init(capacity_mah) initializes with battery capacity
* coulomb_counter_accumulate(current_ma) integrates current at 100 Hz
* coulomb_counter_get_soc_pct() returns SoC 0-100% (255 = invalid)
* coulomb_counter_reset() for charge-complete reset
- Battery module integration:
* battery_accumulate_coulombs() reads motor INA219 currents and accumulates
* battery_get_soc_coulomb() returns coulomb-based SoC with fallback to voltage
* Initialize coulomb counter at startup with DEFAULT_BATTERY_CAPACITY_MAH
- Telemetry updates:
* JLink STATUS: use coulomb SoC if available, fallback to voltage-based
* CRSF battery frame: now includes remaining capacity in mAh (from coulomb counter)
* CRSF capacity field was always 0; now reflects actual remaining mAh
- Mainloop integration:
* Call battery_accumulate_coulombs() every tick for continuous integration
* INA219 motor currents + 200 mA subsystem baseline = total battery draw
Motor current sources (INA219 addresses 0x40/0x41) provide most power draw;
Jetson ROS2 battery_node already prioritizes coulomb-based soc_pct from STATUS frame.
Default capacity: 2200 mAh (typical lab 3S LiPo); configurable via firmware parameter.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Replace safety.c's direct IWDG initialization with watchdog module API
- Use watchdog_init(2000) for ~2s timeout in safety_init()
- Use watchdog_kick() in safety_refresh() to feed the watchdog
- Remove unused watchdog_get_divider() helper function
- Watchdog now configured with automatic prescaler selection
The watchdog module provides a clean, flexible IWDG interface that:
- Automatically calculates prescaler and reload values
- Detects watchdog-triggered resets via watchdog_was_reset_by_watchdog()
- Supports timeout range of ~1ms to ~32 seconds
- Integrates seamlessly with existing safety system
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>