Ultrasonic sensors can miss low-profile objects, clear glass, or acoustic-absorbing fabrics. To handle these blind spots, we add physical Tactile Bumper Switches (CollisionPin_L, CollisionPin_R) as a final layer of defense.To manage all these inputs, the code uses a Strict Priority Hierarchy inside the core runtime evaluation loop:Priority 1 (Highest): Tactile Impact Check. If a physical collision is registered, the robot stops everything, backs up, and executes a wide turning maneuver.Priority 2 (Medium): Ultrasonic Proactive Clearance. If an object is detected within 30cm, the robot performs a quick corrective turn to avoid an accident.Priority 3 (Lowest): Standard Path Following. If both safety systems report a clear path, the robot continues tracking the line.
The full software build adds tactical mechanical bumper microswitches routed to pins A4 (18) and A5 (19). This program runs a rigorous hierarchy matrix: physical bumper triggers grab immediate absolute priority (initiating multi-second reversing maneuvers), sonar triggers handle medium-priority non-contact adjustments, while line following proceeds only when both safety nets report a clear path.
Here is the final, production-ready source architecture merging all four functional modules:
#include <Arduino.h> #include <IRremote.hpp> // --- COMPREHENSIVE HARDWARE PIN CONFIGURATION --- const int motorPinA_1A = 5; const int motorPinA_1B = 6; const int motorPinB_1A = 9; const int motorPinB_1B = 10; const int IR_RECEIVE_PIN = 2; const int trigPin = 4; const int echoPin = 3; // Physical Tactile Bumpers (Mapped to available analog pins working as digital inputs) const int CollisionPin_L = 18; // Pin A4 const int CollisionPin_R = 19; // Pin A5 // Line Tracking Photo-interrupters const int TrackingPin_L = A2; const int TrackingPin_R = A3; enum RobotMode { MODE_MANUAL, MODE_AUTONOMOUS }; RobotMode currentMode = MODE_MANUAL; const int motorSpeed = 200; int spd = 195; // Specific heavy escape/evasive speed vector int trackSpeed = 200; unsigned long lastCommandTime = 0; const unsigned long commandTimeout = 250; void motor(int A1, int A2, int B1, int B2) { analogWrite(motorPinA_1A, A1); analogWrite(motorPinA_1B, A2); analogWrite(motorPinB_1A, B1); analogWrite(motorPinB_1B, B2); } float getDistance() { digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); long duration = pulseIn(echoPin, HIGH, 30000); float d = duration * 0.0343 / 2; if (d == 0) return 999.0; return d; } void exec_cmd(byte key_val) { switch (key_val) { case 0x1C: if (currentMode == MODE_MANUAL) { currentMode = MODE_AUTONOMOUS; Serial.println("System Notification: AUTONOMY MODE ENGAGED."); } else { currentMode = MODE_MANUAL; motor(0, 0, 0, 0); Serial.println("System Notification: MANUAL OVERRIDE ENGAGED."); } break; case 0x18: if (currentMode == MODE_MANUAL) motor(0, motorSpeed, 0, motorSpeed); break; case 0x08: if (currentMode == MODE_MANUAL) motor(motorSpeed, 0, 0, motorSpeed); break; case 0x5A: if (currentMode == MODE_MANUAL) motor(0, motorSpeed, motorSpeed, 0); break; case 0x52: if (currentMode == MODE_MANUAL) motor(motorSpeed, 0, motorSpeed, 0); break; default: if (currentMode == MODE_MANUAL) motor(0, 0, 0, 0); break; } } void setup() { Serial.begin(9600); // pinMode(motorPinA_1A, OUTPUT); // pinMode(motorPinA_1B, OUTPUT); // pinMode(motorPinB_1A, OUTPUT); // pinMode(motorPinB_1B, OUTPUT); IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); // Initialize switches using internal pullups (Switches must connect directly to GND) pinMode(CollisionPin_L, INPUT_PULLUP); pinMode(CollisionPin_R, INPUT_PULLUP); pinMode(TrackingPin_L, INPUT_PULLUP); pinMode(TrackingPin_R, INPUT_PULLUP); Serial.println("System Core Ready."); } void loop() { if (IrReceiver.decode()) { byte command = IrReceiver.decodedIRData.command; exec_cmd(command); if (currentMode == MODE_MANUAL) lastCommandTime = millis(); IrReceiver.resume(); } if (currentMode == MODE_MANUAL) { if (millis() - lastCommandTime > commandTimeout) { motor(0, 0, 0, 0); } } else if (currentMode == MODE_AUTONOMOUS) { float distance = getDistance(); // Evaluate binary conditions of active bumper connections int sumVal = (digitalRead(CollisionPin_L) == LOW ? 1 : 0) * 2 + (digitalRead(CollisionPin_R) == LOW ? 1 : 0); // --- HIERARCHY LEVEL 1: Absolute Physical Impact Interception (Bumpers) --- if (sumVal > 0) { Serial.print("TACTILE COLLISION TRIGGERED. Code: "); Serial.println(sumVal); if (sumVal == 1) { // Right-side crash -> Back up, then pivot left motor(spd, 0, spd, 0); delay(2000); motor(spd, 0, 0, spd); delay(2000); } else if (sumVal == 2) { // Left-side crash -> Back up, then pivot right motor(spd, 0, spd, 0); delay(2000); motor(0, spd, spd, 0); delay(2000); } else if (sumVal == 3) { // Frontal center crash -> Back up, then pivot left motor(spd, 0, spd, 0); delay(2000); motor(spd, 0, 0, spd); delay(2000); } } // --- HIERARCHY LEVEL 2: Medium Proactive Clearance Monitoring (Sonar) --- else if (distance < 30) { motor(motorSpeed, 0, 0, motorSpeed); // Fast defensive evasion left delay(300); } // --- HIERARCHY LEVEL 3: Standard Path Routine (Line Following) --- else { int trackL = digitalRead(TrackingPin_L); int trackR = digitalRead(TrackingPin_R); int Track = trackL * 2 + trackR; if (Track == 0) motor(0, 0, 0, 0); else if (Track == 1) motor(0, trackSpeed, trackSpeed, 0); else if (Track == 2) motor(trackSpeed, 0, 0, trackSpeed); else if (Track == 3) motor(0, trackSpeed, 0, trackSpeed); delay(10); } } }