Welcome to EnviroDIY, a community for do-it-yourself environmental science and monitoring. EnviroDIY is part of WikiWatershed, a web toolkit designed to help citizens, conservation practitioners, municipal decision-makers, researchers, educators, and students advance knowledge and stewardship of fresh water. New to EnviroDIY? Start here

Reply To: XBee Networks of Mayfly Loggers – 900Mhz

Home Forums Mayfly Data Logger XBee Networks of Mayfly Loggers – 900Mhz Reply To: XBee Networks of Mayfly Loggers – 900Mhz


Hi there! I’m hoping to do something similar to what David is doing. I’m using a couple Xbee Pro 900HP modules and couple Mayfly’s. I’m setting up the network using PlatformIO and the Monitoring Station Manual, and I came across a roadblock when I updated the code with some of the Xbee lines. The code runs, printing data to the Mayfly’s SD card until I add the the Xbee code to the loop(). Once I added the Xbee code, the SD card printing prompts don’t pop up in the Serial Monitor (only the “Wake up the Xbee” and “Putting the Xbee to sleep” prompts).

I feel like it’s an ordering issue in the loop() from when I updated the code, but I’m not sure. Any help is greatly appreciated! Thanks!

Also, while I’m here, I also have been struggling on how to take the sensor measurements that are printed to the SD card and communicate them to the Xbee (with the Serial1.println() line in the loop() command). I’m pretty new to Arduino programming, so I feel like I’m probably overcomplicating it.

/** =========================================================================
* @file DRWI_NoCellular.ino
* @brief Example for DRWI CitSci without cellular service.
* @author Sara Geleskie Damiano <sdamiano@stroudcenter.org>
* @copyright (c) 2017-2020 Stroud Water Research Center (SWRC)
* and the EnviroDIY Development Team
* This example is published under the BSD-3 license.
* Build Environment: Visual Studios Code with PlatformIO
* Hardware Platform: EnviroDIY Mayfly Arduino Datalogger
* ======================================================================= */

// ==========================================================================
// Include the libraries required for any data logger
// ==========================================================================
/** Start [includes] */
// The Arduino library is needed for every Arduino program.
#include <Arduino.h>

// EnableInterrupt is used by ModularSensors for external and pin change
// interrupts and must be explicitly included in the main program.
#include <EnableInterrupt.h>

// To get all of the base classes for ModularSensors, include LoggerBase.
// NOTE: Individual sensor definitions must be included separately.
#include <LoggerBase.h>
/** End [includes] */

//Defines the XBEE Sleep pin
#define XBEE_SleepPin 23

// ==========================================================================
// Data Logging Options
// ==========================================================================
/** Start [logging_options] */
// The name of this program file
const char* sketchName = “sensor_test.ino”;
// Logger ID, also becomes the prefix for the name of the data file on SD card
const char* LoggerID = “XXXXX”;
// How frequently (in minutes) to log data
const uint8_t loggingInterval = 1;
// Your logger’s timezone.
const int8_t timeZone = -5; // Eastern Standard Time
// NOTE: Daylight savings time will not be applied! Please use standard time!

// Set the input and output pins for the logger
// NOTE: Use -1 for pins that do not apply
const int32_t serialBaud = 115200; // Baud rate for debugging
const int8_t greenLED = 8; // Pin for the green LED
const int8_t redLED = 9; // Pin for the red LED
const int8_t buttonPin = 21; // Pin for debugging mode (ie, button pin)
const int8_t wakePin = A7; // MCU interrupt/alarm pin to wake from sleep
// Set the wake pin to -1 if you do not want the main processor to sleep.
// In a SAMD system where you are using the built-in rtc, set wakePin to 1
const int8_t sdCardPwrPin = -1; // MCU SD card power pin
const int8_t sdCardSSPin = 12; // SD card chip select/slave select pin
const int8_t sensorPowerPin = 22; // MCU pin controlling main sensor power
/** End [logging_options] */

// ==========================================================================
// Using the Processor as a Sensor
// ==========================================================================
/** Start [processor_sensor] */
#include <sensors/ProcessorStats.h>

// Create the main processor chip “sensor” – for general metadata
const char* mcuBoardVersion = “v0.5b”;
ProcessorStats mcuBoard(mcuBoardVersion);
/** End [processor_sensor] */

// ==========================================================================
// Maxim DS3231 RTC (Real Time Clock)
// ==========================================================================
/** Start [ds3231] */
#include <sensors/MaximDS3231.h>

// Create a DS3231 sensor object
MaximDS3231 ds3231(1);
/** End [ds3231] */

// ==========================================================================
// Campbell OBS 3 / OBS 3+ Analog Turbidity Sensor
// ==========================================================================
/** Start [obs3] */
#include <sensors/CampbellOBS3.h>

const int8_t OBS3Power = sensorPowerPin; // Power pin (-1 if unconnected)
const uint8_t OBS3NumberReadings = 10;
const uint8_t ADSi2c_addr = 0x48; // The I2C address of the ADS1115 ADC
// Campbell OBS 3+ *Low* Range Calibration in Volts
const int8_t OBSLowADSChannel = 0; // ADS channel for *low* range output
const float OBSLow_A = 0.000E+00; // “A” value (X^2) [*low* range]
const float OBSLow_B = 1.000E+00; // “B” value (X) [*low* range]
const float OBSLow_C = 0.000E+00; // “C” value [*low* range]

// Create a Campbell OBS3+ *low* range sensor object
CampbellOBS3 osb3low(OBS3Power, OBSLowADSChannel, OBSLow_A, OBSLow_B, OBSLow_C,
ADSi2c_addr, OBS3NumberReadings);

// Campbell OBS 3+ *High* Range Calibration in Volts
const int8_t OBSHighADSChannel = 1; // ADS channel for *high* range output
const float OBSHigh_A = 0.000E+00; // “A” value (X^2) [*high* range]
const float OBSHigh_B = 1.000E+00; // “B” value (X) [*high* range]
const float OBSHigh_C = 0.000E+00; // “C” value [*high* range]

// Create a Campbell OBS3+ *high* range sensor object
CampbellOBS3 osb3high(OBS3Power, OBSHighADSChannel, OBSHigh_A, OBSHigh_B,
OBSHigh_C, ADSi2c_addr, OBS3NumberReadings);
/** End [obs3] */

// ==========================================================================
// Meter Hydros 21 Conductivity, Temperature, and Depth Sensor
// ==========================================================================
/** Start [decagon_ctd] */
#include <sensors/DecagonCTD.h>

const char* CTDSDI12address = “1”; // The SDI-12 Address of the CTD
const uint8_t CTDNumberReadings = 6; // The number of readings to average
const int8_t SDI12Power = sensorPowerPin; // Power pin (-1 if unconnected)
const int8_t SDI12Data = 7; // The SDI12 data pin

// Create a Decagon CTD sensor object
DecagonCTD ctd(*CTDSDI12address, SDI12Power, SDI12Data, CTDNumberReadings);
/** End [decagon_ctd] */

// ==========================================================================
// Creating the Variable Array[s] and Filling with Variable Objects
// ==========================================================================
/** Start [variable_arrays] */
Variable* variableList[] = {
new DecagonCTD_Cond(&ctd),
new DecagonCTD_Temp(&ctd),
new DecagonCTD_Depth(&ctd),
new CampbellOBS3_Turbidity(&osb3low, “”, “TurbLow”),
new CampbellOBS3_Turbidity(&osb3high, “”, “TurbHigh”),
new ProcessorStats_Battery(&mcuBoard),
new MaximDS3231_Temp(&ds3231),

// All UUID’s, device registration, and sampling feature information can be
// pasted directly from Monitor My Watershed. To get the list, click the “View
// token UUID list” button on the upper right of the site page.
// Even if not publishing live data, this is needed so the logger file will be
// “drag-and-drop” ready for manual upload to the portal.

// Check the order of your variables in the variable list!!!
// Be VERY certain that they match the order of your UUID’s!
// Rearrange the variables in the variable list if necessary to match!
const char* UUIDs[] = {
“12345678-abcd-1234-ef00-1234567890ab”, // Electrical conductivity
// (Decagon_CTD-10_Cond)
“12345678-abcd-1234-ef00-1234567890ab”, // Temperature
// (Decagon_CTD-10_Temp)
“12345678-abcd-1234-ef00-1234567890ab”, // Water depth
// (Decagon_CTD-10_Depth)
“12345678-abcd-1234-ef00-1234567890ab”, // Turbidity (Campbell_OBS3_Turb)
“12345678-abcd-1234-ef00-1234567890ab”, // Turbidity (Campbell_OBS3_Turb)
“12345678-abcd-1234-ef00-1234567890ab”, // Battery voltage
// (EnviroDIY_Mayfly_Batt)
“12345678-abcd-1234-ef00-1234567890ab” // Temperature
// (EnviroDIY_Mayfly_Temp)
const char* registrationToken =
“12345678-abcd-1234-ef00-1234567890ab”; // Device registration token
const char* samplingFeature =
“12345678-abcd-1234-ef00-1234567890ab”; // Sampling feature UUID

// Count up the number of pointers in the array
int variableCount = sizeof(variableList) / sizeof(variableList[0]);

// Create the VariableArray object
VariableArray varArray(variableCount, variableList, UUIDs);
/** End [variable_arrays] */

// ==========================================================================
// The Logger Object[s]
// ==========================================================================
/** Start [loggers] */
// Create a new logger instance
Logger dataLogger(LoggerID, loggingInterval, &varArray);
/** End [loggers] */

// ==========================================================================
// Working Functions
// ==========================================================================
/** Start [working_functions] */
// Flashes the LED’s on the primary board
void greenredflash(uint8_t numFlash = 4, uint8_t rate = 75) {
for (uint8_t i = 0; i < numFlash; i++) {
digitalWrite(greenLED, HIGH);
digitalWrite(redLED, LOW);
digitalWrite(greenLED, LOW);
digitalWrite(redLED, HIGH);
digitalWrite(redLED, LOW);

// Reads the battery voltage
// NOTE: This will actually return the battery level from the previous update!
float getBatteryVoltage() {
if (mcuBoard.sensorValues[0] == -9999) mcuBoard.update();
return mcuBoard.sensorValues[0];
/** End [working_functions] */

// ==========================================================================
// Arduino Setup Function
// ==========================================================================
/** Start [setup] */
void setup() {
// Start the primary serial connection

//Sets up the baud rate for the XBEE

// Print a start-up note to the first serial port
Serial.print(F(“Now running “));
Serial.print(F(” on Logger “));

Serial.print(F(“Using ModularSensors Library version “));

// Set up pins for the LED’s
pinMode(greenLED, OUTPUT);
digitalWrite(greenLED, LOW);
pinMode(redLED, OUTPUT);
digitalWrite(redLED, LOW);

//Sets up pins for the XBEE
pinMode(XBEE_SleepPin,OUTPUT); //Setting XBEE sleep pin as an output
digitalWrite(XBEE_SleepPin, LOW); //Start with XBEE awake
Serial.println(“Xbee is now set up”);

// Blink the LEDs to show the board is on and starting up

// Set the timezones for the logger/data and the RTC
// Logging in the given time zone
// It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0)

// Attach information pins to the logger
dataLogger.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin,

// Begin the logger

// Note: Please change these battery voltages to match your battery
// Set up the sensors, except at lowest battery level
if (getBatteryVoltage() > 3.4) {
Serial.println(F(“Setting up sensors…”));

// Create the log file, adding the default header to it
// Do this last so we have the best chance of getting the time correct and
// all sensor names correct
// Writing to the SD card can be power intensive, so if we’re skipping
// the sensor setup we’ll skip this too.
if (getBatteryVoltage() > 3.4) {
Serial.println(F(“Setting up file on SD card”));
true); // true = wait for card to settle after power up
dataLogger.createLogFile(true); // true = write a new header
true); // true = wait for internal housekeeping after write

// Call the processor sleep
Serial.println(F(“Putting processor to sleep\n”));
/** End [setup] */

// ==========================================================================
// Arduino Loop Function
// ==========================================================================
/** Start [loop] */
// Use this short loop for simple data logging and sending
void loop() {
// Note: Please change these battery voltages to match your battery
// At very low battery, just go back to sleep
if (getBatteryVoltage() < 3.4) {
// If the battery is OK, log data
else {
wakeXbee(); //Wakes up XBEE
delay(3000); //Gives XBEE time to be seen by the network
Serial1.println(); //Text being sent by XBEE module
delay(3000); //Gives XBEE a bit before sending it back to sleep
sleepXbee(); //Puts Xbee to sleep

delay(10000); //wait 10 seconds, then repeat loop


void sleepXbee() {
Serial.println(“Putting the Xbee to sleep”);
pinMode(XBEE_SleepPin,INPUT_PULLUP); // Set the “wake-up pin” to input and enable the internal pullup
digitalWrite(XBEE_SleepPin,HIGH); //put the Xbee to sleep
digitalWrite(9, LOW); //turn off the RED LED when the Xbee is asleep

void wakeXbee() {
Serial.println(“Wake up the Xbee”);
pinMode(XBEE_SleepPin,OUTPUT); // Set the “wake-up pin” to output
digitalWrite(XBEE_SleepPin,LOW); // wake-up XBee
digitalWrite(9, HIGH); // turn on the RED LED when the Xbee is awake
/** End [loop] */