sl-perception 410ace3540 feat: battery coulomb counter (Issue #325)
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>
2026-03-03 17:35:34 -05:00

70 lines
2.3 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef CRSF_H
#define CRSF_H
#include <stdint.h>
#include <stdbool.h>
/*
* CRSF/ExpressLRS RC receiver state.
*
* Updated from ISR context on every valid frame.
* Read from main loop — values are naturally atomic (8/16-bit on Cortex-M).
* last_rx_ms == 0 means no frame received yet (USB-only mode).
*/
typedef struct {
uint16_t channels[16]; /* Raw CRSF values, 172 (988µs) 1811 (2012µs) */
uint32_t last_rx_ms; /* HAL_GetTick() at last valid RC frame */
bool armed; /* CH5 arm switch: true when channels[4] > CRSF_ARM_THRESHOLD */
/* Link statistics (from 0x14 frames, optional) */
int8_t rssi_dbm; /* Uplink RSSI in dBm (negative, e.g. -85) */
uint8_t link_quality; /* Uplink link quality 0100 % */
int8_t snr; /* Uplink SNR in dB */
} CRSFState;
/*
* crsf_init() — configure UART4 (PA0=TX, PA1=RX) at 420000 baud with
* DMA1 circular RX and IDLE interrupt. Call once before safety_init().
*/
void crsf_init(void);
/*
* crsf_parse_byte() — feed one byte into the frame parser.
* Called automatically from DMA/IDLE ISR. Available for unit tests.
*/
void crsf_parse_byte(uint8_t byte);
/*
* crsf_to_range() — map raw CRSF value (1721811) linearly to [min, max].
* Clamps at boundaries. Midpoint 992 → (min+max)/2.
*/
int16_t crsf_to_range(uint16_t val, int16_t min, int16_t max);
/*
* crsf_send_battery() — transmit CRSF battery-sensor telemetry frame (type 0x08)
* back to the ELRS TX module over UART4 TX. Call at CRSF_TELEMETRY_HZ (1 Hz).
*
* voltage_mv : battery voltage in millivolts (e.g. 12600 for 3S full)
* capacity_mah : remaining battery capacity in mAh (Issue #325, coulomb counter)
* remaining_pct: state-of-charge 0100 % (255 = unknown)
*
* Frame: [0xC8][12][0x08][v16_hi][v16_lo][c16_hi][c16_lo][cap24×3][rem][CRC]
* voltage unit: 100 mV (12600 mV → 126)
* capacity unit: mAh (3-byte big-endian, max 16.7M mAh)
*/
void crsf_send_battery(uint32_t voltage_mv, uint32_t capacity_mah,
uint8_t remaining_pct);
/*
* crsf_send_flight_mode() — transmit CRSF flight-mode frame (type 0x21)
* for display on the pilot's handset OSD.
*
* armed: true → "ARMED\0"
* false → "DISARM\0"
*/
void crsf_send_flight_mode(bool armed);
extern volatile CRSFState crsf_state;
#endif /* CRSF_H */