Arduino library 路 Data and time

MsTimer2: call a function on a millisecond schedule

MsTimer2 is a tiny library with one job: call your function every N milliseconds, from a hardware interrupt, so it happens on time even while loop() is stuck in a delay() or a long calculation. It uses Timer2, the 8-bit timer, set to fire once a millisecond, and counts those ticks up to the period you asked for.

Built in on Mokxi: #include <MsTimer2.h> and it builds, with nothing to install. Free, and no account needed.

What MsTimer2 does

There are only three calls, all static: MsTimer2::set(period, function), MsTimer2::start() and MsTimer2::stop(). That makes it a quick way to refresh a multiplexed seven-segment display, scan a keypad, or blink a status LED at a steady rate without rewriting loop() around millis().

Timer2 is also the timer tone() uses, and IRremote uses it on an Uno, and on the real board it drives PWM on pins 3 and 11. A sketch can use only one of those at a time. The ATmega32U4 on the Leonardo has no Timer2, so MsTimer2 is for the Uno, Nano, Pro Mini and Mega.

Library Manager nameMsTimer2
AuthorJavier Valencia, Paul Stoffregen
Include#include <MsTimer2.h>
BoardsArduino Uno, Arduino Nano, Arduino Mega 2560, Arduino Pro Mini 3.3 V
In the simulatorSimulated: it runs against the part the way it does on the bench.

Install it

It builds on Arduino Uno, Arduino Nano, Arduino Mega 2560, Arduino Pro Mini 3.3 V.

In Mokxi it is built in: #include <MsTimer2.h> and the sketch compiles, with no download, no zip and no Library Manager.

On a real board, install "MsTimer2" by Javier Valencia, Paul Stoffregen from the Arduino IDE鈥檚 Library Manager (Sketch, Include Library, Manage Libraries), or add MsTimer2 to lib_deps in PlatformIO. Mokxi follows the calls of release 1.1, so a sketch written here builds there too.

What to know in the simulator: Timer2 is also tone()鈥檚 and IRremote鈥檚, as on the real board. The Leonardo has no Timer2.

Arduino Uno 路 Arduino Nano 路 Arduino Mega 2560 路 Arduino Pro Mini 3.3 V

The calls you will use

In the order a sketch meets them.

MsTimer2::set(500, flash)

Sets the period in milliseconds and the function to call. The function takes no arguments and returns nothing.

MsTimer2::start()

Starts the timer. The first call comes one period later.

void flash() {
  static bool on = false;
  digitalWrite(13, on = !on);
}
MsTimer2::set(500, flash);
MsTimer2::start();

MsTimer2::stop()

Stops calling the function. start() begins counting the period again from zero.

volatile unsigned long ticks;

Not a library call but the rule that goes with it: any variable both the timer function and loop() touch must be volatile so the compiler rereads it.

Example: I2C LCD clock and thermometer

On the Arduino Uno R3. It runs in the browser exactly as printed.

sketch.ino 路 Arduino Uno R3
// A clock and thermometer on an I2C LCD.
//
//   LCD GND -> GND   VCC -> 5V   SDA -> A4   SCL -> A5
//   10k from 5V -> A0; the thermistor from A0 -> GND
//
// MsTimer2 calls tick() every 1000 ms from a timer interrupt, so the seconds
// keep counting however long the rest of the loop takes. Type three numbers,
// like 12,30,0, into the serial monitor to set the time.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <MsTimer2.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);
volatile int seconds = 0;
int hour = 0, minute = 0;

// The thermistor is the bottom of a divider under a 10k, so its resistance
// is 10k * v / (5 - v). The beta equation turns that into kelvin:
// 1/T = 1/T0 + ln(R/R0)/B, with T0 = 25 C, R0 = 10k and B = 3950.
float celsius() {
  float v = analogRead(A0) * 5.0 / 1024.0;
  float rt = 10.0 * v / (5.0 - v);
  float kelvin = 1.0 / (log(rt / 10.0) / 3950.0 + 1.0 / (273.15 + 25.0));
  return kelvin - 273.15;
}

void tick() {
  seconds++;
}

void twoDigits(int n) {
  lcd.print(n / 10);
  lcd.print(n % 10);
}

void setup() {
  lcd.init();
  lcd.backlight();
  lcd.print("starting...");
  delay(500);
  lcd.clear();
  MsTimer2::set(1000, tick);
  MsTimer2::start();
  Serial.begin(9600);
  Serial.println("Type hour,minute,second to set the time.");
}

void loop() {
  if (Serial.available() > 0) {
    int h = Serial.parseInt();
    int m = Serial.parseInt();
    int s = Serial.parseInt();
    hour = constrain(h, 0, 23);
    minute = constrain(m, 0, 59);
    noInterrupts();
    seconds = constrain(s, 0, 59);
    interrupts();
    Serial.println("time set");
  }
  noInterrupts();
  if (seconds >= 60) {
    seconds -= 60;
    if (++minute >= 60) {
      minute = 0;
      if (++hour >= 24) hour = 0;
    }
  }
  int sec = seconds;
  interrupts();
  lcd.setCursor(0, 0);
  lcd.print("TEMP: ");
  lcd.print(celsius());
  lcd.print("C ");
  lcd.setCursor(0, 1);
  lcd.print("TIME: ");
  twoDigits(hour);
  lcd.print(':');
  twoDigits(minute);
  lcd.print(':');
  twoDigits(sec);
  delay(200);
}

Open it in the editor, wired and ready to run 路 I2C LCD clock and thermometer: the project page

Common errors and how to fix them

MsTimer2.h drives the ATmega328P鈥檚 and 2560鈥檚 Timer2, so it is for the Uno, Nano, Pro Mini and Mega.

The board has no Timer2, which is true of the Leonardo and every non-AVR board. Time the work with millis() in loop(), or use that board鈥檚 own timer library.

tone() stopped working, or IR codes are no longer received

MsTimer2, tone() and IRremote all want Timer2 on an Uno. Keep one of them on Timer2 and move the other job to millis() timing or to TimerOne.

Printing from the timer function makes the sketch hang

The function runs inside an interrupt, where Serial output can block waiting for an interrupt that cannot run. Set a flag or count in the function and print from loop().

Try MsTimer2 without installing anything

Open the example, change the sketch, and keep your version in a free account.