Fun with Arduino 09 Variables & Data Types: byte, int, long, unsigned

Before we continue with our User Interface we first have a little challenge with the code of video 8. If you tried the code you may have found that it did not work as we expected: when we threw the switch, the lights went on, but they did not turn off after 180 seconds. Why not? The reason for this lies in the different types of variables the Arduino uses. Let’s have a closer look at variables and let’s get our code to work.

Read on below the video …

 

Variables are memory locations that we can give a name and in which we can store values that can change throughout the execution of the program. To see a variable at work, here is an Arduino sketch that prints a counter on the screen, something we all did back in The Eighties on our first Basic machine :).

byte counter = 250;

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println(counter);
  counter = counter + 1;
  delay(500);
}

Variables are stored in volatile memory. Since this memory is scarce, variables can purposely be given different sizes to take up less space. The table shows some of the variable types we can use and how much bytes of memory they consume:

data_types

See the Arduino code reference page for a complete list of variable types.

Every variable that we want to use in our code must first be declared, with its type. Examples of valid declarations are:

byte counter; // max 255, counting further starts at 0 again
int cycle_time = 180; // [s]
unsigned long time_to_update_servo; // [ms]

When we declare a variable and we do not give it an initial value, it starts with value 0. These statements both declare the variable led_status and load it with 0:

byte led_status; // 0=off 1=on
byte led_status = 0; // 0=off 1=on

Now … what was ‘wrong’ with the code from video 8? The code seemed OK didn’t it? The calculation between the brackets in the  delay(cycle_time * 1000) should give us 180000 ms shouldn’t it? Well … actually it does not … not without a change in the variable types used.

Our choice to declare cycle_time as int was reasonable, although an unsigned int would have been nicer cause we never need the negative numbers. But anyhow, the range of the int is more than enough for our purposes and we spared 2 bytes of memory not declaring it a long. So far so good, all well thought over.

The delay() statement though works with an unsigned long between the brackets. How do we know this? We can read it in the Arduino code reference pages, this is the link to the delay() statement.

When we wrote delay(cycle_time * 1000) we assumed the Arduino would calculate 180 * 1000 to be 180000. Since the delay() uses an unsigned long, does it not automatically create that for us? Well … no … it does not do that. If mathematic is applied to two integers, the result will still be an integer! An integer can not hold a value of 180000, so it ‘overflows’ and the result becomes an unexpected value. We can find the real outcome when we run:

Serial.println(cycle_time * 1000);

The outcome of this multiplication is not 180000 … it is -16608 … an integer, a negative one even. The delay() is now loaded with a completely wrong number.

OK … now that we know this … what can we do to solve it?

One solution is that we can declare cycle_time as an unsigned long. Then the instruction delay(cycle_time * 1000) contains an unsigned long between the brackets and the multiplication result will also be an unsigned long. With our small program we have no need to crank out every available byte of memory, so this is an easy solution and it works well.

But suppose our code uses many variables and we really need every possible byte of memory we can find? Then we can still declare cycle_time as an unsigned int and gain 2 bytes of memory. We now need to explicitly tell the Arduino that we want the multiplication cycle_time * 1000 to become an unsigned long. We can do this by upgrading (official name: typecasting) one of the members in the calculation to an unsigned long, in one of these ways:

delay((unsigned long)cycle_time * 1000);
delay(cycle_time * 1000UL);

This makes that the calculation uses an unsigned long and the result therefore also is an unsigned long.

Here is the final code that works well:

#define DAY_NIGHT_CYCLE_ON_OFF_PIN 2 // input for toggle switch
#define LIGHTS_PIN 8                 // output to FET or relay module
#define LED_PIN A5                   // output to LED on switch panel
unsigned int cycle_time = 180;       // [s] 180 = 3 minutes

void setup() {
  pinMode(DAY_NIGHT_CYCLE_ON_OFF_PIN, INPUT_PULLUP);
  pinMode(LIGHTS_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println("Day/Night cycle system is ready");
  Serial.println(); // This prints an empty new line
  Serial.print("Cycle time: ");
  Serial.print(cycle_time);
  Serial.println(" seconds");
}

void loop() {
  if (digitalRead(DAY_NIGHT_CYCLE_ON_OFF_PIN) == LOW) {
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(LIGHTS_PIN, HIGH);
    delay(cycle_time * 1000UL); 
    digitalWrite(LIGHTS_PIN, LOW);
    delay(cycle_time * 1000UL);
  }
  else {
    digitalWrite(LED_PIN, LOW);
  }
}

Wow … this was digging deep into the technicalities … we are becoming quite the software experts! Now that we have this working we can continue with our User Interface. We are going to show the status of the cycle on our PC screen and in the process we start to use more and more variables.

— 0 —

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s