Open Source Lab
Open Source Lab
Standard blueprints and firmware for high-performance robotics.
Codes
Firmware & Logic
Chassis Design
DXF & CAD Files
STL Files
3D Print Components
Back to Lab
8-Channel Sensor Array
PID Line Follower Firmware
Back to Lab
Chassis Base V2
Main Body Plate
Motor Mount
TT / N20 Bracket
Impeller Setup
For 1/1.2mm Bore Motor, Requires Spacer
Back to Codes
1. Pin-by-Pin Connection Guide
TB6612FNG Motor Driver
| Driver Pin | Arduino Pin | Function |
|---|---|---|
| VM | BAT+ | Motor Power Supply (12V Max) |
| VCC | 5V | Logic Power Supply |
| GND | GND | Common Ground |
| PWMA | D9 | Motor A Speed Control (PWM) |
| AIN1 | D8 | Motor A Direction Input 1 |
| AIN2 | D7 | Motor A Direction Input 2 |
| PWMB | D10 | Motor B Speed Control (PWM) |
| BIN1 | D5 | Motor B Direction Input 1 |
| BIN2 | D4 | Motor B Direction Input 2 |
| STBY | 5V | Standby (Must be HIGH) |
8-Channel Sensor Array
| Array Pin | Arduino Pin | Function |
|---|---|---|
| VCC | 5V | Power Supply for IR LEDs |
| GND | GND | Common Ground |
| A0 to A7 | A0 to A7 | Individual Analog Feedback |
2. Tuning Variables
- Max Robot Speed (0-255) topSpeed = 175
- Initial Starting Velocity velocity = 30
- Black Line Threshold threshold = 400
- Kp (Proportional Gain) gainP = 0.13
- Kd (Derivative Gain) gainD = 2.4
3. PID Tuning Tutorial
Step 1: Increase gainP until the robot oscillates.
Step 2: Increase gainD until motion is smooth.
Step 3: Fine-tune topSpeed for maximum performance without overshooting the line.
4. Basic Sensor Testing Code
SENSOR_TEST.ino
void setup() { Serial.begin(9600); }
void loop() {
for (int i = 0; i < 8; i++) {
Serial.print(analogRead(i));
Serial.print("\t");
}
Serial.println();
delay(100);
}
5. PID Line Follower Firmware
PID_FIRMWARE.ino
/*
8A Sensor Robot Firmware
Logic: Immediate start, 400 threshold, PID control.
*/
#ifndef CLEAR_BIT
#define CLEAR_BIT(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef SET_BIT
#define SET_BIT(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
// Pin Definitions
#define MOTOR_A_PWM 9
#define MOTOR_A_IN1 8
#define MOTOR_A_IN2 7
#define MOTOR_B_PWM 10
#define MOTOR_B_IN1 5
#define MOTOR_B_IN2 4
// Global Configuration
int finalError = 0;
bool darkLineMode = 1;
unsigned int sensorCount = 8;
// PID Components
int propPart, derPart, intPart, lastReading, driveCorrection;
double currentDeviation;
int pwrLeft, pwrRight;
int topSpeed = 175;
int velocity = 30;
int weightFactors[8] = { 4, 3, 2, 1, -1, -2, -3, -4 };
int sensorsTriggered;
// PID Gains
float gainP = 0.13;
float gainD = 2.4;
float gainI = 0;
int lineDetected = 1;
// Calibration Bounds
int floorLevels[8] = {0, 0, 0, 0, 0, 0, 0, 0};
int peakLevels[8] = {1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023};
int triggerPoint[8], valBuffer[8], digitalState[8];
void setup() {
SET_BIT(ADCSRA, ADPS2);
CLEAR_BIT(ADCSRA, ADPS1);
CLEAR_BIT(ADCSRA, ADPS0);
Serial.begin(9600);
pinMode(MOTOR_A_IN1, OUTPUT);
pinMode(MOTOR_A_IN2, OUTPUT);
pinMode(MOTOR_B_IN1, OUTPUT);
pinMode(MOTOR_B_IN2, OUTPUT);
pinMode(MOTOR_A_PWM, OUTPUT);
pinMode(MOTOR_B_PWM, OUTPUT);
pinMode(13, OUTPUT); // Status LED
pinMode(5, OUTPUT);
digitalWrite(5, HIGH); // Driver Enable
}
void loop() {
while (1) {
updateSensorData();
if (velocity < topSpeed) velocity++;
if (lineDetected == 1) {
applyPIDControl();
digitalWrite(13, HIGH);
}
else {
digitalWrite(13, LOW);
int pivotPower = 150;
// Search pattern until line is re-acquired
while (lineDetected == 0) {
if (finalError < 0) {
moveMotorA(-pivotPower);
moveMotorB(pivotPower);
} else {
moveMotorA(pivotPower);
moveMotorB(-pivotPower);
}
updateSensorData();
}
}
}
}
void executeBrake(int direction) {
if (direction <= 0) {
moveMotorA(180);
moveMotorB(-180);
}
delay(15);
}
void applyPIDControl() {
currentDeviation = 0;
sensorsTriggered = 0;
for (int i = 0; i < 8; i++) {
currentDeviation += weightFactors[i] * digitalState[i] * valBuffer[i];
sensorsTriggered += digitalState[i];
}
if (sensorsTriggered != 0) {
currentDeviation = currentDeviation / sensorsTriggered;
finalError = currentDeviation;
}
propPart = currentDeviation;
intPart = intPart + currentDeviation;
derPart = currentDeviation - lastReading;
driveCorrection = (gainP * propPart) + (gainI * intPart) + (gainD * derPart);
lastReading = currentDeviation;
pwrLeft = velocity - driveCorrection;
pwrRight = velocity + driveCorrection;
// Constrain limits for motor stability
if (pwrLeft > 255) pwrLeft = 255;
if (pwrLeft < -255) pwrLeft = -255;
if (pwrRight > 255) pwrRight = 255;
if (pwrRight < -255) pwrRight = -255;
moveMotorA(pwrRight);
moveMotorB(pwrLeft);
}
void updateSensorData() {
lineDetected = 0;
for (int i = 0; i < 8; i++) {
if (darkLineMode) {
valBuffer[i] = map(analogRead(i), floorLevels[i], peakLevels[i], 0, 1000);
} else {
valBuffer[i] = map(analogRead(i), floorLevels[i], peakLevels[i], 1000, 0);
}
valBuffer[i] = constrain(valBuffer[i], 0, 1000);
// Threshold comparison (Fixed at 400)
digitalState[i] = valBuffer[i] > 400;
if (digitalState[i]) lineDetected = 1;
}
}
void moveMotorA(int speedValue) {
speedValue = constrain(speedValue, -255, 255);
if (speedValue > 0) {
digitalWrite(MOTOR_A_IN1, 0);
digitalWrite(MOTOR_A_IN2, 1);
analogWrite(MOTOR_A_PWM, speedValue);
} else if (speedValue < 0) {
digitalWrite(MOTOR_A_IN1, 1);
digitalWrite(MOTOR_A_IN2, 0);
analogWrite(MOTOR_A_PWM, abs(speedValue));
} else {
digitalWrite(MOTOR_A_IN1, 1);
digitalWrite(MOTOR_A_IN2, 1);
analogWrite(MOTOR_A_PWM, 0);
}
}
void moveMotorB(int speedValue) {
speedValue = constrain(speedValue, -255, 255);
if (speedValue > 0) {
digitalWrite(MOTOR_B_IN1, 0);
digitalWrite(MOTOR_B_IN2, 1);
analogWrite(MOTOR_B_PWM, speedValue);
} else if (speedValue < 0) {
digitalWrite(MOTOR_B_IN1, 1);
digitalWrite(MOTOR_B_IN2, 0);
analogWrite(MOTOR_B_PWM, abs(speedValue));
} else {
digitalWrite(MOTOR_B_IN1, 1);
digitalWrite(MOTOR_B_IN2, 1);
analogWrite(MOTOR_B_PWM, 0);
}
}
// Unused reference calibration
void performManualCalib() {
for (int i = 0; i < 8; i++) {
floorLevels[i] = analogRead(i);
peakLevels[i] = analogRead(i);
}
for (int i = 0; i < 10000; i++) {
moveMotorA(70);
moveMotorB(-70);
for (int j = 0; j < 8; j++) {
int readVal = analogRead(j);
if (readVal < floorLevels[j]) floorLevels[j] = readVal;
if (readVal > peakLevels[j]) peakLevels[j] = readVal;
}
}
moveMotorA(0);
moveMotorB(0);
}
Back to STL Files
1. 3D Preview
Loading 3D model…
Drag to rotate · Scroll to zoom · Right-drag to pan
2. Download
3. Recommended Print Settings
| Parameter | Value | Notes |
|---|---|---|
| Material | PLA / PETG | PETG for impact resistance |
| Layer Height | 0.20 mm | 0.16 mm for finer detail |
| Infill | 25% | Gyroid or cubic pattern |
| Supports | Not Required | N/A |
| Nozzle | 0.4 mm |
4. Bill of Materials
- M3 × 12mm Spacer × 2
- M3 x 5mm Bolt × 12
5. Ratings & Comments
0.0
★
★
★
★
★
0 reviews
Leave your feedback
No feedback yet. Be the first!