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

Status
Not open for further replies.

emilhem

Member
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
...
 
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.
 
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)
 
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.
 
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
 
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). :cool:

Thanks everyone for the help. Before I checked this topic I figured out why it was doing it (facepalm) but not a good solution.
 
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.
 
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.
 
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.
 
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...sedMillis/developers/FAw_W0Vn7kg/idozVCF_sCsJ
 
Last edited:
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.
 
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".
 
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".
 
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
 
Way to revive an old thread? elapsedMillis/Micros are really good and useful!

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.
 
Status
Not open for further replies.
Back
Top