10 KiB
Multi-Functional Controller (MFC)
A custom, high-performance handheld remote controller engineered from scratch for dual-role applications: long-range, low-latency RC vehicle control and USB/Bluetooth HID gaming.
Built around the ESP32-S3-N16R8 (Xtensa® 32-bit LX7 dual-core @ 240 MHz), this project bridges hardware and software engineering—combining analog/Hall-effect sensing on ADC1, 2.4 GHz nRF24L01+ RF telemetry, hardware pulse-counted (pulse_cnt) rotary inputs, and a shared high-speed SPI2_HOST (FSPI) bus driving both the 1.3" SH1106 OLED display and radio transceiver without bus collisions.
Prototype Showcase
Figure 1: Top assembly layout — ESP32-S3-N16R8 module, analog joystick, quadrature rotary encoder, 1.3" SH1106 SPI OLED display, and nRF24L01+ RF transceiver.
Figure 2: Ergonomic profile — 2.4 GHz external dipole antenna connected to the nRF24L01+ transceiver, rotary dial, and dual USB-C ports.
Figure 3: Underbody point-to-point bus routing — 6×9 cm perfboard with dedicated decoupled power rails (100 µF smoothing capacitor across nRF24L01+ VCC/GND) and direct FSPI signal lines.
Key Hardware & Firmware Architecture
- Microcontroller Core: Espressif ESP32-S3-N16R8 (16 MB Quad SPI Flash, 8 MB Octal PSRAM) running FreeRTOS.
- Collision-Free Shared FSPI (
SPI2_HOST) Bus:- Shares
MOSI(GPIO 11),MISO(GPIO 13), andSCK(GPIO 12) between the 1.3" SH1106 OLED (10 MHz clock) and the nRF24L01+ 2.4 GHz Transceiver (1 MHz clock) via independent hardware Chip Select lines (GPIO 10andGPIO 14).
- Shares
- Bare-Metal 5×8 OLED Graphics Engine & Multi-Screen UI State Machine:
- Direct register-level driver for the 1.3" SH1106 controller (handling the 132×64 internal RAM 2-pixel column offset) with a complete 5×8 bitmap font table (
A–Z,0–9,<,>,[,],(,),/,-,:,.). - 4 Interactive Screens:
DRIVE VEHICLE: Real-time One-ShotADC1joystick acquisition (VRx/VRy) and live 2.4 GHz packet broadcasting (rc_packet_t).RADIO CONFIG: Verifies physical nRF24L01+ SPI link (SETUP_AWregister0x03) and tunesRF_CH(register0x05) live across channels0–125(2.400–2.525 GHz) using the rotary dial.CALIBRATE: Live rawADC1readout (0–4095) with one-click neutral center-point capture.SYSTEM INFO: Displays target silicon (ESP32-S3) and real-time free RTOS heap memory (esp_get_free_heap_size()).
- Direct register-level driver for the 1.3" SH1106 controller (handling the 132×64 internal RAM 2-pixel column offset) with a complete 5×8 bitmap font table (
- Hardware Quadrature Rotary Decoder (
pulse_cnt):- Uses the ESP32-S3 hardware Pulse Counter peripheral on
GPIO 15(CLK) andGPIO 16(DT) with a1000 nshardware glitch filter and full 4× quadrature edge/level state decoding, paired with a300 msdebounced push-switch (GPIO 17).
- Uses the ESP32-S3 hardware Pulse Counter peripheral on
- Wi-Fi-Safe Analog Joystick Acquisition (
ADC1):- Routes
VRx(GPIO 4,ADC1_CH3) andVRy(GPIO 5,ADC1_CH4) with12 dBattenuation (0–3.3Vfull-scale) onADC_UNIT_1to avoidADC2Wi-Fi/RF arbitration conflicts.
- Routes
- Power Subsystem:
- Dual parallel 18650 Li-ion cells delivering 4800 mAh combined capacity.
- 100 µF low-ESR electrolytic bulk decoupling capacitor suppressing nRF24L01+ TX current spikes.
GPIO Pinout & Peripheral Mapping (ESP32-S3 Hardware Specification)
The physical controller uses a shared high-speed SPI2_HOST (FSPI) bus for both the 1.3" SH1106 OLED and the nRF24L01+ RF transceiver, alongside dedicated ADC1 channels and hardware pulse-counter inputs:
| Peripheral / Signal | GPIO | Hardware Interface | Description |
|---|---|---|---|
| SPI Bus: MOSI | GPIO 11 |
SPI Master Output (SPI2_HOST) |
Shared data line for nRF24L01+ and OLED (SDA / D1) |
| SPI Bus: MISO | GPIO 13 |
SPI Master Input (SPI2_HOST) |
Data line from nRF24L01+ radio module |
| SPI Bus: SCK (Clock) | GPIO 12 |
SPI Master Clock (SPI2_HOST) |
Shared clock line for nRF24L01+ and OLED (CLK / D0) |
| nRF24L01+ Chip Select (CS) | GPIO 14 |
SPI Device Select | Dedicated radio bus enable |
| nRF24L01+ Chip Enable (CE) | GPIO 21 |
Digital Output | Radio TX/RX power state activation line |
| OLED Chip Select (CS) | GPIO 10 |
SPI Device Select | Display SPI bus enable |
| OLED Data/Command (DC) | GPIO 9 |
Digital Output | 1.3" OLED command vs. data selection |
| OLED Reset (RES) | GPIO 8 |
Digital Output | Hardware display reset line |
| Joystick VRx (X-Axis) | GPIO 4 |
ADC1 Channel 3 (12 dB Atten) |
Analog X-axis potentiometer input (0–3.3V safe) |
| Joystick VRy (Y-Axis) | GPIO 5 |
ADC1 Channel 4 (12 dB Atten) |
Analog Y-axis potentiometer input (0–3.3V safe) |
| Rotary Encoder CLK | GPIO 15 |
ESP-IDF pulse_cnt Unit |
Quadrature channel A (hardware edge counter) |
| Rotary Encoder DT | GPIO 16 |
ESP-IDF pulse_cnt Unit |
Quadrature channel B (direction sensing) |
| Rotary Encoder Switch (SW) | GPIO 17 |
Digital Input (PULLUP) |
Push-button (menu enter/select mode) |
Silicon Multiplexing Notes (ESP32-S3-N16R8 vs. Classic ESP32)
- Octal PSRAM & Flash Reservation (
GPIO 26 – GPIO 37): On the ESP32-S3-N16R8 (8 MB Octal PSRAM),GPIO 26throughGPIO 37are wired internally to the Octal SPI Flash/PSRAM bus. AccessingGPIO 26, 27, 32, 33triggers an immediate Guru Meditation Error / Cache Disabled Panic on boot. - Unbonded Silicon Pins (
GPIO 22 – GPIO 25):GPIO 22, 23, 24, 25do not exist on the ESP32-S3 pin multiplexing matrix. - FSPI (
SPI2_HOST) Default Matrix: Uses native ESP32-S3 FSPI pins (GPIO 10, 11, 12, 13) for zero-penalty IOMUX/GPIO matrix routing rather than legacy 2016 ESP32 VSPI defaults (GPIO 5, 18, 23).
Repository Structure
multi-functional-controller/
├── firmware/ # ESP-IDF C99 Firmware Project (ESP32-S3)
│ ├── CMakeLists.txt # Top-level CMake project configuration
│ ├── sdkconfig # ESP32-S3 target configuration
│ └── main/
│ ├── CMakeLists.txt # Component registration
│ └── mmcontroller.c # Master firmware: Shared SPI2, SH1106 5x8 UI, nRF24L01+, PCNT, ADC1
├── hardware/ # KiCad 10 Electronic Design Automation (EDA)
│ ├── mfc-1.kicad_pro # KiCad project file
│ ├── mfc-1.kicad_sch # Complete schematic design & component symbols
│ └── mfc-1.kicad_pcb # Board layout & routing rules
├── images/ # High-resolution prototype photography
│ ├── TOP_VIEW.png # Front control surface overview
│ ├── SIDE_VIEW_WITH_ANTENNA.png # RF antenna and profile perspective
│ └── BOTTOM_VIEW.png # Perfboard underside bus wiring
├── .gitignore # Clean repository filter (builds, caches, local configs)
├── LICENSE # MIT License
└── README.md # Master engineering documentation
Getting Started: Building the Firmware
Prerequisites
- ESP-IDF v5.1+ installed and exported into your environment:
. $HOME/esp/esp-idf/export.sh
Build & Flash
-
Navigate to the firmware directory:
cd firmware -
Set target to ESP32-S3:
idf.py set-target esp32s3 -
Compile the firmware:
idf.py build -
Flash to the controller and open serial monitoring:
idf.py -p /dev/ttyACM0 flash monitor
Hardware CAD & Schematic Editing
The electronic schematic is developed in KiCad 10:
- Open the project:
kicad hardware/mfc-1.kicad_pro - Inspect or modify the schematic in
eeschema(hardware/mfc-1.kicad_sch). - To generate vector schematics for documentation or web publication:
- Select File
\rightarrowPlot\rightarrowSVG or PDF.
- Select File
Roadmap & Milestones
- Initial hardware breadboard and perfboard prototype (6×9 cm assembly).
- Shared
SPI2_HOSTbus driving both 1.3" SH1106 OLED and nRF24L01+ without bus collisions. - Full 5×8 bare-metal font renderer (
A–Z,0–9, and UI punctuation/symbols). - Hardware pulse-counter (
pulse_cnt) quadrature decoder with 1000 ns glitch filter & 300 ms debounced state machine. - Live nRF24L01+ SPI register verification (
0x03) and channel tuning (0–125) withADC1joystick packet transmission. - Custom multi-layer printed circuit board (PCB) design in KiCad.
- 3D-printable ergonomic handheld enclosure (PETG / Resin).
- Bidirectional ACK-payload telemetry from target RC vehicle.
- Native USB Gamepad HID descriptor integration on ESP32-S3 USB-OTG port.
Author & Project Notebook
- Author: Ertunç Özbay
- Personal Workshop & Portfolio: ertuncozbay.dev
- Project Detail Page: ertuncozbay.dev/projects/multi-functional-controller.html
- License: MIT License