Friday, June 25, 2021

Alarm !!! Using the Time and TimeAlarms libraries

For an index to all my stories click this text

In a previous story I showed you how to get the actual time from an NTP server. This is essential for this story so re-read that story here http://lucstechblog.blogspot.com/2020/02/time-is-of-essence.html

Once you got the real-life-time you need to do something with it. Naturally you can build a clock with the accurate time. That should not be to difficult. Later on in this story you'll find a setup for that.

I am doing a large project with my son in law. We are building an aquaponics system. He is doing the mechanics and I am doing the electronic part.
In an aquaponics system it is essential that the water pump runs every XX minutes for some pre-defined time. Further the lights should go on at scheduled times, and the fish need to be fed etc. etc. etc.

Getting the time as discussed in the previous story was just the first step. We now have to do something with that time.

I am going to show you how you can schedule certain events to happen at a certain time. In the example I will use 3 leds and they will all bet set on and off at regular intervals.

Libraries.

To use the time efficiently with an ESP8266 or ESP32 we need several libaries:


ESP8266WiFi.h  is the wifi library for the ESP8266
Wifi.h         is the wifi library for the ESP32

 
NTPClient.h    is the library that processes the time from the NTP server
WiFiUDP.h      is the UDP library needed for communicating with the NTP library

These were discussed in the previous story:  http://lucstechblog.blogspot.com/2020/02/time-is-of-essence.html

Next we need libraries to make processing the time easier and to set alarms.
 
TimeLib.h      a library for processing the time
TimeAlarms.h   a library for setting alarms

Both libraries can be installed directly in the Arduino IDE.
In the Arduino IDE open the sketch tab and chose Include Libraries and from the shown drop-down menu choose Manage Libraries.

You can also download the zip-libraries from their Github pages and install them with the library manager as zip-libraries. You can find both libraries on Github with the following links:

https://github.com/PaulStoffregen/Time
https://github.com/PaulStoffregen/TimeAlarms

Before I give you the example project I will give a short description of these libraries. I am sure you will find a lot of usefull purposes for them.

Time library

The time library has usefull commands for setting the time and for getting time details like hour, minute and seconds.
These are the most important commands:
 
setTime(t);                      // set the system time to the give time t
setTime(hr,min,sec,day,mnth,yr); // same as above, yr is 2 or 4 digit yr

We can use the above commands to set the time. You can do that manually but you can also first get the time from the NTP server and use that with the time library. Another option would be to attach a Real Time Clock (RTC) to your ESP. The RTC is a small PCB with a chip and a battery that keeps the time running accurately even if the ESP (or Arduino) has been powered down.

IMPORTANT !!
The setTime command must be the first command in your setup() routine. Otherwise certain alarm functions will not work properly..

This library makes sure we need to get the time from thye NTP server only once every xx hours so we will not overload the NTP server.

time_t t = now(); // store the current time in time variable t

hour(t);          // returns the hour for the given time t

minute(t);        // returns the minute for the given time t

second(t);        // returns the second for the given time t

day(t);           // the day for the given time t

weekday(t);       // day of the week for the given time t

month(t);         // the month for the given time t

year(t);          // the year for the given time t


Once the time is set we can use these commands to get the hour, minute, seconds in our program.

TimeAlarms library

Normally when we want to check the state of something you will make a loop that tests that state. If we would do that with the time library you would make a loop like this:

if (second(t) == 4)
{
 led1 = HIGH);
}


So you would presume that this will set led1 ON every 4 seconds. However that is not true. This will set led1 only everytime second(t) has the value 4. That's just once a minute.
As this might be fine for some purposes, sometimes we want to set the led on every 4 seconds, every 2 hour or every day at 8 o'clock.
That can be done with the TimeAlarms library.

If you look at the github page you can find a detailed explanation for all the commands. I will highlight a few here.

Alarm.alarmRepeat(2,30,0, getntptime);

This commands the library to run the getntptime routine every day at 02.30 This way you only check the actual time once a day. You can use this command multiple times and each new version can call a different routine for example setting lights on or off or to start your lawn sprinkler once a day etc etc.

Alarm.alarmRepeat(dowSunday, 20,15,0, Sundaytrigger);

This would command the library to call the Sundaytrigger routine once a week on Sunday evening at 20:15 hour in the evening.

Alarm.timerRepeat(Period, TimerFunction);

This would call the TimerFunction every Period. Period being time in seconds. This is what you would use, for example, to blink a led every 4 seconds or so.

Alarm.timerRepeat(2, 10, 0, TimerFunction);

This would call the TimerFunction every 10 minutes after the second hour. So at 2, 4, 6, 8 etc hour and ten minutes.

Alarm.delay(1000);

This works the same as the normal Arduino delay function. However you MUST use this instead of the normal delay() function as Alarm.delay() will not interfere with the TimerAlarms library and the normal delay() function will disrupt the alarms.

Stopping an alarm

It is possible to reset an alarm or just stop it. To do this you first need to make a function from the alarm. This sounds more difficult as it is. Lets look at an example that puts an alarm off after it has run 10 times.

AlarmId idalarm4;

int countalarm4 = 0;

First part is to make a new variable into which we will place the function. We also create a second variable that defines how often the alarm has been activated before it is stopped. Put these at the beginning of your program where you define all variables.


idalarm4 = Alarm.timerRepeat(0,30,0, runalarm4);

This would activate the runalarm4() routine every 30 minutes. It is assigned to the idalarm4 variable. Put this line in your setup() routine.

void runalarm4()
{
  Serial.println("alarm 4 starts");
  digitalWrite(Led, HIGH);   // turn the LED on
  Alarm.delay(500);          // wait for half a second
  digitalWrite(Led, LOW);    // turn the LED off
  countalarm4 ++;

  if (countalarm4 >10)

     {
     Alarm.disable(idalarm4);
     }
}

What happens here is that each time the alarm is activated the countalarm4 variable is increased by 1. When it reaches 10 the alarm is disabled.

There is one important thing left:

void loop() 
{
  Alarm.delay(1000); 
}


IMPORTANT !!!
You MUST put at least the Alarm.delay() command in your loop(). If you don't the alarms will not be triggered !!!!

The amount of delay is arbitrary. Just do not make it too short like under one second. Don't make it too long either as the alamrs will fail. So one to 5 seconds will be ok.

These are the most important commands. As stated earlier you can find more commands on the Github page of this library: https://github.com/PaulStoffregen/TimeAlarms

You can set 6 alarms when using an Arduino. If you use this library with an ESP8266 you can set 20 alarms. 

Advanced users.

You can expand the number of alarms in the TimeAlarms header file.

#if !defined(dtNBR_ALARMS )
#if defined(__AVR__)
#define dtNBR_ALARMS 6   // max is 255
#elif defined(ESP8266)
#define dtNBR_ALARMS 20  // for esp8266 chip - max is 255
#else
#define dtNBR_ALARMS 12  // assume non-AVR has more memory
#endif
#endif

The header file can be found in your documents, arduino, libraries folder.
You can edit this file with any text editor.

#if !defined(dtNBR_ALARMS )
#if defined(__AVR__)
#define dtNBR_ALARMS 6   // max is 255
#elif defined(ESP8266)
#define dtNBR_ALARMS 20  // for esp8266 chip - max is 255
#elif defined(ESP32)
#define dtNBR_ALARMS 20  // for esp32 chip - max is 255
#else
#define dtNBR_ALARMS 12  // assume non-AVR has more memory
#endif
#endif

This is how you make the code universal for Arduino, ESP8266 and ESP32. I just added an extra elif statement that tests for the ESP32 and then sets the amount of alarms to 20.

This was a lot of theory so let's see how this works practically.

Breadboard

I made a simple setup to test these libraries with 3 leds.



The setup is indeed very simple. Just an ESP8266 being a Wemos D1 mini with 3 resistors attached to D5, D6 and D7 through current delimiting resistors of 220 Ohm

Example 1

The first program is a simple program that blinks the 3 leds each at a different pace. The first led will blonk every 4 seconds, the second every 8 seconds and the last every 16 seconds.

#include <TimeLib.h>
#include <TimeAlarms.h>

#define Led1 D5
#define Led2 D6
#define Led3 D7

void setup() 
{
  Serial.begin(115200);
  
  pinMode(Led1, OUTPUT);
  pinMode(Led2, OUTPUT);
  pinMode(Led3, OUTPUT);

  setTime(0,0,0,0,0,0);

  Alarm.timerRepeat(4,  alarm1); //
  Alarm.timerRepeat(8,  alarm2); //
  Alarm.timerRepeat(16,  alarm3); // 

  digitalWrite(Led1, LOW);
  digitalWrite(Led2, LOW);
  digitalWrite(Led3, LOW);
}

void loop()
{
  Alarm.delay(5000);
}

void alarm1() 
{
  Serial.println("ALARM 1");
  digitalWrite(Led1, HIGH);   // turn the LED on (HIGH is the voltage level)
  Alarm.delay(500);                       // wait for a second
  digitalWrite(Led1, LOW);    // turn the LED off by making the voltage LOW
}

void alarm2() 
{
  Serial.println("ALARM 2");
  digitalWrite(Led2, HIGH);   // turn the LED on (HIGH is the voltage level)
  Alarm.delay(500);                       // wait for a second
  digitalWrite(Led2, LOW);    // turn the LED off by making the voltage LOW
}

void alarm3() 
{
  Serial.println("ALARM 3");
  digitalWrite(Led3, HIGH);   // turn the LED on (HIGH is the voltage level)
  Alarm.delay(500);                       // wait for a second
  digitalWrite(Led3, LOW);    // turn the LED off by making the voltage LOW
}

Now you might expect that at certain times they blink all together. But no there is a slight delay. In this example it makes the leds blink after eachother. In a real world example you would not have lamps or pumps switching at such short interval after eachtother, so that would not pose a problem.

You can watch the Serial Monitor as some information is printed there. Adjust it all to your own liking.

Example 2

Example 2 shows you how to get the time from the NTP server and have the leds blink on the same intervals as the previous program. Only now the time is the real life time. As a bonus there is a clock printed in the Serial Monitor and it also shows you the sequence of the alarms. The clock part was shamelesly stolen from the demo program included in the library.

#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUDP.h>

#include <TimeLib.h>
#include <TimeAlarms.h>

// WiFi credentials for your router
const char* ssid = "XXXXXXXXXXXXXX"; 
const char* password = "YYYYYYYYYYYYY"; 

#define Led1 D5
#define Led2 D6
#define Led3 D7

#define NTP_OFFSET   60 * 60      // In seconds
#define NTP_ADDRESS  "europe.pool.ntp.org"

WiFiUDP ntpUDP;

NTPClient timeClient(ntpUDP, NTP_ADDRESS, NTP_OFFSET);

void setup() 
{
  pinMode(Led1, OUTPUT);
  pinMode(Led2, OUTPUT);
  pinMode(Led3, OUTPUT);
  Serial.begin(115200);

  Serial.print("Starting the connection");
  WiFi.begin(ssid, password); // Connect to WiFi

  while (WiFi.status() != WL_CONNECTED) 
    {
      delay(500);
      Serial.print(".");
    }

  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  Alarm.timerRepeat(4,  alarm4); //
  Alarm.timerRepeat(8,  alarm8); //
  Alarm.timerRepeat(16,  alarm16); // 

  timeClient.update();
  String newtime = timeClient.getFormattedTime();
  Serial.print("the time is : ");
  Serial.println(newtime);
  Serial.print("Hour    : ");
  Serial.println((newtime.substring(0,2)).toInt());
  Serial.print("Minute  : ");
  Serial.println((newtime.substring(3,5)).toInt());
  Serial.print("Seconds : ");
  Serial.println((newtime.substring(6,8)).toInt());
  Serial.println(timeClient.getFormattedDate());
  setTime((newtime.substring(0,2)).toInt(),(newtime.substring(3,5)).toInt(),(newtime.substring(6,8)).toInt(),1,1,20); 
}

void loop() 
{
  digitalClockDisplay();
  Alarm.delay(1000); // show clock every second
}

void alarm4() 
{
  Serial.println("ALARM 4 ACTIVATED");
  digitalWrite(Led1, HIGH);   // turn the LED on 
  Alarm.delay(500);           // wait for half a second
  digitalWrite(Led1, LOW);    // turn the LED off 
}

void alarm8() 
{
  Serial.println("ALARM 8 ACTIVATED");
  digitalWrite(Led2, HIGH);   // turn the LED 
  Alarm.delay(500);           // wait for half a second
  digitalWrite(Led2, LOW);    // turn the LED off 
}

void alarm16() 
{
  Serial.println("ALARM 16 ACTIVATED");
  digitalWrite(Led3, HIGH);   // turn the LED on 
  Alarm.delay(500);           // wait for half a second
  digitalWrite(Led3, LOW);    // turn the LED off 
}

void digitalClockDisplay() 
{
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println();
}

void printDigits(int digits) 
{
  Serial.print(":");
  if (digits < 10)
  Serial.print('0');
  Serial.print(digits);
}


As usual: do not forget to replace the XXXXXX and YYYYY with your own routers credentials.

You can re-read how to fetch the time from the NTP server in this story: http://lucstechblog.blogspot.com/2020/02/time-is-of-essence.html

Real life use.

As stated before this is part of the software I need for a large project: building a complete aquaponics system for my son in law.

This is however suitable for may time oriented projects like a clock with an Oled screen or just a TM1637 4 digit seven segment screen. You can use this to set your lampos on and off on unpredictable times when you are not at home, to scare away the burglars. Or just to have a trigger to log data from a thermometer or whatever.
Use your imagination and.......

Have fun
Till next time

Luc Volders