Hello Im an ME by degree and have not used C++ since college.
Using aruino r3 to switch an opticoupler off so the DISA valve in my bmw opens when rpm is > 3750
I used a voltage divider to change my tach signal from 9v to about 2.88v to make it safe for an input to one of the input pins.
The signal is multiplied by 3 coming off the engine tach sensor so instead of being 62.5Hz my dmm reads about 188Hz.
I can pretty much handle the if and when conditions.
What I would like help with is:
How do I set up what pin to read my tach signal in Hz freq (dont need to convert to rpm) ?
Digital or Analog pin for input?
Need it to read Freq and give Low output when > 188 Hz
And High output when < 175 Hz (dead band to prevent unwanted valve cycling)
Thanks
Connect it to one of the digital external interrupt pins and use attachInterrupt() to run a very short function whenever the pin goes HIGH. In the Interrupt Service Routine (ISR) you could have code like this
void myISR() {
latestPulseMicros = micros();
newPulse = true;
}
and then when the main body of your code sees that newPulse is true it can calculate the interval between pulses to figure out the frequency.
...R
I did a tacho for DougsAuto. It was driving a stepper motor guage, and would also do other things under certain conditions. He seemed happy with the result. You may be able to adapt the code:
Thanks for the replies.
Robin 2 what does the code look like to do what you wrote?:
Connect it to one of the digital external interrupt pins and use attachInterrupt() to run a very short function whenever the pin goes HIGH. In the Interrupt Service Routine (ISR) you could have code like this
As in I don't see how a particular pin was assigned in your example code.
Like I stated, I only used C++ in college in one course and never to actuate something like an arduino.
I've been doing some reading and internet searching and have examples that I will use for on off functions. But I'm stuck on the code to assign a particular pin as that ISR you showed.
My arduino only has this one on off function so all pins are open. Can you show me how to assign that freq input to a particular pin?
Thanks
Do you think you could adapt this for Hz:
// CLASS DEFINITION
class FanSpeed {
public:
void
setup(uint8_t irq_pin, void (*ISR_callback)(void), int value),
handleInterrupt(void);
double
getSpeed();
private:
double
_timeConstant = 60000000.0;
uint32_t
_lastMicros = 0UL,
_interval = 60000000UL;
void(*ISR_callback)();
};
void FanSpeed::setup(uint8_t irq_pin, void (*ISR_callback)(void), int value)
{
attachInterrupt(digitalPinToInterrupt(irq_pin), ISR_callback, value);
}
inline void FanSpeed::handleInterrupt(void)
{
uint32_t nowMicros = micros();
_interval = nowMicros - _lastMicros;
_lastMicros = nowMicros;
}
double FanSpeed::getSpeed()
{
if (micros() - _lastMicros < 1000000UL) // has rotated in the last second
{
return _timeConstant / _interval;
}
else
{
return 0;
}
}
// PROGRAM START
FanSpeed* fan1;
uint8_t fan1pin = 2;
FanSpeed* fan2;
uint8_t fan2pin = 3;
void setup()
{
Serial.begin(115200);
pinMode(fan1pin, INPUT_PULLUP);
pinMode(fan2pin, INPUT_PULLUP);
fan1 = new FanSpeed();
fan1->setup(fan1pin, []{fan1->handleInterrupt();}, FALLING);
fan2 = new FanSpeed();
fan2->setup(fan2pin, []{fan2->handleInterrupt();}, FALLING);
}
void loop()
{
static uint32_t lastMillis = 0;
if (millis() - lastMillis > 1000UL)
{
Serial.print(F("Fan one speed = ")); Serial.print(floor(fan1->getSpeed() + 0.5), 0);Serial.println(F(" RPM"));
Serial.print(F("Fan two speed = ")); Serial.print(floor(fan2->getSpeed() + 0.5), 0);Serial.println(F(" RPM"));
Serial.print('\r');
lastMillis = millis();
}
}
if you need some help, let me know
zfiles1701:
Robin 2 what does the code look like to do what you wrote?:
Something like this
byte pulsePin = 2;
unsigned long prevPrintMillis;
unsigned long printIntervalMillis = 1000;
unsigned long prevPulseMicros;
unsigned long newPulseMicros;
volatile unsigned long latestPulseMicros;
volatile boolean newPulse = false;
unsigned long microsBetweenPulses;
void setup() {
Serial.begin(9600);
Serial.println("Starting my program");
pinMode(pulsePin, INPUT_PULLUP);
attachInterrupt(0, pulseDetect, RISING);
}
void loop() {
if (newPulse == true) {
prevPulseMicros = newPulseMicros;
noInterrupts();
newPulseMicros = latestPulseMicros;
newPulse = false;
interrupts();
microsBetweenPulses = newPulseMicros - prevPulseMicros;
}
if (millis() - prevPrintMillis >= printIntervalMillis) {
prevPrintMillis += printIntervalMillis;
Serial.print("Micros Between Pulses ");
Serial.println(microsBetweenPulses);
}
}
void pulseDetect() {
latestPulseMicros = micros();
newPulse = true;
}
It compiles for me but I have not tested it.
Note that Interrupt 0 is actually pin 2
My arduino only has this one on off function so all pins are open
I don't know what you had in mind when you wrote that. It does not make sense to me.
...R
You digital and electrical guys have always impressed me and thanks for all the help.
Robin2
You wrote:
Note that Interrupt 0 is actually pin 2
Does that mean you put 0 by mistake, or that is addressing pin 2?
Robin2
I wrote
My arduino only has this one on off function so all pins are open
What I meant was that I have not wrote any code yet; so, no pins have been assigned yet. Project is open.
In case project was not described well enough:
Projects goal is to de-energize an opticoupler at higher than 3750 engine rpm (which will open the relay contacts which will open a valve in the intake of my engine).
And at less than 3500 engine rpm to energize the opticoupler (which will close the relay contacts which will close the valve in the intake of my engine).
Open and close a valve based on frequency of the tach signal.
the freqs should be 187.5Hz to give the LOW output
and 175Hz to give the HIGH output
Both outputs are the arduino's 5vdc to the opticoupler relay.
BulldogLowell
I wouldn't know how to adapt it for Hz. Years ago, the only things I did with C++ is get a typed input to display an output on the screen. Like I said, once I get it to read the freq in Hz I should be able to write some if and when conditions to give me my high/low outputs based on the Hz its reading. Never done the Hz input so I had no prior idea what it would look like.
Seems like a lot of code. I am a mechanical engineer by degree and very pragmatic. I like to keep things simple as possible. The project does not have to be super accurate. Is there a simpler way to accomplish the task?
Once again I appreciate all the input and help.
zfiles1701:
BulldogLowell
I wouldn't know how to adapt it for Hz.
60 RPM = 1Hz,
simple arithmetic in any programming language
zfiles1701:
You wrote:
Note that Interrupt 0 is actually pin 2
Does that mean you put 0 by mistake, or that is addressing pin 2?
Not a mistake. I just wanted you to know that the attachInterrupt() function uses '0' when it is working with pin 2. (and 1 when it is working with pin 3). RTFM
...R
Had some time to run a few examples this weekend. Ran flash from basic examples and ran button from digital examples.
I'm going to try to use the button code as a backbone and replace the button with the Pulse function.
Not going to use RPM or Hz since the Pulse is in microseconds.
I want it to actuate at 187.5Hz so I'll use 5433 micros instead.
Now getting that code you posted to replace the button is the next step.
button:
https://www.arduino.cc/en/tutorial/button
Any help would of course be appreciated.
Thanks
zfiles1701:
Now getting that code you posted to replace the button is the next step.
button:
I'm not sure what you have in mind. What have you tried?
...R
This is what I'm trying to accomplish.
something like this:
byte pulsePin = 2; //9vac tachometer signal. used voltage divider to get 2.88vac to protect input pin
outPin = 13; //output to opticoupler solenoid
unsigned long prevPrintMillis;
unsigned long printIntervalMillis = 1000;
unsigned long prevPulseMicros;
unsigned long newPulseMicros;
volatile unsigned long latestPulseMicros;
volatile boolean newPulse = false;
unsigned long microsBetweenPulses;
void setup() {
Serial.begin(9600);
Serial.println("Starting my program");
pinMode(pulsePin, INPUT_PULLUP);//the voltage divider has a resistor to ground do I need Pullup?
attachInterrupt(0, pulseDetect, RISING);
}
void loop() {
if (newPulse == true) {
prevPulseMicros = newPulseMicros;
noInterrupts();
newPulseMicros = latestPulseMicros;
newPulse = false;
interrupts();
microsBetweenPulses = newPulseMicros - prevPulseMicros;
}
if (millis() - prevPrintMillis >= printIntervalMillis) {
prevPrintMillis += printIntervalMillis;
Serial.print("Micros Between Pulses ");
Serial.println(microsBetweenPulses);
}
}
void pulseDetect() {
latestPulseMicros = micros();
newPulse = true;
}
int pulseState = 0; // variable for tach status
void setup() {
pinMode(outPin, OUTPUT);
// initialize the tach signal pin as an input:
pinMode(pulsePin, INPUT);
}
void loop() {
// read the state of the input pin value:
pulseState = digitalRead(inputPin);
// if it is >= 5433 microseconds then the outPin should de energize
if (pulseState >= 5433) {
//de energize opti which de energizes DISA solenoid which opens the DISA
digitalWrite(outPin, LOW);
} else {
//energize opti relay which energizes the DISA solenoid and closes DISA valve
digitalWrite(outPin, HIGH);
}
}
you could just look into using pulseIn() like this, though I'm not sure of the hysteresis values...
const uint32_t ON_TRIGGER = 5433; // I'm not sure about these....
const uint32_t OFF_TRIGGER = 4433;
const byte pulsePin = 2; //9vac tachometer signal. used voltage divider to get 2.88vac to protect input pin
cont byte outPin = 13; //output to opticoupler solenoid
void setup()
{
pinMode(pulsePin, INPUT); // you need to verify
pinMode(outPin, OUTPUT);
}
void loop()
{
uint32_t currentPulse = pulseIn(pulsePin, HIGH); // this blocks for one second, but you can change the timeout
if(currentPulse)
{
if (currentPulse > ON_TRIGGER)
{
digitalWrite(outPin, HIGH);
}
else if (currentPulse < OFF_TRIGGER)
{
digitalWrite(outPin, LOW);
}
}
//print to output here...
}
Bulldog
const uint32_t ON_TRIGGER = 5433; // I'm not sure about these....
const uint32_t OFF_TRIGGER = 4433;
What aren't you sure about?
pinMode(pulsePin, INPUT); // you need to verify
pinMode(outPin, OUTPUT);
what and how do I verify? do you mean select a pin?
Thanks
zfiles1701:
Bulldogconst uint32_t ON_TRIGGER = 5433; // I'm not sure about these....
const uint32_t OFF_TRIGGER = 4433;What aren't you sure about?
pinMode(pulsePin, INPUT); // you need to verify
pinMode(outPin, OUTPUT);what and how do I verify? do you mean select a pin?
Thanks
right, you need to select the threshold values for the return of the pulse length...
Also, is the pin using external pullup/pulldown resistor.
You can use a digital pin to read in the tachometer signal, just get that part of it work first, forget about everything else.
Why is the tach input multiplied by 3 ? Anyways ...
Anyways, you can use a pin change interrupt to catch the tachometer input. The interrupt routine will just increment a counter for each input.
after you get that working... Then you can use millis to calculate 60 seconds. When the time passed is greater than 60 seconds read the counter and also reset it to zero.
Or you can set up a timer interrupt that fires every 60 seconds to read the counter.
Both of these ways will give you RPM's.
BulldogLowell:
right, you need to select the threshold values for the return of the pulse length...Like my own numbers on pulse length right?
Mine: ON 5333 and OFF 5700BulldogLowell:
Also, is the pin using external pullup/pulldown resistor.Yes, the 9vac tach signal will go through a voltage divider. That will be the tach going through two in resistors in series. Pin 2 will tap off the middle of these resistors and only get 2.88vac. I made sure current was not to high for pin 2 as well.
So, one resistor will go to ground and I figured that would act as an external pullup resistor.
zfiles1701:
So, one resistor will go to ground and I figured that would act as an external pullup resistor.
that would pull down... now?
BulldogLowell:
Also, is the pin using external pullup/pulldown resistor.
Well, the Tach signal goes through a voltage divider. So signal goes through a resistor then that node has a tap that goes into pin 2, and then there is a resistor to ground in parallel. So, I think that would be an external pullup resistor. Please correct me if I'm wrong.
Please correct me if I'm wrong.
When there is no input to the voltage divider, what state is the pin in? If you can not categorically answer that, you do NOT have a pullup or pulldown resistor in use.
A picture is worth 1000 words.