0% found this document useful (0 votes)
13 views7 pages

Robot Code

The document contains code for a robot's path navigation system using an Adafruit PWM Servo Driver and line sensors. It defines various states for movement and turning, motor control functions, and logic for following a line and detecting junctions. The setup and loop functions manage the robot's behavior based on sensor readings and current path state, allowing it to navigate a predefined path effectively.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views7 pages

Robot Code

The document contains code for a robot's path navigation system using an Adafruit PWM Servo Driver and line sensors. It defines various states for movement and turning, motor control functions, and logic for following a line and detecting junctions. The setup and loop functions manage the robot's behavior based on sensor readings and current path state, allowing it to navigate a predefined path effectively.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 7

// --- Includes and Setup from Keyestudio Examples ---

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// Initialize Adafruit PWM Servo Driver (address 0x47 is common)


Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x47);

// Define sensor pins (as per Keyestudio manual)


#define SENSOR_LEFT_PIN 6
#define SENSOR_MIDDLE_PIN 7
#define SENSOR_RIGHT_PIN 8

// Define motor speeds (0-4095, but Adafruit library takes 0-4095 for on/off, speed
is tricky)
// For simplicity, I will use a base speed and adjust. The manual uses ~1000-2000
for speed.
int baseSpeed = 1500; // Adjust as needed
int turnSpeed = 1200; // Slower speed for turns

// Sensor states
bool sensorL, sensorM, sensorR;

// --- Path Navigation State Machine ---


enum PathState {
MOVING_UP_COL0,
TURNING_RIGHT_AT_COL0_END,
MOVING_RIGHT_TO_COL1,
TURNING_RIGHT_AT_COL1_START,
MOVING_DOWN_COL1,
TURNING_RIGHT_AT_COL1_END,
MOVING_RIGHT_TO_COL2,
TURNING_LEFT_AT_COL2_START,
MOVING_UP_COL2,
TURNING_RIGHT_AT_COL2_END,
MOVING_RIGHT_TO_COL3,
TURNING_RIGHT_AT_COL3_START,
MOVING_DOWN_COL3,
TURNING_RIGHT_AT_COL3_END,
MOVING_RIGHT_TO_COL4,
TURNING_LEFT_AT_COL4_START,
MOVING_UP_COL4,
PATH_COMPLETE
};
PathState currentPathState = MOVING_UP_COL0;

int intersectionsCrossed = 0;
int targetIntersections = 0; // Will be set based on state

bool atJunctionFlag = false; // To handle junction once

// --- Low-Level Motor Control Functions (from Keyestudio examples) ---


// These functions would set the 8 PWM channels for 4 motors
// MA: channels 6 (DIR), 7 (PWM)
// MA1: channels 4 (DIR), 5 (PWM)
// MB: channels 0 (DIR), 1 (PWM)
// MB1: channels 2 (DIR), 3 (PWM)
// DIR: 0,0,0 = stop/coast, 0,0,4095 = one way, 4,0,0 = other way (example, might
be reversed)
// PWM: 0,0,speed_val
void motorSet(int motorNum, bool forward, int speedVal) {
// motorNum: 0=MB, 1=MB1, 2=MA1, 3=MA
// This is a simplified abstraction. Refer to your wiring and the example code.
// Example for MB (motor 0):
// pwm.setPWM(dir_pin, 0, forward ? 4095 : 0); // Or 0 for forward, 4095 for
reverse
// pwm.setPWM(pwm_pin, 0, speedVal);

// Based on Project 8 (Motor Driving) from your manual:


// pwm.setPWM(0,0,4095) & pwm.setPWM(1,0,speed) -> MB clockwise
// pwm.setPWM(0,0,0) & pwm.setPWM(1,0,speed) -> MB anti-clockwise
// Similar for MB1(2,3), MA1(4,5), MA(6,7)

// Let's define pins for clarity (these are PWM channels, not Arduino pins)
int MB_DIR = 0, MB_PWM = 1;
int MB1_DIR = 2, MB1_PWM = 3;
int MA1_DIR = 4, MA1_PWM = 5;
int MA_DIR = 6, MA_PWM = 7;

// Assuming MA/MA1 are right side, MB/MB1 are left side from driver's
perspective
// And forward means positive speed input
// This needs careful calibration based on your physical motor wiring and
shield behavior!

if (motorNum == 0) { // MB (Left Front)


pwm.setPWM(MB_DIR, 0, forward ? 4095 : 0); pwm.setPWM(MB_PWM, 0, speedVal);
} else if (motorNum == 1) { // MB1 (Left Rear)
pwm.setPWM(MB1_DIR, 0, forward ? 4095 : 0); pwm.setPWM(MB1_PWM, 0,
speedVal);
} else if (motorNum == 2) { // MA1 (Right Rear)
pwm.setPWM(MA1_DIR, 0, forward ? 4095 : 0); pwm.setPWM(MA1_PWM, 0,
speedVal);
} else if (motorNum == 3) { // MA (Right Front)
pwm.setPWM(MA_DIR, 0, forward ? 4095 : 0); pwm.setPWM(MA_PWM, 0, speedVal);
}
}

void goForward(int speed) {


// Assuming MA/MA1 are right motors, MB/MB1 are left motors
// And all need to spin "forward"
motorSet(0, true, speed); // MB (Left Front)
motorSet(1, true, speed); // MB1 (Left Rear)
motorSet(2, true, speed); // MA1 (Right Rear)
motorSet(3, true, speed); // MA (Right Front)
}

void goBackward(int speed) {


motorSet(0, false, speed);
motorSet(1, false, speed);
motorSet(2, false, speed);
motorSet(3, false, speed);
}

void turnLeft(int speed) { // Pivot turn: Left motors back, Right motors forward
motorSet(0, false, speed); // MB (Left Front) backward
motorSet(1, false, speed); // MB1 (Left Rear) backward
motorSet(2, true, speed); // MA1 (Right Rear) forward
motorSet(3, true, speed); // MA (Right Front) forward
}

void turnRight(int speed) { // Pivot turn: Left motors forward, Right motors back
motorSet(0, true, speed); // MB (Left Front) forward
motorSet(1, true, speed); // MB1 (Left Rear) forward
motorSet(2, false, speed); // MA1 (Right Rear) backward
motorSet(3, false, speed); // MA (Right Front) backward
}

void stopMotors() {
motorSet(0, true, 0);
motorSet(1, true, 0);
motorSet(2, true, 0);
motorSet(3, true, 0);
}

// --- Higher-Level Movement Abstractions ---


void moveForwardSlightly() {
goForward(baseSpeed / 2); // Slower speed to cross junction
delay(300); // Adjust delay to move just past the center of intersection
// stopMotors(); // Optional: stop after, or let line follower take over
}

void turnRight90() {
// Turn right for a fixed duration/angle
// This needs calibration!
stopMotors();
delay(100);
turnRight(turnSpeed);
delay(500); // Adjust this delay for a ~90 degree turn
stopMotors();
delay(100);
intersectionsCrossed = 0; // Reset for the new segment
atJunctionFlag = false;
}

void turnLeft90() {
// Turn left for a fixed duration/angle
// This needs calibration!
stopMotors();
delay(100);
turnLeft(turnSpeed);
delay(500); // Adjust this delay for a ~90 degree turn
stopMotors();
delay(100);
intersectionsCrossed = 0; // Reset for the new segment
atJunctionFlag = false;
}

// --- Sensor Reading ---


void readLineSensors() {
// Remember: LOW = black line, HIGH = white
sensorL = (digitalRead(SENSOR_LEFT_PIN) == LOW);
sensorM = (digitalRead(SENSOR_MIDDLE_PIN) == LOW);
sensorR = (digitalRead(SENSOR_RIGHT_PIN) == LOW);
}
bool isJunction() {
// Junction detected if all three sensors see black
return sensorL && sensorM && sensorR;
}

// --- Line Following Logic ---


void followLineBasic() {
if (sensorM) { // Middle on line
goForward(baseSpeed);
} else if (sensorL && !sensorR) { // Left on line, Right is not (to avoid
issues at sharp turns)
// Gentle turn right (slow down left motors or speed up right, or
combination)
motorSet(0, true, baseSpeed / 2); // MB (Left Front) slower
motorSet(1, true, baseSpeed / 2); // MB1 (Left Rear) slower
motorSet(2, true, baseSpeed); // MA1 (Right Rear) normal
motorSet(3, true, baseSpeed); // MA (Right Front) normal
} else if (sensorR && !sensorL) { // Right on line, Left is not
// Gentle turn left
motorSet(0, true, baseSpeed); // MB (Left Front) normal
motorSet(1, true, baseSpeed); // MB1 (Left Rear) normal
motorSet(2, true, baseSpeed / 2); // MA1 (Right Rear) slower
motorSet(3, true, baseSpeed / 2); // MA (Right Front) slower
} else if (sensorL && sensorR && !sensorM) { // Both L & R on, M off (unlikely
with 1cm spacing on 2cm line, but for robustness)
goForward(baseSpeed); // Or could be an error state
}
else { // Lost line (no sensors see black)
// Could stop, or try to find line (e.g., last known direction)
// For now, let's assume it mostly stays on track or re-centers quickly
// Or if !sensorL && !sensorM && !sensorR
stopMotors(); // Stop if completely lost
}
}

void setTargetIntersectionsForState() {
switch (currentPathState) {
case MOVING_UP_COL0: targetIntersections = 2; break;
case MOVING_RIGHT_TO_COL1:targetIntersections = 1; break;
case MOVING_DOWN_COL1: targetIntersections = 4; break;
case MOVING_RIGHT_TO_COL2:targetIntersections = 1; break;
case MOVING_UP_COL2: targetIntersections = 4; break;
case MOVING_RIGHT_TO_COL3:targetIntersections = 1; break;
case MOVING_DOWN_COL3: targetIntersections = 4; break;
case MOVING_RIGHT_TO_COL4:targetIntersections = 1; break;
case MOVING_UP_COL4: targetIntersections = 4; break; // Or to center
then stop
default: targetIntersections = 0; break; // Should not
happen in active move states
}
}

// --- Main Setup and Loop ---


void setup() {
Serial.begin(9600);
pwm.begin();
pwm.setPWMFreq(60); // Standard servo/motor frequency
pinMode(SENSOR_LEFT_PIN, INPUT);
pinMode(SENSOR_MIDDLE_PIN, INPUT);
pinMode(SENSOR_RIGHT_PIN, INPUT);

Serial.println("Robot Starting Path Navigation...");


stopMotors();
delay(1000);
setTargetIntersectionsForState(); // Set initial target
}

void loop() {
if (currentPathState == PATH_COMPLETE) {
Serial.println("Path Complete!");
stopMotors();
while(true); // Halt
}

readLineSensors();

if (isJunction()) {
if (!atJunctionFlag) { // Process this junction only once
atJunctionFlag = true; // Set flag to prevent re-entry for this same
physical junction
Serial.print("Junction detected. State: ");
Serial.print(currentPathState);
Serial.print(", Intersections: ");
Serial.println(intersectionsCrossed);

stopMotors();
delay(100); // Short pause
moveForwardSlightly(); // Move forward to center over/past the junction
line
intersectionsCrossed++;
Serial.print("Crossed intersection. Total now: ");
Serial.println(intersectionsCrossed);

if (intersectionsCrossed >= targetIntersections) {


Serial.println("Target intersections reached for this segment.");
// Reached end of current straight segment, decide next state/turn
switch (currentPathState) {
case MOVING_UP_COL0:
currentPathState = TURNING_RIGHT_AT_COL0_END;
Serial.println("Transitioning to
TURNING_RIGHT_AT_COL0_END");
break;
case MOVING_RIGHT_TO_COL1:
currentPathState = TURNING_RIGHT_AT_COL1_START;
Serial.println("Transitioning to
TURNING_RIGHT_AT_COL1_START");
break;
case MOVING_DOWN_COL1:
currentPathState = TURNING_RIGHT_AT_COL1_END;
Serial.println("Transitioning to
TURNING_RIGHT_AT_COL1_END");
break;
case MOVING_RIGHT_TO_COL2:
currentPathState = TURNING_LEFT_AT_COL2_START;
Serial.println("Transitioning to
TURNING_LEFT_AT_COL2_START");
break;
case MOVING_UP_COL2:
currentPathState = TURNING_RIGHT_AT_COL2_END;
Serial.println("Transitioning to
TURNING_RIGHT_AT_COL2_END");
break;
case MOVING_RIGHT_TO_COL3:
currentPathState = TURNING_RIGHT_AT_COL3_START;
Serial.println("Transitioning to
TURNING_RIGHT_AT_COL3_START");
break;
case MOVING_DOWN_COL3:
currentPathState = TURNING_RIGHT_AT_COL3_END;
Serial.println("Transitioning to
TURNING_RIGHT_AT_COL3_END");
break;
case MOVING_RIGHT_TO_COL4:
currentPathState = TURNING_LEFT_AT_COL4_START;
Serial.println("Transitioning to
TURNING_LEFT_AT_COL4_START");
break;
case MOVING_UP_COL4:
currentPathState = PATH_COMPLETE; // End of path
Serial.println("Transitioning to PATH_COMPLETE");
break;
default: break;
}
} else {
// Intermediate junction, continue straight
Serial.println("Intermediate junction, continuing.");
// atJunctionFlag will be reset when line following resumes and no
longer sees a junction
}
}
// If still atJunctionFlag is true, it means I am still on the junction,
// I might need to ensure followLineBasic() or a state transition occurs.
// The flag is mainly to correctly count intersections.
// Let's add a small delay to ensure sensors clear the junction before
resetting the flag.
// The flag will naturally reset when `isJunction()` becomes false.

} else { // Not at a junction


atJunctionFlag = false; // Reset flag if I are off the junction

// Handle turns or continue line following


switch (currentPathState) {
case TURNING_RIGHT_AT_COL0_END:
turnRight90();
currentPathState = MOVING_RIGHT_TO_COL1;
setTargetIntersectionsForState();
Serial.println("Turned. New state: MOVING_RIGHT_TO_COL1");
break;
case TURNING_RIGHT_AT_COL1_START:
turnRight90();
currentPathState = MOVING_DOWN_COL1;
setTargetIntersectionsForState();
Serial.println("Turned. New state: MOVING_DOWN_COL1");
break;
case TURNING_RIGHT_AT_COL1_END:
turnRight90();
currentPathState = MOVING_RIGHT_TO_COL2;
setTargetIntersectionsForState();
Serial.println("Turned. New state: MOVING_RIGHT_TO_COL2");
break;
case TURNING_LEFT_AT_COL2_START:
turnLeft90();
currentPathState = MOVING_UP_COL2;
setTargetIntersectionsForState();
Serial.println("Turned. New state: MOVING_UP_COL2");
break;
case TURNING_RIGHT_AT_COL2_END:
turnRight90();
currentPathState = MOVING_RIGHT_TO_COL3;
setTargetIntersectionsForState();
Serial.println("Turned. New state: MOVING_RIGHT_TO_COL3");
break;
case TURNING_RIGHT_AT_COL3_START:
turnRight90();
currentPathState = MOVING_DOWN_COL3;
setTargetIntersectionsForState();
Serial.println("Turned. New state: MOVING_DOWN_COL3");
break;
case TURNING_RIGHT_AT_COL3_END:
turnRight90();
currentPathState = MOVING_RIGHT_TO_COL4;
setTargetIntersectionsForState();
Serial.println("Turned. New state: MOVING_RIGHT_TO_COL4");
break;
case TURNING_LEFT_AT_COL4_START:
turnLeft90();
currentPathState = MOVING_UP_COL4;
setTargetIntersectionsForState();
Serial.println("Turned. New state: MOVING_UP_COL4");
break;

// If in a moving state, just follow the line


case MOVING_UP_COL0:
case MOVING_RIGHT_TO_COL1:
case MOVING_DOWN_COL1:
case MOVING_RIGHT_TO_COL2:
case MOVING_UP_COL2:
case MOVING_RIGHT_TO_COL3:
case MOVING_DOWN_COL3:
case MOVING_RIGHT_TO_COL4:
case MOVING_UP_COL4:
followLineBasic();
break;

default: // Includes PATH_COMPLETE, do nothing


break;
}
}
delay(10); // Small delay for stability
}

You might also like