A sophisticated ball-balancing platform built on a 3-DOF Stewart mechanism, powered by an ESP32-S3 microcontroller with custom KiCad PCBs, 3D-printed enclosure, and a complete firmware stack with live web-based control interface.
License: Creative Commons Attribution-NonCommercial 4.0 International
- Arduino IDE with ESP32 board support
- ESP32-S3 development board (or compatible)
- Physical BaBot hardware assembled
- Install the ESP32 board package in Arduino IDE
- Open
firmware/firmware.inoin Arduino IDE - Select ESP32S3 Dev Module (or your specific ESP32-S3 variant)
- Click Compile (Sketch → Verify/Compile)
- Power on the robot
- Wait for the ESP32-S3 to start its Wi-Fi access point
- On your phone or computer, connect to the Wi-Fi network:
12345678 - Password:
12345678 - Open your browser and navigate to:
http://192.168.4.1/ - The live control dashboard loads automatically with real-time telemetry
✨ 3-DOF Stewart Platform – Precision ball-balancing mechanism with three independent servo actuators
🎮 Live Web Control UI – Real-time dashboard with tilt visualization, ball position tracking, and live telemetry over WebSocket
📊 Multi-Sensor Array – Dual multiplexed IR sensor arrays for accurate ball detection and positioning
⚙️ Velocity-Scaled PID Control – Adaptive control loop running at 30 Hz with output smoothing
🔧 Hardware Diagnostics – Automatic subsystem detection and degraded-mode operation if sensors/actuators fail
🎨 RGB Status Indicator – Real-time feedback: OFF (dark) → ON idle (amber blink) → balanced (green) → special modes (blue/magenta)
💾 Persistent Calibration – Servo offsets and system parameters stored in NVS (non-volatile storage)
| Module | Purpose |
|---|---|
firmware.ino |
State machine: ON / OFF / ASSEMBLY / CALIBRATION modes; button handling; main loop |
BoardConfig.h |
Pin definitions, mux layout, and firmware tuning constants |
RuntimeState |
Shared state, mode transitions, safety interlocks |
PlatformControl |
Stewart kinematics; servo position calculation and commands |
IRSensorArray |
Dual-mux IR sensor reading; ball position calculation; threshold logic |
X9C104Digipot |
Digital potentiometer control for IR LED brightness tuning |
WebUiServer |
HTTP server; WebSocket telemetry; embedded UI assets |
DebugLogger |
Async serial logging queue; flushed from Wi-Fi task to avoid blocking control loop |
ESP32-S3 Control Pins (defined in BoardConfig.h):
- Servos A/B/C: GPIO 41, 2, 1
- IR LED Control: GPIO 6
- RGB Status LED: GPIO 38
- Boot Button: GPIO 0 (active-low, double-press to enter modes)
- Digipot: CS/INC/U-D on GPIO 14/12/13
- Mux Bank 1: GPIO 39/40/21/47
- Mux Bank 2: GPIO 48/42/4/5
- ADC Inputs: GPIO 9, 10 (multiplexed sensor outputs)
Reserved: GPIO 19 & 20 (DevKitC USB D-/D+)
Default state. Platform leveled, no active balancing.
- No ball detected: RGB blinks amber, platform idle
- Ball detected: RGB solid green, velocity-scaled PID active, platform tilts to keep ball centered
- Ball lost >1s: Platform levels, PID resets
- Custom timeout: Configurable via web UI
Diagnostic mode for manual servo testing.
- Press boot button to step through individual servo movements
- Useful for mechanical validation and range-of-motion checks
Interactive offset calibration.
- Follow on-screen prompts to set neutral servo positions
- Offsets persisted to NVS automatically
1. File → Open → Navigate to: firmware/firmware.ino
2. Tools → Board → esp32 → ESP32S3 Dev Module
3. Tools → Port → Select your serial port
4. Sketch → Upload
# Build
arduino-cli compile --fqbn esp32:esp32:esp32s3 ./firmware
# With project include paths
arduino-cli compile --fqbn esp32:esp32:esp32s3 \
--build-property "build.extra_flags=-I./firmware/src -I./firmware/src/third_party/Adafruit_NeoPixel -I./firmware/src/third_party/CD74HC4067/src -I./firmware/src/third_party/ESP32Servo/src -I./firmware/src/third_party/WebSockets/src" \
./firmware
# Upload
arduino-cli upload --fqbn esp32:esp32:esp32s3 --port /dev/ttyUSB0.\scripts\compile.ps1Once connected to the Wi-Fi AP:
- Control Panel – Tilt visualization, ball position overlay, mode selector
- Telemetry – Real-time sensor readings, servo positions, PID state
- Tuning – Adjust digipot tap, ball detection threshold, PID gains
- Subsystem Status – Live indicator of actuators, sensors, and wireless connectivity
All changes sync bidirectionally over WebSocket (port 81).
- Compile firmware without errors
- Flash to ESP32-S3 via USB
- Power on and wait for Wi-Fi AP to start
- Connect to
12345678network - Open
http://192.168.4.1/and verify UI loads - Confirm all subsystems show green in diagnostics
- Test ASSEMBLY mode: verify each servo moves independently
- Test CALIBRATION mode: set neutral positions
- Test ON mode with ball: platform should track ball movement
- Adjust digipot tap and threshold if ball detection is erratic
- Verify RGB LED status colors match expected states
// Ball detection threshold (12-bit ADC, 0–4095)
const float BALL_THRESHOLD = 800.0;
// Digipot default tap (0–255, controls IR LED brightness)
const int DIGIPOT_DEFAULT_TAP = 90;
// Control loop frequency
const int CONTROL_LOOP_HZ = 30;
// PID gains (velocity-scaled)
const float KP = 0.5, KI = 0.1, KD = 0.05;- SSID:
12345678 - Password:
12345678 - IP:
192.168.4.1 - Broadcast Port: 81 (WebSocket telemetry)
All vendored libraries are included in firmware/src/third_party/:
- ArduinoJson – JSON serialization for web communication
- Adafruit_NeoPixel – RGB LED control
- ESP32Servo – Hardware servo PWM output
- WebSockets – Bidirectional WebSocket server
- CD74HC4067 – 16-channel multiplexer control
For details and license information, see docs/third-party-sources.md.
| Document | Content |
|---|---|
docs/README.md |
Detailed setup, operation, troubleshooting, and Wi-Fi access |
docs/code-reference.md |
Firmware architecture, control flow diagrams, module responsibilities |
docs/hardware-guide.md |
Hardware asset organization and CAD file locations |
docs/third-party-sources.md |
Library versions, upstream sources, and licenses |
docs/reference/how-it-works.pdf |
System theory and control algorithms |
docs/reference/manual.pdf |
Assembly, calibration, and usage guide |
The platform uses two custom KiCad PCBs:
- Microcontroller: ESP32-S3-DevKitC-N4R8
- Motor Driver: Servo control circuitry for 3 independent actuators
- Sensor Interface: Multiplexed IR sensor inputs with ADC conditioning
- Power Management: USB power with optional battery connector
- 16-Channel Mux: CD74HC4067 multiplexer
- IR LEDs: Sensor illumination with software-controlled brightness (X9C104 digipot)
- Dual Banks: Two mux units for expanded sensor count
Fabrication Files: See hardware/fabrication/ for Gerber, drill, and assembly documentation.
Enclosure: 3D-printable parts in hardware/enclosure/printed-parts/ (STL format).
- The boot button (GPIO 0) is remapped to the app button. Standard boot behavior is preserved for recovery.
- GPIO 19 & 20 are intentionally left free to preserve USB connectivity.
- The robot starts in OFF mode on power-up. Press the boot button to switch modes.
- Always ensure the platform is on a stable, non-slip surface before testing.
- Do not cover the IR sensor array during operation.
This project is licensed under the Creative Commons Attribution-NonCommercial 4.0 International license.
- ✅ You may use, modify, and share this project for non-commercial purposes
- ✅ You must provide attribution
- ❌ Commercial use is not permitted without explicit permission
https://www.ba-bot.com https://www.ba-bot.com/manual/
This project is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License.
For inquiries or support, please contact: info@ba-bot.com
The open-source ball-balancing robot you build, program, and bring to life. Happy balancing! 🤖⚽