i know not much about this stuff but my basic aim was to use an encoder to allow a dc motor to turn a pulley a certain number of rotations to raise a door in response to a photoresister reaching a certain light level, then wind the pulley in the other direction when the photoresister reaches another level winding the door back down. i've stolen code from everywhere putting it together what does it look like (i haven't bought the dc motor so wondering if it is close to be a chance to work or am i wasting my time).
const int encoderPinA = 2; //this is the pins for the encoder
const int encoderPinB = 3;
volatile long int currentPosition = 0;
const int photocellPin = A0; // the cell and 10K pulldown are connected to a0
int photocellReading; // the analog reading from the sensor divider
boolean x = true;
const int in3 = 7; // this is the pins for the h-bridge
const int in4 = 6;
void setup() {
pinMode(encoderPinA, INPUT_PULLUP);
pinMode(encoderPinB, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), doEncoderA, CHANGE);
attachInterrupt(digitalPinToInterrupt(3), doEncoderB, CHANGE);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
Serial.begin(9600);
}
void loop() {
photocellReading = analogRead(photocellPin);
Serial.print("Analog reading = ");
Serial.println(photocellReading); // the raw analog reading
delay(100);
if (photocellReading < 300 && x == true) {
currentPosition = 0; // i put these in each cause not sure if it would reset to 0 each time and wind too much
Serial.print("Position = ");
Serial.println(currentPosition);
while (currentPosition < 1800) { // the motor i was looking at has a pulse of 600 per rotation so this would be 3 rotations
digitalWrite(in3, HIGH); // this is to turn the motor in one direction
digitalWrite(in4, LOW);
}
x = false;
}
else if (photocellReading > 800 && x == false) {
currentPosition = 0;
Serial.print("Position = ");
Serial.println(currentPosition);
while (currentPosition > -1800) {
digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);
}
x = true;
}
}
// not sure if this encoder stuff is right
void doEncoderA ()
{
if (digitalRead(encoderPinA) != digitalRead(encoderPinB))
{
currentPosition++;
}
else
{
currentPosition--;
}
}
void doEncoderB()
{
if (digitalRead(encoderPinA) == digitalRead(encoderPinB))
{
currentPosition++;
}
else
{
currentPosition--;
}
}
The encoder interrupts look good. I would personally use the encoder library but it is fun to learn by writing your own.
You declared currentPosition as a long int. That's 4 bytes. An 8-bit Arduino takes 4 clock cycles to simply read that in from memory. That is 4 opportunities for the interrupts to interrupt and change the value while you are reading it.
Imagine you started with 00 00 00 FF and you read the first 3 bytes when the interrupt hit and incremented the counter by 1. Then the value is 00 00 01 00. But you see 00 00 00 00. That is a big error.
Always use noInterrupts() and interrupts() around a section where you take a "safe copy" of the volatile variables. Then use that copy in the rest of loop().
while (currentPosition < 1800) { // the motor i was looking at has a pulse of 600 per rotation so this would be 3 rotations
digitalWrite(in3, HIGH); // this is to turn the motor in one direction
digitalWrite(in4, LOW);
}
I think that reading the encoder counts in a tight while loop is not good practice. You are likely to encounter problems from reading a multi-byte variable while it is changing during an interrupt.
I would prefer to read the positional value at time increments with a millis() timer and a stable value transferred from the position.
Pseudocode
if(millis() - lastReadTime > readInterval)
{
noInterrupts();
copy_currentPosition = currentPosition;
interrupts();
if(copy_currentPosition < target)
{
digitalWrite(in3, HIGH); // this is to turn the motor in one direction
digitalWrite(in4, LOW);
}
else
{
//stopMotor
}
}
What encoder? How many pulses/revolution?
i haven't bought the dc motor
The speed of the motor and the number of counts per second are relevant. How long will it take to open and close the door? Is the encoder on geared output, or on the motor shaft?
I reckon it will be a great deal easier to use a DC motor without an encoder and just install upper and lower limit switches to enable the Arduino to know when the door is open or closed. You will need one limit switch in any case to enable the Arduino to identify the ZERO encoder position.
MorganS:
The encoder interrupts look good. I would personally use the encoder library but it is fun to learn by writing your own.
You declared currentPosition as a long int. That's 4 bytes. An 8-bit Arduino takes 4 clock cycles to simply read that in from memory. That is 4 opportunities for the interrupts to interrupt and change the value while you are reading it.
Imagine you started with 00 00 00 FF and you read the first 3 bytes when the interrupt hit and incremented the counter by 1. Then the value is 00 00 01 00. But you see 00 00 00 00. That is a big error.
Always use noInterrupts() and interrupts() around a section where you take a "safe copy" of the volatile variables. Then use that copy in the rest of loop().
there is some fun when you understand something that you've been looking for hours that you didn't previously but to be honest if i had known there was a encoder library i would have probably gone for the easiest option... i remember seeing one example where the person used some library called wire or something i think and they just wrote turn left 1000 or turn right 1000 and it seemed to work on youtube at least but i thought it could not be that simple so disregarded it.
when you say i declared that long int i took several examples and mashed them together so i didn't know the significance of it, just that it was different to what i'd seen the before. is there another option, can i call it something else and it will work more accurately or is having it a long int necessary and therefore the code the way it is written is no good?
i didn't know the no interrupts thing but from what i've read since you raised it the interrupts would be enabled all the time by default and you put nointerrupts around 'critical' parts of the code that are sensitive, from what i can gather a critical part the code where the poster below you cattledog did was:
while (currentPosition < 1800) { // the motor i was looking at has a pulse of 600 per rotation so this would be 3 rotations
digitalWrite(in3, HIGH); // this is to turn the motor in one direction
digitalWrite(in4, LOW);
}
I think that reading the encoder counts in a tight while loop is not good practice. You are likely to encounter problems from reading a multi-byte variable while it is changing during an interrupt.
I would prefer to read the positional value at time increments with a millis() timer and a stable value transferred from the position.
Pseudocode
if(millis() - lastReadTime > readInterval)
{
noInterrupts();
copy_currentPosition = currentPosition;
interrupts();
if(copy_currentPosition < target)
{
digitalWrite(in3, HIGH); // this is to turn the motor in one direction
digitalWrite(in4, LOW);
}
else
{
//stopMotor
}
}
What encoder? How many pulses/revolution?
The speed of the motor and the number of counts per second are relevant. How long will it take to open and close the door? Is the encoder on geared output, or on the motor shaft?
the 'reading the encoder counts in a tight while loop' part i made myself so that might be why it is not good practice, i couldn't work out how to get the motor to stop at a specific point so that was my attempt. when you say it is not good practice is it the kind of thing that will be disastrous because i appreciate the code you wrote but the lastReadtime, readInterval, copy_currentPosition im not too sure about in terms of defining so the chances of me implementing it all correctly has got to be low.
i was planning on getting this one off ebay not because i like it or anything but because it was the cheapest dc motor at 12V (i have a 12 battery), i was going to get it at the slowest speed which is 10rpm. It has a 'reduction ratio' of 600 for the 10rpm speed so i presume this is the 'count'. the pulley has to do 4-5 rotations so it's looking at 40-50seconds. im not sure where the encoder is but to me that does not look to be on the shaft so it may be on the gear output. my plan was i was going to put a number i thought in 'currentPosition' to rotate with reduction ratio multiplied in and i could adjust it accordingly.
Robin2:
I reckon it will be a great deal easier to use a DC motor without an encoder and just install upper and lower limit switches to enable the Arduino to know when the door is open or closed. You will need one limit switch in any case to enable the Arduino to identify the ZERO encoder position.
...R
was my attempt at code that bad robin.. i didn't know a limit switch to set it at 0 would be needed also.
do i basically put no interrupts through the entire code unless where the encoder is needed.
No. Only "protect" the small sections were the interrupt variables are read. Many other things depends on interrupts, like the millis() timer and serial printing, so disabling interrupts through the entire code can/will break other things.
The end switches Robin2 suggests will help and simplify your program. Motors with encoders do not know their absolute position, and you definitely need to deal with startup and power loss conditions. Trying to save the positional information to eeprom is a complexity you are better off avoiding. In my opinion, moving the motor until you hit the switch if far easier to code for than moving the motor for a given encoder count. Do you need any intermediate positions?
From what I see on the internet regarding similar motors, the encoder is on the shaft, so it will be turning at 600x the speed of the output, or 6000 rpm. (100 revolutions/second) I understand the encoders to be 12 pulses per revolution which means that 12, 24 or 48 quadrature transitions per revolution will be available to read. The Arduino is certainly capable of reading at these speeds.
i declared that long int i took several examples and mashed them together so i didn't know the significance of it,
You should size a variable as the smallest possible for the job.
the pulley has to do 4-5 rotations
5 rotations of the shaft will be 5*600 = 3000 rotations of the motor. If you read the minimum 12 transitions/rev that 36000 counts for your move which is a number bigger than an integer can hold. So, long int is your correct variable.
jumpingjimmy:
was my attempt at code that bad robin..
I was not commenting on the quality of your code - just about the relative ease of using a DC motor vs a stepper motor for a task that does not need the fine position control that stepper motors are designed for.
cattledog:
No. Only "protect" the small sections were the interrupt variables are read. Many other things depends on interrupts, like the millis() timer and serial printing, so disabling interrupts through the entire code can/will break other things.
The end switches Robin2 suggests will help and simplify your program. Motors with encoders do not know their absolute position, and you definitely need to deal with startup and power loss conditions. Trying to save the positional information to eeprom is a complexity you are better off avoiding. In my opinion, moving the motor until you hit the switch if far easier to code for than moving the motor for a given encoder count. Do you need any intermediate positions?
From what I see on the internet regarding similar motors, the encoder is on the shaft, so it will be turning at 600x the speed of the output, or 6000 rpm. (100 revolutions/second) I understand the encoders to be 12 pulses per revolution which means that 12, 24 or 48 quadrature transitions per revolution will be available to read. The Arduino is certainly capable of reading at these speeds.
You should size a variable as the smallest possible for the job.
5 rotations of the shaft will be 5*600 = 3000 rotations of the motor. If you read the minimum 12 transitions/rev that 36000 counts for your move which is a number bigger than an integer can hold. So, long int is your correct variable.
thanks for this reply and how you explained it, i don't need it to do intermediate positions so im gonna try the limit switch way, just a pity i spent like 2 weeks trying to do this!
Robin2:
I was not commenting on the quality of your code - just about the relative ease of using a DC motor vs a stepper motor for a task that does not need the fine position control that stepper motors are designed for.
...R
oh yeah i knew, but it might have been a bit of both too