USB-C Sensor Node
Firmware Starter — ESP32-S3 Sensor Motor Carrier
Board Bring-Up Plan — ESP32-S3 Sensor Motor Carrier
Technical Construction File Notes — ESP32-S3 Sensor Motor Carrier
Preliminary FMEA — ESP32-S3 Sensor Motor Carrier
Table
| Function | ESP32-S3 GPIO | Net | Connected To | Direction | Notes |
|---|---|---|---|---|---|
| USB D- | GPIO19 | USB_DN | J1/D1 | bidirectional | Native USB |
| USB D+ | GPIO20 | USB_DP | J1/D1 | bidirectional | Native USB |
| I2C SDA | GPIO8 | I2C_SDA | U2/U3 | bidirectional | 4.7k pull-up |
| I2C SCL | GPIO9 | I2C_SCL | U2/U3 | output | 4.7k pull-up |
| BNO085 INT | GPIO10 | BNO_INT | U2 H_INTN | input | Active low |
| BNO085 RESET | GPIO11 | BNO_RST | U2 NRST | output | Active low reset |
| GPS TX from ESP | GPIO17 | GPS_RX_FROM_ESP_TX | J2 pin 5 | output | To GPS RX |
| GPS RX to ESP | GPIO18 | GPS_TX_TO_ESP_RX | J2 pin 4 | input | From GPS TX |
| GPS PPS | GPIO4 | GPS_PPS | J2 pin 6 | input | Interrupt-capable |
| GPS enable/reset | GPIO5 | GPS_EN | J2 pin 7 | output | Depends on GPS module |
| Motor PWM | GPIO6 | MOTOR_PWM | J3 pin 7 | output | 100k pull-down |
| Motor DIR | GPIO7 | MOTOR_DIR | J3 pin 8 | output | Direction control |
| Motor enable/sleep | GPIO15 | MOTOR_EN_SLEEP | J3 pin 9 | output | 100k pull-down; default off |
| Motor fault | GPIO16 | MOTOR_FAULT | J3 pin 10 | input | Use pull-up if driver needs it |
| Motor current sense | GPIO1 | MOTOR_CSENSE_ADC | J3 pin 11 | analog input | Must be 0–3.3 V max |
| Motor aux / IN2 | GPIO2 | MOTOR_IN2_AUX | J3 pin 12 | output | Optional second input |
| Motor aux 1 | GPIO38 | MOTOR_AUX1 | J3 pin 13 | GPIO | Spare |
| Motor aux 2 | GPIO39 | MOTOR_AUX2 | J3 pin 14 | GPIO | Spare |
| Motor aux 3 | GPIO40 | MOTOR_AUX3 | J3 pin 15 | GPIO | Spare |
| Status LED | GPIO21 | STATUS_LED | R11/D2 | output | Active high |
| BOOT | GPIO0 | ESP_BOOT | SW2/R6 | input | Pull low to enter bootloader |
| Reset/enable | EN | ESP_EN | SW1/R5/C5 | input | Active low reset |
Table
| Pin | Net | Function |
|---|---|---|
| 1 | GND | Ground |
| 2 | 3V3 | GPS 3.3 V power |
| 3 | 5V_USB | Optional 5 V for GPS breakout boards |
| 4 | GPS_TX_TO_ESP_RX | GPS TX into ESP32 GPIO18 |
| 5 | GPS_RX_FROM_ESP_TX | ESP32 GPIO17 TX into GPS RX |
| 6 | GPS_PPS | GPS pulse-per-second |
| 7 | GPS_EN | GPS enable/reset control |
| 8 | GPS_SPARE | intentionally unused spare |
Table
| Pin | Net | Function |
|---|---|---|
| 1,2,20 | VMOTOR | External motor supply from J4 |
| 3,4,16,17,18,19 | GND | Motor/logic ground return |
| 5 | 3V3 | Logic power |
| 6 | 5V_USB | Optional 5 V accessory power; not for motor current |
| 7 | MOTOR_PWM | PWM command, pulled down |
| 8 | MOTOR_DIR | Direction command |
| 9 | MOTOR_EN_SLEEP | Enable/sleep command, pulled down |
| 10 | MOTOR_FAULT | Fault input |
| 11 | MOTOR_CSENSE_ADC | Current sense ADC input |
| 12 | MOTOR_IN2_AUX | Optional second driver input |
| 13–15 | MOTOR_AUX1/2/3 | Spare GPIOs |
platformio.iniIni
[env:esp32-s3-devkitc-1] platform = espressif32 board = esp32-s3-devkitc-1 framework = arduino monitor_speed = 115200 upload_speed = 921600 build_flags = -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 lib_deps = adafruit/Adafruit BNO08x@^1.2.5 adafruit/Adafruit SHT4x Library@^1.0.5 mikalhart/TinyGPSPlus@^1.1.0
src/main.cppCpp
#include <Arduino.h> #include <Wire.h> #include <WiFi.h> #include <Adafruit_BNO08x.h> #include <Adafruit_SHT4x.h> #include <TinyGPSPlus.h> // Pin definitions from schematic #define PIN_I2C_SDA 8 #define PIN_I2C_SCL 9 #define PIN_BNO_INT 10 #define PIN_BNO_RST 11 #define PIN_GPS_TX 17 // ESP32 TX -> GPS RX, J2 pin 5 #define PIN_GPS_RX 18 // ESP32 RX <- GPS TX, J2 pin 4 #define PIN_GPS_PPS 4 #define PIN_GPS_EN 5 #define PIN_MOTOR_PWM 6 #define PIN_MOTOR_DIR 7 #define PIN_MOTOR_EN_SLEEP 15 #define PIN_MOTOR_FAULT 16 #define PIN_MOTOR_CSENSE_ADC 1 #define PIN_MOTOR_IN2_AUX 2 #define PIN_MOTOR_AUX1 38 #define PIN_MOTOR_AUX2 39 #define PIN_MOTOR_AUX3 40 #define PIN_STATUS_LED 21 #define GPS_BAUD 9600 #define SENSOR_INTERVAL_MS 1000 #define WIFI_RETRY_LIMIT 20 const char* WIFI_SSID = "YOUR_SSID"; const char* WIFI_PASSWORD = "YOUR_PASSWORD"; Adafruit_BNO08x bno08x(PIN_BNO_RST); Adafruit_SHT4x sht4; TinyGPSPlus gps; HardwareSerial GPSSerial(1); unsigned long lastSensorRead = 0; volatile uint32_t gpsPpsCount = 0; void IRAM_ATTR onGpsPps() { gpsPpsCount++; } void setMotorSafeOff() { ledcWrite(0, 0); digitalWrite(PIN_MOTOR_PWM, LOW); digitalWrite(PIN_MOTOR_EN_SLEEP, LOW); digitalWrite(PIN_MOTOR_DIR, LOW); digitalWrite(PIN_MOTOR_IN2_AUX, LOW); } void setMotorCommand(uint8_t dutyPercent, bool dir) { dutyPercent = min<uint8_t>(dutyPercent, 100); digitalWrite(PIN_MOTOR_DIR, dir ? HIGH : LOW); digitalWrite(PIN_MOTOR_EN_SLEEP, dutyPercent > 0 ? HIGH : LOW); ledcWrite(0, map(dutyPercent, 0, 100, 0, 255)); } void initMotorPins() { pinMode(PIN_MOTOR_PWM, OUTPUT); pinMode(PIN_MOTOR_DIR, OUTPUT); pinMode(PIN_MOTOR_EN_SLEEP, OUTPUT); pinMode(PIN_MOTOR_IN2_AUX, OUTPUT); pinMode(PIN_MOTOR_FAULT, INPUT_PULLUP); pinMode(PIN_MOTOR_AUX1, INPUT); pinMode(PIN_MOTOR_AUX2, INPUT); pinMode(PIN_MOTOR_AUX3, INPUT); analogReadResolution(12); ledcSetup(0, 20000, 8); ledcAttachPin(PIN_MOTOR_PWM, 0); setMotorSafeOff(); } void initWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting to WiFi"); for (int i = 0; i < WIFI_RETRY_LIMIT && WiFi.status() != WL_CONNECTED; i++) { digitalWrite(PIN_STATUS_LED, !digitalRead(PIN_STATUS_LED)); delay(500); Serial.print('.'); } if (WiFi.status() == WL_CONNECTED) { Serial.printf("\nWiFi connected: %s\n", WiFi.localIP().toString().c_str()); digitalWrite(PIN_STATUS_LED, HIGH); } else { Serial.println("\nWiFi not connected; continuing offline."); digitalWrite(PIN_STATUS_LED, LOW); } } void initSensors() { Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL, 400000); if (!sht4.begin(&Wire)) { Serial.println("SHT40 not detected at 0x44."); } else { sht4.setPrecision(SHT4X_HIGH_PRECISION); sht4.setHeater(SHT4X_NO_HEATER); Serial.println("SHT40 initialized."); } if (!bno08x.begin_I2C(0x4A, &Wire, PIN_BNO_INT)) { Serial.println("BNO085 not detected. Check I2C address/SA0 strap."); } else { bno08x.enableReport(SH2_ROTATION_VECTOR, 100000); // 100 ms bno08x.enableReport(SH2_ACCELEROMETER, 100000); Serial.println("BNO085 initialized."); } } void initGps() { pinMode(PIN_GPS_EN, OUTPUT); digitalWrite(PIN_GPS_EN, HIGH); pinMode(PIN_GPS_PPS, INPUT); attachInterrupt(digitalPinToInterrupt(PIN_GPS_PPS), onGpsPps, RISING); GPSSerial.begin(GPS_BAUD, SERIAL_8N1, PIN_GPS_RX, PIN_GPS_TX); Serial.println("GPS UART initialized."); } void pollGps() { while (GPSSerial.available()) { gps.encode(GPSSerial.read()); } } void readSensors() { sensors_event_t humidity, temp; if (sht4.getEvent(&humidity, &temp)) { Serial.printf("SHT40: %.2f C, %.2f %%RH\n", temp.temperature, humidity.relative_humidity); } else { Serial.println("SHT40 read failed."); } sh2_SensorValue_t sensorValue; while (bno08x.getSensorEvent(&sensorValue)) { if (sensorValue.sensorId == SH2_ROTATION_VECTOR) { Serial.printf("BNO rot: i=%.4f j=%.4f k=%.4f real=%.4f\n", sensorValue.un.rotationVector.i, sensorValue.un.rotationVector.j, sensorValue.un.rotationVector.k, sensorValue.un.rotationVector.real); } } int rawCurrent = analogRead(PIN_MOTOR_CSENSE_ADC); bool motorFault = digitalRead(PIN_MOTOR_FAULT) == LOW; Serial.printf("Motor: csense_raw=%d fault=%s\n", rawCurrent, motorFault ? "YES" : "no"); if (gps.location.isValid()) { Serial.printf("GPS: %.6f, %.6f sats=%u pps=%lu\n", gps.location.lat(), gps.location.lng(), gps.satellites.value(), gpsPpsCount); } else { Serial.printf("GPS: no fix yet, chars=%lu pps=%lu\n", gps.charsProcessed(), gpsPpsCount); } } void setup() { Serial.begin(115200); delay(200); Serial.println("ESP32-S3 Sensor Motor Carrier starting..."); pinMode(PIN_STATUS_LED, OUTPUT); digitalWrite(PIN_STATUS_LED, LOW); initMotorPins(); initGps(); initSensors(); initWiFi(); Serial.println("Initialization complete. Motor remains disabled until firmware commands it on."); } void loop() { pollGps(); unsigned long now = millis(); if (now - lastSensorRead >= SENSOR_INTERVAL_MS) { lastSensorRead = now; readSensors(); } if (WiFi.status() != WL_CONNECTED) { digitalWrite(PIN_STATUS_LED, (millis() / 500) % 2); } }
platformio.ini above and src/main.cpp source.pio runpio run -t uploadpio device monitorPlatform & Toolchain
Pin Mapping
GPS Header J2 Pinout
Motor Carrier Header J3 Pinout
`platformio.ini`
Complete Firmware Source — `src/main.cpp`
Build & Flash
Open Firmware Assumptions