data_saving.ino example

Example publishing only a portion of the logged variables.

Example publishing only a portion of the logged variables.

See the walkthrough page for detailed instructions.

1/** =========================================================================
2 * @example{lineno} data_saving.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>
6 *
7 * @brief Example publishing only a portion of the logged variables.
8 *
9 * See [the walkthrough page](@ref example_data_saving) for detailed
10 * instructions.
11 *
12 * @m_examplenavigation{example_data_saving,}
13 * ======================================================================= */
14
15// ==========================================================================
16// Defines for TinyGSM
17// ==========================================================================
18/** Start [defines] */
19#ifndef TINY_GSM_RX_BUFFER
20#define TINY_GSM_RX_BUFFER 64
21#endif
22#ifndef TINY_GSM_YIELD_MS
23#define TINY_GSM_YIELD_MS 2
24#endif
25/** End [defines] */
26
27
28// ==========================================================================
29// Include the libraries required for any data logger
30// ==========================================================================
31/** Start [includes] */
32// The Arduino library is needed for every Arduino program.
33#include <Arduino.h>
34
35// Include the main header for ModularSensors
36#include <ModularSensors.h>
37/** End [includes] */
38
39
40// ==========================================================================
41// Creating Additional Serial Ports
42// ==========================================================================
43/** Start [serial_ports] */
44// The modem and a number of sensors communicate over UART/TTL - often called
45// "serial". "Hardware" serial ports (automatically controlled by the MCU) are
46// generally the most accurate and should be configured and used for as many
47// peripherals as possible. In some cases (ie, modbus communication) many
48// sensors can share the same serial port.
49
50// For AVR boards
51#if !defined(ARDUINO_ARCH_SAMD) && !defined(ATMEGA2560)
52// Unfortunately, most AVR boards have only one or two hardware serial ports,
53// so we'll set up three types of extra software serial ports to use
54
55// AltSoftSerial by Paul Stoffregen
56// (https://github.com/PaulStoffregen/AltSoftSerial) is the most accurate
57// software serial port for AVR boards. AltSoftSerial can only be used on one
58// set of pins on each board so only one AltSoftSerial port can be used. Not all
59// AVR boards are supported by AltSoftSerial.
60#include <AltSoftSerial.h>
61AltSoftSerial altSoftSerial;
62#endif // End software serial for avr boards
63
64#if defined(ARDUINO_SAMD_FEATHER_M0)
65#include <wiring_private.h> // Needed for SAMD pinPeripheral() function
66
67// Set up a 'new' UART using SERCOM1
68// The Rx will be on digital pin 11, which is SERCOM1's Pad #0
69// The Tx will be on digital pin 10, which is SERCOM1's Pad #2
70// NOTE: SERCOM1 is undefined on a "standard" Arduino Zero and many clones,
71// but not all! Please check the variant.cpp file for you individual
72// board!
73Uart Serial2(&sercom1, 11, 10, SERCOM_RX_PAD_0, UART_TX_PAD_2);
74// Hand over the interrupts to the sercom port
75void SERCOM1_Handler() {
76 Serial2.IrqHandler();
77}
78#define ENABLE_SERIAL2
79
80#endif // End hardware serial on SAMD21 boards
81/** End [serial_ports] */
82
83
84// ==========================================================================
85// Data Logging Options
86// ==========================================================================
87/** Start [logging_options] */
88// The name of this program file
89const char* sketchName = "data_saving.ino";
90// Logger ID, also becomes the prefix for the name of the data file on SD card
91const char* LoggerID = "XXXXX";
92// How frequently (in minutes) to log data
93const int8_t loggingInterval = 15;
94// Your logger's timezone.
95const int8_t timeZone = -5; // Eastern Standard Time
96// NOTE: Daylight savings time will not be applied! Please use standard time!
97
98// Set the input and output pins for the logger
99// NOTE: Use -1 for pins that do not apply
100const int32_t serialBaud = 115200; // Baud rate for debugging
101const int8_t greenLED = 8; // Pin for the green LED
102const int8_t redLED = 9; // Pin for the red LED
103const int8_t buttonPin = 21; // Pin for debugging mode (ie, button pin)
104const int8_t wakePin = 31; // MCU interrupt/alarm pin to wake from sleep
105// Mayfly 0.x, 1.x D31 = A7
106// Set the wake pin to -1 if you do not want the main processor to sleep.
107// In a SAMD system where you are using the built-in rtc, set wakePin to 1
108const int8_t sdCardPwrPin = -1; // MCU SD card power pin
109const int8_t sdCardSSPin = 12; // SD card chip select/slave select pin
110const int8_t sensorPowerPin = 22; // MCU pin controlling main sensor power
111/** End [logging_options] */
112
113
114// ==========================================================================
115// Wifi/Cellular Modem Options
116// ==========================================================================
117/** Start [sodaq_2g_bee_r6] */
118// For the Sodaq 2GBee R6 and R7 based on the SIMCom SIM800
119// NOTE: The Sodaq GPRSBee doesn't expose the SIM800's reset pin
120#include <modems/Sodaq2GBeeR6.h>
121// Create a reference to the serial port for the modem
122HardwareSerial& modemSerial = Serial1; // Use hardware serial if possible
123const int32_t modemBaud = 9600; // SIM800 does auto-bauding by default
124
125// Modem Pins - Describe the physical pin connection of your modem to your board
126// NOTE: Use -1 for pins that do not apply
127const int8_t modemVccPin = 23; // MCU pin controlling modem power
128const int8_t modemStatusPin = 19; // MCU pin used to read modem status
129const int8_t modemLEDPin = redLED; // MCU pin connected an LED to show modem
130 // status (-1 if unconnected)
131
132// Network connection information
133const char* apn = "xxxxx"; // The APN for the gprs connection
134
135Sodaq2GBeeR6 modem2GB(&modemSerial, modemVccPin, modemStatusPin, apn);
136// Create an extra reference to the modem by a generic name
137Sodaq2GBeeR6 modem = modem2GB;
138
139// Create RSSI and signal strength variable pointers for the modem
140Variable* modemRSSI = new Modem_RSSI(&modem,
141 "12345678-abcd-1234-ef00-1234567890ab");
142Variable* modemSignalPct =
143 new Modem_SignalPercent(&modem, "12345678-abcd-1234-ef00-1234567890ab");
144/** End [sodaq_2g_bee_r6] */
145
146
147// ==========================================================================
148// Using the Processor as a Sensor
149// ==========================================================================
150/** Start [processor_sensor] */
151#include <sensors/ProcessorStats.h>
152
153// Create the main processor chip "sensor" - for general metadata
154const char* mcuBoardVersion = "v1.1";
155ProcessorStats mcuBoard(mcuBoardVersion);
156
157// Create sample number, battery voltage, and free RAM variable pointers for the
158// processor
159Variable* mcuBoardBatt = new ProcessorStats_Battery(
160 &mcuBoard, "12345678-abcd-1234-ef00-1234567890ab");
161Variable* mcuBoardAvailableRAM = new ProcessorStats_FreeRam(
162 &mcuBoard, "12345678-abcd-1234-ef00-1234567890ab");
163Variable* mcuBoardSampNo = new ProcessorStats_SampleNumber(
164 &mcuBoard, "12345678-abcd-1234-ef00-1234567890ab");
165/** End [processor_sensor] */
166
167
168// ==========================================================================
169// Maxim DS3231 RTC (Real Time Clock)
170// ==========================================================================
171/** Start [ds3231] */
172#include <sensors/MaximDS3231.h>
173
174// Create a DS3231 sensor object
175MaximDS3231 ds3231(1);
176
177// Create a temperature variable pointer for the DS3231
178Variable* ds3231Temp =
179 new MaximDS3231_Temp(&ds3231, "12345678-abcd-1234-ef00-1234567890ab");
180/** End [ds3231] */
181
182
183// ==========================================================================
184// Settings shared between Modbus sensors
185// ==========================================================================
186/** Start [modbus_shared] */
187// Create a reference to the serial port for modbus
188#if defined(ENABLE_SERIAL2) || defined(ENVIRODIY_STONEFLY_M4) || \
189 defined(ATMEGA2560) || defined(ARDUINO_AVR_MEGA2560)
190HardwareSerial& modbusSerial = Serial2; // Use hardware serial if possible
191#else
192AltSoftSerial& modbusSerial = altSoftSerial; // For software serial
193#endif
194
195// Define some pins that will be shared by all modbus sensors
196const int8_t rs485AdapterPower =
197 sensorPowerPin; // RS485 adapter power pin (-1 if unconnected)
198const int8_t modbusSensorPower = A3; // Sensor power pin
199const int8_t rs485EnablePin = -1; // Adapter RE/DE pin (-1 if not applicable)
200/** End [modbus_shared] */
201
202
203// ==========================================================================
204// Yosemitech Y504 Dissolved Oxygen Sensor
205// ==========================================================================
206/** Start [Y504] */
207#include <sensors/YosemitechY504.h>
208
209byte y504ModbusAddress = 0x04; // The modbus address of the Y504
210const uint8_t y504NumberReadings = 5;
211// The manufacturer recommends averaging 10 readings, but we take 5 to minimize
212// power consumption
213
214// Create a Yosemitech Y504 dissolved oxygen sensor object
215YosemitechY504 y504(y504ModbusAddress, modbusSerial, rs485AdapterPower,
216 modbusSensorPower, rs485EnablePin, y504NumberReadings);
217
218// Create the dissolved oxygen percent, dissolved oxygen concentration, and
219// temperature variable pointers for the Y504
220Variable* y504DOpct =
221 new YosemitechY504_DOpct(&y504, "12345678-abcd-1234-ef00-1234567890ab");
222Variable* y504DOmgL =
223 new YosemitechY504_DOmgL(&y504, "12345678-abcd-1234-ef00-1234567890ab");
224Variable* y504Temp =
225 new YosemitechY504_Temp(&y504, "12345678-abcd-1234-ef00-1234567890ab");
226/** End [Y504] */
227
228
229// ==========================================================================
230// Yosemitech Y511 Turbidity Sensor with Wiper
231// ==========================================================================
232/** Start [Y511] */
233#include <sensors/YosemitechY511.h>
234
235byte y511ModbusAddress = 0x1A; // The modbus address of the Y511
236const uint8_t y511NumberReadings = 5;
237// The manufacturer recommends averaging 10 readings, but we take 5 to minimize
238// power consumption
239
240// Create a Y511-A Turbidity sensor object
241YosemitechY511 y511(y511ModbusAddress, modbusSerial, rs485AdapterPower,
242 modbusSensorPower, rs485EnablePin, y511NumberReadings);
243
244// Create turbidity and temperature variable pointers for the Y511
245Variable* y511Turb =
246 new YosemitechY511_Turbidity(&y511, "12345678-abcd-1234-ef00-1234567890ab");
247Variable* y511Temp =
248 new YosemitechY511_Temp(&y511, "12345678-abcd-1234-ef00-1234567890ab");
249/** End [Y511] */
250
251
252// ==========================================================================
253// Yosemitech Y514 Chlorophyll Sensor
254// ==========================================================================
255/** Start [Y514] */
256#include <sensors/YosemitechY514.h>
257
258byte y514ModbusAddress = 0x14; // The modbus address of the Y514
259const uint8_t y514NumberReadings = 5;
260// The manufacturer recommends averaging 10 readings, but we take 5 to
261// minimize power consumption
262
263// Create a Y514 chlorophyll sensor object
264YosemitechY514 y514(y514ModbusAddress, modbusSerial, rs485AdapterPower,
265 modbusSensorPower, rs485EnablePin, y514NumberReadings);
266
267// Create chlorophyll concentration and temperature variable pointers for the
268// Y514
269Variable* y514Chloro = new YosemitechY514_Chlorophyll(
270 &y514, "12345678-abcd-1234-ef00-1234567890ab");
271Variable* y514Temp =
272 new YosemitechY514_Temp(&y514, "12345678-abcd-1234-ef00-1234567890ab");
273/** End [Y514] */
274
275
276// ==========================================================================
277// Yosemitech Y520 Conductivity Sensor
278// ==========================================================================
279/** Start [Y520] */
280#include <sensors/YosemitechY520.h>
281
282byte y520ModbusAddress = 0x20; // The modbus address of the Y520
283const uint8_t y520NumberReadings = 5;
284// The manufacturer recommends averaging 10 readings, but we take 5 to minimize
285// power consumption
286
287// Create a Y520 conductivity sensor object
288YosemitechY520 y520(y520ModbusAddress, modbusSerial, rs485AdapterPower,
289 modbusSensorPower, rs485EnablePin, y520NumberReadings);
290
291// Create specific conductance and temperature variable pointers for the Y520
292Variable* y520Cond =
293 new YosemitechY520_Cond(&y520, "12345678-abcd-1234-ef00-1234567890ab");
294Variable* y520Temp =
295 new YosemitechY520_Temp(&y520, "12345678-abcd-1234-ef00-1234567890ab");
296/** End [Y520] */
297
298
299// ==========================================================================
300// Creating the Variable Array[s] and Filling with Variable Objects
301// ==========================================================================
302/** Start [variable_arrays] */
303// FORM2: Fill array with already created and named variable pointers
304// We put ALL of the variable pointers into the first array
305Variable* variableList_complete[] = {
306 mcuBoardSampNo, mcuBoardBatt, mcuBoardAvailableRAM,
307 ds3231Temp, y504DOpct, y504DOmgL,
308 y504Temp, y511Turb, y511Temp,
309 y514Chloro, y514Temp, y520Cond,
310 y520Temp, modemRSSI, modemSignalPct};
311// Count up the number of pointers in the array
312int variableCount_complete = sizeof(variableList_complete) /
313 sizeof(variableList_complete[0]);
314// Create the VariableArray object
315VariableArray arrayComplete(variableCount_complete, variableList_complete);
316
317
318// Put only the particularly interesting variables into a second array
319// NOTE: We can the same variables into multiple arrays
320Variable* variableList_toGo[] = {y504DOmgL, y504Temp, y511Turb,
321 y514Chloro, y520Cond, modemRSSI};
322// Count up the number of pointers in the array
323int variableCount_toGo = sizeof(variableList_toGo) /
324 sizeof(variableList_toGo[0]);
325// Create the VariableArray object
326VariableArray arrayToGo(variableCount_toGo, variableList_toGo);
327/** End [variable_arrays] */
328
329
330// ==========================================================================
331// The Logger Object[s]
332// ==========================================================================
333/** Start [loggers] */
334// Create one new logger instance for the complete array
335Logger loggerAllVars(LoggerID, loggingInterval, &arrayComplete);
336
337// Create "another" logger for the variables to go out over the internet
338Logger loggerToGo(LoggerID, loggingInterval, &arrayToGo);
339/** End [loggers] */
340
341
342// ==========================================================================
343// Creating Data Publisher[s]
344// ==========================================================================
345/** Start [publishers] */
346// Create a publisher to Monitor My Watershed / EnviroDIY Data Sharing Portal
347// Device registration and sampling feature information can be obtained after
348// registration at https://monitormywatershed.org or https://data.envirodiy.org
349const char* registrationToken =
350 "12345678-abcd-1234-ef00-1234567890ab"; // Device registration token
351const char* samplingFeature =
352 "12345678-abcd-1234-ef00-1234567890ab"; // Sampling feature UUID
353
354// Create a data publisher for the Monitor My Watershed/EnviroDIY POST endpoint
355// This is only attached to the logger with the shorter variable array
356#include <publishers/EnviroDIYPublisher.h>
357EnviroDIYPublisher EnviroDIYPost(loggerToGo, registrationToken,
358 samplingFeature);
359/** End [publishers] */
360
361
362// ==========================================================================
363// Working Functions
364// ==========================================================================
365/** Start [working_functions] */
366// Flashes the LED's on the primary board
367void greenRedFlash(uint8_t numFlash = 4, uint8_t rate = 75) {
368 for (uint8_t i = 0; i < numFlash; i++) {
369 digitalWrite(greenLED, HIGH);
370 digitalWrite(redLED, LOW);
371 delay(rate);
372 digitalWrite(greenLED, LOW);
373 digitalWrite(redLED, HIGH);
374 delay(rate);
375 }
376 digitalWrite(redLED, LOW);
377}
378
379// Reads the battery voltage
380// NOTE: This will actually return the battery level from the previous update!
381float getBatteryVoltage() {
382 if (mcuBoard.sensorValues[0] == -9999) mcuBoard.update();
383 return mcuBoard.sensorValues[0];
384}
385/** End [working_functions] */
386
387
388// ==========================================================================
389// Arduino Setup Function
390// ==========================================================================
391/** Start [setup] */
392void setup() {
393// Wait for USB connection to be established by PC
394// NOTE: Only use this when debugging - if not connected to a PC, this
395// could prevent the script from starting
396#if defined(SERIAL_PORT_USBVIRTUAL)
397 while (!SERIAL_PORT_USBVIRTUAL && (millis() < 10000)) {
398 // wait
399 }
400#endif
401
402 // Start the primary serial connection
403 Serial.begin(serialBaud);
404
405 // Print a start-up note to the first serial port
406 Serial.print(F("Now running "));
407 Serial.print(sketchName);
408 Serial.print(F(" on Logger "));
409 Serial.println(LoggerID);
410 Serial.println();
411
412 Serial.print(F("Using ModularSensors Library version "));
413 Serial.println(MODULAR_SENSORS_VERSION);
414
415 // Start the serial connection with the modem
416 modemSerial.begin(modemBaud);
417
418 // Start the stream for the modbus sensors; all currently supported modbus
419 // sensors use 9600 baud
420 modbusSerial.begin(9600);
421
422// Assign pins SERCOM functionality for SAMD boards
423// NOTE: This must happen *after* the various serial.begin statements
424#if defined(ARDUINO_SAMD_FEATHER_M0)
425 // Serial2
426 pinPeripheral(10, PIO_SERCOM); // Serial2 Tx/Dout = SERCOM1 Pad #2
427 pinPeripheral(11, PIO_SERCOM); // Serial2 Rx/Din = SERCOM1 Pad #0
428#endif
429 // Set up pins for the LED's
430 pinMode(greenLED, OUTPUT);
431 digitalWrite(greenLED, LOW);
432 pinMode(redLED, OUTPUT);
433 digitalWrite(redLED, LOW);
434 // Blink the LEDs to show the board is on and starting up
435 greenRedFlash();
436
437 // Set the timezones for the logger/data and the RTC
438 // Logging in the given time zone
439 Logger::setLoggerTimeZone(timeZone);
440 // It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0)
441 loggerClock::setRTCOffset(0);
442
443 // Attach the same modem to both loggers
444 // It is only needed for the logger that will be sending out data, but
445 // attaching it to both allows either logger to control NIST synchronization
446 loggerAllVars.attachModem(modem);
447 loggerToGo.attachModem(modem);
448 modem.setModemLED(modemLEDPin);
449 loggerAllVars.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin,
450 greenLED);
451
452 // Set up the connection information with EnviroDIY for both loggers
453 // Doing this for both loggers ensures that the header of the csv will have
454 // the tokens in it
455 loggerAllVars.setSamplingFeatureUUID(samplingFeature);
456 loggerToGo.setSamplingFeatureUUID(samplingFeature);
457
458 // Note: Please change these battery voltages to match your battery
459
460 // Set up the sensors, except at lowest battery level
461 // Like with the logger, because the variables are duplicated in the arrays,
462 // we only need to do this for the complete array.
463 if (getBatteryVoltage() > 3.4) {
464 Serial.println(F("Setting up sensors..."));
465 arrayComplete.setupSensors();
466 }
467
468 // Sync the clock if it isn't valid or we have battery to spare
469 if (getBatteryVoltage() > 3.55 || !loggerClock::isRTCSane()) {
470 // Synchronize the RTC with NIST
471 // This will also set up the modem
472 loggerAllVars.syncRTC();
473 }
474
475 // Create the log file, adding the default header to it
476 // Do this last so we have the best chance of getting the time correct and
477 // all sensor names correct
478 // Writing to the SD card can be power intensive, so if we're skipping
479 // the sensor setup we'll skip this too.
480 if (getBatteryVoltage() > 3.4) {
481 loggerAllVars.turnOnSDcard(
482 true); // true = wait for card to settle after power up
483 loggerAllVars.createLogFile(true); // true = write a new header
484 loggerAllVars.turnOffSDcard(
485 true); // true = wait for internal housekeeping after write
486 }
487
488 // Call the processor sleep
489 Serial.println(F("Putting processor to sleep"));
490 loggerAllVars.systemSleep();
491}
492/** End [setup] */
493
494
495// ==========================================================================
496// Arduino Loop Function
497// ==========================================================================
498/** Start [loop] */
499// Use this long loop when you want to do something special
500// Because of the way alarms work on the RTC, it will wake the processor and
501// start the loop every minute exactly on the minute.
502// The processor may also be woken up by another interrupt or level change on a
503// pin - from a button or some other input.
504// The "if" statements in the loop determine what will happen - whether the
505// sensors update, testing mode starts, or it goes back to sleep.
506void loop() {
507 // Reset the watchdog
508 extendedWatchDog::resetWatchDog();
509
510 // Assuming we were woken up by the clock, check if the current time is an
511 // even interval of the logging interval
512 // We're only doing anything at all if the battery is above 3.4V
513 if (loggerAllVars.checkInterval() && getBatteryVoltage() > 3.4) {
514 // Flag to notify that we're in already awake and logging a point
515 Logger::isLoggingNow = true;
516 extendedWatchDog::resetWatchDog();
517
518 // Print a line to show new reading
519 Serial.println(F("------------------------------------------"));
520 // Turn on the LED to show we're taking a reading
521 loggerAllVars.alertOn();
522 // Power up the SD Card, but skip any waits after power up
523 loggerAllVars.turnOnSDcard(false);
524 extendedWatchDog::resetWatchDog();
525
526 // Start the stream for the modbus sensors
527 // Because RS485 adapters tend to "steal" current from the data pins
528 // we will explicitly start and end the serial connection in the loop.
529 modbusSerial.begin(9600);
530
531 // Do a complete update on the "full" array.
532 // This this includes powering all of the sensors, getting updated
533 // values, and turing them back off.
534 // NOTE: The wake function for each sensor should force sensor setup
535 // to run if the sensor was not previously set up.
536 arrayComplete.completeUpdate();
537 extendedWatchDog::resetWatchDog();
538
539 // End the stream for the modbus sensors
540 // Because RS485 adapters tend to "steal" current from the data pins
541 // we will explicitly start and end the serial connection in the loop.
542 modbusSerial.end();
543
544#if defined(AltSoftSerial_h)
545 // Explicitly set the pin modes for the AltSoftSerial pins to make sure
546 // they're low
547 pinMode(5, OUTPUT); // On a Mayfly, pin D5 is the AltSoftSerial Tx pin
548 pinMode(6, OUTPUT); // On a Mayfly, pin D6 is the AltSoftSerial Rx pin
549 digitalWrite(5, LOW);
550 digitalWrite(6, LOW);
551#endif
552
553#if defined(ARDUINO_SAMD_FEATHER_M0)
554 digitalWrite(10, LOW);
555 digitalWrite(11, LOW);
556#endif
557
558 // Create a csv data record and save it to the log file
559 loggerAllVars.logToSD();
560 extendedWatchDog::resetWatchDog();
561
562 // Connect to the network
563 // Again, we're only doing this if the battery is doing well
564 if (getBatteryVoltage() > 3.55) {
565 if (modem.modemWake()) {
566 extendedWatchDog::resetWatchDog();
567 if (modem.connectInternet()) {
568 extendedWatchDog::resetWatchDog();
569 // Publish data to remotes
570 loggerToGo.publishDataToRemotes();
571 modem.updateModemMetadata();
572
573 extendedWatchDog::resetWatchDog();
574 // Sync the clock at noon
575 // NOTE: All loggers have the same clock, pick one
576 if (Logger::markedLocalUnixTime != 0 &&
577 Logger::markedLocalUnixTime % 86400 == 43200) {
578 Serial.println(F("Running a daily clock sync..."));
579 loggerClock::setRTClock(modem.getNISTTime(), 0,
580 epochStart::unix_epoch);
581 }
582
583 // Disconnect from the network
584 extendedWatchDog::resetWatchDog();
585 modem.disconnectInternet();
586 }
587 }
588 // Turn the modem off
589 extendedWatchDog::resetWatchDog();
590 modem.modemSleepPowerDown();
591 }
592
593 // Cut power from the SD card - without additional housekeeping wait
594 loggerAllVars.turnOffSDcard(false);
595 extendedWatchDog::resetWatchDog();
596 // Turn off the LED
597 loggerAllVars.alertOff();
598 // Print a line to show reading ended
599 Serial.println(F("------------------------------------------\n"));
600
601 // Unset flag
602 Logger::isLoggingNow = false;
603 }
604
605 // Check if it was instead the testing interrupt that woke us up
606 // Want to enter the testing mode for the "complete" logger so we can see
607 // the data from _ALL_ sensors
608 // NOTE: The testingISR attached to the button at the end of the "setup()"
609 // function turns on the startTesting flag. So we know if that flag is set
610 // then we want to run the testing mode function.
611 if (Logger::startTesting) loggerAllVars.benchTestingMode();
612
613 // Call the processor sleep
614 // Only need to do this for one of the loggers
615 loggerAllVars.systemSleep();
616}
617
618/** End [loop] */