Project Roadmap
Part 1 - Experimental Smart Assistive Platform for Elderly and Disabled People
Part 3 - Wireless Command and H-Bridge Direct Drive
Part 4 - Introducing Autonomous Line Following (TCRT5000)
Part 5 - Non-Contact Proactive Shielding (HC-SR04 Range Finder)
Part 6 - Strict Priority Hierarchy with Tactile Mechanical Bumpers
Part 7 - Mobile Robot Control and Live Video Streaming



Essential Code Snippets Related to the Ultrasonic Sensor
1. Hardware Pin Definitions
Pin assignments for the HC-SR04 sonar module:
// Ultrasonic HC-SR04 Sonar Pins
const int TRIG_PIN = 4;
const int ECHO_PIN = 3;
2. Pin Setup in setup()
Configures TRIG_PIN to output pulses and ECHO_PIN to listen for returning sound waves:
// Ultrasonic Sonar pins configuration
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
3. Distance Measurement Routine (getDistance())
Generates a 10us ultrasonic burst and measures echo response time to calculate distance in centimeters. Includes a 30 ms hardware timeout to prevent code execution freezes:
float getDistance() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// 30ms hardware timeout constraint to avoid freezing the main execution loop
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
float d = duration * 0.0343 / 2;
if (d == 0) return 999.0; // Return out-of-bounds constant on reading error
return d;
}
4. Obstacle Avoidance Logic in loop()
When operating in MODE_AUTONOMOUS, distance is checked before line tracking logic. If an obstacle is detected closer than 30 cm, line tracking is temporarily overridden, and the robot performs an evasive left turn:
else if (currentMode == MODE_AUTONOMOUS) {
float distance = getDistance();
// Obstacle avoidance matching Elecrow's motor logic and 0.3s timing
if (distance < 30.0) {
Serial.println("Obstacle detected! Turning left...");
driveMotors(MOTOR_SPEED, 0, 0, MOTOR_SPEED); // Spin Left in place
delay(300); // Turn duration
}
else {
// Standard Line Tracking routine executes here...
}
}
#include <Arduino.h>
#include <Arduino_LED_Matrix.h> // Library for the 8x13 LED matrix
Arduino_LED_Matrix matrix; // Initialize LED matrix object
// L9110S Motor Driver Pins
const int MOTOR_PIN_A1 = 5;
const int MOTOR_PIN_A2 = 6;
const int MOTOR_PIN_B1 = 9;
const int MOTOR_PIN_B2 = 10;
// Infrared Receiver Pin
const int IR_RECEIVE_PIN = 2;
// Line Tracking Sensor Pins
const int TRACKING_PIN_L = A2;
const int TRACKING_PIN_R = A3;
// Ultrasonic HC-SR04 Sonar Pins
const int TRIG_PIN = 4;
const int ECHO_PIN = 3;
enum RobotMode {
MODE_MANUAL,
MODE_AUTONOMOUS
};
RobotMode currentMode = MODE_MANUAL;
// Speed settings
const int MOTOR_SPEED = 200;
const int TRACK_SPEED = 160;
byte lastCommand = 0;
// LED Matrix Frame Buffer Size (104 pixels)
const uint8_t FRAME_SIZE = 8 * 13;
// --- LED MATRIX ARROW & ICON ARRAYS (Brightness levels 0-7) ---
uint8_t arrow_up[FRAME_SIZE] = {
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0,
0, 0, 0, 7, 7, 0, 7, 0, 7, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0
};
uint8_t arrow_down[FRAME_SIZE] = {
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0,
0, 0, 0, 7, 7, 0, 7, 0, 7, 7, 0, 0, 0,
0, 0, 0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0,
0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0
};
uint8_t arrow_left[FRAME_SIZE] = {
0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0
};
uint8_t arrow_right[FRAME_SIZE] = {
0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0
};
uint8_t stop_icon[FRAME_SIZE] = { 0 };
// --- ONBOARD RGB LED FUNCTIONS ---
void set_led3_color(int r, int g, int b) {
analogWrite(LED3_R, r);
analogWrite(LED3_G, g);
analogWrite(LED3_B, b);
}
void set_led4_color(bool r, bool g, bool b) {
digitalWrite(LED4_R, r ? LOW : HIGH);
digitalWrite(LED4_G, g ? LOW : HIGH);
digitalWrite(LED4_B, b ? LOW : HIGH);
}
// Elecrow IR Decoder
long readElecrowIR() {
int count = 0;
while (digitalRead(IR_RECEIVE_PIN) == LOW && count < 200) { count++; delayMicroseconds(60); }
if (count >= 200) return -1;
count = 0;
while (digitalRead(IR_RECEIVE_PIN) == HIGH && count < 80) { count++; delayMicroseconds(60); }
if (count >= 80) return -1;
int idx = 0, cnt = 0;
byte data[4] = {0, 0, 0, 0};
for (int i = 0; i < 32; i++) {
count = 0;
while (digitalRead(IR_RECEIVE_PIN) == LOW && count < 15) { count++; delayMicroseconds(60); }
count = 0;
while (digitalRead(IR_RECEIVE_PIN) == HIGH && count < 40) { count++; delayMicroseconds(60); }
if (count > 8) data[idx] |= (1 << cnt);
if (cnt == 7) { cnt = 0; idx++; } else { cnt++; }
}
if ((byte)(data[0] + data[1]) == 0xFF && (byte)(data[2] + data[3]) == 0xFF) {
return data[2];
}
return -1;
}
void driveMotors(int a1, int a2, int b1, int b2) {
analogWrite(MOTOR_PIN_A1, a1);
analogWrite(MOTOR_PIN_A2, a2);
analogWrite(MOTOR_PIN_B1, b1);
analogWrite(MOTOR_PIN_B2, b2);
}
float getDistance() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
float d = duration * 0.0343 / 2;
if (d == 0) return 999.0;
return d;
}
void executeCommand(byte command) {
switch (command) {
case 0x1C: // OK Button - Toggle Manual / Autonomous Operations
if (currentMode == MODE_MANUAL) {
currentMode = MODE_AUTONOMOUS;
Serial.println("Mode changed: AUTONOMOUS (Tracking + Sonar)");
}
else {
currentMode = MODE_MANUAL;
driveMotors(0, 0, 0, 0);
set_led4_color(false, false, false);
set_led3_color(0, 0, 0);
matrix.draw(stop_icon);
Serial.println("Mode changed: MANUAL (IR Control)");
}
lastCommand = 0;
delay(500);
break;
case 0x18: // Forward
if (currentMode == MODE_MANUAL) {
driveMotors(0, MOTOR_SPEED, 0, MOTOR_SPEED);
set_led4_color(false, true, false); // Green
set_led3_color(0, 200, 0);
matrix.draw(arrow_up);
}
break;
case 0x08: // Spin Left
if (currentMode == MODE_MANUAL) {
driveMotors(MOTOR_SPEED, 0, 0, MOTOR_SPEED);
set_led4_color(false, false, true); // Blue
set_led3_color(0, 0, 200);
matrix.draw(arrow_left);
}
break;
case 0x5A: // Spin Right
if (currentMode == MODE_MANUAL) {
driveMotors(0, MOTOR_SPEED, MOTOR_SPEED, 0);
set_led4_color(false, false, true); // Blue
set_led3_color(0, 0, 200);
matrix.draw(arrow_right);
}
break;
case 0x52: // Backward
if (currentMode == MODE_MANUAL) {
driveMotors(MOTOR_SPEED, 0, MOTOR_SPEED, 0);
set_led4_color(true, false, false); // Red
set_led3_color(200, 0, 0);
matrix.draw(arrow_down);
}
break;
default:
if (currentMode == MODE_MANUAL) {
driveMotors(0, 0, 0, 0);
set_led4_color(false, false, false);
set_led3_color(0, 0, 0);
matrix.draw(stop_icon);
}
break;
}
}
void setup() {
Serial.begin(115200);
pinMode(IR_RECEIVE_PIN, INPUT_PULLUP);
pinMode(TRACKING_PIN_L, INPUT_PULLUP);
pinMode(TRACKING_PIN_R, INPUT_PULLUP);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
// RGB LED Pin Configuration
pinMode(LED4_R, OUTPUT);
pinMode(LED4_G, OUTPUT);
pinMode(LED4_B, OUTPUT);
set_led3_color(0, 0, 0);
set_led4_color(false, false, false);
// Initialize LED Matrix
matrix.begin();
matrix.setGrayscaleBits(3); // 8 brightness levels (0-7)
matrix.clear();
Serial.println("System Ready on Arduino UNO Q (IR + Line Tracking + Sonar + Matrix/LEDs).");
}
void loop() {
// 1. Process incoming IR signals
if (digitalRead(IR_RECEIVE_PIN) == LOW) {
long result = readElecrowIR();
if (result != -1) {
lastCommand = (byte)result;
executeCommand(lastCommand);
}
}
// 2. Continuous Mode Execution
if (currentMode == MODE_MANUAL) {
if (digitalRead(IR_RECEIVE_PIN) == HIGH) {
driveMotors(0, 0, 0, 0);
set_led4_color(false, false, false);
set_led3_color(0, 0, 0);
matrix.draw(stop_icon);
} else {
executeCommand(lastCommand);
}
}
else if (currentMode == MODE_AUTONOMOUS) {
float distance = getDistance();
// Priority 1: Ultrasonic Obstacle Avoidance
if (distance < 30.0) {
Serial.println("Obstacle detected! Evading left...");
driveMotors(MOTOR_SPEED, 0, 0, MOTOR_SPEED); // Spin Left in place
set_led4_color(true, false, false); // Red warning
set_led3_color(200, 0, 0);
matrix.draw(arrow_left); // Indicate left evasion turn
delay(300); // Turn duration
}
// Priority 2: Line Tracking Routine
else {
int trackL = digitalRead(TRACKING_PIN_L);
int trackR = digitalRead(TRACKING_PIN_R);
int trackState = (trackL * 2) + trackR;
switch (trackState) {
case 0: // Off track -> Stop
driveMotors(0, 0, 0, 0);
set_led4_color(false, false, false);
set_led3_color(0, 0, 0);
matrix.draw(stop_icon);
break;
case 1: // Right sensor active -> Turn Right
driveMotors(0, TRACK_SPEED, TRACK_SPEED, 0);
set_led4_color(false, false, true); // Blue
set_led3_color(0, 0, 200);
matrix.draw(arrow_right);
break;
case 2: // Left sensor active -> Turn Left
driveMotors(TRACK_SPEED, 0, 0, TRACK_SPEED);
set_led4_color(false, false, true); // Blue
set_led3_color(0, 0, 200);
matrix.draw(arrow_left);
break;
case 3: // Both sensors active -> Move Forward
driveMotors(0, TRACK_SPEED, 0, TRACK_SPEED);
set_led4_color(false, true, false); // Green
set_led3_color(0, 200, 0);
matrix.draw(arrow_up);
break;
}
delay(10);
}
}
}