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
2 const 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
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 save cellular data
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
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>
7 * @brief Example publishing only a portion of the logged variables.
9 * See [the walkthrough page](@ref example_data_saving) for detailed
12 * @m_examplenavigation{example_data_saving,}
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
28 // ==========================================================================
29 // Include the libraries required for any data logger
30 // ==========================================================================
31 /** Start [includes] */
32 // The Arduino library is needed for every Arduino program.
35 // Include the main header for ModularSensors
36 #include <ModularSensors.h>
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.
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
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>
61 AltSoftSerial altSoftSerial ;
62 #endif // End software serial for avr boards
64 #if defined(ARDUINO_SAMD_FEATHER_M0)
65 #include <wiring_private.h> // Needed for SAMD pinPeripheral() function
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
73 Uart Serial2 ( & sercom1 , 11 , 10 , SERCOM_RX_PAD_0 , UART_TX_PAD_2 );
74 // Hand over the interrupts to the sercom port
75 void SERCOM1_Handler () {
80 #endif // End hardware serial on SAMD21 boards
81 /** End [serial_ports] */
84 // ==========================================================================
85 // Data Logging Options
86 // ==========================================================================
87 /** Start [logging_options] */
88 // The name of this program file
89 const char * sketchName = "data_saving.ino" ;
90 // Logger ID, also becomes the prefix for the name of the data file on SD card
91 const char * LoggerID = "XXXXX" ;
92 // How frequently (in minutes) to log data
93 const int8_t loggingInterval = 15 ;
94 // Your logger's timezone.
95 const int8_t timeZone = -5 ; // Eastern Standard Time
96 // NOTE: Daylight savings time will not be applied! Please use standard time!
98 // Set the input and output pins for the logger
99 // NOTE: Use -1 for pins that do not apply
100 const int32_t serialBaud = 115200 ; // Baud rate for debugging
101 const int8_t greenLED = 8 ; // Pin for the green LED
102 const int8_t redLED = 9 ; // Pin for the red LED
103 const int8_t buttonPin = 21 ; // Pin for debugging mode (ie, button pin)
104 const 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
108 const int8_t sdCardPwrPin = -1 ; // MCU SD card power pin
109 const int8_t sdCardSSPin = 12 ; // SD card chip select/slave select pin
110 const int8_t sensorPowerPin = 22 ; // MCU pin controlling main sensor power
111 /** End [logging_options] */
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
122 HardwareSerial & modemSerial = Serial1 ; // Use hardware serial if possible
123 const int32_t modemBaud = 9600 ; // SIM800 does auto-bauding by default
125 // Modem Pins - Describe the physical pin connection of your modem to your board
126 // NOTE: Use -1 for pins that do not apply
127 const int8_t modemVccPin = 23 ; // MCU pin controlling modem power
128 const int8_t modemStatusPin = 19 ; // MCU pin used to read modem status
129 const int8_t modemLEDPin = redLED ; // MCU pin connected an LED to show modem
130 // status (-1 if unconnected)
132 // Network connection information
133 const char * apn = "xxxxx" ; // The APN for the gprs connection
135 Sodaq2GBeeR6 modem2GB ( & modemSerial , modemVccPin , modemStatusPin , apn );
136 // Create an extra reference to the modem by a generic name
137 Sodaq2GBeeR6 modem = modem2GB ;
139 // Create RSSI and signal strength variable pointers for the modem
140 Variable * modemRSSI = new Modem_RSSI ( & modem ,
141 "12345678-abcd-1234-ef00-1234567890ab" );
142 Variable * modemSignalPct =
143 new Modem_SignalPercent ( & modem , "12345678-abcd-1234-ef00-1234567890ab" );
144 /** End [sodaq_2g_bee_r6] */
147 // ==========================================================================
148 // Using the Processor as a Sensor
149 // ==========================================================================
150 /** Start [processor_sensor] */
151 #include <sensors/ProcessorStats.h>
153 // Create the main processor chip "sensor" - for general metadata
154 const char * mcuBoardVersion = "v1.1" ;
155 ProcessorStats mcuBoard ( mcuBoardVersion );
157 // Create sample number, battery voltage, and free RAM variable pointers for the
159 Variable * mcuBoardBatt = new ProcessorStats_Battery (
160 & mcuBoard , "12345678-abcd-1234-ef00-1234567890ab" );
161 Variable * mcuBoardAvailableRAM = new ProcessorStats_FreeRam (
162 & mcuBoard , "12345678-abcd-1234-ef00-1234567890ab" );
163 Variable * mcuBoardSampNo = new ProcessorStats_SampleNumber (
164 & mcuBoard , "12345678-abcd-1234-ef00-1234567890ab" );
165 /** End [processor_sensor] */
168 // ==========================================================================
169 // Maxim DS3231 RTC (Real Time Clock)
170 // ==========================================================================
172 #include <sensors/MaximDS3231.h>
174 // Create a DS3231 sensor object
175 MaximDS3231 ds3231 ( 1 );
177 // Create a temperature variable pointer for the DS3231
178 Variable * ds3231Temp =
179 new MaximDS3231_Temp ( & ds3231 , "12345678-abcd-1234-ef00-1234567890ab" );
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)
190 HardwareSerial & modbusSerial = Serial2 ; // Use hardware serial if possible
192 AltSoftSerial & modbusSerial = altSoftSerial ; // For software serial
195 // Define some pins that will be shared by all modbus sensors
196 const int8_t rs485AdapterPower =
197 sensorPowerPin ; // RS485 adapter power pin (-1 if unconnected)
198 const int8_t modbusSensorPower = A3 ; // Sensor power pin
199 const int8_t rs485EnablePin = -1 ; // Adapter RE/DE pin (-1 if not applicable)
200 /** End [modbus_shared] */
203 // ==========================================================================
204 // Yosemitech Y504 Dissolved Oxygen Sensor
205 // ==========================================================================
207 #include <sensors/YosemitechY504.h>
209 byte y504ModbusAddress = 0x04 ; // The modbus address of the Y504
210 const uint8_t y504NumberReadings = 5 ;
211 // The manufacturer recommends averaging 10 readings, but we take 5 to minimize
214 // Create a Yosemitech Y504 dissolved oxygen sensor object
215 YosemitechY504 y504 ( y504ModbusAddress , modbusSerial , rs485AdapterPower ,
216 modbusSensorPower , rs485EnablePin , y504NumberReadings );
218 // Create the dissolved oxygen percent, dissolved oxygen concentration, and
219 // temperature variable pointers for the Y504
221 new YosemitechY504_DOpct ( & y504 , "12345678-abcd-1234-ef00-1234567890ab" );
223 new YosemitechY504_DOmgL ( & y504 , "12345678-abcd-1234-ef00-1234567890ab" );
225 new YosemitechY504_Temp ( & y504 , "12345678-abcd-1234-ef00-1234567890ab" );
229 // ==========================================================================
230 // Yosemitech Y511 Turbidity Sensor with Wiper
231 // ==========================================================================
233 #include <sensors/YosemitechY511.h>
235 byte y511ModbusAddress = 0x1A ; // The modbus address of the Y511
236 const uint8_t y511NumberReadings = 5 ;
237 // The manufacturer recommends averaging 10 readings, but we take 5 to minimize
240 // Create a Y511-A Turbidity sensor object
241 YosemitechY511 y511 ( y511ModbusAddress , modbusSerial , rs485AdapterPower ,
242 modbusSensorPower , rs485EnablePin , y511NumberReadings );
244 // Create turbidity and temperature variable pointers for the Y511
246 new YosemitechY511_Turbidity ( & y511 , "12345678-abcd-1234-ef00-1234567890ab" );
248 new YosemitechY511_Temp ( & y511 , "12345678-abcd-1234-ef00-1234567890ab" );
252 // ==========================================================================
253 // Yosemitech Y514 Chlorophyll Sensor
254 // ==========================================================================
256 #include <sensors/YosemitechY514.h>
258 byte y514ModbusAddress = 0x14 ; // The modbus address of the Y514
259 const uint8_t y514NumberReadings = 5 ;
260 // The manufacturer recommends averaging 10 readings, but we take 5 to
261 // minimize power consumption
263 // Create a Y514 chlorophyll sensor object
264 YosemitechY514 y514 ( y514ModbusAddress , modbusSerial , rs485AdapterPower ,
265 modbusSensorPower , rs485EnablePin , y514NumberReadings );
267 // Create chlorophyll concentration and temperature variable pointers for the
269 Variable * y514Chloro = new YosemitechY514_Chlorophyll (
270 & y514 , "12345678-abcd-1234-ef00-1234567890ab" );
272 new YosemitechY514_Temp ( & y514 , "12345678-abcd-1234-ef00-1234567890ab" );
276 // ==========================================================================
277 // Yosemitech Y520 Conductivity Sensor
278 // ==========================================================================
280 #include <sensors/YosemitechY520.h>
282 byte y520ModbusAddress = 0x20 ; // The modbus address of the Y520
283 const uint8_t y520NumberReadings = 5 ;
284 // The manufacturer recommends averaging 10 readings, but we take 5 to minimize
287 // Create a Y520 conductivity sensor object
288 YosemitechY520 y520 ( y520ModbusAddress , modbusSerial , rs485AdapterPower ,
289 modbusSensorPower , rs485EnablePin , y520NumberReadings );
291 // Create specific conductance and temperature variable pointers for the Y520
293 new YosemitechY520_Cond ( & y520 , "12345678-abcd-1234-ef00-1234567890ab" );
295 new YosemitechY520_Temp ( & y520 , "12345678-abcd-1234-ef00-1234567890ab" );
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
305 Variable * 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
312 int variableCount_complete = sizeof ( variableList_complete ) /
313 sizeof ( variableList_complete [ 0 ]);
314 // Create the VariableArray object
315 VariableArray arrayComplete ( variableCount_complete , variableList_complete );
318 // Put only the particularly interesting variables into a second array
319 // NOTE: We can the same variables into multiple arrays
320 Variable * variableList_toGo [] = { y504DOmgL , y504Temp , y511Turb ,
321 y514Chloro , y520Cond , modemRSSI };
322 // Count up the number of pointers in the array
323 int variableCount_toGo = sizeof ( variableList_toGo ) /
324 sizeof ( variableList_toGo [ 0 ]);
325 // Create the VariableArray object
326 VariableArray arrayToGo ( variableCount_toGo , variableList_toGo );
327 /** End [variable_arrays] */
330 // ==========================================================================
331 // The Logger Object[s]
332 // ==========================================================================
333 /** Start [loggers] */
334 // Create one new logger instance for the complete array
335 Logger loggerAllVars ( LoggerID , loggingInterval , & arrayComplete );
337 // Create "another" logger for the variables to go out over the internet
338 Logger loggerToGo ( LoggerID , loggingInterval , & arrayToGo );
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
349 const char * registrationToken =
350 "12345678-abcd-1234-ef00-1234567890ab" ; // Device registration token
351 const char * samplingFeature =
352 "12345678-abcd-1234-ef00-1234567890ab" ; // Sampling feature UUID
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>
357 EnviroDIYPublisher EnviroDIYPost ( loggerToGo , registrationToken ,
359 /** End [publishers] */
362 // ==========================================================================
364 // ==========================================================================
365 /** Start [working_functions] */
366 // Flashes the LED's on the primary board
367 void 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 );
372 digitalWrite ( greenLED , LOW );
373 digitalWrite ( redLED , HIGH );
376 digitalWrite ( redLED , LOW );
379 // Reads the battery voltage
380 // NOTE: This will actually return the battery level from the previous update!
381 float getBatteryVoltage () {
382 if ( mcuBoard . sensorValues [ 0 ] == -9999 ) mcuBoard . update ();
383 return mcuBoard . sensorValues [ 0 ];
385 /** End [working_functions] */
388 // ==========================================================================
389 // Arduino Setup Function
390 // ==========================================================================
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 )) {
402 // Start the primary serial connection
403 Serial . begin ( serialBaud );
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 );
412 Serial . print ( F ( "Using ModularSensors Library version " ));
413 Serial . println ( MODULAR_SENSORS_VERSION );
415 // Start the serial connection with the modem
416 modemSerial . begin ( modemBaud );
418 // Start the stream for the modbus sensors; all currently supported modbus
419 // sensors use 9600 baud
420 modbusSerial . begin ( 9600 );
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)
426 pinPeripheral ( 10 , PIO_SERCOM ); // Serial2 Tx/Dout = SERCOM1 Pad #2
427 pinPeripheral ( 11 , PIO_SERCOM ); // Serial2 Rx/Din = SERCOM1 Pad #0
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
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 );
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 ,
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
455 loggerAllVars . setSamplingFeatureUUID ( samplingFeature );
456 loggerToGo . setSamplingFeatureUUID ( samplingFeature );
458 // Note: Please change these battery voltages to match your battery
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 ();
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 ();
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
488 // Call the processor sleep
489 Serial . println ( F ( "Putting processor to sleep" ));
490 loggerAllVars . systemSleep ();
495 // ==========================================================================
496 // Arduino Loop Function
497 // ==========================================================================
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.
507 // Reset the watchdog
508 extendedWatchDog :: resetWatchDog ();
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 ();
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 ();
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 );
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 ();
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.
544 #if defined(AltSoftSerial_h)
545 // Explicitly set the pin modes for the AltSoftSerial pins to make sure
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 );
553 #if defined(ARDUINO_SAMD_FEATHER_M0)
554 digitalWrite ( 10 , LOW );
555 digitalWrite ( 11 , LOW );
558 // Create a csv data record and save it to the log file
559 loggerAllVars . logToSD ();
560 extendedWatchDog :: resetWatchDog ();
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 ();
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 );
583 // Disconnect from the network
584 extendedWatchDog :: resetWatchDog ();
585 modem . disconnectInternet ();
588 // Turn the modem off
589 extendedWatchDog :: resetWatchDog ();
590 modem . modemSleepPowerDown ();
593 // Cut power from the SD card - without additional housekeeping wait
594 loggerAllVars . turnOffSDcard ( false );
595 extendedWatchDog :: resetWatchDog ();
597 loggerAllVars . alertOff ();
598 // Print a line to show reading ended
599 Serial . println ( F ( "------------------------------------------ \n " ));
602 Logger :: isLoggingNow = false ;
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 ();
613 // Call the processor sleep
614 // Only need to do this for one of the loggers
615 loggerAllVars . systemSleep ();