ESP32 Reciever
unknown
csharp
2 days ago
6.8 kB
10
Indexable
// ============================================================
// XIAO C6 #2 — Receiver Hub
// Board: Seeed XIAO ESP32-C6
// MAC: 58:E6:C5:00:94:CC
//
// Receives from:
// - XIAO C6 #1 (INA228 @ 0x40 = Solar1, 0x41 = Solar2)
// - XIAO C6 #3 (ESC PWM + VESC telemetry, source = 1)
// - XIAO C6 #4 (HX711 thrust only, source = 2)
// Sends:
// - CSV: prefixed 18-value string over USB Serial to LabVIEW
// - Throttle/ESTOP commands to ESC board via ESP-NOW
//
// CSV Format (18 values):
// 0 Solar1_V
// 1 Solar1_A
// 2 Solar1_W
// 3 Solar2_V
// 4 Solar2_A
// 5 Solar2_W
// 6 Battery_V (0.0 not used)
// 7 Battery_A (0.0 not used)
// 8 Battery_W (0.0 not used)
// 9 Thrust_kg
// 10 Throttle_%
// 11 RPM
// 12 Motor_V
// 13 Motor_A
// 14 Motor_W
// 15 Motor_Temp_C
// 16 PWM_us
// 17 INA_Die_Temp_C
// ============================================================
#include <esp_now.h>
#include <WiFi.h>
#include <esp_wifi.h>
// ---- ESC Board MAC (XIAO C6 #3) ----
uint8_t escBoardMAC[] = {0x58, 0xE6, 0xC5, 0x01, 0x8D, 0x38};
// ---- INA228 Packet ----
typedef struct __attribute__((packed)) {
uint8_t addr7;
uint32_t ms;
uint32_t errorMask;
uint16_t config;
uint16_t adc_config;
uint16_t shunt_cal;
uint16_t shunt_tempco;
int32_t vshunt24;
int32_t vbus24;
int16_t dietemp16;
int32_t current24;
uint32_t power24;
uint64_t energy40;
int64_t charge40;
uint16_t diag_alrt;
uint16_t sovl;
uint16_t suvl;
uint16_t bovl;
uint16_t buvl;
uint16_t temp_limit;
uint16_t pwr_limit;
uint16_t manufacturer_id;
uint16_t device_id;
} InaPacket;
// ---- ESC Board Packet (source = 1) ----
typedef struct __attribute__((packed)) {
uint8_t source;
float throttle_pct;
uint32_t pwm_us;
float rpm;
float motor_voltage;
float motor_current;
float motor_power;
float motor_temp;
bool vesc_valid;
} EscPacket;
// ---- HX711 Board Packet (source = 2) ----
typedef struct __attribute__((packed)) {
uint8_t source;
float thrust_kg;
} HX711Packet;
// ---- Command Packet to ESC board ----
typedef struct __attribute__((packed)) {
uint8_t source;
float throttle_pct;
uint8_t estop;
} CommandPacket;
// ---- INA228 Conversion (0.005 ohm shunt) ----
#define VBUS_LSB 195.3125e-6f
#define CURRENT_LSB (1.0f / (819.2e6f * 0.005f))
#define POWER_LSB (3.2f * CURRENT_LSB)
#define TEMP_LSB 7.8125e-3f
// ---- Data Storage ----
struct PowerData {
float voltage_V;
float current_A;
float power_W;
float temp_C;
bool valid;
};
PowerData ina0 = {0};
PowerData ina1 = {0};
struct EscData {
float throttle_pct;
uint32_t pwm_us;
float rpm;
float motor_voltage;
float motor_current;
float motor_power;
float motor_temp;
bool vesc_valid;
bool valid;
};
EscData escData = {0};
float thrustKg = 0.0;
// ---- E-stop state ----
bool estopActive = false;
float lastThrottleCmd = 0.0;
// ---- Non-blocking serial buffer ----
String serialBuffer = "";
// ---- ESP-NOW peer ----
esp_now_peer_info_t escPeer;
// ---- Decode INA228 ----
PowerData decodeINA(const InaPacket &p) {
PowerData d;
d.voltage_V = p.vbus24 * VBUS_LSB;
d.current_A = p.current24 * CURRENT_LSB;
d.power_W = p.power24 * POWER_LSB;
d.temp_C = p.dietemp16 * TEMP_LSB;
d.valid = (p.errorMask == 0);
return d;
}
// ---- Receive Callback ----
void onDataReceived(const esp_now_recv_info *recvInfo, const uint8_t *data, int len) {
// INA228 from XIAO #1
if (len == sizeof(InaPacket)) {
InaPacket pkt;
memcpy(&pkt, data, sizeof(pkt));
if (pkt.addr7 == 0x40) ina0 = decodeINA(pkt);
else if (pkt.addr7 == 0x41) ina1 = decodeINA(pkt);
return;
}
// ESC board data
if (len == sizeof(EscPacket)) {
EscPacket pkt;
memcpy(&pkt, data, sizeof(pkt));
if (pkt.source == 1) {
escData.throttle_pct = pkt.throttle_pct;
escData.pwm_us = pkt.pwm_us;
escData.rpm = pkt.rpm;
escData.motor_voltage = pkt.motor_voltage;
escData.motor_current = pkt.motor_current;
escData.motor_power = pkt.motor_power;
escData.motor_temp = pkt.motor_temp;
escData.vesc_valid = pkt.vesc_valid;
escData.valid = true;
}
return;
}
// HX711 thrust data
if (len == sizeof(HX711Packet)) {
HX711Packet pkt;
memcpy(&pkt, data, sizeof(pkt));
if (pkt.source == 2) {
thrustKg = pkt.thrust_kg;
}
return;
}
}
// ---- Send command to ESC board ----
void sendCommand(float throttle, bool estop) {
CommandPacket cmd;
cmd.source = 10;
cmd.throttle_pct = throttle;
cmd.estop = estop ? 1 : 0;
esp_now_send(escBoardMAC, (uint8_t *)&cmd, sizeof(cmd));
}
// ---- Non-blocking serial command parser ----
void checkSerialCommands() {
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
serialBuffer.trim();
if (serialBuffer == "ESTOP") {
estopActive = true;
lastThrottleCmd = 0.0;
sendCommand(0.0, true);
} else if (serialBuffer.startsWith("CMD:")) {
float throttle = constrain(serialBuffer.substring(4).toFloat(), 0.0, 100.0);
estopActive = false;
lastThrottleCmd = throttle;
sendCommand(throttle, false);
}
serialBuffer = "";
} else if (c != '\r') {
serialBuffer += c;
}
}
}
void setup() {
Serial.begin(115200);
delay(200);
// ESP-NOW with legacy protocol for C6 compatibility
WiFi.mode(WIFI_STA);
esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
if (esp_now_init() != ESP_OK) {
while (true) delay(1000);
}
esp_now_register_recv_cb(onDataReceived);
memcpy(escPeer.peer_addr, escBoardMAC, 6);
escPeer.channel = 1;
escPeer.encrypt = false;
esp_now_add_peer(&escPeer);
}
void loop() {
checkSerialCommands();
// Send 18-value CSV to LabVIEW
Serial.printf("CSV:"
"%.4f,%.4f,%.4f,"
"%.4f,%.4f,%.4f,"
"%.4f,%.4f,%.4f,"
"%.3f,"
"%.1f,"
"%.0f,"
"%.3f,%.3f,%.3f,"
"%.1f,"
"%lu,"
"%.2f\n",
ina0.voltage_V, ina0.current_A, ina0.power_W,
ina1.voltage_V, ina1.current_A, ina1.power_W,
0.0, 0.0, 0.0,
thrustKg,
escData.throttle_pct,
escData.rpm,
escData.motor_voltage, escData.motor_current, escData.motor_power,
escData.motor_temp,
escData.pwm_us,
ina0.temp_C
);
delay(20);
}
Editor is loading...
Leave a Comment