Hello!
I am using Arduino Uno and an E50S8-1000-3-T-24 encoder. Arduino should read pulses from encoder, calculate RPM and then trigger stepper code functions, when encoder is turning above 20RPM and stop stepper, when encoder RPM is less than 20.
Stepper code is working properly if tested seperately.
I have trouble getting my functions to work properly. I don't really understand it, maybe encoder reading isn't stable? Or there is some obvious problems with my code?
Here's the code:
#include <AccelStepper.h>
AccelStepper stepper(AccelStepper::DRIVER, 4, 5);
int StepCounter = 0;
int i = 0;
int Home = true;
int WorkSequence = false;
int CW;
// Define our analog pot input pin
#define SPEED_PIN 0
// Define our maximum and minimum speed in steps per second (scale pot to these)
#define MAX_SPEED 2000
#define MIN_SPEED 100
// read RPM .
const int numreadings = 10;
int readings[numreadings];
unsigned long average = 0;
int index = 0;
unsigned long total;
const byte interruptPin = 2;
volatile int rpmcount = 0;
int rpm = 0;
unsigned long lastmillis = 0;
void setup() {
pinMode(5, OUTPUT);
pinMode(4, OUTPUT);
pinMode(7, OUTPUT);
digitalWrite(5, HIGH);
digitalWrite(4, LOW);
stepper.setMaxSpeed(3000);
pinMode(9, INPUT_PULLUP);
pinMode(10, INPUT_PULLUP);
pinMode(6,INPUT_PULLUP);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), rpm_shaft, FALLING);
}
void loop() {
Homing();
BackAndForth();
if (millis() - lastmillis >= 1000){ /*Uptade every one second, this will be equal to reading frecuency (Hz).*/
detachInterrupt(digitalPinToInterrupt(interruptPin)); //Disable interrupt when calculating
total = 0;
readings[index] = (rpmcount * 60)/1000; /* Convert frecuency to RPM */
for (int x=0; x<=9; x++){
total = total + readings[x];
}
average = total / numreadings;
rpm = average;
rpmcount = 0; // Restart the RPM counter
index++;
if(index >= numreadings){
index=0;
}
lastmillis = millis(); // Uptade lasmillis
attachInterrupt(digitalPinToInterrupt(interruptPin), rpm_shaft, FALLING); //enable interrupt
}
}
void rpm_shaft(){ /* this code will be executed every time the interrupt (pin2) gets low.*/
rpmcount++;
}
Rather than use detachInterrupt() just get the value from the ISR like this
prevRpmCount = latestRpmCount; // save the current value
noInterrupts();
latestRpmCount = rpmcount; // get the new value
interrupts()
countsThisPeriod = latestRpmCount - prevRpmCount;
That way the interrupts will be disabled for the shortest possible time.
Note that by using subtraction there is no problem if the value in rpmcount overflows and there is no need to zero it. However to make that work you must change this
volatile int rpmcount = 0;
to
volatile unsigned int rpmcount = 0;
Having said all that the timing will almost certainly be more accurate if you get the number of microseconds for a specific number of encoder pulses - perhaps with an ISR like this
void rpm_shaft() {
static byte pulseCount = 0;
pulseCount ++;
if (pulseCount == 8) { // if 8 is not suitable use a more appropriate number
latestPulseMicros = micros();
pulseCount = 0;
newTimeValue = true;
}
}
I still can't get rpm or even latestRpmCount values. Serial monitor just shows "0" , when turning the encoder. But, when I try Serial.println(rpmcount); , it shows all the pulses perfectly.
I am a beginner at programming, but I should really get this project done as soon as possible.
Here's the loop:
void loop() {
Homing();
BackAndForth();
if (millis() - lastmillis >= 1000){ /*Uptade every one second, this will be equal to reading frecuency (Hz).*/
prevRpmCount = latestRpmCount; // save the current value
noInterrupts();
latestRpmCount = rpmcount; // get the new value
interrupts();
countsThisPeriod = latestRpmCount - prevRpmCount;
rpm = (countsThisPeriod/1000)*60; //Encoder gives 1000pulses per sec
}
if (millis() > 10000){ // wait for RPMs average to get stable
Serial.println(latestRpmCount);
Serial.println(rpm);
}
lastmillis = millis(); // Uptade lasmillis
}
void rpm_shaft(){ /* this code will be executed every time the interrupt 0 (pin2) gets low.*/
rpmcount++;
}
Also, what variables should I use for prevRpmCount , latestRpmCount, countsThisPeriod?
unsigned long prevRpmCount = 0;
unsigned long latestRpmCount = 0;
unsigned long countsThisPeriod ;
In serial monitor, rpm value is "0" even when turning the encoder.
#include <AccelStepper.h>
AccelStepper stepper(AccelStepper::DRIVER, 4, 5);
int StepCounter = 0;
int i = 0;
int Home = true;
int WorkSequence = false;
int CW;
int motor_speed = 1500;
// Define our analog pot input pin
#define SPEED_PIN 0
// Define our maximum and minimum speed in steps per second (scale pot to these)
#define MAX_SPEED 4000
#define MIN_SPEED 100
// read RPM and calculate average every then readings.
const int numreadings = 10;
int readings[numreadings];
unsigned long average = 0;
int index = 0;
unsigned long total;
const byte interruptPin = 2;
volatile unsigned int rpmcount = 0;
int rpm = 0;
unsigned long lastmillis = 0;
unsigned long prevRpmCount = 0;
unsigned long latestRpmCount = 0;
unsigned long countsThisPeriod ;
void setup() {
pinMode(5, OUTPUT);
pinMode(4, OUTPUT);
pinMode(7, OUTPUT);
digitalWrite(5, HIGH);
digitalWrite(4, LOW);
stepper.setMaxSpeed(3000);
pinMode(9, INPUT_PULLUP);
pinMode(10, INPUT_PULLUP);
pinMode(6,INPUT_PULLUP);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), rpm_shaft, FALLING);
Serial.begin(9600);
}
void loop() {
Homing();
BackAndForth();
if (millis() - lastmillis >= 1000){ /*Uptade every one second, this will be equal to reading frecuency (Hz).*/
prevRpmCount = latestRpmCount; // save the current value
noInterrupts();
latestRpmCount = rpmcount; // get the new value
interrupts();
countsThisPeriod = latestRpmCount - prevRpmCount;
rpm = (countsThisPeriod/1000)*60; // Encoder has 1000pulses per rev
}
if (millis() > 10000){ // wait for RPMs average to get stable
Serial.println(rpm);
Serial.println(latestRpmCount);
}
lastmillis = millis(); // Uptade lasmillis
}
void rpm_shaft(){ /* this code will be executed every time the interrupt 0 (pin2) gets low.*/
rpmcount++;
}
Well, this is the code, that just reads encoder, calculates rpm and should display RPM, but it only shows "0"in serial monitor.
Encoder is working, if I try to Serial.println(rpmcount); , it shows all pulses precisely. There must be a problem in getting the interrupt value and calculation, i guess?
const byte interruptPin = 2;
volatile unsigned int rpmcount = 0;
int rpm = 0;
unsigned long lastmillis = 0;
unsigned long prevRpmCount = 0;
unsigned long latestRpmCount = 0;
unsigned long countsThisPeriod ;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), rpm_shaft, FALLING);
Serial.begin(9600);
}
void loop() {
if (millis() - lastmillis >= 1000){ /*Uptade every one second, this will be equal to reading frecuency (Hz).*/
prevRpmCount = latestRpmCount; // save the current value
noInterrupts();
latestRpmCount = rpmcount; // get the new value
interrupts();
countsThisPeriod = latestRpmCount - prevRpmCount;
rpm = (countsThisPeriod/1000)*60; // Encoder has 1000pulses per rev
}
if (millis() > 10000){ // wait for RPMs average to get stable
Serial.println(rpm);
}
lastmillis = millis(); // Uptade lasmillis
}
void rpm_shaft(){ /* this code will be executed every time the interrupt 0 (pin2) gets low.*/
rpmcount++;
}
if countsThisPeriod is less than 1000 it will give a zero answer as you are doing integer maths. Things may be better if you do the multiplication first - but probably still not adequate. Do you really need to know the speed in RPM?
Try printing the variable countsThisPeriod
And this will print in every iteration of loop() once the Arduino has been running for 10 seconds. There does not seem to be any need to print the value more often than it is updated.
if (millis() > 10000){ // wait for RPMs average to get stable
Serial.println(rpm);
}
PaulS:
Add an LED. In the (stupidly named) rpm_shaft() function, toggle the LED. Does the ISR even get called?
Sorry for stupid naming, english is not my first language
Yes, checked with a LED too, ISR gets called.
Also, if I try to print rpmcount from , it shows all the pulses correctly in serial monitor.
Robin2:
This is very unlikely to work
rpm = (countsThisPeriod/1000)*60;
if countsThisPeriod is less than 1000 it will give a zero answer as you are doing integer maths. Things may be better if you do the multiplication first - but probably still not adequate. Do you really need to know the speed in RPM?
Try printing the variable countsThisPeriod
Tried printing countsThisPeriod, still shows only "0". Got it with integer maths, I guess I don't really need speed in RPM, but still, if I get only zero values for countsThisPeriod, it doesn't really help.
if (millis() - lastmillis >= 1000) { /*Uptade every one second, this will be equal to reading frecuency (Hz).*/
// prevRpmCount = latestRpmCount; // save the current value
noInterrupts();
latestRpmCount = rpmcount; // get the new value
interrupts();
countsThisPeriod = latestRpmCount - prevRpmCount;
rpm = (countsThisPeriod / 1000) * 60; // Encoder has 1000pulses per rev
prevRpmCount = latestRpmCount; // save the current value
}
TomGeorge:
Hi,
Can you try this order of statements;
if (millis() - lastmillis >= 1000) { /*Uptade every one second, this will be equal to reading frecuency (Hz).*/
// prevRpmCount = latestRpmCount; // save the current value
noInterrupts();
latestRpmCount = rpmcount; // get the new value
interrupts();
countsThisPeriod = latestRpmCount - prevRpmCount;
rpm = (countsThisPeriod / 1000) * 60; // Encoder has 1000pulses per rev
prevRpmCount = latestRpmCount; // save the current value
}
Tom... :)
Tried, nothing changed.
Also, latestRpmCount print shows only zero values. It should be equal to rpmcount value, right?
Huge thanks to you, guys, just had to put lastmillis = millis(); in the if loop, and it worked!
if (millis() - lastmillis >= 1000) { /*Uptade every one second, this will be equal to reading frecuency (Hz).*/
noInterrupts();
latestRpmCount = rpmcount; // get the new value
interrupts();
countsThisPeriod = latestRpmCount - prevRpmCount;
rpm = (countsThisPeriod) * 60/1000; // Encoder has 1000pulses per rev
prevRpmCount = latestRpmCount; // save the current value
Serial.println(rpm);
lastmillis = millis(); // Update lastmillis
}
ArthurK6:
Tried printing countsThisPeriod, still shows only "0". Got it with integer maths, I guess I don't really need speed in RPM, but still, if I get only zero values for countsThisPeriod, it doesn't really help.
Never stop at the first test. Test everything possible.
Have you tried printing latestRpmCount ?
Have you tried changing
volatile unsigned int rpmcount = 0;
to
volatile unsigned long rpmcount = 0;
in case your timing interval is exactly the duration to cause the romcount variable to overflow back to 0?
To be honest, my guess is that your ISR is not detecting anything. How is everything connected? Make a pencil drawing showing all the connections and post a photo of the drawing.
Hi,
What are you using to power your E50S8-1000-3-T-24 encoder?
It has totem pole output, so you do not need an output pullup resistor.
BUT it is the 24Vdc model.
So the totem pole output will swing from 0V to 24V.
Have you got a potential divider on the Arduino inputs to bring the output of the encoder down to 0V to 5V?
Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
TomGeorge:
What are you using to power your E50S8-1000-3-T-24 encoder?
It has totem pole output, so you do not need an output pullup resistor.
BUT it is the 24Vdc model.
So the totem pole output will swing from 0V to 24V.
Have you got a potential divider on the Arduino inputs to bring the output of the encoder down to 0V to 5V?
I made a voltage divider with resistors to get the output down to 5V, and I power the encoder from Arduino Vin pin, which gives about 12V (because I power Arduino with 12V and encoder works with 12-24V).
I have it connected to Arduino pin2 without pullup resistor