Using millis() to run two functions is causing my code to "crash"

Hi,

I am currently doing an ultrasonic project that will behave closely to that of a reversing car system with audible beeps like on a car. I have removed the blocking code as much as I believe I can to have the system flowing as much as possible and aimed to use millis() as much as I can. I have attached my working version of the code.

I am though having an issue running the entire system as the same time. In separate code files I have the piezo speaking working exactly as desired and the ultrasonic sensor also using millis() with no issues. The main problem arises when I attempt to have them both working at the same time as the stability of the sensors fails and the system crashes as soon as the piezo starts to work. Where If I comment out the "BUZZER_ALERT(state)" function the sensors work as expected and are stable.

Where the ideal operation is the buzzer sounds the alert noise dependent on the distance that it is away from an object and the noise is emitted using non blocking code ( such as delay() ). Where if an object is detected across the 4 sensors it will drive a relay high and act as a way of forcefully stopping the system, a bit like an emergency stop. I am using multiplexers within my project as well so add a greater number of sensors to my project if needed to increase the detectable range and sensitivity.

Am I not able to have two functions each using millis() on the same timer and have them change their state or operating condition as and when the distance measured by an ultrasonic sensor changes?

If I need to further clarify things or add more detail please let me know, have been stuck on this for more than I'd like to admit :o .

V5.3.ino (14.4 KB)

Why have you thought it necessary to comment-out line 89? To my mind that is the ideal place to capture the value of millis() for use within the current iteration of loop().

My guess is that your use of WHILE and FOR is getting in the way as they are blocking constructs that must complete before they allow control to other parts. Much better to use IF and allow loop() to do the iteration.

Have a look at how the code is organized in Several Things at a Time

Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

...R

The tone() routine produces a sound by having a timer interrupt the processor at twice the frequency of the tone. As your tone is at 1kHz the processor gets interrupted about every 15cm of the ultrasound traveling to the target. This most probably influences the distances you get from the sensor. So I'd expect the sensors to work but to produce a quite wide field of measurement error. It that correct?

I have removed the blocking code as much as I believe I can to have the system flowing as much as possible and aimed to use millis() as much as I can. I have attached my working version of the code.

@Robin2 says My guess is that your use of WHILE and FOR is getting in the way as they are blocking constructs that must complete before they allow control to other parts. Much better to use IF and allow loop() to do the iteration.

One of the blocking FOR loops is your ultrasonic pulse reading of multiple sensors using pulseIn() which is a blocking function. You throw in 64 analogRead() calls as well with Get_Temp().

// Function used to pulse the ultrasonic sensors attached to the hardware
void Pulse_Sensors(unsigned long currentMillisUS, byte val)
{ 
  while(compensatedDIS<=0)
  {
    currentMillisUS = millis();
    if((US_TRIG == HIGH) && (currentMillisUS - previousMillisUS >= US_TRIG_HT))
      {
        US_TRIG = LOW;
        
        previousMillisUS = currentMillisUS;
        digitalWrite(trigPin, HIGH);
      }
      else if ((US_TRIG == LOW) && (currentMillisUS - previousMillisUS >= US_TRIG_LT))
      {
        US_TRIG = HIGH;
        previousMillisUS = currentMillisUS;
        digitalWrite(trigPin, LOW);
        printSTATE= HIGH;;
      }
      duration = pulseIn(echoPin[val], HIGH, 100000);    // Get the duration of the incoming ECHO
      distance = duration * 0.034 / 2;
      Get_Temp();
      float speedOfSound=331.3+0.606*tempC;
      compensatedDIS = (duration/20000.0) * speedOfSound;
  }
}

Your ultrasonic distance measurement could be based on interrupts and not pulseIn() in order to be non blocking.

Am I not able to have two functions each using millis()

You've had lots of good answers I can't improve on but if anyone answered this I missed it. You can use millis() as many times as you like, wherever you like.

pylon:
The tone() routine produces a sound by having a timer interrupt the processor at twice the frequency of the tone. As your tone is at 1kHz the processor gets interrupted about every 15cm of the ultrasound traveling to the target. This most probably influences the distances you get from the sensor. So I'd expect the sensors to work but to produce a quite wide field of measurement error. It that correct?

Yes this is correct.

"
us1.0==37.89 us1.1==37.19 us1.2==37.17 us1.3==40.12 38.09 41.90 34.28 Total Distance ==152.37 1
us1.0==37.80 us1.1==37.16 us1.2==37.19 us1.3==40.00 38.04 41.84 34.23 Total Distance ==152.15 1
us1.0==37.93 us1.1==37.04 us1.2==37.18 us1.3==40.12 38.07 41.87 34.26 Total Distance ==152.27 1
us1.0==37.91 us1.1==37.18 us1.2==37.19 us1.3==39.68 37.99 41.79 34.19 Total Distance ==151.97 1
us1.0==37.90 us1.1==37.20 us1.2==37.14 us1.3==39.57 37.95 41.75 34.16 Total Distance ==151.80 1
"
The above result from the serial is the standard results I get when the BUZZER_ALERT is commented out.

The below results is what I get when the BUZZER_ALERT is not commented out and active in the code.

"
us1.0==38.00 us1.1==37.20 us1.2==37.19 us1.3==39.66 38.01 41.81 34.21 Total Distance ==152.05 1
us1.0==37.90 us1.1==37.16 us1.2==37.15 us1.3==39.62 stop sound 37.95 41.75 34.16 Total Distance ==151.82 1
us1.0==37.93 us1.1==37.21 us1.2==37.16 us1.3==39.12 start sound 37.85 41.64 34.07 Total Distance ==151.42 1
us1.0==26.39 us1.1==27.11 us1.2==26.71 us1.3==27.36 26.89 29.58 24.20 Total Distance ==107.58 1
us1.0==30.03 us1.1==27.45 us1.2==26.79 us1.3==27.41 27.92 30.71 25.13 Total Distance ==111.69 1
us1.0==30.32 us1.1==27.03 us1.2==27.08 us1.3==27.08 27.88 30.66 25.09 Total Distance ==111.50 1
us1.0==22.72 us1.1==26.79 us1.2==26.83 us1.3==27.13 25.87 28.45 23.28 Total Distance ==103.47 0
"
Once I attempt to use the BUZZER_ALERT after 1 cycle of the buzzer function the distances from the sensors are jumping around all over the place and causes the "error" condition to occur and drives my relay high.

Robin2:
Why have you thought it necessary to comment-out line 89? To my mind that is the ideal place to capture the value of millis() for use within the current iteration of loop().

I was trying different places for the millis() to see if it had an effect on things, in this case, it didn't do that much as far as I am aware.

Robin2:
My guess is that your use of WHILE and FOR is getting in the way as they are blocking constructs that must complete before they allow control to other parts. Much better to use IF and allow loop() to do the iteration.

I did a for while loop at first to guarantee that I got a reading from my sensor each time to avoid false zeros. Could millis() and micros() be used within the same code so that I can have the speeds increase in an attempt to avoid bad readings within the code? As currently I have the ultrasonics trigger high and low using millis() where if I used blocking code such as delayMicroseconds() it would be faster to get a reading from the sensors. I am hoping if I could use micros it would reduce the delay needed to perform actions using the buzzer and get rid of the current issues.

Is it not ideal to have a function complete in all call using millis()?

cattledog:
Your ultrasonic distance measurement could be based on interrupts and not pulseIn() in order to be non blocking.

Okay, I hadn't thought about approaching it like that, if I were to convert the ultrasonic distance measurement to use interrupts and non blocking code like pulsein(), that wouldn't affect the use of millis() and things like that would it? I'm not the most experienced when it comes to interrupts, but I would use external interrupts for that?

Mark_12-10:
I am hoping if I could use micros it would reduce the delay needed to perform actions using the buzzer and get rid of the current issues.

You can use micros() in the same way that millis() is used in Several Things at a Time

Is it not ideal to have a function complete in all call using millis()?

I don't understand this question.

The whole point of the code in Several Things at a Time is NOT to try to complete an action all in one step in order to make it possible to interleave several actions. Of course there may be situations where that is not appropriate, but they are rare.

I have not used them myself but I believe there are blocking and non-blocking libraries for the ultrasonic sensor and libraries that make sounds without blocking - the sound needs to be started and later on it needs to be stopped.

...R

I have not used them myself but I believe there are blocking and non-blocking libraries for the ultrasonic sensor

Yes, take a look at NewPing.h teckel12 / Arduino New Ping / wiki / Home — Bitbucket

There is a timer interrupt method which is non blocking. This method uses Timer2 and conflicts with the use of tone().

Timer 2 Conflict
Having a conflict with the tone library or another library using timer 2? Instead of the tone library, use my NewTone or toneAC libraries which instead uses timer 1 (and also has many other advantages). Or use my Timer Free Tone library which doesn't use any timers to generate tones.

There is more information about work arounds here
https://bitbucket.org/teckel12/arduino-new-ping/wiki/Multiple%20Definition%20of%20"__vector_7"%20Error

Robin2:
You can use micros() in the same way that millis() is used in Several Things at a Time

That is good to know, thank you. Now I know that, I will be speeding up the rate in which my ultrasonics pulse the trigger pin to get a distance measurement.

cattledog:
There is more information about work arounds here
https://bitbucket.org/teckel12/arduino-new-ping/wiki/Multiple%20Definition%20of%20"__vector_7"%20Error

Thank you for the tip. I ended up looking into that in the end and managed to find a new library for the buzzer which used timer1 in order to generate the noise alerts needed for my project. It was NewTone.h and seems to have solved all my issues and now the code works as expected.