Line tracking of ALR:
// ALR Line Tracking Code - Autonomous Logistics Robot
// Platform: Arduino Mega 2560 (C++) with 8x QTR Analog Sensors
and PD Control
//
// This code uses 8 analog QTR sensors to calculate a precise
line position (error)
// and applies a Proportional-Derivative (PD) controller for
smooth, fast tracking.
// The error range is -7000 (far left) to +7000 (far right), with
0 being perfectly centered.
// --- 1. CONFIGURATION: PIN DEFINITIONS ---
// Define QTR Sensor Pins (Analog Pins on Arduino Mega)
// Assuming QTR-8RC or similar module connected to Analog pins
const int QTR_PINS[8] = {A0, A1, A2, A3, A4, A5, A6, A7};
const int NUM_SENSORS = 8;
// Define Motor Driver Pins (2 x IBT-2 / BTS7960)
// IBT-2 requires PWM pin (RPWM/LPWM) and Enable pin (EN).
// We use the same PWM pin for forward/reverse control, setting
the other to LOW.
// Use Mega PWM pins (2-13, 44-46) for EN/PWM.
const int L_PWM = 3; // Left Motor PWM (RPWM/LPWM combined
logic)
const int L_EN = 38; // Left Motor Enable
const int R_PWM = 2; // Right Motor PWM (RPWM/LPWM combined
logic)
const int R_EN = 39; // Right Motor Enable
// Barcode Scanner (MC3000) - Assuming Serial connection
// We'll use Serial3 on the Mega for dedicated communication
(Pins 14 TX3, 15 RX3)
// For now, we will use the default Serial (pins 0, 1) for
simplicity and debugging.
const int BARCODE_SERIAL_PORT = 0; // Placeholder: Using default
Serial for reading
// Start Switch (Digital Input with Pullup)
const int START_SWITCH_PIN = 40; // Digital pin for the start
switch
// Lifting Mechanism Motor Pins (Assuming another IBT-2 or relay)
const int LIFT_PWM = 44; // PWM pin for lift speed control
const int LIFT_IN1 = 45; // Direction 1
const int LIFT_IN2 = 46; // Direction 2
// Define Motor Speeds (0-255)
const int BASE_SPEED = 180; // Base cruising speed (adjust for
24V motors)
const int MAX_SPEED = 255;
const int MIN_SPEED = 50; // Keep a minimum speed to prevent
stalling
// --- 2. PD CONTROL CONSTANTS ---
const float Kp = 0.08; // Proportional Gain: Must be tuned
carefully. Start small.
const float Kd = 0.05; // Derivative Gain: Start very small.
// --- 3. GLOBAL VARIABLES ---
float last_error = 0;
// Arrays to store calibration data for each sensor
int cal_min[NUM_SENSORS]; // Stores the minimum (darkest) reading
for each sensor
int cal_max[NUM_SENSORS]; // Stores the maximum (lightest)
reading for each sensor
// --- 4. MOTOR CONTROL FUNCTIONS ---
void set_motor_speeds(int left_pwm, int right_pwm) {
// IBT-2 requires the PWM pin to be driven high/low for
direction, and EN to be high.
digitalWrite(L_EN, HIGH);
digitalWrite(R_EN, HIGH);
// Note: IBT-2 drivers often use separate RPWM/LPWM.
// Assuming a simplified setup where we use one PWM pin and
rely on separate direction pins.
// For the actual IBT-2 (BTS7960), you should typically connect
L_PWM to LPWM and R_PWM to RPWM.
// The logic below assumes L_PWM/R_PWM are connected to the
main PWM input for speed.
// We ensure both motors are set to move FORWARD.
// For BTS7960, if RPWM=PWM and LPWM=0, it moves Forward (and
vice versa for reverse).
// The following is a simplified, common pin configuration for
IBT-2 with two PWM pins:
analogWrite(L_PWM, left_pwm);
analogWrite(R_PWM, right_pwm);
}
void stop_motors() {
digitalWrite(L_EN, LOW);
digitalWrite(R_EN, LOW);
analogWrite(L_PWM, 0);
analogWrite(R_PWM, 0);
}
void control_lifting_mechanism(int lift_speed, bool direction_up)
{
// Use a simple DC motor control for the lifting mechanism
analogWrite(LIFT_PWM, abs(lift_speed));
if (direction_up) {
digitalWrite(LIFT_IN1, HIGH);
digitalWrite(LIFT_IN2, LOW);
} else {
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, HIGH);
}
}
// --- 5. CALIBRATION FUNCTION ---
void calibrate_sensors() {
stop_motors();
[Link]("------------------------------------");
[Link]("CALIBRATION PHASE: 10 seconds");
[Link]("SLOWLY move the sensors over the line and
surface for 10s.");
[Link]("------------------------------------");
// Initialize min/max arrays
for (int i = 0; i < NUM_SENSORS; i++) {
cal_min[i] = 1023; // Start with max value
cal_max[i] = 0; // Start with min value
}
unsigned long startTime = millis();
while (millis() - startTime < 10000) { // Run for 10 seconds
for (int i = 0; i < NUM_SENSORS; i++) {
int reading = analogRead(QTR_PINS[i]);
// Update min/max values
if (reading < cal_min[i]) {
cal_min[i] = reading;
}
if (reading > cal_max[i]) {
cal_max[i] = reading;
}
}
delay(50); // Small delay to allow time for movement and
reading stability
}
[Link]("CALIBRATION COMPLETE. Readings:");
[Link]("Min (White): ");
for (int i = 0; i < NUM_SENSORS; i++)
[Link](String(cal_min[i]) + " ");
[Link]();
[Link]("Max (Black): ");
for (int i = 0; i < NUM_SENSORS; i++)
[Link](String(cal_max[i]) + " ");
[Link]("\n------------------------------------");
}
// --- 6. BARCODE SCANNER FUNCTION (Placeholder) ---
// Note: The MC3000 is a portable data collector and typically
sends data via
// a physical serial cable or wireless link. This is a simple
placeholder
// to read from the serial port where the barcode data might
arrive.
void check_barcode_scanner() {
if ([Link]() > 0) {
String barcode_data = [Link]('\n');
[Link]("BARCODE SCANNED: ");
[Link](barcode_data);
// TODO: Add logic here (e.g., stop robot, parse data, set
destination)
stop_motors();
// Example: control_lifting_mechanism(200, true); // Lift
cargo
delay(5000); // Wait for action
}
}
// --- 7. SETUP ---
void setup() {
// Motor Driver Pins
pinMode(L_PWM, OUTPUT);
pinMode(L_EN, OUTPUT);
pinMode(R_PWM, OUTPUT);
pinMode(R_EN, OUTPUT);
// Lifting Mechanism Pins
pinMode(LIFT_PWM, OUTPUT);
pinMode(LIFT_IN1, OUTPUT);
pinMode(LIFT_IN2, OUTPUT);
// Start Switch Pin (Internal pullup activated)
pinMode(START_SWITCH_PIN, INPUT_PULLUP);
// Sensor Pins are analog inputs by default, no need to set
pinMode.
[Link](9600);
[Link]("ALR Mega PD Line Tracker Initialized!");
// Perform calibration
calibrate_sensors();
// Wait for the start switch to be pressed (LOW signal when
pressed with PULLUP)
[Link]("WAITING FOR START SWITCH...");
while (digitalRead(START_SWITCH_PIN) == HIGH) {
// Wait until switch is pressed
delay(10);
}
[Link]("START SWITCH ACTIVATED. STARTING ROBOT.");
delay(1000); // Small delay after start to move hand
}
// --- 8. MAIN LOOP ---
void loop() {
// Always check for barcode data first, as it's a mission-
critical trigger
check_barcode_scanner();
// 1. READ & NORMALIZE SENSORS
// This calculates the line position as a single, smooth error
value.
long sum_weighted_values = 0; // The numerator for the weighted
average
long sum_raw_values = 0; // The denominator (total
reflectance)
float normalized_values[NUM_SENSORS];
for (int i = 0; i < NUM_SENSORS; i++) {
int raw_reading = analogRead(QTR_PINS[i]);
// Normalize reading (0 = White/Max Reflectance, 1 =
Black/Min Reflectance)
// Note: If max and min are the same (no range), set
normalized to 0.5 to avoid division by zero.
float range = (float)(cal_max[i] - cal_min[i]);
if (range == 0) range = 1;
// Calculate normalized value (0.0=White to 1.0=Black)
normalized_values[i] = constrain((raw_reading - cal_min[i]) /
range, 0.0, 1.0);
// Apply weights (e.g., -7000, -5000, -3000, -1000, 1000,
3000, 5000, 7000)
// Sensor index 0 is far left, index 7 is far right.
int position_weight = (i * 2000) - 7000; // Weights from -
7000 to +7000 (steps of 2000)
sum_weighted_values += (long)(normalized_values[i] *
position_weight);
sum_raw_values += (long)(normalized_values[i] * 1000); //
Scale up for precision
}
float error = 0;
if (sum_raw_values > 1000) { // Check if we are over the line
(ignore noise)
error = (float)sum_weighted_values / sum_raw_values;
} else {
// If we lose the line, stop or search
stop_motors();
[Link]("Action: STOP (Lost line)");
last_error = 0;
return;
}
// 2. CALCULATE PD CORRECTION
// P-Term (Proportional):
float P_term = Kp * error;
// D-Term (Derivative):
float derivative = error - last_error;
float D_term = Kd * derivative;
// Total Correction
float motor_correction = P_term + D_term;
// 3. APPLY CORRECTION TO MOTORS
// Correction slows down the wheel on the side the line is on
(i.e., reduces turning)
int left_speed = BASE_SPEED - (int)motor_correction;
int right_speed = BASE_SPEED + (int)motor_correction;
// 4. CONSTRAIN SPEEDS
left_speed = constrain(left_speed, MIN_SPEED, MAX_SPEED);
right_speed = constrain(right_speed, MIN_SPEED, MAX_SPEED);
// 5. EXECUTE MOVEMENT
set_motor_speeds(left_speed, right_speed);
// 6. STORE CURRENT ERROR
last_error = error;
// 7. DEBUGGING OUTPUT
[Link]("Error: ");
[Link](error);
[Link](" | L Speed: ");
[Link](left_speed);
[Link](" | R Speed: ");
[Link](right_speed);
}
LIne following with obstacle avoiding :
// ALR Line Tracking Code - Autonomous Logistics Robot
// Platform: Arduino Mega 2560 (C++) with 8x QTR Analog Sensors,
PD Control,
// and Obstacle Detour Logic (No Extra Sensors, based on video
and rules)
//
// The robot detects an obstacle when the line is abruptly lost
(all sensors read white).
// It then executes a fixed, timed detour to bypass the obstacle
and re-acquire the line.
// --- 1. CONFIGURATION: PIN DEFINITIONS ---
// Line Sensor Pins (Analog Pins)
const int QTR_PINS[8] = {A0, A1, A2, A3, A4, A5, A6, A7};
const int NUM_SENSORS = 8;
// Motor Driver Pins (2 x IBT-2 / BTS7960)
const int L_PWM = 3; // Left Motor PWM
const int L_EN = 38; // Left Motor Enable
const int R_PWM = 2; // Right Motor PWM
const int R_EN = 39; // Right Motor Enable
// Barcode Scanner & Switch Pins
// Assuming a camera module or similar QR/Barcode reader that
communicates over Serial
const int START_SWITCH_PIN = 40; // Digital pin for the start
switch
// Lifting Mechanism Motor Pins (Placeholder structure)
const int LIFT_PWM = 44;
const int LIFT_IN1 = 45;
const int LIFT_IN2 = 46;
// Define Motor Speeds (0-255)
const int BASE_SPEED = 180;
const int MAX_SPEED = 255;
const int MIN_SPEED = 50;
const int DETOUR_SPEED = 150; // Slower speed for controlled
detour
// --- 2. OBSTACLE DETOUR CONFIGURATION ---
// These timing values must be tuned based on your robot's size
and speed.
// Assuming the robot detours left to go around the obstacle.
const int DETOUR_TURN_TIME_MS = 300; // Time for the 90-degree
turn
const int DETOUR_FORWARD_TIME_MS = 1500; // Time to drive past
the obstacle
const int DETOUR_RE_ACQUIRE_TURN_MS = 600; // Time to turn back
and search for the line
// Threshold to detect line loss (all sensors reading
white/light)
const long LINE_LOST_THRESHOLD = 800; // If total normalized
reading (0-8000) is below this, the line is lost.
// --- 3. PD CONTROL CONSTANTS ---
const float Kp = 0.08;
const float Kd = 0.05;
// --- 4. GLOBAL VARIABLES ---
float last_error = 0;
int cal_min[NUM_SENSORS];
int cal_max[NUM_SENSORS];
bool is_avoiding = false; // State flag for the avoidance
sequence
// --- 5. MOTOR CONTROL FUNCTIONS ---
void set_motor_speeds(int left_pwm, int right_pwm) {
digitalWrite(L_EN, HIGH);
digitalWrite(R_EN, HIGH);
analogWrite(L_PWM, left_pwm);
analogWrite(R_PWM, right_pwm);
}
void stop_motors() {
digitalWrite(L_EN, LOW);
digitalWrite(R_EN, LOW);
analogWrite(L_PWM, 0);
analogWrite(R_PWM, 0);
}
void turn_robot(int duration_ms, int left_speed, int right_speed)
{
// Utility function to execute a timed movement
set_motor_speeds(left_speed, right_speed);
delay(duration_ms);
stop_motors();
}
void control_lifting_mechanism(int lift_speed, bool direction_up)
{
// Placeholder: control for the lift motor
analogWrite(LIFT_PWM, abs(lift_speed));
if (direction_up) {
digitalWrite(LIFT_IN1, HIGH);
digitalWrite(LIFT_IN2, LOW);
} else {
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, HIGH);
}
}
// --- 6. SENSOR AND CALIBRATION FUNCTIONS ---
void calibrate_sensors() {
stop_motors();
[Link]("------------------------------------");
[Link]("CALIBRATION PHASE: 10 seconds");
[Link]("SLOWLY move the sensors over the line and
surface for 10s.");
[Link]("------------------------------------");
for (int i = 0; i < NUM_SENSORS; i++) {
cal_min[i] = 1023;
cal_max[i] = 0;
}
unsigned long startTime = millis();
while (millis() - startTime < 10000) {
for (int i = 0; i < NUM_SENSORS; i++) {
int reading = analogRead(QTR_PINS[i]);
if (reading < cal_min[i]) cal_min[i] = reading;
if (reading > cal_max[i]) cal_max[i] = reading;
}
delay(50);
}
[Link]("CALIBRATION COMPLETE.");
[Link]("------------------------------------");
}
// --- 7. BARCODE SCANNER FUNCTION (Placeholder) ---
void check_barcode_scanner() {
// This uses the Arduino's primary Serial port for
demonstration.
// In a real robot, this would be a separate Serial port (e.g.,
Serial1 on Mega)
// connected to the QR code reader/camera module.
if ([Link]() > 0) {
String barcode_data = [Link]('\n');
[Link]("BARCODE SCANNED: ");
[Link](barcode_data);
stop_motors();
// Action: Read command, perform pickup/dropoff sequence
(e.g., Lift cargo)
// control_lifting_mechanism(200, true);
delay(5000);
// Clear buffer after action
while([Link]() > 0) [Link]();
}
}
// --- 8. OBSTACLE DETOUR LOGIC ---
void execute_detour() {
[Link]("!!! LINE LOST / OBSTACLE DETECTED. STARTING
DETOUR SEQUENCE !!!");
is_avoiding = true;
stop_motors();
delay(500);
// STEP 1: Turn Left (90 degrees, to move parallel to the
obstacle)
// Robot pivots left (Left wheel reverse/stop, Right wheel
forward)
[Link]("Detour Step 1: Turning Left...");
turn_robot(DETOUR_TURN_TIME_MS, -DETOUR_SPEED, DETOUR_SPEED);
// STEP 2: Move Forward (To drive past the obstacle)
[Link]("Detour Step 2: Moving Forward...");
turn_robot(DETOUR_FORWARD_TIME_MS, DETOUR_SPEED, DETOUR_SPEED);
// STEP 3: Turn Right (90+ degrees, to turn back and search for
the line)
// Robot pivots right (Left wheel forward, Right wheel
reverse/stop)
[Link]("Detour Step 3: Turning Right to Re-acquire
Line...");
turn_robot(DETOUR_RE_ACQUIRE_TURN_MS, DETOUR_SPEED, -
DETOUR_SPEED);
is_avoiding = false;
[Link]("!!! DETOUR COMPLETE. RESUMING LINE
TRACKING. !!!");
last_error = 0; // Reset error after detour
}
// --- 9. SETUP ---
void setup() {
// Configure Motor Pins
pinMode(L_PWM, OUTPUT); pinMode(L_EN, OUTPUT);
pinMode(R_PWM, OUTPUT); pinMode(R_EN, OUTPUT);
// Configure Lifting Mechanism Pins
pinMode(LIFT_PWM, OUTPUT);
pinMode(LIFT_IN1, OUTPUT);
pinMode(LIFT_IN2, OUTPUT);
// Configure Start Switch Pin
pinMode(START_SWITCH_PIN, INPUT_PULLUP);
[Link](9600);
[Link]("ALR Mega PD Line Tracker with Detour Logic
Initialized!");
// Perform calibration
calibrate_sensors();
// Wait for the start switch
[Link]("WAITING FOR START SWITCH...");
while (digitalRead(START_SWITCH_PIN) == HIGH) {
delay(10);
}
[Link]("START SWITCH ACTIVATED. STARTING ROBOT.");
delay(1000);
}
// --- 10. MAIN LOOP ---
void loop() {
// 1. CRITICAL CHECK: BARCODE SCANNER
check_barcode_scanner();
// 2. READ & NORMALIZE SENSORS
long sum_weighted_values = 0;
long sum_normalized_values = 0; // Sum of normalized values (0
to 8000)
float normalized_values[NUM_SENSORS];
bool on_line = false;
for (int i = 0; i < NUM_SENSORS; i++) {
int raw_reading = analogRead(QTR_PINS[i]);
float range = (float)(cal_max[i] - cal_min[i]);
if (range == 0) range = 1;
// Calculate normalized value (0.0=White to 1.0=Black)
// Scaling this by 1000 for easier thresholding/math later
normalized_values[i] = constrain((raw_reading - cal_min[i]) /
range, 0.0, 1.0);
// Check if at least one sensor sees black (i.e., we are
still on the line)
if (normalized_values[i] > 0.5) on_line = true;
// Apply weights (-7000 to +7000)
int position_weight = (i * 2000) - 7000;
sum_weighted_values += (long)(normalized_values[i] *
position_weight);
sum_normalized_values += (long)(normalized_values[i] * 1000);
}
// 3. OBSTACLE/LINE LOSS DETECTION
// If the sum of normalized values is very low, the line has
been lost or interrupted by an obstacle.
if (sum_normalized_values < LINE_LOST_THRESHOLD && !
is_avoiding) {
execute_detour();
return; // Start the loop over after detour
}
// 4. LINE FOLLOWING LOGIC (Only runs if not currently
avoiding)
if (!is_avoiding) {
float error = 0;
if (sum_normalized_values > 0) {
error = (float)sum_weighted_values / sum_normalized_values;
} else {
// Emergency stop if line is completely lost (should be
caught by the Detour logic above,
// but this acts as a final fail-safe for line loss)
stop_motors();
[Link]("Action: Emergency STOP (Complete line
loss)");
last_error = 0;
return;
}
// CALCULATE PD CORRECTION
float P_term = Kp * error;
float derivative = error - last_error;
float D_term = Kd * derivative;
float motor_correction = P_term + D_term;
// APPLY CORRECTION TO MOTORS
int left_speed = BASE_SPEED - (int)motor_correction;
int right_speed = BASE_SPEED + (int)motor_correction;
// CONSTRAIN SPEEDS
left_speed = constrain(left_speed, MIN_SPEED, MAX_SPEED);
right_speed = constrain(right_speed, MIN_SPEED, MAX_SPEED);
// EXECUTE MOVEMENT
set_motor_speeds(left_speed, right_speed);
// STORE CURRENT ERROR
last_error = error;
// DEBUGGING OUTPUT
[Link]("Error: ");
[Link](error);
[Link](" | L Speed: ");
[Link](left_speed);
[Link](" | R Speed: ");
[Link](right_speed);
}
}
Barcode scanning and
lifting and dropping load:
// ALR Logistics Task Handler - Autonomous Logistics Robot
// Platform: Arduino Mega 2560 (C++)
//
// This code focuses on the QR code scanning and the controlled
lifting/dropping
// sequence required for the Autonomous Logistics Robot (ALR)
competition.
// It uses a simple state machine to manage the robot's action
flow.
// This is designed to be integrated into a larger line-following
program.
// --- 1. CONFIGURATION: PIN DEFINITIONS ---
// Motor Driver Pins (Required for stopping during task)
const int L_PWM = 3; // Left Motor PWM
const int L_EN = 38; // Left Motor Enable
const int R_PWM = 2; // Right Motor PWM
const int R_EN = 39; // Right Motor Enable
// Lifting Mechanism Motor Pins (DC Motor control assumed)
const int LIFT_PWM = 44; // PWM for speed control
const int LIFT_IN1 = 45; // Direction Input 1
const int LIFT_IN2 = 46; // Direction Input 2
// Barcode/QR Code Scanner Serial Port
// The Mega has multiple hardware serial ports. Serial1 is often
used for a dedicated
// scanner module (like a camera or TTL scanner).
#define QR_SERIAL Serial1
// Start Switch Pin
const int START_SWITCH_PIN = 40;
// --- 2. LIFTING MECHANISM CONSTANTS ---
const int LIFT_SPEED = 200; // Speed of the lift motor (0-255)
const int LIFT_DURATION_MS = 2500; // Time required to fully lift
or drop the weight
// --- 3. ROBOT STATE MANAGEMENT ---
enum RobotState {
STATE_IDLE, // Waiting for start switch
STATE_LINE_FOLLOWING, // Following the track
STATE_PERFORMING_TASK // Stopped to lift or drop cargo
};
RobotState current_state = STATE_IDLE;
bool has_cargo = false; // Flag to track if the robot is
currently carrying a load
// --- 4. MOTOR AND LIFT CONTROL FUNCTIONS ---
void set_motor_speeds(int left_pwm, int right_pwm) {
// Simple motor control to stop the robot
digitalWrite(L_EN, HIGH);
digitalWrite(R_EN, HIGH);
analogWrite(L_PWM, left_pwm);
analogWrite(R_PWM, right_pwm);
}
void stop_motors() {
set_motor_speeds(0, 0);
digitalWrite(L_EN, LOW);
digitalWrite(R_EN, LOW);
}
void control_lifting_mechanism(bool lift_up) {
// Commands the lift motor to move up or down for a fixed
duration.
[Link](lift_up ? "LIFTING CARGO..." : "DROPPING
CARGO...");
// Set direction
if (lift_up) {
// Lift UP (e.g., IN1=HIGH, IN2=LOW)
digitalWrite(LIFT_IN1, HIGH);
digitalWrite(LIFT_IN2, LOW);
} else {
// Drop DOWN (e.g., IN1=LOW, IN2=HIGH)
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, HIGH);
}
// Set speed
analogWrite(LIFT_PWM, LIFT_SPEED);
// Run for duration
delay(LIFT_DURATION_MS);
// Stop the motor and remove power
analogWrite(LIFT_PWM, 0);
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, LOW); // Important to prevent
braking/heating
if (lift_up) {
has_cargo = true;
[Link]("CARGO LIFTED. STATUS: Loaded.");
} else {
has_cargo = false;
[Link]("CARGO DROPPED. STATUS: Empty.");
}
}
// --- 5. LOGISTICS TASK HANDLER ---
void handle_logistics_task(String qr_command) {
// Task Logic: Scan QR and decide to Pick up or Drop off
// Example QR commands:
// 'P' -> Pickup (Lift)
// 'D' -> Dropoff (Drop)
// 1. Stop the line following process
current_state = STATE_PERFORMING_TASK;
stop_motors();
[Link]("QR Code Scanned: ");
[Link](qr_command);
// Give a moment for the robot to settle
delay(1000);
if (qr_command.startsWith("P")) {
// Pickup instruction received
if (!has_cargo) {
control_lifting_mechanism(true); // true = lift up
} else {
[Link]("Already carrying cargo, skipping pickup.");
}
}
else if (qr_command.startsWith("D")) {
// Drop-off instruction received
if (has_cargo) {
control_lifting_mechanism(false); // false = drop down
} else {
[Link]("No cargo to drop, skipping drop-off.");
}
}
else {
[Link]("Unknown QR command or location. Task
skipped.");
}
// 2. Return to line following
current_state = STATE_LINE_FOLLOWING;
[Link]("Resuming line following.");
}
// --- 6. MAIN SERIAL SCANNER LOOP ---
void check_qr_scanner() {
if (QR_SERIAL.available() > 0) {
// Read the data sent by the QR scanner
String data = QR_SERIAL.readStringUntil('\n');
[Link](); // Remove any whitespace
// Process the command
handle_logistics_task(data);
// Clear buffer after action
while(QR_SERIAL.available() > 0) QR_SERIAL.read();
}
}
// --- 7. SETUP ---
void setup() {
// Initialize Serial ports
[Link](9600); // PC communication
QR_SERIAL.begin(9600); // QR Scanner communication (adjust baud
rate if needed)
[Link]("ALR Logistics Handler Initialized!");
// Configure Motor Pins
pinMode(L_PWM, OUTPUT); pinMode(L_EN, OUTPUT);
pinMode(R_PWM, OUTPUT); pinMode(R_EN, OUTPUT);
// Configure Lifting Mechanism Pins
pinMode(LIFT_PWM, OUTPUT);
pinMode(LIFT_IN1, OUTPUT);
pinMode(LIFT_IN2, OUTPUT);
// Initialize lift to neutral/stop state
analogWrite(LIFT_PWM, 0);
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, LOW);
// Configure Start Switch Pin
pinMode(START_SWITCH_PIN, INPUT_PULLUP);
// Wait for the start switch
[Link]("WAITING FOR START SWITCH...");
stop_motors();
while (digitalRead(START_SWITCH_PIN) == HIGH) {
delay(10);
}
current_state = STATE_LINE_FOLLOWING;
[Link]("START SWITCH ACTIVATED. Entering Line Following
State.");
delay(1000);
}
// --- 8. MAIN LOOP ---
void loop() {
// Always check the QR scanner regardless of the current state
// as the line following portion (which you'll integrate here)
// should keep running until the scanner signals a task.
check_qr_scanner();
// The line following code from the previous file would go
here:
// if (current_state == STATE_LINE_FOLLOWING) {
// // ... PD control logic and motor commands ...
// // Example: set_motor_speeds(BASE_SPEED, BASE_SPEED);
// }
// Since this code is only for the task handling, we will
simulate
// the 'Line Following' state with a simple forward movement.
if (current_state == STATE_LINE_FOLLOWING) {
set_motor_speeds(150, 150); // Just move forward slowly for
simulation
} else if (current_state == STATE_IDLE) {
stop_motors();
}
}
Master
Code
// ALR Competition Master Code - TIF'25 Autonomous Logistics
Robot
// Platform: Arduino Mega 2560 (C++)
// Includes PD Line Tracking, Obstacle Detour, and QR Code
Logistics Handling.
// --- 1. CONFIGURATION: PIN DEFINITIONS ---
// Line Sensor Pins (Analog Pins: QTR Array)
const int QTR_PINS[8] = {A0, A1, A2, A3, A4, A5, A6, A7};
const int NUM_SENSORS = 8;
// Motor Driver Pins (Drive Motors: 2 x IBT-2 / BTS7960 or
similar)
// Use hardware PWM pins for smoother control (2, 3 are used
here)
const int L_PWM = 3; // Left Motor PWM Speed
const int L_DIR = 38; // Left Motor Direction/Enable (using as
Direction)
const int R_PWM = 2; // Right Motor PWM Speed
const int R_DIR = 39; // Right Motor Direction/Enable (using as
Direction)
// Lifting Mechanism Motor Pins (DC Motor control assumed via
L298N or similar)
const int LIFT_PWM = 44; // PWM for speed control
const int LIFT_IN1 = 45; // Direction Input 1
const int LIFT_IN2 = 46; // Direction Input 2
// Start/Stop & Scanner Configuration
const int START_SWITCH_PIN = 40; // Digital pin for the start
switch
#define QR_SERIAL Serial1 // Use Serial1 (Mega Pins
19/18) for the QR Scanner
// --- 2. SPEED, CONTROL, AND TIMING CONSTANTS ---
// Line Following Speeds (Adjust these)
const int BASE_SPEED = 180; // Base PWM speed (0-255)
const int MAX_SPEED = 255; // Maximum speed limit
const int MIN_SPEED = 50; // Minimum speed limit
// PD Control Constants (Critical for performance, must be tuned
on the track)
const float Kp = 0.08;
const float Kd = 0.05;
// Obstacle Detour Configuration (Crucial Timings for bypass)
const int DETOUR_SPEED = 160;
const int DETOUR_TURN_TIME_MS = 350; // Time for initial turn
(e.g., 90 degrees left)
const int DETOUR_FORWARD_TIME_MS = 1600; // Time to drive past
the obstacle
const int DETOUR_RE_ACQUIRE_TURN_MS = 700; // Time to turn back
(90+ degrees right)
// Line Loss Threshold (0-8000 scale: 8 sensors * 1000 max
normalized value)
// If the sum of normalized values is below this, the line is
considered lost/interrupted.
const long LINE_LOST_THRESHOLD = 800;
// Lifting Mechanism Constants
const int LIFT_SPEED = 200; // Speed of the lift motor
const int LIFT_DURATION_MS = 2500; // Time to fully lift or drop
the weight
// --- 3. GLOBAL STATE VARIABLES ---
enum RobotState {
STATE_IDLE, // Waiting for start
STATE_LINE_FOLLOWING, // Normal operation
STATE_PERFORMING_TASK, // Stopped for Pickup/Drop-off
STATE_AVOIDING_OBSTACLE // Executing the timed detour
};
RobotState current_state = STATE_IDLE;
float last_error = 0;
bool has_cargo = false; // Tracks if the robot is currently
carrying a load
// Sensor Calibration Arrays
int cal_min[NUM_SENSORS];
int cal_max[NUM_SENSORS];
// --- 4. MOTOR CONTROL FUNCTIONS ---
/**
* @brief Sets the speed and direction for both drive motors.
* @param left_pwm PWM value for the left motor (-255 to 255).
Negative means reverse.
* @param right_pwm PWM value for the right motor (-255 to 255).
Negative means reverse.
*/
void set_motor_speeds(int left_pwm, int right_pwm) {
// Left Motor Direction
digitalWrite(L_DIR, left_pwm >= 0 ? HIGH : LOW);
analogWrite(L_PWM, abs(left_pwm));
// Right Motor Direction
digitalWrite(R_DIR, right_pwm >= 0 ? HIGH : LOW);
analogWrite(R_PWM, abs(right_pwm));
}
void stop_motors() {
set_motor_speeds(0, 0);
}
/**
* @brief Executes a timed movement (e.g., for detours or
specific maneuvers).
*/
void turn_robot(int duration_ms, int left_speed, int right_speed)
{
set_motor_speeds(left_speed, right_speed);
delay(duration_ms);
stop_motors();
}
/**
* @brief Controls the lifting mechanism motor.
* @param lift_up true to lift cargo (up), false to drop cargo
(down).
*/
void control_lifting_mechanism(bool lift_up) {
[Link](lift_up ? "LIFTING CARGO..." : "DROPPING
CARGO...");
// Set direction
if (lift_up) {
digitalWrite(LIFT_IN1, HIGH);
digitalWrite(LIFT_IN2, LOW);
} else {
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, HIGH);
}
// Set speed and run for duration
analogWrite(LIFT_PWM, LIFT_SPEED);
delay(LIFT_DURATION_MS);
// Stop the motor
analogWrite(LIFT_PWM, 0);
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, LOW);
if (lift_up) {
has_cargo = true;
[Link]("CARGO LIFTED. STATUS: Loaded.");
} else {
has_cargo = false;
[Link]("CARGO DROPPED. STATUS: Empty.");
}
}
// --- 5. CALIBRATION AND SENSOR PROCESSING ---
void calibrate_sensors() {
stop_motors();
[Link]("--- CALIBRATION PHASE (10 seconds) ---");
[Link]("Move sensors over black line and white
surface.");
for (int i = 0; i < NUM_SENSORS; i++) {
cal_min[i] = 1023;
cal_max[i] = 0;
}
unsigned long startTime = millis();
while (millis() - startTime < 10000) {
for (int i = 0; i < NUM_SENSORS; i++) {
int reading = analogRead(QTR_PINS[i]);
if (reading < cal_min[i]) cal_min[i] = reading;
if (reading > cal_max[i]) cal_max[i] = reading;
}
delay(50);
}
[Link]("CALIBRATION COMPLETE. Starting run in 2
seconds.");
delay(2000);
}
/**
* @brief Calculates the weighted position error from the line
sensors.
* @return float The error value (e.g., -7000 to +7000).
*/
float read_line_sensors() {
long sum_weighted_values = 0;
long sum_normalized_values = 0;
for (int i = 0; i < NUM_SENSORS; i++) {
int raw_reading = analogRead(QTR_PINS[i]);
// Normalize reading (0.0 = White to 1.0 = Black)
float range = (float)(cal_max[i] - cal_min[i]);
if (range < 50) range = 50; // Safety against poor
calibration
float normalized_value = constrain((raw_reading - cal_min[i])
/ range, 0.0, 1.0);
// Apply weights: Sensor 0 = -7000, Sensor 7 = +7000
(Increments of 2000)
int position_weight = (i * 2000) - 7000;
sum_weighted_values += (long)(normalized_value *
position_weight);
sum_normalized_values += (long)(normalized_value * 1000); //
Sum on a 0-8000 scale
}
// Check for line loss (used to detect obstacles)
if (sum_normalized_values < LINE_LOST_THRESHOLD) {
// Line lost, switch state to avoidance
if (current_state == STATE_LINE_FOLLOWING) {
current_state = STATE_AVOIDING_OBSTACLE;
}
return 0; // Return zero error while avoiding
}
// Calculate error only if sum_normalized_values is positive
(i.e., we are on the line)
if (sum_normalized_values > 0) {
// Error is a value from -7 to +7 (scaled from the -7000 to
+7000 range)
return (float)sum_weighted_values / sum_normalized_values;
} else {
// Should not happen if threshold check works, but as a
fail-safe
return last_error;
}
}
// --- 6. OBSTACLE AVOIDANCE LOGIC ---
void execute_detour() {
[Link]("!!! LINE LOST / OBSTACLE DETECTED. STARTING
DETOUR SEQUENCE !!!");
stop_motors();
delay(500);
// STEP 1: Turn Left (to move parallel to the obstacle)
// Turn left (Left motor reverse, Right motor forward)
[Link]("Detour Step 1: Turning Left...");
turn_robot(DETOUR_TURN_TIME_MS, -DETOUR_SPEED, DETOUR_SPEED);
// STEP 2: Move Forward (To drive past the obstacle)
[Link]("Detour Step 2: Moving Forward...");
turn_robot(DETOUR_FORWARD_TIME_MS, DETOUR_SPEED, DETOUR_SPEED);
// STEP 3: Turn Right (to turn back and search for the line)
// Turn right (Left motor forward, Right motor reverse)
[Link]("Detour Step 3: Turning Right to Re-acquire
Line...");
turn_robot(DETOUR_RE_ACQUIRE_TURN_MS, DETOUR_SPEED, -
DETOUR_SPEED);
// Revert state back to line following
current_state = STATE_LINE_FOLLOWING;
last_error = 0; // Reset error to prevent immediate over-
correction
[Link]("!!! DETOUR COMPLETE. RESUMING LINE
TRACKING. !!!");
}
// --- 7. LOGISTICS TASK HANDLER (QR Code) ---
void handle_logistics_task(String qr_command) {
// Task Logic: Scan QR and decide to Pick up or Drop off
current_state = STATE_PERFORMING_TASK;
stop_motors();
[Link]("QR Code Scanned: ");
[Link](qr_command);
delay(500); // Pause for stability
if (qr_command.startsWith("P")) {
// Pickup instruction received
if (!has_cargo) {
control_lifting_mechanism(true); // true = lift up
} else {
[Link]("Already carrying cargo, skipping pickup.");
}
}
else if (qr_command.startsWith("D")) {
// Drop-off instruction received
if (has_cargo) {
control_lifting_mechanism(false); // false = drop down
} else {
[Link]("No cargo to drop, skipping drop-off.");
}
}
else {
[Link]("Unknown QR command. Task skipped.");
}
// Resume line following
current_state = STATE_LINE_FOLLOWING;
[Link]("Resuming line following.");
}
void check_qr_scanner() {
// Only check the scanner if the robot is currently moving
(following line or idling before start)
if (QR_SERIAL.available() > 0) {
// Read the data sent by the QR scanner
String data = QR_SERIAL.readStringUntil('\n');
[Link](); // Remove any whitespace
// Only handle task if a valid command is received
if ([Link]("P") || [Link]("D")) {
handle_logistics_task(data);
}
// Clear buffer after action
while(QR_SERIAL.available() > 0) QR_SERIAL.read();
}
}
// --- 8. ARDUINO SETUP ---
void setup() {
// Initialize Communication
[Link](9600); // PC Serial Monitor
QR_SERIAL.begin(9600); // QR Scanner (Make sure the scanner
baud rate matches)
[Link]("ALR Master Program Initialized.");
// Configure Drive Motor Pins
pinMode(L_PWM, OUTPUT); pinMode(L_DIR, OUTPUT);
pinMode(R_PWM, OUTPUT); pinMode(R_DIR, OUTPUT);
// Configure Lifting Mechanism Pins
pinMode(LIFT_PWM, OUTPUT);
pinMode(LIFT_IN1, OUTPUT);
pinMode(LIFT_IN2, OUTPUT);
// Configure Start Switch Pin
pinMode(START_SWITCH_PIN, INPUT_PULLUP);
// Perform sensor calibration
calibrate_sensors();
// Wait for the start switch
[Link]("WAITING FOR START SWITCH...");
stop_motors();
while (digitalRead(START_SWITCH_PIN) == HIGH) {
delay(10);
}
current_state = STATE_LINE_FOLLOWING;
[Link]("START SWITCH ACTIVATED. Entering Line Following
State.");
delay(1000);
}
// --- 9. ARDUINO MAIN LOOP ---
void loop() {
// High-priority check: Always monitor for QR code to interrupt
movement
check_qr_scanner();
switch (current_state) {
case STATE_LINE_FOLLOWING: {
float error = read_line_sensors(); // This function may
change the state to AVOIDING_OBSTACLE
// If the state was changed to AVOIDING, break and let the
next case handle it
if (current_state == STATE_AVOIDING_OBSTACLE) {
break; +0.
}
// Calculate PD CORRECTION
float P_term = Kp * error;
float derivative = error - last_error;
float D_term = Kd * derivative;
float motor_correction = P_term + D_term;
// Apply Correction to Motors
int left_speed = BASE_SPEED - (int)motor_correction;
int right_speed = BASE_SPEED + (int)motor_correction;
// Constrain Speeds to prevent maxing out too often or
going too slow
left_speed = constrain(left_speed, MIN_SPEED, MAX_SPEED);
right_speed = constrain(right_speed, MIN_SPEED, MAX_SPEED);
// Execute Movement
set_motor_speeds(left_speed, right_speed);
// Store Current Error
last_error = error;
// Optional: Serial Debugging (Comment out for fastest
performance)
// [Link]("Error: "); [Link](error);
// [Link](" | L Speed: "); [Link](left_speed);
// [Link](" | R Speed: ");
[Link](right_speed);
break;
}
case STATE_AVOIDING_OBSTACLE: {
// Execute detour maneuver once, then revert to
LINE_FOLLOWING
execute_detour();
break;
}
case STATE_PERFORMING_TASK:
// The task handler already stops the motors and handles
the lift sequence.
// We do nothing here, just wait for the state to be
changed back to LINE_FOLLOWING
// by the handle_logistics_task function.
stop_motors();
break;
case STATE_IDLE:
// Should only happen before the start switch is pressed
stop_motors();
break;
}
}
2nd finalized by deepseek:
// ALR Competition Master Code - TIF'25 Autonomous
Logistics Robot
// Platform: Arduino Mega 2560 (C++)
// Includes PD Line Tracking, Obstacle Detour, and QR Code
Logistics Handling.
// --- 1. CONFIGURATION: PIN DEFINITIONS ---
// Line Sensor Pins (Analog Pins: QTR Array)
const int QTR_PINS[8] = {A0, A1, A2, A3, A4, A5, A6, A7};
const int NUM_SENSORS = 8;
// Motor Driver Pins (Drive Motors: 2 x IBT-2 / BTS7960 or
similar)
const int L_PWM = 3; // Left Motor PWM Speed
const int L_DIR = 38; // Left Motor Direction/Enable
const int R_PWM = 2; // Right Motor PWM Speed
const int R_DIR = 39; // Right Motor Direction/Enable
// Lifting Mechanism Motor Pins
const int LIFT_PWM = 44; // PWM for speed control
const int LIFT_IN1 = 45; // Direction Input 1
const int LIFT_IN2 = 46; // Direction Input 2
// Start/Stop & Scanner Configuration
const int START_SWITCH_PIN = 40; // Digital pin for the start
switch
#define QR_SERIAL Serial1 // Use Serial1 (Mega Pins
19/18) for the QR Scanner
// --- 2. SPEED, CONTROL, AND TIMING CONSTANTS ---
// Line Following Speeds (Adjust these)
const int BASE_SPEED = 180; // Base PWM speed (0-255)
const int MAX_SPEED = 255; // Maximum speed limit
const int MIN_SPEED = 50; // Minimum speed limit
// PD Control Constants
const float Kp = 0.08;
const float Kd = 0.05;
// Obstacle Detour Configuration
const int DETOUR_SPEED = 160;
const int DETOUR_TURN_TIME_MS = 350; // Time for initial
turn
const int DETOUR_FORWARD_TIME_MS = 1600; // Time to
drive past obstacle
const int DETOUR_RE_ACQUIRE_TURN_MS = 700; // Time to
turn back
// Line Loss Threshold - FIXED: Increased threshold for better
detection
const long LINE_LOST_THRESHOLD = 2000; // Increased from
800
// Lifting Mechanism Constants
const int LIFT_SPEED = 200; // Speed of the lift motor
const int LIFT_DURATION_MS = 2500; // Time to fully lift or
drop
// --- 3. GLOBAL STATE VARIABLES ---
enum RobotState {
STATE_IDLE, // Waiting for start
STATE_LINE_FOLLOWING, // Normal operation
STATE_PERFORMING_TASK, // Stopped for Pickup/Drop-off
STATE_AVOIDING_OBSTACLE // Executing the timed detour
};
RobotState current_state = STATE_IDLE;
float last_error = 0;
bool has_cargo = false; // Tracks if robot is carrying a load
// NEW: Non-blocking timing variables
unsigned long detour_start_time = 0;
unsigned long lift_start_time = 0;
int detour_step = 0;
bool is_lifting = false;
// Sensor Calibration Arrays
int cal_min[NUM_SENSORS];
int cal_max[NUM_SENSORS];
// NEW: QR buffer to prevent data loss
String qr_buffer = "";
// --- 4. MOTOR CONTROL FUNCTIONS ---
void set_motor_speeds(int left_pwm, int right_pwm) {
// ADDED: Safety constraints
left_pwm = constrain(left_pwm, -MAX_SPEED, MAX_SPEED);
right_pwm = constrain(right_pwm, -MAX_SPEED,
MAX_SPEED);
// Left Motor Direction
digitalWrite(L_DIR, left_pwm >= 0 ? HIGH : LOW);
analogWrite(L_PWM, abs(left_pwm));
// Right Motor Direction
digitalWrite(R_DIR, right_pwm >= 0 ? HIGH : LOW);
analogWrite(R_PWM, abs(right_pwm));
}
void stop_motors() {
set_motor_speeds(0, 0);
}
// NEW: Non-blocking turn function for detour sequence
void start_detour_sequence() {
detour_start_time = millis();
detour_step = 1;
[Link]("Detour Step 1: Turning Left...");
set_motor_speeds(-DETOUR_SPEED, DETOUR_SPEED);
}
// MODIFIED: Non-blocking lift control
void start_lifting_mechanism(bool lift_up) {
[Link](lift_up ? "STARTING LIFT..." : "STARTING
DROP...");
// Set direction
if (lift_up) {
digitalWrite(LIFT_IN1, HIGH);
digitalWrite(LIFT_IN2, LOW);
} else {
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, HIGH);
}
// Start motor
analogWrite(LIFT_PWM, LIFT_SPEED);
lift_start_time = millis();
is_lifting = true;
}
void stop_lifting_mechanism() {
analogWrite(LIFT_PWM, 0);
digitalWrite(LIFT_IN1, LOW);
digitalWrite(LIFT_IN2, LOW);
is_lifting = false;
}
// --- 5. CALIBRATION AND SENSOR PROCESSING ---
void calibrate_sensors() {
stop_motors();
[Link]("--- CALIBRATION PHASE (10 seconds) ---");
[Link]("Move sensors over black line and white
surface.");
for (int i = 0; i < NUM_SENSORS; i++) {
cal_min[i] = 1023;
cal_max[i] = 0;
}
unsigned long startTime = millis();
while (millis() - startTime < 10000) {
for (int i = 0; i < NUM_SENSORS; i++) {
int reading = analogRead(QTR_PINS[i]);
if (reading < cal_min[i]) cal_min[i] = reading;
if (reading > cal_max[i]) cal_max[i] = reading;
}
delay(50);
}
[Link]("CALIBRATION COMPLETE. Starting run in 2
seconds.");
delay(2000);
}
// FIXED: Improved line sensor reading with better obstacle
detection
float read_line_sensors() {
long sum_weighted_values = 0;
long sum_normalized_values = 0;
bool line_detected = false;
for (int i = 0; i < NUM_SENSORS; i++) {
int raw_reading = analogRead(QTR_PINS[i]);
// Normalize reading (0.0 = White to 1.0 = Black)
float range = (float)(cal_max[i] - cal_min[i]);
if (range < 50) range = 50; // Safety against poor
calibration
float normalized_value = constrain((raw_reading -
cal_min[i]) / range, 0.0, 1.0);
// Apply weights: Sensor 0 = -7000, Sensor 7 = +7000
int position_weight = (i * 2000) - 7000;
sum_weighted_values += (long)(normalized_value *
position_weight);
sum_normalized_values += (long)(normalized_value *
1000);
// Check if any sensor sees the line strongly
if (normalized_value > 0.5) {
line_detected = true;
}
}
// FIXED: Better obstacle detection logic
if (!line_detected && sum_normalized_values <
LINE_LOST_THRESHOLD) {
// Line truly lost - obstacle detected
if (current_state == STATE_LINE_FOLLOWING) {
current_state = STATE_AVOIDING_OBSTACLE;
return 0;
}
}
// Calculate error only if we have sufficient sensor data
if (sum_normalized_values > 500) { // Increased minimum
threshold
return (float)sum_weighted_values /
sum_normalized_values;
} else {
// Return last error to maintain course briefly
return last_error * 0.7; // Dampened last error
}
}
// --- 6. OBSTACLE AVOIDANCE LOGIC ---
// NEW: Non-blocking detour execution
void update_detour_sequence() {
unsigned long current_time = millis();
unsigned long elapsed = current_time - detour_start_time;
switch (detour_step) {
case 1: // Turning left
if (elapsed >= DETOUR_TURN_TIME_MS) {
detour_step = 2;
detour_start_time = current_time;
[Link]("Detour Step 2: Moving Forward...");
set_motor_speeds(DETOUR_SPEED, DETOUR_SPEED);
}
break;
case 2: // Moving forward
if (elapsed >= DETOUR_FORWARD_TIME_MS) {
detour_step = 3;
detour_start_time = current_time;
[Link]("Detour Step 3: Turning Right to Re-
acquire Line...");
set_motor_speeds(DETOUR_SPEED, -DETOUR_SPEED);
}
break;
case 3: // Turning right to re-acquire
if (elapsed >= DETOUR_RE_ACQUIRE_TURN_MS) {
detour_step = 0;
current_state = STATE_LINE_FOLLOWING;
last_error = 0;
stop_motors();
[Link]("!!! DETOUR COMPLETE. RESUMING LINE
TRACKING. !!!");
}
break;
}
}
// --- 7. LOGISTICS TASK HANDLER (QR Code) ---
// MODIFIED: Improved QR handling with buffer
void handle_logistics_task(String qr_command) {
current_state = STATE_PERFORMING_TASK;
stop_motors();
[Link]("QR Code Scanned: ");
[Link](qr_command);
if (qr_command.startsWith("P")) {
// Pickup instruction
if (!has_cargo) {
start_lifting_mechanism(true);
} else {
[Link]("Already carrying cargo, skipping
pickup.");
current_state = STATE_LINE_FOLLOWING;
}
}
else if (qr_command.startsWith("D")) {
// Drop-off instruction
if (has_cargo) {
start_lifting_mechanism(false);
} else {
[Link]("No cargo to drop, skipping drop-off.");
current_state = STATE_LINE_FOLLOWING;
}
}
else {
[Link]("Unknown QR command. Task skipped.");
current_state = STATE_LINE_FOLLOWING;
}
}
// MODIFIED: Improved QR scanner with buffer
void check_qr_scanner() {
// Only process QR codes when not performing critical
maneuvers
if (current_state != STATE_AVOIDING_OBSTACLE &&
QR_SERIAL.available() > 0) {
while (QR_SERIAL.available() > 0) {
char c = QR_SERIAL.read();
if (c == '\n' || c == '\r') {
if (qr_buffer.length() > 0) {
// Process complete command
if (qr_buffer.startsWith("P") ||
qr_buffer.startsWith("D")) {
handle_logistics_task(qr_buffer);
}
qr_buffer = "";
}
} else {
qr_buffer += c;
}
}
}
}
// --- 8. ARDUINO SETUP ---
void setup() {
// Initialize Communication
[Link](9600);
QR_SERIAL.begin(9600);
[Link]("ALR Master Program Initialized.");
// Configure Drive Motor Pins
pinMode(L_PWM, OUTPUT); pinMode(L_DIR, OUTPUT);
pinMode(R_PWM, OUTPUT); pinMode(R_DIR, OUTPUT);
// Configure Lifting Mechanism Pins
pinMode(LIFT_PWM, OUTPUT);
pinMode(LIFT_IN1, OUTPUT);
pinMode(LIFT_IN2, OUTPUT);
// Configure Start Switch Pin
pinMode(START_SWITCH_PIN, INPUT_PULLUP);
// Initialize motor states
stop_motors();
stop_lifting_mechanism();
// Perform sensor calibration
calibrate_sensors();
// Wait for start switch
[Link]("WAITING FOR START SWITCH...");
while (digitalRead(START_SWITCH_PIN) == HIGH) {
delay(10);
}
current_state = STATE_LINE_FOLLOWING;
[Link]("START SWITCH ACTIVATED. Entering Line
Following State.");
}
// --- 9. ARDUINO MAIN LOOP ---
void loop() {
// Always check for QR codes
check_qr_scanner();
// Handle lifting mechanism completion
if (is_lifting && (millis() - lift_start_time >=
LIFT_DURATION_MS)) {
stop_lifting_mechanism();
if (has_cargo) {
has_cargo = false;
[Link]("CARGO DROPPED. STATUS: Empty.");
} else {
has_cargo = true;
[Link]("CARGO LIFTED. STATUS: Loaded.");
}
current_state = STATE_LINE_FOLLOWING;
}
switch (current_state) {
case STATE_LINE_FOLLOWING: {
float error = read_line_sensors();
// If state changed to AVOIDING, break and let next case
handle it
if (current_state == STATE_AVOIDING_OBSTACLE) {
start_detour_sequence();
break;
}
// Calculate PD Correction
float P_term = Kp * error;
float derivative = error - last_error;
float D_term = Kd * derivative;
float motor_correction = P_term + D_term;
// Apply Correction to Motors
int left_speed = BASE_SPEED - (int)motor_correction;
int right_speed = BASE_SPEED + (int)motor_correction;
// Constrain Speeds
left_speed = constrain(left_speed, MIN_SPEED,
MAX_SPEED);
right_speed = constrain(right_speed, MIN_SPEED,
MAX_SPEED);
// Execute Movement
set_motor_speeds(left_speed, right_speed);
// Store Current Error
last_error = error;
break;
}
case STATE_AVOIDING_OBSTACLE: {
update_detour_sequence();
break;
}
case STATE_PERFORMING_TASK:
// Motors already stopped, just wait for task completion
stop_motors();
break;
case STATE_IDLE:
stop_motors();
break;
}
// Small delay to prevent overwhelming the processor
delay(10);
}