micro-ROS workspace for ESP32 (FreeRTOS + ESP-IDF) with ROS 2 Humble, including both the Wall-E control stack and the Gassify gas-robot stack.
For a user-focused setup and operation guide, see user_manual.md.
This repository includes:
- ESP32 firmware app:
firmware/custom/esp32_controller - micro-ROS firmware workspace:
firmware/mcu_ws - Host ROS 2 workspace:
src,build,install - micro-ROS agent container (UDP 9999)
- Gas robot ROS 2 workspace:
gas_robot_ws/src - Gassify system model:
gassify_model.md
- User guide: user_manual.md
- Concepts guide: concepts.md
- Theoretical calculation summary: notes.md
- Gassify mathematical model: gassify_model.md
- PCB and KiCad sources: pcb/wall_e_pcb_v1.0
- Sphinx docs index:
docs/source/index.rst
The Gassify part of this repository focuses on gas-robot modeling and robot description assets alongside the existing Wall-E stack.
- ROS 2 package set location:
gas_robot_ws/src - Robot description package:
gas_robot_ws/src/gas_robot_description - URDF/Xacro model includes a dedicated gas sensor link (
gas_sensor_link) in:gas_robot_ws/src/gas_robot_description/gas_robot_description/src/description/gas_robot.urdf.xacro
- Full mathematical and control model reference:
gassify_model.md
The Sphinx site includes the same high-level concepts and firmware details in a published format.
- Docker
- Docker Compose
- USB access for flashing (device usually
/dev/ttyUSB0)
Optional for CUDA/GPU workflows:
- NVIDIA GPU driver on host
- NVIDIA Container Toolkit on host (
nvidia-container-toolkit)
From the repository root:
- Start workspace and agent
docker compose up -dGPU/CUDA-enabled start (keeps default pipeline unchanged when not used):
docker compose -f docker-compose.yaml -f docker-compose.gpu.yaml up --build -dGPU image includes an object-detection-ready Python stack in this mode:
- CUDA-enabled PyTorch (
torch,torchvision,torchaudio) ultralytics(YOLO)opencv-python-headlessonnxandonnxruntime-gpu==1.18.1(pinned)pycocotoolstensorrt==8.6.1(pinned)
To override TensorRT version, change TENSORRT_PIP_SPEC in docker/docker-compose.gpu.yaml.
To override ONNX Runtime GPU version, change ONNXRUNTIME_GPU_PIP_SPEC in docker/docker-compose.gpu.yaml.
Quick GPU check inside container:
docker exec -it micro_ros_workspace bash -lc "nvidia-smi"Quick Python GPU/object-detection check:
docker exec -it micro_ros_workspace bash -lc "python3 -c 'import torch, ultralytics, cv2; print(torch.__version__); print(torch.cuda.is_available()); print(ultralytics.__version__); print(cv2.__version__)'"Quick TensorRT check:
docker exec -it micro_ros_workspace bash -lc "python3 -c 'import tensorrt as trt; print(trt.__version__)'"- Enter workspace container
docker exec -it micro_ros_workspace bash- Build host ROS 2 workspace (if needed)
cd /micro_ros_ws
colcon build- Source the environment
source /opt/ros/humble/setup.bash
source /micro_ros_ws/install/setup.bashIf you launch RViz or Gazebo GUI from the container and see Authorization required, but no authorization protocol specified, run xhost +si:localuser:root on the host before starting the container. This workspace uses X11 forwarding, and the container runs as root by default.
wall_e.launch.pynow generatesrobot_descriptionusing the Python xacro API (xacro.process_file(...).toxml()) instead of shelling out to thexacroexecutable.gazebo_rosremains optional at launch time. If unavailable, launch continues with a clear log message.- Rear wheel TF visibility in RViz is now guaranteed even when
joint_state_publisheris disabled:- rear wheel static TF publishers are started by default
- they are automatically disabled when
use_joint_state_publisher:=true
- Fresh containers should include runtime ROS GUI/model dependencies from
Dockerfile(ros-humble-xacro,ros-humble-rviz2,ros-humble-gazebo-ros-pkgs,ros-humble-joint-state-publisher).
If your currently running container was created before these package updates, rebuild it:
docker compose up --build -dKiCad is available in the workspace container and the PCB project is mounted at:
/micro_ros_ws/pcb/wall_e_pcb_v1.0- Verify KiCad CLI:
docker exec micro_ros_workspace kicad-cli version- Run DRC report:
docker exec micro_ros_workspace kicad-cli pcb drc /micro_ros_ws/pcb/wall_e_pcb_v1.0/wall_e.kicad_pcb- Export Gerbers:
docker exec micro_ros_workspace kicad-cli pcb export gerbers /micro_ros_ws/pcb/wall_e_pcb_v1.0/wall_e.kicad_pcb --output /micro_ros_ws/pcb/wall_e_pcb_v1.0/gerbers- Launch KiCad GUI from container (host X11):
xhost +si:localuser:root
docker exec -it micro_ros_workspace kicadInside the workspace container:
- Configure firmware app and transport
ros2 run micro_ros_setup configure_firmware.sh esp32_controller -t udp -i 192.168.1.2 -p 8888- Build firmware
ros2 run micro_ros_setup build_firmware.sh- Firmware output files
firmware/freertos_apps/microros_esp32_extensions/build/esp32_controller.bin
firmware/freertos_apps/microros_esp32_extensions/build/esp32_controller.elfInside the workspace container (requires serial device access):
- Flash
ros2 run micro_ros_setup flash_firmware.shIf your board is not /dev/ttyUSB0, update docker-compose.yaml device mapping or run a custom docker command with the correct --device mapping.
Inside the workspace container:
cd /micro_ros_ws/firmware/freertos_apps/microros_esp32_extensions
source /micro_ros_ws/firmware/toolchain/esp-idf/export.sh
idf.py -p /dev/ttyUSB0 monitorYou can also build then monitor:
idf.py build
idf.py -p /dev/ttyUSB0 monitorWokwi does not use /dev/ttyUSB0. It loads your firmware .bin/.elf directly.
- Ensure
wokwi.tomlpoints to the generated firmware files:
[wokwi]
version = 1
firmware = "firmware/freertos_apps/microros_esp32_extensions/build/esp32_controller.bin"
elf = "firmware/freertos_apps/microros_esp32_extensions/build/esp32_controller.elf"- Build firmware before starting simulator:
docker exec micro_ros_workspace bash -lc "cd /micro_ros_ws/firmware/freertos_apps/microros_esp32_extensions && source /micro_ros_ws/firmware/toolchain/esp-idf/export.sh && idf.py build"-
Start Wokwi from VS Code command palette:
Wokwi: Start Simulator. -
Read logs in VS Code
Outputpanel,Wokwichannel (orWokwi Terminal, depending on extension version).
If logs still do not appear, stop simulation, reload VS Code window, and start simulator again.
Both ESP32 controller apps (motor-enabled and sensor-only) include a browser-based WiFi provisioning system for easy credential setup without source code editing.
-
First Boot (No Credentials)
- Device starts WiFi access point:
ESP32-Setup(open, no password) - HTTP provisioning server launches on
http://192.168.4.1/
- Device starts WiFi access point:
-
User Setup
- Connect laptop/phone to
ESP32-Setupnetwork - Open browser to
http://192.168.4.1/ - Enter WiFi SSID and password via form
- Device saves credentials to NVS flash (persistent)
- Connect laptop/phone to
-
Subsequent Boots
- Device reads stored credentials from NVS
- Connects to configured WiFi in normal station mode
- Proceeds with ROS 2 and sensor initialization
- Location: NVS flash partition, namespace
"wifi_creds" - Persistence: Survives power cycles and resets
- Fallback: Kconfig symbols
CONFIG_ESP_WIFI_SSID/CONFIG_ESP_WIFI_PASSWORD(for build-time defaults)
The provisioning system includes:
nvs_read_wifi_creds(): Load SSID/password from flashnvs_write_wifi_creds(): Save credentials after form submission- SoftAP initialization for open access point
- Simple HTML form with POST handler for credential entry
- Automatic server shutdown after successful provisioning
firmware/custom/esp32_controller/: Motor + sensor controller with full provisioningfirmware/freertos_apps/apps/esp32_controller/: Sensor-only reference variant (provisioning included)
The compose service starts this automatically:
microros/micro-ros-agent:humble udp4 --port 9999You can check logs with:
docker logs -f micro_ros_agentThe firmware publishes raw sensor topics:
/imu/data(sensor_msgs/msg/Imu)/range/data(sensor_msgs/msg/Range)
Host-side ROS 2 nodes can process these into cleaner and fused signals:
wall_e_ws/src/feedback/feedback/imu_filter.py- Input:
/imu/data - Output:
/imu/filter
- Input:
wall_e_ws/src/feedback/feedback/ultrasonic_filter.py- Input:
/range/data - Output:
/range/filter
- Input:
wall_e_ws/src/feedback/feedback/sensor_fusion.py- Inputs:
/imu/filter,/range/filter - Outputs:
/fusion/height,/fusion/vertical_velocity,/fusion/debug
- Inputs:
The board design lives in pcb/wall_e_pcb_v1.0.
The current schematic includes the ESP32 module, motor driver stages, the MPU6050, the HC-SR04 range sensor, and paired hall-effect sensors for wheel-speed or rotation feedback.
Use the KiCad project as the source of truth for connector pinouts, board placement, and DRC/GERBER work before changing firmware wiring assumptions.
This keeps firmware responsibilities focused on sensor I/O and control while allowing filter tuning on the host.
Current host-side runtime flow:
control/gamepad_node- Publishes
/gamepad
- Publishes
control/robot_control_node- Inputs:
/gamepad,/range/filter,/imu/filter,/odom - Outputs:
/cmd_vel,/servo_cmd - Supports manual mode and cascaded closed-loop mode:
- outer P/PD position-to-speed setpoint
- inner PI speed tracking loop
- Also maps a gamepad axis to
/servo_cmd(std_msgs/msg/Float32, angle in degrees)
- Inputs:
localization/odometry_node- Inputs:
/cmd_vel,/imu/filter,/range/filter,/fusion/height,/fusion/vertical_velocity - Output:
/odom
- Inputs:
localization/inverse_kinematic_node- Input:
/cmd_vel - Output:
/motor_cmd(4-wheel command array)
- Input:
Flow summary:
esp32_controllerpublishes raw IMU and range data to ROS 2.imu_filterandultrasonic_filterproduce cleaner feedback streams.sensor_fusioncombines filtered streams for downstream state estimation.odometry_nodeestimates vehicle state and publishes/odom.robot_control_nodegenerates/cmd_velusing gamepad + safety + PI hold.robot_control_nodealso publishes/servo_cmdfrom gamepad axis input (with configurable limits/default).- In closed-loop mode, PI acts on speed error (not direct position error).
inverse_kinematic_nodeconverts/cmd_velinto/motor_cmdfor firmware actuation.
- Rebuild host workspace
docker exec -it micro_ros_workspace bash -lc "cd /micro_ros_ws && colcon build"- Rebuild firmware
docker exec -it micro_ros_workspace bash -lc "source /opt/ros/humble/setup.bash && source /micro_ros_ws/install/setup.bash && ros2 run micro_ros_setup build_firmware.sh"-
Confirmed firmware path in use is
firmware/custom/esp32_controller. -
MPU startup now includes gyro bias calibration (
200samples). Keep robot still at boot for best drift reduction. -
Ultrasonic timeout message
Ultrasonic timeout(wait high)means no rising edge was seen on ECHO. -
After wiring/level correction, ultrasonic produced valid pulse widths around
344-401 us(~0.059-0.069 min test setup). -
Reboot right after
Starting ROS node setup...was traced to watchdog interaction in startup; the firmware now logs watchdog delete/add status around ROS init. -
NVS open for read failed: 0x1102is expected when WiFi credentials are not yet stored in namespacewifi_creds. -
Stop everything
docker compose down- Package
micro_ros_setupnot found Source both setup files:
source /opt/ros/humble/setup.bash
source /micro_ros_ws/install/setup.bash- Launch error:
file not found: [Errno 2] No such file or directory: 'xacro'Install xacro in the running container (or rebuild image after pulling latest Dockerfile updates):
apt-get update && apt-get install -y ros-humble-xacro- Launch/CLI says
Package not foundforrviz2orgazebo_rosYour container is missing GUI/simulation runtime packages. Rebuild with the updated image:
docker compose up --build -d-
RViz RobotModel shows
No transform from rear_left_wheelorrear_right_wheelThis is now handled in the launch file by publishing rear-wheel static transforms whenjoint_state_publisheris off. If you still see it, restart launch and verify you are using the updatedwall_e.launch.py. -
Docker compose fails on
/dev/ttyUSB0Your host may expose a different serial device. Adjustdocker-compose.yamlaccordingly. -
Build fails with Git safe.directory for esp-idf Run in container:
git config --global --add safe.directory /micro_ros_ws/firmware/toolchain/esp-idf- ESP-IDF monitor fails with
Device or resource busy: '/dev/ttyUSB0'Another process is already using the serial port. Close any existingscreen,idf.py monitor, or other serial tool and retry. To find the owner:
fuser -v /dev/ttyUSB0
lsof /dev/ttyUSB0make menuconfigfails withpython: No such file or directoryInstall Python alias in the container:
apt-get update && apt-get install -y python-is-python3- ESP-IDF Python dependency warnings (
pkg_resources, setuptools) For ESP-IDF v4.1, pin setuptools in the IDF virtualenv:
VENV=/root/.espressif/python_env/idf4.1_py3.10_env
$VENV/bin/python -m pip install --upgrade pip
$VENV/bin/python -m pip install "setuptools<81" wheel-
Runtime crash:
assertion "netif" failedinesp_netif_create_default_wifi_staCause: duplicate default STA netif creation. Fix applied in:firmware/freertos_apps/apps/esp32_controller/app.cfirmware/custom/esp32_controller/app.c
If this reappears after syncing app sources, ensure Wi-Fi init runs once and
esp_netif_create_default_wifi_sta()is not called multiple times. -
Wi-Fi behavior on mobile hotspots If connection is unstable, force hotspot to 2.4 GHz WPA2 and disable battery-saving/idle sleep on the phone.
-
Wokwi simulator starts but no logs are visible Usually caused by stale build artifacts or viewing the wrong panel. Rebuild firmware, restart simulation, and check VS Code
Output->Wokwi.
src: ROS 2 packagesfirmware/custom/esp32_controller: app sourcefirmware/freertos_apps/microros_esp32_extensions: generated ESP-IDF projectfirmware/mcu_ws: micro-ROS cross-compiled workspacedocs: Sphinx documentation
- Mahony, R., Hamel, T., and Pflimlin, J.-M. (2008). Nonlinear Complementary Filters on the Special Orthogonal Group. IEEE Transactions on Automatic Control, 53(5), 1203-1218.
- Madgwick, S. O. H. (2010). An efficient orientation filter for inertial and inertial/magnetic sensor arrays. University of Bristol, Technical Report.
- Thrun, S., Burgard, W., and Fox, D. (2005). Probabilistic Robotics. MIT Press.
- Siciliano, B., Sciavicco, L., Villani, L., and Oriolo, G. (2009). Robotics: Modelling, Planning and Control. Springer.
- Astrom, K. J., and Murray, R. M. (2008). Feedback Systems: An Introduction for Scientists and Engineers. Princeton University Press.
