I'm trying to make a program that will run a motor back and forth in different positions. I have a rotary optical encoder to decide wether the motor have reach the specified position or not.
I don't know how to make this program work. All I want is to rotate the motor 180 degrees, a delay of maybe 3 seconds, then rotate the motor -90 degrees, a delay of 3 seconds, then rotate the motor -90 degrees again, and a delay of 3 seconds again.
I want a program like this so I can look for errors and test if a encoder is working or not. All I know is that the encoder should loop all the time.
This is my code so far:
//pins:
//usdigital S4 optical encoder:
int a = 2;//channel a
int b = 3;//channel b
//motordriver POLULU MD05A:
int IN1_Motor = 6;//direction of motor
int IN2_Motor = 7;//direction of motor
int D2_Motor = 10;//PWM for motor
int EN = 13;//enable signal
//variables
int loopCounter = 0;
int error = 0;
int position = 0;
int degree = 0;
int velocity = 0;
int cycleCounter = 0;
int EncoderAbsoluteDegree = 0;
int counter = 0;
int cycle = 0;
int currentCycle = 0;
int valA0 = 0;
int valB0 = 0;
int valAB0 = 0;
int valA = 0;
int valB = 0;
void setup()
{
pinMode(a, INPUT);
pinMode(b, INPUT);
pinMode(IN1_Motor, OUTPUT);
pinMode(IN2_Motor, OUTPUT);
pinMode(D2_Motor, OUTPUT);
pinMode(EN, OUTPUT);
Serial.begin(9600);
}
void loop()
{
encoderCycle();
run();
}
void run()
{
degree = 180*5;
velocity = 100;
positionMotor(degree);
delay(5000);
stopMotor();
Serial.print("encoder: ");
Serial.println(cycle);
delay(1000);
degree = -160*5;
positionMotor(degree);
delay(5000);
stopMotor();
Serial.print("encoder: ");
Serial.println(cycle);
delay(1000);
}
void stopMotor()
{
digitalWrite(EN, LOW);
digitalWrite(IN1_Motor, LOW);
digitalWrite(IN2_Motor, LOW);
}
void startMotor(int pos, int Speed)
{
digitalWrite(EN, HIGH);
if(pos < 0)
{
digitalWrite(IN1_Motor, HIGH);
digitalWrite(IN2_Motor, LOW);
}
else
{
digitalWrite(IN1_Motor, LOW);
digitalWrite(IN2_Motor, HIGH);
}
analogWrite(D2_Motor, Speed);
//encoderCycle();
}
void positionMotor(int pos)
{
//encoderCycle();
if(pos != cycle)
{
startMotor(pos, velocity);
}
else
stopMotor();
}
void encoderCycle()
{
valA = digitalRead(a);
valB = digitalRead(b);
while(valA0 != valA || valB0 != valB)
{
int valAB = 0;
valAB = (10*valA) + valB;
switch (valAB)
{
case 0:
if(valAB0 == 1)
{
counter = counter -1;
}
else
{
counter = counter + 1;
}
break;
case 1:
if(valAB0 == 11)
{
counter = counter -1;
}
else
{
counter = counter + 1;
}
break;
case 11:
if(valAB0 == 10)
{
counter = counter -1;
}
else
{
counter = counter + 1;
}
break;
case 10:
if(valAB0 == 0)
{
counter = counter -1;
}
else
{
counter = counter + 1;
}
break;
}
if(counter >= 4)
{
counter = 0;
cycle += 1;
//Serial.println(cycle);
}
if(counter <= -4)
{
counter = 0;
cycle = cycle - 1;
//Serial.println(cycle);
}
if(cycle >= maximum || cycle <= minimum)
{
cycle = 0;
}
valA0 = valA;
valB0 = valB;
valAB0 = (10*valA0) + valB0;
}
}
It would help if you could say what it is doing and why you think this is wrong. Also a little more information about exactly what the decoder is and how it is wired up would help as well.
Now the Encoder (not decoder) and motor is just running in one direction for 5 seconds, then waits for 1 second and runs in opposite direction for 5 seconds, waits for 1 second and so on. It's nothing wrong with encoderCycle(); since I've build a program for just reading the encoder and it works fine. It seems like encoderCycle() stops running when the run() function is runned. I want encoderCycle() to run all the time, even when another function is running. This so the program allways can track how far the motor has rotateted.
The encoder is a S4 rotary optical encoder from usdigital.com wired up to an arduino duemilanove and attached to a toothed gear that is connected to another toothed gear that's being runned by the motor. Pin 2 and 3 on the arduino is channel a and b on the encoder.
So it is not an absolute encoder but a relative one. That means that unless you are constantly monitoring it you will miss pulses and you won't know where you are. This code has no way of doing this. You probably need to use interrupts like in this link:- http://www.arduino.cc/playground/Main/RotaryEncoders
As to why it stops at the run function:-
How far does it get, in other words what printout do you see?
It could be that your motors are causing interference and crashing the arduino. Replace the motor with a LEDs and see if it still stops. If not then you have a decoupling problem. http://www.thebox.myzen.co.uk/Tutorial/De-coupling.html
I've tried to use interrupts before but my problem there was that when I rotated the encoder 360 degrees the program counted 700 pulses or something. With encoderCycle() I get the correct amount of degrees rotated on the encoder. So how can I get the correct amount of degrees rotated on the encoder with interrupts then?
I seriously doubt there is a problem with the motor. I have a older program allmost working perfectly. When I tell the motor to rotate 160 degrees, the motor rotates around 160 degrees. The problem in that program is that the encoderCycle() function stops counting cycles when the motor have reach the specified position. That means that the encoder doesn't count the degrees that's over 160 (if the motor runs a bit more than 160 degrees). I know there is 4-5-6 degrees more actually in that time it takes the arduino to stop the motor when the encoder have reach specified position.
My printouts is "encoder: 0" all the time so I don't know how far it goes. Now the enocder obviously have no function since the motor runs for 5 seconds CW and 5 seconds CCW all the time.
encoderCycle() can only work when it is called. You code does not call it while the motor is going, I can't see how it would work. Even the calls to it you have commented out and not frequent enough to ensure you have not missed a pulse.
but my problem there was that when I rotated the encoder 360 degrees the program counted 700 pulses or something.
You will do if you do nothing to reset the count every revolution. You also need some way of determining the absolute position of the motor to initialise the degree count.
Have you got a link to the data sheet of the encoder you are using? It appears you are expecting 4 pulses per degree, that is quite a lot of resolution, 1440 PPR.
When I tell the motor to rotate 160 degrees, the motor rotates around 160 degrees.
How is that happening with the code you posted?
You need to keep on calling the positionMotor() routine and enable the encoderCycle(); in the startMotor() routine, until the motor is in the correct place, that is until you stop the motor. Only then can you delay. and start it moving in the other direction.
I seriously doubt there is a problem with the motor. I have a older program allmost working perfectly. When I tell the motor to rotate 160 degrees, the motor rotates around 160 degrees.
How is that happening with the code you posted?
This is in another program I've made some months ago.
I have an encoder with 360 CPR and there is 4 different cases before the encoder have reach 1 cycle (or am I wrong here). Is that really 1440PPR(Pulse Per Revolution?)
I have read the datasheet on: http://usdigital.com/assets/general/88_s4_datasheet_0.pdf
The encoder standard page is: S4 | US Digital
With attachInterrupt(0, encoderA, CHANGE); how can I reset the count every revolution?
Simply by having in the interrupt service routine something to the effect
if(cycles > 360) cycles -=360;
if(cycles < 0 ) cycles +=360;
assuming cycles is the variable measuring the degrees.
I solved the problem with the cycle measuring over 700 pulses or something. When I use both interrupt routines (1 for the a channel and 1 for the b channel on the encoder) I simply divides cycle by 4. (2 if I only use one interrupt routine)
But I can't get the motor to stop when the encoder has reach its specific position. I try to do this with a while in positionMotor() but it seems like it's ignoring the while and just do as in run().