Minimizing Cellular Data Use

This is another double logger example, but in this case, both loggers are going at the same interval and the only difference between the loggers is the list of variables. There are two sets of variables, all coming from Yosemitech sensors. Because each sensor outputs temperature and we don't want to waste cellular data sending out multiple nearly identical temperature values, we have one logger that logs every possible variable result to the SD card and another logger that sends only unique results to the EnviroDIY data portal.

The modem used in this example is a SIM800 based Sodaq GPRSBee r6.

The sensors used in this example are Yosemitech Y504 Dissolved Oxygen Sensor, Yosemitech Y511 Turbidity Sensor with Wiper, Yosemitech Y514 Chlorophyll Sensor, and Yosemitech Y520 Conductivity Sensor.



Unique Features of the Data Saving Example

  • Uses AltSoftSerial to create an additional serial port for RS485 communication.
  • All variables are created and named with their parent sensor (as opposed to being created within the variable array).
  • Two different variable arrays and loggers are created and used.
    • Many of the same variables are used in both arrays.
    • Only one of the loggers publishes data.
  • 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.
    • It also shows how to forcibly set serial pins LOW at the start and end of the loop in order to prevent power loss through an RS485 adapter.

To Use this Example

Prepare and set up PlatformIO

  • Register a site and sensors at the Monitor My Watershed/EnviroDIY data portal (http://monitormywatershed.org/)
  • Create a new PlatformIO project
  • Replace the contents of the platformio.ini for your new project with the platformio.ini file in the examples/data_saving 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 data_saving.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
2const char *LoggerID = "XXXX";

Set the universally universal identifiers (UUID) for each variable

  • Go back to the web page for your site at the Monitor My Watershed/EnviroDIY data portal (http://monitormywatershed.org/)
  • For each variable, find the dummy UUID ("12345678-abcd-1234-ef00-1234567890ab") and replace it with the real UUID for the variable.

Upload!

  • Test everything at home before deploying out in the wild!

PlatformIO Configuration

1; PlatformIO Project Configuration File
2;
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
7;
8; Please visit documentation for the other options and examples
9; http://docs.platformio.org/page/projectconf.html
10
11[platformio]
12description = ModularSensors example using two "loggers" to save cellular data
13
14[env:mayfly]
15monitor_speed = 115200
16board = mayfly
17platform = atmelavr
18framework = arduino
19lib_ldf_mode = deep+
20lib_ignore =
21 RTCZero
22 Adafruit NeoPixel
23 Adafruit GFX Library
24 Adafruit SSD1306
25 Adafruit ADXL343
26 Adafruit STMPE610
27 Adafruit TouchScreen
28 Adafruit ILI9341
29build_flags =
30 -DSDI12_EXTERNAL_PCINT
31 -DNEOSWSERIAL_EXTERNAL_PCINT
32 -DMQTT_MAX_PACKET_SIZE=240
33 -DTINY_GSM_RX_BUFFER=64
34 -DTINY_GSM_YIELD_MS=2
35lib_deps =
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
40 https://github.com/PaulStoffregen/AltSoftSerial.git

The Complete Code

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