IR rpm, trigger led on with analog delay ?

Hello everyone. I am new to this board, I have come across hundreds of threads across here for information. I am I guess ( back to new ) for programming in this language… about 7 years ago I was a heavy programmer and did a lot of robotics for competitions… but I have changed my skill set to PLC controls and mechanics. So I guess I need a serious refresher haha… here’s what I am struggling to do, I have tried to modify about a dozen RPM programs I have found around the net… what I am trying to do is have an RPM reader which will trigger an LED to stay on. I would like to be able to adjust how LONG the LED stays on, with an analog input. I am aware that if the delay on the LED is on for a longer period of time than the 1 revolution of the fan is, then it will cause some confusion. so I am having a lot of trouble with interrupts and delays… essentially this is what I have/need. I have an arduino mega 2560. I am trying to simulate an injector being triggered by an RPM, and to increase how long the injector stays open for using an analog signal. right now, I can only get the injector to stay open for as long as the IR sees the white tape. so as the fan spins faster, the injector stays open for a shorter period of time, but of course will pulse faster.

  • LED connected to pin 12. to trigger ON after 2 fan revolutions, and to stay on depending on a potentiometer. to be interrupted/reset by the new 2 fan revolutions that follow.

-IR sensor (3 pin), ground, 5V, and signal connected to pin 2

-potentiometer ( 3 pin ) connected to analog A0

Any help would be greatly appreciated :smiley: thank you for reading

injector_duty_cycle.ino (902 Bytes)

Hi, and welcome !

Everything you're setting out to do is possible, presuming you know your iR sensor will be able to detect the reflection off the tape. The iR sensors I have in my kit that have that pin-out are for receiving remote control signals, so they filter out as much as possible any ambient iR and would not be fit for this purpose.

Setting the duration of the LED pulse through a potentiometer will be a simple. There's a tutorial here http://www.arduino.cc/en/Tutorial/Potentiometer that shows how easy that is (and while it varies the delay on an LED that won't be appropriate to your needs as it blocks the code while waiting for the duration). A non-blocking flashing LED sketch is included with the Arduino IDE under File / Examples / Digital. It's the first in the list, called BlinkWithoutDelay and will give you the mechanism you need - so reading the potentiometer will vary the value of variable interval in that example.

Cheers ! Geoff

so I did some more research and I kicked myself. apparently having a ( delay ) will pause anything in the program until the delay is complete, I forgot about that. now, I read through the blinkwithoutdelay and I dont really understand it. I dont know how to incorporate this to work with what I need. is it possible to ( plug ) in an analog signal to change the value of the ( long interval = 1000 ; ) line ?

I just need an IR signal to read when it goes from ( high ) to ( low ) twice, then trigger 1 LED, and that LED stays on depending on the analog input.

darkdude: is it possible to ( plug ) in an analog signal to change the value of the ( long interval = 1000 ; ) line ?

Yes it is. That's a variable declaration, so all you need to is have a line somewhere after your analogRead() of the potentiometer to assign the new value to that variable.

Next loop around it will be seeing whether that new time has lapsed since the time the LED was turned on. It really is as simple as that !

Geoff

hmm, still no luck. I think i have no clue what I'm doing at this point. I cant even incorporate the analog signal into that millis.

ok lets try a simpler one. the IR reads from High to low and back to high ( white tape on a rotating disc ) . how can I use ( high to low ) to run 1 loop only once.

so as the disc is rotating, the IR sees white tape, then back to black and triggers a loop to run once. IF the IR reads ( high to low ) again, it RESETS that previous loop and starts it over.

I played around with the following. This loop only loops once, which is exactly what I want. but I want this loop to START and RESET when my IR reads from ( high to low ). But from what I understand, if I use delays, then the program will not get around to reading My IR to reset and start the loop again. but I have no clue how to add that ( blink without delay ) program into this. I messed around with it for hours and got no where. :(

void loop() { digitalWrite(ledPin1, HIGH); delay (loadValue) ; // this is the analog input to extend or decrease the time its ON digitalWrite(ledPin1, LOW); delay (loadValue) ; // this is the analog input to extend or decrease the time its ON while(1){} }

I suggest you divide the problem into two halves.

You have an IR input which needs to be polled to measure RPMs. You have a choice of either polling the state directly and seeing how many times it changes in a defined period, or having it trigger an interrupt handler which increments a counter and then see how high the counter goes in a defined period. There are endless other ways to read RPMs and if you prefer a different way then go ahead with that. The end result should be something that outputs the measure RPM value to a global variable in your sketch that is updated regularly as the RPM changed. It isn't essential, but you should aim to do this without making your sketch stop and wait for something to happen.

The second part of your problem is to poll the RPMs variable and decide whether it is over your threshold and whether you should be taking any action as a result, and if you are already taking action whether it is time to stop taking it. The 'blink without delay' example is a good starting point for this.

well i dont actually need to read the RPM.. I just put ( rpm ) in the information above.... I should have been more clear, sorry. I meant to say, I need the IR to reset and start a loop, and that loop will go on once. I will explain exactly what this is going to be used for.

I have a white piece of tape on a crankshaft of a small engine. I have an IR directly facing the rotation. I have 1 injector facing the carb inlet. and I have a vacuum diaphragm with an analog 5V output.

what I am trying to do, is to trigger that injector to pulse every second time the crank turns. during this pulse, I will control the duty cycle of this injector using the vacuum diaphragm. I am basically making a simple fuel injection system. to test and mess around with a project I am building. I hope this clarifies ? sorry about any confusion.. but I actually dont need to read any rpm. nothing complex, just a trigger of the injector which will reset if the crank rotates another 2 times.

Hi

A couple of comments about what you provided us here: delay (loadValue) ;              // this is the analog input to extend or decrease the time its ONBecause your analogRead() which wasn't included in this fragment will be a value from 0 to 1023 inclusive, the delay could be too small to be visible, up to just over a second. Not sure if that's your intention, but if the RPM is over 60, you will be blocking your code for the next sensor revolution when it's more than half a sec potentially since you do the delay twice. Check the no delay blink sketch I referenced earlier to avoid this. while(1){}This will cause your loop to stop here after running the loop once. Since 1 is always true, this test will mean the sketch will execute the instructions inside the {} (ie nothing), forever. The sketch as it's currently written will loop through once, then stop doing anything else for you at this point.

Cheers ! Geoff

yes my intention is that the timeframe in which the injector stays open can run from (5) ms up to maybe half a second. the reason I say 5 ms, is because the SSR I have has a 5 ms responce. so that will be the minimum. it is unlikely it will be as long as half a second. The only way I know how to do it is with a ( delay ), and I am aware that when you use delays, it blocks the rest of the code until the delay is complete. However, I played with the ( blink without delay ) program, and could not figure out how to incorporate the analog signal.... nor am I able to start that loop with an IR ( falling ) input. I just dont get it -.-

However, I played with the ( blink without delay ) program, and could not figure out how to incorporate the analog signal.... nor am I able to start that loop with an IR ( falling ) input. I just dont get it -.-

Rather than saying over and over again that you don't get it, why don't you post the code you're having problems with, and maybe someone can point you in the right direction?

What are the minimum (cranking) and maximum (red line) of the RPM range over which you want to run the engine?

well that's the problem, I don't have a code, I just have pieces and bits of other codes which I am trying to put together as 1 code. but cant get anything to work together. :( Right now I have this basic program I made ( listed below ) using 2 analog signals to pulse an injector. so its not pulsing according to engine RPM, its going at my own frequency. and this was just to test out my project. now that its working, I want this to run on its own triggered by the engines needs rather than the 2 analog inputs. I have no clue how to incorporate the analog signal into the ( blink without delay ). and I have no clue how to trigger that loop with an IR input, nor reset that loop from the beginning. I haven't done any C++ in years and cant remember even the basics at this point.

and the minimum cranking ( not sure why you would need that ) but it would be around 800 idling, and 5k max.

int rpm = A1; // temporarily is analog driven rpm. int load = A0; // will be vaccuum controlled. increases or decreases OPEN on injectors, will be used for load int IR = 2; // IR trigger int ledPin1 = 3; // injector 1 int ledPin2 = 4; // injector 2 int rpmValue = 0; //analog value int loadValue = 1;

void setup() { // declare the ledPin as an OUTPUT: pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT);

} // this is a test loop for 2 injectors void loop() { // read the value from the analog potentiometers rpmValue = analogRead (rpm); loadValue = analogRead (load) ;

digitalWrite(ledPin1, HIGH); //Duty cycle ON delay (5) ; // takes 5 ms for relay to trigger on delay (loadValue) ; digitalWrite(ledPin1, LOW); //Duty cycle OFF delay (5) ; // takes 5 ms for relay to trigger off delay(rpmValue) ; digitalWrite(ledPin2, HIGH); //Duty cycle ON delay (5) ; // takes 5 ms for relay to trigger on delay (loadValue) ; digitalWrite(ledPin2, LOW); //Duty cycle OFF delay (5) ; // takes 5 ms for relay to trigger off delay(rpmValue) ;

}

Hi,

Okay let's take this to basics and make sure your inputs are doing what you expect before tackling the sketch. Which needs help too, don't get me wrong.

Firstly, let's add some debugging code in here - this will output values to the serial monitor in the IDE and give you an idea if everything is working. Add this to your setup() function:

Serial.begin(9600);

Then let's add some debug outputs in your loop():

// this is a test loop for 2 injectors 
void loop() {
  // read the value from the analog potentiometers 
  rpmValue = analogRead (rpm);
  Serial.print("rpmValue=");  // debugging
  Serial.print(rpmValue);     // debugging
  loadValue = analogRead (load) ;
  Serial.print(", loadValue="); // debugging
  Serial.println(loadValue);    // debugging
  
  digitalWrite(ledPin1, HIGH);  //Duty cycle ON
  delay (5) ;    // takes 5 ms for relay to trigger on
  delay (loadValue) ;   
  digitalWrite(ledPin1, LOW);   //Duty cycle OFF
  delay (5) ;    // takes 5 ms for relay to trigger off
  delay(rpmValue) ;  
  digitalWrite(ledPin2, HIGH);  //Duty cycle ON
  delay (5) ;    // takes 5 ms for relay to trigger on
  delay (loadValue) ;        
  digitalWrite(ledPin2, LOW);   //Duty cycle OFF
  delay (5) ;    // takes 5 ms for relay to trigger off
  delay(rpmValue) ;

}

(and to get this to format like this, just copy your sketch lines between CODE tags which are the # button in the forum btw...makes it far easier to read)

Then run your sketch and let us know what you see in the serial monitor.

Cheers ! Geoff

darkdude: I have no clue how to incorporate the analog signal into the ( blink without delay ). and I have no clue how to trigger that loop with an IR input, nor reset that loop from the beginning. I haven't done any C++ in years and cant remember even the basics at this point.

In that case I recommend that you go away and learn the basics first, because I don't see any way you're going to write this yourself otherwise. If you really want somebody to do the project for you (which is how it seems at the moment) you should post a request under Gigs and Collaborations.

Thank you strykeroz for your patience. my analog inputs are working. I used this program ( as is ) running the motor already.

I inserted that code you mentioned into the program, and in the serial monitor its reading both analog values from 0 all the way up to 1023 as mentioned as I turn the knobs. but of course, if I have one cranked up to 1023, and immediately turn it back to 0. it waits the full duration as expected.

"I recommend that you go away " tnx peter.

Alright, now I'm getting somewhere. :D

so now, I have this going pretty well. Now I just need that loop to end after the first shot. so down where it sais ( lastIRState = IRState; ) If the counter reads 2, it starts the SSR loop. which is what I want, however, it continues to loop UNTIL the IR gets triggered again.... I want that loop to only loop ONCE. I tried using the ( while ) code, but when I use it, it wont even allow the loop to start, so I get no ssr output.

Heres what I got. the last piece to the puzzle is that loop stop just once.

const int  IR = 2;    // IR input
const int ledPin = 3; // Injector SSR
int  load = A0 ;      // Analog input to increase or decrease how long the injctor stays energized 
int ledState = LOW;
int IRCounter = 0;   
int IRState = 0;       
int lastIRState = 0;     
long previousMillis = 0; 
long interval = 0;          

void setup() {
  pinMode(IR, INPUT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}


void loop() {
  IRState = digitalRead(IR);
  if (IRState != lastIRState) {
    if (IRState == HIGH) {
      IRCounter++;
      Serial.println("on");
      Serial.print("IR triggers:  ");
      Serial.println(IRCounter);
    }
    else {
    Serial.println("off");
    }
  }
  lastIRState = IRState;         // after 2 revolutions, it begins the SSR trigger
  if (IRCounter % 2 == 0) {
  unsigned long currentMillis = millis();
  interval = analogRead ( load) ;
  if(currentMillis - previousMillis > interval) {
     previousMillis = currentMillis;   
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
    digitalWrite(ledPin, ledState);
  }} else {
   digitalWrite(ledPin, LOW);
  }
}

darkdude: Alright, now I'm getting somewhere. :D

Good to hear

darkdude: If the counter reads 2, it starts the SSR loop. which is what I want, however, it continues to loop UNTIL the IR gets triggered

// after 2 revolutions, it begins the SSR trigger
  if (IRCounter % 2 == 0)

okay, here your intent and code don't match up. Using mod (the % operator) means this is true every even value of IRCounter. Tryif (IRCounter == 2)if you only want it to trigger ON 2.

Cheers! Geoff

Hello, ok I tried that.. I am not sure how it made a difference, but now with [ if (IRCounter == 2) ] I cant even get the SSR on. not sure why. o.o But is there a way to stop that loop from continuing ? instead of having all the millis commands, is there some way I can do the following ?

  if (IRCounter % 2== 0) {
    interval = analogRead ( load) ;
    
//somehow turn on the ssr, leave it on according to interval, then shut off and stop this loop.
   
  }} else {
   digitalWrite(ledPin, LOW);
  }

Can you please copy and paste here the output you're seeing on the serial monitor?

Thanks

Sure, it looks like this. ( its endless and continues as the rpms continue )

IR triggers: 363 off on IR triggers: 364 off on IR triggers: 365 off on IR triggers: 366 off on IR triggers: 367