Final project

An omnidirectional autonomous mobile robot will be developed, capable of detecting and avoiding obstacles. The system is designed to operate on solid, flat, and stable surfaces. Figure 1 presents the initial conceptual design of the autonomous mobile robot.

Main Design Sketch
Figure 1. Initial conceptual design of the omnidirectional autonomous mobile robot.

Background Idea

The motivation for this project arises from a strong personal interest in robotics, particularly in the field of space robotics. This work is conceived as an opportunity to consolidate fundamental knowledge in mechanics, electronics, and programming, as well as to apply these skills in the design and construction of a functional autonomous vehicle.

Sketch of the Final Project

The robot will be equipped with four mecanum wheels, which enable movement in any direction depending on the motion applied to each individual wheel. These wheels will be coupled to TT DC motors and complemented with sensor systems located on the lateral, frontal, and rear sections of the autonomous vehicle. Figure 2 shows the overall robot design with the main components integrated. Figure 3 presents a top view of the robot chassis, while Figure 4 illustrates an exploded view highlighting the central components of the system.

Main Design Sketch
Figure 2. Autonomous mobile robot with integrated mechanical and electronic components.
Main Design Sketch
Figure 3. Top view of the robot chassis.
Main Design Sketch
Figure 4. Exploded view of the autonomous mobile robot showing the main components.

Finally, Figure 5 shows the operation of the robot's movement according to the type of motion imparted to its wheels. This information was obtained from Motion Dynamics.

Main Design Sketch
Figure 5. Operation of the robot's movement according to the type of motion imparted to its wheels.

Expected Challenges

One of the main challenges anticipated during the development of this project is the programming and electronic design of the robot. This is primarily due to the fact that my academic background is not directly focused on these areas, resulting in limited prior experience with the programming languages, development environments, and electronic architectures required for the system. Nevertheless, this project represents an ideal opportunity to acquire and strengthen skills that are not typically addressed within my field of study, contributing significantly to my professional development.

General Schedule of Activities for the Final Project

I wrote a general schedule of activities to organize myself better

Fablab Week Main Tasks Estimated Time
Week 1. Project management
  • Project description
  • Creation of the Wheel of Achievements
  • Establishment of the activity schedule
  • Identification of fundamental concepts
  • Conceptual sketching
  • Identification of required project components
  • Website design and programming
2-3 days
Week 2. Computer Aided Design
  • Design of the robot chassis using CAD software
  • Design of required mechanical parts
  • Design of sensor supports
2-3 days
Week 3. Computer Controlled Cutting
  • Cutting of the chassis and necessary components for robot assembly
  • Implementation of adaptations if required
2-3 days
Week 4. Electronic Production
  • Design and fabrication of a PCB to integrate the ESP32 microcontroller, sensor module sockets, and motor connectors
3-5 days
Week 5. 3D Scanning and printing
  • 3D printing of an adjustable support for the MQ-135 sensor
  • 3D printing of a PCB enclosure
2-3 days
Week 6. Embedded Programming
  • Development of a basic motion control program including motor control, sensor data acquisition, and obstacle avoidance logic
3-5 days
Week 7. Computer Controlled Machining
  • Refinement of mechanical components and chassis
3-4 days
Week 8. Electronics Design
  • Development of a basic electronic schematic
  • PCB design including additional protections and status LEDs
3-4 days
Week 9. Output Devices
  • Motor control implementation
2-3 days
Week 10. Mechanical Design
  • Design of a basic drivetrain system
  • Definition of guides for rapid assembly
  • Implementation of active or passive ventilation
2-3 days
Week 11. Input Devices
  • Integration of sensor systems
  • Functional testing and validation of sensor operation
3-4 days
Week 12. Molding and Casting
  • Fabrication of a basic protective enclosure
3-4 days
Week 13. Embedded Nettworking and Communications
  • Establishment of basic WiFi communication
4-5 days
Week 14. Interface and Application Programming
  • Development of a web-based interface
4-5 days

Preliminary Bill of Materials

Componente Quantity Cost (MXN) Application
TT DC Motor 4 $59 Robot locomotion
L298N H-Bridge Motor Driver 1 $90 Control of motor speed and rotation direction
Rotary Encoders 4 $43 Controlled measurement of vehicle motion
ESP32 DevKit V1 1 $124 Main microcontroller
HC-SR04 Ultrasonic Sensor 3 $53 Obstacle detection
LM7805 Voltage Regulator 1 $48 Voltage regulation for motors and sensors
AMS1117-3.3 Voltage Regulator 1 $12 Voltage regulation for sensors
Male/Female Headers 1 set $350 Electrical connections
Dupont Cables (MM, MF, FF) Various $59 Electrical connections
Rechargeable Battery (2500 mAh) 2 $189 Power supply
Power Switch 1 $29 System power on/off
Double-sided Copper Board 10x10cm $392 PCB fabrication
SMD Resistors 0805 Various $0.24 PCB components
SMD Capacitors 0805 Various $129 PCB components

What is an Omnidirectional Robot? Fundamental Concepts for the Project

Mecanum wheels have free rollers positioned at 45° relative to the wheel axis. Independently controlling each of these wheels allows different rotation combinations, enabling the robot to move along the X and Y axes, rotate in place, and perform any simultaneous combination of these movements.

PCB Design – Planning

A total of three PCB designs will be made.

Brain Board Design

The XIAO ESP32C6 will be used as the brain of the project. Connected to it will be the VL53L0X sensors, WiFi for XBOX controller input, QR code processing, navigation logic, and the transmission of speed or direction commands. This board will be manufactured once.

Components

The pin assignment plan is as follows:

PINFunctionConnected to
D4 (SDA)I2C dataSDA of all 3 VL53L0X + 4.7kΩ pull-up to 3.3V
D5 (SCL)I2C clockSCL of all 3 VL53L0X + 4.7kΩ pull-up to 3.3V
D0XSHUT sensor 1XSHUT VL53L0X #1
D1XSHUT sensor 2XSHUT VL53L0X #2
D2XSHUT sensor 3XSHUT VL53L0X #3
D6 (TX)UART TXRX of GM65
D7 (RX)UART RXTX of GM65
D8 (TX2)UART toward RP2350RX of RP2350
D9 (RX2)UART from RP2350TX of RP2350
3.3VPower supplyVIN of all 3 VL53L0X + GM65 + pull-ups. Note: 4 pins for 3.3V
GNDGroundGND of everything. Note: 4 pins for GND

Motor Board Design

A board based on the XIAO RP2350 will be created to connect the motors and receive commands from the ESP32C6 to independently control the four motors. This board will be manufactured once. The components to be used are:

The pin assignment plan is as follows:

PINFunctionConnected to
D0PWM motor 1IN1 driver board #1
D1DIR motor 1IN2 driver board #1
D2PWM motor 2IN1 driver board #2
D3DIR motor 2IN2 driver board #2
D4PWM motor 3IN1 driver board #3
D5DIR motor 3IN2 driver board #3
D6PWM motor 4IN1 driver board #4
D7DIR motor 4IN2 driver board #4
D8 (TX)UART from ESP32C6TX of ESP32C6
GNDCommon groundGND of ESP32C6

H-Bridge Driver Board Design

The DRV8251A driver will be used. This board will be connected to the motor board to prevent component damage. This board will be manufactured 4 times, one per motor. The board components are:

The pin usage plan is:

DRV8251A PinConnected to
IN1PWM from RP2350
IN2DIR from RP2350
OUT1Motor terminal A
OUT2Motor terminal B
VMMotor power supply (3V–6V)
GNDCommon GND
nSLEEP3.3V (always active)

Additionally, it was recommended to organize the pin headers according to their intended use. To keep better order over the work and simplify the design in Altium, the following tables were created:

ESP32C6 Board

Header StripPin CountIncluded PinsUse
Strip 15GND, 3.3V, SDA, SCL, XSHUTVL53L0X Sensor #1
Strip 25GND, 3.3V, SDA, SCL, XSHUTVL53L0X Sensor #2
Strip 35GND, 3.3V, SDA, SCL, XSHUTVL53L0X Sensor #3
Strip 44GND, 3.3V, TX, RXGM65 Module (QR)
Strip 53GND, TX, RXUART toward RP2350
Strip 625V, GNDBuck module input

RP2350 Board

Header StripPin CountIncluded PinsUse
Strip 14GND, VM, IN1, IN2DRV8251A Driver #1
Strip 24GND, VM, IN1, IN2DRV8251A Driver #2
Strip 34GND, VM, IN1, IN2DRV8251A Driver #3
Strip 44GND, VM, IN1, IN2DRV8251A Driver #4
Strip 53GND, RX, TXUART toward ESP32C6
Strip 625V, GNDMotor buck input
Strip 72VIN, GNDMicrocontroller buck input

H-Bridge Driver Board

Header StripPin CountIncluded PinsUse
Strip 14GND, VM, IN1, IN2Input from RP2350
Strip 22OUT1, OUT2Output toward motor

Following this pin order, it is important to note that strips connecting boards together (such as UART) must be mirrored.

Finally, to regulate the power flow, two MP1584EN buck modules will be used: one for the microcontrollers and one for the four motors.

H-Bridge PCBs – Motor Controllers

For the H-bridge, the following component was used to control the motor: TB67H450FNG (SnapEDA).

To determine the current that must pass through the H-bridge, the startup current was considered. The motors used can instantaneously reach up to 1.5 A. Since this H-bridge is designed to control only one motor, a slightly higher amperage margin was left. This is important because PCB track width depends on the current it must carry. During Week 6, the method for calculating this width using KiCad's automatic calculator was learned and applied, which returned a result of 0.8 mm. This value was then used to set the preferred and allowed track widths in Altium, after which the routing process began. Units can be changed by pressing Q (switching to mm).

For the outline of the H-bridge board, a free space was left to allow for a hole to attach it to the main brain board. The available tooling was also taken into account, so the minimum trace width needed to be 2 mm.

VL53L0X Sensor Testing – Individual and Combined

There are different types of distance-measuring sensors; among them is the VL53L0X. First, it was necessary to test a single sensor. For this purpose, the following code was used:

#include <Wire.h>
#include <VL53L0X.h>

// Pin configuration
#define SENSOR_SDA  6    // GPIO6 = Pin D4
#define SENSOR_SCL  7    // GPIO7 = Pin D5
#define XSHUT_PIN   2    // GPIO2 = Pin D0

VL53L0X sensor;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting Sensor VL53LXX-V2...");

  // Configure I2C bus on pins D4 (SDA) and D5 (SCL)
  Wire1.setSDA(SENSOR_SDA);
  Wire1.setSCL(SENSOR_SCL);
  Wire1.begin();

  // Use XSHUT on D0 to reset the sensor
  pinMode(XSHUT_PIN, OUTPUT);
  digitalWrite(XSHUT_PIN, LOW);   // Turn off sensor
  delay(10);
  digitalWrite(XSHUT_PIN, HIGH);  // Turn on sensor
  delay(50);                       // Wait for stabilization

  // Initialize sensor on Wire1 bus
  sensor.init(true);  // true = use the specified I2C bus (Wire1)
  sensor.setTimeout(500);
  sensor.startContinuous();
  Serial.println("Sensor ready!");
}

void loop() {
  int distance = sensor.readRangeContinuousMillimeters();
  if (sensor.timeoutOccurred()) {
    Serial.println("Error: Timeout occurred");
  } else {
    Serial.print("Distance: ");
    Serial.print(distance);
    Serial.println(" mm");
  }
  delay(100);
}

Motion Programming

For the wheel programming, research was first conducted on the basic movements possible with the purchased wheel type. Based on these observed movements, a code was written to instruct the robot to perform the following maneuvers: forward, backward, left and right lateral translation, clockwise and counterclockwise rotation, full 360° clockwise and counterclockwise turns, diagonal forward-left, forward-right, backward-left, backward-right, and pivoting around both the front and rear axles.

The programming structure is as follows: first, a single motor is controlled; then multiple motors are combined; next, advanced speed calculations are performed; and finally, movements are executed. The first developed code is:

/*
 * Omnidirectional robot with Mecanum wheels – Full movement sequence
 * Board: XIAO ESP32C6
 * Independent H-bridges (2 pins per motor)
 * Movements: forward/backward, lateral, in-place rotation, 360° turn,
 *            diagonals (4 directions), pivot on front/rear axis.
 * A 3-second stop is inserted between each movement.
 */

// ---------- Pins (D0 to D7) ----------
#define FL_PIN1 D0
#define FL_PIN2 D1
#define FR_PIN1 D2
#define FR_PIN2 D3
#define RL_PIN1 D4
#define RL_PIN2 D5
#define RR_PIN1 D6
#define RR_PIN2 D7

// ---------- LEDC Channels (8 channels) ----------
#define CH_FL1 0
#define CH_FL2 1
#define CH_FR1 2
#define CH_FR2 3
#define CH_RL1 4
#define CH_RL2 5
#define CH_RR1 6
#define CH_RR2 7

// ---------- PWM Parameters ----------
#define PWM_FREQ 5000
#define PWM_RES  8          // 0-255
#define SPEED    200        // Base speed (adjustable)

// Time for a full rotation (adjust through testing until 360° is achieved)
#define FULL_ROTATION_TIME_MS  2500

// ---------- Initialization ----------
void setup() {
  Serial.begin(115200);
  ledcSetup(CH_FL1, PWM_FREQ, PWM_RES); ledcAttachPin(FL_PIN1, CH_FL1);
  ledcSetup(CH_FL2, PWM_FREQ, PWM_RES); ledcAttachPin(FL_PIN2, CH_FL2);
  ledcSetup(CH_FR1, PWM_FREQ, PWM_RES); ledcAttachPin(FR_PIN1, CH_FR1);
  ledcSetup(CH_FR2, PWM_FREQ, PWM_RES); ledcAttachPin(FR_PIN2, CH_FR2);
  ledcSetup(CH_RL1, PWM_FREQ, PWM_RES); ledcAttachPin(RL_PIN1, CH_RL1);
  ledcSetup(CH_RL2, PWM_FREQ, PWM_RES); ledcAttachPin(RL_PIN2, CH_RL2);
  ledcSetup(CH_RR1, PWM_FREQ, PWM_RES); ledcAttachPin(RR_PIN1, CH_RR1);
  ledcSetup(CH_RR2, PWM_FREQ, PWM_RES); ledcAttachPin(RR_PIN2, CH_RR2);
  stopMotors();
  Serial.println("Mecanum Robot ready – full sequence");
}

// ---------- Main Loop ----------
void loop() {
  // 1-2 Basic linear movements
  Serial.println(">> Forward");       forward();              delay(3000);
  Serial.println(">> Backward");      backward();             delay(3000);
  // 3-4 Lateral translation
  Serial.println(">> Left (lateral)");left();                 delay(3000);
  Serial.println(">> Right (lateral)");right();               delay(3000);
  // 5-6 In-place rotation
  Serial.println(">> Clockwise rotation (in place)"); rotateClockwise();        delay(3000);
  Serial.println(">> Counterclockwise rotation");     rotateCounterClockwise(); delay(3000);
  // 7-8 Full 360° turns
  Serial.println(">> Full clockwise turn");           rotateFullClockwise();    delay(3000);
  Serial.println(">> Full counterclockwise turn");    rotateFullCounterClockwise(); delay(3000);
  // 9-10 Forward diagonals
  Serial.println(">> Diagonal forward-left");  diagonalForwardLeft();   delay(3000);
  Serial.println(">> Diagonal forward-right"); diagonalForwardRight();  delay(3000);
  // 11-12 Backward diagonals
  Serial.println(">> Diagonal backward-left"); diagonalBackwardLeft();  delay(3000);
  Serial.println(">> Diagonal backward-right");diagonalBackwardRight(); delay(3000);
  // 13-14 Front axis pivot
  Serial.println(">> Front pivot clockwise");       pivotFrontClockwise();        delay(3000);
  Serial.println(">> Front pivot counterclockwise");pivotFrontCounterClockwise(); delay(3000);
  // 15-16 Rear axis pivot
  Serial.println(">> Rear pivot clockwise");        pivotRearClockwise();         delay(3000);
  Serial.println(">> Rear pivot counterclockwise"); pivotRearCounterClockwise();  delay(3000);
  // Sequence repeats indefinitely
}

// =============================================
// Motor control functions
// =============================================
void setMotor(int chPin1, int chPin2, int speed) {
  speed = constrain(speed, -255, 255);
  if (speed > 0)      { ledcWrite(chPin1, speed); ledcWrite(chPin2, 0);      }
  else if (speed < 0) { ledcWrite(chPin1, 0);     ledcWrite(chPin2, -speed); }
  else                { ledcWrite(chPin1, 0);     ledcWrite(chPin2, 0);      }
}

void stopMotors() {
  setMotor(CH_FL1,CH_FL2,0); setMotor(CH_FR1,CH_FR2,0);
  setMotor(CH_RL1,CH_RL2,0); setMotor(CH_RR1,CH_RR2,0);
}

// =============================================
// Basic movements
// =============================================
void forward()              { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
void backward()             { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void left()                 { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void right()                { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
void rotateClockwise()      { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void rotateCounterClockwise(){ setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }

// =============================================
// Full 360° turns
// =============================================
void rotateFullClockwise()        { rotateClockwise();        delay(FULL_ROTATION_TIME_MS); stopMotors(); }
void rotateFullCounterClockwise() { rotateCounterClockwise(); delay(FULL_ROTATION_TIME_MS); stopMotors(); }

// =============================================
// Diagonals (classic Mecanum)
// =============================================
void diagonalForwardLeft()  { setMotor(CH_FL1,CH_FL2,0);      setMotor(CH_FR1,CH_FR2, SPEED);  setMotor(CH_RL1,CH_RL2, SPEED);  setMotor(CH_RR1,CH_RR2,0);     }
void diagonalForwardRight() { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,0);       setMotor(CH_RL1,CH_RL2,0);       setMotor(CH_RR1,CH_RR2, SPEED);}
void diagonalBackwardLeft() { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2,0);       setMotor(CH_RL1,CH_RL2,0);       setMotor(CH_RR1,CH_RR2,-SPEED);}
void diagonalBackwardRight(){ setMotor(CH_FL1,CH_FL2,0);      setMotor(CH_FR1,CH_FR2,-SPEED);  setMotor(CH_RL1,CH_RL2,-SPEED);  setMotor(CH_RR1,CH_RR2,0);     }

// =============================================
// Pivots around axes (physically correct)
// =============================================
// Applies speeds calculated with inverse kinematics and scales to avoid saturation
void setMecanumSpeeds(float vx, float vy, float omega) {
  // Geometric coefficient (adjust if real dimensions are known)
  const float L = 1.0;  // sum of (half wheelbase + half track) normalized
  float v_fl = vx - vy - L * omega;
  float v_fr = vx + vy + L * omega;
  float v_rl = vx + vy - L * omega;
  float v_rr = vx - vy + L * omega;
  // Scale if any absolute value exceeds SPEED
  float maxVal = max(max(abs(v_fl), abs(v_fr)), max(abs(v_rl), abs(v_rr)));
  if (maxVal > SPEED) {
    float scale = SPEED / maxVal;
    v_fl *= scale; v_fr *= scale; v_rl *= scale; v_rr *= scale;
  }
  setMotor(CH_FL1,CH_FL2,(int)v_fl); setMotor(CH_FR1,CH_FR2,(int)v_fr);
  setMotor(CH_RL1,CH_RL2,(int)v_rl); setMotor(CH_RR1,CH_RR2,(int)v_rr);
}

// Front pivot (rotation around the center point of the front axle)
void pivotFrontClockwise()        { setMecanumSpeeds(0, -SPEED, -SPEED); }
void pivotFrontCounterClockwise() { setMecanumSpeeds(0,  SPEED,  SPEED); }

// Rear pivot (rotation around the center point of the rear axle)
void pivotRearClockwise()         { setMecanumSpeeds(0,  SPEED, -SPEED); }
void pivotRearCounterClockwise()  { setMecanumSpeeds(0, -SPEED,  SPEED); }

This code was loaded into Arduino following the steps learned in the corresponding week, which enabled the observation of the robot's movements.

Robot Kinematics

It is important to understand the kinematic model of the robot. This robot uses standard inverse kinematics. The table below shows the movement direction of each wheel type and the combined motion they produce. Each motor receives a signed value: positive means the wheel spins forward, negative means backward, and 0 means the motor is stopped.

Movement FL (Front Left) FR (Front Right) RL (Rear Left) RR (Rear Right)
Forward++++
Backward----
Left (lateral)-++-
Right (lateral)+--+
Clockwise rotation+-+-
Counterclockwise rotation-+-+
Diagonal forward-left0++0
Diagonal forward-right+00+
Diagonal backward-left-00-
Diagonal backward-right0--0
Front pivot clockwiseGeneral formula scaled with dynamic speeds
Front pivot counterclockwiseGeneral formula scaled with dynamic speeds

This basic code provides the movement patterns needed for obstacle avoidance. However, it only allows visualization of the possible motions. The next step toward enabling actual obstacle avoidance is integrating distance sensors that can relay obstacle position data and instruct the program to move according to the type of obstacle encountered.

The following obstacle scenarios and corresponding responses are proposed:

Active SensorsInterpretationAction
NoneClear pathMove forward
CenterObstacle aheadTurn toward the side with the greater distance to the obstacle
Center + LeftFront and left blockedLateral translation to the right
Center + RightFront and right blockedLateral translation to the left
Center + Left + RightCorridor blockedRotate in place until clear
Left onlyObstacle on the leftLateral translation to the right
Right onlyObstacle on the rightLateral translation to the left

For the integration of three VL53L0X sensors, the proposed program is:

/*
 * Mecanum Robot with obstacle avoidance (3x VL53L0X)
 * Board: XIAO ESP32C6
 * Independent H-bridges + 3 front ToF sensors
 */
#include <Wire.h>
#include <Adafruit_VL53L0X.h>

// ========== Motor configuration (unchanged) ==========
#define FL_PIN1 D0
#define FL_PIN2 D1
#define FR_PIN1 D2
#define FR_PIN2 D3
#define RL_PIN1 D4
#define RL_PIN2 D5
#define RR_PIN1 D6
#define RR_PIN2 D7
#define CH_FL1 0
#define CH_FL2 1
#define CH_FR1 2
#define CH_FR2 3
#define CH_RL1 4
#define CH_RL2 5
#define CH_RR1 6
#define CH_RR2 7
#define PWM_FREQ 5000
#define PWM_RES  8
#define SPEED    200         // base speed (0-255)

// ========== Sensor configuration ==========
#define I2C_SDA        D8
#define I2C_SCL        D9
#define XSHUT_LEFT     D10
#define XSHUT_CENTER   A0
#define XSHUT_RIGHT    A1
#define SENSOR_LEFT_ADDR    0x30
#define SENSOR_CENTER_ADDR  0x31
#define SENSOR_RIGHT_ADDR   0x32
#define DISTANCE_THRESHOLD  300  // mm, threshold to consider an obstacle

Adafruit_VL53L0X sensorLeft, sensorCenter, sensorRight;

// ========== Motor functions (identical to above) ==========
void setMotor(int chPin1, int chPin2, int speed) {
  speed = constrain(speed, -255, 255);
  if (speed > 0)      { ledcWrite(chPin1, speed); ledcWrite(chPin2, 0);      }
  else if (speed < 0) { ledcWrite(chPin1, 0);     ledcWrite(chPin2, -speed); }
  else                { ledcWrite(chPin1, 0);     ledcWrite(chPin2, 0);      }
}
void stopMotors()             { setMotor(CH_FL1,CH_FL2,0); setMotor(CH_FR1,CH_FR2,0); setMotor(CH_RL1,CH_RL2,0); setMotor(CH_RR1,CH_RR2,0); }
void forward()                { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
void left()                   { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void right()                  { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
void rotateClockwise()        { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void rotateCounterClockwise() { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }

// ========== Sensor initialization with XSHUT ==========
void initVL53L0X() {
  // 1. Turn off all sensors
  pinMode(XSHUT_LEFT, OUTPUT);   pinMode(XSHUT_CENTER, OUTPUT);  pinMode(XSHUT_RIGHT, OUTPUT);
  digitalWrite(XSHUT_LEFT, LOW); digitalWrite(XSHUT_CENTER, LOW); digitalWrite(XSHUT_RIGHT, LOW);
  delay(10);
  // 2. Initialize left sensor
  digitalWrite(XSHUT_LEFT, HIGH); delay(10);
  sensorLeft.begin(SENSOR_LEFT_ADDR);
  sensorLeft.setMeasurementTimingBudgetMicroSeconds(20000);
  sensorLeft.startRangeContinuous();
  // 3. Initialize center sensor
  digitalWrite(XSHUT_CENTER, HIGH); delay(10);
  sensorCenter.begin(SENSOR_CENTER_ADDR);
  sensorCenter.setMeasurementTimingBudgetMicroSeconds(20000);
  sensorCenter.startRangeContinuous();
  // 4. Initialize right sensor
  digitalWrite(XSHUT_RIGHT, HIGH); delay(10);
  sensorRight.begin(SENSOR_RIGHT_ADDR);
  sensorRight.setMeasurementTimingBudgetMicroSeconds(20000);
  sensorRight.startRangeContinuous();
}

// ========== Distance reading ==========
void readSensors(uint16_t &distLeft, uint16_t &distCenter, uint16_t &distRight) {
  if (sensorLeft.isRangeComplete())   distLeft   = sensorLeft.readRange();
  if (sensorCenter.isRangeComplete()) distCenter = sensorCenter.readRange();
  if (sensorRight.isRangeComplete())  distRight  = sensorRight.readRange();
}

// ========== Obstacle avoidance logic ==========
void avoidObstacles() {
  uint16_t dL, dC, dR;
  readSensors(dL, dC, dR);
  bool obsLeft   = (dL > 0 && dL < DISTANCE_THRESHOLD);
  bool obsCenter = (dC > 0 && dC < DISTANCE_THRESHOLD);
  bool obsRight  = (dR > 0 && dR < DISTANCE_THRESHOLD);

  if (!obsLeft && !obsCenter && !obsRight) {
    // Clear path
    forward();
  }
  else if (obsCenter && obsLeft && obsRight) {
    // Total blockage: rotate in place until an exit is found
    rotateCounterClockwise();
  }
  else if (obsCenter && obsLeft && !obsRight) {
    // Obstacle ahead and to the left -> escape to the right
    right();
  }
  else if (obsCenter && !obsLeft && obsRight) {
    // Obstacle ahead and to the right -> escape to the left
    left();
  }
  else if (obsCenter && !obsLeft && !obsRight) {
    // Only a frontal obstacle: decide which way to turn
    if (dL > dR) { rotateCounterClockwise(); } else { rotateClockwise(); }
  }
  else if (!obsCenter && obsLeft && !obsRight) {
    // Obstacle only to the left -> move right
    right();
  }
  else if (!obsCenter && !obsLeft && obsRight) {
    // Obstacle only to the right -> move left
    left();
  }
  else {
    // Default case (unlikely)
    forward();
  }
}

// ========== Setup ==========
void setup() {
  Serial.begin(115200);
  // Configure I2C with custom pins
  Wire.begin(I2C_SDA, I2C_SCL);
  // Configure LEDC channels and motors (same as before)
  ledcSetup(CH_FL1, PWM_FREQ, PWM_RES); ledcAttachPin(FL_PIN1, CH_FL1);
  ledcSetup(CH_FL2, PWM_FREQ, PWM_RES); ledcAttachPin(FL_PIN2, CH_FL2);
  ledcSetup(CH_FR1, PWM_FREQ, PWM_RES); ledcAttachPin(FR_PIN1, CH_FR1);
  ledcSetup(CH_FR2, PWM_FREQ, PWM_RES); ledcAttachPin(FR_PIN2, CH_FR2);
  ledcSetup(CH_RL1, PWM_FREQ, PWM_RES); ledcAttachPin(RL_PIN1, CH_RL1);
  ledcSetup(CH_RL2, PWM_FREQ, PWM_RES); ledcAttachPin(RL_PIN2, CH_RL2);
  ledcSetup(CH_RR1, PWM_FREQ, PWM_RES); ledcAttachPin(RR_PIN1, CH_RR1);
  ledcSetup(CH_RR2, PWM_FREQ, PWM_RES); ledcAttachPin(RR_PIN2, CH_RR2);
  stopMotors();
  // Initialize the three sensors
  initVL53L0X();
  Serial.println("Sensors OK. Starting avoidance...");
}

void loop() {
  avoidObstacles();
  delay(50);  // Small pause for stability
}

Future Work

Based on the initial design, a potential project extension is proposed, focused on the development of a gas-mapping robot operating within a controlled environment. This extension is conceived as an enhancement and expansion of the base system, with potential applications in environmental monitoring and the analysis of current real-world problems.

Design for the Gas-Mapping Robot

The gas-mapping robot will be based on the initial autonomous vehicle design, with modifications to integrate additional sensors and functionalities. The main components of the system will include:

Design Proposal for Gas-Mapping Robot Design Proposal for Gas-Mapping Robot

Schedule for Future Work: Robot Upgrade to Gas-Mapping System

Fablab Week Main Tasks Estimated Time
Week 1. Project management
  • Definition of project extension proposal
1 day
Week 2. Computer Aided Design
  • Design of a chassis with modular slots for adding or removing components
2-3 days
Week 3. Computer Controlled Cutting
  • Exploration of alternative materials such as MDF and acrylic
  • Design of a logo or project name on structural materials
1-2 days
Week 4. Electronic Production
  • Design and fabrication of a PCB integrating the ESP32 microcontroller, power regulators, and RGB status LEDs
4-5 days
Week 5. 3D Scanning and printing
  • Design and printing of TPU shock absorbers and a sealed enclosure
2-3 days
Week 6. Embedded Programming
  • Implementation of an exploration algorithm
  • Programming of integrated sensor data acquisition
  • Data storage programming in CSV format via SD card or WiFi
5-6 days
Week 7. Computer Controlled Machining
  • Milling of an aluminum base plate for structural support, weight distribution, and heat dissipation
2-3 days
Week 8. Electronics Design
  • Design of a circuit including battery backup port, overvoltage protection, and real-time power consumption monitoring
4-6 days
Week 9. Output Devices
  • Implementation of a telemetry system using an OLED display to show battery level, operating mode, and system status
3-5 days
Week 10. Mechanical Design
  • Improvement of the traction system using high-grip wheels
  • Implementation of a self-leveling mechanism
3-5 days
Week 11. Input Devices
  • Integration of MQ-135 (smoke), SGP30 (CO₂/VOCs), and DHT22 (temperature and humidity) sensors
  • Data collection through a training mode in which the robot navigates a defined space to generate a dataset
3-5 days
Week 12. Molding and Casting
  • Fabrication of protective enclosures for components
2-5 days
Week 13. Embedded Nettworking and Communications
  • Creation of a local SD card backup database
  • Transfer of datasets to a PC for offline processing
3-7 days
Week 14. Interface and Application Programming
  • Development of a 3D heat map
  • Dashboard creation with real-time graphs
  • Prediction of areas with high smoke concentration within 5 to 10 minutes
  • Display of model confidence
4-5 days

Bill of Materials for Future Work

Componente Quantity Cost (MXN) Application
MQ-135 Gas Sensor 1 $53 Gas and smoke detection
SGP30 Sensor 1 $199 CO2 and VOC detection
DHT22 Sensor 1 $135 Temperature and humidity measurement
SD Card Module 1 $49 Local data storage
OLED Display 0.96" 3* $103 Visualization of robot data
Motor Rotary Encoders 2* $165 Estimation of traveled distance
SD Card Reader Module 1 $245 Local data storage
MicroSD Card 16 GB 1 set $57 Data storage
Acrylic Sheets Various $91 Enclosure for controlled environment

Spiral development

As previously discussed, this autonomous mobile robot presents multiple opportunities for future improvement and expansion. Based on this perspective, a Spiral Development is proposed as a visual tool to outline potential milestones and long term development goals, assuming the successful completion of the core project objectives.

Spiral Development