#pragma once /* vesc_can.h — VESC CAN TWAI driver for ESP32-S3 BALANCE (bd-66hx) * * VESC extended CAN ID: (packet_type << 8) | vesc_node_id * Physical layer: TWAI peripheral → SN65HVD230 → 500 kbps shared bus */ #include #include #include "freertos/FreeRTOS.h" #include "freertos/queue.h" /* ── VESC packet types ── */ #define VESC_PKT_SET_RPM 3u #define VESC_PKT_STATUS 9u /* int32 erpm, int16 I×10, int16 duty×1000 */ #define VESC_PKT_PING 17u /* CAN_PACKET_PING: data[0]=requester_id */ #define VESC_PKT_PONG 18u /* CAN_PACKET_PONG: data[0]=vesc_id, data[1]=hw */ #define VESC_PKT_STATUS_4 16u /* int16 T_fet×10, T_mot×10, I_in×10 */ #define VESC_PKT_STATUS_5 27u /* int32 tacho, int16 V_in×10 */ #define VESC_CAN_ID_SELF 1u /* our ESP32 CAN node ID (for PING replies) */ /* ── VESC telemetry snapshot ── */ typedef struct { int32_t erpm; /* electrical RPM (STATUS) */ int16_t current_x10; /* phase current A×10 (STATUS) */ int16_t voltage_x10; /* bus voltage V×10 (STATUS_5) */ int16_t temp_mot_x10; /* motor temp °C×10 (STATUS_4) */ uint32_t last_rx_ms; /* esp_timer ms of last STATUS frame */ } vesc_state_t; /* ── Globals ── */ extern vesc_state_t g_vesc[2]; /* index 0=VESC_ID_A, 1=VESC_ID_B */ extern volatile bool g_can_bus_active; /* true after any extended CAN frame received */ extern volatile bool g_vesc_alive[2]; /* true after PONG from each VESC */ /* ── API ── */ void vesc_can_init(void); void vesc_can_send_rpm(uint8_t vesc_id, int32_t erpm); void vesc_can_ping(uint8_t vesc_id); /* RX task — pass tx_queue as arg; forwards STATUS frames to Orin over serial */ void vesc_can_rx_task(void *arg); /* arg = QueueHandle_t orin_tx_queue */