New package saltybot_system_monitor: - jetson_stats.py: pure-Python data layer (JetsonStats, CpuCore, TegrastatsParser, JtopReader, TegrastatsReader, MockReader, AlertChecker, AlertThresholds) — no ROS2 dependency - system_monitor_node.py: ROS2 node publishing /saltybot/system/stats (JSON) and /saltybot/diagnostics (DiagnosticArray) at 1 Hz - Alerts: CPU/GPU >85% WARN (+10% ERROR), temp >80°C, disk/RAM >90%, power >30 W; each alert produces a DiagnosticStatus entry - Stats source priority: jtop > tegrastats > mock (auto-detected) - config/system_monitor.yaml: all thresholds and rate tunable via params - launch/system_monitor.launch.py: single-node launch with config arg - test/test_system_monitor.py: 50+ pytest tests, ROS2-free Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
130 lines
5.2 KiB
C
130 lines
5.2 KiB
C
#ifndef UART_PROTOCOL_H
|
|
#define UART_PROTOCOL_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
|
|
/*
|
|
* uart_protocol.h -- Structured UART command protocol for Jetson-STM32
|
|
* communication (Issue #629)
|
|
*
|
|
* Hardware: UART5, PC12=TX / PD2=RX, 115200 baud
|
|
* Note: spec references USART1 but USART1 is occupied by JLink (Issue #120)
|
|
* at 921600 baud. UART5 is the designated secondary/debug UART and is used
|
|
* here as the structured-command channel.
|
|
*
|
|
* Frame format (both directions):
|
|
* [STX=0x02][LEN][CMD][PAYLOAD...][CRC8][ETX=0x03]
|
|
*
|
|
* STX : frame start sentinel (0x02)
|
|
* LEN : CMD byte + payload bytes (1 + payload_len)
|
|
* CMD : command / response type byte
|
|
* PAYLOAD : 0..N bytes, CMD-dependent
|
|
* CRC8 : CRC8-SMBUS (poly 0x07, init 0x00) over CMD+PAYLOAD
|
|
* ETX : frame end sentinel (0x03)
|
|
*
|
|
* Host (Jetson) to STM32 commands:
|
|
* 0x01 SET_VELOCITY - int16 left_rpm, int16 right_rpm (4 bytes)
|
|
* 0x02 GET_STATUS - no payload; reply URESP_STATUS
|
|
* 0x03 SET_PID - float kp, float ki, float kd (12 bytes, LE)
|
|
* 0x04 ESTOP - no payload; engage emergency stop
|
|
* 0x05 CLEAR_ESTOP - no payload; clear emergency stop
|
|
*
|
|
* STM32 to host responses:
|
|
* 0x80 ACK - uint8 echoed_cmd (1 byte)
|
|
* 0x81 NACK - uint8 echoed_cmd, uint8 error_code (2 bytes)
|
|
* 0x82 STATUS - uart_prot_status_t (8 bytes)
|
|
*
|
|
* Heartbeat: if no valid frame is received within UART_PROT_HB_TIMEOUT_MS,
|
|
* uart_protocol_is_active() returns false and velocity commands are ignored.
|
|
*
|
|
* DMA: UART5_RX uses DMA1_Stream0_Channel4 in circular mode (256-byte ring).
|
|
* Parser runs in uart_protocol_process() called from the main loop.
|
|
*/
|
|
|
|
/* ---- Frame constants ---- */
|
|
#define UPROT_STX 0x02u
|
|
#define UPROT_ETX 0x03u
|
|
#define UPROT_RX_BUF_LEN 256u /* DMA ring buffer size (power of 2) */
|
|
#define UPROT_MAX_PAYLOAD 16u /* largest payload: SET_PID = 12 bytes */
|
|
|
|
/* ---- Command IDs (host to STM32) ---- */
|
|
#define UCMD_SET_VELOCITY 0x01u /* int16 left_rpm + int16 right_rpm */
|
|
#define UCMD_GET_STATUS 0x02u /* no payload */
|
|
#define UCMD_SET_PID 0x03u /* float kp, ki, kd (12 bytes LE) */
|
|
#define UCMD_ESTOP 0x04u /* no payload */
|
|
#define UCMD_CLEAR_ESTOP 0x05u /* no payload */
|
|
|
|
/* ---- Response IDs (STM32 to host) ---- */
|
|
#define URESP_ACK 0x80u /* 1-byte payload: echoed CMD */
|
|
#define URESP_NACK 0x81u /* 2-byte payload: CMD + error_code */
|
|
#define URESP_STATUS 0x82u /* 8-byte payload: uart_prot_status_t */
|
|
|
|
/* ---- NACK error codes ---- */
|
|
#define UERR_UNKNOWN_CMD 0x01u /* unrecognised CMD byte */
|
|
#define UERR_BAD_LENGTH 0x02u /* LEN does not match CMD expectation */
|
|
#define UERR_BAD_CRC 0x03u /* CRC8 mismatch */
|
|
#define UERR_REFUSED 0x04u /* command refused in current state */
|
|
|
|
/* ---- STATUS response payload (8 bytes, packed) ---- */
|
|
typedef struct __attribute__((packed)) {
|
|
int16_t pitch_x10; /* pitch angle, degrees x10 (0.1° resolution) */
|
|
int16_t motor_cmd; /* balance PID ESC output, -1000..+1000 */
|
|
uint16_t vbat_mv; /* battery voltage (mV) */
|
|
uint8_t balance_state; /* BalanceState: 0=DISARMED, 1=ARMED, 2=TILT */
|
|
uint8_t estop_active; /* 1 = estop currently engaged */
|
|
} uart_prot_status_t; /* 8 bytes */
|
|
|
|
/* ---- Volatile state (read from main loop) ---- */
|
|
typedef struct {
|
|
/* SET_VELOCITY: set by parser, cleared by main loop */
|
|
volatile uint8_t vel_updated; /* 1 = new velocity command pending */
|
|
volatile int16_t left_rpm; /* requested left wheel RPM */
|
|
volatile int16_t right_rpm; /* requested right wheel RPM */
|
|
|
|
/* SET_PID: set by parser, cleared by main loop */
|
|
volatile uint8_t pid_updated; /* 1 = new PID gains pending */
|
|
volatile float pid_kp;
|
|
volatile float pid_ki;
|
|
volatile float pid_kd;
|
|
|
|
/* ESTOP / CLEAR_ESTOP: set by parser, cleared by main loop */
|
|
volatile uint8_t estop_req;
|
|
volatile uint8_t estop_clear_req;
|
|
|
|
/* Heartbeat: updated on every valid frame */
|
|
volatile uint32_t last_rx_ms; /* HAL_GetTick() at last good frame; 0=none */
|
|
} UartProtState;
|
|
|
|
extern volatile UartProtState uart_prot_state;
|
|
|
|
/* ---- API ---- */
|
|
|
|
/*
|
|
* uart_protocol_init() -- configure UART5 + DMA1_Stream0 and start reception.
|
|
* Call once during system init, before the main loop.
|
|
*/
|
|
void uart_protocol_init(void);
|
|
|
|
/*
|
|
* uart_protocol_process() -- parse new bytes from DMA ring buffer, dispatch
|
|
* commands, and send ACK/NACK/STATUS responses.
|
|
* Call every main loop tick (non-blocking, < 5 µs when idle).
|
|
*/
|
|
void uart_protocol_process(void);
|
|
|
|
/*
|
|
* uart_protocol_is_active(now_ms) -- returns true if a valid frame was
|
|
* received within UART_PROT_HB_TIMEOUT_MS. Main loop uses this to gate
|
|
* SET_VELOCITY commands when the host is disconnected.
|
|
*/
|
|
bool uart_protocol_is_active(uint32_t now_ms);
|
|
|
|
/*
|
|
* uart_protocol_send_status(s) -- transmit a URESP_STATUS (0x82) frame.
|
|
* Called from main loop in response to GET_STATUS or periodically.
|
|
*/
|
|
void uart_protocol_send_status(const uart_prot_status_t *s);
|
|
|
|
#endif /* UART_PROTOCOL_H */
|