Pulse varying based on analog input

I am still somewhat new with Arduino and programming in general. So here goes:

The program I am trying to make is using the BlinkWithoutDelay premise and generating 10ms pulses with 250ms periods. I found that i was getting more accuracy when I switched the code from using millis() to micros(). The pulse that is generated on pin 13 should not vary, ever. But the pulse that is being generated on pin 2 should be varied by the value of the analog in pin (A0).

This is working very well when the analog value is going up (closer to 5V), but when it is going down (closer to 0V), the triggers are skipping. It is as if the range of micros() it is going for has already been passed as the analog voltage is being decreased.

There are similar topics being discussed in the Demonstration code for several things at the same time discussion, but I can’t find this particular scenario.

Attached is the sketch and a screen grab of the delayed pulses.

Delayed Trigger.ino (3.49 KB)

Can you please edit and post the code in code tags, so we don't have to download it?

#include <FileIO.h>



////////TIMING SETTINGS/////////
int ledPin =  13;      // the number of the LED pin
int ledState = LOW;             // ledState used to set the LED
unsigned long previousMillis = 0;        // will store last time LED was updated
long OnTime = 10000;           // milliseconds of on-time
long OffTime = 240000;          // milliseconds of off-time

int ledPin2 =  2;      // the number of the LED pin
int ledState2 = LOW;             // ledState used to set the LED

unsigned long previousMillis2 = 0;        // will store last time LED was updated

unsigned long previousMillis4 = 0;      // will store last time LED was updated

unsigned long previousMillis6 = 0;      // will store last time LED was updated

unsigned long currentMillis = 0;

int sensorPin = A0;
unsigned long variable = 0;                          // variable for BMC calc.
unsigned long value = 0;                            // value is 500 * variable

// init settings
void setup()
{
  Serial.begin(9600);                  // Begin Serial comms.
  delay(1000);                          // Delay 1s
}


// Main loop
void loop()
{
  currentMillis = micros();      // get the time at the start of this loop()
  value = (variable * 480); //Value that is being consistantly read from A0 Pin (pressure value * 480)

  readpressure();
  mastertrigger();
  delaytrigger();
}


////////PIN READ SECTION/////////
/*
   This section reads the values on the 10 bit ADC pins as well as 2 digital Pins.
   The first reading (every 50ms)is used to calcuate BMC readings
   The second readings are read and fed into a data string, to be sent serially to a controller software.
*/
void readpressure()
{
  if (currentMillis - previousMillis4 >= 2000) // check that current time - last updated time = 20ms
  {
    variable = analogRead(sensorPin); //read value on Pin A0
    previousMillis4 = currentMillis;  //stamp time
  }
}


////////TRIGGER TIMING SECTION/////////
/*
  This section calculates the trigger without using DELAY function.
  The "BMC" is pretty OK but not great, as there is a glitch where if the bmc delay is greater than the off time, the BMC delay never reoccurs.
*/

void mastertrigger()
{
  if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime)) //check for LED on and OnTime to be equal to or exceeded.
  {
    ledState = LOW;  // Change state variable to LOW
    previousMillis = currentMillis;  // stamp the new time
    digitalWrite(ledPin, ledState);  // Update the actual LED using the value of ledstate

  }

  else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime)) //check for LED off and off time to be equal or exceeded
  {
    ledState = HIGH;  // Change state variable to HIGH

    previousMillis = currentMillis;   // stamp new time
    previousMillis6 = currentMillis;
    digitalWrite(ledPin, ledState);    // Update the actual LED using the value of ledstate

  }
}

void delaytrigger()
{
  if ((ledState2 == LOW) && (currentMillis - previousMillis2 >= OffTime) && micros() - previousMillis6 >= (value) && (micros() - previousMillis6 <= value + 1000))
  {
    ledState2 = HIGH;  // turn it on
    previousMillis2 = currentMillis;   // Remember the time
    previousMillis6 = 0;
    currentMillis = 0;
    digitalWrite(ledPin2, ledState2);    // Update the actual LED
  }

  else if ((ledState2 == HIGH) && (currentMillis - previousMillis2 >= OnTime))
  {
    ledState2 = LOW;  // Turn it off
    previousMillis2 = currentMillis;  // Remember the time
    digitalWrite(ledPin2, ledState2);  // Update the actual LED

  }
}

(Moderator to the rescue!)

  currentMillis = micros();      // get the time at the start of this loop()

Really?

aarg:

  currentMillis = micros();      // get the time at the start of this loop()

Really?

Yes, really.
Besides the variable name being different than the function, is there anything wrong with using micros instead of millis for this application?

What is the minimum analog counts value that appears to work correctly? Said another way, what is the minimum variable pulse width trigger that you can generate?

I don’t know if this has anything to do with the problem, but why do you take 2 new readings of micros() here instead of using the currentMillis reading from the top of loop() ?

void delaytrigger()
{
  if ((ledState2 == LOW) && (currentMillis - previousMillis2 >= OffTime) && micros() - previousMillis6 >= (value) && (micros() - previousMillis6 <= value + 1000))

avr_fred:
What is the minimum analog counts value that appears to work correctly? Said another way, what is the minimum variable pulse width trigger that you can generate?

Based on the Analog In, the delayed trigger can range from 0s to ~0.5s difference from the steady trigger.

This is why the Analog Reading (0-1023) is multiplied by 480:

0480 = 0us delay
1023
480 = 491040 us delay

micros has a 4us limit on resolution. Delays smaller than that would need to use a different method.

CrossRoads:
I don’t know if this has anything to do with the problem, but why do you take 2 new readings of micros() here instead of using the currentMillis reading from the top of loop() ?

void delaytrigger()

{
 if ((ledState2 == LOW) && (currentMillis - previousMillis2 >= OffTime) && micros() - previousMillis6 >= (value) && (micros() - previousMillis6 <= value + 1000))

This was an attempt to get a “newer” updated time rather than the time stamped at the beginning of the loop. I was hoping this would resolve the problem of “value” decreasing as currentMillis was increasing.

It made no difference.

Would I be better off using Timer1? Or something similar?

tgeo90:
Based on the Analog In, the delayed trigger can range from 0s to ~0.5s difference from the steady trigger.

This is why the Analog Reading (0-1023) is multiplied by 480:

0480 = 0us delay
1023
480 = 491040 us delay

That does not answer the question. Those are things I can figure out from the code. What I cannot deduce (without trying it, hard to do when all you have is a tablet) is what the real world results are, which is the information I asked for.

Ultimately, it’s all about the total time it takes to execute the instructions with each iteration through loop();. There will be a minimum pulse width that when you try to go below that value, you get no pulse at all since the elapsed time exceeds the desired pulse width so nothing happens.

If you have to generate microsecond width pulses with absolutely accuracy, you’ll need to resort to the timers. For that, start here

I just did a quick test. This has nothing to do with pulse widths, everything to do with time between pulses.

The delayed pulse fails to be generated when the delay exceeds 250 ms. Limit "value" to 250ms or 250000 and it will function as I think you want it to.

void loop()
{ 
  currentMillis = micros();      // get the time at the start of this loop()
  value = (variable * 480); //Value that is being consistantly read from A0 Pin (pressure value * 480)

  if (value > 250000) value = 250000;

  readpressure();
  mastertrigger();
  delaytrigger();
}

Image from Original Post so we don’t have to download it. See this Image Guide

Delayed Trigger.JPG

…R

tgeo90:
Yes, really.
Besides the variable name being different than the function, is there anything wrong with using micros instead of millis for this application?

You can make your code as confusing as you like, and it will still compile and run. There are even contests for that sort of thing.

avr_fred:
I just did a quick test. This has nothing to do with pulse widths, everything to do with time between pulses.

The delayed pulse fails to be generated when the delay exceeds 250 ms. Limit "value" to 250ms or 250000 and it will function as I think you want it to.

void loop()

{
  currentMillis = micros();      // get the time at the start of this loop()
  value = (variable * 480); //Value that is being consistantly read from A0 Pin (pressure value * 480)

if (value > 250000) value = 250000;

readpressure();
  mastertrigger();
  delaytrigger();
}

That worked really well. Thank you. The delayed trigger "overlooping" the main trigger and would never statisfy the if statements, therefore no delayed trigger.

My main concern is that when I have a pot hooked up to A0 and i increase the voltage, the pulse will move along the time domain nice and smooth, but as i decrease the voltage, the pulse will disappear and not return until the analog voltage is settled.

That is not what I see, the delayed pulse moves smoothly in both directions. It could be the sampling rate in your scope or some other artifact of your setup. Digital scopes can to do goofy things like that.

It could be something as simple as a noisy wiper (bad contact) in your pot that only happens in one direction. That’s easy to test, just reverse the two outer connections on the pot, that’s assuming you have it wired as a voltage divider.

avr_fred,
I've tried this several times and still cannot get the pulse to consistently trigger every 250ms as the voltage is decreasing.