Skip to content

Commit

Permalink
Update RainCounterIC2 to int32. Works!
Browse files Browse the repository at this point in the history
Updating to a 32-bit count enables use with a reed-switch anemometer (EnviroDIY/TippingBucketRainCounter#4) and also fixes a bug described in EnviroDIY/TippingBucketRainCounter#5. Tested and works, including backward compatibility.
  • Loading branch information
aufdenkampe committed Apr 6, 2021
1 parent 9a21bdb commit 87cd0c9
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 5 deletions.
270 changes: 270 additions & 0 deletions sensor_tests/RainCounter_simple_logging/RainCounter_simple_logging.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/** =========================================================================
* @file RainCounter_simple_logging.ino
* @brief Test improvements to RainCounterI2C, based on simple logging example.
*
* @author Sara Geleskie Damiano <[email protected]>
* @author Anthony Aufdenkampe <[email protected]>
* @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: Atom with PlatformIO Core 5.1.1·Home 3.3.4
* Modular Sensors v0.28.3 as of April 5, 2021
* Hardware Platform: EnviroDIY Mayfly Arduino Datalogger
*
* DISCLAIMER:
* THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN.
* ======================================================================= */

// ==========================================================================
// 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] */


// ==========================================================================
// Data Logging Options
// ==========================================================================
/** Start [logging_options] */
// The name of this program file
const char* sketchName = "RainCounter_simple_logging.ino";
// Logger ID, also becomes the prefix for the name of the data file on SD card
const char* LoggerID = "RainCounter_Tester";
// How frequently (in minutes) to log data
const uint8_t loggingInterval = 1;
// Your logger's timezone.
const int8_t timeZone = -6; // 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> // Includes wrapper functions for Maxim DS3231 RTC

// Create a DS3231 sensor object, using this constructor function:
MaximDS3231 ds3231(1);
/** End [ds3231] */


// ==========================================================================
// Settings for Additional Sensors
// ==========================================================================

// ==========================================================================
// External I2C Rain Tipping Bucket Counter, connected to an anemometer
// ==========================================================================
/** Start [i2c_rain] */
#include <sensors/RainCounterI2C.h>

const uint8_t RainCounterI2CAddress = 0x08;
// I2C Address for EnviroDIY external tip counter; 0x08 by default
const float depthPerTipEvent = 0.2; // rain depth in mm per tip event

// Create a Rain Counter sensor object
RainCounterI2C tbi2c(RainCounterI2CAddress, depthPerTipEvent);

// Create number of tips and rain depth variable pointers for the tipping bucket
Variable* tbi2cTips =
new RainCounterI2C_Tips(&tbi2c, "12345678-abcd-1234-ef00-1234567890ab");
// Variable* tbi2cDepth =
// new RainCounterI2C_Depth(&tbi2c, "12345678-abcd-1234-ef00-1234567890ab");
/** End [i2c_rain] */


// ==========================================================================
// Calculated Variables
// ==========================================================================

// Create the function to give your calculated result.
// The function should take no input (void) and return a float.
// You can use any named variable pointers to access values by way of
// variable->getValue()

float calculateWindSpeed(void) {
float windSpeed = -9999; // Always safest to start with a bad value
float period = -9999; // seconds between gettting event counts
float frequency = -9999; // average event frequency in Hz
float eventCount = tbi2cTips->getValue();
if (eventCount != -9999) // make sure both inputs are good
{
period = loggingInterval * 60.0; // in seconds
frequency = eventCount/period; // average event frequency in Hz
windSpeed = frequency * 2.5 * 1.60934; // in km/h,
// 2.5 mph/Hz & 1.60934 kmph/mph and 2.5 mph/Hz conversion factor from
// https://www.store.inspeed.com/Inspeed-Version-II-Reed-Switch-Anemometer-Sensor-Only-WS2R.htm
}
return windSpeed;
}

// Properties of the calculated variable
// The number of digits after the decimal place
const uint8_t calculatedVarResolution = 3;
// This must be a value from http://vocabulary.odm2.org/variablename/
const char *calculatedVarName = "windSpeed";
// This must be a value from http://vocabulary.odm2.org/units/
const char *calculatedVarUnit = "KilometerPerHour";
// A short code for the variable
const char *calculatedVarCode = "WindSpeed";
// The (optional) universallly unique identifier
const char *calculatedVarUUID = "12345678-abcd-1234-ef00-1234567890ab";

// Create a calculated variable pointer and return a variable pointer to it
Variable *calculatedWindSpeed = new Variable(
calculateWindSpeed, calculatedVarResolution, calculatedVarName,
calculatedVarUnit, calculatedVarCode, calculatedVarUUID);


// ==========================================================================
// Creating the Variable Array[s] and Filling with Variable Objects
// ==========================================================================
/** Start [variable_arrays] */
Variable* variableList[] = {
new ProcessorStats_SampleNumber(&mcuBoard),
new ProcessorStats_FreeRam(&mcuBoard),
new ProcessorStats_Battery(&mcuBoard),
new MaximDS3231_Temp(&ds3231),
// Additional sensor variables can be added here, by copying the syntax
// for creating the variable pointer (FORM1) from the
// `menu_a_la_carte.ino` example
// The example code snippets in the wiki are primarily FORM2.
tbi2cTips,
calculatedWindSpeed,
};
// Count up the number of pointers in the array
int variableCount = sizeof(variableList) / sizeof(variableList[0]);

// Create the VariableArray object
VariableArray varArray;
/** End [variable_arrays] */


// ==========================================================================
// The Logger Object[s]
// ==========================================================================
/** Start [loggers] */
// Create a logger instance
Logger dataLogger;
/** 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);
delay(rate);
digitalWrite(greenLED, LOW);
digitalWrite(redLED, HIGH);
delay(rate);
}
digitalWrite(redLED, LOW);
}
/** End [working_functions] */


// ==========================================================================
// Arduino Setup Function
// ==========================================================================
/** Start [setup] */
void setup() {
// Start the primary serial connection
Serial.begin(serialBaud);

// Print a start-up note to the first serial port
Serial.print(F("Now running "));
Serial.print(sketchName);
Serial.print(F(" on Logger "));
Serial.println(LoggerID);
Serial.println();

Serial.print(F("Using ModularSensors Library version "));
Serial.println(MODULAR_SENSORS_VERSION);

// Set up pins for the LED's
pinMode(greenLED, OUTPUT);
digitalWrite(greenLED, LOW);
pinMode(redLED, OUTPUT);
digitalWrite(redLED, LOW);
// Blink the LEDs to show the board is on and starting up
greenredflash();

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

// Set information pins
dataLogger.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin,
greenLED);

// Begin the variable array[s], logger[s], and publisher[s]
varArray.begin(variableCount, variableList);
dataLogger.begin(LoggerID, loggingInterval, &varArray);

// Set up the sensors
Serial.println(F("Setting up sensors..."));
varArray.setupSensors();

// 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
dataLogger.createLogFile(true); // true = write a new header

// Call the processor sleep
dataLogger.systemSleep();
}
/** End [setup] */


// ==========================================================================
// Arduino Loop Function
// ==========================================================================
/** Start [loop] */
void loop() {
dataLogger.logData();
}
/** End [loop] */
5 changes: 5 additions & 0 deletions src/SensorBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,11 @@ void Sensor::verifyAndAddMeasurementResult(uint8_t resultNumber,
float float_val = resultValue; // cast the int16_t to a float
verifyAndAddMeasurementResult(resultNumber, float_val);
}
void Sensor::verifyAndAddMeasurementResult(uint8_t resultNumber,
int32_t resultValue) {
float float_val = resultValue; // cast the int32_t to a float
verifyAndAddMeasurementResult(resultNumber, float_val);
}


void Sensor::averageMeasurements(void) {
Expand Down
6 changes: 6 additions & 0 deletions src/SensorBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,12 @@ class Sensor {
* @brief Average the results of all measurements by dividing the sum of
* all measurements by the number of measurements taken.
*/
void verifyAndAddMeasurementResult(uint8_t resultNumber,
int32_t resultValue);
/**
* @brief Average the results of all measurements by dividing the sum of
* all measurements by the number of measurements taken.
*/
void averageMeasurements(void);

/**
Expand Down
39 changes: 34 additions & 5 deletions src/sensors/RainCounterI2C.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,47 @@ bool RainCounterI2C::setup(void) {
bool RainCounterI2C::addSingleMeasurementResult(void) {
// intialize values
float rain = -9999; // Number of mm of rain
int16_t tips = -9999; // Number of tip events
int32_t tips = -9999; // Number of tip events, increased for anemometer

// Get data from external tip counter
// if the 'requestFrom' returns 0, it means no bytes were received
if (_i2c->requestFrom(static_cast<uint8_t>(_i2cAddressHex),
static_cast<uint8_t>(2))) {
static_cast<uint8_t>(4))) {
MS_DBG(getSensorNameAndLocation(), F("is reporting:"));

uint8_t Byte1 = _i2c->read(); // Low byte of data
uint8_t Byte2 = _i2c->read(); // High byte of data
uint8_t SerialBuffer[4]; // Create a byte array of 4 bytes
uint8_t byte_in = 0; // Start iterator for reading Bytes
while (Wire.available()) { // slave may send less than requested
SerialBuffer[byte_in] = Wire.read();
MS_DBG(F(" SerialBuffer["), byte_in, F("] = "),
SerialBuffer[byte_in]);
byte_in++; // increment by 1
}

// Concatenate bytes into uint32_t by bit-shifting
// https://thewanderingengineer.com/2015/05/06/sending-16-bit-and-32-bit-numbers-with-arduino-i2c/#
if ( (SerialBuffer[0] > 0)
) {
// for Slave with libVersion = v0.1.0, which only sends 1-byte
// NOTE: this can not be falsely selected because it would require
// > 16,777,216 counts from a v0.2.0 slave, which is not possible in 24 hours
MS_DBG(F(" Counted with slave libVersion = v0.1.0"));
tips = SerialBuffer[0];
} else if ( (SerialBuffer[1] == 0) &&
(SerialBuffer[2] == 255) ) {
// for Slave with libVersion = v0.1.0, in which no counts are made
// NOTE: this will be falsely selected if exactly 65535 counts
// were made by a v0.2.0 slave
MS_DBG(F(" No counts with slave libVersion = v0.1.0"));
tips = SerialBuffer[0];
} else {
// for Slave with libVersion >= v0.2.0
tips = SerialBuffer[0];
tips = (tips << 8) | SerialBuffer[1];
tips = (tips << 8) | SerialBuffer[2];
tips = (tips << 8) | SerialBuffer[3];
}

tips = (Byte2 << 8) | (Byte1); // Concatenate tip values
rain = static_cast<float>(tips) *
_rainPerTip; // Multiply by tip coefficient (0.2 by default)

Expand Down

0 comments on commit 87cd0c9

Please sign in to comment.