How to get a fixed number of delays through arduino in a stepper motor? - arduino

I have to stop the stepper motor certain number of times (for one complete rotation) with delays as the stopping parameters.Suppose my requirement is to stop the motor 20 number of times so that my delay value should be evenly distributed between this number (20) for complete one rotation.I used a for loop for these stops(20) but i get delys more than 20.My code for arduino is given below where 8000 are the number of steps in one revolution:
#include <Stepper.h>
const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution
// for your motor
// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);
void setup() {
// set the speed at 60 rpm:
myStepper.setSpeed(60);
// initialize the serial port:
Serial.begin(9600);
}
// step one revolution in one direction:
void loop() {
int noi=20;// set the no of images here
for(int i=0;i<=noi;i++){
delay(8000/noi);
}
Serial.println("clockwise");
myStepper.step(stepsPerRevolution);
}

Your question is still confusing, but more clear than before.
It looks like you have a stepper motor that drives a turntable. The motor takes 200 steps for one revolution, but it takes 8000 steps to turn the turntable one revolution.
In one sense, all that matters is the number 8000. To make the table pause, you need to divide 8000 into equal pieces, as it looks like you attempted. But you have misplaced a }.
void loop() {
int noi=20;// set the no of images here
for(int i=0;i<=noi;i++){
delay(8000/noi);
} <<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE
Serial.println("clockwise");
myStepper.step(stepsPerRevolution);
}
void loop() {
int noi=20;// set the no of images here
for(int i=0;i<=noi;i++){
delay(enough_delay_to_take_image); // or trigger image here?
Serial.println("clockwise");
myStepper.step(8000/noi);
}
}
The only place that stepsPerRevolution = 200 matters is in calculating the speed of the movement, along with myStepper.setSpeed(60);. Do you really wanting the table to move that quickly? It might cause the object to shake too much.
myStepper.setSpeed(1);
will cause the movements betyween images to take 3 seconds. If that is too slow,
myStepper.setSpeed(3);
will cause the movements betyween images to take 1 second.

Related

Arduino timer not counting accuratly

I am working on a school project for which I rotate a servo after a cable disconnects and a specific delay is over. This is my current code. We are using an Arduino Uno powered from the USB port
#include <Servo.h>
int reader=4;
int servo1Pin=8;
Servo servo1;
int value;
int pos=10;
int wacht=5000;
void setup() {
pinMode(reader, INPUT);
servo1.attach(servo1Pin);
}
void loop() {
value = digitalRead(reader);
servo1.write(pos);
if (value == LOW) {
delay(wacht);
pos=180;
}
else {
pos=10;
}
}
wacht is the specific delay. When we disconnected pin 4 to break that circuit we don't have a consistent time between the interruption of the power flow and the opening of the servo. It seems to vary from anywhere between 5 to 40 seconds of delay after triggering. Does anyone have any ideas to solve this issue?
try change
pinMode(reader, INPUT);
to
pinMode(reader, INPUT_PULLUP);
And for clarification delay don't use timer.
The order of your instructions seem strange. the usual order is:
Read
Decide
Act
Also, you may want to avoid sending useless instructions to the servo. For example, when you know the servo is already in the correct position. You should not rely on eternal code to do the right thing. In other terms, you have no idea how long servo1.write() takes to execute.
Which would give for your loop()
void loop()
{
if (digitalRead(reader) == LOW)
{
if (pos != 180)
{
delay(wacht); // delay() is only called once, when circuit breaks.
// this guarantees immediate response when circuit
∕/ closes again.
pos = 180;
servo1.write(pos);
}
}
else if (pos != 10)
{
pos = 10;
servo1.write(pos);
}
}
Also, have a look and implement Peter Plesník's answer. This will probably solve some of your problems. You input will definitely still have a random lag of up to 5 seconds when closing the circuit, though.

How to Get Data from Rotary Encoder at VERY High Speeds (>4500 RPM)

Equipment:
Arduino ATmega2560
Rotary Encoder: https://www.amazon.ca/gp/product/B08RS6M32J/ref=ppx_yo_dt_b_asin_title_o06_s01?ie=UTF8&psc=1
Manual Curved Treadmill (what it looks like if you've never seen one before): https://www.youtube.com/watch?v=0oil6cmUCog&ab_channel=ResolutionFitness
Background:
I have a manual treadmill and I want to use my Arduino to capture distance and speed data. To do this, my approach was to use a rotary encoder, attach it to a skateboard wheel (older picture is attached, I'm using a bigger wheel now and there's no photo for that right now) such that when I run on the treadmill, the wheel spins proportionately.
In the code below, whenever I run it, the counter variable would accumulate to about 2000 (pulses) every time the wheel makes a full rotation. The wheel diameter = 6cm, therefore, it's circumference = 18.8495559215 cm.
This means for every centimeter I travel, there are about 106 pulses. (2000 pulses/18.8495559215 cm = ~106 pulses/cm).
Here's the code: (I got it from this website and only changed 2 lines of code because my sensor doesn't need to go in reverse; the treadmill only travels in one direction - https://electricdiylab.com/how-to-connect-optical-rotary-encoder-with-arduino/)
volatile long temp, counter = 0; //This variable will increase or decrease depending on the rotation of encoder
void setup() {
Serial.begin (9600);
pinMode(2, INPUT_PULLUP); // internal pullup input pin 2
pinMode(3, INPUT_PULLUP); // internalเป็น pullup input pin 3
//Setting up interrupt
//A rising pulse from encodenren activated ai0(). AttachInterrupt 0 is DigitalPin nr 2 on moust Arduino.
attachInterrupt(0, ai0, RISING);
//B rising pulse from encodenren activated ai1(). AttachInterrupt 1 is DigitalPin nr 3 on moust Arduino.
attachInterrupt(1, ai1, RISING);
}
void loop() {
// Send the value of counter
if( counter != temp && counter % 106 == 0 ){ // Only print something if the wheel travels in increments of 1 cm.
Serial.println (counter);
temp = counter;
}
}
void ai0() {
// ai0 is activated if DigitalPin nr 2 is going from LOW to HIGH
// Check pin 3 to determine the direction
if(digitalRead(3)==LOW) {
counter++;
}else{
counter++; // The original code said counter-- but my treadmill only goes one direction and the this variable starts to decrease when I reach a certain speed (around 5 kmk/h or (3.1 mph) or so). After I changed it to counter++, this issue was resolved however, the arduino serial monitor kept freezing at high speeds.
}
}
void ai1() {
// ai0 is activated if DigitalPin nr 3 is going from LOW to HIGH
// Check with pin 2 to determine the direction
if(digitalRead(2)==LOW) {
counter++; //// The original code said counter-- but my treadmill only goes one direction and the this variable starts to decrease when I reach a certain speed (around 5 kmk/h or (3.1 mph) or so). After I changed it to counter++, this issue was resolved however, the arduino serial monitor kept freezing at high speeds.
}else{
counter++;
}
}
The Problem
*To get the speed is easy, it's just not in this code right now. Accomplished by using t1 and t2 variables and the Millis() function.
When I walk on the treadmill, I typically walk at about 4 or 5 km/h (3.1 mph). This works fine. However, the issue is I want to be able to sprint over 30 km/h (18.6 mph) and have the code be able to support up to 50 km/h (31 mph). Right now, I'm unable to run at speeds over 5 km/h without the serial monitor freezing periodically, or there being inaccurate data.
At these speeds, I would need the sensor to support the wheel moving at over 4500 RPM -> 75 RPS or (75 RPS x 2000 P/R = 150,000 Pulses/Second)
I have absolutely no idea why this issue is happening and how I should approach this to achieve my desired speeds.
Possible reasons why this is happening:
I'm not an expert at Arduino at all and am just starting to understand things like interrupts. I have my pins in 2 and 3. Should I leave this alone or change it?
If you click the Amazon link, you'll see that there is a 3rd Orange "Z" wire which I have absolutely no idea what it does. The few tutorials I could find only involve the two "A" and "B" wires. Maybe incorporating the "Z" wire would point me in the right direction?
My Rotary encoder sensor is using the Arduino's 5V. If you click on the Amazon link, you'd see that it actually supports up to 26V. Should I find an external power source that allows up to 26V and pair it with a relay? Not sure if the extra voltage will help me.
I'm not sure why by default, the sensor counted a full rotation for the wheel to be 2000 pulses. On Amazon, you'll see that it supports the following Pulses/Revolution:
Resolution (pulse/rotation) : 100, 200, 300, 360, 400, 500, 600, 1000, 1024, 1200, 2000, 2500, 3600, 5000 (optional)
How can I change my 2000 P/R to 5000 P/R for example? Would this help?
Summary
I want to use my Arduino and rotary sensor to collect speed and distance data from my manual treadmill. At slow speeds (up to 5 km/h), the code works fine, but at high speeds, the data is highly inaccurate, and the serial monitor freezes every few seconds. I want the Arduino to support speeds up to 50 km/h which is about 4500 RPM. If my solution with the rotary sensor is not feasible, I am 100% open to other ideas as I just want the speed and distance data. I'll purchase any new equipment that's necessary but ideally, I'd like to work with what I have right now.
Thank you for your help!
Well I looked at the datasheet of your encoder and I was also very confused regarding the multiple values of resolution... what I suspect is that maybe there are different E6B2-CWZ6C models with differnt ppr's...
But what was useful was that I found out that your encoder is an incermental rotary encoder (this video explains better the inner workings of your type of encoder) what this means is that you have three wires A black, B white and Z the orange one.
As described on the video when your shaft completes 1 full rotation you will get N quantity of pulses from A and B, but depending on which pulse you get first would determines the rotation, according to the diagram below, if you first get a pulse from A it would mean that the rotation is CW but if you get B first it would then be CCW.
So for example lets say you start with a pulse_count=0 and start turning your shaft CW and you start counting the pulses on A, the count would go 0,1,2... and so on, but how would you know when to stop counting? that's when the Z orange cable comes in place, because it makes a pulse only after a whole rotation has been made so let's say you counted up to 200 and then you get a pulse from Z then you would know that you should restart your count back to the begining.
So to solve your problem and if you are only interested in the measured speed i would sugest to only measure how long does it take from one Z pulse to the next one, which would tell you how long it took for your encoder to complete one rotation, which could then be use to calculate the speed.
If we solve for rpms using this formula we get that RPM=(V60)/(2pir) if V=50km/h=13.88 m/s and r=3cm=0.03m we get that at 50km/h we would get 4418.14 rpm or 4418/60= 73.63 revs per sec what means 73 pulses of Z every second.
What was maybe happening is that for example if A had a resolution of 2000 ppr would imply that for every second you were receiving 73*2000 = 146000 pulses every second and as you configured your baudrate for your serial communication at Serial.begin(9600) you were only capable of reading at most 9600 pulses for each second which is way less than your 146000 pulses in A. However there are different baudrates you can set: 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200. Just be sure to select the same audrate when using the serial monitor.
For extremely high speeds you have to use bit operators and port registers.
Try this code with the equivalent pins CLK and DT connected to A2 and A1 receptively.
// Rotary Encoder Inputs
#define CLK A2
#define DT A1
int counter = 0;
String currentDir ="";
unsigned char currentPairBit = 0b0; // 8 bits
unsigned char lastPairBit = 0b0; // 8 bits
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
DDRC = 0b00000000; // Set Analog(C) encoder pins as inputs
}
void loop() {
while(true) { // while cycle is faster than loop!
// reads the analog states!
currentPairBit = PINC >> 1 & 0b11; // Gets A2(PC2) and A1(PC1) bits on C (analog input pins) (>> 1 = Jumps pin 0)
if ((lastPairBit & 0b11) != currentPairBit) {
lastPairBit = lastPairBit << 2 | currentPairBit;
// Bit Pairs Cyclic Sequence:
// 1. 2. 3. 4. 5.
// 11 | 01 | 00 | 10 | 11 for CCW
// 11 | 10 | 00 | 01 | 11 for CW
if (lastPairBit == 0b01001011 || lastPairBit == 0b10000111) {
if (lastPairBit == 0b01001011) {
currentDir = "CCW";
counter--;
} else {
currentDir = "CW";
counter++;
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
}
}
}
Then check the Serial Monitor to see how fast it is. Note that you should avoid the delay() function in your code or any other that interrupts the cycle by too much time. Consider using a second auxiliary Arduino for anything else than counting.
Resulting Serial Output:

How to control the speed of a stepper motor using PID control and ultrasonic sensor?

Little backstory:
I'm currently doing this project that deals with using two cars, called block A and B, which block B has to maintain a distance of 10 cm from block A using PID, PD, PI, or P. I'm using a PID. Block B uses an Arduino whereas Block A is controlled by the user by hand. Block B uses a unipolar stepper motor as the actuator and an ultrasonic sensor to sense the distance. My professor wants the motor to move in both directions and have varying speeds (slow, medium, and fast). I'm using brett's PID since I have used it before in my previous labs.
Problem:
I have an issue with how to create varying speeds for block B like intuitively I know that I want the B should move for example, fast if the car is greater than 20 cm, medium if the car is between 20cm and 14cm, and slow if it's between 14cm and 10cm. But I just can't use the input value retrieved from the sensor directly to control the motor as it would make it an open system. So I used the error retrieved from Brett's PID code to control the stepper motor. So far, I have gotten the directions to work by setting the myPID.SetOutputLimits(-800,800);. But as it tried to use the error to control the speed it would be impossible because the error always fluctuates at a specific distance. For example at 12cm, I would get either 800 or around 300. I'm currently confused about how to implement control of the speed of my stepper motor through PID and any help regarding this issue will be appreciated.
Code:
Code was through Arduino IDE.
#include "SR04.h"
#include <Stepper.h>
#include <PID_v1.h>
#define TRIG_PIN 7
#define ECHO_PIN 6
//intialization of Ultrasonic sensor
SR04 sr04 = SR04(ECHO_PIN,TRIG_PIN);
long s;
//intializing motor variables
int stepsPerRevolution = 2048;
int motorSpeed = 6;
Stepper myStepper (stepsPerRevolution, 8, 10, 9, 11);
//Declared PID variables
double Setpoint = 10; //desired temp value
double Input; //thermsitor
double Output; //DC motor
double Error;
//defined variables for PID parameters
double Kp=100, Ki=10, Kd=1;
//PID equation
PID myPID(&Input, &Output, &Setpoint, Kp, Kd, Ki, REVERSE);
void setup(){
Serial.begin(9600);
//setting PID
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-800,800);
//speed intialized
myStepper.setSpeed(motorSpeed);
}
void loop(){
s=sr04.Distance();
Input = s;
myPID.Compute();
Error = Input - Setpoint;
//Serial.print(Input);
//Serial.print(",");
//Serial.println(Setpoint);
Serial.println(Output);
//Serial.print(",");
//Serial.println(Error);
Error = Output;
//Away from Block B
if (0<Error<800){
myStepper.setSpeed(motorSpeed);
myStepper.step(-300);
} //slow speed
if (Error>=800){
myStepper.setSpeed(motorSpeed*2);
myStepper.step(-128);
} //fast speed
//Towards Block B
if (-800<Error<0) {
myStepper.setSpeed(motorSpeed);
myStepper.step(128);
} //slow speed
if (Error<=-800) {
myStepper.setSpeed(motorSpeed*2);
myStepper.step(128);
}//Fast speed
}
What you need to do is calcuulate how much you need to change your current speed to minimize the error in distance.
Your calculation for error is not in the right place.
void loop()
{
long s=sr04.Distance();
Input = s; // using global variables to pass values to your PID
// is not a good idea. Use function parameters instead.
// You are storing a 32 bit value in a 16 bit variable!!!
// That's only the start of your problems.
myPID.Compute();
Error = Input - Setpoint; //
Since we're starting with a major design flaw, I'll have to assume you'll fix that and change your PID code to accept and compute long integers both as input value as a function parameter, and as the type of its return value..
What you want to do is compute the PID from the error in distance from your set point, and then modulate the current speed accordingly. PIDs work best when used directly, using 7 speeds (1 stopped, 3 forward/3 backwards) is possible, but I don't think it'll give better results, I'll leave the exercise to you.
I haven't tried this, I don't have any cars on hand. This is a skeletoon of how I'd go about it. Tuning the PID should be what takes you the longest.
//...
// speeds are in RPMs.
long curSpeed = 0;
const long MAX_SPEED = XXX; // whatever you max speed is for your car.
const long MIN_NEG_SPEED = -XXX; // whatever you max speed is for your car going reverse.
const long MIN_SPEED = XXX; // below this absolute speed, we're stopped.
const int SLICE_TIME = 10; // time between readings and adjustments, in ms.
// you'll need to adjust this according to you minimum speed, and steps per turn.
const long STEPS_PER_TURN = 200; // change to whatever you steps/turn value is.
// you'll need to limit the output of your PID to match the acceleration your
// motors can handle for your particular car.
// returns the number of steps to run for our slice time.
long Steps(int speed)
{
if (-MIN_SPEED <= speed && speed <= MIN_SPEED)
return 0;
// compute number of steps for our slice time.
// choose slice time and minimum speed wisely!!
long steps = (SLICE_TIME * (speed * STEPS_PER_TURN)) / (60000L);
// for very low speeds. I've added this, because I'm unsure of the
// time domain behaviour of stepper library with less than 2 steps
if (-1 <= steps && steps <= 1)
{
if (speed < 0)
return -2;
else
return 2;
}
return int(steps);
}
void loop()
{
// You may want to filter the sr04 readings with a median of 5
// filter to limit input noise.
// You want to keep the car at a distance of 'set_point'
// from the leading car. distance_error is the error you want to
// minimize to zero by using the PID, and that's what should be
// the PID input.
//
// The way this works. We are rolling at speed curSpeed, we
// measure the error in distance from our set_point, feed that
// to the PID, then accelerate or decelerate by subtracting
// the output of the PID from the current speed.
//
// Note: you can add or subtract the PID to/from the current speed,
// the sign of the PID depends on you coefficients and sensor.
// I've used subtraction here because that's how you express
// negative feedback mathematically. In real life, we'll use what
// best fits our needs. Usually it's the operation that makes P
// positive.
long distance_error = sr04.Distance() - setPoint;
long pid_out = myPID.Compute(distance_error);
// increment or decrement the current speed to try and reduce the error.
long speed = curSpeed - pid_out; // As usual, PID coefficients do matter
// for this to work well.
if (speed > MAX_SPEED)
speed = MAX_SPEED;
if (speed < MIN_NEG_SPEED)
speed = MIN_NEG_SPEED;
curSpeed = speed;
if (speed < 0)
speed = -speed;
myStepper.setSpeed(speed); // modulate speed
int steps = Steps(curSpeed);
if (steps)
myStepper.step(steps); // keep rolling.
}
I haven't tried to compile it either, so this may not compile as is. But most of the tricks and traps are covered, and this should give you a head start, if you want to go the PID route. But I think your professor will really wonder where that one came from :) Still, you should try and make it run, for fun.
The other way, without a PID, and using set speeds is much more straightforward. It may also be closer to what the is required by the exercise. The distance between cars will vary a bit more, of course. And it does not use a PID at all.
const int MAX_SPEED = 3;
int speed = 0; // value range is [-MAX_SPEED, +MAX_SPEED]
long RPMS[MAX_SPEED + 1] = { 0, 200, 400, 800 }; // in RPMs, assuming average speed will be around 400, in this case.
// For 3 speeds, the difference between speeds cannot be higher than max acceleration.
// You can add as many speeds as desired. More speeds = more precision.
const long STEPS_PER_TURN = 200; // change to whatever you steps/turn value is. MUST be 200 or more.
const int STEPS = STEPS_PER_TURN / 100; // 3.6° between speed adjustment.
// it is very small right now, so
// you will want to play with this value.
// this threshold gives some control over aceleration.
// and 'hardness' of distance tracking.
const long THRESHOLD = 0;
void loop()
{
// get the error in distance.
long distance_error = sr04.Distance() - setPoint;
// modulate speed.
if (distance_error > THRESHOLD)
++speed;
if (distance_error < -THRESHOLD)
--speed;
if (speed > MAX_SPEED)
speed = MAX_SPEED;
if (speed < -MAX_SPEED)
speed = -MAX_SPEED;
long rpm = RPMS[(speed < 0) : -speed : speed];
if (rpm)
{
myStepper.setSpeed(rpm);
myStepper.setSpeed((speed < 0) ? -STEPS : STEPS)
}
}
For this code, you must choose speeds and STEPS value that will give you an acceleration without misssed steps.

Move mouse cursor with delays constantly and click when analog value above 300. Arduino Pro Micro

I am using Pro Micro as a USB Host and moving the cursor to predefined positions on the screen with a delay of 5 seconds. I'm using the AbsMouse library for absolute mouse cursor positions. What I want is, when the analog input goes above 300, I want it to perform XYZ function. Because I'm using delays of 5000, I can't poll the analog input always.
Basically, I want the cursor to move to these absolute positions continuously in a loop. Whenever the analog value goes above 300, it should perform the press and release functions.
I am not able to understand how to use Elapsed millis() or interrupts. Please show exactly how can it be done in code.
Help Much appreciated.
#include <AbsMouse.h>
int sensorValue = 0;
void setup()
{
AbsMouse.init(1920, 1080);
}
void loop()
{
sensorValue = analogRead(A0);
AbsMouse.move(640, 127);
delay(5000);
AbsMouse.move(640, 400);
delay(5000);
AbsMouse.move(640, 625);
delay(5000);
AbsMouse.move(1280, 127);
delay(5000);
AbsMouse.move(1280, 400);
delay(5000);
AbsMouse.move(1280, 625);
delay(5000);
if (sensorValue >= 300)
{
AbsMouse.press(MOUSE_LEFT);
AbsMouse.release(MOUSE_LEFT);
}
}
Implementing a delay of 5 seconds measuring elapsed milliseconds and reading the analog value while waiting goes like this:
startTime = millis();
while(millis()-startTime < 5000) {
sensorValue = analogRead(A0);
if (sensorValue >= 300) {
AbsMouse.press(MOUSE_LEFT);
AbsMouse.release(MOUSE_LEFT);
}
}
This has two disadvantages that you'll have to solve if needed. The first one is that the click events will be performed while the value stays above 300 (i.e. it can click more than once). The second problem, is that this is not an exact delay of 5 seconds, as it can have a jitter due to the code being executed inside the while block.
Another option, as you mentioned, is to use timer interrupts to achieve a more exact delay. Using a library like TimerOne it goes like this (inspired from library examples and modified to execute every 5 seconds):
#include <TimerOne.h>
void setup(void) {
Timer1.initialize(5000000);
Timer1.attachInterrupt(fiveSeconds);
}
void fiveSeconds(void) {
// do stuff
}
The function fiveSeconds should be executed every 5 seconds, with more precision than the millis() approach.
Now you should add code to that function, to achieve what you want to do. I'd suggest to use loop() for reading the analog value and clicking, and the interrupt for moving the mouse cursor, but your approach might be different.

Are interrupts the right thing to use for my robot?

So I have this old motorized wheelchair that I am trying to convert into a robot. I replaced the original motor driver with a sabertooth 2x12 and I’m using an Arduino micro to talk to it. The motors shaft goes all the way threw so I attached a magnet and a Hall Effect sensor to the back side to act as a rotary encoder. My current goal is to be able to tell the robot to move forward a certain amount of feet then stop. I wrote some code to do this linearly however this didn't work so well. Then I learned about interrupts and that sounded like exactly what I needed. So I tried my hand at that and things went wrong on several different levels.
LEVEL ONE: I have never seemed to be able to properly drive the motors it seems like any time I put the command to turn them on inside of a loop or if statement they decide to do what they want and move sporadically and unpredictably
LEVEL TWO: I feel like the interrupts are interrupting themselves and the thing I set in place to stop the wheels from moving forward because I can tell it to move 14 rotary encoder clicks forward and one wheel will continue moving way past 1000 clicks while the other stops
LEVEL THREE: couple times I guess I placed my interrupts wrong because when I uploaded the code windows would stop recognizing the Arduino and my driver would break until I uploaded the blink sketch after pressing the reset button which also reloaded and fixed my drivers. Then if I deleted one of my interrupts it would upload normally.
LEVEL FOUR: my Hall Effect sensors seem to not work right when the motors are on. They tend to jump from 1 to 200 clicks in a matter of seconds. This in turn floods my serial port and crashes the Arduino ide.
So as you can see there are several flaws somewhere in the system whether it’s hardware or software I don't know. Am I approaching this the right way or is there some Arduino secret I don’t know about that would make my life easier? If I am approaching this right could you take a look at my code below and see what I’m doing wrong.
#include <Servo.h>//the motor driver uses this library
Servo LEFT, RIGHT;//left wheel right wheel
int RclickNum=0;//used for the rotory encoder
int LclickNum=0;//these are the number of "clicks" each wheel has moved
int D =115;//Drive
int R =70;//Reverse
int B =90;//Break
int Linterrupt = 1;//these are the interrupt numbers. 0 = pin 3 and 1 = pin 2
int Rinterrupt = 0;
int clickConvert = 7;// how many rotery encoder clicks equal a foot
void setup()
{
Serial.begin(9600); //starting serial communication
LEFT.attach( 9, 1000, 2000);//attaching the motor controller that is acting like a servo
RIGHT.attach(10, 1000, 2000);
attachInterrupt(Linterrupt, LclickCounter, FALLING);//attaching the rotory encoders as interrupts that will
attachInterrupt(Rinterrupt, RclickCounter, FALLING);//trip when the encoder pins go from high to low
}
void loop()
{//This is for controling the robot using the standard wasd format
int input= Serial.read();
if(input == 'a')
left(2);
if(input == 'd')
right(2);
if(input == 'w')
forward(2);
if(input == 's')
backward(2);
if(input == 'e')
STOP();
}
void forward(int feet)//this is called when w is sent threw the serial port and is where i am testing all of my code.
{
interrupts(); //turn on the interrupts
while(RclickNum < feet * clickConvert || LclickNum < feet * clickConvert)// while either the left or right wheel hasnt made it to the desired distance
{
if(RclickNum < feet * clickConvert)//check if the right wheel has gone the distance
RIGHT.write(D); //make the right wheel move
else
RIGHT.write(B);//stop the right wheel
if(LclickNum < feet * clickConvert)
LEFT.write(D);
else
LEFT.write(B);
}
noInterrupts();//stop the interrupts
resetCount();//set the click counters back to zero
}
//once i have the forward function working i will implament it through out the other functions
//----------------------------------------------------------------------
void backward(int feet)
{
RIGHT.write(R);
LEFT.write(R);
}
void left(int feet)
{
RIGHT.write(D);
LEFT.write(R);
}
void right(int feet)
{
RIGHT.write(R);
LEFT.write(D);
}
void STOP()
{
resetCount();
RIGHT.write(B);
LEFT.write(B);
}
void LclickCounter()//this is called by the left encoder interrupt
{
LclickNum++;
Serial.print("L");
Serial.println(LclickNum);
}
void RclickCounter()//this is called by the right encoder interrupt
{
RclickNum++;
M Serial.print("R");
Serial.println(RclickNum);
}
void resetCount()
{
RclickNum=0;
LclickNum=0;
}
don't use interrupt() and nointerrupt() (or cli() and sei()) as they will stop timer and serial interrupt, breaking a lot of things. Just set to 0 the counting variable OR use detachInterrupt and attachInterrupt.
variable used inside interrupt AND normal execution flow should be declared as volatile, or their value my be unsyncornized. So declare them like volatile int RclickNum=0;
interrupt should be fast to execute, as by default other interrupt will NOT execute while inside an interrupt.
NEVER use Serial inside interrupt; if Serial buffer is full, it will call Serial.flush(), that will wait for Serial interrupt of byte written, but because you are alreadi inside an interrupt will never happen...dead lock aka you code hangs forever!
because your "moving" function use quite a long time to execute, if multiple command arrive to the serial, thay will remain isnode the buffer until readed. So if in the terminal you write "asd" and then "e", you will see robot go left, backward, right, stop (yes, actually the stop function is not usefull as it does nothing because your "moving" function are "blocking", that mean they won't return until they ended, so the loop() code (and the read of "e") will not execute until the buffer of serial has been processed.

Resources