This post, I configure the ESP32 microcontroller to control robot's motors based on commands received from ROS. We'll establish serial communication between the Jetson Nano and the ESP32, upload the necessary firmware to the ESP32, and create a ROS node to handle motor commands.
ESP32 Development Board
Motor Driver (e.g., L298N, TB6612FNG)
DC Motors
Jumper Wires
Power Supply (appropriate for motors and ESP32)
=> connect ESP32 to Jetson nano via USB.
=> Connect Motors to the Motor Driver.
Program the ESP32 to listen for serial commands from the Jetson Nano and control the motors accordingly.
// Motor Control Pins
const int motorAForward = 5; // IN1
const int motorABackward = 18; // IN2
const int motorBForward = 19; // IN3
const int motorBBackward = 21; // IN4
const int motorASpeed = 22; // ENA (PWM)
const int motorBSpeed = 23; // ENB (PWM)
void setup() {
// Initialize serial communication
Serial.begin(115200);
// Initialize motor control pins
pinMode(motorAForward, OUTPUT);
pinMode(motorABackward, OUTPUT);
pinMode(motorBForward, OUTPUT);
pinMode(motorBBackward, OUTPUT);
pinMode(motorASpeed, OUTPUT);
pinMode(motorBSpeed, OUTPUT);
// Stop motors initially
stopMotors();
}
void loop() {
if (Serial.available() > 0) {
char command = Serial.read();
switch (command) {
case 'F':
moveForward();
break;
case 'B':
moveBackward();
break;
case 'L':
turnLeft();
break;
case 'R':
turnRight();
break;
case 'S':
stopMotors();
break;
default:
stopMotors();
break;
}
}
}
void moveForward() {
digitalWrite(motorAForward, HIGH);
digitalWrite(motorABackward, LOW);
digitalWrite(motorBForward, HIGH);
digitalWrite(motorBBackward, LOW);
analogWrite(motorASpeed, 255); // Full speed
analogWrite(motorBSpeed, 255); // Full speed
}
void moveBackward() {
digitalWrite(motorAForward, LOW);
digitalWrite(motorABackward, HIGH);
digitalWrite(motorBForward, LOW);
digitalWrite(motorBBackward, HIGH);
analogWrite(motorASpeed, 255); // Full speed
analogWrite(motorBSpeed, 255); // Full speed
}
void turnLeft() {
digitalWrite(motorAForward, LOW);
digitalWrite(motorABackward, HIGH);
digitalWrite(motorBForward, HIGH);
digitalWrite(motorBBackward, LOW);
analogWrite(motorASpeed, 255); // Full speed
analogWrite(motorBSpeed, 255); // Full speed
}
void turnRight() {
digitalWrite(motorAForward, HIGH);
digitalWrite(motorABackward, LOW);
digitalWrite(motorBForward, LOW);
digitalWrite(motorBBackward, HIGH);
analogWrite(motorASpeed, 255); // Full speed
analogWrite(motorBSpeed, 255); // Full speed
}
void stopMotors() {
digitalWrite(motorAForward, LOW);
digitalWrite(motorABackward, LOW);
digitalWrite(motorBForward, LOW);
digitalWrite(motorBBackward, LOW);
analogWrite(motorASpeed, 0); // Stop
analogWrite(motorBSpeed, 0); // Stop
}
After connecting the ESP32, identify the serial port assigned to it.
ls /dev/ttyUSB* /dev/ttyACM*
/dev/ttyUSB0
/dev/ttyACM0
My port is
/dev/ttyACM0
Ensure your user has the necessary permissions to access the serial port.
sudo usermod -a -G dialout $USER

We'll create a ROS package that sends serial commands to the ESP32 based on ROS topics or node logic (e.g., obstacle avoidance).
cd ~/catkin_ws/src
catkin_create_pkg motor_control rospy std_msgs
mkdir -p ~/catkin_ws/src/motor_control/scripts
cd ~/catkin_ws/src/motor_control/scripts
nano motor_control_node.py
*code
#!/usr/bin/env python3
import rospy
import serial
from std_msgs.msg import String
class MotorControlNode:
def __init__(self):
rospy.init_node('motor_control_node', anonymous=True)
# Serial port parameters
serial_port = rospy.get_param('~serial_port', '/dev/ttyACM0') # Update as needed
baud_rate = rospy.get_param('~baud_rate', 115200)
try:
self.ser = serial.Serial(serial_port, baud_rate, timeout=1)
rospy.loginfo(f"Connected to ESP32 on {serial_port} at {baud_rate} baud.")
except serial.SerialException as e:
rospy.logerr(f"Failed to connect to ESP32 on {serial_port}: {e}")
rospy.signal_shutdown("Serial connection failed.")
# Subscriber to 'motor_command' topic
rospy.Subscriber('motor_command', String, self.command_callback)
def command_callback(self, msg):
command = msg.data.upper()
if command in ['F', 'B', 'L', 'R', 'S']:
rospy.loginfo(f"Sending command to ESP32: {command}")
try:
self.ser.write(command.encode())
except serial.SerialException as e:
rospy.logerr(f"Failed to send command: {e}")
else:
rospy.logwarn(f"Received unknown command: {command}")
def run(self):
rospy.spin()
if __name__ == '__main__':
node = MotorControlNode()
node.run()
chmod +x motor_control_node.py
nano ~/catkin_ws/src/motor_control/package.xml
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>

nano ~/catkin_ws/src/motor_control/CMakeLists.txt
catkin_install_python(PROGRAMS scripts/motor_control_node.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

find_package(catkin REQUIRED COMPONENTS
rospy
std_msgs
)

4.5.4. Build the ROS Package
cd ~/catkin_ws
catkin_make
Source the Workspace:
source devel/setup.bash
4.5.5. Create a Launch File for Motor Control
mkdir -p ~/catkin_ws/src/motor_control/launch
cd ~/catkin_ws/src/motor_control/launch
nano motor_control.launch
<launch>
<node name="motor_control_node" pkg="motor_control" type="motor_control_node.py" output="screen">
<!-- Parameters -->
<param name="serial_port" value="/dev/ttyACM0"/> <!-- Update as needed -->
<param name="baud_rate" value="115200"/>
</node>
</launch>

4.5.6. Launch the Motor Control Node
roslaunch motor_control motor_control.launch
Verify Connection:

4.6.1. Publish Commands to motor_command Topic
source /opt/ros/noetic/setup.bash
source ~/catkin_ws/devel/setup.bash
Use rostopic to Publish Commands:
rostopic pub /motor_command std_msgs/String "F" -1
rostopic pub /motor_command std_msgs/String "B" -1
rostopic pub /motor_command std_msgs/String "L" -1
rostopic pub /motor_command std_msgs/String "R" -1
rostopic pub /motor_command std_msgs/String "S" -1

