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 // 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.
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
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>
60 AltSoftSerial altSoftSerial ;
61 #endif // End software serial for avr boards
64 #if defined(ARDUINO_ARCH_SAMD)
65 #include <wiring_private.h> // Needed for SAMD pinPeripheral() function
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'
75 Uart Serial2 ( & sercom1 , 11 , 10 , SERCOM_RX_PAD_0 , UART_TX_PAD_2 );
76 // Hand over the interrupts to the sercom port
77 void SERCOM1_Handler () {
82 #endif // End hardware serial on SAMD21 boards
83 /** End [serial_ports] */
86 // ==========================================================================
87 // Data Logging Options
88 // ==========================================================================
89 /** Start [logging_options] */
90 // The name of this program file
91 const char * sketchName = "data_saving.ino" ;
92 // Logger ID, also becomes the prefix for the name of the data file on SD card
93 const char * LoggerID = "XXXXX" ;
94 // How frequently (in minutes) to log data
95 const uint8_t loggingInterval = 15 ;
96 // Your logger's timezone.
97 const int8_t timeZone = -5 ; // Eastern Standard Time
98 // NOTE: Daylight savings time will not be applied! Please use standard time!
100 // Set the input and output pins for the logger
101 // NOTE: Use -1 for pins that do not apply
102 const int32_t serialBaud = 115200 ; // Baud rate for debugging
103 const int8_t greenLED = 8 ; // Pin for the green LED
104 const int8_t redLED = 9 ; // Pin for the red LED
105 const int8_t buttonPin = 21 ; // Pin for debugging mode (ie, button pin)
106 const 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
110 const int8_t sdCardPwrPin = -1 ; // MCU SD card power pin
111 const int8_t sdCardSSPin = 12 ; // SD card chip select/slave select pin
112 const int8_t sensorPowerPin = 22 ; // MCU pin controlling main sensor power
113 /** End [logging_options] */
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
124 HardwareSerial & modemSerial = Serial1 ; // Use hardware serial if possible
125 const int32_t modemBaud = 9600 ; // SIM800 does auto-bauding by default
127 // Modem Pins - Describe the physical pin connection of your modem to your board
128 // NOTE: Use -1 for pins that do not apply
129 const int8_t modemVccPin = 23 ; // MCU pin controlling modem power
130 const int8_t modemStatusPin = 19 ; // MCU pin used to read modem status
131 const int8_t modemLEDPin = redLED ; // MCU pin connected an LED to show modem
132 // status (-1 if unconnected)
134 // Network connection information
135 const char * apn = "xxxxx" ; // The APN for the gprs connection
137 Sodaq2GBeeR6 modem2GB ( & modemSerial , modemVccPin , modemStatusPin , apn );
138 // Create an extra reference to the modem by a generic name
139 Sodaq2GBeeR6 modem = modem2GB ;
141 // Create RSSI and signal strength variable pointers for the modem
142 Variable * modemRSSI = new Modem_RSSI ( & modem ,
143 "12345678-abcd-1234-ef00-1234567890ab" );
144 Variable * modemSignalPct =
145 new Modem_SignalPercent ( & modem , "12345678-abcd-1234-ef00-1234567890ab" );
146 /** End [sodaq_2g_bee_r6] */
149 // ==========================================================================
150 // Using the Processor as a Sensor
151 // ==========================================================================
152 /** Start [processor_sensor] */
153 #include <sensors/ProcessorStats.h>
155 // Create the main processor chip "sensor" - for general metadata
156 const char * mcuBoardVersion = "v1.1" ;
157 ProcessorStats mcuBoard ( mcuBoardVersion );
159 // Create sample number, battery voltage, and free RAM variable pointers for the
161 Variable * mcuBoardBatt = new ProcessorStats_Battery (
162 & mcuBoard , "12345678-abcd-1234-ef00-1234567890ab" );
163 Variable * mcuBoardAvailableRAM = new ProcessorStats_FreeRam (
164 & mcuBoard , "12345678-abcd-1234-ef00-1234567890ab" );
165 Variable * mcuBoardSampNo = new ProcessorStats_SampleNumber (
166 & mcuBoard , "12345678-abcd-1234-ef00-1234567890ab" );
167 /** End [processor_sensor] */
170 // ==========================================================================
171 // Maxim DS3231 RTC (Real Time Clock)
172 // ==========================================================================
174 #include <sensors/MaximDS3231.h>
176 // Create a DS3231 sensor object
177 MaximDS3231 ds3231 ( 1 );
179 // Create a temperature variable pointer for the DS3231
180 Variable * ds3231Temp =
181 new MaximDS3231_Temp ( & ds3231 , "12345678-abcd-1234-ef00-1234567890ab" );
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) || \
192 HardwareSerial & modbusSerial = Serial2 ; // Use hardware serial if possible
194 AltSoftSerial & modbusSerial = altSoftSerial ; // For software serial
197 // Define some pins that will be shared by all modbus sensors
198 const int8_t rs485AdapterPower =
199 sensorPowerPin ; // RS485 adapter power pin (-1 if unconnected)
200 const int8_t modbusSensorPower = A3 ; // Sensor power pin
201 const int8_t rs485EnablePin = -1 ; // Adapter RE/DE pin (-1 if not applicable)
202 /** End [modbus_shared] */
205 // ==========================================================================
206 // Yosemitech Y504 Dissolved Oxygen Sensor
207 // ==========================================================================
209 #include <sensors/YosemitechY504.h>
211 byte y504ModbusAddress = 0x04 ; // The modbus address of the Y504
212 const uint8_t y504NumberReadings = 5 ;
213 // The manufacturer recommends averaging 10 readings, but we take 5 to minimize
216 // Create a Yosemitech Y504 dissolved oxygen sensor object
217 YosemitechY504 y504 ( y504ModbusAddress , modbusSerial , rs485AdapterPower ,
218 modbusSensorPower , rs485EnablePin , y504NumberReadings );
220 // Create the dissolved oxygen percent, dissolved oxygen concentration, and
221 // temperature variable pointers for the Y504
223 new YosemitechY504_DOpct ( & y504 , "12345678-abcd-1234-ef00-1234567890ab" );
225 new YosemitechY504_DOmgL ( & y504 , "12345678-abcd-1234-ef00-1234567890ab" );
227 new YosemitechY504_Temp ( & y504 , "12345678-abcd-1234-ef00-1234567890ab" );
231 // ==========================================================================
232 // Yosemitech Y511 Turbidity Sensor with Wiper
233 // ==========================================================================
235 #include <sensors/YosemitechY511.h>
237 byte y511ModbusAddress = 0x1A ; // The modbus address of the Y511
238 const uint8_t y511NumberReadings = 5 ;
239 // The manufacturer recommends averaging 10 readings, but we take 5 to minimize
242 // Create a Y511-A Turbidity sensor object
243 YosemitechY511 y511 ( y511ModbusAddress , modbusSerial , rs485AdapterPower ,
244 modbusSensorPower , rs485EnablePin , y511NumberReadings );
246 // Create turbidity and temperature variable pointers for the Y511
248 new YosemitechY511_Turbidity ( & y511 , "12345678-abcd-1234-ef00-1234567890ab" );
250 new YosemitechY511_Temp ( & y511 , "12345678-abcd-1234-ef00-1234567890ab" );
254 // ==========================================================================
255 // Yosemitech Y514 Chlorophyll Sensor
256 // ==========================================================================
258 #include <sensors/YosemitechY514.h>
260 byte y514ModbusAddress = 0x14 ; // The modbus address of the Y514
261 const uint8_t y514NumberReadings = 5 ;
262 // The manufacturer recommends averaging 10 readings, but we take 5 to
263 // minimize power consumption
265 // Create a Y514 chlorophyll sensor object
266 YosemitechY514 y514 ( y514ModbusAddress , modbusSerial , rs485AdapterPower ,
267 modbusSensorPower , rs485EnablePin , y514NumberReadings );
269 // Create chlorophyll concentration and temperature variable pointers for the
271 Variable * y514Chloro = new YosemitechY514_Chlorophyll (
272 & y514 , "12345678-abcd-1234-ef00-1234567890ab" );
274 new YosemitechY514_Temp ( & y514 , "12345678-abcd-1234-ef00-1234567890ab" );
278 // ==========================================================================
279 // Yosemitech Y520 Conductivity Sensor
280 // ==========================================================================
282 #include <sensors/YosemitechY520.h>
284 byte y520ModbusAddress = 0x20 ; // The modbus address of the Y520
285 const uint8_t y520NumberReadings = 5 ;
286 // The manufacturer recommends averaging 10 readings, but we take 5 to minimize
289 // Create a Y520 conductivity sensor object
290 YosemitechY520 y520 ( y520ModbusAddress , modbusSerial , rs485AdapterPower ,
291 modbusSensorPower , rs485EnablePin , y520NumberReadings );
293 // Create specific conductance and temperature variable pointers for the Y520
295 new YosemitechY520_Cond ( & y520 , "12345678-abcd-1234-ef00-1234567890ab" );
297 new YosemitechY520_Temp ( & y520 , "12345678-abcd-1234-ef00-1234567890ab" );
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
307 Variable * 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
314 int variableCount_complete = sizeof ( variableList_complete ) /
315 sizeof ( variableList_complete [ 0 ]);
316 // Create the VariableArray object
317 VariableArray arrayComplete ( variableCount_complete , variableList_complete );
320 // Put only the particularly interesting variables into a second array
321 // NOTE: We can the same variables into multiple arrays
322 Variable * variableList_toGo [] = { y504DOmgL , y504Temp , y511Turb ,
323 y514Chloro , y520Cond , modemRSSI };
324 // Count up the number of pointers in the array
325 int variableCount_toGo = sizeof ( variableList_toGo ) /
326 sizeof ( variableList_toGo [ 0 ]);
327 // Create the VariableArray object
328 VariableArray arrayToGo ( variableCount_toGo , variableList_toGo );
329 /** End [variable_arrays] */
332 // ==========================================================================
333 // The Logger Object[s]
334 // ==========================================================================
335 /** Start [loggers] */
336 // Create one new logger instance for the complete array
337 Logger loggerAllVars ( LoggerID , loggingInterval , & arrayComplete );
339 // Create "another" logger for the variables to go out over the internet
340 Logger loggerToGo ( LoggerID , loggingInterval , & arrayToGo );
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
351 const char * registrationToken =
352 "12345678-abcd-1234-ef00-1234567890ab" ; // Device registration token
353 const char * samplingFeature =
354 "12345678-abcd-1234-ef00-1234567890ab" ; // Sampling feature UUID
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>
359 EnviroDIYPublisher EnviroDIYPOST ( loggerToGo , & modem . gsmClient ,
360 registrationToken , samplingFeature );
361 /** End [publishers] */
364 // ==========================================================================
366 // ==========================================================================
367 /** Start [working_functions] */
368 // Flashes the LED's on the primary board
369 void 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 );
374 digitalWrite ( greenLED , LOW );
375 digitalWrite ( redLED , HIGH );
378 digitalWrite ( redLED , LOW );
381 // Reads the battery voltage
382 // NOTE: This will actually return the battery level from the previous update!
383 float getBatteryVoltage () {
384 if ( mcuBoard . sensorValues [ 0 ] == -9999 ) mcuBoard . update ();
385 return mcuBoard . sensorValues [ 0 ];
387 /** End [working_functions] */
390 // ==========================================================================
391 // Arduino Setup Function
392 // ==========================================================================
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 )) {
404 // Start the primary serial connection
405 Serial . begin ( serialBaud );
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 );
414 Serial . print ( F ( "Using ModularSensors Library version " ));
415 Serial . println ( MODULAR_SENSORS_VERSION );
417 // Start the serial connection with the modem
418 modemSerial . begin ( modemBaud );
420 // Start the stream for the modbus sensors; all currently supported modbus
421 // sensors use 9600 baud
422 modbusSerial . begin ( 9600 );
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
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
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 );
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 ,
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
458 loggerAllVars . setSamplingFeatureUUID ( samplingFeature );
459 loggerToGo . setSamplingFeatureUUID ( samplingFeature );
461 // Note: Please change these battery voltages to match your battery
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 ();
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 ();
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
491 // Call the processor sleep
492 Serial . println ( F ( "Putting processor to sleep" ));
493 loggerAllVars . systemSleep ();
498 // ==========================================================================
499 // Arduino Loop Function
500 // ==========================================================================
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.
510 // Reset the watchdog
511 loggerAllVars . watchDogTimer . resetWatchDog ();
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 ();
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 ();
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 );
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 ();
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.
547 #if defined(AltSoftSerial_h)
548 // Explicitly set the pin modes for the AltSoftSerial pins to make sure
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 );
556 #if defined(ARDUINO_SAMD_ZERO)
557 digitalWrite ( 10 , LOW );
558 digitalWrite ( 11 , LOW );
561 // Create a csv data record and save it to the log file
562 loggerAllVars . logToSD ();
563 loggerAllVars . watchDogTimer . resetWatchDog ();
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 ();
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 ());
585 // Disconnect from the network
586 loggerAllVars . watchDogTimer . resetWatchDog ();
587 modem . disconnectInternet ();
590 // Turn the modem off
591 loggerAllVars . watchDogTimer . resetWatchDog ();
592 modem . modemSleepPowerDown ();
595 // Cut power from the SD card - without additional housekeeping wait
596 loggerAllVars . turnOffSDcard ( false );
597 loggerAllVars . watchDogTimer . resetWatchDog ();
599 loggerAllVars . alertOff ();
600 // Print a line to show reading ended
601 Serial . println ( F ( "------------------------------------------ \n " ));
604 Logger :: isLoggingNow = false ;
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 ();
615 // Call the processor sleep
616 // Only need to do this for one of the loggers
617 loggerAllVars . systemSleep ();