Project Started
Table
| Function | ESP32-S3 pin | Schematic connection |
|---|---|---|
| VBAT ADC | GPIO1 / ADC1 | J2 pin 4, ADC_VBAT_GPIO1 |
| ESC Current ADC | GPIO2 / ADC1 | J2 pin 5, ADC_CURRENT_GPIO2 |
| Motor 1 | GPIO4 | J2 pin 7 → R8 → J1 pin 3 |
| Motor 2 | GPIO5 | J2 pin 8 → R9 → J1 pin 4 |
| Motor 3 | GPIO6 | J2 pin 9 → R10 → J1 pin 5 |
| Motor 4 | GPIO7 | J2 pin 10 → R11 → J1 pin 6 |
| I2C SDA | GPIO8 | J3 pin 1 → J4 pin 5 |
| I2C SCL | GPIO9 | J3 pin 2 → J4 pin 4 |
Before assembling or powering the board, compare the exact purchased ESP32-S3 SuperMini silkscreen labels with socket rows J2/J3. The carrier uses a selected common 1×10 + 1×8 representation; clone variants can differ.
platformio.ini:Ini
[env:esp32-s3-supermini] platform = espressif32 board = esp32-s3-devkitc-1 framework = arduino monitor_speed = 115200 board_build.flash_mode = qio board_build.arduino.memory_type = qio_qspi build_flags = -D ARDUINO_USB_CDC_ON_BOOT=1 -D BOARD_HAS_PSRAM
BOARD_HAS_PSRAM and board_build.arduino.memory_type. Build and upload:Bash
pio run pio run -t upload pio device monitor
Wire.h and WiFi.h are provided by Arduino-ESP32.Cpp
#include <Arduino.h> #include <Wire.h> #include <WiFi.h> static constexpr uint8_t PIN_VBAT_ADC = 1; static constexpr uint8_t PIN_CURRENT_ADC = 2; static constexpr uint8_t PIN_MOTOR[4] = {4, 5, 6, 7}; static constexpr uint8_t PIN_I2C_SDA = 8; static constexpr uint8_t PIN_I2C_SCL = 9; static constexpr uint32_t COMMAND_TIMEOUT_MS = 500; static constexpr float VBAT_R_TOP = 180000.0f; static constexpr float VBAT_R_BOTTOM = 68000.0f; WiFiServer configServer(80); uint32_t lastValidCommandMs = 0; bool armed = false; void forceDisarmed() { armed = false; for (uint8_t pin : PIN_MOTOR) { digitalWrite(pin, LOW); } } void initMotorOutputsSafe() { // Configure outputs and force LOW before starting I2C, radio, or ADC work. for (uint8_t pin : PIN_MOTOR) { pinMode(pin, OUTPUT); digitalWrite(pin, LOW); } forceDisarmed(); } void scanI2C() { Serial.println("I2C scan:"); for (uint8_t address = 1; address < 127; ++address) { Wire.beginTransmission(address); if (Wire.endTransmission() == 0) { Serial.printf(" found 0x%02X\n", address); } } } float readVbatVolts() { // analogReadMilliVolts uses the Arduino-ESP32 calibration path when supported. const float adcV = analogReadMilliVolts(PIN_VBAT_ADC) / 1000.0f; return adcV * (VBAT_R_TOP + VBAT_R_BOTTOM) / VBAT_R_BOTTOM; } uint32_t readCurrentSenseMilliVolts() { // Do not convert this to amperes until the exact ESC scale is calibrated. return analogReadMilliVolts(PIN_CURRENT_ADC); } void startSafeConfigInterface() { // Prototype-only AP. Replace credentials and add authentication before flight use. WiFi.mode(WIFI_AP); WiFi.softAP("FC-BRINGUP", "change-this-password"); configServer.begin(); Serial.print("Config AP IP: "); Serial.println(WiFi.softAPIP()); // BLE placeholder: initialize a BLE service here only after the disarm/failsafe // behavior has been validated. Never let radio initialization arm motors. } void serviceConfigInterface() { WiFiClient client = configServer.available(); if (!client) return; client.setTimeout(50); String line = client.readStringUntil('\n'); line.trim(); // Only a harmless heartbeat/status command is recognized in this starter. if (line == "PING") { lastValidCommandMs = millis(); client.printf("DISARMED=%d VBAT=%.3fV CURRENT_RAW=%lumV\n", armed ? 0 : 1, readVbatVolts(), static_cast<unsigned long>(readCurrentSenseMilliVolts())); } else { client.println("ERR SAFE STARTER: ARM/THROTTLE NOT IMPLEMENTED"); } client.stop(); } void enforceFailsafe() { if ((millis() - lastValidCommandMs) > COMMAND_TIMEOUT_MS) { forceDisarmed(); } } void setup() { initMotorOutputsSafe(); Serial.begin(115200); delay(200); Serial.println("Flight controller safe bring-up firmware"); analogReadResolution(12); analogSetPinAttenuation(PIN_VBAT_ADC, ADC_11db); analogSetPinAttenuation(PIN_CURRENT_ADC, ADC_11db); Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL, 400000); scanI2C(); lastValidCommandMs = millis(); startSafeConfigInterface(); forceDisarmed(); } void loop() { serviceConfigInterface(); enforceFailsafe(); static uint32_t nextReport = 0; if (millis() >= nextReport) { nextReport = millis() + 1000; Serial.printf("DISARMED VBAT=%.3fV CURRENT_RAW=%lumV\n", readVbatVolts(), static_cast<unsigned long>(readCurrentSenseMilliVolts())); } delay(1); }
Hardware Pin Map
PlatformIO Setup
Safe Starter Firmware
Startup Sequence
Sensor Notes
Motor Output Development