Hi all, I have managed to get my Teensy Arduino to reliably count pulses using interrupts, and I'm now trying to turn that pulse rate into a speed but I'm struggling with the calculation.
The first problem I have is calculating toothDistance; it seems to equal zero, but I'm not sure why? What am I doing wrong with this calc?
Once I have this, my thought was to count the time taken between pulses and compare to the distance traveled, but I'm struggling to get it to work correctly - any help would be much appreciated... Here's what I have so far:
//Speed
volatile unsigned long VSS_count=0;
unsigned long VSS_prior_count=0;
int pin_VSS = 14;
void pulse() {
VSS_count = VSS_count + 1;
}
int SPEED_MPH;
int SPEED_STEPS;
int SPEED_SEND;
int currentMillis;
int lastMillis;
unsigned long VSS_new_count;
int teeth = 17;
float diffRatio = 3.42;
int WheelCirc = 2126; //mm
float toothDistance; //= 36566.91;
float elapsedTime;
int speedRaw;
void setup()
{
Serial.begin(9600);
toothDistance = (1/teeth)/diffRatio*WheelCirc;
//---Speed Input Count-----------------------------------
pinMode(pin_VSS, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_VSS), pulse, RISING);
NVIC_SET_PRIORITY(IRQ_PORTA, 0);
}
void loop() {
currentMillis = millis();
VSS_new_count = VSS_count;
if (VSS_new_count != VSS_prior_count) {
elapsedTime = (currentMillis - lastMillis)/1000;
speedRaw = (toothDistance)/(elapsedTime);
Serial.println(speedRaw);
//VSS_prior_count = VSS_new_count;
}
}
Ok so I have update the code to this, and now I get a string of zeros on the serial monitor with a speed populated every 50 or so lines. Also, what's the best way to work with the average pulse length, and elapsed time of maybe the last 10 pulses to try and get a stable reading..?
//Speed
volatile unsigned long VSS_count=0;
unsigned long VSS_prior_count=0;
int pin_VSS = 14;
void pulse() {
VSS_count = VSS_count + 1;
}
int SPEED_MPH;
int SPEED_STEPS;
int SPEED_SEND;
int currentMillis;
int lastMillis;
unsigned long VSS_new_count;
int teeth = 17;
unsigned long diffRatio = 3.42;
int WheelCirc = 2126; //mm
unsigned long toothDistance; //= 36566.91;
unsigned long elapsedTime;
unsigned long speedRaw;
void setup()
{
Serial.begin(9600);
//toothDistance = (1/teeth)/diffRatio*WheelCirc;
//---Speed Input Count-----------------------------------
pinMode(pin_VSS, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_VSS), pulse, RISING);
NVIC_SET_PRIORITY(IRQ_PORTA, 0);
}
void loop() {
VSS_new_count = VSS_count;
if (VSS_new_count != VSS_prior_count) {
elapsedTime = (millis() - lastMillis);
speedRaw = 60 * 1000 / (teeth * elapsedTime);
Serial.println(speedRaw);
lastMillis = millis();
}
}
Ok, I now have a reliable speed figure now coming out.. But I have 2 problems:
The first is that I wanted to construct the length traveled per pulse of the tooth wheel using "(1/teeth)/(diffRatio*WheelCirc)" but the 1/teeth part seems to always return a zero. What's the best way of dealing with this calculation?
Furthermore, I'm currently only looking at each pulse on its own. Is there a neat way of averaging maybe the last 10 values on a rolling basis to give a smoother input?
Current code:
//Speed
volatile unsigned long VSS_count=0;
unsigned long VSS_prior_count=0;
int pin_VSS = 14;
void pulse() {
VSS_count = VSS_count + 1;
}
int SPEED_MPH;
int SPEED_STEPS;
int SPEED_SEND;
int currentMillis;
int lastMillis;
unsigned long VSS_new_count;
int teeth = 17;
unsigned long diffRatio = 3.42;
int WheelCirc = 2126; //mm
float toothDistance=427701; //(1/teeth)/(diffRatio*WheelCirc); //= 36566.91;
unsigned long elapsedTime;
unsigned long speedRaw;
void setup()
{
Serial.begin(9600);
//toothDistance = (1/teeth)/(diffRatio*WheelCirc);
//---Speed Input Count-----------------------------------
pinMode(pin_VSS, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_VSS), pulse, RISING);
NVIC_SET_PRIORITY(IRQ_PORTA, 0);
}
void loop() {
if (VSS_count != VSS_prior_count) {
elapsedTime = (millis() - lastMillis);
speedRaw = (toothDistance)/(elapsedTime);
VSS_prior_count = VSS_count;
Serial.println(speedRaw/1000);
lastMillis = millis();
}
}
one way to avoid the "atomic" issue is to use bytes. are there likely to be >256 interrupts between when loop() runs? but i doubt this is the issue
the following will make the result smaller
Serial.println(speedRaw/1000);
is the integer math truncating any result to zero. making one of the constants a float by adding a decimal point will force the compiler to perform the computation using float. instead of printing RPM, check if elapsedTime make sense.
the equation is for a single increment of the count, the time between adjacent pulse.
(new - prior) * 60 * 1000.0 / (teeth * elapsedTime)
and do you really want a print every iteration of loop()? capturing the # of counts between prints will average
Thanks all for your help, I now have some code that works quite well, but the pointer on the output stepper motor is still a bit wavey... I've added the pulsegroup count so that it now averages over 10 reads. Perhaps I need to put a rolling average calculator on the SPEED_STEPS counter too?
#include <AccelStepper.h>
// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::DRIVER, 5, 6);
const int RESET = 18; // pin for RESET
//------------------------------------------
volatile unsigned long VSS_count=0;
unsigned long VSS_prior_count=0;
int pin_VSS = 14;
void pulse() {
VSS_count = VSS_count + 1;
}
int SPEED_MPH;
int SPEED_STEPS;
int SPEED_SEND;
int currentMillis;
int lastMillis;
unsigned long VSS_new_count;
int teeth = 17;
float diffRatio = 3.42;
int WheelCirc = 2126; //mm
float toothDistance=(1.0/(teeth));
float disttravelled = WheelCirc/diffRatio;
unsigned long elapsedTime;
unsigned long speedRaw;
int pulsegroup = 0;
//--------------------------------------------------
void setup()
{
pinMode(RESET, OUTPUT);
digitalWrite(RESET, LOW);
delay(1); // keep reset low min 1ms
digitalWrite(RESET, HIGH);
stepper1.setMaxSpeed(10000.0);
stepper1.setAcceleration(50000.0);
stepper1.runToNewPosition(3780);
stepper1.runToNewPosition(0);
//---------------------------------------------------
toothDistance = toothDistance*disttravelled;
//---Speed Input Count-----------------------------------
pinMode(pin_VSS, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_VSS), pulse, RISING);
NVIC_SET_PRIORITY(IRQ_PORTA, 0);
//---------------------------------------------------
}
void loop(){
noInterrupts ();
VSS_new_count = VSS_count;
interrupts ();
pulsegroup = VSS_new_count - VSS_prior_count;
if (pulsegroup >9) {
elapsedTime = (millis() - lastMillis);
speedRaw = ((VSS_new_count - VSS_prior_count)*60*1000/(toothDistance*elapsedTime))/10;
VSS_prior_count = VSS_new_count;
lastMillis = millis();
}
SPEED_STEPS = map(speedRaw,10,200,0,3780);
stepper1.moveTo(SPEED_STEPS);
stepper1.run();
}
Also here's the code for the pulse generating arduino:
/* 2126mm per tyre revolution = 470 revs per km
diff ratio 3.42 so 1607 revs per km on gearbox = 1.6074 revs per m
So 10mph = 4.4704 m/s = *3.42 diff ratio = 7.19 revs/s = 122.23 pulses /s (17 teeth on gearbox wheel) = interval time of 8.181ms = 8181
200mph = 89.408 m/s = 42.0545 revs/s *3.42 diff ratio = 143.826 GB revs/s = 2445.05 pulses /s = interval time of 0.4089ms = 409
*/
int sensorPin = A0; // select the input pin for the potentiometer
unsigned long potReading = 0; // variable to store the value coming from the sensor
unsigned long val = 0; //value to be used with map
const int speedled = 13; // the number of the LED pin //speed pulse
const int speedpin = 12; // the number of the pulse pin //speed pulse
int ledState = LOW; // ledState used to set the LED
int ledState2 = LOW; // ledState used to set the LED
unsigned long previousMicros = 0; // will store last time LED was updated
unsigned long interval = 500; // interval at which to blink (milliseconds
unsigned long time = 0;
unsigned long previousTime = 0; // will store last time Time was updated
float Hz = 0;
int i = 1;
void setup()
{
Serial.begin(115200);
// Serial.println("Start...");
// set the digital pin as output:
pinMode(speedpin, OUTPUT);
pinMode(speedled, OUTPUT);
}
void loop()
{
potReading = analogRead(sensorPin);
//Serial.println(analogRead(sensorPin));
potReading = potReading*potReading;
potReading = potReading/100;
interval = map(potReading, 10465, 100, 8181, 409); // interval adjuster from .40ms to 8.18ms //8196 1169
unsigned long currentMicros = micros();
time = currentMicros - previousMicros;
if (time > interval) // 17 cycles of HIGH to LOW on pin 14 speedpin (17 teeth on T56 speed sensor)
{
previousMicros = currentMicros;
i++;
if (i == 2)
{
time = currentMicros - previousTime;
previousTime = currentMicros;
Hz = 1000000.0 / time;
Serial.println(Hz, 2); // 2 digits
}
ledState = !ledState; // 1 -> 0 -> 1 -> 0 etc
digitalWrite(speedpin, ledState);
digitalWrite(speedled, ledState);
}
}
may i ask why you display the speed thru a stepper motor?
that is an analogue display; is that better than digital?
john.
I'm building a restomod TVR Chimaera and have decided to build my own dash display to match the Sagaris instrument pod design, but with some of my own tweaks. I really like the analogue look much more than digital dash displays so I've kept my display to an absolute minimum with a tiny 128x32 OLED to display odometer and some engine data as required. Here's a link to my build diary which has some pictures of the dash design and overall ideas: BUILD DIARY
gcjr:
someone on another forum said the used stepper motor in analog dials for aviation cockpits. Bu they used encoder to verify the position of the motor.
you may want to consider leaky integration averaging
An = (sn - An-1) / Rate
How would one include that in one's Arduino project...?
Whandall:
You still introduce possible inconsistency by using two calls of millis, why?