From b8a4f4c070ff34c7ddf0429ab22a00673688bf0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 22:27:03 +0000 Subject: [PATCH 1/3] Initial plan From 9e0cec7fcca7b7ddce4be8564fc7bf7d407eef23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 22:28:58 +0000 Subject: [PATCH 2/3] feat: add unified ESP32 BLE actuator and ESC sketch Agent-Logs-Url: https://github.com/uscmakers/SkateMo/sessions/1ab7fa0a-dd82-4346-afcf-1e686e1c6bbe Co-authored-by: ycjohnp <198846355+ycjohnp@users.noreply.github.com> --- .../BLEActuatorEscUnified.ino | 252 ++++++++++++++++++ README.md | 10 +- 2 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 BLEActuatorEscUnified/BLEActuatorEscUnified.ino diff --git a/BLEActuatorEscUnified/BLEActuatorEscUnified.ino b/BLEActuatorEscUnified/BLEActuatorEscUnified.ino new file mode 100644 index 0000000..91bb805 --- /dev/null +++ b/BLEActuatorEscUnified/BLEActuatorEscUnified.ino @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include + +// ── Potentiometer ──────────────────────────────────────────────── +#define PIN_SLIDE_POT_A 35 + +// Position targets (tuned to your pot's real range) +#define RIGHT_TARGET 850 +#define LEFT_TARGET 275 +#define MID_TARGET 600 + +// Hard safety limits +#define RIGHT_LIMIT 950 +#define LEFT_LIMIT 100 + +#define TOLERANCE 25 +#define STEER_PWM_SPEED 200 + +// ── Actuator H-bridge pins ─────────────────────────────────────── +const int IN1 = 25; +const int IN2 = 26; +const int ENA = 27; + +// ── ESC output ──────────────────────────────────────────────────── +// Safe default pin for ESP32 signal output that does not conflict with actuator pins. +const int ESC_PIN = 14; + +// Typical RC ESC pulse range is 1000–2000 us. Keep limits configurable and cap throttle. +const int ESC_MIN_US = 1000; +const int ESC_MAX_US = 2000; +const int ESC_THROTTLE_CAP_US = 1500; // Conservative forward throttle cap. +const int ESC_ARM_DELAY_MS = 2000; + +const int THROTTLE_STEP_US = 10; +const int THROTTLE_UPDATE_MS = 20; + +// ── BLE config (must match BLEMotorCombined) ───────────────────── +#define SERVICE_UUID "12345678-1234-1234-1234-123456789012" +#define CHARACTERISTIC_UUID "87654321-4321-4321-4321-210987654321" + +BLECharacteristic* pCharacteristic = nullptr; +bool deviceConnected = false; +Servo esc; + +// ── Commands ────────────────────────────────────────────────────── +enum Command { + FWD = 0, + BACK = 1, + LEFT = 2, + RIGHT = 3, + CMD_STOP = 4 +}; + +// ── Steering state machine ──────────────────────────────────────── +enum State { STOP, STEER_LEFT, STEER_RIGHT, STRAIGHT }; +State currentState = STOP; + +int currentThrottleUs = ESC_MIN_US; +int targetThrottleUs = ESC_MIN_US; +unsigned long lastThrottleUpdateMs = 0; + +void handleCommand(char cmd); + +// ── Actuator helpers ────────────────────────────────────────────── +void moveLeft() { + ledcWrite(ENA, STEER_PWM_SPEED); + digitalWrite(IN1, HIGH); + digitalWrite(IN2, LOW); +} + +void moveRight() { + ledcWrite(ENA, STEER_PWM_SPEED); + digitalWrite(IN1, LOW); + digitalWrite(IN2, HIGH); +} + +void stopSteeringMotor() { + digitalWrite(IN1, LOW); + digitalWrite(IN2, LOW); + ledcWrite(ENA, 0); +} + +// ── ESC helpers ─────────────────────────────────────────────────── +int clampEscUs(int value) { + if (value < ESC_MIN_US) return ESC_MIN_US; + if (value > ESC_MAX_US) return ESC_MAX_US; + return value; +} + +void writeEscUs(int value) { + currentThrottleUs = clampEscUs(value); + esc.writeMicroseconds(currentThrottleUs); +} + +void applyFailSafeStop() { + currentState = STOP; + targetThrottleUs = ESC_MIN_US; + stopSteeringMotor(); + writeEscUs(ESC_MIN_US); +} + +void updateThrottleRamp() { + unsigned long now = millis(); + if (now - lastThrottleUpdateMs < THROTTLE_UPDATE_MS) { + return; + } + lastThrottleUpdateMs = now; + + if (currentThrottleUs < targetThrottleUs) { + writeEscUs(currentThrottleUs + THROTTLE_STEP_US); + } else if (currentThrottleUs > targetThrottleUs) { + writeEscUs(currentThrottleUs - THROTTLE_STEP_US); + } +} + +// ── BLE callbacks ───────────────────────────────────────────────── +class ServerCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + Serial.println("BLE client connected."); + } + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + applyFailSafeStop(); + Serial.println("BLE disconnected. Fail-safe stop and restart advertising."); + pServer->startAdvertising(); + } +}; + +class CharacteristicCallbacks : public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + String value = pCharacteristic->getValue(); + if (value.length() > 0) { + handleCommand((char)value[0]); + } + } +}; + +// ── Command handler ─────────────────────────────────────────────── +void handleCommand(char cmd) { + switch (cmd) { + case FWD: + Serial.println("CMD: FWD -> straight + throttle"); + currentState = STRAIGHT; + targetThrottleUs = ESC_THROTTLE_CAP_US; + break; + + case BACK: + Serial.println("CMD: BACK -> stop steering + throttle min"); + currentState = STOP; + stopSteeringMotor(); + targetThrottleUs = ESC_MIN_US; + break; + + case LEFT: + Serial.println("CMD: LEFT"); + currentState = STEER_LEFT; + break; + + case RIGHT: + Serial.println("CMD: RIGHT"); + currentState = STEER_RIGHT; + break; + + case CMD_STOP: + Serial.println("CMD: STOP"); + currentState = STOP; + stopSteeringMotor(); + targetThrottleUs = ESC_MIN_US; + break; + + default: + Serial.println("CMD: Unknown"); + break; + } + + if (deviceConnected && pCharacteristic) { + uint8_t ack = 0x80 | (uint8_t)cmd; + pCharacteristic->setValue(&ack, 1); + pCharacteristic->notify(); + } +} + +// ── Setup ───────────────────────────────────────────────────────── +void setup() { + Serial.begin(115200); + + pinMode(PIN_SLIDE_POT_A, INPUT); + pinMode(IN1, OUTPUT); + pinMode(IN2, OUTPUT); + ledcAttach(ENA, 5000, 8); + stopSteeringMotor(); + + esc.setPeriodHertz(50); + esc.attach(ESC_PIN, ESC_MIN_US, ESC_MAX_US); + writeEscUs(ESC_MIN_US); + targetThrottleUs = ESC_MIN_US; + + Serial.println("Arming ESC at minimum throttle..."); + delay(ESC_ARM_DELAY_MS); + Serial.println("ESC armed."); + + BLEDevice::init("ESP32-SteerBot"); + BLEServer* pServer = BLEDevice::createServer(); + pServer->setCallbacks(new ServerCallbacks()); + + BLEService* pService = pServer->createService(SERVICE_UUID); + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY + ); + pCharacteristic->setCallbacks(new CharacteristicCallbacks()); + pCharacteristic->addDescriptor(new BLE2902()); + pCharacteristic->setValue("Unified BLE actuator+ESC ready"); + + pService->start(); + BLEDevice::getAdvertising()->addServiceUUID(SERVICE_UUID); + BLEDevice::getAdvertising()->setScanResponse(true); + BLEDevice::startAdvertising(); + + Serial.println("BLE advertising. Waiting for connection..."); +} + +// ── Loop ────────────────────────────────────────────────────────── +void loop() { + int potVal = analogRead(PIN_SLIDE_POT_A); + + int target = potVal; + if (currentState == STEER_LEFT) target = LEFT_TARGET; + else if (currentState == STEER_RIGHT) target = RIGHT_TARGET; + else if (currentState == STRAIGHT) target = MID_TARGET; + + if (currentState == STOP) { + stopSteeringMotor(); + } else { + if (potVal < target - TOLERANCE) moveRight(); + else if (potVal > target + TOLERANCE) moveLeft(); + else stopSteeringMotor(); + } + + if (potVal > RIGHT_LIMIT) moveLeft(); + if (potVal < LEFT_LIMIT) moveRight(); + + updateThrottleRamp(); + delay(20); +} diff --git a/README.md b/README.md index 5c171ce..0f3b97d 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -# SkateMo \ No newline at end of file +# SkateMo + +## Unified ESP32 BLE + Actuator + ESC sketch + +- New sketch: `BLEActuatorEscUnified/BLEActuatorEscUnified.ino` +- Combines BLE command handling, closed-loop steering actuator control (potentiometer feedback + H-bridge), and ESC throttle output via `ESP32Servo` (`writeMicroseconds`). +- Keeps BLE UUIDs from `BLEMotorCombined` and sends command ACK as `0x80 | cmd`. +- Uses safe ESC defaults (`1000–2000 us`) with startup arming and BLE-disconnect fail-safe to minimum throttle. +- Default ESC signal pin is GPIO14 (chosen to avoid conflicts with GPIO35/25/26/27 used by steering control). From 7ec2d32841d05828a4b4ad75575f3e3cf94774bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 22:31:33 +0000 Subject: [PATCH 3/3] chore: refine unified sketch safety logic and constants Agent-Logs-Url: https://github.com/uscmakers/SkateMo/sessions/1ab7fa0a-dd82-4346-afcf-1e686e1c6bbe Co-authored-by: ycjohnp <198846355+ycjohnp@users.noreply.github.com> --- .../BLEActuatorEscUnified.ino | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/BLEActuatorEscUnified/BLEActuatorEscUnified.ino b/BLEActuatorEscUnified/BLEActuatorEscUnified.ino index 91bb805..d4f4b16 100644 --- a/BLEActuatorEscUnified/BLEActuatorEscUnified.ino +++ b/BLEActuatorEscUnified/BLEActuatorEscUnified.ino @@ -23,6 +23,8 @@ const int IN1 = 25; const int IN2 = 26; const int ENA = 27; +const int LEDC_FREQ_HZ = 5000; +const int LEDC_RES_BITS = 8; // ── ESC output ──────────────────────────────────────────────────── // Safe default pin for ESP32 signal output that does not conflict with actuator pins. @@ -36,6 +38,7 @@ const int ESC_ARM_DELAY_MS = 2000; const int THROTTLE_STEP_US = 10; const int THROTTLE_UPDATE_MS = 20; +const int LOOP_DELAY_MS = 20; // ── BLE config (must match BLEMotorCombined) ───────────────────── #define SERVICE_UUID "12345678-1234-1234-1234-123456789012" @@ -192,7 +195,7 @@ void setup() { pinMode(PIN_SLIDE_POT_A, INPUT); pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT); - ledcAttach(ENA, 5000, 8); + ledcAttach(ENA, LEDC_FREQ_HZ, LEDC_RES_BITS); stopSteeringMotor(); esc.setPeriodHertz(50); @@ -231,22 +234,31 @@ void setup() { void loop() { int potVal = analogRead(PIN_SLIDE_POT_A); - int target = potVal; + int target = MID_TARGET; if (currentState == STEER_LEFT) target = LEFT_TARGET; else if (currentState == STEER_RIGHT) target = RIGHT_TARGET; else if (currentState == STRAIGHT) target = MID_TARGET; - if (currentState == STOP) { - stopSteeringMotor(); - } else { - if (potVal < target - TOLERANCE) moveRight(); - else if (potVal > target + TOLERANCE) moveLeft(); - else stopSteeringMotor(); + // Hard safety limits override steering state-machine motion for this cycle. + bool safetyOverride = false; + if (potVal > RIGHT_LIMIT) { + moveLeft(); + safetyOverride = true; + } else if (potVal < LEFT_LIMIT) { + moveRight(); + safetyOverride = true; } - if (potVal > RIGHT_LIMIT) moveLeft(); - if (potVal < LEFT_LIMIT) moveRight(); + if (!safetyOverride) { + if (currentState == STOP) { + stopSteeringMotor(); + } else { + if (potVal < target - TOLERANCE) moveRight(); + else if (potVal > target + TOLERANCE) moveLeft(); + else stopSteeringMotor(); + } + } updateThrottleRamp(); - delay(20); + delay(LOOP_DELAY_MS); }