Skip to content

Instantly share code, notes, and snippets.

@ShawnHymel
Last active October 25, 2023 17:13
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 15 You must be signed in to fork a gist
  • Save ShawnHymel/1de08ffaca990b65fade81cb8d01a44a to your computer and use it in GitHub Desktop.
Save ShawnHymel/1de08ffaca990b65fade81cb8d01a44a to your computer and use it in GitHub Desktop.
Use two magnetic encoders to make a robot drive in a straight line for 1 meter
/**
* Adventures in Science: Fred Bot
* SparkFun Electronics
* Author: M. Hord (October 8, 2013)
* Modified by: B. Huang (October 31, 2014)
* Modified by: Shawn Hymel (July 21, 2017)
*
* Use two magnetic encoders on Fred's motor shafts (48:1
* gearbox, 60mm wheels) to make him move in a straight line for
* 1m.
*
* License: Beerware (https://en.wikipedia.org/wiki/Beerware)
*/
// Parameters
const int drive_distance = 100; // cm
const int motor_power = 200; // 0-255
const int motor_offset = 5; // Diff. when driving straight
const int wheel_d = 60; // Wheel diameter (mm)
const float wheel_c = PI * wheel_d; // Wheel circumference (mm)
const int counts_per_rev = 384; // (4 pairs N-S) * (48:1 gearbox) * (2 falling/rising edges) = 384
// Pins
const int enc_l_pin = 2; // Motor A
const int enc_r_pin = 3; // Motor B
const int pwma_pin = 5;
const int ain1_pin = 9;
const int ain2_pin = 4;
const int pwmb_pin = 6;
const int bin1_pin = 7;
const int bin2_pin = 8;
const int stby_pin = 10;
// Globals
volatile unsigned long enc_l = 0;
volatile unsigned long enc_r = 0;
void setup() {
// Debug
Serial.begin(9600);
// Set up pins
pinMode(enc_l_pin, INPUT_PULLUP);
pinMode(enc_r_pin, INPUT_PULLUP);
pinMode(pwma_pin, OUTPUT);
pinMode(ain1_pin, OUTPUT);
pinMode(ain2_pin, OUTPUT);
pinMode(pwmb_pin, OUTPUT);
pinMode(bin1_pin, OUTPUT);
pinMode(bin2_pin, OUTPUT);
pinMode(stby_pin, OUTPUT);
// Set up interrupts
attachInterrupt(digitalPinToInterrupt(enc_l_pin), countLeft, CHANGE);
attachInterrupt(digitalPinToInterrupt(enc_r_pin), countRight, CHANGE);
// Drive straight
delay(1000);
enableMotors(true);
driveStraight(drive_distance, motor_power);
}
void loop() {
// Do nothing
}
void driveStraight(float dist, int power) {
unsigned long num_ticks_l;
unsigned long num_ticks_r;
// Set initial motor power
int power_l = motor_power;
int power_r = motor_power;
// Used to determine which way to turn to adjust
unsigned long diff_l;
unsigned long diff_r;
// Reset encoder counts
enc_l = 0;
enc_r = 0;
// Remember previous encoder counts
unsigned long enc_l_prev = enc_l;
unsigned long enc_r_prev = enc_r;
// Calculate target number of ticks
float num_rev = (dist * 10) / wheel_c; // Convert to mm
unsigned long target_count = num_rev * counts_per_rev;
// Debug
Serial.print("Driving for ");
Serial.print(dist);
Serial.print(" cm (");
Serial.print(target_count);
Serial.print(" ticks) at ");
Serial.print(power);
Serial.println(" motor power");
// Drive until one of the encoders reaches desired count
while ( (enc_l < target_count) && (enc_r < target_count) ) {
// Sample number of encoder ticks
num_ticks_l = enc_l;
num_ticks_r = enc_r;
// Print out current number of ticks
Serial.print(num_ticks_l);
Serial.print("\t");
Serial.println(num_ticks_r);
// Drive
drive(power_l, power_r);
// Number of ticks counted since last time
diff_l = num_ticks_l - enc_l_prev;
diff_r = num_ticks_r - enc_r_prev;
// Store current tick counter for next time
enc_l_prev = num_ticks_l;
enc_r_prev = num_ticks_r;
// If left is faster, slow it down and speed up right
if ( diff_l > diff_r ) {
power_l -= motor_offset;
power_r += motor_offset;
}
// If right is faster, slow it down and speed up left
if ( diff_l < diff_r ) {
power_l += motor_offset;
power_r -= motor_offset;
}
// Brief pause to let motors respond
delay(20);
}
// Brake
brake();
}
void enableMotors(boolean en) {
if ( en ) {
digitalWrite(stby_pin, HIGH);
} else {
digitalWrite(stby_pin, LOW);
}
}
void drive(int power_a, int power_b) {
// Constrain power to between -255 and 255
power_a = constrain(power_a, -255, 255);
power_b = constrain(power_b, -255, 255);
// Left motor direction
if ( power_a < 0 ) {
digitalWrite(ain1_pin, LOW);
digitalWrite(ain2_pin, HIGH);
} else {
digitalWrite(ain1_pin, HIGH);
digitalWrite(ain2_pin, LOW);
}
// Right motor direction
if ( power_b < 0 ) {
digitalWrite(bin1_pin, LOW);
digitalWrite(bin2_pin, HIGH);
} else {
digitalWrite(bin1_pin, HIGH);
digitalWrite(bin2_pin, LOW);
}
// Set speed
analogWrite(pwma_pin, abs(power_a));
analogWrite(pwmb_pin, abs(power_b));
}
void brake() {
digitalWrite(ain1_pin, LOW);
digitalWrite(ain2_pin, LOW);
digitalWrite(bin1_pin, LOW);
digitalWrite(bin2_pin, LOW);
analogWrite(pwma_pin, 0);
analogWrite(pwmb_pin, 0);
}
void countLeft() {
enc_l++;
}
void countRight() {
enc_r++;
}
@DanielStewart82
Copy link

Here are my two cents. For whatever it is worth.

I got this code to work after a tiny bit of messing around. I designed a circuit in Tinkercad to test it first, before I built a physical model.

For some reason I couldn't get Serial.println to return any values until I turned down the delay on line 59 from 1000 to 75. Then I had a reading and the code worked.

The motor driver I used didn't have a standby. So I didn't need to initialise pin 10 or use the EnableMotors function from line 145 or use the function call on line 60. Easy peasy.

When I finally built the model in real life, I had a problem where the robot would detect slippage or a difference in wheel speed then spin a wheel in the opposite direction. This was making it impossible to use. To get around this I changed the constrain power on lines 156 and 157 so that the values couldn't go negative anymore. Got rid of the -255 and replaced it with a 0. It worked for me.

This code saved my bacon and I hope that my comments here save someone else a few hours of their day.

Dan

@dilipkumar003
Copy link

can u share the code to run a single encoder motor to required limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment