SPDX-License-Identifier: Apache-2.0
The Basic SKU (¥999) is a standalone ESP32-S3 mobile platform for learning embedded programming, robotics fundamentals, and FreeRTOS concepts. No companion computer (Pi 5) or ROS2 knowledge is required.
This repo contains code for the full product line — from basic teleop to autonomous competition robots. Use this table to focus on what matters for your goal and safely ignore the rest:
| Goal | Packages to focus on | Safe to ignore |
|---|---|---|
| Remote-controlled chassis | firmware/, robot_bringup/, sdk/web_basic/ |
competition, collection, docking, perception, simulation |
| Custom payload development | + sdk/payload_interface/, sdk/examples/ |
competition, collection |
| Autonomous navigation | + robot_description/, robot_perception/, robot_simulation/ |
competition, collection |
| Competition / ball collection demo | All packages | — |
Each row builds on the previous. Start from the top and expand downward as your project requires.
- 4WD Mecanum chassis (300 × 250 mm)
- ESP32-S3-WROOM-1 control board (8MB Flash, 8MB PSRAM)
- 4× N20 gear motors (1:90) with magnetic encoders
- 4× HC-SR04 ultrasonic sensors
- BNO055 IMU module
- 2S 6000mAh LiPo battery
- USB-C programming cable
Follow the full setup in firmware_guide.md — Prerequisites to install ESP-IDF 5.x and USB drivers for your platform. Then build the Basic firmware:
cd firmware/esp32
idf.py set-target esp32s3
idf.py build flash monitorSee firmware_guide.md for detailed build options, menuconfig, and troubleshooting.
The ESP32-S3 is compatible with the Arduino ESP32 core. Install via Board Manager:
- Board: ESP32-S3 Dev Module
- Flash: 8MB, PSRAM: Enabled
The Basic SKU uses WiFi UDP direct control (no ROS2). Connect from any device on the same network:
import socket
import struct
import time
ROBOT_IP = "192.168.4.1" # AP mode default
CMD_PORT = 8888
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
def send_velocity(vx, vy, omega):
"""Send cmd_vel: vx (m/s), vy (m/s), omega (rad/s)"""
packet = struct.pack('<3f', vx, vy, omega)
sock.sendto(packet, (ROBOT_IP, CMD_PORT))
# Drive forward at 0.2 m/s for 3 seconds
for _ in range(30):
send_velocity(0.2, 0.0, 0.0)
time.sleep(0.1)
# Stop
send_velocity(0.0, 0.0, 0.0)The firmware runs a PID velocity controller at 50Hz. Adjust gains via menuconfig:
idf.py menuconfig
# Navigate: Robot Platform → Motor PID Tuning
# Kp=120 (1.20), Ki=80 (0.80), Kd=1 (0.01)Monitor encoder feedback over UDP telemetry (port 9999) to visualize step response.
The 4 ultrasonic sensors (front, back, left, right) trigger emergency stop at 5cm. Write custom avoidance logic:
// In your main loop, read ultrasonic distances:
float front_dist = ultrasonic_get_range_m(US_FRONT);
if (front_dist < 0.20f) {
// Turn away from obstacle
motor_mecanum_drive(&(cmd_vel_t){.vx=0, .vy=0, .omega=1.0f});
}The firmware uses FreeRTOS tasks for concurrent operation:
| Task | Priority | Stack | Function |
|---|---|---|---|
| Motor control | 5 | 4096 | PID loop at 50Hz |
| Ultrasonic scan | 3 | 2048 | Sensor polling |
| WiFi/UDP | 2 | 4096 | Command reception |
| Battery monitor | 1 | 2048 | Voltage sampling |
Create your own tasks to experiment with priorities, queues, and semaphores.
Read wheel encoders to estimate robot position:
// Each encoder gives pulses via PCNT hardware
int32_t fl_count = encoder_get_count(MOTOR_FL);
float fl_rps = encoder_get_speed_rps(MOTOR_FL); // revolutions/second
// Convert to linear velocity:
// v = rps * 2π * wheel_radius
float v_fl = fl_rps * 2.0f * 3.14159f * 0.024f; // 24mm radiusWhen ready for autonomous navigation, add a Raspberry Pi 5 and the Standard SKU software stack. The ESP32 firmware remains the same — it communicates with the Pi 5 over UART using micro-ROS.