New Library: muTimer - On/Off-Delay / Cycle Timer (non-blocking) for Arduino

I have not found an easy to use non-blocking on/off-delay / cycle timer library and for this reason I created the muTimer library.
This library does not use any hardware timers, it uses the Arduino millis() function to store the current time and compares it then with the current time later.
It is also registered in the PlatformIO registry.
I hope it is easy to understand for beginners as well.

The library probably works with any other platform if you replace the millis() function with any other function that returns msec as uint32_t.

The functions
delayOn()
delayOff()
delayOnOff()
provide simple on and off delays for any digital signal.

The function cycleOnOff() provides a simple cyclically on/off output, for example to blink a LED.

cycleTrigger() allows to run periodically any action once after each cycle.

Example code to create a simple on/off-delay for a digital signal.
Can be used as general on/off delay as well as button/switch debounce.

#include <muTimer.h>

// input and output variables, typically assigned to hardware in- and outputs
bool input1;
bool output1;

// create timer object
muTimer myDelay1 = muTimer();

void setup()
{
}

void loop()
{
   // on-delay: 2000 ms, off-delay: 1000 ms
   output1 = myDelay1.timerOnOff(input1, 2000, 1000);
}

// in : _______------------______---______________------------_------
// out: ___________----------_________________________---------------

Example code for a flashing LED:

#include <muTimer.h>

// processor pin definitions
#define PIN_LED 9

// LED1 status variable
bool LED1;

// muTimer object
muTimer myCycle1 = muTimer();

void setup()
{
  // configure output hardware pin
  pinMode(PIN_LED, OUTPUT);
}

void loop()
{
  // LED flashing with 100ms on and 500ms off
  LED1 = myCycle1.cycleOnOff(100, 500);

  // write LED1 status to hardware output
  digitalWrite(PIN_LED, LED1);
}

// out: _____-_____-_____-_____-_____

I started about one month ago with the programming of an Arduino Mega unit and this is actually my first C++ library (I just programmed in C before), any comments are welcome.

2 Likes

Morning @michael-uray,

Your library looks good.

I’ve had a few dealings with this type of thing in the past – I’ve worked with pseudo-schedulers for the Arduino. They always forget that the millis() counter will rollover from 0xffffffff to 0 at some point.

Granted, that point is 49 days, 17 hours, 2 minutes, and 47.29487424 seconds (plus one solitary millisecond to cause the rollover) after the sketch starts running, but it does happen.

I think your code should be ok as the calculations should all be done as unsigned, but if you wanted to, you can test rollover without waiting all that time like this:

void setup() {
    extern unsigned long timer0_millis;
    ...
    noInterrupts();
    timer0_millis = 0xffffffff - (10 * 1000000);
    interrupts();
}

That gives 10 seconds before rollover.

If necessary, I’m sure that changing lines similar to if (millis() - _startTime >= delayTimeSwitchOn) to if (abs(millis() - _startTime) >= delayTimeSwitchOn) will suffice. You might need to #include <cstdlib> for PlatformIO and non-Arduino framework. But as I mentioned, probably not required – just a thought.

When I get some playtime, I’ll have a better look.

Cheers,
Norm.

1 Like

HI michael-uray

Thanks for Lib, will also thinker with for the next few days…

Hi @normandunbar

I’ve just tried the flashing LED example, and it complied and ran with no errors. I’ve no
experience with other platforms…

[env:nucleo_f401re]
board = nucleo_f401re
platform = ststm32@6.1.1
framework = arduino

upload_protocol = stlink
debug_tool = stlink

;monitor_port = /dev/ttyACM0
monitor_speed = 115200

lib_deps = michael-uray/muTimer@^0.0.1



Processing nucleo_f401re (board: nucleo_f401re; platform: ststm32; framework: arduino)
------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/ststm32/nucleo_f401re.html
PLATFORM: ST STM32 (10.0.1) > ST Nucleo F401RE
HARDWARE: STM32F401RET6 84MHz, 96KB RAM, 512KB Flash
DEBUG: Current (stlink) On-board (stlink) External (blackmagic, cmsis-dap, jlink)
PACKAGES: 
 - framework-arduinoststm32 4.10900.200819 (1.9.0) 
 - framework-cmsis 2.50501.200527 (5.5.1) 
 - tool-dfuutil 1.9.200310 
 - tool-openocd 2.1000.200630 (10.0) 
 - tool-stm32duino 1.0.1 
 - toolchain-gccarmnoneeabi 1.90201.191206 (9.2.1)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 12 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <muTimer> 0.0.1
Building in release mode
Checking size .pio/build/nucleo_f401re/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [          ]   0.9% (used 892 bytes from 98304 bytes)
Flash: [          ]   2.1% (used 10772 bytes from 524288 bytes)
Configuring upload protocol...
AVAILABLE: blackmagic, cmsis-dap, jlink, mbed, stlink
CURRENT: upload_protocol = stlink
Uploading .pio/build/nucleo_f401re/firmware.elf
xPack OpenOCD, x86_64 Open On-Chip Debugger 0.10.0+dev-00378-ge5be992df (2020-06-26-09:27)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
debug_level: 1

srst_only separate srst_nogate srst_open_drain connect_deassert_srst

target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000854 msp: 0x20018000
** Programming Started **
** Programming Finished **
** Verify Started **
** Verified OK **
** Resetting Target **
shutdown command invoked
========= [SUCCESS] Took 5.84 seconds ====
2 Likes

I actually did consider the overflow, but good idea to run that test.

I guess you meant there

timer0_millis = 0xffffffff - (10 * 1000);

since it is msec.

I just tested the overflow behaviour with the following code:

#include <muTimer.h>

muTimer myTimer1 = muTimer();
extern unsigned long timer0_millis;

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

  noInterrupts();
  timer0_millis = UINT32_MAX - 5000;

  Serial.print("timer0_millis HEX: ");
  Serial.println(timer0_millis, HEX);
  Serial.print("timer0_millis DEC: ");
  Serial.println(timer0_millis, DEC);

  interrupts();

  Serial.println("Init done.");
  Serial.println("-----------------------------");
}

void loop()
{
  // 10000ms on delay, condition gets true only once if time is elapsed
  if (myTimer1.delayOnTrigger(1, 10000))
  {
    Serial.println("Delay-On triggered.");

    Serial.print("timer0_millis HEX: ");
    Serial.println(timer0_millis, HEX);
    Serial.print("timer0_millis DEC: ");
    Serial.println(timer0_millis, DEC);
    Serial.println("-----------------------------");
  }

  // The library is non-blocking.
  // Other code can get executed there in the meanwhile.
  // ...
}

The serial monitor did show the following result, means to me there is no overflow issue:

timer0_millis HEX: FFFFEC77
timer0_millis DEC: 4294962295
Init done.
-----------------------------
Delay-On triggered.
timer0_millis HEX: 13AD
timer0_millis DEC: 5037
-----------------------------
1 Like

Sorry, 1000 not 1000000 – I’ve been working with microseconds recently.

Glad it works, thanks for testing.

Cheers,
Norm.

That’s what I thought.
I am wondering if there is a need for such a library which can handle microseconds as well.

It is always good to test, even if you are that sure that your code must work.

I just added the overflow test-code to the example folder of the library that everyone can prove that on all plattforms.

Hi Michael,

The Arduino micros() function rolls over far too quickly – in my book I worked it out at 71 minutes 34.967295 microSeconds. (Plus 1 for the rollover).

I suppose it’s possible to have a micros() based delay library but most of the schedulers etc I’ve seen (ahem, all three of them!) use millis().

Cheers,
Norm.

I think microseconds might be usefull for some applications.
I just added configuration options to use ms or us as time base.