Footprint Inspection
Inspect current board geometry, placement, connectivity, routing, and DRC state
Determine and execute required placement cleanup
Determine and execute required PCB routing
Independently verify final placement, routing, constraints, and DRC
platformio.ini:Ini
[env:esp32-s3] platform = espressif32@6.8.1 board = esp32-s3-devkitc-1 framework = arduino monitor_speed = 115200 build_flags = -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1
Table
| Function | GPIO | Electrical behavior |
|---|---|---|
| Encoder push / wake | 4 | Active low, external 10 kΩ pull-up + 100 nF |
| Encoder A | 5 | Active low contact, external 10 kΩ pull-up |
| Encoder B | 6 | Active low contact, external 10 kΩ pull-up |
| RGB red | 7 | Active-high PWM, 510 Ω series resistor |
| RGB green | 8 | Active-high PWM, 220 Ω series resistor |
| Charger CHG_N | 9 | Active-low status input |
| Charger PGOOD_N | 10 | Active-low status input |
| RGB blue | 15 | Active-high PWM, 220 Ω series resistor |
| USB D− / D+ | 19 / 20 | Native USB peripheral |
| BOOT | 0 | Internal button/strap only |
| RESET | EN | Internal button to GND |
src/main.cpp)Cpp
#include <Arduino.h> #include "USB.h" #include "USBHIDKeyboard.h" #include "esp_sleep.h" USBHIDKeyboard Keyboard; constexpr uint8_t PIN_ENC_SW = 4; constexpr uint8_t PIN_ENC_A = 5; constexpr uint8_t PIN_ENC_B = 6; constexpr uint8_t PIN_RGB_R = 7; constexpr uint8_t PIN_RGB_G = 8; constexpr uint8_t PIN_CHG_N = 9; constexpr uint8_t PIN_PGOOD_N = 10; constexpr uint8_t PIN_RGB_B = 15; constexpr uint8_t PWM_CH_R = 0; constexpr uint8_t PWM_CH_G = 1; constexpr uint8_t PWM_CH_B = 2; constexpr uint32_t PWM_FREQ_HZ = 5000; constexpr uint8_t PWM_BITS = 8; volatile int32_t encoderCount = 0; uint8_t encoderState = 0; bool lastButton = HIGH; uint32_t buttonChangedMs = 0; // Valid quadrature transition lookup: previous AB in bits 3:2, new AB in 1:0. const int8_t QUAD_TABLE[16] = { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 }; void setRgb(uint8_t r, uint8_t g, uint8_t b) { // Common-cathode LED: active-high PWM. ledcWrite(PWM_CH_R, r); ledcWrite(PWM_CH_G, g); ledcWrite(PWM_CH_B, b); } void pollEncoder() { const uint8_t ab = (digitalRead(PIN_ENC_A) << 1) | digitalRead(PIN_ENC_B); const uint8_t transition = ((encoderState & 0x03) << 2) | ab; encoderCount += QUAD_TABLE[transition]; encoderState = ab; } void enterDeepSleep() { setRgb(0, 0, 0); delay(10); gpio_hold_dis((gpio_num_t)PIN_RGB_R); gpio_hold_dis((gpio_num_t)PIN_RGB_G); gpio_hold_dis((gpio_num_t)PIN_RGB_B); esp_sleep_enable_ext0_wakeup((gpio_num_t)PIN_ENC_SW, 0); // Wake on active-low press. Serial.println("Entering deep sleep; press encoder to wake."); Serial.flush(); delay(50); esp_deep_sleep_start(); } void setup() { pinMode(PIN_ENC_A, INPUT_PULLUP); pinMode(PIN_ENC_B, INPUT_PULLUP); pinMode(PIN_ENC_SW, INPUT_PULLUP); pinMode(PIN_CHG_N, INPUT_PULLUP); pinMode(PIN_PGOOD_N, INPUT_PULLUP); ledcSetup(PWM_CH_R, PWM_FREQ_HZ, PWM_BITS); ledcSetup(PWM_CH_G, PWM_FREQ_HZ, PWM_BITS); ledcSetup(PWM_CH_B, PWM_FREQ_HZ, PWM_BITS); ledcAttachPin(PIN_RGB_R, PWM_CH_R); ledcAttachPin(PIN_RGB_G, PWM_CH_G); ledcAttachPin(PIN_RGB_B, PWM_CH_B); setRgb(0, 0, 0); encoderState = (digitalRead(PIN_ENC_A) << 1) | digitalRead(PIN_ENC_B); lastButton = digitalRead(PIN_ENC_SW); Serial.begin(115200); // Native USB CDC with build flags above. Keyboard.begin(); // Adds USB HID keyboard interface. USB.begin(); delay(1000); Serial.println("ESP32 Rotary UI Controller ready"); Serial.println("Commands: r/g/b/off, h=HID test, sleep"); } void loop() { static int32_t lastReportedCount = 0; static uint32_t lastStatusMs = 0; pollEncoder(); if (encoderCount != lastReportedCount) { lastReportedCount = encoderCount; Serial.printf("Encoder count: %ld\n", (long)encoderCount); } const bool button = digitalRead(PIN_ENC_SW); if (button != lastButton && millis() - buttonChangedMs > 20) { buttonChangedMs = millis(); lastButton = button; if (button == LOW) { Serial.println("Encoder push"); } } if (millis() - lastStatusMs >= 1000) { lastStatusMs = millis(); Serial.printf("CHG=%s PGOOD=%s\n", digitalRead(PIN_CHG_N) == LOW ? "charging" : "inactive/done", digitalRead(PIN_PGOOD_N) == LOW ? "valid-input" : "no-input"); } if (Serial.available()) { String command = Serial.readStringUntil('\n'); command.trim(); if (command == "r") setRgb(64, 0, 0); else if (command == "g") setRgb(0, 64, 0); else if (command == "b") setRgb(0, 0, 64); else if (command == "off") setRgb(0, 0, 0); else if (command == "h") Keyboard.print("ESP32 Rotary UI Controller"); else if (command == "sleep") enterDeepSleep(); } delay(1); }
Bash
pio run pio run -t upload pio device monitor
CHG_N is low while charging. PGOOD_N is low when a valid USB input is detected.PlatformIO Configuration
Final Pin Map
Compilable Starter (`src/main.cpp`)
Build and Flash
Notes