Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 21 of 21

Thread: Problem with "multitasking". Only works for the first 32 seconds [Arduino IDE]

  1. #1
    Junior Member
    Join Date
    Dec 2013
    Location
    Sweden
    Posts
    7

    Problem with "multitasking". Only works for the first 32 seconds [Arduino IDE]

    So I have the below code compiled in my Tennsy 2.0. I hook up the serial monitor to see the analog temperature readings (not yet implemented 100%) and switch four led's on an off in a loop.

    But, after 32 seconds (every time) the Teensy goes to "hyperspeed mode" and ignores the if(millis()... code. See monitor readings below the code.

    Does anyone know why this happens?

    Code:
    static int firstPin = 2;
    static int secondPin = 1;
    static int thirdPin = 0;
    static int fourthPin = 3;
    
    static int pins[4] = {firstPin, secondPin, thirdPin, fourthPin};
    int currentPin = 0;
    
    int temperatureMillis = 0;
    static int temperatureInterval = 1000;
    
    int switchPinMillis = 0;
    static int switchPinInterval = 100;
    
    void setup() {
    	Serial.begin(38400);
    	pinMode(firstPin, OUTPUT);
    	pinMode(secondPin, OUTPUT);
    	pinMode(thirdPin, OUTPUT);
    	pinMode(fourthPin, OUTPUT);
    	digitalWrite(pins[0], HIGH);
    	switchPinMillis = millis();
    }
    
    float code;
    float celsius;
    
    void loop() {
    	if(millis() - temperatureMillis >= temperatureInterval) {
    		temperatureMillis = millis();
    		code = analogRead(10);
    		celsius = code;
    		Serial.print("temperature: ");
    		Serial.print(celsius);
    		Serial.print(" Celsius millis: ");
    		Serial.println(millis());
    	}
    	if(millis() - switchPinMillis >= switchPinInterval) {
    		switchPinMillis = millis();
    		digitalWrite(pins[currentPin], LOW);
    		if(currentPin == 3) {
    			currentPin = 0;
    		} else {
    			currentPin++;
    		}
    		digitalWrite(pins[currentPin], HIGH);
    	}
    }

    Code:
    temperature: 149.00 Celsius millis: 1000
    temperature: 149.00 Celsius millis: 2000
    temperature: 149.00 Celsius millis: 3000
    temperature: 151.00 Celsius millis: 4000
    ...
    temperature: 150.00 Celsius millis: 31000
    temperature: 149.00 Celsius millis: 32000
    temperature: 150.00 Celsius millis: 33000
    temperature: 151.00 Celsius millis: 33000
    temperature: 151.00 Celsius millis: 33000
    temperature: 150.00 Celsius millis: 33001
    temperature: 150.00 Celsius millis: 33001
    temperature: 149.00 Celsius millis: 33001
    temperature: 149.00 Celsius millis: 33001
    temperature: 148.00 Celsius millis: 33002
    temperature: 150.00 Celsius millis: 33002
    temperature: 148.00 Celsius millis: 33002
    temperature: 148.00 Celsius millis: 33003
    temperature: 149.00 Celsius millis: 33003
    temperature: 149.00 Celsius millis: 33003
    temperature: 150.00 Celsius millis: 33003
    temperature: 149.00 Celsius millis: 33004
    temperature: 148.00 Celsius millis: 33004
    temperature: 149.00 Celsius millis: 33004
    temperature: 149.00 Celsius millis: 33005
    temperature: 150.00 Celsius millis: 33005
    temperature: 148.00 Celsius millis: 33005
    temperature: 149.00 Celsius millis: 33005
    temperature: 149.00 Celsius millis: 33006
    temperature: 148.00 Celsius millis: 33006
    temperature: 149.00 Celsius millis: 33006
    ...

  2. #2
    Senior Member
    Join Date
    Aug 2013
    Location
    Gothenburg, Sweden
    Posts
    301
    This looks like a signed/unsigned 16 bit integer problem.
    It happens when the time goes past 32000, this is when
    a signed 16 bit integer switches from positiv to negative.

    Try to use unsigned long for temperatureMillis.

  3. #3
    My guess would be that temperatureMillis and switchPinMillis overflows. Try declaring them as unsigned long. That is also what millis() returns. Then it will overflow after approximately 50 days

    Code:
    unsigned long temperatureMillis = 0;
    unsigned long switchPinMillis = 0;
    (Haha, what mlu said)

  4. #4
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,412
    switchPinMillis should be unsigned long too.

    Pete

  5. #5
    Junior Member
    Join Date
    Nov 2013
    Posts
    15
    Then it will overflow after approximately 50 days
    The counters will overflow, but the code will work indefinitely. For example, if switchPinMillis in the code below is 0xFFFFFFFF, and millis() is 0x00000063, the difference is 0x64 (100), and the condition will evaluate to true.
    Code:
    unsigned long switchPinMillis = 0;
    static int switchPinInterval = 100;
    [...]
    if(millis() - switchPinMillis >= switchPinInterval) {
    32 bits isn't even needed here. This would also work fine (i.e. if millis() returned a 16-bit int):
    Code:
    unsigned int switchPinMillis = 0;
    static int switchPinInterval = 100;
    [...]
    if((unsigned int) millis() - switchPinMillis >= switchPinInterval) {
    Note the cast of millis() to an unsigned int to simulate this.

  6. #6
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,412
    It's problem is nothing to do with millis() overflowing. As soon as temperatureMillis exceeds 32767 it becomes negative. Then the comparison:
    Code:
    if(millis() - temperatureMillis >= temperatureInterval)
    goes wonky because if, for example, temperatureMillis is -1324, the comparison is actually testing:
    Code:
    if(millis() + 1324 >= temperatureInterval)
    Once temperatureMillis is less than -999 the conditional is always true which is why the code suddenly goes into "hyperspeed mode".

    The cure is to use unsigned long.

    Pete

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,458
    Teensy also has a special elapsedMillis variable type. It's easier to use. It also properly handles the millis() 32 bit rollover internally, so as long as you deal with times under 24 days, it always works, even when millis() rolls back to zero.

  8. #8
    Junior Member
    Join Date
    Dec 2013
    Location
    Sweden
    Posts
    7
    Quote Originally Posted by blargg View Post
    32 bits isn't even needed here. This would also work fine (i.e. if millis() returned a 16-bit int):
    Code:
    unsigned int switchPinMillis = 0;
    static int switchPinInterval = 100;
    [...]
    if((unsigned int) millis() - switchPinMillis >= switchPinInterval) {
    Note the cast of millis() to an unsigned int to simulate this.
    I will use this solution! It works great and is in my mind unlimited (won't crash).

    Thanks everyone for the help. Before I checked this topic I figured out why it was doing it (facepalm) but not a good solution.

  9. #9
    Junior Member
    Join Date
    Dec 2013
    Location
    Sweden
    Posts
    7
    Quote Originally Posted by PaulStoffregen View Post
    Teensy also has a special elapsedMillis variable type. It's easier to use. It also properly handles the millis() 32 bit rollover internally, so as long as you deal with times under 24 days, it always works, even when millis() rolls back to zero.
    That looks really interesting too. I would like it to run for quite some time though without interruption.

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,458
    Internally, elapsedMillis simply checks millis() when you access the variable. It does the same thing. You can go for seconds, minutes, hours or days without checking an elapsedMillis variable... and it will have the correct elapsed number of milliseconds when you use it. All the details are handled internally by very well tested code, so it "just works" very easily.

  11. #11
    Junior Member
    Join Date
    Dec 2013
    Location
    Sweden
    Posts
    7
    Quote Originally Posted by emilhem View Post
    That looks really interesting too. I would like it to run for quite some time though without interruption.
    (facepalm) I looked through that page again. That is going to work great for my project! Thanks Paul!

  12. #12
    Junior Member
    Join Date
    Nov 2013
    Posts
    15
    Interesting, elapsedMillis is an elegant solution. It encapsulates using the correct type, correct casts for comparisons even when millis() overflows, and updating without drift. It's a little odd that it's an active value, but conceptually one can just think of it as being incremented every millisecond by an interrupt.

  13. #13
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,828
    Paul: nice example of how to use operator overloading to make things look simple. I suppose it is a lost cause to try and get it in the standard Arduino libraries.

    Blargg: It isn't really an active variable, but each time you look at it, converting it to a long (via operator overloading), it calls the millis () function and does the subtract.

  14. #14
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,458
    Quote Originally Posted by MichaelMeissner View Post
    Paul: nice example of how to use operator overloading to make things look simple. I suppose it is a lost cause to try and get it in the standard Arduino libraries.
    TL;DR: The Arduino Team rejected adding elapsedMillis to Arduino's official core library. Instead, they decided it should be made available for Arduino on this playground page.

    It was discussed on the Arduino developers mail list. Here's a long link that might take you to that conversation:

    https://groups.google.com/a/arduino....g/idozVCF_sCsJ
    Last edited by PaulStoffregen; 12-12-2013 at 08:58 PM.

  15. #15
    Junior Member
    Join Date
    Nov 2013
    Posts
    15
    Quote Originally Posted by MichaelMeissner View Post
    Blargg: It isn't really an active variable, but each time you look at it, converting it to a long (via operator overloading), it calls the millis () function and does the subtract.
    Its value changes even when you haven't assigned anything new to it. To the user it doesn't matter whether it's the bits inside the object changing or bits elsewhere, it still behaves like a variable whose value changes on its own. This is the point of operator overloading, so things can act like other things and users can use the same mental tools as on those other things without having to remember that it's really doing something special behind the scenes. A variable that changes on its own is not a usual thing and thus hard to think about without a mental model for its behavior. That of some interrupt/other thread incrementing its value every millisecond seems the most intuitive mental model to use. Then it doesn't seem odd to sit in a loop waiting for it to become great enough.

  16. #16
    Senior Member
    Join Date
    Aug 2013
    Location
    Gothenburg, Sweden
    Posts
    301
    Thats the meaning of 'volatile'. Not so strange.

  17. #17
    Junior Member
    Join Date
    Nov 2013
    Posts
    15
    This would be strange to me, since there's no code changing i other than the i-=100 line:
    Code:
    volatile int i;
    for ( ;; )
    {
        if ( i >= 100 )
        {
            i -= 100;
            some_task();
        }
    }
    Volatile is to tell the compiler something that you the programmer know but it doesn't. If you had an interrupt handler modifying the variable, you'd know that it was being modified asynchronously, and make it volatile to tell the compiler this. There's no visible asynchronous modification of i here, so volatile doesn't give the programmer any explanation or mental model to use.

    This is what code using elapsedMillis looks like. Hence the useful model of "By the way, the object's constructor conceptually adds it to a list of variables that are incremented every millisecond by an interrupt".

  18. #18
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,458
    The mental model for elapsedMillis is a number that automatically increments 1000 times per second. It's really very simple, if you can accept it simply works, without worrying about why it works. Many people have used elapsedMillis successfully over the last couple years.

    But this is somewhat controversial, especially among experts, and especially when talking theoretically about what novices may or may not be able to grasp.

    Every day I answer questions related to Teensy. Many things actually are confusing, but so far elapsedMillis simply hasn't been one of them. It's controversial, but in practice people seem to "get it".

  19. #19
    Senior Member
    Join Date
    Nov 2012
    Location
    Los Angeles
    Posts
    125
    I use elapsedMillis/Micros quite a bit, but am now intrigued as to why the experts consider it controversial.

  20. #20
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,828
    Quote Originally Posted by virtualdave View Post
    I use elapsedMillis/Micros quite a bit, but am now intrigued as to why the experts consider it controversial.
    Quickly scanning the email thread, it is pretty much the same issue blargg raises, in that it looks like a variable that gets modified behind your back. I can understand both sides of the issue. I suspect if it was a class that instead of using operator overloading, used an accessor function to give the elapsed micros/millis since the last reset, and a function to reset the time point, it might be more palatable to some.

    The whole issue seems to come up again and again. Nick Gammon wrote this tutorial that I think is better than the Blink without delay example: http://www.gammon.com.au/blink

  21. #21
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    12,176
    Way to revive an old thread? elapsedMillis/Micros are really good and useful!

    Quote Originally Posted by Jelly2 View Post
    I guess you'd better turn to thos source 25-tips-how-to-multitask-successfully
    where you can receive experts' aid as well as look for data or tips you require.
    This must be SPAM as the count of 25 tips stops at 18 - somebody couldn't multitask well enough to finish the list? Also it relates to human not computer issues.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •