Delays and unsigned long

PeterH:
It'll cause the (truncated) value of micros() to roll over every 64K micros(), but the code handles rollover correctly.

Is this part correct?

			start += 1000;

That isn't doing subtraction. I must admit I get confused when I look at rollover on unsigned variables.

Let's say the time (in micros, truncated to 16 bits) is 65500, that is 0xFFDC.

We add 1000 to that getting 66500, that is 0x103C4. Truncated though that is 0x3C4 or 964 in decimal.

Now we execute this line 5 microseconds later:

		if (((uint16_t)micros() - start) >= 1000) {

That is:

if ((65505 - 964) >= 1000)

Well, it is, isn't it? So we immediately do this:

ms--;

Or do I have the wrong end of the stick?

That isn't doing subtraction. I must admit I get confused when I look at rollover on unsigned variables.

The add works too but isn't useful for watching millis() approach. Your clocks example is easier to see it here. I know that 2 - 10 is 4 on our base-12 clock and I can look for >= 4. But if I add 4 to 10 and get 2 then think that I can say if ( now >= 2 ) it will be true at 11. But the rollover works fine and the difference is figured elsewhere.

Not that you didn't have it figured before you wrote the line I quoted, I am sure. You're the type that would have to figure it out before letting go, aren't you?

Slightly off topic, you have a number of issues with if statements in your code snippets. Here are two, but the pattern repeats elsewhere. This:

if(volume >= 1, volume < 32) {
      drinkozarray[array] = drinkozarray[array] + volume;
    }
    else if(volume = 33) {
           drinkozarray[array] = drinkozarray[array] + 2; 
    }

Is probably intended to be this:

    if(volume >= 1 && volume < 32) {
      drinkozarray[array] = drinkozarray[array] + volume;
    }
    else if(volume == 33) {
           drinkozarray[array] = drinkozarray[array] + 2; 
    }

The first is an odd use of the comma operator that doesn't do what you are apparently looking for.
The second is the classic of all classic C mistakes. = (assignment) versus == (comparison).

GoForSmoke:
But the rollover works fine and the difference is figured elsewhere.

Are you sure?

Quite right, this is definitely wrong:

else if (volume = 0){
 ...
else if (volume = 33){

Unless you're just having me on for saying that you would have figured it out before posting, I can't prove that part so I can't be 100% sure you did.

start += 1000 moves the start point even past rollover, from 10 to 2 as it were.

Here is where the difference, the subtraction is taken:

if (((uint16_t)micros() - start) >= 1000) {

I am a bit in wonder that that code does the conditional and update start in under 1 usec or if it should be start += 999? Is the Arduino millisecond there really 1.000 or 1.001 msec? :smiley:

I've messed around with trying to reduce the time difference condition to just two values instead of three; now and target time rather than now, start and interval and never got a fully clean solution. I can make it work if I cut the maximum interval by half using an if-else but it's not clean nor worth saving 4 bytes.

The code looks correct to me. Apart from using 16-bit values instead of 32-bit, it's equivalent to code that we'd use to deal with millis() and will handle the rollover correctly. All it's being used for is to detect when 1000 micros have elapsed, so there's no issue with the difference (between micros() and start) exceeding a 16-bit value.

In what way is my arithmetic in reply #21 wrong then?

I understand that the subtraction part is OK, but the figure being compared to ("start") is calculated by an addition.

Let's say the time (in micros, truncated to 16 bits) is 65500, that is 0xFFDC.

We add 1000 to that getting 66500, that is 0x103C4. Truncated though that is 0x3C4 or 964 in decimal.

Now we execute this line 5 microseconds later:

Code:

if (((uint16_t)micros() - start) >= 1000) {

That is:

Code:

if ((65505 - 964) >= 1000)

Well, it is, isn't it? So we immediately do this:

Code:

ms--;

Or do I have the wrong end of the stick?

void delay(unsigned long ms)
{
	uint16_t start = (uint16_t)micros();

	while (ms > 0) {
		if (((uint16_t)micros() - start) >= 1000) {
			ms--;
			start += 1000; // at this point start == micros, not micros + 1000
		}
	}
}

I don't see start bumped up until micros() reach or pass start + 1000.
AFAICT start never gets ahead of micros() while start + 1000 does so you get

65505 - 65500 >= 1000

not

65505 - 967 >= 1000

I was dead wrong before. Start is not moved from 10 to 2. It was moved from 6 to 10 and start + interval becomes the 2 o'clock.

NOW can you say you had it all figured out from the start as have just been leading things to this?

You have a good point, although I'm still uneasy about that code.

Why can't delay look like this?

void delay(unsigned long ms)
{
  ms *= 1000;  // turn into microseconds
  unsigned long start = micros ();
  while (micros () - start < ms)
    { }
}

I know, that will wrap around after 71 minutes, but who delays that long?

LOL, Procrastinators!

Really though, that code looks like it might be efficient or sumthing. XD

GoForSmoke:
Really though, that code looks like it might be efficient or sumthing. XD

Or this, which handles longer delays:

void delay(unsigned long ms)
{
  unsigned long start;
  
  // short delays, allow for the fact that we might
  // be half-way through a millisecond
  if (ms < 60000)
    {
    ms *= 1000;  // turn into microseconds
    start = micros ();
    while (micros () - start < ms)
      { }
    }  // end of if short delay
  else
    {  // longer delays
    start = millis ();
    while (millis () - start < ms)
      { }
    }  // end of longer delay
}

Bit long-winded maybe.

I'm inclined to go with the simple one:

void delay(unsigned long ms)
{
  unsigned long start = millis ();
  while (millis () - start < ms)
    { }
 }

And just warn users that for very short delays, the time may be out a bit depending on how far through the next "millis" tick you start. However for short delays there is always delayMicroseconds.

The code in reply # 30 is the better one.

WOW!

I got taken away on a short notice work trip, but I didn't expect to see three pages of replies when I returned! You guys got way into it!

I am glad you spotted my folly with the = instead of ==, I'll fix that right away.

As for the suggestion that I should just pass delay(); an unsigned long- if you look at my code from page one, you will see that the array used AND the "pourdelay" ARE in fact unsigned long variables. I am only hoping you can spot where I am overlooking something I am passing delay that isn't- as that would be an easy fix!

The discussion as to how delay works was a bit over my head, but what I pulled from it is that even though delay accepts an unsigned long variable, it still won't count past a regular integer? (In the original post, I realized that 32 seconds alluded to it stopping after reaching integer length- I just hoped it wasn't true?)

So,if I follow discussion on the last page... I should create a new version of the delay function, which will be able to count higher but slightly less accurately? Let me know if I understood that incorrectly?

P.S. Due to the pumps I am using and the vertical distance being covered by the lines, the pour is very slow. I'll fix that later by ordering bigger pumps... maybe... it's still more convenient than digging out all the ingredients in a seven ingredient drink...- but for now anyways pour times can easily exceed 32 seconds even for regular sized drinks. Sorry, Goforsmoke- no giant drinks here :wink:

Additionally, with the power supply I have used- only one pump is capable of running at any given time. The idea that I should have the computer checking other sensors or running other pumps or doing anything at all whilst pumping isn't something I am interested in incorporating. The code already keeps track of every bottle and alerts me when they reach a certain level- which is all the help I require from it. When it is pouring- that's all I want it doing.

If I can fix this delay problem- I will be in business here. Fingers crossed that I can!

Jim_Socks:
So,if I follow discussion on the last page... I should create a new version of the delay function, which will be able to count higher but slightly less accurately? Let me know if I understood that incorrectly?

Delay should not fail after 32 seconds, something else will be wrong.

You're not using CO2 pressure to drive the liquids? Any chance to feed from overhead and use gravity?

As for the suggestion that I should just pass delay(); an unsigned long- if you look at my code from page one, you will see that the array used AND the "pourdelay" ARE in fact unsigned long variables. I am only hoping you can spot where I am overlooking something I am passing delay that isn't- as that would be an easy fix!

volume is a byte, not an unsigned long.

void countandpour(byte array, byte volume)

So what will the result of the multiplication be ?

unsigned long pourdelay = 0;
pourdelay = volume * 9250;
delay(pourdelay);

Could it ever result in a negative number ?
Using delay() with a negative number never seems to end. Sound familiar ?

UKHeliBob:
volume is a byte, not an unsigned long.

void countandpour(byte array, byte volume)

So what will the result of the multiplication be ?

unsigned long pourdelay = 0;

pourdelay = volume * 9250;
delay(pourdelay);



**Could it ever result in a negative number ?**

How does an unsigned number ever result in a negative?

I don't know. Hence the question.
Would multiplying a byte by an unsigned long result in an unsigned long ?

This certainly does not produce the result that I would expect at first sight, but no doubt there is an explanation

unsigned long pourdelay = 0;
byte volume = 100;

void setup() 
{
  Serial.begin(9600);
  pourdelay = volume * 9250;
  Serial.print("pourdelay is ");
  Serial.println(pourdelay);
}

void loop(){}

Output

pourdelay is 7496