This is a more complicated example using two different logger instances to log data at two different intervals, in this case, an AM3215 logging every minute, while checking the battery voltage only every 5 minutes. This showcases both how to use two different logging instances and how to use some of the functions to set up your own logging loop rather than using the logData() function.
Unique Features of the Double Logger Example Two different variable arrays and loggers are created and used.The Variables for the arrays are created within the array. There is no variable overlap between the two arrays or loggers. The loop
function is expanded into its components rather than using the logData
functions.This demonstrates how to write the loop out, without using the logData
functions. This shows which functions are required for each of the two loggers and which can be used in common. To Use this Example Prepare and set up PlatformIO Create a new PlatformIO project Replace the contents of the platformio.ini for your new project with the platformio.ini file in the examples/double_logger folder on GitHub.It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. Without this, the program won't compile. Open double_ logger.ino and save it to your computer.After opening the link, you should be able to right click anywhere on the page and select "Save Page As". Move it into the src directory of your project. Delete main.cpp in that folder. Set the logger ID Change the "XXXX" in this section of code to the loggerID assigned by Stroud: 1 // Logger ID, also becomes the prefix for the name of the data file on SD card
2 const char * LoggerID = "XXXX" ;
Upload! Test everything at home before deploying out in the wild! PlatformIO Configuration 1 ; PlatformIO Project Configuration File
3 ; Build options: build flags, source filter
4 ; Upload options: custom upload port, speed and extra flags
5 ; Library options: dependencies, extra library storages
6 ; Advanced options: extra scripting
8 ; Please visit documentation for the other options and examples
9 ; http://docs.platformio.org/page/projectconf.html
12 description = ModularSensors example using two "loggers" to log at different intervals
30 -DSDI12_EXTERNAL_PCINT
31 -DNEOSWSERIAL_EXTERNAL_PCINT
32 -DMQTT_MAX_PACKET_SIZE = 240
33 -DTINY_GSM_RX_BUFFER = 64
36 envirodiy/EnviroDIY_ModularSensors
37 ; ^^ Use this when working from an official release of the library
38 ; https://github.com/EnviroDIY/ModularSensors.git#develop
39 ; ^^ Use this when if you want to pull from the develop branch
The Complete Code 1 /** =========================================================================
2 * @example{lineno} double_logger.ino
3 * @copyright Stroud Water Research Center
4 * @license This example is published under the BSD-3 license.
5 * @author Sara Geleskie Damiano <sdamiano@stroudcenter.org>
7 * @brief Example logging at two different timing intervals
9 * See [the walkthrough page](@ref example_double_log) for detailed
12 * @m_examplenavigation{example_double_log,}
13 * ======================================================================= */
15 // ==========================================================================
17 // ==========================================================================
19 #ifndef TINY_GSM_RX_BUFFER
20 #define TINY_GSM_RX_BUFFER 64
22 #ifndef TINY_GSM_YIELD_MS
23 #define TINY_GSM_YIELD_MS 2
27 // ==========================================================================
28 // Include the libraries required for any data logger
29 // ==========================================================================
30 /** Start [includes] */
31 // The Arduino library is needed for every Arduino program.
34 // Include the main header for ModularSensors
35 #include <ModularSensors.h>
39 // ==========================================================================
40 // Data Logging Options
41 // ==========================================================================
42 /** Start [logging_options] */
43 // The name of this program file
44 const char * sketchName = "double_logger.ino" ;
45 // Logger ID - we're only using one logger ID for both "loggers"
46 const char * LoggerID = "XXXXX" ;
47 // The TWO filenames for the different logging intervals
48 const char * FileName5min = "Logger_5MinuteInterval.csv" ;
49 const char * FileName1min = "Logger_1MinuteInterval.csv" ;
50 // Your logger's timezone.
51 const int8_t timeZone = -5 ; // Eastern Standard Time
52 // NOTE: Daylight savings time will not be applied! Please use standard time!
54 // Set the input and output pins for the logger
55 // NOTE: Use -1 for pins that do not apply
56 const int32_t serialBaud = 115200 ; // Baud rate for debugging
57 const int8_t greenLED = 8 ; // Pin for the green LED
58 const int8_t redLED = 9 ; // Pin for the red LED
59 const int8_t buttonPin = 21 ; // Pin for debugging mode (ie, button pin)
60 const int8_t wakePin = 31 ; // MCU interrupt/alarm pin to wake from sleep
62 // Set the wake pin to -1 if you do not want the main processor to sleep.
63 // In a SAMD system where you are using the built-in rtc, set wakePin to 1
64 const int8_t sdCardPwrPin = -1 ; // MCU SD card power pin
65 const int8_t sdCardSSPin = 12 ; // SD card chip select/slave select pin
66 const int8_t sensorPowerPin = 22 ; // MCU pin controlling main sensor power
67 /** End [logging_options] */
70 // ==========================================================================
71 // Wifi/Cellular Modem Options
72 // ==========================================================================
73 /** Start [xbee_wifi] */
74 // For the Digi Wifi XBee (S6B)
75 #include <modems/DigiXBeeWifi.h>
76 // Create a reference to the serial port for the modem
78 HardwareSerial & modemSerial = Serial1 ; // Use hardware serial if possible
79 const int32_t modemBaud = 9600 ; // All XBee's use 9600 by default
81 // Modem Pins - Describe the physical pin connection of your modem to your board
82 // NOTE: Use -1 for pins that do not apply
83 const int8_t modemVccPin = -2 ; // MCU pin controlling modem power
84 const int8_t modemStatusPin = 19 ; // MCU pin used to read modem status
85 const bool useCTSforStatus = true ; // Flag to use the modem CTS pin for status
86 const int8_t modemResetPin = 20 ; // MCU pin connected to modem reset pin
87 const int8_t modemSleepRqPin = 23 ; // MCU pin for modem sleep/wake request
88 const int8_t modemLEDPin = redLED ; // MCU pin connected an LED to show modem
89 // status (-1 if unconnected)
91 // Network connection information
92 const char * wifiId = "xxxxx" ; // WiFi access point, unnecessary for GPRS
93 const char * wifiPwd = "xxxxx" ; // WiFi password, unnecessary for GPRS
95 DigiXBeeWifi modemXBWF ( & modemSerial , modemVccPin , modemStatusPin ,
96 useCTSforStatus , modemResetPin , modemSleepRqPin , wifiId ,
98 // Create an extra reference to the modem by a generic name
99 DigiXBeeWifi modem = modemXBWF ;
100 /** End [xbee_wifi] */
103 // ==========================================================================
104 // Using the Processor as a Sensor
105 // ==========================================================================
106 /** Start [processor_sensor] */
107 #include <sensors/ProcessorStats.h>
109 // Create the main processor chip "sensor" - for general metadata
110 const char * mcuBoardVersion = "v1.1" ;
111 ProcessorStats mcuBoard ( mcuBoardVersion );
112 /** End [processor_sensor] */
115 // ==========================================================================
116 // Maxim DS3231 RTC (Real Time Clock)
117 // ==========================================================================
119 #include <sensors/MaximDS3231.h>
121 // Create a DS3231 sensor object
122 MaximDS3231 ds3231 ( 1 );
126 // ==========================================================================
127 // AOSong AM2315 Digital Humidity and Temperature Sensor
128 // ==========================================================================
129 /** Start [ao_song_am2315] */
130 #include <sensors/AOSongAM2315.h>
132 const int8_t I2CPower = sensorPowerPin ; // Power pin (-1 if unconnected)
134 // Create and return the AOSong AM2315 sensor object
135 AOSongAM2315 am2315 ( I2CPower );
136 /** End [ao_song_am2315] */
139 // ==========================================================================
140 // Creating the Variable Array[s] and Filling with Variable Objects
141 // ==========================================================================
142 /** Start [variable_arrays] */
143 // The variables to record at 1 minute intervals
144 Variable * variableList_at1min [] = { new AOSongAM2315_Humidity ( & am2315 ),
145 new AOSongAM2315_Temp ( & am2315 )};
146 // Count up the number of pointers in the 1-minute array
147 int variableCount1min = sizeof ( variableList_at1min ) /
148 sizeof ( variableList_at1min [ 0 ]);
149 // Create the 1-minute VariableArray object
150 VariableArray array1min ;
152 // The variables to record at 5 minute intervals
153 Variable * variableList_at5min [] = { new MaximDS3231_Temp ( & ds3231 ),
154 new ProcessorStats_Battery ( & mcuBoard ),
155 new ProcessorStats_FreeRam ( & mcuBoard )};
156 // Count up the number of pointers in the 5-minute array
157 int variableCount5min = sizeof ( variableList_at5min ) /
158 sizeof ( variableList_at5min [ 0 ]);
159 // Create the 5-minute VariableArray object
160 VariableArray array5min ;
161 /** End [variable_arrays] */
164 // ==========================================================================
165 // The Logger Object[s]
166 // ==========================================================================
167 /** Start [loggers] */
168 // Create the 1-minute logger instance
171 // Create the 5-minute logger instance
176 // ==========================================================================
178 // ==========================================================================
179 /** Start [working_functions] */
180 // Flashes the LED's on the primary board
181 void greenredflash ( uint8_t numFlash = 4 , uint8_t rate = 75 ) {
182 for ( uint8_t i = 0 ; i < numFlash ; i ++ ) {
183 digitalWrite ( greenLED , HIGH );
184 digitalWrite ( redLED , LOW );
186 digitalWrite ( greenLED , LOW );
187 digitalWrite ( redLED , HIGH );
190 digitalWrite ( redLED , LOW );
192 /** End [working_functions] */
195 // ==========================================================================
196 // Arduino Setup Function
197 // ==========================================================================
200 // Start the primary serial connection
201 Serial . begin ( serialBaud );
203 // Print a start-up note to the first serial port
204 Serial . print ( F ( "Now running " ));
205 Serial . print ( sketchName );
206 Serial . print ( F ( " on Logger " ));
207 Serial . println ( LoggerID );
210 Serial . print ( F ( "Using ModularSensors Library version " ));
211 Serial . println ( MODULAR_SENSORS_VERSION );
212 Serial . print ( F ( "TinyGSM Library version " ));
213 Serial . println ( TINYGSM_VERSION );
216 // Start the serial connection with the modem
217 modemSerial . begin ( modemBaud );
219 // Set up pins for the LED's
220 pinMode ( greenLED , OUTPUT );
221 digitalWrite ( greenLED , LOW );
222 pinMode ( redLED , OUTPUT );
223 digitalWrite ( redLED , LOW );
224 // Blink the LEDs to show the board is on and starting up
227 // Set the timezones for the logger/data and the RTC
228 // Logging in the given time zone
229 Logger :: setLoggerTimeZone ( timeZone );
230 // It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0)
231 Logger :: setRTCTimeZone ( 0 );
233 // Begin the variable array[s], logger[s], and publisher[s]
234 array1min . begin ( variableCount1min , variableList_at1min );
235 array5min . begin ( variableCount5min , variableList_at5min );
236 logger1min . begin ( LoggerID , 1 , & array1min );
237 logger5min . begin ( LoggerID , 5 , & array5min );
238 logger1min . setLoggerPins ( wakePin , sdCardSSPin , sdCardPwrPin , buttonPin ,
240 logger5min . setLoggerPins ( wakePin , sdCardSSPin , sdCardPwrPin , buttonPin ,
244 modem . setModemLED ( modemLEDPin );
246 // Set up the sensors (do this directly on the VariableArray)
247 array1min . setupSensors ();
248 array5min . setupSensors ();
250 // Print out the current time
251 Serial . print ( F ( "Current RTC time is: " ));
252 Serial . println ( Logger :: formatDateTime_ISO8601 ( Logger :: getNowUTCEpoch ()));
253 Serial . print ( F ( "Current localized logger time is: " ));
254 Serial . println ( Logger :: formatDateTime_ISO8601 ( Logger :: getNowLocalEpoch ()));
255 // Connect to the network
256 if ( modem . connectInternet ()) {
257 // Synchronize the RTC
258 logger1min . setRTClock ( modem . getNISTTime ());
259 modem . updateModemMetadata ();
260 // Disconnect from the network
261 modem . disconnectInternet ();
263 // Turn off the modem
264 modem . modemSleepPowerDown ();
266 // Give the loggers different file names
267 // If we wanted to auto-generate the file name, that could also be done by
268 // not calling this function, but in that case if both "loggers" have the
269 // same logger id, they will end up with the same filename
270 logger1min . setFileName ( FileName1min );
271 logger5min . setFileName ( FileName5min );
273 // Setup the logger files. Specifying true will put a default header at
274 // on to the file when it's created.
275 // Because we've already called setFileName, we do not need to specify the
276 // file name for this function.
277 logger1min . turnOnSDcard (
278 true ); // true = wait for card to settle after power up
279 logger1min . createLogFile ( true ); // true = write a new header
280 logger5min . createLogFile ( true ); // true = write a new header
281 logger1min . turnOffSDcard (
282 true ); // true = wait for internal housekeeping after write
284 Serial . println ( F ( "Logger setup finished! \n " ));
285 Serial . println ( F ( "------------------------------------------" ));
288 // Call the processor sleep
289 // Only need to do this for one of the loggers
290 logger1min . systemSleep ();
295 // ==========================================================================
296 // Arduino Loop Function
297 // ==========================================================================
299 // Because of the way alarms work on the RTC, it will wake the processor and
300 // start the loop every minute exactly on the minute.
301 // The processor may also be woken up by another interrupt or level change on a
302 // pin - from a button or some other input.
303 // The "if" statements in the loop determine what will happen - whether the
304 // sensors update, testing mode starts, or it goes back to sleep.
306 // Check if the current time is an even interval of the logging interval
307 // For whichever logger we call first, use the checkInterval() function.
308 if ( logger1min . checkInterval ()) {
309 // Print a line to show new reading
310 Serial . println ( F ( "--------------------->111<---------------------" ));
311 // Turn on the LED to show we're taking a reading
312 digitalWrite ( greenLED , HIGH );
314 // Send power to all of the sensors (do this directly on the
316 Serial . print ( F ( "Powering sensors... \n " ));
317 array1min . sensorsPowerUp ();
318 logger1min . watchDogTimer . resetWatchDog ();
319 // Wake up all of the sensors (do this directly on the VariableArray)
320 Serial . print ( F ( "Waking sensors... \n " ));
321 array1min . sensorsWake ();
322 logger1min . watchDogTimer . resetWatchDog ();
323 // Update the values from all attached sensors (do this directly on the
325 Serial . print ( F ( "Updating sensor values... \n " ));
326 array1min . updateAllSensors ();
327 logger1min . watchDogTimer . resetWatchDog ();
328 // Put sensors to sleep (do this directly on the VariableArray)
329 Serial . print ( F ( "Putting sensors back to sleep... \n " ));
330 array1min . sensorsSleep ();
331 logger1min . watchDogTimer . resetWatchDog ();
332 // Cut sensor power (do this directly on the VariableArray)
333 Serial . print ( F ( "Cutting sensor power... \n " ));
334 array1min . sensorsPowerDown ();
335 logger1min . watchDogTimer . resetWatchDog ();
337 // Stream the csv data to the SD card
338 logger1min . turnOnSDcard ( true );
339 logger1min . logToSD ();
340 logger1min . turnOffSDcard ( true );
341 logger1min . watchDogTimer . resetWatchDog ();
344 digitalWrite ( greenLED , LOW );
345 // Print a line to show reading ended
346 Serial . println ( F ( "---------------------<111>--------------------- \n " ));
348 // Check if the already marked time is an even interval of the logging
349 // interval For logger[s] other than the first one, use the
350 // checkMarkedInterval() function.
351 if ( logger5min . checkMarkedInterval ()) {
352 // Print a line to show new reading
353 Serial . println ( F ( "--------------------->555<---------------------" ));
354 // Turn on the LED to show we're taking a reading
355 digitalWrite ( redLED , HIGH );
357 // Send power to all of the sensors (do this directly on the
359 Serial . print ( F ( "Powering sensors... \n " ));
360 array5min . sensorsPowerUp ();
361 logger1min . watchDogTimer . resetWatchDog ();
362 // Wake up all of the sensors (do this directly on the VariableArray)
363 Serial . print ( F ( "Waking sensors... \n " ));
364 array5min . sensorsWake ();
365 logger1min . watchDogTimer . resetWatchDog ();
366 // Update the values from all attached sensors (do this directly on the
368 Serial . print ( F ( "Updating sensor values... \n " ));
369 array5min . updateAllSensors ();
370 logger1min . watchDogTimer . resetWatchDog ();
371 // Put sensors to sleep (do this directly on the VariableArray)
372 Serial . print ( F ( "Putting sensors back to sleep... \n " ));
373 array5min . sensorsSleep ();
374 logger1min . watchDogTimer . resetWatchDog ();
375 // Cut sensor power (do this directly on the VariableArray)
376 Serial . print ( F ( "Cutting sensor power... \n " ));
377 array5min . sensorsPowerDown ();
378 logger1min . watchDogTimer . resetWatchDog ();
380 // Stream the csv data to the SD card
381 logger5min . turnOnSDcard ( true );
382 logger5min . logToSD ();
383 logger5min . turnOffSDcard ( true );
384 logger1min . watchDogTimer . resetWatchDog ();
387 digitalWrite ( redLED , LOW );
388 // Print a line to show reading ended
389 Serial . println ( F ( "--------------------<555>--------------------- \n " ));
391 // Once a day, at noon, sync the clock
392 if ( Logger :: markedLocalEpochTime % 86400 == 43200 ) {
395 // Connect to the network
396 if ( modem . connectInternet ()) {
397 // Synchronize the RTC
398 logger1min . setRTClock ( modem . getNISTTime ());
399 // Disconnect from the network
400 modem . disconnectInternet ();
402 // Turn off the modem
403 modem . modemSleepPowerDown ();
406 // Call the processor sleep
407 // Only need to do this for one of the loggers
408 logger1min . systemSleep ();