2

In my recent project, I want to set my time according to the requirement. And somehow I'm able to manage that. But when I'm decreasing my time value then one weird problem happens. When decreasing my time value, when I reach ZERO (0) and when I again decrease value then it shows the maximum range of that data type even if I put the condition for stop value at ZERO(0).

My code is below:

const int kPinSw1 = 2;
const int kPinSw2 = 3;

int plusBtnIncState = 0;         
int lastPlusBtnIncState = 0;

int minusBtnDecState = 0;         
int lastMinusBtnDecState = 0;

unsigned long currentMillis;
unsigned long previousMillis;
unsigned long interval = 0UL;

void setup() {
  Serial.begin(9600);
  pinMode(kPinSw1, INPUT_PULLUP);
  pinMode(kPinSw2, INPUT_PULLUP);
}

void loop() {
  //---------------INC_TIMER---------------//
  plusBtnIncState = digitalRead(kPinSw1);
  if(plusBtnIncState != lastPlusBtnIncState) {
    if(plusBtnIncState == HIGH) {
      interval = interval + 1000;
    }
    delay(50);
  }
  lastPlusBtnIncState = plusBtnIncState;

  //---------------DEC_TIMER---------------//
  minusBtnDecState = digitalRead(kPinSw2);
  if(minusBtnDecState != lastMinusBtnDecState) {
    if(minusBtnDecState == HIGH) {
      interval = interval - 1000;
      if(interval <= 0) {
        interval = 0;
      }
    }
    delay(50);
  }
  lastMinusBtnDecState = minusBtnDecState;  

  //---------------------------------------//
  Serial.print("Int:");
  Serial.print(interval);
  Serial.print("\n");     
}

And output:

Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:2000
Int:2000
Int:2000
Int:2000
Int:2000
Int:2000
Int:2000
Int:2000
Int:2000
Int:2000
Int:2000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:1000
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:0
Int:4294966296
Int:4294966296
Int:4294966296
Int:4294966296
Int:4294966296
Int:4294966296
Int:4294966296
Int:4294966296

As you can see in output when I reach ZERO and then again decrease the value then the value is like 4294966296 instead of ZERO. I put the logic for that if we reached ZERO or less ZERO then it must stay at ZERO. I don't understand why this happens. Please suggest me something.

  • do not decrement once you reach zero – jsotola Dec 23 '17 at 20:47
5

The interval variable is unsigned long.

In this example, we are using int that are 4 bits long (just to keep thing short) just to subtract 1 from 0:

    0b0000
   -0b0001
    ------
    0b1111

That is normal arithmetic, but in binary.

The result 0b1111 can read as a signed int, in which case we read the value '-1', or you can read as unsigned, and you read it as '15'.

The same thing is working in your case, only that your int are 32 bits long.

The unsigned keyword is not a promise that "0-1" will produce "0". It only indicates that whatever bit configuration is inside that variable, it will be treat as a positive value.

2

The variable "interval" is an unsigned long. This means it can not go negative. It also means that it is made up of a 32 bits.

Instead of going negative, an unsigned variable will cycle to its most maximum value by following a modulo 2 math behavior. So a 32 bit value will become 2^32 or 4294967296 if, for example, decremented 1 past 0.

In the above program the 32 bit value was decremented by 1000 past 0 resulting in 4294967296 - 1000 or 4294966296.

Testing if an unsigned number is negative is futile. So this code:

  if(interval <= 0)
  {
    interval = 0;
  }

...does nothing. Instead, test if "interval" is larger than 1000 before subtracting 1000.

1

As already mentioned in previous answers, the issue is that an unsigned number wraps modulo MAX+1. The obvious fix would be to make interval a signed number.

Another option would be to avoid subtracting from interval anything larger that interval itself. You could do this by replacing

interval = interval - 1000;
if(interval <= 0) {
  interval = 0;
}

by

if (interval >= 1000)
  interval -= 1000;
else
  interval = 0;

Note that the else clause could be dropped if you can guarantee that interval is always a multiple of 1000.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.