Untitled
unknown
plain_text
2 days ago
7.9 kB
8
Indexable
// ============================================================
// XIAO C6 #3 — ESC + VESC Controller
// Board: Seeed XIAO ESP32-C6
// MAC: 58:E6:C5:01:8D:38
//
// - Reads incoming PWM signal from ESC
// - Reads VESC telemetry (RPM, voltage, current, temp)
// - Outputs throttle PWM to ESC from LabVIEW commands
// - Sends all ESC/VESC data to XIAO C6 #2 via ESP-NOW
//
// XIAO C6 Pin Mapping:
// D6 = GPIO16 (VESC TX out)
// D7 = GPIO17 (VESC RX in)
// D8 = GPIO19 (PWM OUT to ESC)
// D9 = GPIO20 (PWM IN from ESC monitor)
// ============================================================
#include <esp_now.h>
#include <WiFi.h>
#include <esp_wifi.h>
#include <ESP32Servo.h>
#include <VescUart.h>
#include <HardwareSerial.h>
// ---- Pin Definitions (XIAO C6) ----
#define PWM_OUT_PIN 19 // D8 → ESC signal input
#define PWM_IN_PIN 20 // D9 ← ESC PWM signal monitor
// ---- VESC UART (Serial1) ----
#define VESC_RX_PIN 17 // D7
#define VESC_TX_PIN 16 // D6
// ---- Motor pole pairs — update to match your motor ----
#define MOTOR_POLE_PAIRS 7
// ---- Safety ----
#define PWM_MIN_US 1000
#define PWM_MAX_US 2000
#define PWM_ARM_US 1000
#define TIMEOUT_MS 2000
// ---- Receiver MAC (XIAO C6 #2) ----
uint8_t receiverMAC[] = {0x58, 0xE6, 0xC5, 0x00, 0x94, 0xCC};
// ---- Servo/ESC Output ----
Servo esc;
// ---- VESC ----
VescUart vesc;
// ---- PWM Input Reading ----
volatile unsigned long pwmRiseTime = 0;
volatile unsigned long pwmPulseWidth = 0;
volatile bool pwmNewReading = false;
void IRAM_ATTR pwmFallingEdge();
void IRAM_ATTR pwmRisingEdge() {
pwmRiseTime = micros();
attachInterrupt(digitalPinToInterrupt(PWM_IN_PIN), pwmFallingEdge, FALLING);
}
void IRAM_ATTR pwmFallingEdge() {
pwmPulseWidth = micros() - pwmRiseTime;
pwmNewReading = true;
attachInterrupt(digitalPinToInterrupt(PWM_IN_PIN), pwmRisingEdge, RISING);
}
// ---- Command Packet (received from XIAO #2) ----
typedef struct __attribute__((packed)) {
uint8_t source;
float throttle_pct;
uint8_t estop;
} CommandPacket;
// ---- Data Packet (sent to XIAO #2) ----
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;
// ---- State ----
float currentThrottle = 0.0;
bool estopActive = false;
bool prevEstopState = false;
String estopSource = "none";
unsigned long lastCommandTime = 0;
EscPacket outPacket;
esp_now_peer_info_t receiverPeer;
// ---- Receive Callback ----
void onDataReceived(const esp_now_recv_info *recvInfo, const uint8_t *data, int len) {
if (len != sizeof(CommandPacket)) return;
CommandPacket cmd;
memcpy(&cmd, data, sizeof(cmd));
if (cmd.source != 10) return;
lastCommandTime = millis();
if (cmd.estop) {
estopActive = true;
currentThrottle = 0.0;
estopSource = "LabVIEW";
} else {
estopActive = false;
estopSource = "none";
currentThrottle = constrain(cmd.throttle_pct, 0.0, 100.0);
}
}
// ---- Send Callback ----
void onDataSent(const wifi_tx_info_t *txInfo, esp_now_send_status_t status) {
if (status != ESP_NOW_SEND_SUCCESS) {
Serial.println("ESC board: Send FAILED");
}
}
int throttleToPWM(float pct) {
return (int)(PWM_MIN_US + (pct / 100.0) * (PWM_MAX_US - PWM_MIN_US));
}
void setup() {
Serial.begin(115200);
delay(200);
// -- ESC PWM Output --
esc.attach(PWM_OUT_PIN, PWM_MIN_US, PWM_MAX_US);
esc.writeMicroseconds(PWM_ARM_US);
Serial.println("ESC arming...");
delay(3000);
Serial.println("ESC armed");
// -- PWM Input --
pinMode(PWM_IN_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(PWM_IN_PIN), pwmRisingEdge, RISING);
Serial.println("PWM monitor ready");
// -- VESC UART --
Serial1.begin(115200, SERIAL_8N1, VESC_RX_PIN, VESC_TX_PIN);
vesc.setSerialPort(&Serial1);
Serial.println("VESC UART ready");
// -- 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);
Serial.print("ESC board MAC: ");
Serial.println(WiFi.macAddress());
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed");
while (true) delay(1000);
}
esp_now_register_recv_cb(onDataReceived);
esp_now_register_send_cb(onDataSent);
memcpy(receiverPeer.peer_addr, receiverMAC, 6);
receiverPeer.channel = 1;
receiverPeer.encrypt = false;
if (esp_now_add_peer(&receiverPeer) != ESP_OK) {
Serial.println("Failed to add peer");
while (true) delay(1000);
}
outPacket.source = 1;
Serial.println("ESC board ready");
}
void loop() {
// -- Watchdog E-stop --
if (millis() - lastCommandTime > TIMEOUT_MS && lastCommandTime > 0) {
if (!estopActive) {
estopActive = true;
currentThrottle = 0.0;
estopSource = "timeout";
}
}
// -- Detect estop state changes --
if (estopActive && !prevEstopState) {
Serial.println("╔══════════════════════════════╗");
Serial.printf ("║ !!! ESTOP ACTIVE !!! ║\n");
Serial.printf ("║ Source: %-20s║\n", estopSource.c_str());
Serial.println("║ PWM locked to 1000us (0%) ║");
Serial.println("╚══════════════════════════════╝");
} else if (!estopActive && prevEstopState) {
Serial.println("╔══════════════════════════════╗");
Serial.println("║ ESTOP CLEARED ║");
Serial.printf ("║ Resuming throttle: %.1f%% ║\n", currentThrottle);
Serial.println("╚══════════════════════════════╝");
}
prevEstopState = estopActive;
// -- Apply throttle to ESC --
int pwmOut = estopActive ? PWM_MIN_US : throttleToPWM(currentThrottle);
esc.writeMicroseconds(pwmOut);
// -- Read incoming PWM --
if (pwmNewReading) {
noInterrupts();
unsigned long pw = pwmPulseWidth;
pwmNewReading = false;
interrupts();
pw = constrain(pw, 1000, 2000);
outPacket.pwm_us = pw;
outPacket.throttle_pct = (pw - 1000.0) / 10.0;
}
// -- Read VESC telemetry --
if (vesc.getVescValues()) {
outPacket.rpm = abs(vesc.data.rpm) / MOTOR_POLE_PAIRS;
outPacket.motor_voltage = vesc.data.inpVoltage;
outPacket.motor_current = vesc.data.avgInputCurrent;
outPacket.motor_power = vesc.data.inpVoltage * vesc.data.avgInputCurrent;
outPacket.motor_temp = vesc.data.tempMosfet;
outPacket.vesc_valid = true;
} else {
outPacket.vesc_valid = false;
}
// -- Send to receiver --
esp_now_send(receiverMAC, (uint8_t *)&outPacket, sizeof(outPacket));
// -- Debug every 2 seconds --
static unsigned long lastDebug = 0;
if (millis() - lastDebug >= 2000) {
Serial.println("------ ESC Board STATUS ------");
Serial.printf("E-Stop: %s (source: %s)\n",
estopActive ? "ACTIVE" : "clear", estopSource.c_str());
Serial.printf("Throttle: %.1f%%\n", currentThrottle);
Serial.printf("PWM out: %d us\n", pwmOut);
Serial.printf("PWM in: %lu us\n", outPacket.pwm_us);
Serial.printf("RPM: %.0f\n", outPacket.rpm);
Serial.printf("VESC: %s\n", outPacket.vesc_valid ? "OK" : "NO SIGNAL");
Serial.println("------------------------------");
lastDebug = millis();
}
delay(20);
}
Editor is loading...
Leave a Comment