Adding a timer to schedule readings¶
Note
This example builds on the previous example titled: Basics. Only the sections of code which have been changed or added are discussed here. However, a full copy of the complete sketch can be found at the end of this page.
In this example we will demonstrate the use of a RTCTimer to schedule regular events. This example builds on the previous A Basic DataLogger example, but instead of using the delay()method, it uses a scheduling timer to control the frequency of the readings.
If you examine the output from the Basics example you will see that the actual sensor readings are not exactly at one second intervals. This is due to the use of the method delay() which pauses the execution for a specified number of milliseconds. However, since the actual time between readings is the sum of the execution time and the specified delay, this results in readings which in this case are taken at intervals of longer than one second.
The RTCTimer library provides the functionality for scheduling specific methods at fixed intervals. Here we will modify the A Basic DataLogger example to take the sensor readings using a method which is called at fixed intervals by a RTCTimer object.
Additional Required Components¶
- None
Additional Required Libraries¶
- RTCTimer
Library Installation¶
The RTCTimer library is included with the SODAQ Mbili files that you have already installed.
If necessary, refer to Section 2 of the Getting Started guide for details on where to download from and how to install the SODAQ Mbili files.
Hardware Setup¶
You should refer to both the board diagram and Grove sockets page for additional information.
- First, plug the TPH Sensor into the Grove I2C socket.
- Then, plug the 0.5W solar panel and the 1A LiPo battery into their respective sockets.
Turn on the SODAQ Mbili board, compile and upload the following sketch from the Arduino IDE onto the SODAQ Mbili board. Leave the USB cable plugged in and open the Serial Monitor (Ctrl-Shift-M) and ensure that it is set to the 9600 baud rate.
Additional Sketch Code¶
Libarary Includes¶
In addition to the existing libraries, we must now also include the RTCTimer library in the sketch using the #include compiler directive.
Globals¶
In addition to the existing Globals we declare a RTCTimer object.
setup()¶
In addition to the existing setup code, we make a call to the user defined method setupTimer()which handles the initialisation of the timer and the scheduling of the main datalogging method takeReading(). We also make a call to takeReading() to take first sensor reading immediately instead of waiting for the first scheduled reading.
//Setup timer events
setupTimer();
//Echo the data header to the serial connection
Serial.println(DATA_HEADER);
//Take first reading immediately
takeReading(getNow());
loop()¶
The main functionality of this sketch is handled by the scheduled callback methodtakeReading(), which is called automatically at a scheduled interval. Here in the loop() method all we do is make a call to RTCTimer.update() which updates the timer object and ensures any scheduled events are called if they are now due.
takeReading()¶
This method is scheduled to be called automatically by the timer object. It is scheduled to be called every READ_DELAY number of ticks. In this particular setup each tick is equal to one millisecond.
Here we start by getting the sensor readings with a call to the user defined methodcreateDataRecord() which returns us a String containing the current sensor readings. We then pass that String to the user defined method logData() which writes it to the log file. We also send the data to the Serial Monitor using the Serial.println() method.
void takeReading(uint32_t ts)
{
//Create the data record
String dataRec = createDataRecord();
//Save the data record to the log file
logData(dataRec);
//Echo the data to the serial connection
Serial.println(dataRec);
}
setupTimer()¶
Here we schedule the takeReading() method to be called every READ_DELAY number of ticks. This is done by a call to the RTCTimer.every() method where the first parameter specifies the number of ticks between each call to the method specified by the second parameter.
void setupTimer()
{
//Instruct the RTCTimer how to get the current time reading
timer.setNowCallback(getNow);
//Schedule the reading every second
timer.every(READ_DELAY, takeReading);
}
Note
The scheduled callback methods must be declared in the following format, where label is replaced with the actual name of the method:
Note
You must specify a separate callback method that RTCTimer will use to determine the current time reading (you should do this before scheduling any callbacks). This method must be declared in the following format, where label is replaced with the actual name of the method:
getNow()¶
This method is called by the RTCTimer object to update its current time reading. Here we use the method millis() which returns the number of milliseconds which have elapsed since the current sketch began.
Note
In this example one tick or one increment to the time value is equal to one millisecond. It is also possible to use one second intervals by using readings from the Real Time Clock. Just remember that the scheduled callbacks will be called after the specified number of ticks have elapsed regardless of what unit of time that equates to.
Output¶
If you open the Serial Monitor (Ctrl-Shift-M), you should see output similar to this. Notice that the sensor readings are now at regular one second intervals:
Final Sketch Code¶
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
//SODAQ Mbili libraries
#include <RTCTimer.h>
#include <Sodaq_BMP085.h>
#include <Sodaq_SHT2x.h>
#include <Sodaq_DS3231.h>
//The delay between the sensor readings
#define READ_DELAY 1000
//Digital pin 11 is the MicroSD slave select pin on the Mbili
#define SD_SS_PIN 11
//The data log file
#define FILE_NAME "DataLog.txt"
//Data header
#define DATA_HEADER "TimeDate, TempSHT21, TempBMP, PressureBMP, HumiditySHT21"
//TPH BMP sensor
Sodaq_BMP085 bmp;
//RTC Timer
RTCTimer timer;
void setup()
{
//Initialise the serial connection
Serial.begin(9600);
//Initialise sensors
setupSensors();
//Initialise log file
setupLogFile();
//Setup timer events
setupTimer();
//Echo the data header to the serial connection
Serial.println(DATA_HEADER);
//Take first reading immediately
takeReading(getNow());
}
void loop()
{
//Update the timer
timer.update();
}
void takeReading(uint32_t ts)
{
//Create the data record
String dataRec = createDataRecord();
//Save the data record to the log file
logData(dataRec);
//Echo the data to the serial connection
Serial.println(dataRec);
}
void setupSensors()
{
//Initialise the wire protocol for the TPH sensors
Wire.begin();
//Initialise the TPH BMP sensor
bmp.begin();
//Initialise the DS3231 RTC
rtc.begin();
}
void setupLogFile()
{
//Initialise the SD card
if (!SD.begin(SD_SS_PIN))
{
Serial.println("Error: SD card failed to initialise or is missing.");
//Hang
while (true);
}
//Check if the file already exists
bool oldFile = SD.exists(FILE_NAME);
//Open the file in write mode
File logFile = SD.open(FILE_NAME, FILE_WRITE);
//Add header information if the file did not already exist
if (!oldFile)
{
logFile.println(DATA_HEADER);
}
//Close the file to save it
logFile.close();
}
void setupTimer()
{
//Instruct the RTCTimer how to get the current time reading
timer.setNowCallback(getNow);
//Schedule the reading every second
timer.every(READ_DELAY, takeReading);
}
void logData(String rec)
{
//Re-open the file
File logFile = SD.open(FILE_NAME, FILE_WRITE);
//Write the CSV data
logFile.println(rec);
//Close the file to save it
logFile.close();
}
String createDataRecord()
{
//Create a String type data record in csv format
//TimeDate, TempSHT21, TempBMP, PressureBMP, HumiditySHT21
String data = getDateTime() + ", ";
data += String(SHT2x.GetTemperature()) + ", ";
data += String(bmp.readTemperature()) + ", ";
data += String(bmp.readPressure() / 100) + ", ";
data += String(SHT2x.GetHumidity());
return data;
}
String getDateTime()
{
String dateTimeStr;
//Create a DateTime object from the current time
DateTime dt(rtc.makeDateTime(rtc.now().getEpoch()));
//Convert it to a String
dt.addToString(dateTimeStr);
return dateTimeStr;
}
uint32_t getNow()
{
return millis();
}