A comprehensive autonomous line-following robot built on the Raspberry Pi Pico, featuring advanced control algorithms, sensor fusion, and robust hardware integration.
- Overview
- Features
- Hardware Requirements
- Software Architecture
- Project Structure
- Getting Started
- Building the Project
- Running Tests
- Configuration
- Control Modes
- Contributing
- License
This project implements a sophisticated line-following robot capable of autonomous navigation using IR sensors, with additional capabilities including:
- Closed-loop motor control with PID algorithms
- IMU-based heading keeping for straight-line accuracy
- Barcode detection for waypoint recognition
- Obstacle detection and avoidance
- Ultrasonic distance sensing
- Multiple operational modes via state machine
The system is designed following the Barr C Coding Standard for embedded systems, ensuring reliability, maintainability, and real-time performance.
- ✅ Line Following: IR sensor array for precise path tracking
- ✅ Motor Control: Closed-loop PID speed regulation
- ✅ Heading Keeping: IMU-based directional stability
- ✅ Obstacle Avoidance: Ultrasonic sensor integration
- ✅ Barcode Recognition: Waypoint detection and navigation
- ✅ State Machine: Robust mode switching and task management
- 🎯 Encoder-based odometry and distance measurement
- 🧭 LSM303DLHC IMU with magnetometer and accelerometer
- 📊 Real-time performance monitoring and diagnostics
- 🔧 Calibration routines for sensors and motors
- 🛡️ Safety features: collision detection, boundary limits
- 📡 USB serial communication for debugging
- 🌐 WiFi Connectivity: Wireless network integration
- 📨 MQTT Client: Cloud communication and remote monitoring
- 📈 Telemetry System: Real-time data streaming and logging
- ⏱️ Timer Management: Precision timing for tasks
- 🔄 Remote Control: Command and control via MQTT
- 📊 Data Analytics: Performance metrics and diagnostics
- Left Motor: GPIO 8 (M1A), GPIO 9 (M1B)
- Right Motor: GPIO 10 (M2A), GPIO 11 (M2B)
- Left Encoder: GPIO 2 (A), GPIO 3 (B)
- Right Encoder: GPIO 4 (A), GPIO 5 (B)
- IMU (I2C): GPIO 16 (SDA), GPIO 17 (SCL)
- IR Line Sensors: GPIO 7 (ADC)
- Ultrasonic: GPIO 14 (Trigger), GPIO 15 (Echo)
- Barcode IR Sensor: GPIO 6 (Digital)
- Calibration Button: GPIO 20
- LED Indicators: GPIO 21-22
┌─────────────────────────────────────────────────────────────┐
│ Connectivity Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ WiFi │ │ MQTT │ │ Telemetry │ │
│ │ Manager │ │ Client │ │ System │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Line Follow │ │ Obstacle │ │ Barcode │ │
│ │ Control │ │ Avoidance │ │ Control │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Control Algorithms │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ PID │ │ Heading │ │ State │ │
│ │ Controller │ │ Control │ │ Machine │ │
│ └──────────────┘ └──────────────┘ └──────────────┘. │
└─────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Hardware Drivers │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │Motor│ │Encdr│ │ IMU │ │ IR │ │Ultra│ │Bcode│ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Raspberry Pi Pico SDK │
│ (Hardware Abstraction + lwIP TCP/IP Stack) │
└─────────────────────────────────────────────────────────────┘
- motor.c/h: PWM-based motor control with direction
- encoder.c/h: Quadrature encoder pulse counting
- imu.c/h: LSM303DLHC IMU interface (I2C)
- ir_sensor.c/h: IR sensor array management
- ultrasonic.c/h: HC-SR04 distance measurement
- barcode_scanner.c/h: Barcode pattern detection
- servo.c/h: Servo motor control (optional)
- calibration.c/h: Sensor calibration utilities
- pid.c/h: Generic PID controller implementation
- heading_control.c/h: IMU-based heading correction
- line_following.c/h: Line tracking algorithm
- obstacle_control.c/h: Obstacle avoidance logic
- obstacle_scanner.c/h: 180° scanning with servo
- avoidance_maneuver.c/h: Obstacle navigation strategy
- barcode_control.c/h: Barcode navigation
- state_machine.c/h: System state management
- wifi.c/h: WiFi connection management
- mqtt_client.c/h: MQTT publish/subscribe client
- telemetry.c/h: Real-time data logging and streaming
- timer_manager.c/h: System timer management
- config.h: System-wide configuration parameters
- pin_definitions.h: Hardware pin mappings
- lwipopts.h: lwIP TCP/IP stack configuration
LINE-FOLLOWING-ROBOT/
├── build/ # Build output directory
├── docs/ # Documentation
├── include/
│ ├── configuration/ # Configuration headers
│ │ ├── config.h # System-wide configuration
│ │ ├── lwipopts.h # lwIP TCP/IP stack options
│ │ └── pin_definitions.h # Hardware pin mappings
│ ├── control_algo/ # Control algorithm headers
│ │ ├── avoidance_maneuver.h
│ │ ├── barcode_control.h
│ │ ├── heading_control.h
│ │ ├── line_following.h
│ │ ├── obstacle_control.h
│ │ ├── obstacle_scanner.h
│ │ ├── pid.h
│ │ └── state_machine.h
│ ├── hardware_drivers/ # Hardware driver headers
│ │ ├── barcode_scanner.h
│ │ ├── calibration.h
│ │ ├── encoder.h
│ │ ├── encoder_utils.h
│ │ ├── imu.h
│ │ ├── ir_sensor.h
│ │ ├── motor.h
│ │ ├── servo.h
│ │ └── ultrasonic.h
│ └── utilities/ # Utility headers
│ ├── mqtt_client.h
│ ├── telemetry.h
│ ├── timer_manager.h
│ └── wifi.h
├── src/
│ ├── control_algo/ # Control algorithm implementations
│ │ ├── avoidance_maneuver.c
│ │ ├── barcode_control.c
│ │ ├── heading_control.c
│ │ ├── line_following.c
│ │ ├── obstacle_control.c
│ │ ├── obstacle_scanner.c
│ │ └── pid.c
│ ├── hardware_drivers/ # Hardware driver implementations
│ │ ├── barcode_scanner.c
│ │ ├── calibration.c
│ │ ├── encoder.c
│ │ ├── imu.c
│ │ ├── ir_sensor.c
│ │ ├── motor.c
│ │ ├── servo.c
│ │ └── ultrasonic.c
│ ├── utilities/ # Utility implementations
│ │ ├── mqtt_client.c # MQTT communication
│ │ ├── telemetry.c # Data logging and telemetry
│ │ ├── timer_manager.c # Timer management
│ │ └── wifi.c # WiFi connectivity
│ └── main.c # Main application entry point
├── test/
│ ├── unit_test/ # Unit tests
│ │ ├── MOTOR_T1-5.c # Motor subsystem tests
│ │ ├── MOTOR_T6-7.c
│ │ ├── IMU_T1-5.c # IMU tests
│ │ ├── IR_T1-4.c # IR sensor tests
│ │ └── OBS_T1-8.c # Obstacle detection tests
│ ├── integration_test/ # Integration tests
│ │ └── heading_keeping.c
│ ├── build/ # Test build directory
│ └── CMakeLists.txt # Test build configuration
├── .gitignore # Git ignore rules
├── CMakeLists.txt # Main build configuration
├── pico_sdk_import.cmake # Pico SDK import
└── README.md # This file
| File/Folder | Purpose |
|---|---|
| build/ | Contains compiled binaries, object files, and build artifacts. Generated by CMake. |
| docs/ | Project documentation including hardware setup, API reference, and calibration guides. |
| include/ | All header files (.h) organized by category. |
| src/ | All source code implementations (.c files). |
| test/ | Unit tests and integration tests for all subsystems. |
| CMakeLists.txt | Main build configuration file for CMake. Defines all targets and dependencies. |
| pico_sdk_import.cmake | Imports the Raspberry Pi Pico SDK into the project. |
| .gitignore | Specifies files and directories to be ignored by Git version control. |
| README.md | This file - comprehensive project documentation. |
| File | Purpose |
|---|---|
| config.h | System-wide configuration: WiFi credentials, MQTT settings, PID gains, motor parameters, sensor thresholds, speed limits, and all tunable parameters. |
| lwipopts.h | lwIP TCP/IP stack configuration: memory buffers, TCP settings, protocol enables, DHCP, DNS settings. Required for WiFi/MQTT. |
| pin_definitions.h | GPIO pin assignments for all hardware: motors (M1A, M1B, M2A, M2B), encoders (left/right), sensors (IMU, IR, ultrasonic, barcode). |
| File | Purpose |
|---|---|
| pid.h | Generic PID controller interface. Used for motor speed control and heading correction. |
| heading_control.h | IMU-based heading keeping algorithm. Maintains straight-line motion using magnetometer feedback. |
| line_following.h | Line tracking algorithm using IR sensors. Implements PID-based steering correction. |
| obstacle_control.h | Obstacle detection and avoidance logic. Integrates ultrasonic sensor data. |
| obstacle_scanner.h | 180° scanning routine using servo motor. Finds clear path around obstacles. |
| avoidance_maneuver.h | Navigation strategy for obstacle avoidance. Calculates turn angles and distances. |
| barcode_control.h | Barcode detection and waypoint navigation. Recognizes patterns and executes actions. |
| state_machine.h | System state management. Coordinates transitions between operational modes. |
| File | Purpose |
|---|---|
| motor.h | PWM-based DC motor control with direction (forward/backward/stop). |
| encoder.h | Quadrature encoder interface for wheel rotation tracking. |
| encoder_utils.h | Encoder utility functions: distance calculation, speed measurement. |
| imu.h | LSM303DLHC IMU driver (I2C). Provides heading, acceleration, and magnetic field data. |
| ir_sensor.h | IR sensor array management. Line detection and position calculation. |
| ultrasonic.h | HC-SR04 ultrasonic sensor driver. Distance measurement (2-400cm range). |
| barcode_scanner.h | IR-based barcode reader. Pattern recognition and pulse width measurement. |
| servo.h | Servo motor control for ultrasonic sensor scanning. |
| calibration.h | Sensor calibration routines: IMU zeroing, IR threshold tuning, encoder calibration. |
| File | Purpose |
|---|---|
| wifi.h | WiFi connection management for Pico W. Handles SSID/password authentication and reconnection. |
| mqtt_client.h | MQTT publish/subscribe client. Connects to broker, sends telemetry, receives commands. |
| telemetry.h | Real-time data logging and streaming. Formats sensor data as JSON for MQTT transmission. |
| timer_manager.h | System timer management. Schedules periodic tasks (PID updates, telemetry, sensor reads). |
| File | Purpose |
|---|---|
| pid.c | PID controller implementation. Calculates proportional, integral, derivative terms. |
| heading_control.c | Heading correction using IMU. Adjusts motor speeds to maintain target heading. |
| line_following.c | Line tracking algorithm. Uses IR sensor array to follow black line on white surface. |
| obstacle_control.c | Obstacle avoidance state machine. Detects obstacles and initiates avoidance maneuver. |
| obstacle_scanner.c | Servo-based scanning. Sweeps ultrasonic sensor 180° to find clearest path. |
| avoidance_maneuver.c | Maneuver execution. Turns around obstacle and returns to line. |
| barcode_control.c | Barcode recognition. Detects patterns and triggers waypoint actions. |
| File | Purpose |
|---|---|
| motor.c | Motor driver. Sets PWM duty cycle and direction pins. Includes soft start/stop. |
| encoder.c | Encoder driver. Uses interrupts to count rising/falling edges on quadrature signals. |
| imu.c | IMU driver. Initializes LSM303DLHC via I2C, reads accelerometer and magnetometer, calculates heading. |
| ir_sensor.c | IR sensor driver. Reads analog values from sensor array, applies threshold for line detection. |
| ultrasonic.c | Ultrasonic driver. Sends trigger pulse, measures echo duration, calculates distance. |
| barcode_scanner.c | Barcode reader. Measures pulse widths on IR sensor, decodes binary patterns. |
| servo.c | Servo driver. Generates PWM signal (500-2500µs) to control servo position. |
| calibration.c | Calibration routines. Stores and applies calibration values for all sensors. |
| File | Purpose |
|---|---|
| wifi.c | WiFi manager. Connects to network, handles DHCP, manages reconnection on dropout. |
| mqtt_client.c | MQTT client. Establishes TCP connection to broker, sends/receives messages with QoS. |
| telemetry.c | Telemetry system. Collects sensor data, formats as JSON, publishes via MQTT at 1Hz. |
| timer_manager.c | Timer scheduler. Manages periodic callbacks for control loops and data logging. |
Purpose: Application entry point. Initializes all hardware, starts control loops, and runs state machine.
Key Functions:
- System initialization
- WiFi/MQTT connection
- Main control loop
- Error handling and recovery
| File(s) | Purpose |
|---|---|
| MOTOR_T1.c | Tests motor GPIO initialization (M1A, M1B, M2A, M2B). |
| MOTOR_T2.c | Tests PWM speed levels (0%, 25%, 50%, 75%, 100%). |
| MOTOR_T3.c | Tests encoder synchronization (left/right count matching within 10%). |
| MOTOR_T4.c | Tests PID calculation accuracy (proportional, integral, derivative terms). |
| MOTOR_T5.c | Tests PID output limits (clamping to [-100, +100] range). |
| MOTOR_T6.c | Tests RPM stability over 30 seconds (±5% of 50 RPM target). |
| MOTOR_T7.c | Tests distance calibration (encoder counts vs. actual distance traveled). |
| IMU_T1-5.c | Tests IMU initialization, heading accuracy, calibration, magnetometer, accelerometer. |
| IR_T1-4.c | Tests IR sensor initialization, line detection, calibration, position calculation. |
| OBS_T1-8.c | Tests ultrasonic initialization, distance measurement, obstacle detection, avoidance. |
| File | Purpose |
|---|---|
| heading_keeping.c | Tests integrated IMU-motor system. Verifies robot can maintain straight heading for 10 seconds using closed-loop feedback. Measures stability percentage (target: ≥85% within ±3°). |
Purpose: Defines build targets for all unit and integration tests. Each test compiles as separate executable (.uf2 file) for individual testing.
-
Install Pico SDK
# Clone Pico SDK cd ~ git clone https://github.com/raspberrypi/pico-sdk.git cd pico-sdk git submodule update --init # Set environment variable export PICO_SDK_PATH=~/pico-sdk
-
Install Build Tools
# Ubuntu/Debian sudo apt update sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi \ libstdc++-arm-none-eabi-newlib build-essential # macOS brew install cmake brew tap ArmMbed/homebrew-formulae brew install arm-none-eabi-gcc
-
Clone This Repository
git clone https://github.com/Gerald-codes/Line-Following-Robot cd line-following-robot -
Configure WiFi and MQTT (Optional)
If using Pico W with WiFi/MQTT features:
# Edit config.h nano include/configuration/config.h # Update these values: #define WIFI_SSID "YourNetworkName" #define WIFI_PASSWORD "YourPassword" #define MQTT_BROKER_HOST "broker.hivemq.com" #define MQTT_CLIENT_ID "your_unique_id"
MQTT Broker Options:
- Public brokers (testing): broker.hivemq.com, test.mosquitto.org
- Self-hosted: Mosquitto on Raspberry Pi or cloud server
- Cloud services: AWS IoT, Azure IoT Hub, HiveMQ Cloud
Setup Mosquitto (Optional):
# On Raspberry Pi or Linux server sudo apt install mosquitto mosquitto-clients sudo systemctl start mosquitto sudo systemctl enable mosquitto # Test broker mosquitto_sub -h localhost -t "robot/#" -v
Ensure you have all required tools installed:
# Ubuntu/Debian
sudo apt update
sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi \
libstdc++-arm-none-eabi-newlib build-essential git
# macOS
brew install cmake
brew tap ArmMbed/homebrew-formulae
brew install arm-none-eabi-gcc git# Clone Pico SDK to home directory
cd ~
git clone https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule update --init
# Set environment variable (add to ~/.bashrc or ~/.zshrc for persistence)
export PICO_SDK_PATH=~/pico-sdkgit clone https://github.com/yourusername/line-following-robot.git
cd line-following-robotIf using Pico W with WiFi features:
# Edit configuration file
nano include/configuration/config.h
# Update these lines:
#define WIFI_SSID "BARC" // Your WiFi network name
#define WIFI_PASSWORD "BarcHotspot" // Your WiFi password
#define MQTT_BROKER_IP "10.170.105.160" // Your MQTT broker IP# Create build directory
mkdir -p build
cd build
# Configure project with CMake
cmake ..
# Compile (use -j4 for parallel compilation with 4 cores)
make -j4
# Build artifacts will be in build/:
# - main.uf2 (ready to flash to Pico)
# - main.elf (ELF binary for debugging)
# - main.hex (Intel HEX format)
# - main.bin (raw binary)Build Output:
[100%] Built target main
Generated:
- main.uf2
- main.elf
- main.hex
- main.bin
# Build only motor driver
make motor_driver
# Build only IMU driver
make imu_driver
# Build only PID controller
make pid_controller
# Build only main application
make maincd build
make clean # Remove build artifacts
rm -rf * # Complete clean (re-run cmake .. after this)-
Enter Bootloader Mode:
- Hold down the BOOTSEL button on Pico W
- While holding, plug Pico W into computer via USB
- Release button
- Pico W appears as USB drive named RPI-RP2
-
Copy Firmware:
# Linux/macOS cp build/main.uf2 /media/$USER/RPI-RP2/ # Or drag-and-drop main.uf2 to RPI-RP2 drive
-
Automatic Reboot:
- Pico W automatically reboots after copying
- LED should blink indicating successful boot
- Robot is now running!
For development and debugging:
# Terminal 1: Start OpenOCD
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg
# Terminal 2: Start GDB
arm-none-eabi-gdb build/main.elf
(gdb) target remote localhost:3333
(gdb) load
(gdb) monitor reset init
(gdb) continueView debug output and telemetry:
# Linux
screen /dev/ttyACM0 115200
# macOS
screen /dev/tty.usbmodem* 115200
# Exit screen: Ctrl+A then K then Y
# Alternative: Use minicom
minicom -D /dev/ttyACM0 -b 115200Expected Serial Output:
[INIT] Initializing hardware...
✓ Motors initialized
✓ Encoders initialized
✓ IMU initialized
[WIFI] Connecting to BARC...
[WIFI] Connected! IP: 10.170.105.123
[MQTT] Connected to broker
[SYSTEM] Ready! Starting line following...
# Navigate to test directory
cd test/build
# Configure test suite
cmake ..
# Compile all tests
make -j4This creates individual test executables:
MOTOR_T1.uf2throughMOTOR_T7.uf2IMU_T1.uf2throughIMU_T5.uf2IR_T1.uf2throughIR_T4.uf2OBS_T1.uf2throughOBS_T8.uf2heading_keeping.uf2
# Build specific test
make MOTOR_T1
# Flash to Pico W (enter BOOTSEL mode first)
cp MOTOR_T1.uf2 /media/$USER/RPI-RP2/
# Monitor serial output
screen /dev/ttyACM0 115200make MOTOR_T1 # GPIO initialization test
make MOTOR_T2 # PWM speed levels test
make MOTOR_T3 # Encoder synchronization test
make MOTOR_T4 # PID calculation test
make MOTOR_T5 # PID output limits test
make MOTOR_T6 # RPM stability test (30 seconds)
make MOTOR_T7 # Distance calibration testExample Test Output (MOTOR_T1):
╔═══════════════════════════════════════════════════════════════╗
║ MOTOR-T1: GPIO Initialization ║
╚═══════════════════════════════════════════════════════════════╝
Testing: Forward, Backward, Stop commands
Progress: [100/100] [████████████████████████████████████████] 100%
┌────────────────────────────────────────────────────────────┐
│ TEST RESULTS │
├────────────────────────────────────────────────────────────┤
│ Total Tests: 100 │
│ Passed: 100 │
│ Failed: 0 │
│ Success Rate: 100.0% │
└────────────────────────────────────────────────────────────┘
✓ MOTOR-T1 PASSED: All direction commands working correctly
make IMU_T1 # IMU initialization test
make IMU_T2 # Heading accuracy test
make IMU_T3 # Calibration drift test
make IMU_T4 # Magnetometer reading test
make IMU_T5 # Accelerometer reading testmake IR_T1 # IR sensor initialization
make IR_T2 # Line detection test
make IR_T3 # Sensor calibration test
make IR_T4 # Position calculation testmake OBS_T1 # Ultrasonic initialization
make OBS_T2 # Distance measurement
make OBS_T3 # Obstacle detection
make OBS_T4 # Avoidance behavior
# ... OBS_T5 through OBS_T8make heading_keeping # 10-second heading stability test# Create test runner script
cat > run_all_tests.sh << 'EOF'
#!/bin/bash
TESTS=(MOTOR_T1 MOTOR_T2 MOTOR_T3 MOTOR_T4 MOTOR_T5 MOTOR_T6 MOTOR_T7
IMU_T1 IMU_T2 IMU_T3 IMU_T4 IMU_T5
IR_T1 IR_T2 IR_T3 IR_T4
heading_keeping)
echo "Building all tests..."
for test in "${TESTS[@]}"; do
echo "Building $test..."
make $test
done
echo "All tests built successfully!"
echo "Flash each .uf2 file to Pico W to run tests."
EOF
chmod +x run_all_tests.sh
./run_all_tests.shEach test outputs:
- ✅ Pass/Fail Status: Green ✓ for pass, Red ✗ for fail
- 📊 Statistics: Min/max/average values
- 📈 Iteration Count: Number of test cycles completed
- 💡 Failure Reasons: Detailed error messages if test fails
Pass Criteria:
- MOTOR_T1: All GPIO pins respond correctly (100% pass rate)
- MOTOR_T2: All speed levels functional (0%, 25%, 50%, 75%, 100%)
- MOTOR_T3: Encoder mismatch <10%
- MOTOR_T4: PID calculations within 0.1 tolerance
- MOTOR_T5: Output properly clamped to [-100, +100]
- MOTOR_T6: RPM stays within ±5% for >90% of 30-second test
- MOTOR_T7: Distance error <10mm for 80% of runs
- heading_keeping: Heading stability ≥85% (within ±3° for 10 seconds)
#define MOTOR_PID_KP 2.0f // Proportional gain
#define MOTOR_PID_KI 0.5f // Integral gain
#define MOTOR_PID_KD 0.1f // Derivative gain
#define MOTOR_PID_OUTPUT_MIN 0 // Minimum PWM (0-100)
#define MOTOR_PID_OUTPUT_MAX 100 // Maximum PWM (0-100)#define HEADING_PID_KP_DEMO1 1.5f // Heading P gain
#define HEADING_PID_KI_DEMO1 0.1f // Heading I gain
#define HEADING_PID_KD_DEMO1 0.05f // Heading D gain
#define HEADING_CORRECTION_MAX 30 // Max correction (%)#define LEFT_MM_PER_PULSE 0.5f // Left wheel mm/pulse
#define RIGHT_MM_PER_PULSE 0.5f // Right wheel mm/pulse
#define WHEEL_DIAMETER_MM 65.0f // Wheel diameter
#define WHEEL_BASE_MM 120.0f // Distance between wheels#define LINE_THRESHOLD 500 // IR line detection threshold
#define OBSTACLE_DISTANCE_CM 20 // Obstacle detection distance
#define BARCODE_PULSE_MIN_US 500 // Min barcode pulse width
#define BARCODE_PULSE_MAX_US 5000 // Max barcode pulse width# Run calibration routine
make calibration
# Follow on-screen instructions
# Keep robot stationary for 5 seconds# Place robot on line
# Run IR calibration
make ir_calibration
# Move robot slowly across line# Measure wheel rotation
# Update MM_PER_PULSE values in config.h/* WiFi Settings */
#define WIFI_SSID "YourNetwork"
#define WIFI_PASSWORD "YourPassword"
#define WIFI_COUNTRY CYW43_COUNTRY_SINGAPORE
#define WIFI_AUTH CYW43_AUTH_WPA2_AES_PSK
#define WIFI_TIMEOUT_MS 30000 // 30 second timeout
#define WIFI_RECONNECT_DELAY_MS 5000 // Retry every 5 seconds/* MQTT Settings */
#define MQTT_BROKER_HOST "broker.hivemq.com" // Or your broker
#define MQTT_BROKER_PORT 1883
#define MQTT_CLIENT_ID "line_robot_001"
#define MQTT_USERNAME "" // Leave empty if not required
#define MQTT_PASSWORD "" // Leave empty if not required
#define MQTT_KEEPALIVE_SEC 60
#define MQTT_QOS 1 // Quality of Service (0, 1, or 2)
/* MQTT Topics */
#define MQTT_TOPIC_TELEMETRY "robot/telemetry" // Publish sensor data
#define MQTT_TOPIC_STATUS "robot/status" // Publish status updates
#define MQTT_TOPIC_COMMAND "robot/command" // Subscribe for commands
#define MQTT_TOPIC_CONFIG "robot/config" // Subscribe for config/* Telemetry Settings */
#define TELEMETRY_PUBLISH_RATE_MS 1000 // Publish every 1 second
#define TELEMETRY_BUFFER_SIZE 512 // JSON buffer size
/* Telemetry Data Points */
- Position (X, Y)
- Heading (degrees)
- Speed (left/right motors)
- IR sensor readings (5 sensors)
- Distance to obstacle (cm)
- Battery voltage
- Current state
- Error codes (if any)The project uses lwIP for TCP/IP networking. Key configurations:
#define NO_SYS 0 // Use OS (FreeRTOS)
#define LWIP_SOCKET 1 // Enable sockets API
#define LWIP_NETCONN 1 // Enable netconn API
#define LWIP_DHCP 1 // Enable DHCP client
#define LWIP_DNS 1 // Enable DNS resolver
/* Memory settings */
#define MEM_SIZE 8192 // Heap memory (bytes)
#define MEMP_NUM_TCP_PCB 5 // Max TCP connections
#define PBUF_POOL_SIZE 16 // Packet buffer pool sizeThe robot operates in multiple modes managed by the state machine:
- Purpose: Follow black line on white surface
- Sensors Used: IR sensor array
- Control: PID-based steering correction
- Activation: Default startup mode
- Purpose: Maintain straight line using IMU
- Sensors Used: IMU magnetometer
- Control: Heading error correction with motor differential
- Activation: When line is lost for >1 second
- Purpose: Navigate around obstacles
- Sensors Used: Ultrasonic sensor
- Control: Stop → Rotate → Resume
- Activation: Object detected within 20cm
- Purpose: Detect and act on waypoint markers
- Sensors Used: Barcode scanner
- Control: Pattern recognition → Action execution
- Activation: Barcode detected on line
- Purpose: Remote control for testing
- Sensors Used: None
- Control: Direct motor commands
- Activation: Serial command interface
- Purpose: Wireless remote control via MQTT
- Sensors Used: WiFi + MQTT client
- Control: Cloud-based commands
- Activation: MQTT command received
// Start line following
{
"command": "start",
"mode": "line_follow"
}
// Set motor speeds manually
{
"command": "motor_control",
"left_speed": 50,
"right_speed": 50
}
// Stop robot
{
"command": "stop"
}
// Request status
{
"command": "status"
}
// Update configuration
{
"command": "config",
"parameter": "heading_kp",
"value": 1.8
}Published to robot/telemetry every 1 second:
{
"timestamp": 1234567890,
"position": {"x": 1.5, "y": 2.3},
"heading": 45.2,
"speed": {"left": 42, "right": 40},
"sensors": {
"ir": [120, 450, 890, 430, 110],
"distance": 35.5
},
"battery": 7.2,
"state": "LINE_FOLLOWING",
"errors": []
}Connect via USB and monitor output:
# Linux
screen /dev/ttyACM0 115200
# macOS
screen /dev/tty.usbmodem* 115200
# Windows (using PuTTY)
# COM port, 115200 baud// Enable debug output in config.h
#define DEBUG_LEVEL_VERBOSE 3
#define DEBUG_LEVEL_INFO 2
#define DEBUG_LEVEL_WARNING 1
#define DEBUG_LEVEL_ERROR 0
#define CURRENT_DEBUG_LEVEL DEBUG_LEVEL_INFOMonitor robot telemetry via MQTT:
# Subscribe to all robot topics
mosquitto_sub -h broker.hivemq.com -t "robot/#" -v
# Subscribe to specific topics
mosquitto_sub -h broker.hivemq.com -t "robot/telemetry"
mosquitto_sub -h broker.hivemq.com -t "robot/status"
# Send commands to robot
mosquitto_pub -h broker.hivemq.com -t "robot/command" \
-m '{"command":"start","mode":"line_follow"}'
# Stop robot
mosquitto_pub -h broker.hivemq.com -t "robot/command" \
-m '{"command":"stop"}'import paho.mqtt.client as mqtt
import json
def on_connect(client, userdata, flags, rc):
print(f"Connected with result code {rc}")
client.subscribe("robot/#")
def on_message(client, userdata, msg):
print(f"{msg.topic}: {msg.payload.decode()}")
if msg.topic == "robot/telemetry":
data = json.loads(msg.payload)
print(f"Position: ({data['position']['x']}, {data['position']['y']})")
print(f"Heading: {data['heading']}°")
print(f"Speed: L={data['speed']['left']}, R={data['speed']['right']}")
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.hivemq.com", 1883, 60)
client.loop_forever()Create a simple web dashboard to visualize telemetry:
<!DOCTYPE html>
<html>
<head>
<title>Robot Monitor</title>
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
</head>
<body>
<h1>Line Following Robot Dashboard</h1>
<div id="telemetry"></div>
<script>
const client = mqtt.connect('wss://broker.hivemq.com:8884/mqtt');
client.on('connect', () => {
client.subscribe('robot/telemetry');
});
client.on('message', (topic, message) => {
const data = JSON.parse(message.toString());
document.getElementById('telemetry').innerHTML =
`<pre>${JSON.stringify(data, null, 2)}</pre>`;
});
</script>
</body>
</html>| Issue | Possible Cause | Solution |
|---|---|---|
| Robot drifts | Motor speeds unbalanced | Adjust BASE_SPEED_LEFT/RIGHT |
| Line lost frequently | IR threshold wrong | Re-calibrate IR sensors |
| Heading unstable | IMU not calibrated | Run IMU calibration routine |
| Motors not responding | Wiring issue | Check H-bridge connections |
| Erratic behavior | Power supply insufficient | Use fully charged battery |
| WiFi won't connect | Wrong credentials | Check SSID/password in config.h |
| WiFi connects but drops | Weak signal | Move closer to router |
| MQTT not connecting | Broker unreachable | Test broker with mosquitto_sub |
| MQTT publishes fail | Network congestion | Reduce publish rate |
| Telemetry data missing | Buffer overflow | Increase TELEMETRY_BUFFER_SIZE |
| High latency | Poor WiFi signal | Use 2.4GHz band, check interference |
This project follows the Barr Embedded C Coding Standard:
- ✅ All variables declared at top of function
- ✅ Snake_case for functions and variables
- ✅ SCREAMING_SNAKE_CASE for constants
- ✅ Detailed function header comments
- ✅ Magic numbers replaced with named constants
- ✅ Maximum function length: 100 lines
- ✅ Maximum line length: 80 characters
- Create driver files in
src/hardware_drivers/orsrc/control_algo/ - Create header files in corresponding
include/directory - Add to CMakeLists.txt
- Write unit tests in
test/unit_test/ - Document in header with detailed comments
- Test thoroughly before integration
# Create feature branch
git checkout -b feature/new-sensor
# Make changes
git add .
git commit -m "feat: Add new sensor driver"
# Push and create PR
git push origin feature/new-sensor| Metric | Value |
|---|---|
| Max Speed | 1.5 m/s |
| Line Following Accuracy | ±2mm deviation |
| Heading Stability | ±3° over 10 seconds |
| PID Loop Rate | 100 Hz |
| Sensor Sample Rate | 100 Hz (IMU), 50 Hz (IR) |
| Obstacle Detection Range | 2-400 cm |
| Battery Life | ~2 hours continuous operation |
| Boot Time | <2 seconds |
- ✅ Motor subsystem: 7 tests (T1-T7)
- ✅ IMU subsystem: 5 tests (T1-T5)
- ✅ IR sensors: 4 tests (T1-T4)
- ✅ Obstacle detection: 8 tests (T1-T8)
- ✅ Heading keeping: 1 comprehensive test
⚠️ Full system: Pending
Total Tests: 25
Passed: 25
Failed: 0
Coverage: ~85%
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Follow coding standards (Barr C Standard)
- Write tests for new features
- Commit your changes (
git commit -m 'feat: Add AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
type(scope): brief description
[optional body]
[optional footer]
Types: feat, fix, docs, style, refactor, test, chore
Examples:
- feat(motor): Add soft start functionality
- fix(imu): Correct heading calculation wrap-around
- docs(readme): Update pin configuration table
This project is licensed under the MIT License - see the LICENSE file for details.
- Raspberry Pi Foundation - Pico SDK and documentation
- STMicroelectronics - LSM303DLHC sensor documentation
- Barr Group - Embedded C coding standards
- Open Source Community - Various libraries and inspiration
Project Team Members: Gerald Tan, Chervelle Tan, Yip Jun Wei, Javier Wong, Bao Quan Tan Project Link: https://github.com/Gerald-codes/Line-Following-Robot
- v1.1.0: Advanced path planning with A* algorithm
- v1.2.0: Machine learning-based line detection
- v1.3.0: Multi-robot communication and coordination
- v2.0.0: Full SLAM implementation
- v2.1.0: Web-based control interface
- v2.2.0: ROS2 integration
- Core line following
- PID motor control
- IMU heading keeping
- Obstacle avoidance
- Barcode navigation (80% complete)
- Full system integration test
- Getting Started with Raspberry Pi Pico
- Introduction to PID Control
- Understanding Quadrature Encoders
⭐ Star this repository if you find it helpful! ⭐
Made with ❤️ for robotics enthusiasts