Flight Controller Design
Table
| Function | ESP32-C3 GPIO | Net | Connected To |
|---|---|---|---|
| I2C SDA | 1 | I2C_SDA | LSM6DSO32 SDA, R8 pull-up |
| I2C SCL | 0 | I2C_SCL | LSM6DSO32 SCL, R9 pull-up |
| IMU INT1 | 3 | IMU_INT1 | LSM6DSO32 INT1 |
| SPI SCK | 4 | SPI_SCK | W25Q16 CLK |
| SPI MISO | 5 | SPI_MISO | W25Q16 DO |
| SPI MOSI | 6 | SPI_MOSI | W25Q16 DI |
| Flash CS | 7 | FLASH_CS | W25Q16 CS, R10 pull-up |
| Status LED | 10 | STATUS_LED | R11 / D1 |
| Boot button | 9 | ESP_BOOT_GPIO9 | SW2 to GND, R4 pull-up |
| EN reset | EN | ESP_EN | SW1 to GND, R3/C9 RC |
| USB D- | 18 | USB_DN | USB-C DN via ESD |
| USB D+ | 19 | USB_DP | USB-C DP via ESD |
Ini
[env:esp32-c3-devkitm-1] platform = espressif32 board = esp32-c3-devkitm-1 framework = arduino monitor_speed = 115200 lib_deps =
Cpp
#include <Arduino.h> #include <Wire.h> #include <SPI.h> #include <WiFi.h> #include <HTTPClient.h> #define I2C_SDA_PIN 1 #define I2C_SCL_PIN 0 #define IMU_INT1_PIN 3 #define FLASH_SCK_PIN 4 #define FLASH_MISO_PIN 5 #define FLASH_MOSI_PIN 6 #define FLASH_CS_PIN 7 #define STATUS_LED_PIN 10 static const uint8_t LSM6DSO32_ADDR = 0x6A; // SA0 tied low static const uint8_t REG_WHO_AM_I = 0x0F; static const uint8_t REG_CTRL1_XL = 0x10; static const uint8_t REG_CTRL2_G = 0x11; static const uint8_t REG_OUTX_L_G = 0x22; const char* WIFI_SSID = "YOUR_WIFI_SSID"; const char* WIFI_PASS = "YOUR_WIFI_PASSWORD"; const char* TELEMETRY_URL = "http://example.com/telemetry"; unsigned long lastSampleMs = 0; const uint32_t SAMPLE_INTERVAL_MS = 20; // 50 Hz starter rate bool i2cWrite8(uint8_t addr, uint8_t reg, uint8_t value) { Wire.beginTransmission(addr); Wire.write(reg); Wire.write(value); return Wire.endTransmission() == 0; } bool i2cRead(uint8_t addr, uint8_t reg, uint8_t* data, size_t len) { Wire.beginTransmission(addr); Wire.write(reg); if (Wire.endTransmission(false) != 0) return false; size_t got = Wire.requestFrom((int)addr, (int)len); if (got != len) return false; for (size_t i = 0; i < len; i++) data[i] = Wire.read(); return true; } bool initImu() { uint8_t who = 0; if (!i2cRead(LSM6DSO32_ADDR, REG_WHO_AM_I, &who, 1)) { Serial.println("IMU WHO_AM_I read failed"); return false; } Serial.printf("LSM6DSO32 WHO_AM_I: 0x%02X\n", who); // Starter config: enable accel + gyro. Refine ODR/full-scale bits against final firmware driver. bool ok = true; ok &= i2cWrite8(LSM6DSO32_ADDR, REG_CTRL1_XL, 0xA4); // 6.66 kHz class / high range starter setting placeholder ok &= i2cWrite8(LSM6DSO32_ADDR, REG_CTRL2_G, 0xA0); // gyro enabled starter setting if (!ok) Serial.println("IMU config write failed"); return ok; } bool readImuRaw(int16_t& gx, int16_t& gy, int16_t& gz, int16_t& ax, int16_t& ay, int16_t& az) { uint8_t b[12]; if (!i2cRead(LSM6DSO32_ADDR, REG_OUTX_L_G, b, sizeof(b))) return false; gx = (int16_t)(b[1] << 8 | b[0]); gy = (int16_t)(b[3] << 8 | b[2]); gz = (int16_t)(b[5] << 8 | b[4]); ax = (int16_t)(b[7] << 8 | b[6]); ay = (int16_t)(b[9] << 8 | b[8]); az = (int16_t)(b[11] << 8 | b[10]); return true; } uint32_t readFlashJedecId() { SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); digitalWrite(FLASH_CS_PIN, LOW); SPI.transfer(0x9F); uint32_t id = ((uint32_t)SPI.transfer(0) << 16) | ((uint32_t)SPI.transfer(0) << 8) | ((uint32_t)SPI.transfer(0)); digitalWrite(FLASH_CS_PIN, HIGH); SPI.endTransaction(); return id; } void connectWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASS); Serial.print("WiFi connecting"); for (int i = 0; i < 20 && WiFi.status() != WL_CONNECTED; i++) { digitalWrite(STATUS_LED_PIN, !digitalRead(STATUS_LED_PIN)); delay(250); Serial.print("."); } Serial.println(); if (WiFi.status() == WL_CONNECTED) { Serial.print("IP: "); Serial.println(WiFi.localIP()); digitalWrite(STATUS_LED_PIN, HIGH); } else { Serial.println("WiFi not connected; continuing local telemetry over USB serial"); digitalWrite(STATUS_LED_PIN, LOW); } } void postTelemetry(int16_t gx, int16_t gy, int16_t gz, int16_t ax, int16_t ay, int16_t az) { if (WiFi.status() != WL_CONNECTED) return; HTTPClient http; http.begin(TELEMETRY_URL); http.addHeader("Content-Type", "application/json"); String body = "{\"gx\":" + String(gx) + ",\"gy\":" + String(gy) + ",\"gz\":" + String(gz) + ",\"ax\":" + String(ax) + ",\"ay\":" + String(ay) + ",\"az\":" + String(az) + "}"; int code = http.POST(body); Serial.printf("HTTP POST code: %d\n", code); http.end(); } void setup() { pinMode(STATUS_LED_PIN, OUTPUT); pinMode(IMU_INT1_PIN, INPUT); pinMode(FLASH_CS_PIN, OUTPUT); digitalWrite(FLASH_CS_PIN, HIGH); Serial.begin(115200); delay(1000); Serial.println("ESP32-C3 pickleball tracker starting"); Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN, 400000); SPI.begin(FLASH_SCK_PIN, FLASH_MISO_PIN, FLASH_MOSI_PIN, FLASH_CS_PIN); bool imuOk = initImu(); Serial.printf("IMU init: %s\n", imuOk ? "OK" : "FAILED"); uint32_t jedec = readFlashJedecId(); Serial.printf("Flash JEDEC ID: 0x%06lX\n", (unsigned long)jedec); connectWiFi(); } void loop() { unsigned long now = millis(); if (now - lastSampleMs >= SAMPLE_INTERVAL_MS) { lastSampleMs = now; int16_t gx, gy, gz, ax, ay, az; if (readImuRaw(gx, gy, gz, ax, ay, az)) { Serial.printf("g:%d,%d,%d a:%d,%d,%d\n", gx, gy, gz, ax, ay, az); static uint8_t postDivider = 0; if (++postDivider >= 50) { postDivider = 0; postTelemetry(gx, gy, gz, ax, ay, az); } } else { Serial.println("IMU read failed"); digitalWrite(STATUS_LED_PIN, LOW); } } }
Bash
pio run pio run -t upload pio device monitor
Platform & Toolchain
Pin Mapping
platformio.ini
Complete Firmware Source
Build & Flash
Firmware Notes