Firmware for the rover-side ESP32-S3 board. Runs a dual-port TCP server, reads an LSM6DSOX IMU, and drives DC motors + a steering servo from joystick commands received over WiFi.
Board: Adafruit Feather ESP32-S3 TFT
Framework: Arduino (PlatformIO, espressif32)
Full PlantUML source:
docs/architecture.puml
The server board runs two FreeRTOS tasks on separate cores and exposes two TCP ports on its own WiFi access point:
| Port | Role |
|---|---|
| 50051 | Command channel — binary MessagePack request/response |
| 50052 | Stream channel — server-push IMU frames at 20 Hz |
Full PlantUML source:
docs/sequence.puml
Full PlantUML source:
docs/server-control-flow.puml
See docs/ for all diagram sources and rendered PNGs.
- Dual-port TCP server — command (
:50051) and IMU stream (:50052) on separate sockets; fully non-blocking. - Binary MessagePack protocol — compact 3-byte framing header + MsgPack payload (no text JSON).
- LSM6DSOX IMU — 6-axis accelerometer + gyroscope, sampled at 50 Hz on Core 0.
- FreeRTOS dual-core — SensorDataTask (Core 0) + WebServerTask (Core 1) share IMU data through a mutex-protected struct.
- Differential drive — left/right H-bridge motors (GPIO 5/6, 9/10) driven by LEDC PWM from joystick axes.
- Ackermann steering servo — GPIO 11, 50 Hz PWM, maps right-X joystick axis to 45°–135° angle.
- NeoPixel heartbeat — brief green flash every ~400 ms while the IMU is sampling.
- WiFi Access Point — SSID
MOONBASE-II, single-client model.
- Adafruit Feather ESP32-S3 TFT — 4 MB Flash, 2 MB PSRAM, 240×135 TFT
| Peripheral | Interface | Pins |
|---|---|---|
| LSM6DSOX IMU | I²C | SDA=GPIO42, SCL=GPIO41 |
| NeoPixel LED | GPIO | Data=GPIO33, Power=GPIO34 |
| Left H-Bridge (IN1/IN2) | LEDC PWM | GPIO5, GPIO6 |
| Right H-Bridge (IN3/IN4) | LEDC PWM | GPIO9, GPIO10 |
| Steering Servo | ESP32Servo (50 Hz) | GPIO11 |
LEDC channels 4–7 are reserved for motor PWM (leaves 0–3 free for ESP32Servo).
Command Request [method_id (1 B)] [payload_len BE (2 B)] [MsgPack payload (N B)]
Command Response [status (1 B)] [payload_len BE (2 B)] [MsgPack payload (N B)]
Stream Push [payload_len BE (2 B)] [MsgPack payload (N B)]
| ID | Name | Direction |
|---|---|---|
0x01 |
CMD_TURN_LED_ON |
client → server |
0x02 |
CMD_TURN_LED_OFF |
client → server |
0x03 |
CMD_GET_ALL_IMU |
client → server |
0x04 |
CMD_GET_SPECIFIC_IMU |
client → server |
0x05 |
CMD_SEND_JOYSTICK |
client → server |
| (stream) | IMU push | server → client |
{ "left_x": 0-4095, "left_y": 0-4095,
"right_x": 0-4095, "right_y": 0-4095,
"left_button": bool, "right_button": bool,
"timestamp": millis() }{ "acc_x": float, "acc_y": float, "acc_z": float,
"gyro_x": float, "gyro_y": float, "gyro_z": float,
"temperature": float, "timestamp": uint32 }# Build
pio run -e esp32s3_feather_tft
# Flash + monitor
pio run -e esp32s3_feather_tft -t upload -t monitor| Library | Purpose |
|---|---|
Adafruit LSM6DS |
IMU driver |
Adafruit NeoPixel |
LED |
ESP32Servo |
Steering servo PWM |
ArduinoJson |
MessagePack serialization |
rover-esp32-control-server/
├── docs/
│ ├── architecture.puml / .png ← System architecture
│ ├── sequence.puml / .png ← Communication sequence
│ └── server-control-flow.puml / .png ← FreeRTOS task flow
├── include/
│ ├── RoverServer.h ← Task handles, mutex, shared IMU data
│ ├── RoverProtocol.h ← Binary frame helpers & method IDs
│ ├── SensorData.h ← imu_data_t struct
│ └── JoystickData.h ← joystick_data_t struct
├── lib/
│ ├── GrpcServer/ ← Dual-port TCP server
│ ├── MotorController/ ← H-bridge + servo actuator
│ ├── NeoPixel/ ← NeoPixel wrapper
│ └── AccessPointHelper/ ← WiFi AP setup
└── src/main.cpp ← FreeRTOS task entrypoints
- gRPC-like Protocol: TCP-based server on port 50051 for reliable communication
- IMU Sensor Integration: LSM6DSOX 6-axis accelerometer and gyroscope
- Real-time Data Streaming: Configurable IMU data streaming (1-50Hz)
- Joystick Control Processing: Dual analog joystick input handling
- LED Control: NeoPixel LED control with status indication
- WiFi Access Point: Creates rover network for client connections
- Optimized Performance: Efficient memory usage (17.4% RAM, 54.2% Flash)
- Adafruit Feather ESP32-S3 TFT (primary target)
- ESP32-S3 with WiFi capability and TFT display
- Minimum 4MB Flash, 320KB RAM
- LSM6DSOX IMU: 6-axis accelerometer and gyroscope sensor
- NeoPixel LED: Addressable RGB LED for status indication
- Power Supply: USB-C or battery power for mobile operation
The server provides a gRPC-like TCP service on port 50051 with the following RPC endpoints:
MSG_SET_LED: Control NeoPixel LED on/off stateMSG_GET_IMU: Return current IMU sensor readingsMSG_JOYSTICK_DATA: Process joystick control commandsMSG_STREAM_IMU: Start/stop continuous IMU data streaming
- Protocol:
STREAM:length:dataformat for real-time data - Configurable Rate: 1-50Hz streaming frequency
- Client Management: Multiple client support with individual streaming sessions
- Error Handling: Automatic cleanup on client disconnect
- PlatformIO IDE or CLI
- ESP32 development environment
- LSM6DSOX IMU sensor connected via I2C
- NeoPixel LED connected to GPIO33
# Clone the repository
git clone https://github.com/arunkumar-mourougappane/rover-esp32-control-server.git
cd rover-esp32-control-server
# Build the project
platformio run
# Upload to ESP32-S3
platformio run --target upload
# Monitor serial output
platformio device monitorThe server automatically creates a WiFi access point:
- SSID:
MOONBASE-II - Password:
Trypt1c0n$ - IP Address:
192.168.4.1 - gRPC Port:
50051
| Component | Pin | GPIO | Description |
|---|---|---|---|
| IMU SDA | SDA | GPIO42 | I2C Data Line |
| IMU SCL | SCL | GPIO41 | I2C Clock Line |
| NeoPixel Data | DATA | GPIO33 | LED Data Signal |
| NeoPixel Power | PWR | GPIO34 | LED Power Control |
The server continuously reads sensor data from the LSM6DSOX:
- Accelerometer: ±2g to ±16g range, 3-axis acceleration
- Gyroscope: ±125°/s to ±2000°/s range, 3-axis angular velocity
- Temperature: Integrated temperature sensor
- Sampling Rate: Up to 6.66kHz internal, configurable output rate
Processes dual joystick control data:
- Left Stick: X/Y axis values (-32768 to 32767)
- Right Stick: X/Y axis values (-32768 to 32767)
- Button States: Left and right joystick button presses
- Timestamp: Command timing for synchronization
NeoPixel LED provides visual feedback:
- Connection Status: Color-coded client connection state
- Command Processing: Visual confirmation of received commands
- System Status: Boot sequence and error indication
- GrpcServer: Main gRPC-like protocol server
- AccessPointHelper: WiFi AP management
- EmbeddedWebServer: HTTP server (legacy, being phased out)
- NeoPixel: LED control library
- Adafruit LSM6DSOX: IMU sensor driver
GrpcServer.h/cpp: Protocol server implementationJoystickData.h: Joystick data structure definitionsSensorData.h: IMU and sensor data structuresproto/rover_service.proto: Protocol service definitionsgenerate_proto.sh: Protocol documentation generator
- Define new message types in
proto/rover_service.proto - Add handler methods in
GrpcServerclass - Update message routing in main server loop
- Test with client implementation
Configure streaming parameters:
// Set streaming rate (Hz)
m_StreamingRate = 20; // 20Hz updates
// Configure IMU sensor range
lsm6ds.setAccelRange(LSM6DS_ACCEL_RANGE_2_G);
lsm6ds.setGyroRange(LSM6DS_GYRO_RANGE_250_DPS);- Serial debug output at 115200 baud
- Comprehensive logging with
log_i(),log_e()macros - WiFi connection status monitoring
- Client connection tracking
- RAM Usage: 17.4% (57,076 bytes / 327,680 bytes)
- Flash Usage: 54.2% (781,553 bytes / 1,441,792 bytes)
- IMU Sample Rate: Up to 50Hz streaming
- Client Connections: Multiple concurrent clients supported
- WiFi Range: Typical ESP32 AP range (~50m)
- Connect IMU sensor to I2C pins (GPIO41/42)
- Connect NeoPixel LED to GPIO33
- Power up the ESP32-S3
- Connect client device to
MOONBASE-IIWiFi - Test gRPC communication on port 50051
Use the client application or tools like nc (netcat) to test:
# Connect to server
nc 192.168.4.1 50051
# Send LED control command
MSG_SET_LED:{"state": true}
# Request IMU data
MSG_GET_IMU:{}- Fork the repository
- Create a feature branch
- Implement and test changes
- Update documentation
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.


