The full code repository is in this github-repo link!
Our code development process is structured into six modules, with the main code file managing the state machine behavior. It orchestrates the execution of helper functions from these modules to control various tasks, including orientation, line-following-based driving, and ultrasonic sensor-based detection of the pot's handle. Additionally, it handles the engagement and placement of the pot onto the burner. The system also incorporates accessories for pressing the ignition button and operating the arm-gate to unload the popcorn, each integrated into the corresponding state of the process.
1.) Gordonrocks
Main operational code that contains the core logic of Gordon Botsy
The main operational code that implements the core logic.
Continuously reads IR line sensor values in each loop iteration for real-time decision-making.
Manages state transitions and executes corresponding actions based on predefined logic.
Designed with clear and intuitive function and variable names, ensuring readability and ease of understanding.
Imports all global variables from global.h for modularity and maintainability:
File defines the state machine for GordonBotsy, listing all possible operational states in an enumerated type (States_t). It declares essential global variables, including state tracking, IR sensor readings, ultrasonic sensor distances, motor control parameters, and line-tracking error handling. Additionally, it specifies pin assignments for IR sensors, motors, ultrasonic sensors, and servo motors, ensuring hardware components are correctly interfaced. The file also declares external servo objects (servo1, servo2) for arm and gate control, facilitating modular and organized code structure by centralizing global configurations.
2.) Motor Drive
Controls three major functionality that are utlized to move bot:
motor_drive: This function takes leftspeed and right speed as argument to pass the PWM frequency to each motor. The individual values are decided by the PD controller logic we are utilizing to move the Gordan!
turn_by: This function takes leftSpeed, rightSpeed, and startTime as arguments to control the PWM frequency for each motor, enabling the bot to rotate in place or follow a curved trajectory for a fixed duration. However, we chose not to use this function for turning, as implementing while loops in Arduino code often leads to execution issues. Instead, we introduced a dedicated state to handle turning for Gordon, ensuring smoother and more reliable operation where we used dedicated metro timers to execute the loop!
handleStopBot: This function also utilizes a dedicated Metro timer to halt the bot for a specified duration. While not strictly necessary, it helps clearly distinguish state transitions during hardware testing and interaction, making debugging and validation more intuitive.
3.) Beacon Detection
The handleBeaconDetection function ensures that the bot continues rotating in place as long as both IR-beacon sensors detect a signal. The code follows a straightforward flow, utilizing two interrupt service routines (ISRs) to continuously monitor and update the detection status of each sensor. We have explored multiple iterative approaches for this functionality, which are explained in detail in POT Sensing!
4.) Line Follow:
This module utilizes PD-based control for line tracking, Metro timers for timed actions, and state-based logic to handle different behaviors like searching for a line, tracking it, detecting objects, and executing turns. Below is a detailed breakdown of each function:
handleLineSearch
This function rotates the bot left until it detects a line using any of the IR sensors.
Once a line is detected, the bot transitions to STATE_STOP_BOT before continuing to STATE_LINE_TRACKING_WITH_DETECTION.
Logic:
The bot rotates left.
If any of the five IR sensors detect the line, the next state is set to STATE_LINE_TRACKING_WITH_DETECTION, and the bot stops.
handleLineTracking
This function implements PID-based line tracking, adjusting motor speeds based on IR sensor inputs.
It also handles state transitions if the bot overshoots or encounters a turn.
Logic:
Calculates position error using weighted IR sensor values.
Uses PD control (KP, KD) to compute motor speed adjustments.
Applies speed constraints to ensure smooth motion.
If all IR sensors detect the line (or none do), the bot continues moving forward.
If certain IR sensor combinations indicate a sharp turn, the bot transitions to STATE_TURN.
handleLineTrackingWithUltrasonicDetection
This function integrates ultrasonic sensing with line tracking.
It ensures the ignition sequence is executed before allowing normal movement.
Uses a hold-off phase where the bot moves at reduced speed before full operation
If the pot is detected, the bot transitions to a 180-degree turn state.
Logic:
Executes servo-based ignition sequence (if not already triggered).
Uses a Metro timer to maintain a hold-off period before increasing speed.
Calls CheckifNearPotUltrasonic to check for proximity to the pot.
If detected, the bot transitions to STATE_180_DEG_LEFT_TURN.
Otherwise, it continues regular line tracking.
handleTurn
This function executes controlled turns without using blocking while loops.
It determines the turn direction based on IR sensor readings and executes it for a fixed duration.
Logic:
Uses a flag to initialize Metro timer and determine turn direction.
If IRval_5 is active, the bot turns left; if IRval_1 is active, it turns right.
Runs the turn for a predefined time interval using metTimer1.
Once the timer expires, the bot transitions back to STATE_LINE_TRACKING_WITH_DETECTION.
5.) Pot Movement:
This module is responsible for handling the movement of the pot, the unloading process, and a 180-degree turn using motor and servo controls. The code also employs Metro timers to control time-based actions without blocking the execution of other operations. The logic ensures smooth transitions between the different states, such as moving the pot, unloading it, and turning the bot.
handle180degLeftTurn
This function is responsible for performing the unloading action once the pot is in position. It uses a servo motor to execute the unloading sequence and then transitions to the STATE_BOT_OFF state.
Logic:
The Metro timer (metTimer3) is initialized when the function first runs and keeps track of the turn duration.
Motor speeds are set to lowerTurnSpeed and higherTurnSpeed to rotate the bot. The right motor is given an additional speed boost (rightSpeed + 70) to ensure the turn is executed.
Once the Metro timer reaches the specified interval (LEFT_TURN_INTERVAL), the bot transitions to STATE_MOVE_POT and continues its task.
handleMovePot
This function moves the pot toward its destination and then halts once the ultrasonic sensor detects the pot is in the correct position. It transitions to STATE_UNLOAD when the desired position is reached
Logic:
The bot moves forward by setting motor speeds to 210 and 200 for the left and right motors respectively (motor_drive(210, 200)).
The Metro timer (metTimer1) ensures that the movement is only evaluated after a certain duration.
If the ultrasonic sensor detects an object in front (distance ≤ ultrasonic_threshold), the bot stops (motor_drive(0,0)) and transitions to the STATE_UNLOAD state
handleUnload
This function is responsible for performing the unloading action once the pot is in position. It uses a servo motor to execute the unloading sequence and then transitions to the STATE_BOT_OFF state.
Logic:
The servo1 (used for the arm) is set to 180 to execute the unloading action (servo1.write(180)) for 3 seconds (delay(3000)).
After unloading, the bot transitions to STATE_BOT_OFF, effectively stopping the bot
6.) Ultrasonic Sensor:
The code defines functions for triggering and reading ultrasonic sensors to measure distances from objects. The triggerUltrasonic() and triggerUltrasonicLeft() functions send a pulse to the respective ultrasonic sensors. The readUltrasonic() and readUltrasonicLeft() functions measure the echo pulse duration and convert it into distance in centimeters. The updateUltrasonic() and updateUltrasonicLeft() functions regularly trigger and read the ultrasonic sensors every 100ms to ensure continuous updates. The CheckifNearPotUltrasonic() function checks if the left ultrasonic sensor detects an object (e.g., a pot) within 24 cm, and if so, stops the motors and returns true.
7.) Servo:
The rotateservo() function smoothly moves a servo motor back and forth between 0 and 180 degrees. It uses a non-blocking timing mechanism with a 30ms update interval, tracked by previous Millis_Servo. The servo position (pos_servo) is incremented or decremented by 5 degrees in each cycle, and the direction of movement (increasing_servo) is reversed once the position reaches the limits (0 or 180 degrees). The servo is then updated to the new position using servo1.write(pos_servo), creating a smooth oscillating motion.