Millis()

In class, you have used delay(), but there are cases you would want to use millis() to count time. Although the delay() function is easy to use, it has side effects: the main one of which is that it stops all activity on the Arduino until the delay is finished.  Sometimes you need to do two things at once. For example, you might want to blink an LED while reading a button press. In this case, you can’t use delay(), because Arduino pauses your program during the delay(). If the button is pressed while Arduino is paused waiting for the delay() to pass, your program will miss the button press.

What is the millis() function exactly?

Arduino is equipped with a timer/counter, which starts counting from zero once it’s connected to a power supply. the millis() function gives us access to the running tally that the timer/counter has been keeping track of. When you call the millis() function, it returns the current value of the timer/counter in milliseconds (hence the millis() function name).

Important information about millis()!

When you want to use millis() as a variable value, you have to consider the variable type and the amount of data it can hold. Because counting time can go for days with Arduino, regular variable types fill up quickly. We have bytes, integers, float, their storage capacities are far less than the long data type’s storage capacity. A long can hold 32 bits of data, from -2 billion and some change to positive 2 billion and some change! But, since millis() will never return a negative value, we can use an unsigned long instead. What this variable allows us to do is to shift the negative value storage capacity of a long data type to the positive side, effectively doubling the available data. 

Let’s look at some examples to show what this means! 

Example1 – A Blinking LED

I used the example circuits and code provided by TinkerCad.

This program blinks pin 13 of the Arduino (the
built-in LED)
*/

void setup()
{
pinMode(13, OUTPUT);
}

void loop()
{
// turn the LED on (HIGH is the voltage level)
digitalWrite(13, HIGH);
delay(1000); // Wait for 1000 millisecond(s)
// turn the LED off by making the voltage LOW
digitalWrite(13, LOW);
delay(1000); // Wait for 1000 millisecond(s)
}

Example2 – Multitasking with a blinking LED and reading button state using delay() function

/*millis example: LED blinks while reading buttonPressed*/

int buttonValue = 0;
void setup(){
Serial.begin(9600);
pinMode(13, OUTPUT);
}

void loop(){
buttonValue = digitalRead(2);
Serial.println(buttonValue);
//read the button value, if the button is pressed or not
// turn the LED on (HIGH is the voltage level)
digitalWrite(13, HIGH);
delay(1000); // Wait for 1000 millisecond(s)
// turn the LED off by making the voltage LOW
digitalWrite(13, LOW);
delay(1000); // Wait for 1000 millisecond(s)
}

I started out in Tinkercad with the above circuit and the code modifying the Blink Example. If you try the code above, although the LED will blink, the button state is not reading accurately all the time. What we wanted was that the button state is read properly while the LED is blinking, not after it blinked. So why is it happening? 

This scenario could be explained by what we previously mentioned in the first paragraph, “it stops all activity on the Arduino until the delay is finished”. 

Example3 –  Using millis() instead

/*millis example: LED blinks while reading buttonPressed*/

unsigned long _time; //declare a variable stores the current millis() value
int buttonValue = 0; //store the button state in this variable
int LED_pin = 13; //store the led pin in this variable
int btn_pin = 2; //store the button pin in this variable
bool led_on = false; //create a boolean variale to store the led state: on->true, off->false
void setup(){
  Serial.begin(9600);
  pinMode(LED_pin, OUTPUT); //LED
  pinMode(btn_pin, INPUT); // Switch
  _time = millis(); // initiate _time variable
}

void loop(){
  //read the button value, if the button is pressed or not
  buttonValue = digitalRead(btn_pin);
  /* we want to turn on the led for 1s and 
  turn off the led for 1s 
  using millis() function*/

  //here, we use the current time minus the previous time to calculate the period
  if(millis()-_time >= 1000){
    if(led_on == false){
    //if the current led state is off, then turn the light on
      digitalWrite(LED_pin, HIGH);
      led_on = true;
    }else{
    //if the current led state is on, then turn the light off
      digitalWrite(LED_pin, LOW);
      led_on = false;
    }
  //restore the current time in _time variable
    _time = millis();
  }
//print the button state in the serial monitor
  Serial.print("Button State: ");
  Serial.println(buttonValue);
}

We used the same circuit as before but we modified the code with millis() function. We use millis() to keep track of the Arduino running time, and the LED changes state (on->off/off->on) every one second. But at the same time, the activities in Arduino won’t stop, so it keeps reading the value of the button state.

Summary

If you have two, repetitive, timed events and those events overlap then you may find that using the delay() function is not going to be the best solution. However, when we have two timed events and one happens after the other (i.e. they are sequential), then using the delay function is perfect!

Button: State v.s. Moment

In this tutorial, we will learn two ways of using a button (digital input) to control an LED (digital output). You have learned in class how to turn an LED on by pressing down a button, and off by releasing the button. But sometimes you might not want to hold a button to keep an LED on (if you want it to be on for hours!). How do we press a button to turn a light on, and again to turn it off?

How to detect a moment of a trigger (a key/mouse/button gets pressed) is actually a common issue you might encounter not only in Arduino, but also in other programming circumstances like Processing, Unity, and MaxMSP. Once you understand the logic of the examples below, you will be able to achieve the same goal in other software.

For the two examples below, we will be using the same circuit. Here is a diagram of the circuit that I made with Tinkercad: 

diagram of button controlling LED

You can find the circuit and code of the two examples here in Tinkercad.

Example1 – Push for on, release for off: 
built-in LED at pin 13
built-in LED at pin 13

You have probably learned this example in class — the “Button” example(Arduino>File>Example>Digital>Button) in Arduino shows how to use a button to control the built-in LED on Arduino (digital 13) by synchronising the state of the input and the output. (You can also connect an external LED to digital pin 13, as I did in the diagram above. The external LED will basically behave the same as the built-in one, controlled by the same code that’s controlling the built-in LED.)

/*
  Button

  Turns on and off a light emitting diode(LED) connected to digital pin 13,
  when pressing a pushbutton attached to pin 2.

  The circuit:
  - LED attached from pin 13 to ground
  - pushbutton attached to pin 2 from +5V
  - 10K resistor attached to pin 2 from ground

  - Note: on most Arduinos there is already an LED on the board
    attached to pin 13.

  created 2005
  by DojoDave <http://www.0j0.org>
  modified 30 Aug 2011
  by Tom Igoe

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/Button
*/

// constants won't change. They're used here to set pin numbers:
const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
  } else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
  }
}

 

EXAMPLE2 – Press once to turn on, press again to turn off: 

Sometimes we don’t want to hold a button in order to keep an LED on. Things like light switches in our daily life work in the way that we push a button once to turn it on, and again to turn it off. How do we realize this?

In order to do so, we need to detect the moment of a button getting pressed down, instead of counting on the state of it. In other words, we need to detect the moment when the value of the digital input changes from 0 to 1. The key to realizing this is to create a pair of booleans: prevButtonStateand currentButtonState in which keep records of the previous state of the button, as well as the current state. We also need a third boolean LEDstatus to store the state of the LED.

The full code would write as below:

int ledPin = 13; //connect pin 13 to led
int buttonPin = 2; //connect pin 2 to button

// creating variables to store the current and previous button states
//false for not pressed, true for pressed
bool prevButtonState = false;
bool currentButtonState = false;

//creating a variable to store whether the LED is on or not
bool LEDstatus = false;

void setup() {
   //initialize the LED pin as output and the button pin as input
   pinMode(ledPin, OUTPUT);
   pinMode(buttonPin, INPUT);
}

void loop() {
  //read currentButtonState
   currentButtonState = digitalRead(buttonPin);

  //if my current button state is NOT the same as previously, AND the button is being pressed at this moment,
  //then this is the MOMENT the button just got pressed down
  if ( currentButtonState != prevButtonState && currentButtonState == true )  {
    //if the LED is off, then turn it on.
    if ( LEDstatus == false ) {
      digitalWrite(ledPin, HIGH);
      LEDstatus = true;
    }
    //else, turn the LED off.
    else {
      digitalWrite(ledPin, LOW);
      LEDstatus = false;
    }
  }        
    //at the end of the loop, store the currentButtonState in variable prevButtonState to update it for the next loop
    prevButtonState = currentButtonState;  
}
Variations and simplifications of the code in example2

While the code above can realize the effect we want, it is not the most optimized version. There are variations and simplifications that could be done for certain parts of the code. Some are simply another way to write the code without optimizing it, for example —

//false for not pressed, true for pressed 
bool prevButtonState = false;
bool currentButtonState = false;

//can also be written as --

//0 for not pressed, 1 for pressed 
bool prevButtonState = 0; 
bool currentButtonState = 0;

Some can make the code a bit shorter —

  if ( currentButtonState != prevButtonState && currentButtonState == true )

//works in the same way as --

  if ( prevButtonState == false && currentButtonState == true )

Some help shorten the code massively. The farthest thing you could do to the code is probably to rewrite the section below —

  if ( currentButtonState != prevButtonState && currentButtonState == false )  {
    //if the LED is off, then turn it on.
    if ( LEDstatus == false ) {
      digitalWrite(ledPin, HIGH);
      LEDstatus = true;
    }
    //else, turn the LED off.
    else {
      digitalWrite(ledPin, LOW);
      LEDstatus = false;
    }
  }        

into —

 if ( currentButtonState != prevButtonState && currentButtonState == false )  {
    //when button pressed, reverse the LEDstatus
    LEDstatus = !LEDstatus;
    digitalWrite(ledPin, LEDstatus);
  }

The full code after optimization will look like:

// Defining variables
int ledPin = 13; //connect pin 13 to led
int buttonPin = 2; //connect pin 2 to button

// creating variables to store the cuurent and previous button states
//false for not pressed, true for pressed
bool prevButtonState = false;
bool currentButtonState = false;

//creating a variable to store whether the LED is on or not
boolean LEDstatus = false;

void setup() {
   //initialize the LED pin as output and the button pin as input
   pinMode(ledPin, OUTPUT);
   pinMode(buttonPin, INPUT);
}

void loop() {
  //update currentButtonState
   currentButtonState = digitalRead(buttonPin);

  //if my current button state is NOT the same as previously, AND the button is being pressed at this moment,
  //then this is the MOMENT the button just got pressed down
  if ( prevButtonState == false && currentButtonState == true )  {
    //if the LED is off, then turn it on.
    LEDstatus = !LEDstatus;
    digitalWrite(ledPin, LEDstatus);    
  }

  //at the end of the loop, store the currentButtonState in variable prevButtonState to update it for the next loop
  prevButtonState = currentButtonState;  
}