diff --git a/.travis.yml b/.travis.yml index a12b30f7e..6e4b77f9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,6 @@ install: - git rm library.json - pip install -U platformio - pio upgrade - - pio update - pio lib -g install 2079 # DS3231 - pio lib -g install 311 # EnableInterrupt - pio lib -g install 322 # SdFat @@ -53,6 +52,6 @@ install: - pio lib -g install https://github.com/PaulStoffregen/AltSoftSerial.git # #73, but need the git until Paul S. has a new release - pio lib -g install https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git # - pio lib -g install https://github.com/todbot/SoftI2CMaster.git - - pio lib -g update + - pio update script: make travis-build diff --git a/README.md b/README.md index b45d1f263..66739ae3c 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,9 @@ To use a sensor and variable in your sketch, you must separately include xxx.h f Each sensor is implemented as a subclass of the "Sensor" class contained in "SensorBase.h". Each variable is separately implemented as a subclass of the "Variable" class contained in "VariableBase.h". The variables are tied to the sensor using an "[Observer](https://en.wikipedia.org/wiki/Observer_pattern)" software pattern. -The "VariableArray" class contained in "VariableArray.h" defines the logic for iterating through many variable objects. The various "Logger" classes are all sub-classes of variable arrays and add functionality for sleeping the processor, writing to an SD card, and communicating with the internet. +The "VariableArray" class contained in "VariableArray.h" defines the logic for iterating through many variable objects. The VariableArray class takes advantage of various time stamps within the Sensor class to optimize the timing of communcations with many sensors. + +The "Logger" class defines functions for sleeping the processor and writing to an SD card. The logger subclasses add functionality for communicating with the internet and sending data to particular data receivers. @@ -71,7 +73,7 @@ Within this library, a sensor, a variable, and a logger mean very specific thing **Sensor** - A sensor is some sort of device that is capable of taking one or more measurements using some sort of method. Most often we can think of these as probes or other instruments that can give back information about the world around them. Sensors can usually be given power or have that power cut. They may be awoken or activated and then returned to a sleeping/low power use state. The may be able to be asked to begin a single reading. They _**must**_ be capable of returning the value of their readings to a logger of some type. -**Variable** - A variable is a single measurement value taken by a sensor. It is characterized by a name (what it is a measurement of), a unit of measurement, and a resolution. The [names](http://vocabulary.odm2.org/variablename/) and [units](http://vocabulary.odm2.org/units/) of measurements for all variables come from the controlled vocabularies developed for the ODM2 data system. (http://vocabulary.odm2.org/) The resolution is determined by the method used to take the measurement by the sensor. A variable may also be assigned a universally unique identifier (UUID) and a unique variable code. Many sensors are capable of measuring multiple variables at a single time. For example, a Decagon CTD-10 is a _sensor_. It is able to measure 3 _variables_: specific conductance, temperature, and water depth. The variable named "specificConductance" has _units_ of microsiemens per centimeter (µS/cm) and a _resolution_ of 1 µS/cm. Each variable is explicitly tied to the "parent" sensor that "notifies" the variable when a new value has been measured. +**Variable** - A variable is a result value taken by a sensor _or_ calculated from the results of one or more sensors. It is characterized by a name (what it is a measurement of), a unit of measurement, and a resolution. The [names](http://vocabulary.odm2.org/variablename/) and [units](http://vocabulary.odm2.org/units/) of measurements for all variables come from the controlled vocabularies developed for the ODM2 data system. (http://vocabulary.odm2.org/) The resolution is determined by the method used to take the measurement by the sensor. A variable may also be assigned a universally unique identifier (UUID) and a unique variable code. Many sensors are capable of measuring multiple variables at a single time. For example, a Decagon CTD-10 is a _sensor_. It is able to measure 3 _variables_: specific conductance, temperature, and water depth. The variable named "specificConductance" has _units_ of microsiemens per centimeter (µS/cm) and a _resolution_ of 1 µS/cm. Each measured variable is explicitly tied to the "parent" sensor that "notifies" the variable when a new value has been measured. Each calculated variable has a parent function returning a float which is the value for that variable. **Logger** - A logger is a device that can control all functions of the sensors that are attached to it and save the values of all variables measured by those sensors to an attached SD card. In this library, all loggers are Arduino-style small processor circuit boards. @@ -99,12 +101,12 @@ This library is designed for wireless, solar-powered environmental data logging In order to support multiple functions and sensors, there are quite a lot of sub-libraries that this library is dependent on. _Even if you do not use the modules, you must have all of the dependencies installed for the library itself to properly compile._ Please check the [library.json](https://github.com/EnviroDIY/ModularSensors/blob/master/library.json) file for more details on the versions required of each library. If you are using [PlatformIO](https://platformio.org), you can list "EnviroDIY_ModularSensors" in the ```lib_deps``` section of your platformio.ini file and all of these libraries will be installed automatically. If using the "standard" Arduino IDE, you must install each of these libraries individually, or in a bundle from the [EnviroDIY Libraries](https://github.com/EnviroDIY/Libraries) meta-repository. - [EnableInterrupt](https://github.com/GreyGnome/EnableInterrupt) - Administrates and handles pin change interrupts, allowing the logger to sleep and save battery. This also controls the interrupts for the versions of SoftwareSerial and SDI-12 linked below that have been stripped of interrupt control. Because we use this library, _you must always add the line ```#include ``` to the top of your sketch._ -- AVR sleep library - This is for low power sleeping for AVR processors. (This library is built in to the Arduino IDE.) +- AVR sleep library - This is for low power sleeping for AVR processors. (This library is built in to the Arduino and PlatformIO IDEs.) - [EnviroDIY DS-3231](https://github.com/EnviroDIY/Sodaq_DS3231) - For real time clock control - [RTCZero library](https://github.com/arduino-libraries/RTCZero) - This real time clock control and low power sleeping on SAMD processors. (This library may be built in to the Arduino IDE.) NOTE: If using an AVR board, you must explicitly _ignore_ this library when compiling with PlatformIO or you will have compiler errors. - [SdFat library](https://github.com/greiman/SdFat) - This enables communication with the SD card. - [EnviroDIY version of the TinyGSM library](https://github.com/EnviroDIY/TinyGSM) - This provides internet (TCP/IP) connectivity. -- [Adafruit ADS1X15 library](https://github.com/soligen2010/Adafruit_ADS1X15/) - For high-resolution analog to digital conversion. Note that this is soligen2010's fork of the original Adafruit library; it corrects many problems in the Adafruit library such as a bug which gives the same output on all four inputs regardless of their values. Do NOT use the original Adafruit version! +- [Adafruit ADS1X15 library](https://github.com/soligen2010/Adafruit_ADS1X15/) - For high-resolution analog to digital conversion. Note that this is soligen2010's fork of the original Adafruit library; it corrects many problems in the Adafruit library such as a bug which gives the same output on all four inputs regardless of their values. Do _NOT_ use the original Adafruit version! - [EnviroDIY Arduino SDI-12 library](https://github.com/EnviroDIY/Arduino-SDI-12/tree/ExtInts) - For control of SDI-12 based sensors. This modified version is needed so there are no pin change interrupt conflicts with the SoftwareSerial library or the software pin change interrupt library used to wake the processor. - [SensorModbusMaster](https://github.com/EnviroDIY/SensorModbusMaster) - for easy communication with Modbus devices. - [OneWire](https://github.com/PaulStoffregen/OneWire) - This enables communication with Maxim/Dallas OneWire devices. @@ -125,7 +127,7 @@ Generally useful functions: - **Constructor** - Each sensor has a unique constructor, the exact format of which is dependent on the individual sensor. - **getSensorName()** - This gets the name of the sensor and returns it as a string. - **getSensorLocation()** - This returns the Arduino pin sending and receiving data or other sensor installation information as a string. This is the location where the sensor is connected to the data logger, NOT the position of the sensor in the environment. Generally this value is set in the constructor for the sensor. -- **setNumberMeasurementsToAverage(int nReadings)** - Sets the number of readings for the sensor to take. This value can also be set by the constructor. +- **setNumberMeasurementsToAverage(int nReadings)** - Sets the number of readings for the sensor to take. This value can also be set by the constructor. NOTE: This will beome the number of readings actually taken by a sensor prior to data averaging. Any "bad" (-9999) values returned by the sensor will not be included in the final averaging. This the actual number of "good" values that are averaged may be less than what is set by setNumberMeasurementsToAverage or in the sensor constructor. - **getNumberMeasurementsToAverage()** - Returns an unsigned 8-bit integer with the number of readings the sensor will be taking before averaging and giving a final result. - **getStatus()** - This returns the 8-bit code for the current status of the sensor: - Bit 0 - 0=Not powered, 1=Powered @@ -161,16 +163,20 @@ These functions are also available for each sensor, but should be used with caut - **waitForMeasurementCompletion()** - Delays until time is passed for measurement completion. ### Functions for Each Variable -- **Constructor** - Every variable requires a pointer to its parent sensor as part of the constructor. Every variable also has two optional string entries, for a universally unique identifier (UUID or GUID) and a custom variable code. _The UUID must always be listed first!_ In cases where you would like a custom variable code, but do not have a UUID, you **must** enter '""' as your UUID. -- **getVarName()** - This returns the variable's name ,using http://vocabulary.odm2.org/variablename/, as a String. +- **Constructor** - There are two forms of the constructor, one for measured variables (ie, ones whose values come directly from a senor) and another for calculated variables (ie, ones whose values are calculated from other vales). + - For _measured_ variables, the base variable constructor should not be used, but instead the constructor for the specific type of variable tied to a sensor should be used. (That is, use the constructor for the MaxBotixSonar_Range variable sub-object, not the raw variable constructor.) Each sensor-measured variable constructor requires a pointer to its parent sensor as the first argument of the constructor. There are also two optional string entries, for a universally unique identifier (UUID or GUID) and a custom variable code. _The UUID must always be listed first!_ In cases where you would like a custom variable code, but do not have a UUID, you **must** enter '""' as your UUID. + - For _calculated_ variables, you use the base variable object constructor. The constructor requires a function which returns a float as its first argument, followed by Strings from the variable's name and unit. Both of these strings should be values from the [ODM2 controlled vocabularies](http://vocabulary.odm2.org/). Next an integer variable resolution is required. Then two Strings for the variable UUID and variable code. _All arguments are required in the calculated variable constructor!_ +- **getVarName()** - This returns the variable's name, using http://vocabulary.odm2.org/variablename/, as a String. - **getVarUnit()** - This returns the variable's unit, using http://vocabulary.odm2.org/units/, as a String. - **getVarCode()** - This returns a String with a customized code for the variable, if one is given, and a default if not - **getVarUUID()** - This returns the universally unique identifier of a variables, if one is assigned, as a String - **setup()** - This "sets up" the variable - attaching it to its parent sensor. This must always be called for each sensor within the "setup" loop of your Arduino program _after_ calling the sensor setup. -- **getValue(bool updateValue)** - This returns the current value of the variable as a float. By default, it does not ask the parent sensor for a new value, but simply returns the last value a parent sensor notified it of, no matter the age of the value. If you would like to ask the sensor to measure a new value and for that new value to be returned, set the boolean flag as true. +- **getValue(bool updateValue)** - This returns the current value of the variable as a float. By default, it does not ask the parent sensor for a new value, but simply returns the last value a parent sensor notified it of, no matter the age of the value. If you would like to ask the sensor to measure a new value and for that new value to be returned, set the boolean flag as true. Calculated variables will never ask any sensors for updates. - **getValueString(bool updateValue)** - This is identical to getValue, except that it returns a string with the proper precision available from the sensor. -- **attachSensor(int varNum, Sensor \*parentSense)** - The compliment to a sensor's registerVariable() function. This attaches a variable object to the sensor that is giving the value to the variable. The variable is generally responsible for calling this function! -- **onSensorUpdate()** - This is the variable's response to the sensor's notifyVariables() function. It accepts the new value from the sensor. This is generally called by the sensor. +- **attachSensor(int varNum, Sensor \*parentSense)** - The compliment to a sensor's registerVariable() function. This attaches a variable object to the sensor that is giving the value to the variable. The variable is generally responsible for calling this function! This should never be called for a calculated variable. +- **onSensorUpdate()** - This is the variable's response to the sensor's notifyVariables() function. It accepts the new value from the sensor. This is generally called by the sensor. This should never be called for a calculated variable. +- - **getParentSensorName()** - This is a helper - it returns the name of the parent sensor, if applicable +- - **getParentSensorLocation()** - This is a helper - it returns the "location" of the parent sensor, if applicable ### Examples Using Individual Sensor and Variable Functions To access and get values from a sensor, you must create an instance of the sensor class you are interested in using its constructor. Each variable has different parameters that you must specify; these are described below within the section for each sensor. You must then create a new instance for each _variable_, and reference a pointer to the parent sensor in the constructor. Many variables can (and should) call the same parent sensor. The variables are specific to the individual sensor because each sensor collects data and returns data in a unique way. The constructors are all best called outside of the "setup()" or "loop()" functions. The setup functions are then called (sensor, then variables) in the main "setup()" function and the update() and getValues() are called in the loop(). A very simple program to get data from a Decagon CTD might be something like: @@ -211,13 +217,13 @@ loop() } ``` -The "[single_sensor](https://github.com/EnviroDIY/ModularSensors/tree/master/examples/single_sensor)" example in the examples folder shows the same functionality for a MaxBotix Ultrasonic Range Finder. +The "[single_sensor](https://github.com/EnviroDIY/ModularSensors/tree/master/examples/single_sensor)" example in the examples folder shows the same functionality for a MaxBotix Ultrasonic Range Finder. This example also includes using a calculated variable to output a water depth calculated from the sonar range. ## Grouped Sensor Functions Having a unified set of functions to access many sensors allows us to quickly poll through a list of sensors to get all results quickly. To this end, "VariableArray.h" adds the class "VariableArray" with functions to use on an array of pointers to variable objects. ### Functions Available for a VariableArray Object: -- **init(int variableCount, Variable variableList[])** - This initializes the variable array. This must be called in the setup() function. Note that the objects in the variable list must be pointers, not the variable objects themselves. +- **VariableArray(int variableCount, Variable variableList[])** - This is the constructor; it creates the variable array object. Note that the objects in the variable list must be pointers, not the variable objects themselves. The pointers may be to calculated or measured variable objects. - **getVariableCount()** - Simply returns the number of variables. - **getSensorCount()** - Returns the number of independent sensors. This will often be different from the number of variables because many sensors can return multiple variables. - **setupSensors()** - This sets up all of the variables in the array and their respective sensors by running all of their setup() functions. If a sensor doesn't respond to its setup command, the command is called 5 times in attempt to make a connection. If all sensors are set up successfully, returns true. @@ -227,72 +233,71 @@ Having a unified set of functions to access many sensors allows us to quickly po - **sensorsPowerDown()** - This cuts power to all sensors, skipping repeated sensors. No return. - **updateAllSensors()** - This updates all sensor values, skipping repeated sensors. Returns true. Does NOT return any values. - **printSensorData(Stream stream)** - This prints current sensor values along with meta-data to a stream (either hardware or software serial). By default, it will print to the first Serial port. Note that the input is a pointer to a stream instance so to use a hardware serial instance you must use an ampersand before the serial name (ie, &Serial1). -- **generateSensorDataCSV()** - This returns an Arduino String containing comma separated list of sensor values. This string does _NOT_ contain a time stamp of any kind. ### VariableArray Examples: -To use the VariableArray module, you must first create the array of pointers. This should be done outside of the setup() or loop() functions. Remember that you must create a new instance for each variable and each sensor. The sensor functions for sensors within a variable array take advantage of all of the timestamps and status bits within the sensor object to minimize the amount of time that all sensors are powered and the processor is awake. That is, the first sensor to be warmed up will be set up or activated first; the first sensor to stabilize will be asked for values first. The order of the variables within the array should not matter, though for code readability, I strongly suggest putting all the variables attached to a single sensor next to each other in the array. - -Following the example from above, with a Decagon CTD, you would create an array with the three CTD variables like this: - -```cpp -// Create a new VariableArray object -VariableArray myVars; -// Create the array of variables named "variableList" using the pre-created variable objects -Variable \*variableList[] = {\*cond, \*temp, \*depth}; -// Optionally, count the number of variables in the array (in this case, it's 3) -int variableCount = sizeof(variableList) / sizeof(variableList[0]); -``` +To use the VariableArray module, you must first create the array of pointers. This should be done outside of the setup() or loop() functions. Remember that for measured variables you must first create a new sensor instance and then one or more new variable instances for that sensor (depending on how many values it can return.) The sensor functions for sensors within a variable array take advantage of all of the timestamps and status bits within the sensor object to minimize the amount of time that all sensors are powered and the processor is awake. That is, the first sensor to be warmed up will be set up or activated first; the first sensor to stabilize will be asked for values first. All calculations for any calculated variables happen after all the sensor updating has finished. The order of the variables within the array should not matter, though for code readability, I strongly suggest putting all the variables attached to a single sensor next to each other in the array. The asterisk must be put in front of the variable name to indicate that it is a pointer to your variable object. With many variables, it is easier to create the object and the pointer to it all at once in the variable array. This can be done using the "new" keyword like so: ```cpp -// Create a new VariableArray object -VariableArray myVars; // Create new variable objects in an array named "variableList" using the "new" keyword // UUID's and customVarCodes are optional Variable \*variableList[] = { new Sensor1_Variable1(&parentSensor1, "UUID", "customVarCode1"), new Sensor1_Variable2(&parentSensor1, "UUID", "customVarCode2"), - new Sensor2_Variable1(&parentSensor2, "UUID", "customVarCode3"), ... new SensorX_VariableX(&parentSensorx, "UUID", "customVarCode4") }; -// Optionally, count the number of variables in the array +// Count the number of variables in the array (you can count them manually, too) int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray myVarArray(variableCount, variableList); ``` You can also create and name variable pointer objects outside of the array and then reference those pointers inside of the array like so: ```cpp -// Create variable object and return pointers to them +// Create measured variable objects and return pointers to them +// NOTE: We are actually creating subclasses of variables here (tied to particular +// sensors) but returning pointers to the variable superclass as required for +// the variable array. Variable *var1 = new sensor1_var1(&parentSensor1, "UUID", "customVarCode1"); Variable *var2 = new sensor1_var2(&parentSensor1, "UUID", "customVarCode1"); Variable *var3 = new sensor2_var1(&parentSensor2, "UUID", "customVarCode1"); -Variable *varX = new sensorX_varX(&parentSensor2, "UUID", "customVarCode1"); -// Create a new VariableArray object -VariableArray myVars; -// Create new variable objects in an array named "variableList" using the "new" keyword -// UUID's and customVarCodes are optional +// Create a calculated measured variable object and return a pointer to it +float calculationFunction(void) +{ + return var1->getValue() + 10; +} +Variable *calcVar4 = new Variable(calculationFunction, "varName", + "varUnit", varResolution, + "UUID", "varCode"); + +// Continue creating variables.. +Variable *varX = new sensorX_varX(&parentSensorX, "UUID", "customVarCodeX"); + +// Put the already created variable type pointers into an array Variable \*variableList[] = { var1, var2, var3, + var4, ... varX }; -// Optionally, count the number of variables in the array +// Count the number of variables in the array (you can count them manually, too) int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray myVarArray(variableCount, variableList); ``` Creating the variable pointers outside of the array is particularly helpful when you want to reference the same variables in multiple arrays or you want to do any post measurement calculations on the variables. -Once you have created the array of pointers, you can initialize the VariableArray module and setup all of the sensors at once in the setup function: +Once you have created the array of pointers, you can initialize the VariableArray module and setup all of the variables and their parent sensors at once in the setup function: ```cpp -// Initialize the sensor array; -myVars.init(variableCount, variableList); // Set up all the sensors AND variables (BOTH are done by this function) myVars.setupSensors(); ``` @@ -300,38 +305,49 @@ myVars.setupSensors(); You can then get values or variable names for all of the sensors within the loop with calls like: ```cpp -// Update the sensor value(s) -myVars.updateAllSensors(); +// Send power to all of the sensors +myVarArray.sensorsPowerUp(); +// Wake up all of the sensors +myVarArray.sensorsWake(); +// Update the values from all attached sensors +myVarArray.updateAllSensors(); +// Put sensors to sleep +myVarArray.sensorsSleep(); +// Cut sensor power +myVarArray.sensorsPowerDown(); // Print the data to the screen -myVars.printSensorData(); +myVarArray.printSensorData(); ``` ## Basic Logger Functions -Our main reason to unify the output from many sensors and variables is to easily log the data to an SD card and to send it to a live streaming data receiver, like the [MonitorMyWatershed/EnviroDIY data portal](http://data.envirodiy.org/). There are several modules available to use with the sensors to log data and stream data: LoggerBase, LoggerEnviroDIY, and LoggerModem. The classes Logger (in LoggerBase) is a sub-class of VariableArray and LoggerEnviroDIY (in LoggerEnviroDIY) is in-turn a sub-class of Logger. They contain all of the functions available to a VariableArray as described above. The Logger class adds the abilities to communicate with a real time clock, to put the board into deep sleep between readings to conserver power, and to write the data from the sensors to a csv file on a connected SD card. The LoggerModem module is essentially a wrapper for [TinyGSM](https://github.com/EnviroDIY/TinyGSM) which adds quick functions for turning modem on and off to save power and to synchronize the real-time clock with the [NIST Internet time service](https://www.nist.gov/pml/time-and-frequency-division/services/internet-time-service-its). The LoggerEnviroDIY class uses LoggerModem to add the ability to properly format and send data to the [EnviroDIY data portal](http://data.envirodiy.org/). +Our main reason to unify the output from many sensors and variables is to easily log the data to an SD card and to send it to a live streaming data receiver, like the [MonitorMyWatershed/EnviroDIY data portal](http://data.envirodiy.org/). There are several modules available to use with the sensors to log data and stream data: LoggerBase, LoggerEnviroDIY, and LoggerModem. The classes Logger (in LoggerBase) is a parent class for LoggerEnviroDIY (in LoggerEnviroDIY). Both also contain an internal VariableArray object. The Logger class has the abilities to communicate with a real time clock, to put the board into deep sleep between readings to conserver power, and to write the data from the sensors to a csv file on a connected SD card. The LoggerEnviroDIY class can also have a LoggerModem attached, synchronize the time to NIST, and format and send data to the [EnviroDIY data portal](http://data.envirodiy.org/). The LoggerModem module is essentially a wrapper for [TinyGSM](https://github.com/EnviroDIY/TinyGSM) which adds quick functions for turning modem on and off to save power and to synchronize the real-time clock with the [NIST Internet time service](https://www.nist.gov/pml/time-and-frequency-division/services/internet-time-service-its). ### Functions Available for a Logger Object: #### Setup and initialization functions: -- **init(int SDCardPin, int mcuWakePin, int variableCount, Variable \*variableList[], float loggingIntervalMinutes, const char \*loggerID = 0)** - Initializes the logger object. Must happen within the setup function. Note that the variableList[], and loggerID are pointers. The SDCardPin is the pin of the chip select/slave select for the SPI connection to the SD card. +- **Logger(const char *loggerID, uint16_t loggingIntervalMinutes, int8_t SDCardPin, int8_t mcuWakePin, VariableArray *inputArray)** - The constructor; creates the logger object. Note that the variableList[], and loggerID are pointers. The SDCardPin is the pin of the chip select/slave select for the SPI connection to the SD card. - NOTE regarding *loggingIntervalMinutes*: For the first 20 minutes that a logger has been powered up for a deployment, the logger will take readings at 2 minute intervals for 10 measurements, to assist with confirming that the deployment is successful. Afterwards, the time between measurements will revert to the number of minutes set with *loggingIntervalMinutes*. - **setAlertPin(int ledPin)** - Optionally sets a pin to put out an alert that a measurement is being logged. This is intended to be a pin with a LED on it so you can see the light come on when a measurement is being taken. +- **setTestingModePin(int buttonPin)** - Optionally sets a pin that is attached to a button or some other interrupt source to force the logger to enter sensor testing mode. #### Timezone functions: - **setTimeZone(int timeZone)** - Sets the timezone that you wish data to be logged in (in +/- hours from UTC). _This must always be set!_ +- **getTimeZone()** - Returns the set timezone. - **setTZOffset(int offset)** - This sets the offset between the built-in clock and the timezone the data should be logged in. If your clock is set in UTC, then the TZOffset should be the same as the TimeZone. For example, if you would like your clock to be set in UTC but your data should be output in Eastern Standard Time, both setTimeZone and setTZOffset should be called with -5. On the other hand, if your clock is already set EST, you do not need to call the setTZOffset function (or can call it with 0). A note about timezones: It is possible to create multiple logger objects in your code if you want to log different sensors at different intervals, _but every logger object will always have the same timezone and timezone offset_. If you attempt to call these functions more than once for different loggers, whatever value was called last will apply to every logger. +- **getTZOffset()** - Returns the set timezone offset. #### Functions to access the clock in proper format and time zone: - **syncRTClock(uint32_t timestamp)** - This synchronizes the real time clock with the provided timestamp, which should be a unix timestamp _in UTC_. -- **getNow()** - This gets the current epoch time (unix timestamp - number of seconds since Jan 1, 1970) and corrects it for the specified logger time zone offset. +- **getNowEpoch()** - This gets the current epoch time (unix timestamp - number of seconds since Jan 1, 1970) and corrects it for the specified logger time zone offset. - **formatDateTime_ISO8601(DateTime dt)** - Formats a DateTime object into an ISO8601 formatted Arduino String. - **formatDateTime_ISO8601(uint32_t unixTime)** - Formats a unix timestamp into an ISO8601 formatted Arduino String. -- **checkInterval()** - This returns true if the _current_ time is an even interval of the logging interval, otherwise false. This uses getNow() to get the curernt time. +- **checkInterval()** - This returns true if the _current_ time is an even interval of the logging interval, otherwise false. This uses getNowEpoch() to get the curernt time. - **markTime()** - This sets static variables for the date/time - this is needed so that all data outputs (SD, EnviroDIY, serial printing, etc) print the same time for updating the sensors - even though the routines to update the sensors and to output the data may take several seconds. It is not currently possible to output the instantaneous time an individual sensor was updated, just a single marked time. By custom, this should be called before updating the sensors, not after. If you do not call this function before saving or sending data, there will be no timestamps associated with your data. This is called for you every time the checkInterval() function is run. -- **checkMarkedInterval()** - This returns true if the _marked_ time is an even interval of the logging interval, otherwise false. This uses the static time value set by markTime() to get the time. It does not check the real-time-clock directly. +- **checkMarkedInterval()** - This returns true if the _marked_ time is an even interval of the logging interval, otherwise false. This uses the static time value set by markTime() to get the time. It does not check the current time. #### Functions for the processor sleep modes: @@ -341,29 +357,21 @@ A note about timezones: It is possible to create multiple logger objects in you #### Functions for logging data: -- **setFileName(fileName)** - This sets a specified file name for data to be saved as, if you want to decide on it in advance. Note that you must include the file extention (ie., '.txt') in the file name. If you do not call the setFileName function with a specific name, a csv file name will automatically be generated from the logger id and the current date. +- **setFileName(fileName)** - This sets a specified file name for data to be saved as, if you want to decide on it in advance. Note that you must include the file extension (ie., '.txt') in the file name. If you do not call the setFileName function with a specific name, a csv file name will automatically be generated from the logger id and the current date. - **getFileName()** - This returns the current filename as an Arduino String. -- **setupLogFile()** - This creates a file on the SD card and writes a header to it. It also sets the "file created" time stamp. -- **logToSD(String rec)** - This writes a data line containing "rec" the the SD card and sets the "file modified" timestamp. -- **generateFileHeader()** - This returns and Aruduino String with a comma separated list of headers for the csv. The headers will be ordered based on the order variables are listed in the array fed to the init function. -- **generateSensorDataCSV()** - This returns an Arduino String containing the time and a comma separated list of sensor values. The data will be ordered based on the order variables are listed in the array fed to the init function. +- **createLogFile(String filename, bool writeDefaultHeader = false)** or **createLogFile(bool writeDefaultHeader = false)** - These functions create a file on an SD card and set the created/modified/accessed timestamps in that file. The filename may either be the one automatically generated by the logger id and the date, the one set by setFileName(String), or can be specified in the function. If asked to, these functions will also write a header to the file based on the variable information from the variable array. This can be used to force a logger to create a file with a secondary file name. +- **logToSD(String filename, String rec)** or **logToSD(String rec)** or **logToSD(void)** - These functions create a file on an SD card and set the modified/accessed timestamps in that file. The filename may either be the one automatically generated by the logger id and the date, the one set by setFileName(String), or can be specified in the function. If the file does not already exist, the file will be created. The line to be written to the file can either be specified or will be a comma separated list of the current values of all variables in the variable array. +- **streamFileHeader(Stream \*stream)** - This prints a header for a csv out to an Arduino stream. The header will be ordered based on the order variables are listed in the VariableArray. + - If you would prefer to manually work with and change the header, the function **generateFileHeader()** returns an Aruduino String with a comma separated list of headers for the csv. This header _will_ be a very, very long string. _If the string is too long, it may not be printed or cause the entire program to crash!_ Only try to work directly with the header string for VariableArray's with a small number of variables. +- **streamSensorDataCSV(Stream \*stream)** - This returns an Arduino String containing the time and a comma separated list of sensor values. The data will be ordered based on the order variables are listed in the VariableArray. + - If you would like the manipulate or add to the csv data, **generateSensorDataCSV()** returns an Arduino String containing the same information as is printed streamSensorDataCSV(Stream \*stream). Before using this function, take into account the same warnings as given with generateFileHeader(), although this string will be much, much shorter than the string created by generateFileHeader(). #### Functions for sensor testing and communication debugging: By default, some status and set-up information will be sent to the Serial (for AVR processors) or SerialUSB (for SAMD processors). To stop all print out or to change the port the print out goes to, open ModSensorDebugger.h and change or remove lines 15-21 that define the STANDARD_SERIAL_OUTPUT. There is also a built-in function for "testing" sensors within sensor arrays - that is, continuously displaying current sensor values to test that the sensors are working properly: -- **testingMode()** - Enters a "testing" mode for the sensors. It prints out all of the sensor results for 25 records worth of data with a 5-second delay between readings. The printouts go to whichever serial port is given in the ```#define STANDARD_SERIAL_OUTPUT``` statement. - -Testing mode can be entered directly in a set-up or loop function by calling the ```testingMode()``` function. There is also a function to hold code to wait for a button press to enter test testing mode. I suggest only running this as the very last step of the setup function. -- **checkForTestingMode(int buttonPin)** - This stops everything and waits for five seconds for a button to be pressed to enter allow the user to enter "sensor testing" mode. - -If you would like to be able to enter the testing mode via a button press at any time while the logger is asleep, you can use the testingISR() function to define an interrupt for entering testing mode. Please note that the ISR verifies that you are not in the middle of taking/logging a measurement before beginning the testing mode, so you cannot enter testing mode during that time. Also note that the testing mode will not return any valid results until after the full setup is complete, so don't try to enter testing mode before then. -```cpp -pinMode(pinNumber, INPUT_PULLUP); -enableInterrupt(buttonPin, Logger::testingISR, CHANGE); -``` - +- **testingMode()** - Enters a "testing" mode for the sensors. It prints out all of the sensor results for 25 records worth of data with a 5-second delay between readings. The printouts go to whichever serial port is given in the ```#define STANDARD_SERIAL_OUTPUT``` statement. Testing mode can be entered directly in a set-up or loop function by calling the ```testingMode()``` function. If you have set a pin for testing mode via an interrupt with the setTestingModePin(buttonPin) function, you can also enter testing mode any time except when the logger is already taking a data point by creating an interrupt on that pin (ie, by pushing a button). For more intense _code_ debugging for any individual component of the library (sensor communication, modem communication, variable array functions, etc), open the source file header (\*.h), for that component. Find the line ```// #define DEBUGGING_SERIAL_OUTPUT xxxxx```, where xxxxx is the name of a serial output (ie, Serial or USBSerial). Remove the two comment slashes from that line. Then recompile and upload your code. This will (sometimes dramatically) increase the number of statements going out to the debugging serial port. A few sensors also the line ```// #define DEEP_DEBUGGING_SERIAL_OUTPUT xxxxx```, uncommenting of which will send even more information to the defined port. Note that this type of debugging is intended to help find errors in the code of this library, not problems with the sensors themselves! @@ -376,13 +384,19 @@ For more intense _code_ debugging for any individual component of the library (s A loggerModem serves two functions: First, it communicates with the internet via WiFi or cellular service and sends data to remote services. Second, it acts as a sensor which can return the strength of the WiFi or cellular connection. A loggerModem object is a combination of a [TinyGsm](https://github.com/EnviroDIY/TinyGSM) (modem instance), a TinyGsmClient, and a ModemOnOff to control modem power. -Before creating a loggerModem instance, _you must add one of these lines to the top of your sketch_, before any include statements: - -- ```#define TINY_GSM_MODEM_SIM800``` - for a SIMCom SIM800, SIM900, or variant thereof (including [Sodaq GPRSBees](https://shop.sodaq.com/en/gprsbee.html)) +Before creating a loggerModem instance, _you must define your modem at top of your sketch_, before any include statements. ie: +- ```#define TINY_GSM_MODEM_SIM900``` - for a SIMCom SIM900, or variant thereof (including older [Sodaq GPRSBees](https://shop.sodaq.com/en/gprsbee.html)) +- ```#define TINY_GSM_MODEM_SIM800``` - for a SIMCom SIM800 or variant thereof (including current [Sodaq GPRSBees](https://shop.sodaq.com/en/gprsbee.html)) - ```#define TINY_GSM_MODEM_SIM808``` - for a SIMCom SIM808 (essentially a SIMCom SIM800 with GPS support) -- ```#define TINY_GSM_MODEM_A6``` - for an AI-Thinker A6 or A7 +- ```#define TINY_GSM_MODEM_SIM868``` - for a SIMCom SIM868 (another SIM800 variant with GPS support) +- ```#define TINY_GSM_MODEM_UBLOX``` - for most u-blox cellular modems (LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx, or a Digi 3G XBee running in bypass mode) +- ```#define TINY_GSM_MODEM_M95``` - for an Quectel M95 +- ```#define TINY_GSM_MODEM_BG96``` - for an Quectel BG96 +- ```#define TINY_GSM_MODEM_A6``` - for an AI-Thinker A6 +- ```#define TINY_GSM_MODEM_A7``` - for an AI-Thinker A7 - ```#define TINY_GSM_MODEM_M590``` - for a Neoway M590 -- ```#define TINY_GSM_MODEM_UBLOX``` - for most u-blox cellular modems ((LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx, or a Digi 3G XBee running in bypass mode) +- ```#define TINY_GSM_MODEM_MC60``` - for a Quectel MC60 +- ```#define TINY_GSM_MODEM_MC60E``` - for a Quectel MC60E - ```#define TINY_GSM_MODEM_ESP8266``` - for an ESP8266 using the _default AT command firmware_ - ```#define TINY_GSM_MODEM_XBEE``` - for Digi brand WiFi or Cellular XBee's running in normal (transparent) mode @@ -393,13 +407,14 @@ Then you must create the modem object: loggerModem modem; ``` -See [TinyGSM's documentation](https://github.com/vshymanskyy/TinyGSM/blob/master/README.md) for a full list of all of the chip variants and modules that are supported. +See [TinyGSM's documentation](https://github.com/vshymanskyy/TinyGSM/blob/master/README.md) for a more details about of all of the chip variants and modules that are supported. After defining your modem, set it up using one of these two commands, depending on whether you are using cellular or WiFi communication: - **setupModem(Stream modemStream, int vcc33Pin, int modemStatusPin, int modemSleepRqPin, ModemSleepType sleepType, const char \*APN)** - Sets up the internet communcation with a cellular modem. Note that the modemStream and APN should be pointers. Use -1 for any pins that are not connected. - **setupModem(Stream modemStream, int vcc33Pin, int modemStatusPin, int modemSleepRqPin, ModemSleepType sleepType, const char \*ssid, const char \*pwd)** - Sets up the internet communication with a WiFi modem. Note that the modemStream, ssid, and password should be pointers. Use -1 for any pins that are not connected. -- The **vcc33Pin** is the pin that controls whether or not the modem itself is powered. Use -1 if your modem is always receiving power from your logger board or if you want to control modem power independently. +- The **vcc33Pin** is the pin that controls whether or not the modem itself is powered. Use -1 if your modem is always receiving power or if you want to control modem power independently. + - NOTE: _Many_ modem chips require more power than the 0.5A that most Arduino-style boards can provide! The power draw is particularly high during network connection and sending. Some chips require up to 2.5A. _Know your modem's specs!_ If it requires more power than your board can provide, ensure that the modem has an alternate battery connection or power source! - The **modemStatusPin** is the pin that indicates whether the modem is turned on and it is clear to send data. If you use -1, the modem is assumed to always be ready. - The **modemSleepRqPin** is the _pin_ used to put the modem to sleep or to wake it up. - The **ModemSleepType** controls _how the modemSleepRqPin is used_ to put the modem to sleep between readings. @@ -431,22 +446,21 @@ Variable *modemSinalPct = Modem_SignalPercent(&modem, "UUID", "customVarCode"); The modem does not behave quite the same as all the other sensors do, though. Setup must be done with the ```setupModem(...)``` function; the normal ```setup()``` function does not do anything. The ```powerUp()``` and ```powerDown()``` functions also do not work, the modem will only go on with the ```modemPowerUp()``` function and off with the ```modemPowerDown()``` function. -Note for GPRSBee modems: To start the modem you will need to power the logger board off, connect the battery to the logger board, and finally attach the modem to the logger board. Then you may power the board and run your sketch. We have found that attaching a GPRSBee modem to power in a different sequence results in the modem reporting zero signal strength. Note, the Mayfly connected to a computer via USB does not supply sufficient power to the GPRSBee. If the community finds this true for other modems, please let us know. +Special note for Sodaq GPRSBee modems: To start the modem you will need to power the logger board off, connect the battery to the logger board, and finally attach the modem to the logger board. Then you may power the board and run your sketch. We have found that attaching a GPRSBee modem to power in a different sequence results in the modem reporting zero signal strength. ### Additional Functions Available for a LoggerEnviroDIY Object: These functions attach a modem and set up the required registration token and sampling feature UUID for the EnviroDIY web streaming data loader API. **All three** functions must be called before calling any of the other EnviroDIYLogger functions. You *must* also add the correct variable UUID's to the constructors for each variable you are using. All of the UUID and token values can be obtained after registering at http://data.envirodiy.org/. -- **attachModem(loggerModem &modem)** - Attaches a loggerModem to the logger, which the logger then can use to send data to the internet. See [Modem and Internet Functions](#Modem) for more information on how the modem must be set up before it is attached to the logger. You must include an ampersand to tie in the already created modem! +- **attachModem(loggerModem &modem)** - Attaches a loggerModem to the logger, which the logger then can use to send data to the internet. See [Modem and Internet Functions](#Modem) for more information on how the modem must be set up before it is attached to the logger. You must include an ampersand to tie in the already created modem! If you do not attach a modem, you cannot send data to The EnviroDIY data portal! - **setToken(const char \*registrationToken)** - Sets the registration token to access the EnviroDIY streaming data loader API. Note that the input is a pointer to the registrationToken. - **setSamplingFeatureUUID(const char \*samplingFeatureUUID)** - Sets the universally unique identifier (UUID or GUID) of the sampling feature. Note that the input is a pointer to the samplingFeatureUUID. -Because sending data to EnviroDIY depends on having some sort of modem or internet connection, there is a modem object created within the LoggerEnviroDIY Object. To set up that modem object, you still need to call the functions listed in the LoggerModem section, but you need to add an extra "modem." before the function name to call the internal modem object. You do not need to separately create the object. - -Within the loop, these two functions will then format and send out data: +Within the loop, these functions can then format and send out data: -- **generateSensorDataJSON()** - Generates a properly formatted JSON string to go to the EnviroDIY streaming data loader API. -- **postDataEnviroDIY()** - Creates proper headers and sends data to the EnviroDIY data portal. Depends on the modem support module. Returns an HTML response code. +- **streamSensorDataJSON(Stream \*stream)** - Prints a properly formatted EnviroDIY datqa portal JSON string to the selected Arduino stream. + - If you would like the manipulate or add to the csv data, **generateSensorDataJSON()** returns an Arduino String containing the same information as is printed by streamSensorDataJSON(Stream \*stream). Keep in mind, the JSON is likely to be a very long string and _may crash your program_. Only use generateSensorDataJSON() when working with a small variable array +- **postDataEnviroDIY(String enviroDIYjson = "");** - Creates proper HTTP headers and sends the entered JSON to the EnviroDIY data portal. Depends on the having a modem attached. Returns an HTML response code. If you do not enter a JSON, the streamSensorDataJSON(Stream \*stream) function will be used internally to generate one. Please keep in mind when attempting to enter a String JSON that very long strings _may crash your program_. ### Logger Examples: @@ -465,20 +479,20 @@ Logger logger; // Import Logger Module #include // Create a new logger instance -LoggerEnviroDIY EnviroDIYLogger; +LoggerEnviroDIY EnviroDIYLogger(loggerID, loggingIntervalMinutes, SDCardPin, mcuWakePin, inputArray); ``` -_Within the setup function_, you must then initialize the logger and then run the logger setup. For the EnviroDIY logger, you must also set up the communication. (Please note that these are shown with default values.): +_Within the setup function_, you must then set the logger time zones and attach informational pins. For the EnviroDIY logger, you must also set up the communication.: ```cpp // Set the time zone and offset from the RTC logger.setTimeZone(timeZone); logger.setTZOffset(offset); -// Initialize the logger -logger.init(SDCardPin, mcuWakePin, variableCount, variableList, loggingIntervalMinutes, loggerID); // OPTIONAL - specify a pin to give an alert when a measurement is taken // This should generally be a pin with an LED setAlertPin(int ledPin); +// OPTIONAL - specify a pin for entering testing mode +setTestingModePin(int buttonPin); // Begin the logger; logger.begin(); ``` @@ -489,11 +503,11 @@ logger.begin(); // Set the time zone and offset from the RTC EnviroDIYLogger.setTimeZone(timeZone); EnviroDIYLogger.setTZOffset(offset); -// Initialize the logger -EnviroDIYLogger.init(SDCardPin, mcuWakePin, variableCount, variableList, loggingIntervalMinutes, loggerID); // OPTIONAL - specify a pin to give an alert when a measurement is taken // This should generally be a pin with an LED setAlertPin(ledPin); +// OPTIONAL - specify a pin for entering testing mode +setTestingModePin(int buttonPin); // Set up the communication with EnviroDIY EnviroDIYLogger.setToken(registrationToken); EnviroDIYLogger.setSamplingFeature(samplingFeature); @@ -503,22 +517,13 @@ EnviroDIYLogger.setUUIDs(UUIDs[]); modem.setupModem(modemStream, vcc33Pin, modemStatusPin, modemSleepRqPin, sleepType, APN); // Attach the modem to the logger -EnviroDIYLogger.attachModem(&modem); - -// Connect to the network -if (modem.connectInternet()) -{ - // Synchronize the RTC - EnviroDIYLogger.syncRTClock(); -} -// Disconnect from the network -modem.disconnectInternet(); +EnviroDIYLogger.attachModem(modem); // Begin the logger; EnviroDIYLogger.begin(); ``` -_Within the main loop function_, all logging and sending of data can be done using a single program line. Because the built-in log functions already handle sleeping and waking the board processor, **there cannot be nothing else within the loop function.** +_Within the main loop function_, all logging and sending of data can be done using a single program line. Because the built-in log functions already handle sleeping and waking the board processor, **there cannot be nothing else within the loop function.** This is a wonderful helper if you are setting up your logger to do only the "normal" things. Please keep in mind, _this is not the only option_. ```cpp void loop() @@ -547,7 +552,7 @@ If you would like to do other things within the loop function, you should access - After updating the sensors, then call any functions you want to send/print/save data. - Finish by putting the logger back to sleep, if desired, with ```systemSleep()```. -The [double_logger example program](https://github.com/EnviroDIY/ModularSensors/tree/master/examples/double_logger) demonstrates using a custom loop function in order to log two different groups of sensors at different logging intervals. The [baro_rho_correction example program](https://github.com/EnviroDIY/ModularSensors/tree/master/examples/baro_rho_correction) demonstrates using a custom loop function in order to create calculated variables before saving the data and sending it to the EnviroDIY data portal. The [data_saving example program](https://github.com/EnviroDIY/ModularSensors/tree/master/examples/data_saving) shows using a custom loop in order to save cellular data by saving data from many variables on the SD card, but only sending a portion of the data to the EnviroDIY data portal. +The [double_logger example program](https://github.com/EnviroDIY/ModularSensors/tree/master/examples/double_logger) demonstrates using a custom loop function in order to log two different groups of sensors at different logging intervals. The [data_saving example program](https://github.com/EnviroDIY/ModularSensors/tree/master/examples/data_saving) shows using a custom loop in order to save cellular data by saving data from many variables on the SD card, but only sending a portion of the data to the EnviroDIY data portal. ## Available sensors diff --git a/examples/DRWI_CitSci/DRWI_CitSci.ino b/examples/DRWI_CitSci/DRWI_CitSci.ino index 5e82bb3eb..b9a772197 100644 --- a/examples/DRWI_CitSci/DRWI_CitSci.ino +++ b/examples/DRWI_CitSci/DRWI_CitSci.ino @@ -30,19 +30,16 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. #include // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "DWRI_CitSci.ino"; - // Logger ID, also becomes the prefix for the name of the data file on SD card -const char *LoggerID = "XXXX"; +const char *LoggerID = "XXXXX"; // How frequently (in minutes) to log data const uint8_t loggingInterval = 5; // Your logger's timezone. const int8_t timeZone = -5; -// Create a new logger instance -LoggerDreamHost EnviroDIYLogger; // ========================================================================== @@ -50,7 +47,7 @@ LoggerDreamHost EnviroDIYLogger; // ========================================================================== #include -const long serialBaud = 57600; // Baud rate for the primary serial port for debugging +const long serialBaud = 115200; // Baud rate for the primary serial port for debugging const int8_t greenLED = 8; // Pin for the green LED (-1 if unconnected) const int8_t redLED = 9; // Pin for the red LED (-1 if unconnected) const int8_t buttonPin = 21; // Pin for a button to use to enter debugging mode (-1 if unconnected) @@ -59,7 +56,7 @@ const int8_t wakePin = A7; // Interrupt/Alarm pin to wake from sleep // In a SAMD system where you are using the built-in rtc, set wakePin to 1 const int8_t sdCardPin = 12; // SD Card Chip Select/Slave Select Pin (must be defined!) -// Create the processor "sensor" +// Create and return the processor "sensor" const char *MFVersion = "v0.5b"; ProcessorStats mayfly(MFVersion) ; @@ -125,6 +122,8 @@ DecagonCTD ctd(*CTDSDI12address, SDI12Power, SDI12Data, CTDnumberReadings); // ========================================================================== // The array that contains all variables to be logged // ========================================================================== +// Create pointers for all of the variables from the sensors +// at the same time putting them into an array Variable *variableList[] = { new ProcessorStats_Batt(&mayfly, "12345678-abcd-1234-efgh-1234567890ab"), new MaximDS3231_Temp(&ds3231, "12345678-abcd-1234-efgh-1234567890ab"), @@ -136,7 +135,12 @@ Variable *variableList[] = { new Modem_RSSI(&modem, "12345678-abcd-1234-efgh-1234567890ab"), new Modem_SignalPercent(&modem, "12345678-abcd-1234-efgh-1234567890ab"), }; +// Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray varArray(variableCount, variableList); +// Create a new logger instance +LoggerDreamHost EnviroDIYLogger(LoggerID, loggingInterval, sdCardPin, wakePin, &varArray); // ========================================================================== @@ -195,18 +199,15 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the logger - EnviroDIYLogger.init(sdCardPin, wakePin, variableCount, variableList, - loggingInterval, LoggerID); - EnviroDIYLogger.setAlertPin(greenLED); - // Setup the logger modem modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, apn); - // Attach the modem to the logger - EnviroDIYLogger.attachModem(&modem); + // Attach the modem and information pins to the logger + EnviroDIYLogger.attachModem(modem); + EnviroDIYLogger.setAlertPin(greenLED); + EnviroDIYLogger.setTestingModePin(buttonPin); - // Set up the connection with EnviroDIY + // Enter the tokens for the connection with EnviroDIY EnviroDIYLogger.setToken(registrationToken); EnviroDIYLogger.setSamplingFeatureUUID(samplingFeature); @@ -215,19 +216,6 @@ void setup() // Begin the logger EnviroDIYLogger.begin(); - - // Hold up for 10-seconds to allow immediate entry into sensor testing mode - // EnviroDIYLogger.checkForTestingMode(buttonPin); - - // Set up an interrupt on a pin to enter sensor testing mode at any time - pinMode(buttonPin, INPUT_PULLUP); - enableInterrupt(buttonPin, Logger::testingISR, CHANGE); - Serial.print(F("Push button on pin ")); - Serial.print(buttonPin); - Serial.println(F(" at any time to enter sensor testing mode.")); - - // Blink the LEDs really fast to show start-up is done - greenredflash(6, 25); } diff --git a/examples/DRWI_CitSci/platformio.ini b/examples/DRWI_CitSci/platformio.ini index ed8b1d7e4..0e1892c66 100644 --- a/examples/DRWI_CitSci/platformio.ini +++ b/examples/DRWI_CitSci/platformio.ini @@ -18,6 +18,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/DRWI_NoCellular/DRWI_NoCellular.ino b/examples/DRWI_NoCellular/DRWI_NoCellular.ino index e6e33a0ac..c4fed82ca 100644 --- a/examples/DRWI_NoCellular/DRWI_NoCellular.ino +++ b/examples/DRWI_NoCellular/DRWI_NoCellular.ino @@ -21,20 +21,18 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. #include // for external and pin change interrupts #include + // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "DRWI_NoCellular.ino"; - // Logger ID, also becomes the prefix for the name of the data file on SD card -const char *LoggerID = "XXXX"; +const char *LoggerID = "XXXXX"; // How frequently (in minutes) to log data const uint8_t loggingInterval = 5; // Your logger's timezone. const int8_t timeZone = -5; -// Create a new logger instance -Logger logger; // ========================================================================== @@ -42,7 +40,7 @@ Logger logger; // ========================================================================== #include -const long serialBaud = 57600; // Baud rate for the primary serial port for debugging +const long serialBaud = 115200; // Baud rate for the primary serial port for debugging const int8_t greenLED = 8; // Pin for the green LED (-1 if unconnected) const int8_t redLED = 9; // Pin for the red LED (-1 if unconnected) const int8_t buttonPin = 21; // Pin for a button to use to enter debugging mode (-1 if unconnected) @@ -51,7 +49,7 @@ const int8_t wakePin = A7; // Interrupt/Alarm pin to wake from sleep // In a SAMD system where you are using the built-in rtc, set wakePin to 1 const int8_t sdCardPin = 12; // SD Card Chip Select/Slave Select Pin (must be defined!) -// Create the processor "sensor" +// Create and return the processor "sensor" const char *MFVersion = "v0.5b"; ProcessorStats mayfly(MFVersion) ; @@ -102,16 +100,30 @@ DecagonCTD ctd(*CTDSDI12address, SDI12Power, SDI12Data, CTDnumberReadings); // ========================================================================== // The array that contains all variables to be logged // ========================================================================== +// Create pointers for all of the variables from the sensors +// at the same time putting them into an array Variable *variableList[] = { - new ProcessorStats_Batt(&mayfly), - new MaximDS3231_Temp(&ds3231), - new DecagonCTD_Cond(&ctd), - new DecagonCTD_Temp(&ctd), - new DecagonCTD_Depth(&ctd), - new CampbellOBS3_Turbidity(&osb3low, "", "TurbLow"), - new CampbellOBS3_Turbidity(&osb3high, "", "TurbHigh"), + new ProcessorStats_Batt(&mayfly, "12345678-abcd-1234-efgh-1234567890ab"), + new MaximDS3231_Temp(&ds3231, "12345678-abcd-1234-efgh-1234567890ab"), + new DecagonCTD_Cond(&ctd, "12345678-abcd-1234-efgh-1234567890ab"), + new DecagonCTD_Temp(&ctd, "12345678-abcd-1234-efgh-1234567890ab"), + new DecagonCTD_Depth(&ctd, "12345678-abcd-1234-efgh-1234567890ab"), + new CampbellOBS3_Turbidity(&osb3low, "12345678-abcd-1234-efgh-1234567890ab", "TurbLow"), + new CampbellOBS3_Turbidity(&osb3high, "12345678-abcd-1234-efgh-1234567890ab", "TurbHigh") }; +// Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray varArray(variableCount, variableList); +// Create a new logger instance +Logger logger(LoggerID, loggingInterval, sdCardPin, wakePin, &varArray); + +// ========================================================================== +// Device registration and sampling feature information +// This should be obtained after registration at http://data.envirodiy.org +// ========================================================================== +const char *registrationToken = "12345678-abcd-1234-efgh-1234567890ab"; // Device registration token +const char *samplingFeature = "12345678-abcd-1234-efgh-1234567890ab"; // Sampling feature UUID // ========================================================================== @@ -159,26 +171,12 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the logger - logger.init(sdCardPin, wakePin, variableCount, variableList, - loggingInterval, LoggerID); + // Set pins for alerts and entering testing mode logger.setAlertPin(greenLED); + logger.setTestingModePin(buttonPin); // Begin the logger logger.begin(); - - // Hold up for 10-seconds to allow immediate entry into sensor testing mode - // EnviroDIYLogger.checkForTestingMode(buttonPin); - - // Set up an interrupt on a pin to enter sensor testing mode at any time - pinMode(buttonPin, INPUT_PULLUP); - enableInterrupt(buttonPin, Logger::testingISR, CHANGE); - Serial.print(F("Push button on pin ")); - Serial.print(buttonPin); - Serial.println(F(" at any time to enter sensor testing mode.")); - - // Blink the LEDs really fast to show start-up is done - greenredflash(6, 25); } diff --git a/examples/DRWI_NoCellular/platformio.ini b/examples/DRWI_NoCellular/platformio.ini index 49f8ba285..c342574e4 100644 --- a/examples/DRWI_NoCellular/platformio.ini +++ b/examples/DRWI_NoCellular/platformio.ini @@ -18,6 +18,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/ReadMe.md b/examples/ReadMe.md index c2f18135b..aacb278a1 100644 --- a/examples/ReadMe.md +++ b/examples/ReadMe.md @@ -19,7 +19,7 @@ To adjust any of these to work with your own sensor arrangements: 14. Program your board! ### single_sensor.ino -This shows making use of the unified set of commands to print data from a MaxBotix ultrasonic range finder to the serial port. +This shows making use of the unified set of commands to print data from a MaxBotix ultrasonic range finder to the serial port. It also shows creating a calculated variable which is the water depth. ### multisensor_print.ino This shows using an array of sensors to easily update all of them and print all results to the serial port. This example calls on at least one of every single sensor available in this library. @@ -39,11 +39,11 @@ This is a simplified version of logging_to_EnviroDIY.ino using just the sensors ### DWRI_NoCellular.ino This is uses just the sensors and equipment standard to the DWRI Citizen Science grant for circumstances where there is no cellular signal. +### baro_rho_correction.ino +This example demonstrates how to work with calculated variables. + ### double_logger.ino This is a more complicated example using two different logger instances to log data at two different intervals, in this case, an AM3215 logging every minute, while checking the battery voltage only every 5 minutes. This showcases both how to use two different logging instances and how to use some of the functions to set up your own logging loop rather than using the log() function. -### baro_rho_correction.ino -Like the double_logger example, this showcases how to set up your own logging loop rather than using the log() function. In this case, though, there is only a single logger, but we are adding some extra calculated variables to the final output. - ### data_saving.ino 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. diff --git a/examples/baro_rho_correction/ReadMe.md b/examples/baro_rho_correction/ReadMe.md index d4f987a14..184d2c053 100644 --- a/examples/baro_rho_correction/ReadMe.md +++ b/examples/baro_rho_correction/ReadMe.md @@ -1,3 +1,3 @@ # Example using the Modular Sensors Library to Calculate Results based on Measured Values and Send Both Measured and Calculated Values to the EnviroDIY Data Portal -Like the double_logger example, this showcases how to set up your own logging loop rather than using the log() function. In this case, though, there is only a single logger, but we are adding some extra calculated variables to the final output. +This example demonstrates how to work with calculated variables. diff --git a/examples/baro_rho_correction/baro_rho_correction.ino b/examples/baro_rho_correction/baro_rho_correction.ino index 97f9b4a49..3591f5c29 100644 --- a/examples/baro_rho_correction/baro_rho_correction.ino +++ b/examples/baro_rho_correction/baro_rho_correction.ino @@ -19,8 +19,8 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. // #define TINY_GSM_MODEM_A6 // Select for a AI-Thinker A6 or A7 chip // #define TINY_GSM_MODEM_M590 // Select for a Neoway M590 // #define TINY_GSM_MODEM_UBLOX // Select for most u-blox cellular modems -#define TINY_GSM_MODEM_ESP8266 // Select for an ESP8266 using the DEFAULT AT COMMAND FIRMWARE -// #define TINY_GSM_MODEM_XBEE // Select for Digi brand WiFi or Cellular XBee's +// #define TINY_GSM_MODEM_ESP8266 // Select for an ESP8266 using the DEFAULT AT COMMAND FIRMWARE +#define TINY_GSM_MODEM_XBEE // Select for Digi brand WiFi or Cellular XBee's // ========================================================================== // Include the base required libraries @@ -31,19 +31,16 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "baro_rho_correction.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; +const uint8_t loggingInterval = 5; // Your logger's timezone. const int8_t timeZone = -5; -// Create a new logger instance -LoggerEnviroDIY EnviroDIYLogger; // ========================================================================== @@ -116,7 +113,7 @@ loggerModem modem; // Create the RSSI and signal strength variable objects for the modem and return // variable-type pointers to them Variable *modemRSSI = new Modem_RSSI(&modem, "12345678-abcd-1234-efgh-1234567890ab"); -Variable *modemSinalPct = new Modem_SignalPercent(&modem, "12345678-abcd-1234-efgh-1234567890ab"); +Variable *modemSignalPct = new Modem_SignalPercent(&modem, "12345678-abcd-1234-efgh-1234567890ab"); // ========================================================================== @@ -162,7 +159,6 @@ MaximDS18 ds18_u(OneWirePower, OneWireBus); Variable *ds18Temp = new MaximDS18_Temp(&ds18_u, "12345678-abcd-1234-efgh-1234567890ab"); - // ========================================================================== // MeaSpecMS5803 (Pressure, Temperature) // ========================================================================== @@ -171,16 +167,108 @@ Variable *ds18Temp = new MaximDS18_Temp(&ds18_u, "12345678-abcd-1234-efgh-123456 const uint8_t MS5803i2c_addr = 0x76; // The MS5803 can be addressed either as 0x76 (default) or 0x77 const int MS5803maxPressure = 14; // The maximum pressure measurable by the specific MS5803 model const uint8_t MS5803ReadingsToAvg = 1; -// Create and return the MeaSpec MS5803 sensor object +// Create and return the MeaSpec MS5803 pressure and temperature sensor object MeaSpecMS5803 ms5803(I2CPower, MS5803i2c_addr, MS5803maxPressure, MS5803ReadingsToAvg); // Create the temperature and pressure variable objects for the MS5803 and return variable pointers to them Variable *msTemp = new MeaSpecMS5803_Temp(&ms5803, "12345678-abcd-1234-efgh-1234567890ab"); Variable *msPress = new MeaSpecMS5803_Pressure(&ms5803, "12345678-abcd-1234-efgh-1234567890ab"); +// ========================================================================== +// Calculated Variables +// ========================================================================== + +// Create the function to calculate the water pressure +// Water pressure = pressure from MS5803 (water+baro) - pressure from BME280 (baro) +// The MS5803 reports pressure in millibar, the BME280 in pascal +// 1 pascal = 0.01 mbar +float calculateWaterPressure(void) +{ + float totalPressureFromMS5803 = msPress->getValue(); + float baroPressureFromBME280 = bPress->getValue(); + float waterPressure = totalPressureFromMS5803 - (baroPressureFromBME280)*0.01; + if (totalPressureFromMS5803 == -9999 || baroPressureFromBME280 == -9999) + waterPressure = -9999; + // Serial.print(F("Water pressure is ")); // for debugging + // Serial.println(waterPressure); // for debugging + return waterPressure; +} +// Properties of the calculated water pressure variable +const char *waterPressureVarName = "pressureGauge"; // This must be a value from http://vocabulary.odm2.org/variablename/ +const char *waterPressureVarUnit = "millibar"; // This must be a value from http://vocabulary.odm2.org/units/ +int waterPressureVarResolution = 3; +const char *waterPressureUUID = "12345678-abcd-1234-efgh-1234567890ab"; +const char *waterPressureVarCode = "CorrectedPressure"; +// Create the calculated water pressure variable objects and return a variable pointer to it +Variable *calcWaterPress = new Variable(calculateWaterPressure, waterPressureVarName, + waterPressureVarUnit, waterPressureVarResolution, + waterPressureUUID, waterPressureVarCode); + +// Create the function to calculate the "raw" water depth +// For this, we're using the conversion between mbar and mm pure water at 4°C +// This calculation gives a final result in mm of water +float calculateWaterDepthRaw(void) +{ + float waterDepth = calculateWaterPressure()*10.1972; + if (calculateWaterPressure() == -9999) waterDepth = -9999; + // Serial.print(F("'Raw' water depth is ")); // for debugging + // Serial.println(waterDepth); // for debugging + return waterDepth; +} +// Properties of the calculated water depth variable +const char *waterDepthVarName = "waterDepth"; // This must be a value from http://vocabulary.odm2.org/variablename/ +const char *waterDepthVarUnit = "millimeter"; // This must be a value from http://vocabulary.odm2.org/units/ +int waterDepthVarResolution = 3; +const char *waterDepthUUID = "12345678-abcd-1234-efgh-1234567890ab"; +const char *waterDepthVarCode = "CalcDepth"; +// Create the calculated raw water depth variable objects and return a variable pointer to it +Variable *calcRawDepth = new Variable(calculateWaterDepthRaw, waterDepthVarName, + waterDepthVarUnit, waterDepthVarResolution, + waterDepthUUID, waterDepthVarCode); + +// Create the function to calculate the water depth after correcting water density for temperature +// This calculation gives a final result in mm of water +float calculateWaterDepthTempCorrected(void) +{ + const float gravitationalConstant = 9.80665; // m/s2, meters per second squared + // First get water pressure in Pa for the calculation: 1 mbar = 100 Pa + float waterPressurePa = 100 * calculateWaterPressure(); + float waterTempertureC = msTemp->getValue(); + // Converting water depth for the changes of pressure with depth + // Water density (kg/m3) from equation 6 from JonesHarris1992-NIST-DensityWater.pdf + float waterDensity = + 999.84847 + + 6.337563e-2 * waterTempertureC + - 8.523829e-3 * pow(waterTempertureC,2) + + 6.943248e-5 * pow(waterTempertureC,3) + - 3.821216e-7 * pow(waterTempertureC,4) + ; + // This calculation gives a final result in mm of water + // from P = rho * g * h + float rhoDepth = 1000 * waterPressurePa/(waterDensity * gravitationalConstant); + if (calculateWaterPressure() == -9999 || waterTempertureC == -9999) + rhoDepth = -9999; + // Serial.print(F("Temperature corrected water depth is ")); // for debugging + // Serial.println(rhoDepth); // for debugging + return rhoDepth; +} +// Properties of the calculated temperature corrected water depth variable +const char *rhoDepthVarName = "waterDepth"; // This must be a value from http://vocabulary.odm2.org/variablename/ +const char *rhoDepthVarUnit = "millimeter"; // This must be a value from http://vocabulary.odm2.org/units/ +int rhoDepthVarResolution = 3; +const char *rhoDepthUUID = "12345678-abcd-1234-efgh-1234567890ab"; +const char *rhoDepthVarCode = "DensityDepth"; +// Create the temperature corrected water depth variable objects and return a variable pointer to it +Variable *calcCorrDepth = new Variable(calculateWaterDepthTempCorrected, rhoDepthVarName, + rhoDepthVarUnit, rhoDepthVarResolution, + rhoDepthUUID, rhoDepthVarCode); + + // ========================================================================== // The array that contains all variables to be logged // ========================================================================== +// Put all of the variable pointers into an Array +// NOTE: Since we've created all of the variable pointers above, we can just +// reference them by name here. Variable *variableList[] = { mayflyBatt, mayflyRAM, @@ -192,10 +280,18 @@ Variable *variableList[] = { msTemp, msPress, ds18Temp, + calcWaterPress, + calcRawDepth, + calcCorrDepth, modemRSSI, - modemSinalPct + modemSignalPct }; +// Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray varArray(variableCount, variableList); +// Create a new logger instance +LoggerEnviroDIY EnviroDIYLogger(LoggerID, loggingInterval, sdCardPin, wakePin, &varArray); // ========================================================================== @@ -206,34 +302,6 @@ const char *registrationToken = "12345678-abcd-1234-efgh-1234567890ab"; // Dev const char *samplingFeature = "12345678-abcd-1234-efgh-1234567890ab"; // Sampling feature UUID -// ========================================================================== -// Extra information and tokens for calculted variables -// ========================================================================== -const char *waterPressureUUID = "12345678-abcd-1234-efgh-1234567890ab"; -const char *waterPresureVarName = "pressureGauge"; -const char *waterPresureVarUnit = "Millibar"; -const char *waterPresureVarCode = "CorrectedPressure"; -const char *waterDepthUUID = "12345678-abcd-1234-efgh-1234567890ab"; -const char *waterDepthVarName = "waterDepth"; -const char *waterDepthVarUnit = "millimeter"; -const char *waterDepthVarCode = "CalcDepth"; -const char *rhoDepthUUID = "12345678-abcd-1234-efgh-1234567890ab"; -const char *rhoDepthVarName = "rhoDepth"; -const char *rhoDepthVarUnit = "millimeter"; -const char *rhoDepthVarCode = "DensityDepth"; - - -// ========================================================================== -// Initialize variables for density correction -// ========================================================================== -float waterPressureBar = -9999.0; -float waterPressurePa = -9999.0; -float waterTempertureC = -9999.0; -float waterDensity = -9999.0; -float rhoDepth = -9999.0; -const float gravitationalConstant = 9.80665; // m/s2, meters per second squared - - // ========================================================================== // Working Functions // ========================================================================== @@ -282,19 +350,6 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the logger - EnviroDIYLogger.init(sdCardPin, wakePin, variableCount, variableList, - loggingInterval, LoggerID); - EnviroDIYLogger.setAlertPin(greenLED); - - // Begin the real time clock - rtc.begin(); - delay(100); - - // Print out the current time - Serial.print(F("Current RTC time is: ")); - Serial.println(Logger::formatDateTime_ISO8601(Logger::getNowEpoch())); - // Setup the logger modem #if defined(TINY_GSM_MODEM_ESP8266) modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, wifiId, wifiPwd); @@ -305,224 +360,25 @@ void setup() modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, apn); #endif - // Attach the modem to the logger - EnviroDIYLogger.attachModem(&modem); - // Immediately turn on the modem - EnviroDIYLogger._logModem->modemPowerUp(); + // Attach the modem and information pins to the logger + EnviroDIYLogger.attachModem(modem); + EnviroDIYLogger.setAlertPin(greenLED); + EnviroDIYLogger.setTestingModePin(buttonPin); // Enter the tokens for the connection with EnviroDIY EnviroDIYLogger.setToken(registrationToken); EnviroDIYLogger.setSamplingFeatureUUID(samplingFeature); - // Set up the sensors - EnviroDIYLogger.setupSensors(); - - // Sync the clock with NIST - Serial.print(F("Attempting to synchronize RTC with NIST\n")); - // Connect to the network - if (EnviroDIYLogger._logModem->connectInternet()) - { - EnviroDIYLogger.syncRTClock(EnviroDIYLogger._logModem->getNISTTime()); - // Disconnect from the network - EnviroDIYLogger._logModem->disconnectInternet(); - } - // Turn off the modem - EnviroDIYLogger._logModem->modemPowerDown(); - - // Generate a logger file name from the LoggerID and the date/time on the RTC - // This will start a new file every time the logger is reset - EnviroDIYLogger.setFileName(); - - // Set up the log file and add a header to it with all of the information - // for the sensors and variables in the variable array - EnviroDIYLogger.setupLogFile(); - - // Now we're going to add another set of rows to the file header with - // just the information for the calculated variable - // We could make a "prettier" header by adding commas and values onto the - // end of each line of the multi-line header generated by the - // generateFileHeader() function, but that would take a bunch of really - // annoying string subscripting and re-combining (or just rewriting that - // whole generateFileHeader function, which is already a long annoying - // string concatenation function) so instead we're just creating another set - // of header rows for the csv with no values in the first spaces where - // header values exist and then starting the new header information shifted - // over by that many columns/commas - String extraHeaderSpacer = ""; - for (uint8_t i=0; i <= variableCount; i++) extraHeaderSpacer += ','; - // the first row is the variable UUID's, we have two: - // for calculated pressure and calculated depth - EnviroDIYLogger.logToSD(extraHeaderSpacer + String(waterPressureUUID) + - F(", ") + String(waterDepthUUID) + F(", ") + - String(rhoDepthUUID)); - // the next row is the sensor name, which is blank, so we'll put in a blank row - EnviroDIYLogger.logToSD(extraHeaderSpacer + F(", ")); - // the third row is the variable name - EnviroDIYLogger.logToSD(extraHeaderSpacer + String(waterPresureVarName) + - F(", ") + String(waterDepthVarName) + F(", ") + - String(rhoDepthVarName)); - // the fourth row is the variable unit name - EnviroDIYLogger.logToSD(extraHeaderSpacer + String(waterPresureVarUnit) + - F(", ") + String(waterDepthVarUnit) + F(", ") + - String(rhoDepthVarUnit)); - // the last row is the variable code - EnviroDIYLogger.logToSD(extraHeaderSpacer + String(waterPresureVarCode) + - F(", ") + String(waterDepthVarCode) + F(", ") + - String(rhoDepthVarCode)); - - // Setup the logger sleep mode - EnviroDIYLogger.setupSleep(); - - // Hold up for 10-seconds to allow immediate entry into sensor testing mode - // EnviroDIYLogger.checkForTestingMode(buttonPin); - - // Set up an interrupt on a pin to enter sensor testing mode at any time - pinMode(buttonPin, INPUT_PULLUP); - enableInterrupt(buttonPin, Logger::testingISR, CHANGE); - Serial.print(F("Push button on pin ")); - Serial.print(buttonPin); - Serial.println(F(" at any time to enter sensor testing mode.")); - - Serial.print(F("Logger setup finished!\n")); - Serial.print(F("------------------------------------------\n\n")); - - // Blink the LEDs really fast to show start-up is done - greenredflash(6, 25); + // Begin the logger + EnviroDIYLogger.begin(); } // ========================================================================== // Main loop function // ========================================================================== - -// Because of the way the sleep mode is set up, the processor will wake up -// and start the loop every minute exactly on the minute. void loop() { - // Assuming we were woken up by the clock, check if the current time is an - // even interval of the logging interval - if (EnviroDIYLogger.checkInterval()) - { - // Print a line to show new reading - Serial.print(F("------------------------------------------\n")); - // Turn on the LED to show we're taking a reading - digitalWrite(greenLED, HIGH); - - // Turn on the modem to let it start searching for the network - EnviroDIYLogger._logModem->modemPowerUp(); - - // Send power to all of the sensors - Serial.print(F("Powering sensors...\n")); - EnviroDIYLogger.sensorsPowerUp(); - // Wake up all of the sensors - Serial.print(F("Waking sensors...\n")); - EnviroDIYLogger.sensorsWake(); - // Update the values from all attached sensors - Serial.print(F("Updating sensor values...\n")); - EnviroDIYLogger.updateAllSensors(); - // Put sensors to sleep - Serial.print(F("Putting sensors back to sleep...\n")); - EnviroDIYLogger.sensorsSleep(); - // Cut sensor power - Serial.print(F("Cutting sensor power...\n")); - EnviroDIYLogger.sensorsPowerDown(); - - // Generate a csv with the sensor-recorded variables - String csvRaw = EnviroDIYLogger.generateSensorDataCSV(); - - // Generate a json with the sensor-recorded variables - String jsonRaw = EnviroDIYLogger.generateSensorDataJSON(); - - // Calculate the water pressure after barometic correction - // As long as the seensor updates took less than two minutes, these - // getValue() functions will return the values from the last update - // that happened in the updateAllSensors() function - // The MS5803 reports pressure in millibar, the BME280 in pascal - // 1 pascal = 0.01 mbar - // This calculation gives a final result in mbar - Serial.print(F("Calculating water pressure...\n")); - float waterPressure = msPress->getValue() - (bPress->getValue())*0.01; - float waterTempertureC = msTemp->getValue(); - // Now calculate the depth of the water - // For this, we're using the conversion between mbar and mm pure water at 4°C - // This calculation gives a final result in mm of water - float waterDepth = waterPressure*10.1972; - // Converting water depth for the changes of pressure with depth - // First get water pressure in Pa for the calculation: 1 mbar = 100 Pa - waterPressurePa = 100 * waterPressure; - // Water density (kg/m3) from equation 6 from JonesHarris1992-NIST-DensityWater.pdf - waterDensity = + 999.84847 - + 6.337563e-2 * waterTempertureC - - 8.523829e-3 * pow(waterTempertureC,2) - + 6.943248e-5 * pow(waterTempertureC,3) - - 3.821216e-7 * pow(waterTempertureC,4) - ; - // This calculation gives a final result in mm of water - rhoDepth = 1000 * waterPressurePa/(waterDensity * gravitationalConstant); // from P = rho * g * h - - - // Add the water pressure and depth to the csv string - String csvToGo = csvRaw + ","; - csvToGo += String(waterPressure); - csvToGo += ","; - csvToGo += String(waterDepth); - csvToGo += ","; - csvToGo += String(rhoDepth); - - // Add the total water pressure to the raw json - // Figure out how long the original json is - int jsonLength = jsonRaw.length(); - // Crop off the last ' }' from the json - String jsonToGo = jsonRaw.substring(0,jsonLength-1); - // add the UUID for the water pressure - jsonToGo += F(", \""); - jsonToGo += String(waterPressureUUID); - jsonToGo += F("\": "); - // add the water pressure value - jsonToGo += String(waterPressure); - // add the UUID for the water depth - jsonToGo += F(", \""); - jsonToGo += String(waterDepthUUID); - jsonToGo += F("\": "); - // add the water depth value - jsonToGo += String(waterDepth); - // add the UUID for the density corrected water depth - jsonToGo += F(", \""); - jsonToGo += String(rhoDepthUUID); - jsonToGo += F("\": "); - // add the density corrected water depth value - jsonToGo += String(rhoDepth); - // re-add the last '}' - jsonToGo += F("}"); - - // Connect to the network - Serial.print(F("Connecting to the internet...\n")); - if (EnviroDIYLogger._logModem->connectInternet()) - { - // Post the data to the WebSDL - EnviroDIYLogger.postDataEnviroDIY(jsonToGo); - - // Disconnect from the network - EnviroDIYLogger._logModem->disconnectInternet(); - } - // Turn the modem off - EnviroDIYLogger._logModem->modemPowerDown(); - - // Create a csv data record and save it to the log file - EnviroDIYLogger.logToSD(csvToGo); - - // Turn off the LED - digitalWrite(greenLED, LOW); - // Print a line to show reading ended - Serial.print(F("------------------------------------------\n\n")); - } - - // Check if it was instead the testing interrupt that woke us up - // NOTE: This testing mode will *NOT* show the values from any of the extra - // calculated variables added in the loop. It will only show the variables - // listed in the variableList which is fed to the EnviroDIYLogger. - if (Logger::startTesting) EnviroDIYLogger.testingMode(); - - // Sleep - EnviroDIYLogger.systemSleep(); + // Log the data + EnviroDIYLogger.log(); } diff --git a/examples/baro_rho_correction/platformio.ini b/examples/baro_rho_correction/platformio.ini index 098134fd0..d8c8b1204 100644 --- a/examples/baro_rho_correction/platformio.ini +++ b/examples/baro_rho_correction/platformio.ini @@ -19,6 +19,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/data_saving/data_saving.ino b/examples/data_saving/data_saving.ino index 186f6c7f8..c6fae0733 100644 --- a/examples/data_saving/data_saving.ino +++ b/examples/data_saving/data_saving.ino @@ -31,22 +31,16 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "data_saving.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 = 5; // Your logger's timezone. const int8_t timeZone = -5; -// Create TWO new logger instances -// one is a simple logger with all variables -// one is an enviroDIY logger with an abbreviated list of variables -LoggerEnviroDIY loggerComplete; -LoggerEnviroDIY loggerToGo; // ========================================================================== @@ -65,7 +59,7 @@ const int8_t sdCardPin = 12; // SD Card Chip Select/Slave Select Pin (must be d // Create and return the processor "sensor" const char *MFVersion = "v0.5"; -ProcessorStats mayfly(MFVersion) ; +ProcessorStats mayfly(MFVersion); // Create the battery voltage and free RAM variable objects for the Y504 and return variable-type pointers to them Variable *mayflyBatt = new ProcessorStats_Batt(&mayfly, "12345678-abcd-1234-efgh-1234567890ab"); Variable *mayflyRAM = new ProcessorStats_FreeRam(&mayfly, "12345678-abcd-1234-efgh-1234567890ab"); @@ -203,6 +197,10 @@ Variable *y520Temp = new YosemitechY520_Temp(&y520, "12345678-abcd-1234-efgh-123 // ========================================================================== // The array that contains all variables to be logged // ========================================================================== + +// Put all of the variable pointers into an Array +// NOTE: Since we've created all of the variable pointers above, we can just +// reference them by name here. Variable *variableList_complete[] = { mayflyBatt, mayflyRAM, @@ -219,12 +217,21 @@ Variable *variableList_complete[] = { modemRSSI, modemSinalPct }; +// Count up the number of pointers in the array int variableCount_complete = sizeof(variableList_complete) / sizeof(variableList_complete[0]); +// Create the VariableArray object +VariableArray arrayComplete(variableCount_complete, variableList_complete); +// Create the new logger instance +LoggerEnviroDIY loggerComplete(LoggerID, loggingInterval, sdCardPin, wakePin, &arrayComplete); // ========================================================================== // The array that contains all variables to have their values sent out over the internet // ========================================================================== + +// Put all of the variable pointers into an Array +// NOTE: Since we've created all of the variable pointers above, we can just +// reference them by name here. Variable *variableList_toGo[] = { y504DOmgL, y504Temp, @@ -233,7 +240,12 @@ Variable *variableList_toGo[] = { y520Cond, modemRSSI }; +// Count up the number of pointers in the array int variableCount_toGo = sizeof(variableList_toGo) / sizeof(variableList_toGo[0]); +// Create the VariableArray object +VariableArray arrayToGo(variableCount_toGo, variableList_toGo); +// Create the new logger instance +LoggerEnviroDIY loggerToGo(LoggerID, loggingInterval,sdCardPin, wakePin, &arrayToGo); // ========================================================================== @@ -295,14 +307,6 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the two logger instances - loggerComplete.init(sdCardPin, wakePin, variableCount_complete, variableList_complete, - loggingInterval, LoggerID); - loggerToGo.init(sdCardPin, wakePin, variableCount_toGo, variableList_toGo, - loggingInterval, LoggerID); - // There is no reason to call the setAlertPin() function, because we have to - // write the loop on our own. - // Setup the logger modem #if defined(TINY_GSM_MODEM_ESP8266) modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, wifiId, wifiPwd); @@ -316,8 +320,11 @@ void setup() // Attach the same modem to both loggers // It is only needed for the logger that will be sending out data, but // attaching it to both allows either logger to control NIST synchronization - loggerComplete.attachModem(&modem); - loggerToGo.attachModem(&modem); + loggerComplete.attachModem(modem); + loggerToGo.attachModem(modem); + loggerComplete.setTestingModePin(buttonPin); + // There is no reason to call the setAlertPin() function, because we have to + // write the loop on our own. // Set up the connection information with EnviroDIY for both loggers // Doing this for both loggers ensures that the header of the csv will have the tokens in it @@ -331,19 +338,6 @@ void setup() // and all of the sensors. We don't need to bother with the "begin" for the // other logger because it has the same processor and clock. loggerComplete.begin(); - - // Hold up for 10-seconds to allow immediate entry into sensor testing mode - // loggerComplete.checkForTestingMode(buttonPin); - - // Set up an interrupt on a pin to enter sensor testing mode at any time - pinMode(buttonPin, INPUT_PULLUP); - enableInterrupt(buttonPin, Logger::testingISR, CHANGE); - Serial.print(F("Push button on pin ")); - Serial.print(buttonPin); - Serial.println(F(" at any time to enter sensor testing mode.")); - - // Blink the LEDs really fast to show start-up is done - greenredflash(6, 25); } @@ -372,29 +366,30 @@ void loop() // we will explicitly start and end the serial connection in the loop. modbusSerial.begin(9600); - // Send power to all of the sensors + // Send power to all of the sensors (do this directly on the VariableArray) Serial.print(F("Powering sensors...\n")); - loggerComplete.sensorsPowerUp(); - // Wake up all of the sensors + arrayComplete.sensorsPowerUp(); + // Wake up all of the sensors (do this directly on the VariableArray) Serial.print(F("Waking sensors...\n")); - loggerComplete.sensorsWake(); - // Update the values from all attached sensors + arrayComplete.sensorsWake(); + // Update the values from all attached sensors (do this directly on the VariableArray) Serial.print(F("Updating sensor values...\n")); - loggerComplete.updateAllSensors(); - // Put sensors to sleep + arrayComplete.updateAllSensors(); + // Put sensors to sleep (do this directly on the VariableArray) Serial.print(F("Putting sensors back to sleep...\n")); - loggerComplete.sensorsSleep(); - // Cut sensor power + arrayComplete.sensorsSleep(); + // Cut sensor power (do this directly on the VariableArray) Serial.print(F("Cutting sensor power...\n")); - loggerComplete.sensorsPowerDown(); + arrayComplete.sensorsPowerDown(); // End the stream for the modbus sensors // Because RS485 adapters tend to "steal" current from the data pins - // we will explicitly start and end the serial connection in the loop. + // we will explicitly start and end the serial connection in the loop. modbusSerial.end(); - // Create a csv data record and save it to the log file - loggerComplete.logToSD(loggerComplete.generateSensorDataCSV()); + // Stream the variable results from the complete set of variables to + // the SD card + loggerComplete.logToSD(); // Connect to the network Serial.print(F("Connecting to the internet...\n")); diff --git a/examples/data_saving/platformio.ini b/examples/data_saving/platformio.ini index 07ba525c6..4545b5e47 100644 --- a/examples/data_saving/platformio.ini +++ b/examples/data_saving/platformio.ini @@ -18,6 +18,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/double_logger/double_logger.ino b/examples/double_logger/double_logger.ino index 7ce048dc0..31028a667 100644 --- a/examples/double_logger/double_logger.ino +++ b/examples/double_logger/double_logger.ino @@ -33,20 +33,17 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "logger_test.ino"; - -// Logger ID, also becomes the prefix for the name of the data file on SD card +// Logger ID - since it's the same logger device, we only need one const char *LoggerID = "XXXXX"; -const char *FileName5min = "SL099_5MinuteInterval.csv"; -const char *FileName1min = "SL099_1MinuteInterval.csv"; +// The TWO filenames for the different logging intervals +const char *FileName5min = "Logger_5MinuteInterval.csv"; +const char *FileName1min = "Logger_1MinuteInterval.csv"; // Your logger's timezone. const int8_t timeZone = -5; -// Create TWO new logger instances -Logger logger1min; -Logger logger5min; // ========================================================================== @@ -54,7 +51,7 @@ Logger logger5min; // ========================================================================== #include -const long serialBaud = 57600; // Baud rate for the primary serial port for debugging +const long serialBaud = 115200; // Baud rate for the primary serial port for debugging const int8_t greenLED = 8; // Pin for the green LED (-1 if unconnected) const int8_t redLED = 9; // Pin for the red LED (-1 if unconnected) const int8_t buttonPin = 21; // Pin for a button to use to enter debugging mode (-1 if unconnected) @@ -106,8 +103,10 @@ ModemSleepType ModemSleepMode = modem_sleep_held; // How the modem is put to sl // Use "modem_always_on" if you do not want the library to control the modem power and sleep or if none of the above apply. #endif -const char *wifiId = "XXXXXXX"; // The WiFi access point -const char *wifiPwd = "XXXXXXX"; // The password for connecting to WiFi +const char *apn = "xxxxx"; // The APN for the gprs connection, unnecessary for WiFi +const char *wifiId = "xxxxx"; // The WiFi access point, unnecessary for gprs +const char *wifiPwd = "xxxxx"; // The password for connecting to WiFi, unnecessary for gprs + // Create the loggerModem instance // A "loggerModem" is a combination of a TinyGSM Modem, a TinyGSM Client, and an on/off method loggerModem modem; @@ -133,20 +132,34 @@ AOSongAM2315 am2315(I2CPower); // ========================================================================== // The two arrays that contains the variables for the different intervals // ========================================================================== +// Create pointers for all of the variables from the sensors recording at 1 +// minute intervals and at the same time putting them into an array Variable *variableList_at1min[] = { new AOSongAM2315_Humidity(&am2315), new AOSongAM2315_Temp(&am2315) // new YOUR_variableName_HERE(&) }; +// Count up the number of pointers in the 1-minute array int variableCount1min = sizeof(variableList_at1min) / sizeof(variableList_at1min[0]); +// Create the 1-minute VariableArray object +VariableArray array1min(variableCount1min, variableList_at1min); +// Create the 1-minute logger instance +Logger logger1min(LoggerID, 1, sdCardPin, wakePin, &array1min); + +// Create pointers for all of the variables from the sensors recording at 5 +// minute intervals and at the same time putting them into an array Variable *variableList_at5min[] = { new MaximDS3231_Temp(&ds3231), new ProcessorStats_Batt(&mayfly), new ProcessorStats_FreeRam(&mayfly) // new YOUR_variableName_HERE(&) }; +// Count up the number of pointers in the 5-minute array int variableCount5min = sizeof(variableList_at5min) / sizeof(variableList_at5min[0]); - +// Create the 5-minute VariableArray object +VariableArray array5min(variableCount5min, variableList_at5min); +// Create the 1-minute logger instance +Logger logger5min(LoggerID, 5, sdCardPin, wakePin, &array5min); // ========================================================================== @@ -201,23 +214,15 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the two logger instances - logger1min.init(sdCardPin, wakePin, variableCount1min, variableList_at1min, - 1, LoggerID); - logger5min.init(sdCardPin, wakePin, variableCount5min, variableList_at5min, - 5, LoggerID); - // There is no reason to call the setAlertPin() function, because we have to - // write the loop on our own. - // Setup the logger modem modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, wifiId, wifiPwd); // Turn on the modem modem.modemPowerUp(); - // Set up the sensors on both loggers - logger1min.setupSensors(); - logger5min.setupSensors(); + // Set up the sensors (do this directly on the VariableArray) + array1min.setupSensors(); + array5min.setupSensors(); // Print out the current time Serial.print(F("Current RTC time is: ")); @@ -240,9 +245,12 @@ void setup() logger1min.setFileName(FileName1min); logger5min.setFileName(FileName5min); - // Setup the logger files. This will automatically add headers to each - logger1min.setupLogFile(); - logger5min.setupLogFile(); + // Setup the logger files. Specifying true will put a default header at + // on to the file when it's created. + // Because we've already called setFileName, we do not need to specify the + // file name for this function. + logger1min.createLogFile(true); + logger5min.createLogFile(true); // Set up the processor sleep mode // Because there's only one processor, we only need to do this once @@ -251,6 +259,10 @@ void setup() Serial.println(F("Logger setup finished!\n")); Serial.println(F("------------------------------------------")); Serial.println(); + + // Call the processor sleep + // Only need to do this for one of the loggers + logger1min.systemSleep(); } @@ -271,24 +283,24 @@ void loop() // Turn on the LED to show we're taking a reading digitalWrite(greenLED, HIGH); - // Send power to all of the sensors + // Send power to all of the sensors (do this directly on the VariableArray) Serial.print(F("Powering sensors...\n")); - logger1min.sensorsPowerUp(); - // Wake up all of the sensors + array1min.sensorsPowerUp(); + // Wake up all of the sensors (do this directly on the VariableArray) Serial.print(F("Waking sensors...\n")); - logger1min.sensorsWake(); - // Update the values from all attached sensors + array1min.sensorsWake(); + // Update the values from all attached sensors (do this directly on the VariableArray) Serial.print(F("Updating sensor values...\n")); - logger1min.updateAllSensors(); - // Put sensors to sleep + array1min.updateAllSensors(); + // Put sensors to sleep (do this directly on the VariableArray) Serial.print(F("Putting sensors back to sleep...\n")); - logger1min.sensorsSleep(); - // Cut sensor power + array1min.sensorsSleep(); + // Cut sensor power (do this directly on the VariableArray) Serial.print(F("Cutting sensor power...\n")); - logger1min.sensorsPowerDown(); + array1min.sensorsPowerDown(); - // Create a csv data record and save it to the log file - logger1min.logToSD(logger1min.generateSensorDataCSV()); + // Stream the csv data to the SD card + logger1min.logToSD(); // Turn off the LED digitalWrite(greenLED, LOW); @@ -304,24 +316,24 @@ void loop() // Turn on the LED to show we're taking a reading digitalWrite(redLED, HIGH); - // Send power to all of the sensors + // Send power to all of the sensors (do this directly on the VariableArray) Serial.print(F("Powering sensors...\n")); - logger5min.sensorsPowerUp(); - // Wake up all of the sensors + array1min.sensorsPowerUp(); + // Wake up all of the sensors (do this directly on the VariableArray) Serial.print(F("Waking sensors...\n")); - logger5min.sensorsWake(); - // Update the values from all attached sensors + array1min.sensorsWake(); + // Update the values from all attached sensors (do this directly on the VariableArray) Serial.print(F("Updating sensor values...\n")); - logger5min.updateAllSensors(); - // Put sensors to sleep + array1min.updateAllSensors(); + // Put sensors to sleep (do this directly on the VariableArray) Serial.print(F("Putting sensors back to sleep...\n")); - logger5min.sensorsSleep(); - // Cut sensor power + array1min.sensorsSleep(); + // Cut sensor power (do this directly on the VariableArray) Serial.print(F("Cutting sensor power...\n")); - logger5min.sensorsPowerDown(); + array1min.sensorsPowerDown(); - // Create a csv data record and save it to the log file - logger5min.logToSD(logger5min.generateSensorDataCSV()); + // Stream the csv data to the SD card + logger5min.logToSD(); // Turn off the LED digitalWrite(redLED, LOW); diff --git a/examples/double_logger/platformio.ini b/examples/double_logger/platformio.ini index 85e4a7ff0..7b6d19415 100644 --- a/examples/double_logger/platformio.ini +++ b/examples/double_logger/platformio.ini @@ -18,6 +18,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/logging_to_EnviroDIY/logging_to_EnviroDIY.ino b/examples/logging_to_EnviroDIY/logging_to_EnviroDIY.ino index 56f09dbaa..f3e4a56fa 100644 --- a/examples/logging_to_EnviroDIY/logging_to_EnviroDIY.ino +++ b/examples/logging_to_EnviroDIY/logging_to_EnviroDIY.ino @@ -31,19 +31,16 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "logging_to_EnviroDIY.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 = 5; // Your logger's timezone. const int8_t timeZone = -5; -// Create a new logger instance -LoggerEnviroDIY EnviroDIYLogger; // ========================================================================== @@ -51,7 +48,7 @@ LoggerEnviroDIY EnviroDIYLogger; // ========================================================================== #include -const long serialBaud = 57600; // Baud rate for the primary serial port for debugging +const long serialBaud = 115200; // Baud rate for the primary serial port for debugging const int8_t greenLED = 8; // Pin for the green LED (-1 if unconnected) const int8_t redLED = 9; // Pin for the red LED (-1 if unconnected) const int8_t buttonPin = 21; // Pin for a button to use to enter debugging mode (-1 if unconnected) @@ -85,7 +82,7 @@ const int8_t modemVCCPin = -1; // Modem power pin, if it can be turned on or of ModemSleepType ModemSleepMode = modem_always_on; // How the modem is put to sleep #elif defined(TINY_GSM_MODEM_UBLOX) -const long ModemBaud = 9600; +const long ModemBaud = 9600; // SARA-U201 default seems to be 9600 const int8_t modemSleepRqPin = 23; // Modem SleepRq Pin (for sleep requests) (-1 if unconnected) const int8_t modemStatusPin = 19; // Modem Status Pin (indicates power status) (-1 if unconnected) const int8_t modemVCCPin = -1; // Modem power pin, if it can be turned on or off (-1 if unconnected) @@ -248,10 +245,18 @@ ExternalVoltage extvolt(VoltPower, VoltData, VoltGain, Volt_ADS1115Address, Volt // Neither hardware serial nor AltSoftSerial require any modifications to // deal with interrupt conflicts. -#include // for the stream communication const int SonarData = 11; // data receive pin + +#include // for the stream communication SoftwareSerial_ExtInts sonarSerial(SonarData, -1); // No Tx pin is required, only Rx +// #include // for the stream communication +// NeoSWSerial sonarSerial(SonarData, -1); // No Tx pin is required, only Rx +// void NeoSWSISR() +// { +// NeoSWSerial::rxISR( *portInputRegister( digitalPinToPort( SonarData ) ) ); +// } + #include const int8_t SonarPower = 22; // Excite (power) pin (-1 if unconnected) const int8_t Sonar1Trigger = A1; // Trigger pin (a negative number if unconnected) (A1 = 25) @@ -279,7 +284,7 @@ MaximDS18 ds18_2(OneWireAddress2, OneWirePower, OneWireBus); MaximDS18 ds18_3(OneWireAddress3, OneWirePower, OneWireBus); MaximDS18 ds18_4(OneWireAddress4, OneWirePower, OneWireBus); MaximDS18 ds18_5(OneWireAddress5, OneWirePower, OneWireBus); -// Create and return the Maxim DS18 sensor object (use this form for a single sensor on bus with an unknown/unassigned address) +// Create and return the Maxim DS18 sensor object (use this form for a single sensor on bus with an unknown address) // MaximDS18 ds18_u(OneWirePower, OneWireBus); @@ -429,6 +434,8 @@ ZebraTechDOpto dopto(*DOptoDI12address, SDI12Power, SDI12Data); // ========================================================================== // The array that contains all variables to be logged // ========================================================================== +// Create pointers for all of the variables from the sensors +// at the same time putting them into an array Variable *variableList[] = { new ApogeeSQ212_PAR(&SQ212, "12345678-abcd-1234-efgh-1234567890ab"), new AOSongAM2315_Humidity(&am2315, "12345678-abcd-1234-efgh-1234567890ab"), @@ -458,7 +465,7 @@ Variable *variableList[] = { new MaximDS18_Temp(&ds18_3, "12345678-abcd-1234-efgh-1234567890ab"), new MaximDS18_Temp(&ds18_4, "12345678-abcd-1234-efgh-1234567890ab"), new MaximDS18_Temp(&ds18_5, "12345678-abcd-1234-efgh-1234567890ab"), - new MaximDS18_Temp(&ds18_u, "12345678-abcd-1234-efgh-1234567890ab"), + // new MaximDS18_Temp(&ds18_u, "12345678-abcd-1234-efgh-1234567890ab"), new MeaSpecMS5803_Temp(&ms5803, "12345678-abcd-1234-efgh-1234567890ab"), new MeaSpecMS5803_Pressure(&ms5803, "12345678-abcd-1234-efgh-1234567890ab"), new MPL115A2_Temp(&mpl115a2, "12345678-abcd-1234-efgh-1234567890ab"), @@ -500,7 +507,12 @@ Variable *variableList[] = { new Modem_SignalPercent(&modem, "12345678-abcd-1234-efgh-1234567890ab"), // new YOUR_variableName_HERE(&) }; +// Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray varArray(variableCount, variableList); +// Create a new logger instance +LoggerEnviroDIY EnviroDIYLogger(LoggerID, loggingInterval, sdCardPin, wakePin, &varArray); // ========================================================================== @@ -548,7 +560,10 @@ void setup() sonarSerial.begin(9600); // Allow interrupts for software serial #if defined SoftwareSerial_ExtInts_h - enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); + enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); + #endif + #if defined NeoSWSerial_h + enableInterrupt(SonarData, NeoSWSISR, CHANGE); #endif // Set up pins for the LED's @@ -569,11 +584,6 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the logger - EnviroDIYLogger.init(sdCardPin, wakePin, variableCount, variableList, - loggingInterval, LoggerID); - EnviroDIYLogger.setAlertPin(greenLED); - // Setup the logger modem #if defined(TINY_GSM_MODEM_ESP8266) modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, wifiId, wifiPwd); @@ -584,33 +594,17 @@ void setup() modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, apn); #endif - // Attach the modem to the logger - EnviroDIYLogger.attachModem(&modem); + // Attach the modem and information pins to the logger + EnviroDIYLogger.attachModem(modem); + EnviroDIYLogger.setAlertPin(greenLED); + EnviroDIYLogger.setTestingModePin(buttonPin); // Enter the tokens for the connection with EnviroDIY EnviroDIYLogger.setToken(registrationToken); EnviroDIYLogger.setSamplingFeatureUUID(samplingFeature); - // Set up the connection with DreamHost - #ifdef DreamHostPortalRX - EnviroDIYLogger.setDreamHostPortalRX(DreamHostPortalRX); - #endif - // Begin the logger EnviroDIYLogger.begin(); - - // Hold up for 10-seconds to allow immediate entry into sensor testing mode - // EnviroDIYLogger.checkForTestingMode(buttonPin); - - // Set up an interrupt on a pin to enter sensor testing mode at any time - pinMode(buttonPin, INPUT_PULLUP); - enableInterrupt(buttonPin, Logger::testingISR, CHANGE); - Serial.print(F("Push button on pin ")); - Serial.print(buttonPin); - Serial.println(F(" at any time to enter sensor testing mode.")); - - // Blink the LEDs really fast to show start-up is done - greenredflash(6, 25); } diff --git a/examples/logging_to_EnviroDIY/platformio.ini b/examples/logging_to_EnviroDIY/platformio.ini index f8def144a..5b9f5a1b6 100644 --- a/examples/logging_to_EnviroDIY/platformio.ini +++ b/examples/logging_to_EnviroDIY/platformio.ini @@ -18,6 +18,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/logging_to_EnviroDIY_Zero/logging_to_EnviroDIY_Zero.ino b/examples/logging_to_EnviroDIY_Zero/logging_to_EnviroDIY_Zero.ino index c5abfedcc..400d4a676 100644 --- a/examples/logging_to_EnviroDIY_Zero/logging_to_EnviroDIY_Zero.ino +++ b/examples/logging_to_EnviroDIY_Zero/logging_to_EnviroDIY_Zero.ino @@ -32,19 +32,16 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "logging_to_EnviroDIY_Zero.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 = 5; // Your logger's timezone. const int8_t timeZone = -5; -// Create a new logger instance -LoggerEnviroDIY EnviroDIYLogger; // ========================================================================== @@ -52,7 +49,7 @@ LoggerEnviroDIY EnviroDIYLogger; // ========================================================================== #include -const long serialBaud = 57600; // Baud rate for the primary serial port for debugging +const long serialBaud = 115200; // Baud rate for the primary serial port for debugging const int8_t greenLED = 8; // Pin for the green LED (-1 if unconnected) const int8_t redLED = 13; // Pin for the red LED (-1 if unconnected) const int8_t buttonPin = 21; // Pin for a button to use to enter debugging mode (-1 if unconnected) @@ -280,8 +277,8 @@ MaximDS18 ds18_2(OneWireAddress2, OneWirePower, OneWireBus); MaximDS18 ds18_3(OneWireAddress3, OneWirePower, OneWireBus); MaximDS18 ds18_4(OneWireAddress4, OneWirePower, OneWireBus); MaximDS18 ds18_5(OneWireAddress5, OneWirePower, OneWireBus); -// Create and return the Maxim DS18 sensor object (use this form for a single sensor on bus with an unknow address) -// MaximDS18 ds18_5(OneWirePower, OneWireBus); +// Create and return the Maxim DS18 sensor object (use this form for a single sensor on bus with an unknown address) +// MaximDS18 ds18_u(OneWirePower, OneWireBus); // ========================================================================== @@ -437,9 +434,12 @@ const char *DOptoDI12address = "5"; // The SDI-12 Address of the Zebra Tech D-O // Create and return the Zebra Tech DOpto dissolved oxygen sensor object ZebraTechDOpto dopto(*DOptoDI12address, SDI12Power, SDI12Data); + // ========================================================================== // The array that contains all variables to be logged // ========================================================================== +// Create pointers for all of the variables from the sensors +// at the same time putting them into an array Variable *variableList[] = { new ApogeeSQ212_PAR(&SQ212, "12345678-abcd-1234-efgh-1234567890ab"), new AOSongAM2315_Humidity(&am2315, "12345678-abcd-1234-efgh-1234567890ab"), @@ -469,6 +469,7 @@ Variable *variableList[] = { new MaximDS18_Temp(&ds18_3, "12345678-abcd-1234-efgh-1234567890ab"), new MaximDS18_Temp(&ds18_4, "12345678-abcd-1234-efgh-1234567890ab"), new MaximDS18_Temp(&ds18_5, "12345678-abcd-1234-efgh-1234567890ab"), + // new MaximDS18_Temp(&ds18_u, "12345678-abcd-1234-efgh-1234567890ab"), new MeaSpecMS5803_Temp(&ms5803, "12345678-abcd-1234-efgh-1234567890ab"), new MeaSpecMS5803_Pressure(&ms5803, "12345678-abcd-1234-efgh-1234567890ab"), new MPL115A2_Temp(&mpl115a2, "12345678-abcd-1234-efgh-1234567890ab"), @@ -510,7 +511,12 @@ Variable *variableList[] = { new Modem_SignalPercent(&modem, "12345678-abcd-1234-efgh-1234567890ab"), // new YOUR_variableName_HERE(&) }; +// Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray varArray(variableCount, variableList); +// Create a new logger instance +LoggerEnviroDIY EnviroDIYLogger(LoggerID, loggingInterval, sdCardPin, wakePin, &varArray); // ========================================================================== @@ -581,11 +587,6 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the logger - EnviroDIYLogger.init(sdCardPin, wakePin, variableCount, variableList, - loggingInterval, LoggerID); - EnviroDIYLogger.setAlertPin(greenLED); - // Setup the logger modem #if defined(TINY_GSM_MODEM_ESP8266) modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, wifiId, wifiPwd); @@ -596,18 +597,15 @@ void setup() modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, apn); #endif - // Attach the modem to the logger - EnviroDIYLogger.attachModem(&modem); + // Attach the modem and information pins to the logger + EnviroDIYLogger.attachModem(modem); + EnviroDIYLogger.setAlertPin(greenLED); + // No button on the feather, so we're not attaching a testingMode pin // Enter the tokens for the connection with EnviroDIY EnviroDIYLogger.setToken(registrationToken); EnviroDIYLogger.setSamplingFeatureUUID(samplingFeature); - // Set up the connection with DreamHost - #ifdef DreamHostPortalRX - EnviroDIYLogger.setDreamHostPortalRX(DreamHostPortalRX); - #endif - // Begin the logger EnviroDIYLogger.begin(); } diff --git a/examples/logging_to_EnviroDIY_Zero/platformio.ini b/examples/logging_to_EnviroDIY_Zero/platformio.ini index 3e5ea3686..f95780907 100644 --- a/examples/logging_to_EnviroDIY_Zero/platformio.ini +++ b/examples/logging_to_EnviroDIY_Zero/platformio.ini @@ -18,5 +18,5 @@ framework = arduino lib_ldf_mode = deep lib_ignore = SoftwareSerial_ExtInts, AltSoftSerial lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 RTCZero diff --git a/examples/multisensor_print/multisensor_print.ino b/examples/multisensor_print/multisensor_print.ino index 24fb07b0a..fd76776af 100644 --- a/examples/multisensor_print/multisensor_print.ino +++ b/examples/multisensor_print/multisensor_print.ino @@ -14,9 +14,6 @@ DISCLAIMER: THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. *****************************************************************************/ -// Some define statements -#define STANDARD_SERIAL_OUTPUT Serial // Without this there will be no output - // ========================================================================== // Include the base required libraries // ========================================================================== @@ -26,30 +23,22 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. #include -// ========================================================================== -// Basic Logger Settings -// ========================================================================== // The name of this file const char *sketchName = "modular_sensors.ino"; -// Your logger's timezone. -const int8_t timeZone = -5; -// Create a new sensor array instance -VariableArray sensors; - // ========================================================================== // Primary Arduino-Based Board and Processor // ========================================================================== #include -const long serialBaud = 57600; // Baud rate for the primary serial port for debugging +const long serialBaud = 115200; // Baud rate for the primary serial port for debugging const int8_t greenLED = 8; // Pin for the green LED (-1 if unconnected) const int8_t redLED = 9; // Pin for the red LED (-1 if unconnected) // Create and return the processor "sensor" const char *MFVersion = "v0.5"; -ProcessorStats mayfly(MFVersion) ; +ProcessorStats mayfly(MFVersion); // ========================================================================== @@ -188,10 +177,18 @@ ExternalVoltage extvolt(VoltPower, VoltData, VoltGain, Volt_ADS1115Address, Volt // Neither hardware serial nor AltSoftSerial require any modifications to // deal with interrupt conflicts. -#include // for the stream communication const int SonarData = 11; // data receive pin + +#include // for the stream communication SoftwareSerial_ExtInts sonarSerial(SonarData, -1); // No Tx pin is required, only Rx +// #include // for the stream communication +// NeoSWSerial sonarSerial(SonarData, -1); // No Tx pin is required, only Rx +// void NeoSWSISR() +// { +// NeoSWSerial::rxISR( *portInputRegister( digitalPinToPort( SonarData ) ) ); +// } + #include const int8_t SonarPower = 22; // Excite (power) pin (-1 if unconnected) const int8_t Sonar1Trigger = A1; // Trigger pin (a negative number if unconnected) (A1 = 25) @@ -369,6 +366,8 @@ ZebraTechDOpto dopto(*DOptoDI12address, SDI12Power, SDI12Data); // ========================================================================== // The array that contains all variables to be logged // ========================================================================== +// Create pointers for all of the variables from the sensors +// at the same time putting them into an array Variable *variableList[] = { new ApogeeSQ212_PAR(&SQ212), new AOSongAM2315_Humidity(&am2315), @@ -437,7 +436,10 @@ Variable *variableList[] = { new MaximDS3231_Temp(&ds3231), // new YOUR_variableName_HERE(&) }; +// Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray sensors(variableCount, variableList); // ========================================================================== @@ -458,6 +460,9 @@ void greenredflash(int numFlash = 4, int rate = 75) digitalWrite(redLED, LOW); } +// The clock's timezone. +const int8_t timeZone = -5; + // Helper function to get the current date/time from the RTC // as a unix timestamp - and apply the correct time zone. long currentepochtime = 0; @@ -518,7 +523,10 @@ void setup() sonarSerial.begin(9600); // Allow interrupts for software serial #if defined SoftwareSerial_ExtInts_h - enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); + enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); + #endif + #if defined NeoSWSerial_h + enableInterrupt(SonarData, NeoSWSISR, CHANGE); #endif // Start the Real Time Clock @@ -540,9 +548,6 @@ void setup() Serial.print(String(variableCount)); Serial.println(F(" variables to be recorded.")); - // Initialize the sensor array; - sensors.init(variableCount, variableList); - // Set up all the sensors sensors.setupSensors(); @@ -585,8 +590,6 @@ void loop() Serial.print(F("Updated all sensors at ")); Serial.println(getDateTime_ISO8601()); sensors.printSensorData(&Serial); - Serial.print(F("In CSV Format: ")); - Serial.println(sensors.generateSensorDataCSV()); // Turn off the LED to show we're done with the reading digitalWrite(greenLED, LOW); // Print a to close it off diff --git a/examples/multisensor_print/platformio.ini b/examples/multisensor_print/platformio.ini index c016437ce..2b8f5c68b 100644 --- a/examples/multisensor_print/platformio.ini +++ b/examples/multisensor_print/platformio.ini @@ -18,6 +18,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/simple_logging/platformio.ini b/examples/simple_logging/platformio.ini index 1b7832563..345a84468 100644 --- a/examples/simple_logging/platformio.ini +++ b/examples/simple_logging/platformio.ini @@ -18,6 +18,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/simple_logging/simple_logging.ino b/examples/simple_logging/simple_logging.ino index 5941b7ba4..99e0393c4 100644 --- a/examples/simple_logging/simple_logging.ino +++ b/examples/simple_logging/simple_logging.ino @@ -22,19 +22,16 @@ THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "simple_logging.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 = 5; // Your logger's timezone. const int8_t timeZone = -5; -// Create a new logger instance -Logger logger; // ========================================================================== @@ -42,7 +39,7 @@ Logger logger; // ========================================================================== #include -const long serialBaud = 57600; // Baud rate for the primary serial port for debugging +const long serialBaud = 115200; // Baud rate for the primary serial port for debugging const int8_t greenLED = 8; // Pin for the green LED (-1 if unconnected) const int8_t redLED = 9; // Pin for the red LED (-1 if unconnected) const int8_t buttonPin = 21; // Pin for a button to use to enter debugging mode (-1 if unconnected) @@ -192,10 +189,18 @@ ExternalVoltage extvolt(VoltPower, VoltData, VoltGain, Volt_ADS1115Address, Volt // Neither hardware serial nor AltSoftSerial require any modifications to // deal with interrupt conflicts. -#include // for the stream communication const int SonarData = 11; // data receive pin + +#include // for the stream communication SoftwareSerial_ExtInts sonarSerial(SonarData, -1); // No Tx pin is required, only Rx +// #include // for the stream communication +// NeoSWSerial sonarSerial(SonarData, -1); // No Tx pin is required, only Rx +// void NeoSWSISR() +// { +// NeoSWSerial::rxISR( *portInputRegister( digitalPinToPort( SonarData ) ) ); +// } + #include const int8_t SonarPower = 22; // Excite (power) pin (-1 if unconnected) const int8_t Sonar1Trigger = A1; // Trigger pin (a negative number if unconnected) (A1 = 25) @@ -373,6 +378,8 @@ ZebraTechDOpto dopto(*DOptoDI12address, SDI12Power, SDI12Data); // ========================================================================== // The array that contains all variables to be logged // ========================================================================== +// Create pointers for all of the variables from the sensors +// at the same time putting them into an array Variable *variableList[] = { new ApogeeSQ212_PAR(&SQ212), new AOSongAM2315_Humidity(&am2315), @@ -441,7 +448,12 @@ Variable *variableList[] = { new MaximDS3231_Temp(&ds3231), // new YOUR_variableName_HERE(&) }; +// Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray varArray(variableCount, variableList); +// Create a new logger instance +Logger logger(LoggerID, loggingInterval, sdCardPin, wakePin, &varArray); // ========================================================================== @@ -478,7 +490,10 @@ void setup() sonarSerial.begin(9600); // Allow interrupts for software serial #if defined SoftwareSerial_ExtInts_h - enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); + enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); + #endif + #if defined NeoSWSerial_h + enableInterrupt(SonarData, NeoSWSISR, CHANGE); #endif // Set up pins for the LED's @@ -499,24 +514,12 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the logger - logger.init(sdCardPin, wakePin, variableCount, variableList, - loggingInterval, LoggerID); + // Set information pins logger.setAlertPin(greenLED); + logger.setTestingModePin(buttonPin); // Begin the logger logger.begin(); - - // Hold up for 10-seconds to allow immediate entry into sensor testing mode - // EnviroDIYLogger.checkForTestingMode(buttonPin); - - // Set up an interrupt on a pin to enter sensor testing mode at any time - pinMode(buttonPin, INPUT_PULLUP); - enableInterrupt(buttonPin, Logger::testingISR, CHANGE); - Serial.print(F("Push button on pin ")); - Serial.print(buttonPin); - Serial.println(F(" at any time to enter sensor testing mode.")); - } diff --git a/examples/single_sensor/ReadMe.md b/examples/single_sensor/ReadMe.md index d55f1ce26..2ec3ceffc 100644 --- a/examples/single_sensor/ReadMe.md +++ b/examples/single_sensor/ReadMe.md @@ -1,3 +1,3 @@ # Example using the Modular Sensors Library to Communicate with a Single Sensor -This somewhat trivial example show making use of the unified set of commands to print data from a MaxBotix ultrasonic range finder to the serial port. It is somewhat complicated by showing the defining and selection of a HardwareSerial port for a SAMD21 board and the selection of a SoftwareSerial port for a AVR board. +This somewhat trivial example show making use of the unified set of commands to print data from a MaxBotix ultrasonic range finder to the serial port. It also shows creating a calculated variable which is the water depth. It is somewhat complicated by showing the defining and selection of a HardwareSerial port for a SAMD21 board and the selection of a SoftwareSerial port for a AVR board. diff --git a/examples/single_sensor/platformio.ini b/examples/single_sensor/platformio.ini index 4be9a7f9b..57f40f03f 100644 --- a/examples/single_sensor/platformio.ini +++ b/examples/single_sensor/platformio.ini @@ -18,6 +18,6 @@ framework = arduino lib_ldf_mode = deep lib_ignore = RTCZero lib_deps = - EnviroDIY_ModularSensors@>=0.11.6 + EnviroDIY_ModularSensors@>=0.12.2 https://github.com/PaulStoffregen/AltSoftSerial.git https://github.com/EnviroDIY/SoftwaterSerial_ExternalInts.git diff --git a/examples/single_sensor/single_sensor.ino b/examples/single_sensor/single_sensor.ino index e766aeb78..25eb91d4a 100644 --- a/examples/single_sensor/single_sensor.ino +++ b/examples/single_sensor/single_sensor.ino @@ -45,10 +45,24 @@ const int SonarData = 11; // data pin const int SonarTrigger = -1; // Trigger pin const int8_t SonarPower = 22; // excite (power) pin + #if defined __AVR__ + #include // for the stream communication SoftwareSerial_ExtInts sonarSerial(SonarData, -1); // No Tx pin is required, only Rx -#elif defined __SAMD21G18A__ + +// #include // for the stream communication +// NeoSWSerial sonarSerial(SonarData, -1); // No Tx pin is required, only Rx +// void NeoSWSISR() +// { +// NeoSWSerial::rxISR( *portInputRegister( digitalPinToPort( SonarData ) ) ); +// } + +#endif + + + +#if defined __SAMD21G18A__ #include "wiring_private.h" // pinPeripheral() function Uart Serial3(&sercom2, 5, 2, SERCOM_RX_PAD_3, UART_TX_PAD_2); void SERCOM2_Handler() @@ -56,8 +70,6 @@ void SERCOM2_Handler() Serial3.IrqHandler(); } HardwareSerial &sonarSerial = Serial3; -#else -HardwareSerial &sonarSerial = Serial1; #endif // Create a new instance of the sonar sensor; @@ -66,6 +78,20 @@ MaxBotixSonar sonar(sonarSerial, SonarPower, SonarTrigger) ; // Create a new instance of the range variable; MaxBotixSonar_Range sonar_range(&sonar); +// Create a function to calculate the water depth from the sonar range +// For this example, we'll assume that the sonar is mounted 5m above the stream bottom +float calcDepth(void) +{ + float mountHeight = 5000; + float sonarRange = sonar_range.getValue(); + return mountHeight - sonarRange; +} +// Create a calculated variable for the water depth +// Variable calcVar(functionName, VariableName, VariableUnit, Resolution, UUID, Code); +// VariableName must be a value from http://vocabulary.odm2.org/variablename/ +// VariableUnit must be a value from http://vocabulary.odm2.org/units/ +Variable waterDepth(calcDepth, "waterDepth", "millimeter", 0, "", "sonarDepth"); + // ========================================================================== // Board setup info // ========================================================================== @@ -96,10 +122,15 @@ void setup() Serial.begin(serialBaud); // Start the stream for the sonar sonarSerial.begin(9600); + // Allow interrupts for software serial #if defined SoftwareSerial_ExtInts_h enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); #endif + #if defined NeoSWSerial_h + enableInterrupt(SonarData, NeoSWSISR, CHANGE); + #endif + #if defined __SAMD21G18A__ // Assign pins to SERCOM functionality pinPeripheral(2, PIO_SERCOM); @@ -141,6 +172,8 @@ void loop() // Print the sonar result Serial.print("Current sonar range: "); Serial.println(sonar_range.getValueString()); + Serial.print("Calculated water depth: "); + Serial.println(waterDepth.getValueString()); // Put the sensor back to sleep sonar.sleep(); diff --git a/library.json b/library.json index 9ac12538b..4266ad7f2 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "EnviroDIY_ModularSensors", - "version": "0.11.7", + "version": "0.12.2", "description": "A library that allows access to multiple sensors through a unified interface. This allows the user to simply access many sensors to log the data or send the data to data repositories like the EnviroDIY data portal.", "keywords": "modular, sensor, sensors, datalogger, logger, low power, sleeping, EnviroDIY, ModularSensors, Mayfly, WikiWatershed", "platforms": "atmelavr, atmelsam", @@ -195,7 +195,7 @@ { "name": "Arduino-SDI-12_ExtInts", "version": "https://github.com/EnviroDIY/Arduino-SDI-12.git#ExtInts", - "version_note": ">=1.3.1", + "version_note": ">=1.3.2", "note": "EnviroDIY SDI-12 Library, dependent on external library for interrupts", "authors": [ "Kevin M. Smith", diff --git a/library.properties b/library.properties index a0ed99256..9f1d8447b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=EnviroDIY_ModularSensors -version=0.11.7 +version=0.12.2 author=Sara Damiano , Shannon Hicks maintainer=Sara Damiano sentence=A library that allows access to multiple sensors through a unified interface. diff --git a/sensor_tests/AnthonyTest2/AnthonyTest2.ino b/sensor_tests/AnthonyTest2/AnthonyTest2.ino index 829572338..64c452446 100644 --- a/sensor_tests/AnthonyTest2/AnthonyTest2.ino +++ b/sensor_tests/AnthonyTest2/AnthonyTest2.ino @@ -36,19 +36,16 @@ https://github.com/EnviroDIY/ModularSensors/commit/7d0d15ae5bc6dddf13adbd735032e // ========================================================================== -// Basic Logger Settings +// Data Logger Settings // ========================================================================== // The name of this file const char *sketchName = "AnthonyTest2.ino"; - // Logger ID, also becomes the prefix for the name of the data file on SD card const char *LoggerID = "AnthonyTest2"; // How frequently (in minutes) to log data const uint8_t loggingInterval = 10; // Your logger's timezone. const int8_t timeZone = -6; // Central Standard Time (CST=-6) -// Create a new logger instance -LoggerEnviroDIY EnviroDIYLogger; // ========================================================================== @@ -65,7 +62,7 @@ const int8_t wakePin = A7; // Interrupt/Alarm pin to wake from sleep // In a SAMD system where you are using the built-in rtc, set wakePin to 1 const int8_t sdCardPin = 12; // SD Card Chip Select/Slave Select Pin (must be defined!) -// Create the processor "sensor" +// Create and return the processor "sensor" const char *MFVersion = "v0.5b"; ProcessorStats mayfly(MFVersion) ; @@ -76,35 +73,36 @@ ProcessorStats mayfly(MFVersion) ; HardwareSerial &ModemSerial = Serial1; // The serial port for the modem - software serial can also be used. #if defined(TINY_GSM_MODEM_XBEE) +const long ModemBaud = 9600; // Default for XBee is 9600, I've sped mine up to 57600 const int8_t modemSleepRqPin = 23; // Modem SleepRq Pin (for sleep requests) (-1 if unconnected) const int8_t modemStatusPin = 19; // Modem Status Pin (indicates power status) (-1 if unconnected) const int8_t modemVCCPin = -1; // Modem power pin, if it can be turned on or off (-1 if unconnected) ModemSleepType ModemSleepMode = modem_sleep_reverse; // How the modem is put to sleep #elif defined(TINY_GSM_MODEM_ESP8266) +const long ModemBaud = 57600; // Default for ESP8266 is 115200, but the Mayfly itself stutters above 57600 const int8_t modemSleepRqPin = 19; // Modem SleepRq Pin (for sleep requests) (-1 if unconnected) const int8_t modemStatusPin = -1; // Modem Status Pin (indicates power status) (-1 if unconnected) const int8_t modemVCCPin = -1; // Modem power pin, if it can be turned on or off (-1 if unconnected) ModemSleepType ModemSleepMode = modem_always_on; // How the modem is put to sleep +#elif defined(TINY_GSM_MODEM_UBLOX) +const long ModemBaud = 9600; // SARA-U201 default seems to be 9600 +const int8_t modemSleepRqPin = 23; // Modem SleepRq Pin (for sleep requests) (-1 if unconnected) +const int8_t modemStatusPin = 19; // Modem Status Pin (indicates power status) (-1 if unconnected) +const int8_t modemVCCPin = -1; // Modem power pin, if it can be turned on or off (-1 if unconnected) +ModemSleepType ModemSleepMode = modem_sleep_held; // How the modem is put to sleep + #else +const long ModemBaud = 9600; // SIM800 auto-detects, but I've had trouble making it fast (19200 works) const int8_t modemSleepRqPin = 23; // Modem SleepRq Pin (for sleep requests) (-1 if unconnected) const int8_t modemStatusPin = 19; // Modem Status Pin (indicates power status) (-1 if unconnected) const int8_t modemVCCPin = -1; // Modem power pin, if it can be turned on or off (-1 if unconnected) ModemSleepType ModemSleepMode = modem_sleep_held; // How the modem is put to sleep -#endif // Use "modem_sleep_held" if the DTR pin is held HIGH to keep the modem awake, as with a Sodaq GPRSBee rev6. // Use "modem_sleep_pulsed" if the DTR pin is pulsed high and then low to wake the modem up, as with an Adafruit Fona or Sodaq GPRSBee rev4. // Use "modem_sleep_reverse" if the DTR pin is held LOW to keep the modem awake, as with all XBees. // Use "modem_always_on" if you do not want the library to control the modem power and sleep or if none of the above apply. -#if defined(TINY_GSM_MODEM_ESP8266) -const long ModemBaud = 9600; // Default for ESP8266 is 115200, but the Mayfly itself stutters above 57600 -#elif defined(TINY_GSM_MODEM_SIM800) -const long ModemBaud = 9600; // SIM800 auto-detects, but I've had trouble making it fast (19200 works) -#elif defined(TINY_GSM_MODEM_XBEE) -const long ModemBaud = 9600; // Default for XBee is 9600, I've sped mine up to 57600 -#else -const long ModemBaud = 9600; // Modem baud rate #endif const char *apn = "apn.konekt.io"; // The APN for the gprs connection, unnecessary for WiFi @@ -120,14 +118,17 @@ loggerModem modem; // Maxim DS3231 RTC (Real Time Clock) // ========================================================================== #include +// Create and return the DS3231 sensor object MaximDS3231 ds3231(1); + /**** // ========================================================================== // AOSong AM2315 Digital Humidity and Temperature Sensor // ========================================================================== #include const int8_t I2CPower = 22; // Pin to switch power on and off (-1 if unconnected) +// Create and return the AOSong AM2315 sensor object AOSongAM2315 am2315(I2CPower); @@ -138,6 +139,7 @@ AOSongAM2315 am2315(I2CPower); const int8_t DHTPower = 22; // Pin to switch power on and off (-1 if unconnected) const int8_t DHTPin = 10; // DHT data pin DHTtype dhtType = DHT11; // DHT type, either DHT11, DHT21, or DHT22 +// Create and return the AOSong DHT sensor object AOSongDHT dht(DHTPower, DHTPin, dhtType); @@ -148,17 +150,21 @@ AOSongDHT dht(DHTPower, DHTPin, dhtType); const int8_t SQ212Power = 22; // Pin to switch power on and off (-1 if unconnected) const int8_t SQ212Data = 2; // The data pin ON THE ADS1115 (NOT the Arduino Pin Number) const uint8_t SQ212_ADS1115Address = 0x48; // The I2C address of the ADS1115 ADC +// Create and return the Apogee SQ212 sensor object ApogeeSQ212 SQ212(SQ212Power, SQ212Data); ***/ + // ========================================================================== // Bosch BME280 Environmental Sensor (Temperature, Humidity, Pressure) // ========================================================================== #include uint8_t BMEi2c_addr = 0x77; // The BME280 can be addressed either as 0x76 or 0x77 const int8_t I2CPower = 22; // Pin to switch power on and off (-1 if unconnected) +// Create and return the Bosch BME280 sensor object BoschBME280 bme280(I2CPower, BMEi2c_addr); + /*** // ========================================================================== // CAMPBELL OBS 3 / OBS 3+ Analog Turbidity Sensor @@ -172,12 +178,14 @@ const int8_t OBSLowPin = 0; // The low voltage analog pin ON THE ADS1115 (NOT t const float OBSLow_A = 4.0749E+00; // The "A" value (X^2) from the low range calibration const float OBSLow_B = 9.1011E+01; // The "B" value (X) from the low range calibration const float OBSLow_C = -3.9570E-01; // The "C" value from the low range calibration +// Create and return the Campbell OBS3+ LOW RANGE sensor object CampbellOBS3 osb3low(OBS3Power, OBSLowPin, OBSLow_A, OBSLow_B, OBSLow_C, OBS3_ADS1115Address, OBS3numberReadings); // Campbell OBS 3+ High Range calibration in Volts const int8_t OBSHighPin = 1; // The high voltage analog pin ON THE ADS1115 (NOT the Arduino Pin Number) const float OBSHigh_A = 5.2996E+01; // The "A" value (X^2) from the high range calibration const float OBSHigh_B = 3.7828E+02; // The "B" value (X) from the high range calibration const float OBSHigh_C = -1.3927E+00; // The "C" value from the high range calibration +// Create and return the Campbell OBS3+ HIGH RANGE sensor object CampbellOBS3 osb3high(OBS3Power, OBSHighPin, OBSHigh_A, OBSHigh_B, OBSHigh_C, OBS3_ADS1115Address, OBS3numberReadings); @@ -188,6 +196,7 @@ CampbellOBS3 osb3high(OBS3Power, OBSHighPin, OBSHigh_A, OBSHigh_B, OBSHigh_C, OB const char *TMSDI12address = "2"; // The SDI-12 Address of the 5-TM const int8_t SDI12Data = 7; // The pin the 5TM is attached to const int8_t SDI12Power = 22; // Pin to switch power on and off (-1 if unconnected) +// Create and return the Decagon 5TM sensor object Decagon5TM fivetm(*TMSDI12address, SDI12Power, SDI12Data); @@ -199,6 +208,7 @@ 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 SDI12Data = 7; // The pin the CTD is attached to // const int8_t SDI12Power = 22; // Pin to switch power on and off (-1 if unconnected) +// Create and return the Decagon CTD sensor object DecagonCTD ctd(*CTDSDI12address, SDI12Power, SDI12Data, CTDnumberReadings); @@ -210,9 +220,11 @@ const char *ES2SDI12address = "3"; // The SDI-12 Address of the ES2 // const int8_t SDI12Data = 7; // The pin the ES2 is attached to // const int8_t SDI12Power = 22; // Pin to switch power on and off (-1 if unconnected) const uint8_t ES2NumberReadings = 3; +// Create and return the Decagon ES2 sensor object DecagonES2 es2(*ES2SDI12address, SDI12Power, SDI12Data, ES2NumberReadings); ***/ + // ========================================================================== // External Voltage via TI ADS1115 // ========================================================================== @@ -222,16 +234,14 @@ const int8_t VoltData = 0; // The data pin ON THE ADS1115 (NOT the Arduino Pin const float VoltGain = 10; // Default 1/gain for grove voltage divider is 10x const uint8_t Volt_ADS1115Address = 0x48; // The I2C address of the ADS1115 ADC const uint8_t VoltReadsToAvg = 1; // Only read one sample +// Create and return the External Voltage sensor object ExternalVoltage extvolt(VoltPower, VoltData, VoltGain, Volt_ADS1115Address, VoltReadsToAvg); + /*** // ========================================================================== // Maxbotix HRXL Ultrasonic Range Finder // ========================================================================== -#include -const int8_t SonarPower = 22; // Excite (power) pin (-1 if unconnected) -const int8_t Sonar1Trigger = A1; // Trigger pin (a negative number if unconnected) -const int8_t Sonar2Trigger = A2; // Trigger pin (a negative number if unconnected) // Set up a serial port for receiving sonar data - in this case, using software serial // Because the standard software serial library uses interrupts that conflict @@ -245,11 +255,23 @@ const int8_t Sonar2Trigger = A2; // Trigger pin (a negative number if unconnect // Neither hardware serial nor AltSoftSerial require any modifications to // deal with interrupt conflicts. -#include // for the stream communication const int SonarData = 11; // data receive pin + +#include // for the stream communication SoftwareSerial_ExtInts sonarSerial(SonarData, -1); // No Tx pin is required, only Rx -// Now actually creating the sensor object +// #include // for the stream communication +// NeoSWSerial sonarSerial(SonarData, -1); // No Tx pin is required, only Rx +// void NeoSWSISR() +// { +// NeoSWSerial::rxISR( *portInputRegister( digitalPinToPort( SonarData ) ) ); +// } + +#include +const int8_t SonarPower = 22; // Excite (power) pin (-1 if unconnected) +const int8_t Sonar1Trigger = A1; // Trigger pin (a negative number if unconnected) (A1 = 25) +const int8_t Sonar2Trigger = A2; // Trigger pin (a negative number if unconnected) (A2 = 26) +// Create and return the MaxBotix Sonar sensor object MaxBotixSonar sonar1(sonarSerial, SonarPower, Sonar1Trigger) ; MaxBotixSonar sonar2(sonarSerial, SonarPower, Sonar2Trigger) ; @@ -266,11 +288,13 @@ DeviceAddress OneWireAddress4 = {0x28, 0xFF, 0xB6, 0x6E, 0x84, 0x16, 0x05, 0x9B} DeviceAddress OneWireAddress5 = {0x28, 0xFF, 0x3B, 0x07, 0x82, 0x16, 0x03, 0xB3}; const int8_t OneWireBus = A0; // Pin attached to the OneWire Bus (-1 if unconnected) const int8_t OneWirePower = 22; // Pin to switch power on and off (-1 if unconnected) +// Create and return the Maxim DS18 sensor objects (use this form for a known address) MaximDS18 ds18_1(OneWireAddress1, OneWirePower, OneWireBus); MaximDS18 ds18_2(OneWireAddress2, OneWirePower, OneWireBus); MaximDS18 ds18_3(OneWireAddress3, OneWirePower, OneWireBus); MaximDS18 ds18_4(OneWireAddress4, OneWirePower, OneWireBus); MaximDS18 ds18_5(OneWireAddress5, OneWirePower, OneWireBus); +// Create and return the Maxim DS18 sensor object (use this form for a single sensor on bus with an unknow address) // MaximDS18 ds18_5(OneWirePower, OneWireBus); @@ -282,6 +306,7 @@ MaximDS18 ds18_5(OneWireAddress5, OneWirePower, OneWireBus); const uint8_t MS5803i2c_addr = 0x76; // The MS5803 can be addressed either as 0x76 or 0x77 const int MS5803maxPressure = 14; // The maximum pressure measurable by the specific MS5803 model const uint8_t MS5803ReadingsToAvg = 1; +// Create and return the MeaSpec MS5803 pressure and temperature sensor object MeaSpecMS5803 ms5803(I2CPower, MS5803i2c_addr, MS5803maxPressure, MS5803ReadingsToAvg); @@ -291,15 +316,18 @@ MeaSpecMS5803 ms5803(I2CPower, MS5803i2c_addr, MS5803maxPressure, MS5803Readings #include // const int8_t I2CPower = 22; // Pin to switch power on and off (-1 if unconnected) const uint8_t MPL115A2ReadingsToAvg = 1; +// Create and return the MPL115A2 barometer sensor object MPL115A2 mpl115a2(I2CPower, MPL115A2ReadingsToAvg); ***/ + // ========================================================================== // External I2C Rain Tipping Bucket Counter // ========================================================================== #include const uint8_t RainCounterI2CAddress = 0x08; // I2C Address for external tip counter const float depthPerTipEvent = 0.2; // rain depth in mm per tip event +// Create and return the Rain Counter sensor object RainCounterI2C tip(RainCounterI2CAddress, depthPerTipEvent); @@ -315,17 +343,20 @@ byte acculevelModbusAddress = 0x01; // The modbus address of KellerAcculevel const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) const uint8_t acculevelNumberReadings = 5; // The manufacturer recommends taking and averaging a few readings +// Create and return the Keller Acculevel sensor object KellerAcculevel acculevel(acculevelModbusAddress, modbusSerial, modbusPower, max485EnablePin, acculevelNumberReadings); + /*** // ========================================================================== // Yosemitech Y504 Dissolved Oxygen Sensor // ========================================================================== #include byte y504modbusAddress = 0x04; // The modbus address of the Y504 -const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) -const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) +// const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) +// const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) const uint8_t y504NumberReadings = 5; // The manufacturer recommends averaging 10 readings, but we take 5 to minimize power consumption +// Create and return the Yosemitech Y504 dissolved oxygen sensor object YosemitechY504 y504(y504modbusAddress, modbusSerial, modbusPower, max485EnablePin, y504NumberReadings); @@ -337,6 +368,7 @@ byte y510modbusAddress = 0x0B; // The modbus address of the Y510 // const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) // const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) const uint8_t y510NumberReadings = 5; // The manufacturer recommends averaging 10 readings, but we take 5 to minimize power consumption +// Create and return the Y510-B Turbidity sensor object YosemitechY510 y510(y510modbusAddress, modbusSerial, modbusPower, max485EnablePin, y510NumberReadings); @@ -348,6 +380,7 @@ byte y511modbusAddress = 0x1A; // The modbus address of the Y511 // const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) // const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) const uint8_t y511NumberReadings = 5; // The manufacturer recommends averaging 10 readings, but we take 5 to minimize power consumption +// Create and return the Y511-A Turbidity sensor object YosemitechY511 y511(y511modbusAddress, modbusSerial, modbusPower, max485EnablePin, y511NumberReadings); @@ -359,6 +392,7 @@ byte y514modbusAddress = 0x14; // The modbus address of the Y514 // const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) // const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) const uint8_t y514NumberReadings = 5; // The manufacturer recommends averaging 10 readings, but we take 5 to minimize power consumption +// Create and return the Y514 chlorophyll sensor object YosemitechY514 y514(y514modbusAddress, modbusSerial, modbusPower, max485EnablePin, y514NumberReadings); @@ -370,6 +404,7 @@ byte y520modbusAddress = 0x20; // The modbus address of the Y520 // const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) // const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) const uint8_t y520NumberReadings = 5; // The manufacturer recommends averaging 10 readings, but we take 5 to minimize power consumption +// Create and return the Y520 conductivity sensor object YosemitechY520 y520(y520modbusAddress, modbusSerial, modbusPower, max485EnablePin, y520NumberReadings); @@ -381,9 +416,11 @@ byte y532modbusAddress = 0x32; // The modbus address of the Y532 // const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) // const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) const uint8_t y532NumberReadings = 1; // The manufacturer actually doesn't mention averaging for this one +// Create and return the Yosemitech Y532 pH sensor object YosemitechY532 y532(y532modbusAddress, modbusSerial, modbusPower, max485EnablePin, y532NumberReadings); ***/ + // ========================================================================== // Yosemitech Y4000 Multiparameter Sonde (DOmgL, Turbidity, Cond, pH, Temp, ORP, Chlorophyll, BGA) // ========================================================================== @@ -392,8 +429,10 @@ byte y4000modbusAddress = 0x05; // The modbus address of the Y4000 // const int8_t modbusPower = 22; // Pin to switch power on and off (-1 if unconnected) // const int8_t max485EnablePin = -1; // Pin connected to the RE/DE on the 485 chip (-1 if unconnected) const uint8_t y4000NumberReadings = 3; // The manufacturer recommends averaging 10 readings, but we take 5 to minimize power consumption +// Create and return the Yosemitech Y4000 multi-parameter sensor object YosemitechY4000 y4000(y4000modbusAddress, modbusSerial, modbusPower, max485EnablePin, y4000NumberReadings); + /*** // ========================================================================== // Zebra Tech D-Opto Dissolved Oxygen Sensor @@ -402,12 +441,16 @@ YosemitechY4000 y4000(y4000modbusAddress, modbusSerial, modbusPower, max485Enabl const char *DOptoDI12address = "5"; // The SDI-12 Address of the Zebra Tech D-Opto // const int8_t SDI12Data = 7; // The pin the D-Opto is attached to // const int8_t SDI12Power = 22; // Pin to switch power on and off (-1 if unconnected) +// Create and return the Zebra Tech DOpto dissolved oxygen sensor object ZebraTechDOpto dopto(*DOptoDI12address, SDI12Power, SDI12Data); ***/ + // ========================================================================== // The array that contains all variables to be logged // ========================================================================== +// Create pointers for all of the variables from the sensors +// at the same time putting them into an array Variable *variableList[] = { // new ApogeeSQ212_PAR(&SQ212, "12345678-abcd-1234-efgh-1234567890ab"), // new AOSongAM2315_Humidity(&am2315, "12345678-abcd-1234-efgh-1234567890ab"), @@ -478,7 +521,12 @@ Variable *variableList[] = { // new Modem_SignalPercent(&modem, "12345678-abcd-1234-efgh-1234567890ab"), // new YOUR_variableName_HERE(&) }; +// Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); +// Create the VariableArray object +VariableArray varArray(variableCount, variableList); +// Create a new logger instance +LoggerEnviroDIY EnviroDIYLogger(LoggerID, loggingInterval, sdCardPin, wakePin, &varArray); // ========================================================================== @@ -526,7 +574,10 @@ void setup() // sonarSerial.begin(9600); // // Allow interrupts for software serial // #if defined SoftwareSerial_ExtInts_h - // enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); + // enableInterrupt(SonarData, SoftwareSerial_ExtInts::handle_interrupt, CHANGE); + // #endif + // #if defined NeoSWSerial_h + // enableInterrupt(SonarData, NeoSWSISR, CHANGE); // #endif // Set up pins for the LED's @@ -547,11 +598,6 @@ void setup() // Offset is the same as the time zone because the RTC is in UTC Logger::setTZOffset(timeZone); - // Initialize the logger - EnviroDIYLogger.init(sdCardPin, wakePin, variableCount, variableList, - loggingInterval, LoggerID); - EnviroDIYLogger.setAlertPin(greenLED); - // Setup the logger modem #if defined(TINY_GSM_MODEM_ESP8266) modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, wifiId, wifiPwd); @@ -562,33 +608,17 @@ void setup() modem.setupModem(&ModemSerial, modemVCCPin, modemStatusPin, modemSleepRqPin, ModemSleepMode, apn); #endif - // Attach the modem to the logger - EnviroDIYLogger.attachModem(&modem); + // Attach the modem and information pins to the logger + EnviroDIYLogger.attachModem(modem); + EnviroDIYLogger.setAlertPin(greenLED); + EnviroDIYLogger.setTestingModePin(buttonPin); - // Set up the connection with EnviroDIY + // Enter the tokens for the connection with EnviroDIY EnviroDIYLogger.setToken(registrationToken); EnviroDIYLogger.setSamplingFeatureUUID(samplingFeature); - // Set up the connection with DreamHost - #ifdef DreamHostPortalRX - EnviroDIYLogger.setDreamHostPortalRX(DreamHostPortalRX); - #endif - // Begin the logger EnviroDIYLogger.begin(); - - // Hold up for 10-seconds to allow immediate entry into sensor testing mode - // EnviroDIYLogger.checkForTestingMode(buttonPin); - - // Set up an interrupt on a pin to enter sensor testing mode at any time - pinMode(buttonPin, INPUT_PULLUP); - enableInterrupt(buttonPin, Logger::testingISR, CHANGE); - Serial.print(F("Push button on pin ")); - Serial.print(buttonPin); - Serial.println(F(" at any time to enter sensor testing mode.")); - - // Blink the LEDs really fast to show start-up is done - greenredflash(6, 25); } diff --git a/sensor_tests/bme280timingTest/bme280timingTest.ino b/sensor_tests/bme280timingTest/bme280timingTest.ino new file mode 100644 index 000000000..120f9485b --- /dev/null +++ b/sensor_tests/bme280timingTest/bme280timingTest.ino @@ -0,0 +1,90 @@ +/*************************************************************************** + This is a library for the BME280 humidity, temperature & pressure sensor + + Designed specifically to work with the Adafruit BME280 Breakout + ----> http://www.adafruit.com/products/2650 + + These sensors use I2C or SPI to communicate, 2 or 4 pins are required + to interface. The device's I2C address is either 0x76 or 0x77. + + Adafruit invests time and resources providing this open source code, + please support Adafruit andopen-source hardware by purchasing products + from Adafruit! + + Written by Limor Fried & Kevin Townsend for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ***************************************************************************/ + +#include +#include +#include +#include + +// #define BME_SCK 13 +// #define BME_MISO 12 +// #define BME_MOSI 11 +// #define BME_CS 10 + +uint8_t BMEi2c_addr = 0x76; // The BME280 can be addressed either as 0x76 or 0x77 +const int8_t I2CPower = 22; // Pin to switch power on and off (-1 if unconnected) + + + +#define SEALEVELPRESSURE_HPA (1013.25) + +Adafruit_BME280 bme; // I2C +//Adafruit_BME280 bme(BME_CS); // hardware SPI +//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI + +unsigned long delayTime; + +void setup() { + Serial.begin(57600); + Serial.println(F("BME280 test")); + + // Turn on switched power + pinMode(I2CPower, OUTPUT); + digitalWrite(I2CPower, HIGH); + + bool status; + + // default settings + // (you can also pass in a Wire library object like &Wire2) + status = bme.begin(BMEi2c_addr); + if (!status) { + Serial.println("Could not find a valid BME280 sensor, check wiring!"); + while (1); + } + + Serial.println("-- Timing Test --"); + delayTime = 1100; + + Serial.println(); + + // Print table headers + Serial.println(" Time, Temp, Humid, Press, Alt"); + Serial.println(" ms, *C, %, Pa, m"); + +} + + +void loop() { + + for (int i=0; i <= 30; i++) + { + Serial.print(" "); + Serial.print(millis()); + Serial.print(", "); + Serial.print(bme.readTemperature()); + Serial.print(", "); + Serial.print(bme.readHumidity()); + Serial.print(", "); + Serial.print(bme.readPressure()); + Serial.print(", "); + Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA)); + Serial.println(); + delay(delayTime); + } + + delay(100000); +} diff --git a/src/AOSongAM2315.cpp b/src/AOSongAM2315.cpp index a1168d3f9..c7d012d74 100644 --- a/src/AOSongAM2315.cpp +++ b/src/AOSongAM2315.cpp @@ -27,7 +27,7 @@ // The constructor - because this is I2C, only need the power pin // This sensor has a set I2C address of 0XB8 AOSongAM2315::AOSongAM2315(int8_t powerPin, uint8_t measurementsToAverage) - : Sensor(F("AOSongAM2315"), AM2315_NUM_VARIABLES, + : Sensor("AOSongAM2315", AM2315_NUM_VARIABLES, AM2315_WARM_UP_TIME_MS, AM2315_STABILIZATION_TIME_MS, AM2315_MEASUREMENT_TIME_MS, powerPin, -1, measurementsToAverage) {} diff --git a/src/AOSongAM2315.h b/src/AOSongAM2315.h index fdbd80dc6..c5623d2af 100644 --- a/src/AOSongAM2315.h +++ b/src/AOSongAM2315.h @@ -65,11 +65,11 @@ class AOSongAM2315_Humidity : public Variable { public: AOSongAM2315_Humidity(Sensor *parentSense, - String UUID = "", String customVarCode = "") : + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, AM2315_HUMIDITY_VAR_NUM, - F("relativeHumidity"), F("percent"), + "relativeHumidity", "percent", AM2315_HUMIDITY_RESOLUTION, - F("AM2315Humidity"), UUID, customVarCode) + "AM2315Humidity", UUID, customVarCode) {} }; @@ -79,11 +79,11 @@ class AOSongAM2315_Temp : public Variable { public: AOSongAM2315_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") : + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, AM2315_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", AM2315_TEMP_RESOLUTION, - F("AM2315Temp"), UUID, customVarCode) + "AM2315Temp", UUID, customVarCode) {} }; diff --git a/src/AOSongDHT.cpp b/src/AOSongDHT.cpp index 4d69c9a2c..3d8ccaf05 100644 --- a/src/AOSongDHT.cpp +++ b/src/AOSongDHT.cpp @@ -28,7 +28,7 @@ // The constructor - need the power pin, data pin, and type of DHT AOSongDHT::AOSongDHT(int8_t powerPin, int8_t dataPin, DHTtype type, uint8_t measurementsToAverage) - : Sensor(F("AOSongDHT"), DHT_NUM_VARIABLES, + : Sensor("AOSongDHT", DHT_NUM_VARIABLES, DHT_WARM_UP_TIME_MS, DHT_STABILIZATION_TIME_MS, DHT_MEASUREMENT_TIME_MS, powerPin, dataPin, measurementsToAverage), dht_internal(dataPin, type) diff --git a/src/AOSongDHT.h b/src/AOSongDHT.h index a693e86ae..ef740855c 100644 --- a/src/AOSongDHT.h +++ b/src/AOSongDHT.h @@ -90,11 +90,11 @@ class AOSongDHT_Humidity : public Variable { public: AOSongDHT_Humidity(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, DHT_HUMIDITY_VAR_NUM, - F("relativeHumidity"), F("percent"), + "relativeHumidity", "percent", DHT_HUMIDITY_RESOLUTION, - F("DHTHumidity"), UUID, customVarCode) + "DHTHumidity", UUID, customVarCode) {} }; @@ -104,11 +104,11 @@ class AOSongDHT_Temp : public Variable { public: AOSongDHT_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, DHT_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", DHT_TEMP_RESOLUTION, - F("DHTTemp"), UUID, customVarCode) + "DHTTemp", UUID, customVarCode) {} }; @@ -118,11 +118,11 @@ class AOSongDHT_HI : public Variable { public: AOSongDHT_HI(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, DHT_HI_VAR_NUM, - F("heatIndex"), F("degreeCelsius"), + "heatIndex", "degreeCelsius", DHT_HI_RESOLUTION, - F("DHTHI"), UUID, customVarCode) + "DHTHI", UUID, customVarCode) {} }; diff --git a/src/ApogeeSQ212.cpp b/src/ApogeeSQ212.cpp index 36f151057..37d95d296 100644 --- a/src/ApogeeSQ212.cpp +++ b/src/ApogeeSQ212.cpp @@ -36,7 +36,7 @@ // The constructor - need the power pin and the data pin ApogeeSQ212::ApogeeSQ212(int8_t powerPin, int8_t dataPin, uint8_t i2cAddress, uint8_t measurementsToAverage) - : Sensor(F("ApogeeSQ212"), SQ212_NUM_VARIABLES, + : Sensor("ApogeeSQ212", SQ212_NUM_VARIABLES, SQ212_WARM_UP_TIME_MS, SQ212_STABILIZATION_TIME_MS, SQ212_MEASUREMENT_TIME_MS, powerPin, dataPin, measurementsToAverage) { @@ -48,7 +48,7 @@ String ApogeeSQ212::getSensorLocation(void) { String sensorLocation = F("ADS1115_0x"); sensorLocation += String(_i2cAddress, HEX); - sensorLocation += F("_Pin"); + sensorLocation += F("_Pin"); sensorLocation += String(_dataPin); return sensorLocation; } diff --git a/src/ApogeeSQ212.h b/src/ApogeeSQ212.h index ae773a136..85dd28b48 100644 --- a/src/ApogeeSQ212.h +++ b/src/ApogeeSQ212.h @@ -74,11 +74,11 @@ class ApogeeSQ212_PAR : public Variable { public: ApogeeSQ212_PAR(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, SQ212_PAR_VAR_NUM, - F("radiationIncomingPAR"), F("microeinsteinPerSquareMeterPerSecond"), + "radiationIncomingPAR", "microeinsteinPerSquareMeterPerSecond", SQ212_PAR_RESOLUTION, - F("photosyntheticallyActiveRadiation"), UUID, customVarCode) + "photosyntheticallyActiveRadiation", UUID, customVarCode) {} }; diff --git a/src/BoschBME280.cpp b/src/BoschBME280.cpp index d0eab7812..54d9d4b20 100644 --- a/src/BoschBME280.cpp +++ b/src/BoschBME280.cpp @@ -33,7 +33,7 @@ // The constructor - because this is I2C, only need the power pin BoschBME280::BoschBME280(int8_t powerPin, uint8_t i2cAddressHex, uint8_t measurementsToAverage) - : Sensor(F("BoschBME280"), BME280_NUM_VARIABLES, + : Sensor("BoschBME280", BME280_NUM_VARIABLES, BME280_WARM_UP_TIME_MS, BME280_STABILIZATION_TIME_MS, BME280_MEASUREMENT_TIME_MS, powerPin, -1, measurementsToAverage) { diff --git a/src/BoschBME280.h b/src/BoschBME280.h index 949b1275e..3c4f0cdec 100644 --- a/src/BoschBME280.h +++ b/src/BoschBME280.h @@ -45,8 +45,8 @@ #define BME280_NUM_VARIABLES 4 #define BME280_WARM_UP_TIME_MS 100 -#define BME280_STABILIZATION_TIME_MS 0 -#define BME280_MEASUREMENT_TIME_MS 1000 +#define BME280_STABILIZATION_TIME_MS 4000 // 0.5 s for good numbers, but optimal at 4 s based on tests using bme280timingTest.ino +#define BME280_MEASUREMENT_TIME_MS 1100 // 1.0 s according to datasheet, but slightly better stdev when 1.1 s #define BME280_TEMP_RESOLUTION 2 #define BME280_TEMP_VAR_NUM 0 @@ -84,11 +84,11 @@ class BoschBME280_Temp : public Variable { public: BoschBME280_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, BME280_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", BME280_TEMP_RESOLUTION, - F("BoschBME280Temp"), UUID, customVarCode) + "BoschBME280Temp", UUID, customVarCode) {} }; @@ -98,11 +98,11 @@ class BoschBME280_Humidity : public Variable { public: BoschBME280_Humidity(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, BME280_HUMIDITY_VAR_NUM, - F("relativeHumidity"), F("percent"), + "relativeHumidity", "percent", BME280_HUMIDITY_RESOLUTION, - F("BoschBME280Humidity"), UUID, customVarCode) + "BoschBME280Humidity", UUID, customVarCode) {} }; @@ -112,11 +112,11 @@ class BoschBME280_Pressure : public Variable { public: BoschBME280_Pressure(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, BME280_PRESSURE_VAR_NUM, - F("barometricPressure"), F("pascal"), + "barometricPressure", "pascal", BME280_PRESSURE_RESOLUTION, - F("BoschBME280Pressure"), UUID, customVarCode) + "BoschBME280Pressure", UUID, customVarCode) {} }; @@ -126,11 +126,11 @@ class BoschBME280_Altitude : public Variable { public: BoschBME280_Altitude(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, BME280_ALTITUDE_VAR_NUM, - F("heightAboveSeaFloor"), F("meter"), + "heightAboveSeaFloor", "meter", BME280_ALTITUDE_RESOLUTION, - F("BoschBME280Altitude"), UUID, customVarCode) + "BoschBME280Altitude", UUID, customVarCode) {} }; diff --git a/src/CampbellOBS3.cpp b/src/CampbellOBS3.cpp index b05bbbd9e..18f3cba52 100644 --- a/src/CampbellOBS3.cpp +++ b/src/CampbellOBS3.cpp @@ -34,7 +34,7 @@ CampbellOBS3::CampbellOBS3(int8_t powerPin, int8_t dataPin, float x2_coeff_A, float x1_coeff_B, float x0_coeff_C, uint8_t i2cAddress, uint8_t measurementsToAverage) - : Sensor(F("CampbellOBS3"), OBS3_NUM_VARIABLES, + : Sensor("CampbellOBS3", OBS3_NUM_VARIABLES, OBS3_WARM_UP_TIME_MS, OBS3_STABILIZATION_TIME_MS, OBS3_MEASUREMENT_TIME_MS, powerPin, dataPin, measurementsToAverage) { diff --git a/src/CampbellOBS3.h b/src/CampbellOBS3.h index b64a9e2ba..61fc4c868 100644 --- a/src/CampbellOBS3.h +++ b/src/CampbellOBS3.h @@ -74,11 +74,11 @@ class CampbellOBS3_Turbidity : public Variable { public: CampbellOBS3_Turbidity(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, OBS3_TURB_VAR_NUM, - F("turbidity"), F("nephelometricTurbidityUnit"), + "turbidity", "nephelometricTurbidityUnit", OBS3_RESOLUTION, - F("Turbidity"), UUID, customVarCode) + "turbidity", UUID, customVarCode) {} }; diff --git a/src/Decagon5TM.h b/src/Decagon5TM.h index 4c25f05cc..8af5e4261 100644 --- a/src/Decagon5TM.h +++ b/src/Decagon5TM.h @@ -53,17 +53,17 @@ class Decagon5TM : public SDI12Sensors // Constructors with overloads Decagon5TM(char SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("Decagon5TM"), TM_NUM_VARIABLES, + "Decagon5TM", TM_NUM_VARIABLES, TM_WARM_UP_TIME_MS, TM_STABILIZATION_TIME_MS, TM_MEASUREMENT_TIME_MS) {} Decagon5TM(char *SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("Decagon5TM"), TM_NUM_VARIABLES, + "Decagon5TM", TM_NUM_VARIABLES, TM_WARM_UP_TIME_MS, TM_STABILIZATION_TIME_MS, TM_MEASUREMENT_TIME_MS) {} Decagon5TM(int SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("Decagon5TM"), TM_NUM_VARIABLES, + "Decagon5TM", TM_NUM_VARIABLES, TM_WARM_UP_TIME_MS, TM_STABILIZATION_TIME_MS, TM_MEASUREMENT_TIME_MS) {} @@ -75,11 +75,11 @@ class Decagon5TM : public SDI12Sensors class Decagon5TM_Ea : public Variable { public: - Decagon5TM_Ea(Sensor *parentSense, String UUID = "", String customVarCode = "") + Decagon5TM_Ea(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, TM_EA_VAR_NUM, - F("permittivity"), F("faradPerMeter"), + "permittivity", "faradPerMeter", TM_EA_RESOLUTION, - F("SoilEa"), UUID, customVarCode) + "SoilEa", UUID, customVarCode) {} }; @@ -88,11 +88,11 @@ class Decagon5TM_Ea : public Variable class Decagon5TM_Temp : public Variable { public: - Decagon5TM_Temp(Sensor *parentSense, String UUID = "", String customVarCode = "") + Decagon5TM_Temp(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, TM_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", TM_TEMP_RESOLUTION, - F("SoilTemp"), UUID, customVarCode) + "SoilTemp", UUID, customVarCode) {} }; @@ -101,11 +101,11 @@ class Decagon5TM_Temp : public Variable class Decagon5TM_VWC : public Variable { public: - Decagon5TM_VWC(Sensor *parentSense, String UUID = "", String customVarCode = "") + Decagon5TM_VWC(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, TM_VWC_VAR_NUM, - F("volumetricWaterContent"), F("percent"), + "volumetricWaterContent", "percent", TM_VWC_RESOLUTION, - F("SoilVWC"), UUID, customVarCode) + "SoilVWC", UUID, customVarCode) {} }; diff --git a/src/DecagonCTD.h b/src/DecagonCTD.h index 812f96ba1..951bb15b5 100644 --- a/src/DecagonCTD.h +++ b/src/DecagonCTD.h @@ -58,17 +58,17 @@ class DecagonCTD : public SDI12Sensors // Constructors with overloads DecagonCTD(char SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("DecagonCTD"), CTD_NUM_VARIABLES, + "DecagonCTD", CTD_NUM_VARIABLES, CTD_WARM_UP_TIME_MS, CTD_STABILIZATION_TIME_MS, CTD_MEASUREMENT_TIME_MS) {} DecagonCTD(char *SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("DecagonCTD"), CTD_NUM_VARIABLES, + "DecagonCTD", CTD_NUM_VARIABLES, CTD_WARM_UP_TIME_MS, CTD_STABILIZATION_TIME_MS, CTD_MEASUREMENT_TIME_MS) {} DecagonCTD(int SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("DecagonCTD"), CTD_NUM_VARIABLES, + "DecagonCTD", CTD_NUM_VARIABLES, CTD_WARM_UP_TIME_MS, CTD_STABILIZATION_TIME_MS, CTD_MEASUREMENT_TIME_MS) {} }; @@ -78,11 +78,11 @@ class DecagonCTD : public SDI12Sensors class DecagonCTD_Cond : public Variable { public: - DecagonCTD_Cond(Sensor *parentSense, String UUID = "", String customVarCode = "") + DecagonCTD_Cond(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, CTD_COND_VAR_NUM, - F("specificConductance"), F("microsiemenPerCentimeter"), + "specificConductance", "microsiemenPerCentimeter", CTD_COND_RESOLUTION, - F("CTDcond"), UUID, customVarCode) + "CTDcond", UUID, customVarCode) {} }; @@ -91,11 +91,11 @@ class DecagonCTD_Cond : public Variable class DecagonCTD_Temp : public Variable { public: - DecagonCTD_Temp(Sensor *parentSense, String UUID = "", String customVarCode = "") + DecagonCTD_Temp(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, CTD_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", CTD_TEMP_RESOLUTION, - F("CTDtemp"), UUID, customVarCode) + "CTDtemp", UUID, customVarCode) {} }; @@ -104,11 +104,11 @@ class DecagonCTD_Temp : public Variable class DecagonCTD_Depth : public Variable { public: - DecagonCTD_Depth(Sensor *parentSense, String UUID = "", String customVarCode = "") + DecagonCTD_Depth(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, CTD_DEPTH_VAR_NUM, - F("waterDepth"), F("millimeter"), + "waterDepth", "millimeter", CTD_DEPTH_RESOLUTION, - F("CTDdepth"), UUID, customVarCode) + "CTDdepth", UUID, customVarCode) {} }; diff --git a/src/DecagonES2.h b/src/DecagonES2.h index 3d83dc98b..8134a3e5d 100644 --- a/src/DecagonES2.h +++ b/src/DecagonES2.h @@ -49,17 +49,17 @@ class DecagonES2 : public SDI12Sensors // Constructors with overloads DecagonES2(char SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("DecagonES2"), ES2_NUM_VARIABLES, + "DecagonES2", ES2_NUM_VARIABLES, ES2_WARM_UP_TIME_MS, ES2_STABILIZATION_TIME_MS, ES2_MEASUREMENT_TIME_MS) {} DecagonES2(char *SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("DecagonES2"), ES2_NUM_VARIABLES, + "DecagonES2", ES2_NUM_VARIABLES, ES2_WARM_UP_TIME_MS, ES2_STABILIZATION_TIME_MS, ES2_MEASUREMENT_TIME_MS) {} DecagonES2(int SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("DecagonES2"), ES2_NUM_VARIABLES, + "DecagonES2", ES2_NUM_VARIABLES, ES2_WARM_UP_TIME_MS, ES2_STABILIZATION_TIME_MS, ES2_MEASUREMENT_TIME_MS) {} }; @@ -69,11 +69,11 @@ class DecagonES2 : public SDI12Sensors class DecagonES2_Cond : public Variable { public: - DecagonES2_Cond(Sensor *parentSense, String UUID = "", String customVarCode = "") + DecagonES2_Cond(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, ES2_COND_VAR_NUM, - F("specificConductance"), F("microsiemenPerCentimeter"), + "specificConductance", "microsiemenPerCentimeter", ES2_COND_RESOLUTION, - F("ES2Cond"), UUID, customVarCode) + "ES2Cond", UUID, customVarCode) {} }; @@ -81,11 +81,11 @@ class DecagonES2_Cond : public Variable class DecagonES2_Temp : public Variable { public: - DecagonES2_Temp(Sensor *parentSense, String UUID = "", String customVarCode = "") + DecagonES2_Temp(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, ES2_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", ES2_TEMP_RESOLUTION, - F("ES2temp"), UUID, customVarCode) + "ES2temp", UUID, customVarCode) {} }; diff --git a/src/ExternalVoltage.cpp b/src/ExternalVoltage.cpp index db86e3c0d..c33a52cd6 100644 --- a/src/ExternalVoltage.cpp +++ b/src/ExternalVoltage.cpp @@ -40,7 +40,7 @@ // The constructor - need the power pin the data pin, and gain if non standard ExternalVoltage::ExternalVoltage(int8_t powerPin, int8_t dataPin, float gain, uint8_t i2cAddress, uint8_t measurementsToAverage) - : Sensor(F("ExternalVoltage"), EXT_VOLT_NUM_VARIABLES, + : Sensor("ExternalVoltage", EXT_VOLT_NUM_VARIABLES, EXT_VOLT_WARM_UP_TIME_MS, EXT_VOLT_STABILIZATION_TIME_MS, EXT_VOLT_MEASUREMENT_TIME_MS, powerPin, dataPin, measurementsToAverage) { @@ -53,7 +53,7 @@ String ExternalVoltage::getSensorLocation(void) { String sensorLocation = F("ADS1115_0x"); sensorLocation += String(_i2cAddress, HEX); - sensorLocation += F("_Pin"); + sensorLocation += F("_Pin"); sensorLocation += String(_dataPin); return sensorLocation; } diff --git a/src/ExternalVoltage.h b/src/ExternalVoltage.h index 75cca75ce..a3353d935 100644 --- a/src/ExternalVoltage.h +++ b/src/ExternalVoltage.h @@ -28,7 +28,7 @@ * http://wiki.seeedstudio.com/Grove-Voltage_Divider * * Technical specifications for the TI ADS1115 can be found at: - * http://www.ti.com/product/ADS1115 + * http://www.ti.com/product/ADS1115 * * Response time: < 1ms * Resample time: max of ADC (860/sec) @@ -81,11 +81,11 @@ class ExternalVoltage_Volt : public Variable { public: ExternalVoltage_Volt(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, EXT_VOLT_VAR_NUM, - F("voltage"), F("volt"), + "voltage", "volt", EXT_VOLT_RESOLUTION, - F("extVoltage"), UUID, customVarCode) + "extVoltage", UUID, customVarCode) {} }; diff --git a/src/FreescaleMPL115A2.cpp b/src/FreescaleMPL115A2.cpp index bb8897825..b5a6ded6f 100644 --- a/src/FreescaleMPL115A2.cpp +++ b/src/FreescaleMPL115A2.cpp @@ -27,7 +27,7 @@ // The constructor - because this is I2C, only need the power pin // This sensor has a set I2C address of 0x60. MPL115A2::MPL115A2(int8_t powerPin, uint8_t measurementsToAverage) - : Sensor(F("MPL115A2"), MPL115A2_NUM_VARIABLES, + : Sensor("MPL115A2", MPL115A2_NUM_VARIABLES, MPL115A2_WARM_UP_TIME_MS, MPL115A2_STABILIZATION_TIME_MS, MPL115A2_MEASUREMENT_TIME_MS, powerPin, -1, measurementsToAverage) {} diff --git a/src/FreescaleMPL115A2.h b/src/FreescaleMPL115A2.h index e2260d3cc..3a3a6ad95 100644 --- a/src/FreescaleMPL115A2.h +++ b/src/FreescaleMPL115A2.h @@ -67,11 +67,11 @@ class MPL115A2_Temp : public Variable { public: MPL115A2_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, MPL115A2_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", MPL115A2_TEMP_RESOLUTION, - F("MPL115A2_Temp"), UUID, customVarCode) + "MPL115A2_Temp", UUID, customVarCode) {} }; @@ -81,11 +81,11 @@ class MPL115A2_Pressure : public Variable { public: MPL115A2_Pressure(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, MPL115A2_PRESSURE_VAR_NUM, - F("atmosphericPressure"), F("kPa"), + "atmosphericPressure", "kilopascal", MPL115A2_PRESSURE_RESOLUTION, - F("MPL115A2_Pressure"), UUID, customVarCode) + "MPL115A2_Pressure", UUID, customVarCode) {} }; diff --git a/src/KellerAcculevel.h b/src/KellerAcculevel.h index b2c0f7298..4d3e16c5c 100644 --- a/src/KellerAcculevel.h +++ b/src/KellerAcculevel.h @@ -39,13 +39,13 @@ class KellerAcculevel : public KellerParent KellerAcculevel(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : KellerParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Acculevel, F("KellerAcculevel"), KELLER_NUM_VARIABLES, + Acculevel, "KellerAcculevel", KELLER_NUM_VARIABLES, KellerAcculevel_WARM_UP_TIME_MS, KellerAcculevel_STABILIZATION_TIME_MS, KellerAcculevel_MEASUREMENT_TIME_MS) {} KellerAcculevel(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : KellerParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Acculevel, F("KellerAcculevel"), KELLER_NUM_VARIABLES, + Acculevel, "KellerAcculevel", KELLER_NUM_VARIABLES, KellerAcculevel_WARM_UP_TIME_MS, KellerAcculevel_STABILIZATION_TIME_MS, KellerAcculevel_MEASUREMENT_TIME_MS) {} }; @@ -55,11 +55,11 @@ class KellerAcculevel : public KellerParent class KellerAcculevel_Pressure : public Variable { public: - KellerAcculevel_Pressure(Sensor *parentSense, String UUID = "", String customVarCode = "") + KellerAcculevel_Pressure(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, KELLER_PRESSURE_VAR_NUM, - F("pressureGauge"), F("millibar"), + "pressureGauge", "millibar", KellerAcculevel_PRESSURE_RESOLUTION, - F("kellerPress"), UUID, customVarCode) + "kellerPress", UUID, customVarCode) {} }; @@ -68,11 +68,11 @@ class KellerAcculevel_Pressure : public Variable class KellerAcculevel_Temp : public Variable { public: - KellerAcculevel_Temp(Sensor *parentSense, String UUID = "", String customVarCode = "") + KellerAcculevel_Temp(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, KELLER_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", KellerAcculevel_TEMP_RESOLUTION, - F("kellerTemp"), UUID, customVarCode) + "kellerTemp", UUID, customVarCode) {} }; @@ -80,11 +80,11 @@ class KellerAcculevel_Temp : public Variable class KellerAcculevel_Height : public Variable { public: - KellerAcculevel_Height(Sensor *parentSense, String UUID = "", String customVarCode = "") + KellerAcculevel_Height(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, KELLER_HEIGHT_VAR_NUM, - F("gaugeHeight"), F("meter"), + "gaugeHeight", "meter", KellerAcculevel_HEIGHT_RESOLUTION, - F("kellerHeight"), UUID, customVarCode) + "kellerHeight", UUID, customVarCode) {} }; diff --git a/src/KellerParent.cpp b/src/KellerParent.cpp index 40d08d583..e09907204 100644 --- a/src/KellerParent.cpp +++ b/src/KellerParent.cpp @@ -20,7 +20,7 @@ // The constructor - need the sensor type, modbus address, power pin, stream for data, and number of readings to average KellerParent::KellerParent(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin, uint8_t measurementsToAverage, - kellerModel model, String sensName, int numVariables, + kellerModel model, const char *sensName, int numVariables, uint32_t warmUpTime_ms, uint32_t stabilizationTime_ms, uint32_t measurementTime_ms) : Sensor(sensName, numVariables, warmUpTime_ms, stabilizationTime_ms, measurementTime_ms, @@ -33,7 +33,7 @@ KellerParent::KellerParent(byte modbusAddress, Stream* stream, } KellerParent::KellerParent(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin, uint8_t measurementsToAverage, - kellerModel model, String sensName, int numVariables, + kellerModel model, const char *sensName, int numVariables, uint32_t warmUpTime_ms, uint32_t stabilizationTime_ms, uint32_t measurementTime_ms) : Sensor(sensName, numVariables, warmUpTime_ms, stabilizationTime_ms, measurementTime_ms, @@ -59,7 +59,7 @@ String KellerParent::getSensorLocation(void) bool KellerParent::setup(void) { bool retVal = Sensor::setup(); // sets time stamp and status bits - if (_RS485EnablePin > 0) pinMode(_RS485EnablePin, OUTPUT); + if (_RS485EnablePin >= 0) pinMode(_RS485EnablePin, OUTPUT); #if defined(DEEP_DEBUGGING_SERIAL_OUTPUT) sensor.setDebugStream(&DEEP_DEBUGGING_SERIAL_OUTPUT); diff --git a/src/KellerParent.h b/src/KellerParent.h index 2a1c572cb..aa69ef975 100644 --- a/src/KellerParent.h +++ b/src/KellerParent.h @@ -38,11 +38,11 @@ class KellerParent : public Sensor public: KellerParent(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1, - kellerModel model = OTHER, String sensName = "Keller-Sensor", int numVariables = 3, + kellerModel model = OTHER, const char *sensName = "Keller-Sensor", int numVariables = 3, uint32_t warmUpTime_ms = 500, uint32_t stabilizationTime_ms = 5000, uint32_t measurementTime_ms = 1500); KellerParent(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1, - kellerModel model = OTHER, String sensName = "Keller-Sensor", int numVariables = 3, + kellerModel model = OTHER, const char *sensName = "Keller-Sensor", int numVariables = 3, uint32_t warmUpTime_ms = 500, uint32_t stabilizationTime_ms = 5000, uint32_t measurementTime_ms = 1500); String getSensorLocation(void) override; diff --git a/src/LoggerBase.cpp b/src/LoggerBase.cpp index aff9dc58d..980c1bd58 100644 --- a/src/LoggerBase.cpp +++ b/src/LoggerBase.cpp @@ -7,7 +7,7 @@ *This file is for the basic logging functions - ie, saving to an SD card. */ -#include "LoggerBase.h" // To communicate with the internet +#include "LoggerBase.h" // To prevent compiler/linker crashes with Enable interrupt #define LIBCALL_ENABLEINTERRUPT @@ -38,39 +38,32 @@ volatile bool Logger::startTesting = false; // Initialization - cannot do this in constructor arduino has issues creating // instances of classes with non-empty constructors -void Logger::init(int8_t SDCardPin, int8_t mcuWakePin, - uint8_t variableCount, - Variable *variableList[], - uint8_t loggingIntervalMinutes, - const char *loggerID) +Logger::Logger(const char *loggerID, uint16_t loggingIntervalMinutes, + int8_t SDCardPin, int8_t mcuWakePin, + VariableArray *inputArray) { - // initialize the variable array - VariableArray::init(variableCount, variableList); - - PRINTOUT(F("Initializing logger "), loggerID, F(" to record at "), - loggingIntervalMinutes, F(" minute intervals ... ")); - + // Set parameters from constructor + _loggerID = loggerID; + _loggingIntervalMinutes = loggingIntervalMinutes; _SDCardPin = SDCardPin; _mcuWakePin = mcuWakePin; - _loggingIntervalMinutes = loggingIntervalMinutes; - _loggingIntervalSeconds = round(_loggingIntervalMinutes*60); // convert to even seconds - _loggerID = loggerID; - _autoFileName = false; - _isFileNameSet = false; - _numTimepointsLogged = 0; + _internalArray = inputArray; - // Set sleep variable, if an interrupt pin is given - if(_mcuWakePin != -1) - { - _sleep = true; - } + // Initialize with no points recorded + _numTimepointsLogged = 0; - // Set the testing/logging flags + // Set the testing/logging flags to false isLoggingNow = false; isTestingNow = false; startTesting = false; - PRINTOUT(F(" ... Success!\n")); + // Initialize with informational pins set void + _ledPin = -1; + _buttonPin = -1; + + // Initialize with no file name + _fileName = ""; + _autoFileName = true; }; @@ -84,7 +77,6 @@ void Logger::setTimeZone(int8_t timeZone) else if (_timeZone > 0) PRINTOUT(F("UTC+")); else PRINTOUT(F("UTC")); if (_timeZone != 0) PRINTOUT(_timeZone, F("\n")); - } @@ -115,6 +107,14 @@ void Logger::setAlertPin(int8_t ledPin) } +// Sets up a pin for an interrupt to enter testing mode +void Logger::setTestingModePin(int8_t buttonPin) +{ + _buttonPin = buttonPin; + MS_DBG(F("Pin "), _buttonPin, F(" set as testing mode entry pin\n")); +} + + // ===================================================================== // // Public functions to access the clock in proper format and time zone // ===================================================================== // @@ -151,7 +151,7 @@ DateTime Logger::dtFromEpoch(uint32_t epochTime) } // This converts a date-time object into a ISO8601 formatted string -String Logger::formatDateTime_ISO8601(DateTime dt) +String Logger::formatDateTime_ISO8601(DateTime& dt) { // Set up an inital string String dateTimeStr; @@ -256,11 +256,12 @@ bool Logger::checkInterval(void) bool retval; uint32_t checkTime = getNowEpoch(); MS_DBG(F("Current Unix Timestamp: "), checkTime, F("\n")); - MS_DBG(F("Logging interval in seconds: "), _loggingIntervalSeconds, F("\n")); - MS_DBG(F("Mod of Logging Interval: "), checkTime % _loggingIntervalSeconds, F("\n")); + MS_DBG(F("Logging interval in seconds: "), (_loggingIntervalMinutes*60), F("\n")); + MS_DBG(F("Mod of Logging Interval: "), checkTime % (_loggingIntervalMinutes*60), F("\n")); MS_DBG(F("Number of Readings so far: "), _numTimepointsLogged, F("\n")); MS_DBG(F("Mod of 120: "), checkTime % 120, F("\n")); - if ((checkTime % _loggingIntervalSeconds == 0 ) or + + if ((checkTime % (_loggingIntervalMinutes*60) == 0 ) or (_numTimepointsLogged < 10 and checkTime % 120 == 0)) { // Update the time variables with the current time @@ -293,12 +294,13 @@ bool Logger::checkMarkedInterval(void) { bool retval; MS_DBG(F("Marked Time: "), markedEpochTime, F("\n")); - MS_DBG(F("Logging interval in seconds: "), _loggingIntervalSeconds, F("\n")); - MS_DBG(F("Mod of Logging Interval: "), markedEpochTime % _loggingIntervalSeconds, F("\n")); + MS_DBG(F("Logging interval in seconds: "), (_loggingIntervalMinutes*60), F("\n")); + MS_DBG(F("Mod of Logging Interval: "), markedEpochTime % (_loggingIntervalMinutes*60), F("\n")); MS_DBG(F("Number of Readings so far: "), _numTimepointsLogged, F("\n")); MS_DBG(F("Mod of 120: "), markedEpochTime % 120, F("\n")); + if (markedEpochTime != 0 && - ((markedEpochTime % _loggingIntervalSeconds == 0 ) or + ((markedEpochTime % (_loggingIntervalMinutes*60) == 0 ) or (_numTimepointsLogged < 10 and markedEpochTime % 120 == 0))) { // Update the number of readings taken @@ -487,45 +489,53 @@ void Logger::wakeISR(void){MS_DBG(F("Clock interrupt!\n"));} // ===================================================================== // // This sets a file name, if you want to decide on it in advance -void Logger::setFileName(char *fileName) +void Logger::setFileName(String& fileName) { - // Save the filename to the static String _fileName = fileName; - _isFileNameSet = true; - - // Print out the file name - PRINTOUT(F("Data will be saved as "), _fileName, '\n'); - if (!_autoFileName) PRINTOUT(F("\n")); + _autoFileName = false; } -// Same as above, with a string (overload function) -void Logger::setFileName(String fileName) +// Same as above, with a character array (overload function) +void Logger::setFileName(const char *fileName) { - // Convert the string filename to a character file name - uint8_t fileNameLength = fileName.length() + 1; - char charFileName[fileNameLength]; - fileName.toCharArray(charFileName, fileNameLength); - setFileName(charFileName); + String StrName = String(fileName); + setFileName(StrName); } // This generates a file name from the logger id and the current date // This will be used if the setFileName function is not called before // the begin() function is called. -void Logger::setFileName(void) +void Logger::generateAutoFileName(void) { - _autoFileName = true; - // Generate the file name from logger ID and date - String fileName = ""; - if (_loggerID) + if (_autoFileName) { - fileName += String(_loggerID); + // Generate the file name from logger ID and date + String fileName = String(_loggerID); fileName += F("_"); + fileName += formatDateTime_ISO8601(getNowEpoch()).substring(0, 10); + fileName += F(".csv"); + setFileName(fileName); + _fileName = fileName; } - fileName += formatDateTime_ISO8601(getNowEpoch()).substring(0, 10); - fileName += F(".csv"); - setFileName(fileName); } +// This is a PRE-PROCESSOR MACRO to speed up generating header rows +// Again, THIS IS NOT A FUNCTION, it is a pre-processor macro +#define makeHeaderRowMacro(firstCol, function) \ + dataHeader += F("\""); \ + dataHeader += firstCol; \ + dataHeader += F("\","); \ + for (uint8_t i = 0; i < _internalArray->getVariableCount(); i++) \ + { \ + dataHeader += F("\""); \ + dataHeader += function; \ + dataHeader += F("\""); \ + if (i + 1 != _internalArray->getVariableCount()) \ + { \ + dataHeader += F(","); \ + } \ + } \ + dataHeader += F("\r\n"); // This creates a header for the logger file String Logger::generateFileHeader(void) @@ -537,21 +547,63 @@ String Logger::generateFileHeader(void) // Create the header rows String dataHeader = ""; // Next line will be the parent sensor names - makeHeaderRowMacro(logIDRowHeader, _variableList[i]->parentSensor->getSensorName()) + makeHeaderRowMacro(logIDRowHeader, _internalArray->arrayOfVars[i]->getParentSensorName()) // Next comes the ODM2 variable name - makeHeaderRowMacro(logIDRowHeader, _variableList[i]->getVarName()) + makeHeaderRowMacro(logIDRowHeader, _internalArray->arrayOfVars[i]->getVarName()) // Next comes the ODM2 unit name - makeHeaderRowMacro(logIDRowHeader, _variableList[i]->getVarUnit()) + makeHeaderRowMacro(logIDRowHeader, _internalArray->arrayOfVars[i]->getVarUnit()) + // Next comes the variable UUIDs + makeHeaderRowMacro(logIDRowHeader, _internalArray->arrayOfVars[i]->getVarUUID()) // We'll finish up the the custom variable codes String dtRowHeader = F("Date and Time in UTC"); dtRowHeader += _timeZone; - makeHeaderRowMacro(dtRowHeader, _variableList[i]->getVarCode()) + makeHeaderRowMacro(dtRowHeader, _internalArray->arrayOfVars[i]->getVarCode()) // Return everything return dataHeader; } +// This is another PRE-PROCESSOR MACRO to speed up generating header rows +// Again, THIS IS NOT A FUNCTION, it is a pre-processor macro +#define streamHeaderRowMacro(firstCol, function) \ + stream->print(F("\"")); \ + stream->print(firstCol); \ + stream->print(F("\",")); \ + for (uint8_t i = 0; i < _internalArray->getVariableCount(); i++) \ + { \ + stream->print(F("\"")); \ + stream->print(function); \ + stream->print(F("\"")); \ + if (i + 1 != _internalArray->getVariableCount()) \ + { \ + stream->print(F(",")); \ + } \ + } \ + stream->print(F("\r\n")); + +// This sends a file header out over an Arduino stream +void Logger::streamFileHeader(Stream *stream) +{ + // Very first column of the header is the logger ID + String logIDRowHeader = F("Data Logger: "); + logIDRowHeader += String(_loggerID); + + // Next line will be the parent sensor names + streamHeaderRowMacro(logIDRowHeader, _internalArray->arrayOfVars[i]->getParentSensorName()) + // Next comes the ODM2 variable name + streamHeaderRowMacro(logIDRowHeader, _internalArray->arrayOfVars[i]->getVarName()) + // Next comes the ODM2 unit name + streamHeaderRowMacro(logIDRowHeader, _internalArray->arrayOfVars[i]->getVarUnit()) + // Next comes the variable UUIDs + streamHeaderRowMacro(logIDRowHeader, _internalArray->arrayOfVars[i]->getVarUUID()) + + // We'll finish up the the custom variable codes + String dtRowHeader = F("Date and Time in UTC"); + dtRowHeader += _timeZone; + streamHeaderRowMacro(dtRowHeader, _internalArray->arrayOfVars[i]->getVarCode()); +} + // This generates a comma separated list of volues of sensor data - including the time String Logger::generateSensorDataCSV(void) @@ -559,16 +611,43 @@ String Logger::generateSensorDataCSV(void) String csvString = ""; markedDateTime.addToString(csvString); csvString += F(","); - csvString += VariableArray::generateSensorDataCSV(); + + for (uint8_t i = 0; i < _internalArray->getVariableCount(); i++) + { + csvString += _internalArray->arrayOfVars[i]->getValueString(); + if (i + 1 != _internalArray->getVariableCount()) + { + csvString += F(","); + } + } + return csvString; } +// This sends a comma separated list of volues of sensor data - including the +// time - out over an Arduino stream +void Logger::streamSensorDataCSV(Stream *stream) +{ + String csvString = ""; + markedDateTime.addToString(csvString); + csvString += F(","); + stream->print(csvString); + for (uint8_t i = 0; i < _internalArray->getVariableCount(); i++) + { + stream->print(_internalArray->arrayOfVars[i]->getValueString()); + if (i + 1 != _internalArray->getVariableCount()) + { + stream->print(F(",")); + } + } + stream->println(); +} -// This checks if the SD card is available and ready -bool Logger::initializeSDCard(uint8_t Pin) +// Private helper function - This checks if the SD card is available and ready +bool Logger::initializeSDCard(void) { // Initialise the SD card - if (!sd.begin(Pin, SPI_FULL_SPEED)) + if (!sd.begin(_SDCardPin, SPI_FULL_SPEED)) { PRINTOUT(F("Error: SD card failed to initialize or is missing.\n")); PRINTOUT(F("Data will not be saved!\n")); @@ -577,18 +656,14 @@ bool Logger::initializeSDCard(uint8_t Pin) else // skip everything else if there's no SD card, otherwise it might hang { PRINTOUT(F("Successfully connected to SD Card with card/slave select on pin ")); - PRINTOUT(Pin, F("\n")); + PRINTOUT(_SDCardPin, F("\n")); return true; } } -bool Logger::initializeSDCard(void) -{ - return initializeSDCard(_SDCardPin); -} -// This sets a timestamp on a file -void Logger::setFileTimestame(SdFile fileToStamp, uint8_t stampFlag) +// Private helper function - This sets a timestamp on a file +void Logger::setFileTimestamp(File fileToStamp, uint8_t stampFlag) { fileToStamp.timestamp(stampFlag, dtFromEpoch(getNowEpoch()).year(), dtFromEpoch(getNowEpoch()).month(), @@ -599,83 +674,164 @@ void Logger::setFileTimestame(SdFile fileToStamp, uint8_t stampFlag) } -// This initializes a file on the SD card with the given filename and writes the given header to it -void Logger::setupLogFile(String filename, String header) +// Private helper function - This opens or creates a file, converting a string +// file name to a character file name +bool Logger::openFile(String& filename, bool createFile, bool writeDefaultHeader) { // Initialise the SD card // skip everything else if there's no SD card, otherwise it might hang - if (!initializeSDCard()) return; - else - { - // Convert the string filename to a character file name for SdFat - uint8_t fileNameLength = filename.length() + 1; - char charFileName[fileNameLength]; - filename.toCharArray(charFileName, fileNameLength); + if (!initializeSDCard()) return false; - // Open the file in write mode (and create it if it did not exist) - logFile.open(charFileName, O_CREAT | O_WRITE | O_AT_END); - // Set creation date time - setFileTimestame(logFile, T_CREATE); - // Set write/modification date time - setFileTimestame(logFile, T_WRITE); + // Convert the string filename to a character file name for SdFat + uint8_t fileNameLength = filename.length() + 1; + char charFileName[fileNameLength]; + filename.toCharArray(charFileName, fileNameLength); + + // First attempt to open an already existing file (in write mode), so we + // don't try to re-create something that's already there. + // This should also prevent the header from being written over and over + // in the file. + if (logFile.open(charFileName, O_WRITE | O_AT_END)) + { + MS_DBG(F("Opened existing file: "), filename, F("\n")); // Set access date time - setFileTimestame(logFile, T_ACCESS); - PRINTOUT(F("File created!\n")); + setFileTimestamp(logFile, T_ACCESS); + return true; + } + else if (createFile) + { + // Create and then open the file in write mode + if (logFile.open(charFileName, O_CREAT | O_WRITE | O_AT_END)) + { + MS_DBG(F("Created new file: "), filename, F("\n")); + // Set creation date time + setFileTimestamp(logFile, T_CREATE); + // Write out a header, if requested + if (writeDefaultHeader) + { + // Add header information + streamFileHeader(&logFile); + // Print out the header for debugging + #if defined(DEBUGGING_SERIAL_OUTPUT) + MS_DBG(F("File Header:\n")); + streamFileHeader(&DEBUGGING_SERIAL_OUTPUT); + #endif + // Set write/modification date time + setFileTimestamp(logFile, T_WRITE); + } + // Set access date time + setFileTimestamp(logFile, T_ACCESS); + return true; + } + // Return false if we couldn't create the file + else + { + MS_DBG(F("Unable to create new file: "), filename, F("\n")); + return false; + } + } + // Return false if we couldn't access the file (and were not told to create it) + else + { + MS_DBG(F("Unable to to write to file: "), filename, F("\n")); + return false; + } +} - // Add header information - logFile.print(header); - MS_DBG(header, F("\n")); - //Close the file to save it +// These functions create a file on the SD card with the given filename and +// set the proper timestamps to the file. +// The filename may either be the one set by setFileName(String)/setFileName(void) +// or can be specified in the function. +// If specified, it will also write a header to the file based on +// the sensors in the group. +// This can be used to force a logger to create a file with a secondary file name. +bool Logger::createLogFile(String& filename, bool writeDefaultHeader) +{ + // Attempt to create and open a file + if (openFile(filename, true, writeDefaultHeader)) + { + // Close the file to save it (only do this if we'd opened it) logFile.close(); + return true; } + else return false; } -// This initializes a file on the SD card and writes a header to it -void Logger::setupLogFile(void) +bool Logger::createLogFile(bool writeDefaultHeader) { - setupLogFile(_fileName, generateFileHeader()); + if (_autoFileName) generateAutoFileName(); + return createLogFile(_fileName, writeDefaultHeader); } -// This writes a record to the SD card with the given filename -void Logger::logToSD(String rec, String filename) +// These functions write a file on the SD card with the given filename and +// set the proper timestamps to the file. +// The filename may either be the one set by setFileName(String)/setFileName(void) +// or can be specified in the function. +// If the file does not already exist, the file will be created. +// This can be used to force a logger to write to a file with a secondary file name. +bool Logger::logToSD(String& filename, String& rec) { - // Initialise the SD card - // skip everything else if there's no SD card, otherwise it might hang - if (!initializeSDCard()) return; - else // skip everything else if there's no SD card, otherwise it might hang + // First attempt to open the file without creating a new one + if (openFile(filename, false, false)) goto writeRecord; + // Next try to create the file, bail if we couldn't create it + // This will not attempt to generate a new file name or add a header! + else if (openFile(filename, true, false)) goto writeRecord; + else { - // Convert the string filename to a character file name for SdFat - uint8_t fileNameLength = filename.length() + 1; - char charFileName[fileNameLength]; - filename.toCharArray(charFileName, fileNameLength); - - // Check that the file exists, just in case someone yanked the SD card - if (!logFile.open(charFileName, O_WRITE | O_AT_END)) - { - PRINTOUT(F("SD Card File Lost! Starting new file.\n")); - setupLogFile(filename, ""); - } + PRINTOUT(F("Unable to write to SD card!\n")); + return false; + } - // Write the CSV data + writeRecord: + { + // If we could successfully open or create the file, write the data to it logFile.println(rec); // Echo the line to the serial port PRINTOUT(F("\n \\/---- Line Saved to SD Card ----\\/ \n")); PRINTOUT(rec, F("\n")); // Set write/modification date time - setFileTimestame(logFile, T_WRITE); - // Set access date time - setFileTimestame(logFile, T_ACCESS); - + setFileTimestamp(logFile, T_WRITE); // Close the file to save it logFile.close(); + return true; } } -// This writes a record to the SD card, using the logger's filename -void Logger::logToSD(String rec) +bool Logger::logToSD(String& rec) { - logToSD(rec, _fileName); + return logToSD(_fileName, rec); +} +// NOTE: This is structured differently than the version with a string input +// record. This is to avoid the creation/passing of very long strings. +bool Logger::logToSD(void) +{ + // First attempt to open the file without creating a new one + if (openFile(_fileName, false, false)) goto writeRecord; + // Next try to create a new file, bail if we couldn't create it + // Generate a filename with the current date, if the file name isn't set + if (_autoFileName) generateAutoFileName(); + // Do add a default header to the new file! + if (openFile(_fileName, true, true)) goto writeRecord; + else return false; + + writeRecord: + { + // Write the data + streamSensorDataCSV(&logFile); + // Echo the line to the serial port + #if defined(STANDARD_SERIAL_OUTPUT) + PRINTOUT(F("\n \\/---- Line Saved to SD Card ----\\/ \n")); + streamSensorDataCSV(&STANDARD_SERIAL_OUTPUT); + PRINTOUT(F("\r\n")); + #endif + + // Set write/modification date time + setFileTimestamp(logFile, T_WRITE); + // Close the file to save it + logFile.close(); + return true; + } } @@ -685,29 +841,29 @@ void Logger::logToSD(String rec) // This checks to see if you want to enter the sensor mode // This should be run as the very last step within the setup function -void Logger::checkForTestingMode(int8_t buttonPin) -{ - // Set the pin attached to some button to enter debug mode - if (buttonPin > 0) pinMode(buttonPin, INPUT_PULLUP); - - // Flash the LED to let user know it is now possible to enter debug mode - for (uint8_t i = 0; i < 15; i++) - { - digitalWrite(_ledPin, HIGH); - delay(50); - digitalWrite(_ledPin, LOW); - delay(50); - } - - // Look for up to 5 seconds for a button press - PRINTOUT(F("Push button NOW to enter sensor testing mode.\n")); - for (uint32_t start = millis(); millis() - start < 5000; ) - { - if (digitalRead(buttonPin) == HIGH) testingMode(); - } - PRINTOUT(F("------------------------------------------\n\n")); - PRINTOUT(F("End of sensor testing mode.\n")); -} +// void Logger::checkForTestingMode(int8_t buttonPin) +// { +// // Set the pin attached to some button to enter debug mode +// if (buttonPin >= 0) pinMode(buttonPin, INPUT_PULLUP); +// +// // Flash the LED to let user know it is now possible to enter debug mode +// for (uint8_t i = 0; i < 15; i++) +// { +// digitalWrite(_ledPin, HIGH); +// delay(50); +// digitalWrite(_ledPin, LOW); +// delay(50); +// } +// +// // Look for up to 5 seconds for a button press +// PRINTOUT(F("Push button NOW to enter sensor testing mode.\n")); +// for (uint32_t start = millis(); millis() - start < 5000; ) +// { +// if (digitalRead(buttonPin) == HIGH) testingMode(); +// } +// PRINTOUT(F("------------------------------------------\n\n")); +// PRINTOUT(F("End of sensor testing mode.\n")); +// } // A static function if you'd prefer to enter testing based on an interrupt void Logger::testingISR() @@ -734,38 +890,38 @@ void Logger::testingMode() delay(100); // This seems to prevent crashes, no clue why .... // Power up all of the sensors - sensorsPowerUp(); + _internalArray->sensorsPowerUp(); // Wake up all of the sensors - sensorsWake(); + _internalArray->sensorsWake(); // Update the sensors and print out data 25 times for (uint8_t i = 0; i < 25; i++) { PRINTOUT(F("------------------------------------------\n")); // Update the values from all attached sensors - updateAllSensors(); + _internalArray->updateAllSensors(); // Print out the current logger time PRINTOUT(F("Current logger time is ")); PRINTOUT(formatDateTime_ISO8601(getNowEpoch()), F("\n")); PRINTOUT(F(" -----------------------\n")); // Print out the sensor data #if defined(STANDARD_SERIAL_OUTPUT) - printSensorData(&STANDARD_SERIAL_OUTPUT); + _internalArray->printSensorData(&STANDARD_SERIAL_OUTPUT); #endif PRINTOUT(F(" -----------------------\n")); delay(5000); } // Put sensors to sleep - sensorsSleep(); - sensorsPowerDown(); + _internalArray->sensorsSleep(); + _internalArray->sensorsPowerDown(); // Unset testing mode flag Logger::isTestingNow = false; // Sleep - if(_sleep){systemSleep();} + if(_mcuWakePin >= 0){systemSleep();} } @@ -777,7 +933,8 @@ void Logger::testingMode() void Logger::begin(void) { // Set up pins for the LED's - if (_ledPin > 0) pinMode(_ledPin, OUTPUT); + if (_ledPin >= 0) pinMode(_ledPin, OUTPUT); + if (_buttonPin >= 0) pinMode(_buttonPin, INPUT_PULLUP); #if defined ARDUINO_ARCH_SAMD zero_sleep_rtc.begin(); @@ -790,25 +947,39 @@ void Logger::testingMode() PRINTOUT(F("Current RTC time is: ")); PRINTOUT(formatDateTime_ISO8601(getNowEpoch()), F("\n")); - // Set up the sensors - setupSensors(); + PRINTOUT(F("Setting up logger "), _loggerID, F(" to record at "), + _loggingIntervalMinutes, F(" minute intervals.\n")); - // Set the filename for the logger to save to, if it hasn't been done - if(!_isFileNameSet){setFileName();} - else if(_autoFileName){setFileName();} - else setFileName(_fileName); // This just for a nice print-out + PRINTOUT(F("This logger has a variable array with "), + _internalArray->getVariableCount(), F(" variables, of which "), + _internalArray->getVariableCount() - _internalArray->getCalculatedVariableCount(), + F(" come from "),_internalArray->getSensorCount(), F(" sensors and "), + _internalArray->getCalculatedVariableCount(), F(" are calculated.\n")); - // Set up the log file - setupLogFile(); + // Create the log file, adding the default header to it + if (createLogFile(true)) PRINTOUT(F("Data will be saved as "), _fileName, '\n'); + else PRINTOUT(F("Unable to create a file to save data to!\n")); + + // Set up the sensors + _internalArray->setupSensors(); // Setup sleep mode - if(_sleep){setupSleep();} + if(_mcuWakePin >= 0){setupSleep();} + + // Set up the interrupt to be able to enter sensor testing mode + if (_buttonPin >= 0) + { + enableInterrupt(_buttonPin, Logger::testingISR, CHANGE); + PRINTOUT(F("Push button on pin ")); + PRINTOUT(_buttonPin); + PRINTOUT(F(" at any time to enter sensor testing mode.\n")); + } PRINTOUT(F("Logger setup finished!\n")); PRINTOUT(F("------------------------------------------\n\n")); // Sleep - if(_sleep){systemSleep();} + if(_mcuWakePin >= 0){systemSleep();} } @@ -825,28 +996,28 @@ void Logger::log(void) // Print a line to show new reading PRINTOUT(F("------------------------------------------\n")); // Turn on the LED to show we're taking a reading - digitalWrite(_ledPin, HIGH); + if (_ledPin >= 0) digitalWrite(_ledPin, HIGH); // Send power to all of the sensors MS_DBG(F(" Powering sensors...\n")); - sensorsPowerUp(); + _internalArray->sensorsPowerUp(); // Wake up all of the sensors MS_DBG(F(" Waking sensors...\n")); - sensorsWake(); + _internalArray->sensorsWake(); // Update the values from all attached sensors MS_DBG(F(" Updating sensor values...\n")); - updateAllSensors(); + _internalArray->updateAllSensors(); // Put sensors to sleep MS_DBG(F(" Putting sensors back to sleep...\n")); - sensorsSleep(); + _internalArray->sensorsSleep(); // Cut sensor power MS_DBG(F(" Cutting sensor power...\n")); - sensorsPowerDown(); + _internalArray->sensorsPowerDown(); // Create a csv data record and save it to the log file - logToSD(generateSensorDataCSV()); + logToSD(); // Turn off the LED - digitalWrite(_ledPin, LOW); + if (_ledPin >= 0) digitalWrite(_ledPin, LOW); // Print a line to show reading ended PRINTOUT(F("------------------------------------------\n\n")); @@ -858,5 +1029,5 @@ void Logger::log(void) if (Logger::startTesting) testingMode(); // Sleep - if(_sleep){systemSleep();} + if(_mcuWakePin >= 0){systemSleep();} } diff --git a/src/LoggerBase.h b/src/LoggerBase.h index e1e0cdcfb..7075694b9 100644 --- a/src/LoggerBase.h +++ b/src/LoggerBase.h @@ -29,23 +29,24 @@ // Bring in the library to commuinicate with an external high-precision real time clock // This also implements a needed date/time class #include -#define EPOCH_TIME_OFF 946684800 // This is 2000-jan-01 00:00:00 in epoch time -// Need this b/c the date/time class in Sodaq_DS3231 treats a 32-bit long timestamp -// as time from 2000-jan-01 00:00:00 instead of the standard epoch of 1970-jan-01 00:00:00 +#define EPOCH_TIME_OFF 946684800 +// This is 2000-jan-01 00:00:00 in "epoch" time +// Need this b/c the date/time class in Sodaq_DS3231 treats a 32-bit long +// timestamp as time from 2000-jan-01 00:00:00 instead of the standard (unix) +// epoch beginning 1970-jan-01 00:00:00. #include // To communicate with the SD card +static String LOGGER_EMPTY = ""; + // Defines the "Logger" Class -class Logger : public VariableArray +class Logger { public: - // Initialization - cannot do this in constructor arduino has issues creating - // instances of classes with non-empty constructors - void init(int8_t SDCardPin, int8_t mcuWakePin, - uint8_t variableCount, - Variable *variableList[], - uint8_t loggingIntervalMinutes, - const char *loggerID = 0); + // Constructor + Logger(const char *loggerID, uint16_t loggingIntervalMinutes, + int8_t SDCardPin, int8_t mcuWakePin, + VariableArray *inputArray); // Sets the static timezone - this must be set static void setTimeZone(int8_t timeZone); @@ -61,6 +62,9 @@ class Logger : public VariableArray // Sets up a pin for an LED or other way of alerting that data is being logged void setAlertPin(int8_t ledPin); + // Sets up a pin for an interrupt to enter testing mode + void setTestingModePin(int8_t buttonPin); + // ===================================================================== // // Public functions to access the clock in proper format and time zone @@ -77,7 +81,7 @@ class Logger : public VariableArray static DateTime dtFromEpoch(uint32_t epochTime); // This converts a date-time object into a ISO8601 formatted string - static String formatDateTime_ISO8601(DateTime dt); + static String formatDateTime_ISO8601(DateTime& dt); // This converts an epoch time (unix time) into a ISO8601 formatted string static String formatDateTime_ISO8601(uint32_t epochTime); @@ -123,52 +127,48 @@ class Logger : public VariableArray // Public functions for logging data to an SD card // ===================================================================== // // This sets a file name, if you want to decide on it in advance - void setFileName(char *fileName); + void setFileName(const char *fileName); // Same as above, with a string (overload function) - void setFileName(String fileName); - // This generates a file name from the logger id and the current date - // This will be used if the setFileName function is not called before - // the begin() function is called. - void setFileName(void); + void setFileName(String& fileName); // This returns the current filename. Must be run after setFileName. String getFileName(void){return _fileName;} - // This is a PRE-PROCESSOR MACRO to speed up generating header rows - // Again, THIS IS NOT A FUNCTION, it is a pre-processor macro - #define makeHeaderRowMacro(firstCol, function) \ - dataHeader += F("\""); \ - dataHeader += firstCol; \ - dataHeader += F("\","); \ - for (uint8_t i = 0; i < _variableCount; i++) \ - { \ - dataHeader += F("\""); \ - dataHeader += function; \ - dataHeader += F("\""); \ - if (i + 1 != _variableCount) \ - { \ - dataHeader += F(","); \ - } \ - } \ - dataHeader += F("\r\n"); - // This creates a header for the logger file virtual String generateFileHeader(void); + // This prints a header onto a stream - this removes need to pass around + // very long string objects which can crash the logger + virtual void streamFileHeader(Stream *stream); // This generates a comma separated list of volues of sensor data - including the time String generateSensorDataCSV(void); - - // This initializes a file on the SD card with the given filename and - // writes the given header to it - void setupLogFile(String filename, String header); - // This initializes a file on the SD card using the logger's filename and - // writes a header to it based on the sensors attached to variables in the array - void setupLogFile(void); - - // This writes a record to the SD card with the given filename - void logToSD(String rec, String filename); - // This writes a record to the SD card, using the logger's filename - void logToSD(String rec); + // This sends a comma separated list of volues of sensor data - including the + // time - out over an Arduino stream + void streamSensorDataCSV(Stream *stream); + + // These functions create a file on an SD card and set the created/modified/ + // accessed timestamps in that file. + // The filename may either be the one automatically generated by the logger + // id and the date, the one set by setFileName(String), or can be specified + // in the function. + // If asked to, these functions will also write a header to the file based + // on the variable information from the variable array. + // This can be used to force a logger to create a file with a secondary file name. + bool createLogFile(String& filename, bool writeDefaultHeader = false); + bool createLogFile(bool writeDefaultHeader = false); + + // These functions create a file on an SD card and set the modified/accessed + // timestamps in that file. + // The filename may either be the one automatically generated by the logger + // id and the date, the one set by setFileName(String), or can be specified + // in the function. + // If the file does not already exist, the file will be created. + // The line to be written to the file can either be specified or will be + // a comma separated list of the current values of all variables in the + // variable array. + bool logToSD(String& filename, String& rec); + bool logToSD(String& rec); + bool logToSD(void); // ===================================================================== // @@ -177,7 +177,7 @@ class Logger : public VariableArray // This checks to see if you want to enter the sensor mode // This should be run as the very last step within the setup function - void checkForTestingMode(int8_t buttonPin); + // void checkForTestingMode(int8_t buttonPin); // A function if you'd prefer to enter testing based on an interrupt static void testingISR(void); @@ -185,6 +185,7 @@ class Logger : public VariableArray // This defines what to do in the testing mode virtual void testingMode(); + // ===================================================================== // // Convience functions to call several of the above functions // ===================================================================== // @@ -199,8 +200,8 @@ class Logger : public VariableArray static uint32_t markedEpochTime; // These are flag fariables noting the current state (logging/testing) - // NOTE: if the logger isn't currently logging or testing or in the middle of set-up, - // it's probably sleeping + // NOTE: if the logger isn't currently logging or testing or in the middle + // of set-up, it's probably sleeping // Setting these as volatile because the flags can be changed in ISR's static volatile bool isLoggingNow; static volatile bool isTestingNow; @@ -215,8 +216,9 @@ class Logger : public VariableArray // The SD card and file SdFat sd; - SdFile logFile; + File logFile; String _fileName; + bool _autoFileName; // Static variables - identical for EVERY logger static int8_t _timeZone; @@ -227,23 +229,31 @@ class Logger : public VariableArray static char markedISO8601Time[26]; // Initialization variables + const char *_loggerID; + uint16_t _loggingIntervalMinutes; int8_t _SDCardPin; int8_t _mcuWakePin; - float _loggingIntervalMinutes; - uint16_t _loggingIntervalSeconds; - const char *_loggerID; - bool _autoFileName; - bool _isFileNameSet; + VariableArray *_internalArray; + uint8_t _numTimepointsLogged; - bool _sleep; int8_t _ledPin; + int8_t _buttonPin; // This checks if the SD card is available and ready - bool initializeSDCard(uint8_t Pin); + // We run this check before every communication with the SD card to prevent + // hanging. bool initializeSDCard(void); + // This generates a file name from the logger id and the current date + // NOTE: This cannot be called until *after* the RTC is started + void generateAutoFileName(void); + // This sets a timestamp on a file - void setFileTimestame(SdFile fileToStamp, uint8_t stampFlag); + void setFileTimestamp(File fileToStamp, uint8_t stampFlag); + + // This opens or creates a file, converting a string file name to a + // character file name + bool openFile(String& filename, bool createFile, bool writeDefaultHeader); }; #endif diff --git a/src/LoggerDreamHost.cpp b/src/LoggerDreamHost.cpp index 7560cb2a7..db751b036 100644 --- a/src/LoggerDreamHost.cpp +++ b/src/LoggerDreamHost.cpp @@ -14,6 +14,13 @@ // Functions for the SWRC Sensors DreamHost data receivers. // ============================================================================ +// Constructor +LoggerDreamHost::LoggerDreamHost(const char *loggerID, uint16_t loggingIntervalMinutes, + int8_t SDCardPin, int8_t mcuWakePin, + VariableArray *inputArray) + : LoggerEnviroDIY(loggerID, loggingIntervalMinutes, SDCardPin, mcuWakePin, inputArray) + {} + // Functions for private SWRC server void LoggerDreamHost::setDreamHostPortalRX(const char *URL) { @@ -31,36 +38,48 @@ String LoggerDreamHost::generateSensorDataDreamHost(void) dhString += F("&Loggertime="); dhString += String(Logger::markedEpochTime - 946684800); // Coorect time from epoch to y2k - for (int i = 0; i < Logger::_variableCount; i++) + for (int i = 0; i < _internalArray->getVariableCount(); i++) { dhString += F("&"); - dhString += Logger::_variableList[i]->getVarCode(); + dhString += _internalArray->arrayOfVars[i]->getVarCode(); dhString += F("="); - dhString += Logger::_variableList[i]->getValueString(); + dhString += _internalArray->arrayOfVars[i]->getValueString(); } return dhString; } - - -// This generates a fully structured GET request for DreamHost -String LoggerDreamHost::generateDreamHostGetRequest(String fullURL) -{ - String GETstring = String(F("GET ")); - GETstring += String(fullURL); - GETstring += String(F(" HTTP/1.1")); - GETstring += String(F("\r\nHost: swrcsensors.dreamhosters.com")); - GETstring += String(F("\r\n\r\n")); - return GETstring; -} -String LoggerDreamHost::generateDreamHostGetRequest(void) +void LoggerDreamHost::streamSensorDataDreamHost(Stream *stream) { - return generateDreamHostGetRequest(generateSensorDataDreamHost()); + stream->print(String(_DreamHostPortalRX)); + stream->print(String(F("?LoggerID=")) + String(Logger::_loggerID)); + stream->print(String(F("?Loggertime=")) + String(Logger::markedEpochTime - 946684800)); // Correct time from epoch to y2k + + for (int i = 0; i < _internalArray->getVariableCount(); i++) + { + stream->print(String(F("&")) + String(_internalArray->arrayOfVars[i]->getVarCode()) \ + + String(F("=")) + String(_internalArray->arrayOfVars[i]->getValueString())); + } } +// // This generates a fully structured GET request for DreamHost +// String LoggerDreamHost::generateDreamHostGetRequest(String fullURL) +// { +// String GETstring = String(F("GET ")); +// GETstring += String(fullURL); +// GETstring += String(F(" HTTP/1.1")); +// GETstring += String(F("\r\nHost: swrcsensors.dreamhosters.com")); +// GETstring += String(F("\r\n\r\n")); +// return GETstring; +// } +// String LoggerDreamHost::generateDreamHostGetRequest(void) +// { +// return generateDreamHostGetRequest(generateSensorDataDreamHost()); +// } + + // This prints a fully structured GET request for DreamHost to the // specified stream using the specified url. -void LoggerDreamHost::streamDreamHostRequest(Stream *stream, String fullURL) +void LoggerDreamHost::streamDreamHostRequest(Stream *stream, String& fullURL) { stream->print(String(F("GET "))); stream->print(fullURL); @@ -70,18 +89,13 @@ void LoggerDreamHost::streamDreamHostRequest(Stream *stream, String fullURL) } void LoggerDreamHost::streamDreamHostRequest(Stream *stream) { + // Start the request stream->print(String(F("GET "))); - stream->print(String(_DreamHostPortalRX)); - stream->print(String(F("?LoggerID=")) + String(Logger::_loggerID)); - stream->print(String(F("?Loggertime=")) + String(Logger::markedEpochTime - 946684800)); // Correct time from epoch to y2k - - for (int i = 0; i < Logger::_variableCount; i++) - { - stream->print(String(F("&")) + String(Logger::_variableList[i]->getVarCode()) \ - + String(F("=")) + String(Logger::_variableList[i]->getValueString())); - } + // Stream the full URL with parameters + streamSensorDataDreamHost(stream); + // Send the rest of the HTTP header stream->print(String(F(" HTTP/1.1"))); stream->print(String(F("\r\nHost: swrcsensors.dreamhosters.com"))); stream->print(String(F("\r\n\r\n"))); @@ -89,7 +103,7 @@ void LoggerDreamHost::streamDreamHostRequest(Stream *stream) // Post the data to dream host. -int LoggerDreamHost::postDataDreamHost(String fullURL) +int LoggerDreamHost::postDataDreamHost(String& fullURL) { // do not continue if no modem! if (!_modemAttached) @@ -103,7 +117,7 @@ int LoggerDreamHost::postDataDreamHost(String fullURL) int did_respond = 0; // Open a TCP/IP connection to DreamHost - if(_logModem->openTCP("swrcsensors.dreamhosters.com", 80)) + if(_logModem.openTCP("swrcsensors.dreamhosters.com", 80)) { // Send the request to the serial for debugging #if defined(STANDARD_SERIAL_OUTPUT) @@ -114,23 +128,23 @@ int LoggerDreamHost::postDataDreamHost(String fullURL) #endif // Send the request to the modem stream - if (fullURL.length() > 1) streamDreamHostRequest(_logModem->_client, fullURL); - else streamDreamHostRequest(_logModem->_client); - _logModem->_client->flush(); // wait for sending to finish + if (fullURL.length() > 1) streamDreamHostRequest(_logModem._client, fullURL); + else streamDreamHostRequest(_logModem._client); + _logModem._client->flush(); // wait for sending to finish uint32_t start_timer = millis(); - while ((millis() - start_timer) < 10000L && _logModem->_client->available() < 12) + while ((millis() - start_timer) < 10000L && _logModem._client->available() < 12) {delay(10);} // Read only the first 12 characters of the response // We're only reading as far as the http code, anything beyond that // we don't care about so we're not reading to save on total // data used for transmission. - did_respond = _logModem->_client->readBytes(response_buffer, 12); + did_respond = _logModem._client->readBytes(response_buffer, 12); // Close the TCP/IP connection as soon as the first 12 characters are read // We don't need anything else and stoping here should save data use. - _logModem->closeTCP(); + _logModem.closeTCP(); } else PRINTOUT(F("\n -- Unable to Establish Connection to DreamHost -- \n")); @@ -178,34 +192,35 @@ void LoggerDreamHost::log(void) // Print a line to show new reading PRINTOUT(F("------------------------------------------\n")); // Turn on the LED to show we're taking a reading - digitalWrite(_ledPin, HIGH); + if (_ledPin >= 0) digitalWrite(_ledPin, HIGH); if (_modemAttached) { // Turn on the modem to let it start searching for the network - _logModem->modemPowerUp(); + _logModem.modemPowerUp(); } // Send power to all of the sensors MS_DBG(F(" Powering sensors...\n")); - sensorsPowerUp(); + _internalArray->sensorsPowerUp(); // Wake up all of the sensors MS_DBG(F(" Waking sensors...\n")); - sensorsWake(); + _internalArray->sensorsWake(); // Update the values from all attached sensors MS_DBG(F(" Updating sensor values...\n")); - updateAllSensors(); + _internalArray->updateAllSensors(); // Put sensors to sleep MS_DBG(F(" Putting sensors back to sleep...\n")); - sensorsSleep(); + _internalArray->sensorsSleep(); // Cut sensor power MS_DBG(F(" Cutting sensor power...\n")); - sensorsPowerDown(); + _internalArray->sensorsPowerDown(); if (_modemAttached) { // Connect to the network - if (_logModem->connectInternet()) + MS_DBG(F(" Connecting to the Internet...\n")); + if (_logModem.connectInternet()) { if(_dualPost) { @@ -217,23 +232,25 @@ void LoggerDreamHost::log(void) postDataDreamHost(); // Sync the clock every 288 readings (1/day at 5 min intervals) + MS_DBG(F(" Running a daily clock sync...\n")); if (_numTimepointsLogged % 288 == 0) { - syncRTClock(_logModem->getNISTTime()); + syncRTClock(_logModem.getNISTTime()); } // Disconnect from the network - _logModem->disconnectInternet(); + MS_DBG(F(" Disconnecting from the Internet...\n")); + _logModem.disconnectInternet(); } // Turn the modem off - _logModem->modemPowerDown(); + _logModem.modemPowerDown(); } // Create a csv data record and save it to the log file - logToSD(generateSensorDataCSV()); + logToSD(); // Turn off the LED - digitalWrite(_ledPin, LOW); + if (_ledPin >= 0) digitalWrite(_ledPin, LOW); // Print a line to show reading ended PRINTOUT(F("------------------------------------------\n\n")); @@ -245,5 +262,5 @@ void LoggerDreamHost::log(void) if (Logger::startTesting) testingMode(); // Sleep - if(_sleep){systemSleep();} + if(_mcuWakePin >= 0){systemSleep();} } diff --git a/src/LoggerDreamHost.h b/src/LoggerDreamHost.h index 2ad5321b5..ef0805ec6 100644 --- a/src/LoggerDreamHost.h +++ b/src/LoggerDreamHost.h @@ -26,20 +26,26 @@ class LoggerDreamHost : public LoggerEnviroDIY { public: + // Constructor + LoggerDreamHost(const char *loggerID, uint16_t loggingIntervalMinutes, + int8_t SDCardPin, int8_t mcuWakePin, + VariableArray *inputArray); + // Functions for private SWRC server void setDreamHostPortalRX(const char *URL); // This creates all of the URL parameters String generateSensorDataDreamHost(void); + void streamSensorDataDreamHost(Stream *stream); - // This generates a fully structured GET request for DreamHost - String generateDreamHostGetRequest(String fullURL); - String generateDreamHostGetRequest(void); + // // This generates a fully structured GET request for DreamHost + // String generateDreamHostGetRequest(String fullURL); + // String generateDreamHostGetRequest(void); // This prints a fully structured GET request for DreamHost to the // specified stream using the specified url. // This may be necessary to work around very long strings for the post request. - void streamDreamHostRequest(Stream *stream, String fullURL); + void streamDreamHostRequest(Stream *stream, String& fullURL); // This prints a fully structured GET request for DreamHost to the // specified stream with the default url. void streamDreamHostRequest(Stream *stream); @@ -48,7 +54,7 @@ class LoggerDreamHost : public LoggerEnviroDIY // DreamHost URL and then streams out a get request // over that connection. // The return is the http status code of the response. - int postDataDreamHost(String fullURL = ""); + int postDataDreamHost(String& fullURL = LOGGER_EMPTY); // This prevents the logging function from dual-posting to EnviroDIY void disableDualPost(void); diff --git a/src/LoggerEnviroDIY.cpp b/src/LoggerEnviroDIY.cpp index 6b137601a..d1358cd2b 100644 --- a/src/LoggerEnviroDIY.cpp +++ b/src/LoggerEnviroDIY.cpp @@ -10,13 +10,21 @@ #include "LoggerEnviroDIY.h" +// To prevent compiler/linker crashes with Enable interrupt +#define LIBCALL_ENABLEINTERRUPT +// To handle external and pin change interrupts +#include + // ============================================================================ // Functions for the EnviroDIY data portal receivers. // ============================================================================ // Constructor -LoggerEnviroDIY::LoggerEnviroDIY() +LoggerEnviroDIY::LoggerEnviroDIY(const char *loggerID, uint16_t loggingIntervalMinutes, + int8_t SDCardPin, int8_t mcuWakePin, + VariableArray *inputArray) + : Logger(loggerID, loggingIntervalMinutes, SDCardPin, mcuWakePin, inputArray) { _modemAttached = false; } @@ -25,7 +33,7 @@ LoggerEnviroDIY::LoggerEnviroDIY() // Set up communications // Adds a loggerModem objct to the logger // loggerModem = TinyGSM modem + TinyGSM client + Modem On Off -void LoggerEnviroDIY::attachModem(loggerModem *modem) +void LoggerEnviroDIY::attachModem(loggerModem& modem) { _logModem = modem; _modemAttached = true; @@ -50,18 +58,21 @@ void LoggerEnviroDIY::setSamplingFeatureUUID(const char *samplingFeature) // This adds extra data to the datafile header String LoggerEnviroDIY::generateFileHeader(void) { - String dataHeader = ""; - - // Add additional UUID information - String SFHeaderString = F("Sampling Feature: "); - SFHeaderString += _samplingFeature; - makeHeaderRowMacro(SFHeaderString, _variableList[i]->getVarUUID()) - + // All we're doing is putting the Sampling Feature UUID at the top + String dataHeader = F("Sampling Feature: "); + dataHeader += _samplingFeature; + dataHeader += "\r\n"; // Put the basic header below dataHeader += Logger::generateFileHeader(); return dataHeader; } +void LoggerEnviroDIY::streamFileHeader(Stream *stream) +{ + stream->print(F("Sampling Feature: ")); + stream->println(_samplingFeature); + Logger::streamFileHeader(stream); +} // This generates a properly formatted JSON for EnviroDIY @@ -73,12 +84,12 @@ String LoggerEnviroDIY::generateSensorDataJSON(void) jsonString += F("\"timestamp\": \""); jsonString += String(Logger::markedISO8601Time) + F("\", "); - for (int i = 0; i < Logger::_variableCount; i++) + for (int i = 0; i < _internalArray->getVariableCount(); i++) { jsonString += F("\""); - jsonString += Logger::_variableList[i]->getVarUUID() + F("\": "); - jsonString += Logger::_variableList[i]->getValueString(); - if (i + 1 != Logger::_variableCount) + jsonString += _internalArray->arrayOfVars[i]->getVarUUID() + F("\": "); + jsonString += _internalArray->arrayOfVars[i]->getValueString(); + if (i + 1 != _internalArray->getVariableCount()) { jsonString += F(", "); } @@ -87,29 +98,49 @@ String LoggerEnviroDIY::generateSensorDataJSON(void) jsonString += F("}"); return jsonString; } +void LoggerEnviroDIY::streamSensorDataJSON(Stream *stream) +{ + stream->print(String(F("{"))); + stream->print(String(F("\"sampling_feature\": \""))); + stream->print(String(_samplingFeature)); + F(""); + stream->print(String(F("\", \"timestamp\": \""))); + stream->print(String(Logger::markedISO8601Time) + F("\", ")); + for (int i = 0; i < _internalArray->getVariableCount(); i++) + { + stream->print(String(F("\"")) + _internalArray->arrayOfVars[i]->getVarUUID() + String(F("\": ")) + _internalArray->arrayOfVars[i]->getValueString()); + if (i + 1 != _internalArray->getVariableCount()) + { + stream->print(F(", ")); + } + } -// This generates a fully structured POST request for EnviroDIY -String LoggerEnviroDIY::generateEnviroDIYPostRequest(String enviroDIYjson) -{ - String POSTstring = String(F("POST /api/data-stream/ HTTP/1.1")); - POSTstring += String(F("\r\nHost: data.envirodiy.org")); - POSTstring += String(F("\r\nTOKEN: ")) + String(_registrationToken); - // POSTstring += String(F("\r\nCache-Control: no-cache")); - // POSTstring += String(F("\r\nConnection: close")); - POSTstring += String(F("\r\nContent-Length: ")) + String(enviroDIYjson.length()); - POSTstring += String(F("\r\nContent-Type: application/json\r\n\r\n")); - POSTstring += String(enviroDIYjson); - return POSTstring; -} -String LoggerEnviroDIY::generateEnviroDIYPostRequest(void) -{ - return generateEnviroDIYPostRequest(generateSensorDataJSON()); + stream->print(F("}")); } + +// // This generates a fully structured POST request for EnviroDIY +// String LoggerEnviroDIY::generateEnviroDIYPostRequest(String enviroDIYjson) +// { +// String POSTstring = String(F("POST /api/data-stream/ HTTP/1.1")); +// POSTstring += String(F("\r\nHost: data.envirodiy.org")); +// POSTstring += String(F("\r\nTOKEN: ")) + String(_registrationToken); +// // POSTstring += String(F("\r\nCache-Control: no-cache")); +// // POSTstring += String(F("\r\nConnection: close")); +// POSTstring += String(F("\r\nContent-Length: ")) + String(enviroDIYjson.length()); +// POSTstring += String(F("\r\nContent-Type: application/json\r\n\r\n")); +// POSTstring += String(enviroDIYjson); +// return POSTstring; +// } +// String LoggerEnviroDIY::generateEnviroDIYPostRequest(void) +// { +// return generateEnviroDIYPostRequest(generateSensorDataJSON()); +// } + + // This prints a fully structured post request for EnviroDIY to the // specified stream using the specified json. -void LoggerEnviroDIY::streamEnviroDIYRequest(Stream *stream, String enviroDIYjson) +void LoggerEnviroDIY::streamEnviroDIYRequest(Stream *stream, String& enviroDIYjson) { stream->print(String(F("POST /api/data-stream/ HTTP/1.1"))); stream->print(String(F("\r\nHost: data.envirodiy.org"))); @@ -122,25 +153,27 @@ void LoggerEnviroDIY::streamEnviroDIYRequest(Stream *stream, String enviroDIYjso } void LoggerEnviroDIY::streamEnviroDIYRequest(Stream *stream) { - // first we need to calculate how long the json string is going to be + // First we need to calculate how long the json string is going to be + // This is needed for the "Content-Length" header int jsonLength = 22; // {"sampling_feature": " jsonLength += 36; // sampling feature UUID jsonLength += 17; // ", "timestamp": " jsonLength += 25; // markedISO8601Time jsonLength += 3; // ",_ - for (int i = 0; i < Logger::_variableCount; i++) + for (int i = 0; i < _internalArray->getVariableCount(); i++) { jsonLength += 1; // " jsonLength += 36; // variable UUID jsonLength += 3; // ":_ - jsonLength += Logger::_variableList[i]->getValueString().length(); - if (i + 1 != Logger::_variableCount) + jsonLength += _internalArray->arrayOfVars[i]->getValueString().length(); + if (i + 1 != _internalArray->getVariableCount()) { jsonLength += 2; // ,_ } } jsonLength += 1; // } + // Stream the HTTP headers for the post request stream->print(String(F("POST /api/data-stream/ HTTP/1.1"))); stream->print(String(F("\r\nHost: data.envirodiy.org"))); stream->print(String(F("\r\nTOKEN: ")) + String(_registrationToken)); @@ -148,21 +181,9 @@ void LoggerEnviroDIY::streamEnviroDIYRequest(Stream *stream) // stream->print(String(F("\r\nConnection: close"))); stream->print(String(F("\r\nContent-Length: ")) + String(jsonLength)); stream->print(String(F("\r\nContent-Type: application/json\r\n\r\n"))); - stream->print(String(F("{\"sampling_feature\": \""))); - stream->print(String(_samplingFeature)); + F(""); - stream->print(String(F("\", \"timestamp\": \""))); - stream->print(String(Logger::markedISO8601Time) + F("\", ")); - for (int i = 0; i < Logger::_variableCount; i++) - { - stream->print(String(F("\"")) + Logger::_variableList[i]->getVarUUID() + String(F("\": ")) + Logger::_variableList[i]->getValueString()); - if (i + 1 != Logger::_variableCount) - { - stream->print(F(", ")); - } - } - - stream->print(F("}")); + // Stream the JSON itself + streamSensorDataJSON(stream); } @@ -170,7 +191,7 @@ void LoggerEnviroDIY::streamEnviroDIYRequest(Stream *stream) // EnviroDIY/ODM2DataSharingPortal and then streams out a post request // over that connection. // The return is the http status code of the response. -int LoggerEnviroDIY::postDataEnviroDIY(String enviroDIYjson) +int LoggerEnviroDIY::postDataEnviroDIY(String& enviroDIYjson) { // do not continue if no modem! if (!_modemAttached) @@ -184,7 +205,7 @@ int LoggerEnviroDIY::postDataEnviroDIY(String enviroDIYjson) int did_respond = 0; // Open a TCP/IP connection to the Enviro DIY Data Portal (WebSDL) - if(_logModem->openTCP("data.envirodiy.org", 80)) + if(_logModem.openTCP("data.envirodiy.org", 80)) { // Send the request to the serial for debugging #if defined(STANDARD_SERIAL_OUTPUT) @@ -196,23 +217,23 @@ int LoggerEnviroDIY::postDataEnviroDIY(String enviroDIYjson) #endif // Send the request to the modem stream - if (enviroDIYjson.length() > 1) streamEnviroDIYRequest(_logModem->_client, enviroDIYjson); - else streamEnviroDIYRequest(_logModem->_client); - _logModem->_client->flush(); // wait for sending to finish + if (enviroDIYjson.length() > 1) streamEnviroDIYRequest(_logModem._client, enviroDIYjson); + else streamEnviroDIYRequest(_logModem._client); + _logModem._client->flush(); // wait for sending to finish uint32_t start_timer = millis(); - while ((millis() - start_timer) < 10000L && _logModem->_client->available() < 12) + while ((millis() - start_timer) < 10000L && _logModem._client->available() < 12) {delay(10);} // Read only the first 12 characters of the response // We're only reading as far as the http code, anything beyond that // we don't care about so we're not reading to save on total // data used for transmission. - did_respond = _logModem->_client->readBytes(response_buffer, 12); + did_respond = _logModem._client->readBytes(response_buffer, 12); // Close the TCP/IP connection as soon as the first 12 characters are read // We don't need anything else and stoping here should save data use. - _logModem->closeTCP(); + _logModem.closeTCP(); } else PRINTOUT(F("\n -- Unable to Establish Connection to EnviroDIY Data Portal -- \n")); @@ -237,7 +258,7 @@ int LoggerEnviroDIY::postDataEnviroDIY(String enviroDIYjson) // ===================================================================== // -// Convience functions to call several of the above functions +// Public functions for a "sensor testing" mode // ===================================================================== // // This defines what to do in the testing mode @@ -256,39 +277,39 @@ void LoggerEnviroDIY::testingMode() { // Turn on the modem to let it start searching for the network // Turn on the modem - _logModem->modemPowerUp(); + _logModem.modemPowerUp(); // Connect to the network to make sure we have signal (only try for 10sec) - _logModem->connectInternet(10000L); + _logModem.connectInternet(10000L); } // Power up all of the sensors - sensorsPowerUp(); + _internalArray->sensorsPowerUp(); // Wake up all of the sensors - sensorsWake(); + _internalArray->sensorsWake(); // Update the sensors and print out data 25 times for (uint8_t i = 0; i < 25; i++) { PRINTOUT(F("------------------------------------------\n")); // Update the values from all attached sensors - updateAllSensors(); + _internalArray->updateAllSensors(); // Print out the current logger time PRINTOUT(F("Current logger time is ")); PRINTOUT(formatDateTime_ISO8601(getNowEpoch()), F("\n")); PRINTOUT(F(" -----------------------\n")); // Print out the sensor data #if defined(STANDARD_SERIAL_OUTPUT) - printSensorData(&STANDARD_SERIAL_OUTPUT); + _internalArray->printSensorData(&STANDARD_SERIAL_OUTPUT); #endif PRINTOUT(F(" -----------------------\n")); if (_modemAttached) { // Specially highlight the modem signal quality in the debug mode - _logModem->update(); + _logModem.update(); PRINTOUT(F("Current modem signal is ")); - PRINTOUT(_logModem->getSignalPercent()); + PRINTOUT(_logModem.getSignalPercent()); PRINTOUT(F("%\n")); } @@ -296,30 +317,35 @@ void LoggerEnviroDIY::testingMode() } // Put sensors to sleep - sensorsSleep(); - sensorsPowerDown(); + _internalArray->sensorsSleep(); + _internalArray->sensorsPowerDown(); if (_modemAttached) { // Disconnect from the network - _logModem->disconnectInternet(); + _logModem.disconnectInternet(); // Turn off the modem - _logModem->modemPowerDown(); + _logModem.modemPowerDown(); } // Unset testing mode flag Logger::isTestingNow = false; // Sleep - if(_sleep){systemSleep();} + if(_mcuWakePin >= 0){systemSleep();} } +// ===================================================================== // +// Convience functions to call several of the above functions +// ===================================================================== // + // This calls all of the setup functions - must be run AFTER init void LoggerEnviroDIY::begin(void) { // Set up pins for the LED's - if (_ledPin > 0) pinMode(_ledPin, OUTPUT); + if (_ledPin >= 0) pinMode(_ledPin, OUTPUT); + if (_buttonPin >= 0) pinMode(_buttonPin, INPUT_PULLUP); #if defined ARDUINO_ARCH_SAMD zero_sleep_rtc.begin(); @@ -332,46 +358,64 @@ void LoggerEnviroDIY::begin(void) PRINTOUT(F("Current RTC time is: ")); PRINTOUT(formatDateTime_ISO8601(getNowEpoch()), F("\n")); + PRINTOUT(F("Setting up logger "), _loggerID, F(" to record at "), + _loggingIntervalMinutes, F(" minute intervals.\n")); + + PRINTOUT(F("This logger has a variable array with "), + _internalArray->getVariableCount(), F(" variables, of which "), + _internalArray->getVariableCount() - _internalArray->getCalculatedVariableCount(), + F(" come from "),_internalArray->getSensorCount(), F(" sensors and "), + _internalArray->getCalculatedVariableCount(), F(" are calculated.\n")); + + // Create the log file, adding the default header to it + if (createLogFile(true)) PRINTOUT(F("Data will be saved as "), _fileName, '\n'); + else PRINTOUT(F("Unable to create a file to save data to!")); + if (_modemAttached) { + // Print out the modem info + PRINTOUT(F("This logger is also tied to a ")); + PRINTOUT(_logModem.getSensorName(), F(" for internet connectivity.\n")); // Turn on the modem to let it start searching for the network - _logModem->modemPowerUp(); + _logModem.modemPowerUp(); } // Set up the sensors - setupSensors(); + _internalArray->setupSensors(); if (_modemAttached) { // Synchronize the RTC with NIST PRINTOUT(F("Attempting to synchronize RTC with NIST\n")); + PRINTOUT(F("This may take up to two minutes!\n")); // Connect to the network - if (_logModem->connectInternet(120000L)) + if (_logModem.connectInternet(120000L)) { - syncRTClock(_logModem->getNISTTime()); + syncRTClock(_logModem.getNISTTime()); // Disconnect from the network - _logModem->disconnectInternet(); + _logModem.disconnectInternet(); } // Turn off the modem - _logModem->modemPowerDown(); + _logModem.modemPowerDown(); } - // Set the filename for the logger to save to, if it hasn't been done - if(!_isFileNameSet){setFileName();} - else if(_autoFileName){setFileName();} - else setFileName(_fileName); // This just for a nice print-out - - // Set up the log file - setupLogFile(); - // Setup sleep mode - if(_sleep){setupSleep();} + if(_mcuWakePin >= 0){setupSleep();} + + // Set up the interrupt to be able to enter sensor testing mode + if (_buttonPin >= 0) + { + enableInterrupt(_buttonPin, Logger::testingISR, CHANGE); + PRINTOUT(F("Push button on pin ")); + PRINTOUT(_buttonPin); + PRINTOUT(F(" at any time to enter sensor testing mode.\n")); + } PRINTOUT(F("Logger setup finished!\n")); PRINTOUT(F("------------------------------------------\n\n")); // Sleep - if(_sleep){systemSleep();} + if(_mcuWakePin >= 0){systemSleep();} } @@ -388,56 +432,59 @@ void LoggerEnviroDIY::log(void) // Print a line to show new reading PRINTOUT(F("------------------------------------------\n")); // Turn on the LED to show we're taking a reading - digitalWrite(_ledPin, HIGH); + if (_ledPin >= 0) digitalWrite(_ledPin, HIGH); if (_modemAttached) { // Turn on the modem to let it start searching for the network - _logModem->modemPowerUp(); + _logModem.modemPowerUp(); } // Send power to all of the sensors MS_DBG(F(" Powering sensors...\n")); - sensorsPowerUp(); + _internalArray->sensorsPowerUp(); // Wake up all of the sensors MS_DBG(F(" Waking sensors...\n")); - sensorsWake(); + _internalArray->sensorsWake(); // Update the values from all attached sensors MS_DBG(F(" Updating sensor values...\n")); - updateAllSensors(); + _internalArray->updateAllSensors(); // Put sensors to sleep MS_DBG(F(" Putting sensors back to sleep...\n")); - sensorsSleep(); + _internalArray->sensorsSleep(); // Cut sensor power MS_DBG(F(" Cutting sensor power...\n")); - sensorsPowerDown(); + _internalArray->sensorsPowerDown(); if (_modemAttached) { // Connect to the network - if (_logModem->connectInternet()) + MS_DBG(F(" Connecting to the Internet...\n")); + if (_logModem.connectInternet()) { // Post the data to the WebSDL postDataEnviroDIY(); // Sync the clock every 288 readings (1/day at 5 min intervals) + MS_DBG(F(" Running a daily clock sync...\n")); if (_numTimepointsLogged % 288 == 0) { - syncRTClock(_logModem->getNISTTime()); + syncRTClock(_logModem.getNISTTime()); } // Disconnect from the network - _logModem->disconnectInternet(); + MS_DBG(F(" Disconnecting from the Internet...\n")); + _logModem.disconnectInternet(); } // Turn the modem off - _logModem->modemPowerDown(); + _logModem.modemPowerDown(); } // Create a csv data record and save it to the log file - logToSD(generateSensorDataCSV()); + logToSD(); // Turn off the LED - digitalWrite(_ledPin, LOW); + if (_ledPin >= 0) digitalWrite(_ledPin, LOW); // Print a line to show reading ended PRINTOUT(F("------------------------------------------\n\n")); @@ -449,5 +496,5 @@ void LoggerEnviroDIY::log(void) if (Logger::startTesting) testingMode(); // Sleep - if(_sleep){systemSleep();} + if(_mcuWakePin >= 0){systemSleep();} } diff --git a/src/LoggerEnviroDIY.h b/src/LoggerEnviroDIY.h index ed35a4c7c..a67eea951 100644 --- a/src/LoggerEnviroDIY.h +++ b/src/LoggerEnviroDIY.h @@ -25,13 +25,14 @@ class LoggerEnviroDIY : public Logger { public: - - // need a constructor to initially not have an modem attached - LoggerEnviroDIY(); + // Constructor + LoggerEnviroDIY(const char *loggerID, uint16_t loggingIntervalMinutes, + int8_t SDCardPin, int8_t mcuWakePin, + VariableArray *inputArray); // Adds a loggerModem objct to the logger // loggerModem = TinyGSM modem + TinyGSM client + Modem On Off - void attachModem(loggerModem *modem); + void attachModem(loggerModem& modem); // Adds the site registration token void setToken(const char *registrationToken); @@ -40,19 +41,23 @@ class LoggerEnviroDIY : public Logger void setSamplingFeatureUUID(const char *samplingFeature); // This adds extra data to the datafile header - String generateFileHeader(void); + String generateFileHeader(void) override; + // This prints a header onto a stream - this removes need to pass around + // very long string objects which can crash the logger + void streamFileHeader(Stream *stream) override; // This generates a properly formatted JSON for EnviroDIY String generateSensorDataJSON(void); + void streamSensorDataJSON(Stream *stream); - // This generates a fully structured POST request for EnviroDIY - String generateEnviroDIYPostRequest(String enviroDIYjson); - String generateEnviroDIYPostRequest(void); + // // This generates a fully structured POST request for EnviroDIY + // String generateEnviroDIYPostRequest(String enviroDIYjson); + // String generateEnviroDIYPostRequest(void); // This prints a fully structured post request for EnviroDIY to the // specified stream using the specified json. // This may be necessary to work around very long strings for the post request. - void streamEnviroDIYRequest(Stream *stream, String enviroDIYjson); + void streamEnviroDIYRequest(Stream *stream, String& enviroDIYjson); // This prints a fully structured post request for EnviroDIY to the // specified stream with the default json. void streamEnviroDIYRequest(Stream *stream); @@ -61,7 +66,7 @@ class LoggerEnviroDIY : public Logger // EnviroDIY/ODM2DataSharingPortal and then streams out a post request // over that connection. // The return is the http status code of the response. - int postDataEnviroDIY(String enviroDIYjson = ""); + int postDataEnviroDIY(String& enviroDIYjson = LOGGER_EMPTY); // ===================================================================== // // Convience functions to call several of the above functions @@ -80,7 +85,7 @@ class LoggerEnviroDIY : public Logger // The internal modem instance bool _modemAttached; - loggerModem *_logModem; + loggerModem _logModem; private: diff --git a/src/LoggerModem.h b/src/LoggerModem.h index dc0619419..25a64ed3e 100644 --- a/src/LoggerModem.h +++ b/src/LoggerModem.h @@ -44,20 +44,22 @@ #define MODEM_NAME "SIMCom SIM868" #elif defined(TINY_GSM_MODEM_SIM900) #define MODEM_NAME "SIMCom SIM900" -#elif defined(TINY_GSM_MODEM_A6) - #define MODEM_NAME "AI-Thinker A6" -#elif defined(TINY_GSM_MODEM_A7) - #define MODEM_NAME "AI-Thinker A7" #elif defined(TINY_GSM_MODEM_UBLOX) #define MODEM_NAME "u-blox Cellular" -#elif defined(TINY_GSM_MODEM_M590) - #define MODEM_NAME "Neoway M590" #elif defined(TINY_GSM_MODEM_M95) #define MODEM_NAME "Quectel M95" #elif defined(TINY_GSM_MODEM_BG96) #define MODEM_NAME "Quectel BG96" +#elif defined(TINY_GSM_MODEM_A6) + #define MODEM_NAME "AI-Thinker A6" +#elif defined(TINY_GSM_MODEM_A7) + #define MODEM_NAME "AI-Thinker A7" +#elif defined(TINY_GSM_MODEM_M590) + #define MODEM_NAME "Neoway M590" #elif defined(TINY_GSM_MODEM_MC60) #define MODEM_NAME "Quectel MC60" +#elif defined(TINY_GSM_MODEM_MC60E) + #define MODEM_NAME "Quectel MC60E" #elif defined(TINY_GSM_MODEM_ESP8266) #define MODEM_NAME "ESP8266" #elif defined(TINY_GSM_MODEM_XBEE) @@ -134,7 +136,7 @@ class loggerModem : public Sensor public: // Constructors loggerModem() - : Sensor(F(MODEM_NAME), MODEM_NUM_VARIABLES, MODEM_WARM_UP_TIME_MS, 0, 0, -1, -1, 1) + : Sensor(MODEM_NAME, MODEM_NUM_VARIABLES, MODEM_WARM_UP_TIME_MS, 0, 0, -1, -1, 1) {_lastNISTrequest = 0;} String getSensorLocation(void) override { return F("modemSerial"); } @@ -156,84 +158,101 @@ class loggerModem : public Sensor MS_MOD_DBG(F("Skipping modem in sensor power down!\n")); } - bool startSingleMeasurement(void) override - { - bool success = true; - - // Check if activated, only go on if it is - if (_millisSensorActivated > 0 && bitRead(_sensorStatus, 3)) - { - // Connect to the network before asking for quality - // Only waiting for up to 5 seconds here for the internet! - if (!(_modem->isNetworkConnected())) - { - MS_MOD_DBG(F("No prior internet connection, attempting to make a connection.")); - success &= connectInternet(5000L); - } - if (success == false) return false; - - // Mark the time that a measurement was requested - _millisMeasurementRequested = millis(); - } - // Make sure that the time of a measurement request is not set - else _millisMeasurementRequested = 0; - - // Even if we failed to start a measurement, we still want to set the status - // bit to show that we attempted to start the measurement. - // Set the status bits for measurement requested (bit 5) - _sensorStatus |= 0b00100000; - // Verify that the status bit for a single measurement completion is not set (bit 6) - _sensorStatus &= 0b10111111; - - return success; - } + // bool startSingleMeasurement(void) override + // { + // bool success = true; + // + // // Check if activated, only go on if it is + // if (_millisSensorActivated > 0 && bitRead(_sensorStatus, 3)) + // { + // // Connect to the network before asking for quality + // // Only waiting for up to 5 seconds here for the internet! + // if (!(_modem->isNetworkConnected())) + // { + // MS_MOD_DBG(F("No prior internet connection, attempting to make a connection.")); + // success &= connectInternet(2000L); + // } + // if (success == false) return false; + // + // // Mark the time that a measurement was requested + // _millisMeasurementRequested = millis(); + // } + // // Make sure that the time of a measurement request is not set + // else _millisMeasurementRequested = 0; + // + // // Even if we failed to start a measurement, we still want to set the status + // // bit to show that we attempted to start the measurement. + // // Set the status bits for measurement requested (bit 5) + // _sensorStatus |= 0b00100000; + // // Verify that the status bit for a single measurement completion is not set (bit 6) + // _sensorStatus &= 0b10111111; + // + // return success; + // } bool addSingleMeasurementResult(void) override { - // The XBee needs to make a connection and have that connection return a - // value before it knows the signal quality - // Connecting to the NIST daytime server, which immediately returns a - // 4 byte response and then closes the connection - if (loggerModemChip == sim_chip_XBeeWifi || loggerModemChip == sim_chip_XBeeCell) - { - // Must ensure that we do not ping the daylight more than once every 4 seconds - // NIST clearly specifies here that this is a requirement for all software - /// that accesses its servers: https://tf.nist.gov/tf-cgi/servers.cgi - while (millis() < _lastNISTrequest + 4000) {} - MS_MOD_DBG("Connecting to NIST daytime server to check connection strength...\n"); - IPAddress ip(129, 6, 15, 30); // This is the IP address of time-c-g.nist.gov - openTCP(ip, 37); - _client->print(F("Hi!")); // Need to send something before connection is made - delay(100); // Need this delay! Can get away with 50, but 100 is safer. - while (_client->available()) _client->read(); // Delete anything returned - _lastNISTrequest = millis(); - } - int signalQual = 0; int percent = 0; int rssi = -9999; - // Get signal quality - if (_modem->isNetworkConnected()) - { - MS_MOD_DBG("Getting signal quality:\n"); - signalQual = _modem->getSignalQuality(); - // Convert signal quality to RSSI, if necessary - if (loggerModemChip == sim_chip_XBeeWifi || - loggerModemChip == sim_chip_XBeeCell || - loggerModemChip == sim_chip_ESP8266) - { - rssi = signalQual; - percent = getPctFromRSSI(signalQual); - } - else + // Check that the modem is responding to AT commands. If not, give up. + MS_MOD_DBG(F("\nWaiting up to 5 seconds for modem to respond to AT commands...\n")); + if (_modem->testAT(5000)) + { + // The XBee needs to make an actual TCP connection and get some sort + // of response on that connection before it knows the signal quality. + // Connecting to the NIST daytime server, which immediately returns a + // 4 byte response and then closes the connection + if (loggerModemChip == sim_chip_XBeeWifi || loggerModemChip == sim_chip_XBeeCell) { - rssi = getRSSIFromCSQ(signalQual); - percent = getPctFromCSQ(signalQual); + // Connect to the network + // Only waiting for up to 5 seconds here for the internet! + if (!(_modem->isNetworkConnected())) + { + MS_MOD_DBG(F("No prior internet connection, attempting to make a connection.")); + connectInternet(5000L); + } + // Must ensure that we do not ping the daylight more than once every 4 seconds + // NIST clearly specifies here that this is a requirement for all software + /// that accesses its servers: https://tf.nist.gov/tf-cgi/servers.cgi + while (millis() < _lastNISTrequest + 4000) {} + MS_MOD_DBG("Connecting to NIST daytime server to check connection strength...\n"); + IPAddress ip(129, 6, 15, 30); // This is the IP address of time-c-g.nist.gov + openTCP(ip, 37); + _client->print(F("Hi!")); // Need to send something before connection is made + delay(100); // Need this delay! Can get away with 50, but 100 is safer. + while (_client->available()) _client->read(); // Delete anything returned + _lastNISTrequest = millis(); } + + // Get signal quality + // Non XBee's do not need to be registered or "connnected" to the + // network to get quality + // if (_modem->isNetworkConnected()) + // { + + MS_MOD_DBG("Getting signal quality:\n"); + signalQual = _modem->getSignalQuality(); + + // Convert signal quality to RSSI, if necessary + if (loggerModemChip == sim_chip_XBeeWifi || + loggerModemChip == sim_chip_XBeeCell || + loggerModemChip == sim_chip_ESP8266) + { + rssi = signalQual; + percent = getPctFromRSSI(signalQual); + } + else + { + rssi = getRSSIFromCSQ(signalQual); + percent = getPctFromCSQ(signalQual); + } + // } + // else MS_MOD_DBG("Insufficient signal to connect to the internet!\n"); } - else MS_MOD_DBG("Insufficient signal to connect to the internet!\n"); + else MS_MOD_DBG(F("\nModem does not respond to AT commands!\n")); MS_MOD_DBG(F("RSSI: "), rssi, F("\n")); MS_MOD_DBG(F("Percent signal strength: "), percent, F("\n")); @@ -319,6 +338,7 @@ class loggerModem : public Sensor } // Check that the modem is responding to AT commands. If not, give up. + MS_MOD_DBG(F("\nWaiting up to 5 seconds for modem to respond to AT commands...\n")); if (!_modem->testAT(5000)) { MS_MOD_DBG(F("\nModem does not respond to AT commands!\n")); @@ -430,21 +450,17 @@ class loggerModem : public Sensor bool modemPowerDown(void) { MS_MOD_DBG(F("Turning modem off.\n")); - bool retVal = true; // Wait for any sending to complete _client->flush(); // Turn the modem off .. whether it was on or not // Need to turn off no matter what because some modems don't have an // effective way of telling us whether they're on or not modemOnOff->off(); - // Double check if the modem is on; turn it off if so - if(modemOnOff->isOn()) retVal = modemOnOff->off(); - else retVal = true; // Unset the status bits for sensor power (bit 0), warm-up (bit 2), // activation (bit 3), stability (bit 4), measurement request (bit 5), and // measurement completion (bit 6) _sensorStatus &= 0b10000010; - return retVal; + return true; } // Get the time from NIST via TIME protocol (rfc868) @@ -454,7 +470,8 @@ class loggerModem : public Sensor { bool connectionMade = false; // bail if not connected to the internet - // TODO: Figure out why _model->isNetworkConnected() isn't working here + // TODO: Figure out why _modem->isNetworkConnected() isn't working here + // if (!_modem->isNetworkConnected()) if (!(connectInternet(1000))) { MS_MOD_DBG(F("No internet connection, cannot connect to NIST.\n")); @@ -545,7 +562,7 @@ class loggerModem : public Sensor F(" and on/off via 2.5 second pulse on pin "), modemSleepRqPin, F(".\n")); static pulsedOnOff modem_sleep_pulsed; modemOnOff = &modem_sleep_pulsed; - modem_sleep_pulsed.init(vcc33Pin, modemSleepRqPin, modemStatusPin); + modem_sleep_pulsed.init(vcc33Pin, modemSleepRqPin, modemStatusPin, true); break; } case modem_sleep_held: @@ -556,7 +573,7 @@ class loggerModem : public Sensor F(" and on/off by holding pin "), modemSleepRqPin, F(" high.\n")); static heldOnOff modem_sleep_held; modemOnOff = &modem_sleep_held; - modem_sleep_held.init(vcc33Pin, modemSleepRqPin, modemStatusPin); + modem_sleep_held.init(vcc33Pin, modemSleepRqPin, modemStatusPin, true); break; } case modem_sleep_reverse: @@ -565,9 +582,9 @@ class loggerModem : public Sensor F(" with power on pin "), vcc33Pin, F(" status on pin "), modemStatusPin, F(" and on/off by holding pin "), modemSleepRqPin, F(" low.\n")); - static reverseOnOff modem_sleep_reverse; - modemOnOff = &modem_sleep_reverse; - modem_sleep_reverse.init(vcc33Pin, modemSleepRqPin, modemStatusPin); + static heldOnOff modem_sleep_held; + modemOnOff = &modem_sleep_held; + modem_sleep_held.init(vcc33Pin, modemSleepRqPin, modemStatusPin, false); break; } default: @@ -687,11 +704,11 @@ class loggerModem : public Sensor class Modem_RSSI : public Variable { public: - Modem_RSSI(Sensor *parentSense, String UUID = "", String customVarCode = "") + Modem_RSSI(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, RSSI_VAR_NUM, - F("RSSI"), F("decibelMiliWatt"), + "RSSI", "decibelMiliWatt", RSSI_RESOLUTION, - F("RSSI"), UUID, customVarCode) + "RSSI", UUID, customVarCode) {} }; @@ -700,11 +717,11 @@ class Modem_RSSI : public Variable class Modem_SignalPercent : public Variable { public: - Modem_SignalPercent(Sensor *parentSense, String UUID = "", String customVarCode = "") + Modem_SignalPercent(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, PERCENT_SIGNAL_VAR_NUM, - F("signalPercent"), F("percent"), + "signalPercent", "percent", PERCENT_SIGNAL_RESOLUTION, - F("signalPercent"), UUID, customVarCode) + "signalPercent", UUID, customVarCode) {} }; diff --git a/src/MaxBotixSonar.cpp b/src/MaxBotixSonar.cpp index 90ce110fa..6f344b495 100644 --- a/src/MaxBotixSonar.cpp +++ b/src/MaxBotixSonar.cpp @@ -15,7 +15,7 @@ MaxBotixSonar::MaxBotixSonar(Stream* stream, int8_t powerPin, int8_t triggerPin, uint8_t measurementsToAverage) - : Sensor(F("MaxBotixMaxSonar"), HRXL_NUM_VARIABLES, + : Sensor("MaxBotixMaxSonar", HRXL_NUM_VARIABLES, HRXL_WARM_UP_TIME_MS, HRXL_STABILIZATION_TIME_MS, HRXL_MEASUREMENT_TIME_MS, powerPin, -1, measurementsToAverage) { @@ -23,7 +23,7 @@ MaxBotixSonar::MaxBotixSonar(Stream* stream, int8_t powerPin, int8_t triggerPin, _stream = stream; } MaxBotixSonar::MaxBotixSonar(Stream& stream, int8_t powerPin, int8_t triggerPin, uint8_t measurementsToAverage) - : Sensor(F("MaxBotixMaxSonar"), HRXL_NUM_VARIABLES, + : Sensor("MaxBotixMaxSonar", HRXL_NUM_VARIABLES, HRXL_WARM_UP_TIME_MS, HRXL_STABILIZATION_TIME_MS, HRXL_MEASUREMENT_TIME_MS, powerPin, -1, measurementsToAverage) { @@ -44,7 +44,7 @@ String MaxBotixSonar::getSensorLocation(void) bool MaxBotixSonar::setup(void) { // Set up the trigger, if applicable - if(_triggerPin > 0) + if(_triggerPin >= 0) { pinMode(_triggerPin, OUTPUT); digitalWrite(_triggerPin, LOW); @@ -76,6 +76,9 @@ bool MaxBotixSonar::wake(void) // RoHS 1.8b090 0713 // TempI + // NOTE ALSO: Depending on what type of serial stream you are using, there + // may also be a bunch of junk in the buffer that this will clear out. + MS_DBG(F("Parsing Header Lines from MaxBotix on "), getSensorLocation(), '\n'); for(int i = 0; i < 6; i++) { @@ -93,6 +96,15 @@ bool MaxBotixSonar::addSingleMeasurementResult(void) int rangeAttempts = 0; int result = -9999; + // Clear anything out of the stream buffer + int junkChars = _stream->available(); + if (junkChars) + { + MS_DBG(F("Dumping "), junkChars, " characters from MaxBotix stream buffer\n"); + for (uint8_t i = 0; i < junkChars; i++) + _stream->read(); + } + if (_millisMeasurementRequested > 0) { MS_DBG(F("Getting readings from MaxBotix on "), getSensorLocation(), '\n'); @@ -104,7 +116,7 @@ bool MaxBotixSonar::addSingleMeasurementResult(void) // for each "single measurement" until a valid value is returned // and the measurement time is <166ms, we'll actually activate // the trigger here. - if(_triggerPin > 0) + if(_triggerPin >= 0) { MS_DBG(F("Triggering Sonar with "), _triggerPin, '\n'); digitalWrite(_triggerPin, HIGH); diff --git a/src/MaxBotixSonar.h b/src/MaxBotixSonar.h index aea38fa60..4e6e19b19 100644 --- a/src/MaxBotixSonar.h +++ b/src/MaxBotixSonar.h @@ -56,11 +56,11 @@ class MaxBotixSonar_Range : public Variable { public: MaxBotixSonar_Range(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, HRXL_VAR_NUM, - F("distance"), F("millimeter"), + "distance", "millimeter", HRXL_RESOLUTION, - F("SonarRange"), UUID, customVarCode) + "SonarRange", UUID, customVarCode) {} }; diff --git a/src/MaximDS18.cpp b/src/MaximDS18.cpp index 409c6ce2e..dd47834db 100644 --- a/src/MaximDS18.cpp +++ b/src/MaximDS18.cpp @@ -22,7 +22,7 @@ // The constructor - if the hex address is known - also need the power pin and the data pin MaximDS18::MaximDS18(DeviceAddress OneWireAddress, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage) - : Sensor(F("MaximDS18"), DS18_NUM_VARIABLES, + : Sensor("MaximDS18", DS18_NUM_VARIABLES, DS18_WARM_UP_TIME_MS, DS18_STABILIZATION_TIME_MS, DS18_MEASUREMENT_TIME_MS, powerPin, dataPin, measurementsToAverage), oneWire(dataPin), tempSensors(&oneWire) @@ -34,7 +34,7 @@ MaximDS18::MaximDS18(DeviceAddress OneWireAddress, int8_t powerPin, int8_t dataP // The constructor - if the hex address is NOT known - only need the power pin and the data pin // Can only use this if there is only a single sensor on the pin MaximDS18::MaximDS18(int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage) - : Sensor(F("MaximDS18"), DS18_NUM_VARIABLES, + : Sensor("MaximDS18", DS18_NUM_VARIABLES, DS18_WARM_UP_TIME_MS, DS18_STABILIZATION_TIME_MS, DS18_MEASUREMENT_TIME_MS, powerPin, dataPin, measurementsToAverage), oneWire(dataPin), tempSensors(&oneWire) diff --git a/src/MaximDS18.h b/src/MaximDS18.h index 1215937ee..d6ec01629 100644 --- a/src/MaximDS18.h +++ b/src/MaximDS18.h @@ -72,11 +72,11 @@ class MaximDS18_Temp : public Variable { public: MaximDS18_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, DS18_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", DS18_TEMP_RESOLUTION, - F("DS18Temp"), UUID, customVarCode) + "DS18Temp", UUID, customVarCode) {} }; diff --git a/src/MaximDS3231.h b/src/MaximDS3231.h index a93af80ec..87551fbfd 100644 --- a/src/MaximDS3231.h +++ b/src/MaximDS3231.h @@ -44,7 +44,7 @@ class MaximDS3231 : public Sensor public: // Only input is the number of readings to average MaximDS3231(uint8_t measurementsToAverage = 1) - : Sensor(F("MaximDS3231"), DS3231_NUM_VARIABLES, + : Sensor("MaximDS3231", DS3231_NUM_VARIABLES, DS3231_WARM_UP_TIME_MS, DS3231_STABILIZATION_TIME_MS, DS3231_MEASUREMENT_TIME_MS, -1, -1, measurementsToAverage) {} @@ -67,11 +67,11 @@ class MaximDS3231_Temp : public Variable { public: MaximDS3231_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, DS3231_TEMP_VAR_NUM, - F("temperatureRTC"), F("degreeCelsius"), + "temperatureDatalogger", "degreeCelsius", DS3231_TEMP_RESOLUTION, - F("BoardTemp"), UUID, customVarCode) + "BoardTemp", UUID, customVarCode) {} }; diff --git a/src/MeaSpecMS5803.cpp b/src/MeaSpecMS5803.cpp index 656a544b5..451ce69e1 100644 --- a/src/MeaSpecMS5803.cpp +++ b/src/MeaSpecMS5803.cpp @@ -39,7 +39,7 @@ // The constructor - because this is I2C, only need the power pin MeaSpecMS5803::MeaSpecMS5803(int8_t powerPin, uint8_t i2cAddressHex, int maxPressure, uint8_t measurementsToAverage) - : Sensor(F("MeaSpecMS5803"), MS5803_NUM_VARIABLES, + : Sensor("MeaSpecMS5803", MS5803_NUM_VARIABLES, MS5803_WARM_UP_TIME_MS, MS5803_STABILIZATION_TIME_MS, MS5803_MEASUREMENT_TIME_MS, powerPin, -1, measurementsToAverage) { @@ -79,6 +79,11 @@ bool MeaSpecMS5803::addSingleMeasurementResult(void) if (isnan(temp)) temp = -9999; if (isnan(press)) press = -9999; + if (temp < -50 || temp > 95) // Range is -40°C to +85°C + { + temp = -9999; + press = -9999; + } MS_DBG(F("Temperature: "), temp); MS_DBG(F("Pressure: "), press); diff --git a/src/MeaSpecMS5803.h b/src/MeaSpecMS5803.h index ff6082495..9c096611d 100644 --- a/src/MeaSpecMS5803.h +++ b/src/MeaSpecMS5803.h @@ -89,11 +89,11 @@ class MeaSpecMS5803_Temp : public Variable { public: MeaSpecMS5803_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, MS5803_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", MS5803_TEMP_RESOLUTION, - F("MeaSpecMS5803Temp"), UUID, customVarCode) + "MeaSpecMS5803Temp", UUID, customVarCode) {} }; @@ -103,11 +103,11 @@ class MeaSpecMS5803_Pressure : public Variable { public: MeaSpecMS5803_Pressure(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, MS5803_PRESSURE_VAR_NUM, - F("barometricPressure"), F("Millibar"), + "barometricPressure", "millibar", MS5803_PRESSURE_RESOLUTION, - F("MeaSpecMS5803Pressure"), UUID, customVarCode) + "MeaSpecMS5803Pressure", UUID, customVarCode) {} }; diff --git a/src/ModemOnOff.cpp b/src/ModemOnOff.cpp index a331cc0d5..9c982e40d 100644 --- a/src/ModemOnOff.cpp +++ b/src/ModemOnOff.cpp @@ -29,47 +29,64 @@ ModemOnOff::ModemOnOff() } // Initializes the instance -void ModemOnOff::init(int vcc33Pin, int modemSleepRqPin, int modemStatusPin) +void ModemOnOff::init(int vcc33Pin, int modemSleepRqPin, int modemStatusPin, + bool isHighWhenOn) { MS_DBG(F("Initializing modem on/off...")); - if (vcc33Pin >= 0) { - _vcc33Pin = vcc33Pin; - // First write the output value, and only then set the output mode. - digitalWrite(_vcc33Pin, LOW); - pinMode(_vcc33Pin, OUTPUT); + + // Set whether using high or low to turn on + _isHighWhenOn = isHighWhenOn; + + // Set pin modes + _vcc33Pin = vcc33Pin; + if (vcc33Pin >= 0) + { + pinMode(_vcc33Pin, OUTPUT); // Set pin mode + digitalWrite(_vcc33Pin, LOW); // Set power off } - if (modemSleepRqPin >= 0) { - _modemSleepRqPin = modemSleepRqPin; - // First write the output value, and only then set the output mode. - digitalWrite(_modemSleepRqPin, LOW); - pinMode(_modemSleepRqPin, OUTPUT); + _modemSleepRqPin = modemSleepRqPin; + if (modemSleepRqPin >= 0) + { + pinMode(_modemSleepRqPin, OUTPUT); // Set pin mode + digitalWrite(_modemSleepRqPin, !isHighWhenOn); // Set to off } - if (modemStatusPin >= 0) { - _modemStatusPin = modemStatusPin; + _modemStatusPin = modemStatusPin; + if (modemStatusPin >= 0) + { pinMode(_modemStatusPin, INPUT_PULLUP); } + + // Initialize assuming modem is off + _isNowOn = false; + MS_DBG(F(" ... Success!\n")); } +// Function to check if the modem is currently on bool ModemOnOff::isOn(void) { - if (_modemStatusPin >= 0) { + if (_modemStatusPin >= 0) + { bool status = digitalRead(_modemStatusPin); + if (!_isHighWhenOn) status = !status; // MS_DBG(F("Is modem on? "), status, F("\n")); return status; } - // No status pin. Let's assume it is on. - return true; + // No status pin. Return the "internal" status code. + return _isNowOn; } +// Function to supply power to the modem - sets power pin high void ModemOnOff::powerOn(void) { - if (_vcc33Pin >= 0) { + if (_vcc33Pin >= 0) + { digitalWrite(_vcc33Pin, HIGH); MS_DBG(F("Sending power to modem.\n")); } } +// Function to cut power from the modem - sets power pin low void ModemOnOff::powerOff(void) { if (_vcc33Pin >= 0) { @@ -88,170 +105,172 @@ void ModemOnOff::powerOff(void) * ========================================================================= */ // Turns the modem on and off by pulsing the onoff/DTR/Key pin on for 2 seconds - bool pulsedOnOff::on(void) - { - powerOn(); - MS_DBG(F("Pulsing modem to on with pin ")); - MS_DBG(_modemSleepRqPin, F("\n")); - if (!isOn()) {pulse();} - // Wait until is actually on - for (uint32_t start = millis(); millis() - start < 5000; ) - { - if (isOn()) - { - MS_DBG(F("Modem now on.\n")); - return true; - } - delay(5); - } - MS_DBG(F("Failed to turn modem on.\n")); - return false; - } - -bool pulsedOnOff::off(void) +bool pulsedOnOff::on(void) { - if (isOn()) - { - MS_DBG(F("Pulsing modem off with pin ")); - MS_DBG(_modemSleepRqPin, F("\n")); - pulse(); - } - else MS_DBG(F("Modem was not ever on.\n")); - // Wait until is off - for (uint32_t start = millis(); millis() - start < 5000; ) + // Power up + powerOn(); + + // If no pin assigned to turn it on or off, assume it's on and return + if (_modemSleepRqPin <= 0) { - if (!isOn()) - { - MS_DBG(F("Modem now off.\n")); - powerOff(); - return true; - } - delay(5); + MS_DBG(F("No modem on/sleep pin assigned, assuming modem is on/awake.")); + _isNowOn = true; + return true; } - MS_DBG(F("Failed to turn modem off.\n")); - powerOff(); - return false; -} -void pulsedOnOff::pulse(void) -{ - if (_modemSleepRqPin >= 0) + // Check if it's already on before sending pulse + if (isOn()) { - digitalWrite(_modemSleepRqPin, LOW); - delay(200); - digitalWrite(_modemSleepRqPin, HIGH); - delay(2500); - digitalWrite(_modemSleepRqPin, LOW); + MS_DBG(F("Modem was already on.\n")); + _isNowOn = true; + return true; } -} - - -/* =========================================================================== -* Functions for the held on-off method. -* This turns the modem on by setting the onoff/DTR/Key pin high and off by -* setting it low. -* This is used by the Sodaq GPRSBee v0.6. -* ========================================================================= */ - -// Turns the modem on by setting the onoff/DTR/Key high and off by setting it low -bool heldOnOff::on(void) -{ - powerOn(); - if (_modemSleepRqPin <= 0) {return true;} else { - MS_DBG(F("Turning modem on by setting pin ")); - MS_DBG(_modemSleepRqPin); - MS_DBG(F(" high\n")); - digitalWrite(_modemSleepRqPin, HIGH); + MS_DBG(F("Turning modem on with a "), _isHighWhenOn, F(" pulse on pin "), + _modemSleepRqPin, F(".\n")); + pulse(); + // Wait until is actually on for (uint32_t start = millis(); millis() - start < 5000; ) { if (isOn()) { MS_DBG(F("Modem now on.\n")); + _isNowOn = true; return true; } delay(5); } - MS_DBG(F("Failed to turn modem on.\n")); + + // If the modem doesn't show it's on within 5 seconds, return false + MS_DBG(F("Failed to turn modem on!\n")); + _isNowOn = false; return false; } } -bool heldOnOff::off(void) +bool pulsedOnOff::off(void) { - if (_modemSleepRqPin <= 0) {return true;} + // If no pin assigned to turn it on or off, assume it's pff and return + if (_modemSleepRqPin <= 0) + { + MS_DBG(F("No modem on/sleep pin assigned, assuming modem is off/asleep.")); + _isNowOn = false; + return true; + } + + // Check if it's already off before sending pulse + if (!isOn()) + { + MS_DBG(F("Modem was not ever on.\n")); + _isNowOn = false; + return true; + } else { - if (!isOn()) MS_DBG(F("Modem was not ever on.\n")); - digitalWrite(_modemSleepRqPin, LOW); + MS_DBG(F("Turning modem off with a "), !_isHighWhenOn, F(" pulse on pin "), + _modemSleepRqPin, F(".\n")); + pulse(); + // Wait until is off - for (uint32_t start = millis(); millis() - start < 5000; ) + for (uint32_t start = millis(); millis() - start < 1000; ) { if (!isOn()) { MS_DBG(F("Modem now off.\n")); + _isNowOn = false; powerOff(); return true; } delay(5); } - MS_DBG(F("Failed to turn modem off.\n")); + + // If the modem doesn't show it's off within 5 seconds, cut the power + // anyway and return true + MS_DBG(F("Failed to turn modem off with on/sleep pin!\n")); + // Power down anyway powerOff(); - return false; + _isNowOn = false; + return true; } } - -/* =========================================================================== -* Functions for the reverse on-off method. -* This turns the modem on by setting the onoff/DTR/Key pin LOW and off by -* setting it HIGH. -* This is used by the XBee's -* ========================================================================= */ - -// Turns the modem on by setting the onoff/DTR/Key LOW and off by setting it HIGH -bool reverseOnOff::isOn(void) +void pulsedOnOff::pulse(void) { - if (_modemStatusPin >= 0) { - bool status = digitalRead(_modemStatusPin); - // MS_DBG(F("Is modem on? "), status, F("\n")); - return !status; + if (_modemSleepRqPin >= 0) + { + digitalWrite(_modemSleepRqPin, !_isHighWhenOn); + delay(200); + digitalWrite(_modemSleepRqPin, _isHighWhenOn); + delay(2500); + digitalWrite(_modemSleepRqPin, !_isHighWhenOn); } - // No status pin. Let's assume it is on. - return true; } -bool reverseOnOff::on(void) + +/* =========================================================================== +* Functions for the held on-off method. +* This turns the modem on by setting and holding the onoff/DTR/Key pin to +* either high or low. +* A "high" on is used by the Sodaq GPRSBee v0.6 and Sodaq 3GBee. +* A "low" on is used by the all Digi XBee's. +* ========================================================================= */ + +// Turns the modem on by setting the onoff/DTR/Key high and off by setting it low +bool heldOnOff::on(void) { + // Power up powerOn(); - MS_DBG(F("Turning modem on on by setting pin ")); - MS_DBG(_modemSleepRqPin); - MS_DBG(F(" low\n")); - if (_modemSleepRqPin >= 0) { - digitalWrite(_modemSleepRqPin, LOW); + + // If no pin assigned to turn it on or off, assume it's on and return + if (_modemSleepRqPin <= 0) + { + MS_DBG(F("No modem on/sleep pin assigned, assuming modem is on/awake.")); + _isNowOn = true; + return true; } + + // Do not check if on or off; just set the pin to whatever it should be held + // at to turn the modem on + MS_DBG(F("Turning modem on by setting pin "), _modemSleepRqPin, F(" to "), + _isHighWhenOn, F(".\n")); + digitalWrite(_modemSleepRqPin, HIGH); + // Wait until is actually on for (uint32_t start = millis(); millis() - start < 5000; ) { if (isOn()) { MS_DBG(F("Modem now on.\n")); + _isNowOn = true; return true; } delay(5); } + + // If the modem doesn't show it's on within 5 seconds, return false MS_DBG(F("Failed to turn modem on.\n")); + _isNowOn = false; return false; } -bool reverseOnOff::off(void) +bool heldOnOff::off(void) { - if (!isOn()) MS_DBG(F("Modem was not ever on.\n")); - if (_modemSleepRqPin >= 0) { - digitalWrite(_modemSleepRqPin, HIGH); + // If no pin assigned to turn it on or off, assume it's off and return + if (_modemSleepRqPin <= 0) + { + MS_DBG(F("No modem on/sleep pin assigned, assuming modem is off/asleep.")); + _isNowOn = false; + return true; } + + // Do not check if on or off; just set the pin to whatever it should be held + // at to turn the modem off + MS_DBG(F("Turning modem off by setting pin "), _modemSleepRqPin, F(" to "), + !_isHighWhenOn, F(".\n")); + digitalWrite(_modemSleepRqPin, LOW); + // Wait until is off for (uint32_t start = millis(); millis() - start < 5000; ) { @@ -263,7 +282,12 @@ bool reverseOnOff::off(void) } delay(5); } + + // If the modem doesn't show it's off within 5 seconds, cut the power + // anyway and return true MS_DBG(F("Failed to turn modem off.\n")); + // Power down powerOff(); + _isNowOn = false; return false; } diff --git a/src/ModemOnOff.h b/src/ModemOnOff.h index 6cc311073..2a00e77fa 100644 --- a/src/ModemOnOff.h +++ b/src/ModemOnOff.h @@ -32,7 +32,8 @@ class ModemOnOff ModemOnOff(); // Initializes the instance - virtual void init(int vcc33Pin, int modemSleepRqPin, int modemStatusPin); + virtual void init(int vcc33Pin, int modemSleepRqPin, int modemStatusPin, + bool isHighWhenOn = true); virtual bool isOn(void); virtual bool on(void) = 0; @@ -42,6 +43,8 @@ class ModemOnOff int8_t _vcc33Pin; int8_t _modemSleepRqPin; int8_t _modemStatusPin; + bool _isNowOn; + bool _isHighWhenOn; void powerOn(void); void powerOff(void); @@ -58,6 +61,7 @@ class ModemOnOff * ========================================================================= */ // Turns the modem on and off by pulsing the onoff/DTR/Key pin on for 2 seconds +// "On" can either be a high or low pulse class pulsedOnOff : public ModemOnOff { public: @@ -72,9 +76,10 @@ class pulsedOnOff : public ModemOnOff /* =========================================================================== * Functions for the held on-off method. -* This turns the modem on by setting the onoff/DTR/Key pin high and off by -* setting it low. -* This is used by the Sodaq GPRSBee v0.6. +* This turns the modem on by setting and holding the onoff/DTR/Key pin to +* either high or low. +* A "high" on is used by the Sodaq GPRSBee v0.6 and Sodaq 3GBee. +* A "low" on is used by the all Digi XBee's. * ========================================================================= */ // Turns the modem on by setting the onoff/DTR/Key high and off by setting it low @@ -85,21 +90,4 @@ class heldOnOff : public ModemOnOff bool off(void) override; }; - -/* =========================================================================== -* Functions for the reverse on-off method. -* This turns the modem on by setting the onoff/DTR/Key pin LOW and off by -* setting it HIGH. -* This is used by the XBee's -* ========================================================================= */ - -// Turns the modem on by setting the onoff/DTR/Key LOW and off by setting it HIGH -class reverseOnOff : public ModemOnOff -{ -public: - bool isOn(void) override; - bool on(void) override; - bool off(void) override; -}; - #endif /* ModemOnOff_h */ diff --git a/src/ProcessorStats.cpp b/src/ProcessorStats.cpp index abc9fdf76..8c62150e9 100644 --- a/src/ProcessorStats.cpp +++ b/src/ProcessorStats.cpp @@ -97,7 +97,7 @@ // Need to know the Mayfly version because the battery resistor depends on it ProcessorStats::ProcessorStats(const char *version) - : Sensor(F(BOARD), PROCESSOR_NUM_VARIABLES, + : Sensor(BOARD, PROCESSOR_NUM_VARIABLES, PROCESSOR_WARM_UP_TIME_MS, PROCESSOR_STABILIZATION_TIME_MS, PROCESSOR_MEASUREMENT_TIME_MS, -1, -1, 1) { diff --git a/src/ProcessorStats.h b/src/ProcessorStats.h index d1cf35c4d..15e70dd12 100644 --- a/src/ProcessorStats.h +++ b/src/ProcessorStats.h @@ -61,11 +61,11 @@ class ProcessorStats_Batt : public Variable { public: ProcessorStats_Batt(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, PROCESSOR_BATTERY_VAR_NUM, - F("batteryVoltage"), F("Volt"), + "batteryVoltage", "volt", PROCESSOR_BATTERY_RESOLUTION, - F("Battery"), UUID, customVarCode) + "Battery", UUID, customVarCode) {} }; @@ -75,11 +75,11 @@ class ProcessorStats_FreeRam : public Variable { public: ProcessorStats_FreeRam(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, PROCESSOR_RAM_VAR_NUM, - F("Free SRAM"), F("Bit"), + "Free SRAM", "Bit", PROCESSOR_RAM_RESOLUTION, - F("FreeRam"), UUID, customVarCode) + "FreeRam", UUID, customVarCode) {} }; diff --git a/src/RainCounterI2C.cpp b/src/RainCounterI2C.cpp index 69f5dec8f..208902aaa 100644 --- a/src/RainCounterI2C.cpp +++ b/src/RainCounterI2C.cpp @@ -22,7 +22,7 @@ // The constructor - because this is I2C, only need the power pin and rain per event if a non-standard value is used RainCounterI2C::RainCounterI2C(uint8_t i2cAddressHex, float rainPerTip) - : Sensor(F("RainCounterI2C"), BUCKET_NUM_VARIABLES, + : Sensor("RainCounterI2C", BUCKET_NUM_VARIABLES, BUCKET_WARM_UP_TIME_MS, BUCKET_STABILIZATION_TIME_MS, BUCKET_MEASUREMENT_TIME_MS, -1, -1, 1) { diff --git a/src/RainCounterI2C.h b/src/RainCounterI2C.h index 5bc7dd4ba..cce7206ee 100644 --- a/src/RainCounterI2C.h +++ b/src/RainCounterI2C.h @@ -64,11 +64,11 @@ class RainCounterI2C_Tips : public Variable { public: RainCounterI2C_Tips(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, BUCKET_TIPS_VAR_NUM, - F("precipitation"), F("event"), + "precipitation", "event", BUCKET_TIPS_RESOLUTION, - F("RainCounterI2CTips"), UUID, customVarCode) + "RainCounterI2CTips", UUID, customVarCode) {} }; @@ -77,11 +77,11 @@ class RainCounterI2C_Depth : public Variable { public: RainCounterI2C_Depth(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, BUCKET_RAIN_VAR_NUM, - F("precipitation"), F("millimeter"), + "precipitation", "millimeter", BUCKET_RAIN_RESOLUTION, - F("RainCounterI2CVol"), UUID, customVarCode) + "RainCounterI2CVol", UUID, customVarCode) {} }; diff --git a/src/SDI12Sensors.cpp b/src/SDI12Sensors.cpp index 9e183304a..1819bff66 100644 --- a/src/SDI12Sensors.cpp +++ b/src/SDI12Sensors.cpp @@ -16,7 +16,7 @@ // The constructor - need the number of measurements the sensor will return, SDI-12 address, the power pin, and the data pin SDI12Sensors::SDI12Sensors(char SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage, - String sensorName, uint8_t numReturnedVars, + const char *sensorName, uint8_t numReturnedVars, uint32_t warmUpTime_ms, uint32_t stabilizationTime_ms, uint32_t measurementTime_ms) : Sensor(sensorName, numReturnedVars, warmUpTime_ms, stabilizationTime_ms, measurementTime_ms, @@ -26,7 +26,7 @@ SDI12Sensors::SDI12Sensors(char SDI12address, int8_t powerPin, int8_t dataPin, u _SDI12address = SDI12address; } SDI12Sensors::SDI12Sensors(char *SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage, - String sensorName, uint8_t numReturnedVars, + const char *sensorName, uint8_t numReturnedVars, uint32_t warmUpTime_ms, uint32_t stabilizationTime_ms, uint32_t measurementTime_ms) : Sensor(sensorName, numReturnedVars, warmUpTime_ms, stabilizationTime_ms, measurementTime_ms, @@ -36,7 +36,7 @@ SDI12Sensors::SDI12Sensors(char *SDI12address, int8_t powerPin, int8_t dataPin, _SDI12address = *SDI12address; } SDI12Sensors::SDI12Sensors(int SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage, - String sensorName, uint8_t numReturnedVars, + const char *sensorName, uint8_t numReturnedVars, uint32_t warmUpTime_ms, uint32_t stabilizationTime_ms, uint32_t measurementTime_ms) : Sensor(sensorName, numReturnedVars, warmUpTime_ms, stabilizationTime_ms, measurementTime_ms, @@ -67,6 +67,13 @@ bool SDI12Sensors::setup(void) retVal &= getSensorInfo(); + // Empty the SDI-12 buffer + _SDI12Internal.clearBuffer(); + + // De-activate the SDI-12 Object + // Use end() instead of just forceHold to un-set the timers + _SDI12Internal.end(); + return retVal; } @@ -130,9 +137,13 @@ bool SDI12Sensors::getSensorInfo(void) { MS_DBG(F(" Activating SDI-12 instance for "), getSensorName(), F(" at "), getSensorLocation(), '\n'); - // Make this the currently active SDI-12 Object + // Check if this the currently active SDI-12 Object + bool wasActive = _SDI12Internal.isActive(); + // If it wasn't active, activate it now. // Use begin() instead of just setActive() to ensure timer is set correctly. - _SDI12Internal.begin();; + if (wasActive) MS_DBG(F(" SDI-12 instance for "), getSensorName(), + F(" at "), getSensorLocation(), " was already active!\n"); + if (!wasActive) _SDI12Internal.begin(); // Empty the buffer _SDI12Internal.clearBuffer(); @@ -162,7 +173,7 @@ bool SDI12Sensors::getSensorInfo(void) // De-activate the SDI-12 Object // Use end() instead of just forceHold to un-set the timers - _SDI12Internal.end(); + if (!wasActive) _SDI12Internal.end(); if (sdiResponse.length() > 1) { @@ -225,9 +236,13 @@ bool SDI12Sensors::startSingleMeasurement(void) MS_DBG(F(" Activating SDI-12 instance for "), getSensorName(), F(" at "), getSensorLocation(), '\n'); - // Make this the currently active SDI-12 Object + // Check if this the currently active SDI-12 Object + bool wasActive = _SDI12Internal.isActive(); + if (wasActive) MS_DBG(F(" SDI-12 instance for "), getSensorName(), + F(" at "), getSensorLocation(), " was already active!\n"); + // If it wasn't active, activate it now. // Use begin() instead of just setActive() to ensure timer is set correctly. - _SDI12Internal.begin();; + if (!wasActive) _SDI12Internal.begin(); // Empty the buffer _SDI12Internal.clearBuffer(); @@ -260,7 +275,7 @@ bool SDI12Sensors::startSingleMeasurement(void) // De-activate the SDI-12 Object // Use end() instead of just forceHold to un-set the timers - _SDI12Internal.end(); + if (!wasActive) _SDI12Internal.end(); // Verify the number of results the sensor will send // int numVariables = sdiResponse.substring(4,5).toInt(); @@ -308,9 +323,13 @@ bool SDI12Sensors::addSingleMeasurementResult(void) { MS_DBG(F(" Activating SDI-12 instance for "), getSensorName(), F(" at "), getSensorLocation(), '\n'); - // Make this the currently active SDI-12 Object + // Check if this the currently active SDI-12 Object + bool wasActive = _SDI12Internal.isActive(); + if (wasActive) MS_DBG(F(" SDI-12 instance for "), getSensorName(), + F(" at "), getSensorLocation(), " was already active!\n"); + // If it wasn't active, activate it now. // Use begin() instead of just setActive() to ensure timer is set correctly. - _SDI12Internal.begin();; + if (!wasActive) _SDI12Internal.begin(); // Empty the buffer _SDI12Internal.clearBuffer(); @@ -347,7 +366,7 @@ bool SDI12Sensors::addSingleMeasurementResult(void) // De-activate the SDI-12 Object // Use end() instead of just forceHold to un-set the timers - _SDI12Internal.forceHold(); + if (!wasActive) _SDI12Internal.end(); // Unset the time stamp for the beginning of this measurement _millisMeasurementRequested = 0; diff --git a/src/SDI12Sensors.h b/src/SDI12Sensors.h index 7334fdbae..37e7fb5f1 100644 --- a/src/SDI12Sensors.h +++ b/src/SDI12Sensors.h @@ -33,13 +33,13 @@ class SDI12Sensors : public Sensor public: SDI12Sensors(char SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1, - String sensorName = "SDI12-Sensor", uint8_t numReturnedVars = 1, + const char *sensorName = "SDI12-Sensor", uint8_t numReturnedVars = 1, uint32_t warmUpTime_ms = 0, uint32_t stabilizationTime_ms = 0, uint32_t measurementTime_ms = 0); SDI12Sensors(char *SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1, - String sensorName = "SDI12-Sensor", uint8_t numReturnedVars = 1, + const char *sensorName = "SDI12-Sensor", uint8_t numReturnedVars = 1, uint32_t warmUpTime_ms = 0, uint32_t stabilizationTime_ms = 0, uint32_t measurementTime_ms = 0); SDI12Sensors(int SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1, - String sensorName = "SDI12-Sensor", uint8_t numReturnedVars = 1, + const char *sensorName = "SDI12-Sensor", uint8_t numReturnedVars = 1, uint32_t warmUpTime_ms = 0, uint32_t stabilizationTime_ms = 0, uint32_t measurementTime_ms = 0); String getSensorVendor(void); diff --git a/src/SensorBase.cpp b/src/SensorBase.cpp index 942f9d223..cddbca16b 100644 --- a/src/SensorBase.cpp +++ b/src/SensorBase.cpp @@ -15,7 +15,7 @@ // ============================================================================ // The constructor -Sensor::Sensor(String sensorName, uint8_t numReturnedVars, +Sensor::Sensor(const char *sensorName, uint8_t numReturnedVars, uint32_t warmUpTime_ms, uint32_t stabilizationTime_ms, uint32_t measurementTime_ms, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage) { @@ -109,7 +109,7 @@ uint8_t Sensor::getStatus(void) // This turns on sensor power void Sensor::powerUp(void) { - if (_powerPin > 0) + if (_powerPin >= 0) { MS_DBG(F("Powering "), getSensorName(), F(" at "), getSensorLocation(), F(" with pin "), _powerPin, F("\n")); @@ -130,7 +130,7 @@ void Sensor::powerUp(void) // This turns off sensor power void Sensor::powerDown(void) { - if (_powerPin > 0) + if (_powerPin >= 0) { MS_DBG(F("Turning off power to "), getSensorName(), F(" at "), getSensorLocation(), F(" with pin "), _powerPin, F("\n")); @@ -165,8 +165,8 @@ bool Sensor::setup(void) MS_DBG(_measurementsToAverage); MS_DBG(F(" individual measurements will be averaged for each reading.\n")); - if (_powerPin > 0) pinMode(_powerPin, OUTPUT); - if (_dataPin > 0) pinMode(_dataPin, INPUT_PULLUP); + if (_powerPin >= 0) pinMode(_powerPin, OUTPUT); + if (_dataPin >= 0) pinMode(_dataPin, INPUT_PULLUP); // Set the status bit marking that the sensor has been set up (bit 1) _sensorStatus |= 0b00000010; @@ -413,7 +413,7 @@ bool Sensor::checkPowerOn(bool debug) { if (debug) MS_DBG(F("Checking power status: Power to "), getSensorName(), F(" at "), getSensorLocation()); - if (_powerPin > 0) + if (_powerPin >= 0) { int powerBitNumber = log(digitalPinToBitMask(_powerPin))/log(2); diff --git a/src/SensorBase.h b/src/SensorBase.h index f17225f97..585cbf94a 100644 --- a/src/SensorBase.h +++ b/src/SensorBase.h @@ -27,7 +27,7 @@ class Sensor { public: - Sensor(String sensorName = "Unknown", uint8_t numReturnedVars = 1, + Sensor(const char *sensorName = "Unknown", uint8_t numReturnedVars = 1, uint32_t warmUpTime_ms = 0, uint32_t stabilizationTime_ms = 0, uint32_t measurementTime_ms = 0, int8_t powerPin = -1, int8_t dataPin = -1, uint8_t measurementsToAverage = 1); @@ -61,9 +61,9 @@ class Sensor virtual bool setup(void); // This updates the sensor's values - // This includes clears the values array, starts and averages as many - // measurement readings as requested, and then notifies the registerd - // variables of the new resutls. + // This clears the values array, starts and averages as many measurement + // readings as requested, and then notifies the registered variables + // of the new results. All possible waits are included in this function! virtual bool update(void); // This turns on the sensor power, if applicable @@ -76,6 +76,7 @@ class Sensor // This wakes the sensor up, if necessary - that is, does whatever it takes to // get a sensor in the proper state to begin a measurement after the power is on. // This *may* require a waitForWarmUp() before wake commands can be sent. + // The wait is NOT included in this function! // This also sets the _millisSensorActivated timestamp. // By default, verifies the power is on and returns true virtual bool wake(void); @@ -88,6 +89,7 @@ class Sensor // This also sets the _millisMeasurementRequested timestamp. // This *may* require a waitForWarmUp() before measurement commands can be sent. // This *may* also require a waitForStability() before returned measurements will be any good. + // The waits are NOT included in this function! virtual bool startSingleMeasurement(void); // This next function must be implemented for ever sensor!! @@ -95,13 +97,14 @@ class Sensor // This also un-sets the _millisMeasurementRequested timestamp. // This *may* also require a waitForStability() before returned measurements will be any good. // This will often require a waitForMeasurementCompletion() to ensure a measurement is done. + // The waits are NOT included in this function! virtual bool addSingleMeasurementResult(void) = 0; // This is the array of result values for each sensor float sensorValues[MAX_NUMBER_VARS]; // Clears the values array void clearValues(); - // This verifies that a measurement is OK before adding it to the array + // This verifies that a measurement is OK (ie, not -9999) before adding it to the array void verifyAndAddMeasurementResult(int resultNumber, float resultValue); void verifyAndAddMeasurementResult(int resultNumber, int resultValue); void averageMeasurements(void); @@ -143,7 +146,7 @@ class Sensor int8_t _dataPin; // SIGNED int, to allow negative numbers for unused pins int8_t _powerPin; // SIGNED int, to allow negative numbers for unused pins - String _sensorName; + const char *_sensorName; uint8_t _numReturnedVars; uint8_t _measurementsToAverage; uint8_t numberGoodMeasurementsMade[MAX_NUMBER_VARS]; diff --git a/src/VariableArray.cpp b/src/VariableArray.cpp index cb4a4c018..b0d861870 100644 --- a/src/VariableArray.cpp +++ b/src/VariableArray.cpp @@ -10,18 +10,28 @@ #include "VariableArray.h" -// Initialization - cannot do this in constructor arduino has issues creating -// instances of classes with non-empty constructors -void VariableArray::init(int variableCount, Variable *variableList[]) +// Constructor +VariableArray::VariableArray(int variableCount, Variable *variableList[]) { - MS_DBG(F("Initializing variable array with "), variableCount, F(" variables...\n")); _variableCount = variableCount; - _variableList = variableList; + arrayOfVars = variableList; _maxSamplestoAverage = countMaxToAverage(); _sensorCount = getSensorCount(); +} - MS_DBG(F(" ... Success!\n")); +// This counts and returns the number of calculated variables +int VariableArray::getCalculatedVariableCount(void) +{ + int numCalc = 0; + // Check for unique sensors + for (int i = 0; i < _variableCount; i++) + { + if (arrayOfVars[i]->isCalculated) numCalc++; + } + MS_DBG(F("There are "), numCalc, + F(" calculated variables in the group.\n")); + return numCalc; } @@ -41,6 +51,8 @@ int VariableArray::getSensorCount(void) // Public functions for interfacing with a list of sensors // This sets up all of the sensors in the list +// NOTE: Calculated variables will always be skipped in this process because +// a calculated variable will never be marked as the last variable from a sensor. bool VariableArray::setupSensors(void) { bool success = true; @@ -60,11 +72,11 @@ bool VariableArray::setupSensors(void) { if (isLastVarFromSensor(i)) // Skip non-unique sensors { - if (bitRead(_variableList[i]->parentSensor->getStatus(), 1) == 1) // already set up + if (bitRead(arrayOfVars[i]->parentSensor->getStatus(), 1) == 1) // already set up { - MS_DBG(F(" ... "), _variableList[i]->parentSensor->getSensorName()); + MS_DBG(F(" ... "), arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG(F(" was already set up!\n")); nSensorsSetup++; @@ -83,17 +95,17 @@ bool VariableArray::setupSensors(void) bool sensorSuccess = false; if (isLastVarFromSensor(i)) // Skip non-unique sensors { - if (bitRead(_variableList[i]->parentSensor->getStatus(), 1) == 0) // not yet set up + if (bitRead(arrayOfVars[i]->parentSensor->getStatus(), 1) == 0) // not yet set up { - if (_variableList[i]->parentSensor->isWarmedUp()) // is warmed up + if (arrayOfVars[i]->parentSensor->isWarmedUp()) // is warmed up { MS_DBG(F(" ... Set up of ")); - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG(F(" ...\n")); - sensorSuccess = _variableList[i]->parentSensor->setup(); // set it up + sensorSuccess = arrayOfVars[i]->parentSensor->setup(); // set it up success &= sensorSuccess; nSensorsSetup++; @@ -111,7 +123,7 @@ bool VariableArray::setupSensors(void) // Now attach all of the variables to their parents MS_DBG(F("Attaching variables to their parent sensors.\n")); for (int i = 0; i < _variableCount; i++){ - success &= _variableList[i]->setup(); + success &= arrayOfVars[i]->setup(); } if (success) @@ -122,6 +134,8 @@ bool VariableArray::setupSensors(void) // This powers up the sensors // There's no checking or waiting here, just turning on pins +// NOTE: Calculated variables will always be skipped in this process because +// a calculated variable will never be marked as the last variable from a sensor. void VariableArray::sensorsPowerUp(void) { MS_DBG(F("Powering up sensors...\n")); @@ -130,12 +144,12 @@ void VariableArray::sensorsPowerUp(void) if (isLastVarFromSensor(i)) // Skip non-unique sensors { MS_DBG(F(" ... Powering up ")); - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG('\n'); - _variableList[i]->parentSensor->powerUp(); + arrayOfVars[i]->parentSensor->powerUp(); } } } @@ -143,6 +157,8 @@ void VariableArray::sensorsPowerUp(void) // This wakes/activates the sensors // Before a sensor is "awoken" we have to make sure it's had time to warm up +// NOTE: Calculated variables will always be skipped in this process because +// a calculated variable will never be marked as the last variable from a sensor. bool VariableArray::sensorsWake(void) { MS_DBG(F("Waking sensors...\n")); @@ -154,11 +170,11 @@ bool VariableArray::sensorsWake(void) { if (isLastVarFromSensor(i)) // Skip non-unique sensors { - if (bitRead(_variableList[i]->parentSensor->getStatus(), 3) == 1) // already awake + if (bitRead(arrayOfVars[i]->parentSensor->getStatus(), 3) == 1) // already awake { - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG(F(" was already awake.\n")); nSensorsAwake++; } @@ -175,18 +191,18 @@ bool VariableArray::sensorsWake(void) { if (isLastVarFromSensor(i)) // Skip non-unique sensors { - if (bitRead(_variableList[i]->parentSensor->getStatus(), 3) == 0) // NOT yet awake + if (bitRead(arrayOfVars[i]->parentSensor->getStatus(), 3) == 0) // NOT yet awake { - if (_variableList[i]->parentSensor->isWarmedUp()) // already warmed up + if (arrayOfVars[i]->parentSensor->isWarmedUp()) // already warmed up { MS_DBG(F(" ... Wake up of ")); - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG(F(" ...\n")); // Make a single attempt to wake the sensor after it is warmed up - bool sensorSuccess = _variableList[i]->parentSensor->wake(); + bool sensorSuccess = arrayOfVars[i]->parentSensor->wake(); success &= sensorSuccess; // We increment up the number of sensors awake/active, even // if the wake up command failed! @@ -206,6 +222,8 @@ bool VariableArray::sensorsWake(void) // This puts sensors to sleep // We're not waiting for anything to be ready, we're just sending the command // to put it to sleep no matter what its current state is. +// NOTE: Calculated variables will always be skipped in this process because +// a calculated variable will never be marked as the last variable from a sensor. bool VariableArray::sensorsSleep(void) { MS_DBG(F("Putting sensors to sleep...\n")); @@ -215,11 +233,11 @@ bool VariableArray::sensorsSleep(void) if (isLastVarFromSensor(i)) // Skip non-unique sensors { MS_DBG(F(" ... ")); - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); - bool sensorSuccess = _variableList[i]->parentSensor->sleep(); + bool sensorSuccess = arrayOfVars[i]->parentSensor->sleep(); success &= sensorSuccess; if (sensorSuccess) MS_DBG(F(" successfully put to sleep.\n")); @@ -232,6 +250,8 @@ bool VariableArray::sensorsSleep(void) // This cuts power to the sensors // We're not waiting for anything to be ready, we're just cutting power. +// NOTE: Calculated variables will always be skipped in this process because +// a calculated variable will never be marked as the last variable from a sensor. void VariableArray::sensorsPowerDown(void) { MS_DBG(F("Powering down sensors...\n")); @@ -240,12 +260,12 @@ void VariableArray::sensorsPowerDown(void) if (isLastVarFromSensor(i)) // Skip non-unique sensors { MS_DBG(F(" ... powering down ")); - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG('\n'); - _variableList[i]->parentSensor->powerDown(); + arrayOfVars[i]->parentSensor->powerDown(); } } } @@ -255,6 +275,8 @@ void VariableArray::sensorsPowerDown(void) // Please note that this does NOT run the update functions, it instead uses // the startSingleMeasurement and addSingleMeasurementResult functions to // take advantage of the ability of sensors to be measuring concurrently. +// NOTE: Calculated variables will always be skipped in this process because +// a calculated variable will never be marked as the last variable from a sensor. bool VariableArray::updateAllSensors(void) { bool success = true; @@ -265,7 +287,7 @@ bool VariableArray::updateAllSensors(void) { if (isLastVarFromSensor(i)) { - _variableList[i]->parentSensor->clearValues(); + arrayOfVars[i]->parentSensor->clearValues(); } } @@ -280,16 +302,16 @@ bool VariableArray::updateAllSensors(void) { if (isLastVarFromSensor(i)) // Skip non-unique sensors { - if (bitRead(_variableList[i]->parentSensor->getStatus(), 3) == 0) // NOT awake/activated + if (bitRead(arrayOfVars[i]->parentSensor->getStatus(), 3) == 0) // NOT awake/activated { - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG(F(" isn't awake/active! No readings will be taken!\n")); // Set the number of readings already equal to whatever total // number requested to ensure the sensor is skipped in further loops. - nMeasurementsCompleted[i] = _variableList[i]->parentSensor->getNumberMeasurementsToAverage(); + nMeasurementsCompleted[i] = arrayOfVars[i]->parentSensor->getNumberMeasurementsToAverage(); // Bump up the finished count. nSensorsCompleted++; } @@ -304,25 +326,25 @@ bool VariableArray::updateAllSensors(void) // Leave this whole section commented out unless you want excessive // printouts (ie, thousands of lines) of the timing information!! // if (isLastVarFromSensor(i) and - // _variableList[i]->parentSensor->getNumberMeasurementsToAverage() > nMeasurementsCompleted[i]) + // arrayOfVars[i]->parentSensor->getNumberMeasurementsToAverage() > nMeasurementsCompleted[i]) // { - // _variableList[i]->parentSensor->updateStatusBits(true); + // arrayOfVars[i]->parentSensor->updateStatusBits(true); // MS_DBG(i); // MS_DBG(F(" - ")); - // MS_DBG(_variableList[i]->parentSensor->getSensorName()); + // MS_DBG(arrayOfVars[i]->getParentSensorName()); // MS_DBG(F(" at ")); - // MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + // MS_DBG(arrayOfVars[i]->getParentSensorLocation()); // MS_DBG(F(" - millis: ")); // MS_DBG(millis()); // MS_DBG(F(" - status: 0b")); - // MS_DBG(bitRead(_variableList[i]->parentSensor->getStatus(), 7)); - // MS_DBG(bitRead(_variableList[i]->parentSensor->getStatus(), 6)); - // MS_DBG(bitRead(_variableList[i]->parentSensor->getStatus(), 5)); - // MS_DBG(bitRead(_variableList[i]->parentSensor->getStatus(), 4)); - // MS_DBG(bitRead(_variableList[i]->parentSensor->getStatus(), 3)); - // MS_DBG(bitRead(_variableList[i]->parentSensor->getStatus(), 2)); - // MS_DBG(bitRead(_variableList[i]->parentSensor->getStatus(), 1)); - // MS_DBG(bitRead(_variableList[i]->parentSensor->getStatus(), 0)); + // MS_DBG(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 7)); + // MS_DBG(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 6)); + // MS_DBG(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 5)); + // MS_DBG(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 4)); + // MS_DBG(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 3)); + // MS_DBG(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 2)); + // MS_DBG(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 1)); + // MS_DBG(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 0)); // MS_DBG(F(" - measurement #")); // MS_DBG(nMeasurementsCompleted[i] + 1); // MS_DBG('\n'); @@ -331,25 +353,25 @@ bool VariableArray::updateAllSensors(void) // Only do checks on sensors that still have readings to finish if (isLastVarFromSensor(i) and - _variableList[i]->parentSensor->getNumberMeasurementsToAverage() > nMeasurementsCompleted[i]) + arrayOfVars[i]->parentSensor->getNumberMeasurementsToAverage() > nMeasurementsCompleted[i]) { // first, make sure the sensor is stable - if ( _variableList[i]->parentSensor->isStable()) + if ( arrayOfVars[i]->parentSensor->isStable()) { // now, if the sensor is not currently measuring... - if (bitRead(_variableList[i]->parentSensor->getStatus(), 5) == 0) // NOT currently measuring + if (bitRead(arrayOfVars[i]->parentSensor->getStatus(), 5) == 0) // NOT currently measuring { // Start a reading MS_DBG(F("- Starting reading ")); MS_DBG(nMeasurementsCompleted[i]+1); MS_DBG(F(" on ")); - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG(F(" -\n")); - bool sensorSuccess_start = _variableList[i]->parentSensor->startSingleMeasurement(); + bool sensorSuccess_start = arrayOfVars[i]->parentSensor->startSingleMeasurement(); success &= sensorSuccess_start; if (sensorSuccess_start) MS_DBG(F("- Success -\n")); @@ -358,18 +380,18 @@ bool VariableArray::updateAllSensors(void) // otherwise, it is currently measuring so... // if a measurement is finished, get the result and tick up the number of finished readings - if(_variableList[i]->parentSensor->isMeasurementComplete()) + if(arrayOfVars[i]->parentSensor->isMeasurementComplete()) { // Get the value MS_DBG(F("-- Collected result of reading ")); MS_DBG(nMeasurementsCompleted[i]+1); MS_DBG(F(" from ")); - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG(F(" --\n")); - bool sensorSuccess_result = _variableList[i]->parentSensor->addSingleMeasurementResult(); + bool sensorSuccess_result = arrayOfVars[i]->parentSensor->addSingleMeasurementResult(); success &= sensorSuccess_result; nMeasurementsCompleted[i] += 1; // increment the number of measurements that sensor has completed @@ -380,12 +402,12 @@ bool VariableArray::updateAllSensors(void) } // if all the readings are done, mark the whole sensor as done - if (nMeasurementsCompleted[i] == _variableList[i]->parentSensor->getNumberMeasurementsToAverage()) + if (nMeasurementsCompleted[i] == arrayOfVars[i]->parentSensor->getNumberMeasurementsToAverage()) { MS_DBG(F("--- Finished all readings from ")); - MS_DBG(_variableList[i]->parentSensor->getSensorName()); + MS_DBG(arrayOfVars[i]->getParentSensorName()); MS_DBG(F(" at ")); - MS_DBG(_variableList[i]->parentSensor->getSensorLocation()); + MS_DBG(arrayOfVars[i]->getParentSensorLocation()); MS_DBG(F(" ---\n")); nSensorsCompleted++; @@ -401,12 +423,12 @@ bool VariableArray::updateAllSensors(void) if (isLastVarFromSensor(i)) { // MS_DBG(F("--- Averaging results from ")); - // MS_DBG(_variableList[i]->parentSensor->getSensorName()); - _variableList[i]->parentSensor->averageMeasurements(); + // MS_DBG(arrayOfVars[i]->getParentSensorName()); + arrayOfVars[i]->parentSensor->averageMeasurements(); // MS_DBG(F(" ---\n")); // MS_DBG(F("--- Notifying variables from ")); - // MS_DBG(_variableList[i]->parentSensor->getSensorName()); - _variableList[i]->parentSensor->notifyVariables(); + // MS_DBG(arrayOfVars[i]->getParentSensorName()); + arrayOfVars[i]->parentSensor->notifyVariables(); // MS_DBG(F(" ---\n")); } } @@ -415,70 +437,134 @@ bool VariableArray::updateAllSensors(void) // This function prints out the results for any connected sensors to a stream +// Calculated variable results will be included void VariableArray::printSensorData(Stream *stream) { for (int i = 0; i < _variableCount; i++) { - stream->print(_variableList[i]->parentSensor->getSensorName()); - stream->print(F(" at ")); - stream->print(_variableList[i]->parentSensor->getSensorLocation()); - // stream->print(F(" with status 0b")); - // stream->print(bitRead(_variableList[i]->parentSensor->getStatus(), 7)); - // stream->print(bitRead(_variableList[i]->parentSensor->getStatus(), 6)); - // stream->print(bitRead(_variableList[i]->parentSensor->getStatus(), 5)); - // stream->print(bitRead(_variableList[i]->parentSensor->getStatus(), 4)); - // stream->print(bitRead(_variableList[i]->parentSensor->getStatus(), 3)); - // stream->print(bitRead(_variableList[i]->parentSensor->getStatus(), 2)); - // stream->print(bitRead(_variableList[i]->parentSensor->getStatus(), 1)); - // stream->print(bitRead(_variableList[i]->parentSensor->getStatus(), 0)); - stream->print(F(" reports ")); - stream->print(_variableList[i]->getVarName()); - stream->print(F(" is ")); - stream->print(_variableList[i]->getValueString()); - stream->print(F(" ")); - stream->print(_variableList[i]->getVarUnit()); - stream->println(); + if (arrayOfVars[i]->isCalculated) + { + stream->print(arrayOfVars[i]->getVarName()); + stream->print(F(" is calculated to be ")); + stream->print(arrayOfVars[i]->getValueString()); + stream->print(F(" ")); + stream->print(arrayOfVars[i]->getVarUnit()); + stream->println(); + } + else + { + stream->print(arrayOfVars[i]->getParentSensorName()); + stream->print(F(" at ")); + stream->print(arrayOfVars[i]->getParentSensorLocation()); + // stream->print(F(" with status 0b")); + // stream->print(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 7)); + // stream->print(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 6)); + // stream->print(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 5)); + // stream->print(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 4)); + // stream->print(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 3)); + // stream->print(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 2)); + // stream->print(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 1)); + // stream->print(bitRead(arrayOfVars[i]->parentSensor->getStatus(), 0)); + stream->print(F(" reports ")); + stream->print(arrayOfVars[i]->getVarName()); + stream->print(F(" is ")); + stream->print(arrayOfVars[i]->getValueString()); + stream->print(F(" ")); + stream->print(arrayOfVars[i]->getVarUnit()); + stream->println(); + } } } +// These generate some helpful comma-separated lists of variable information +// This is a PRE-PROCESSOR MACRO to speed up generating header rows +// Again, THIS IS NOT A FUNCTION, it is a pre-processor macro +// #define makeVarListCSV(function) \ +// { \ +// String csvString = ""; \ +// for (uint8_t i = 0; i < _variableCount; i++) \ +// { \ +// csvString += arrayOfVars[i]->function; \ +// if (i + 1 != _variableCount) \ +// { \ +// csvString += F(","); \ +// } \ +// } \ +// return csvString; \ +// } // This generates a comma separated list of sensor values WITHOUT TIME STAMP -String VariableArray::generateSensorDataCSV(void) +// String VariableArray::generateSensorDataCSV(void){makeVarListCSV(getValueString())}; +// This generates a comma separated list of parent sensor names +// String VariableArray::listParentSensorNames(void){makeVarListCSV(getParentSensorName())}; +// This generates a comma separated list of variable names +// String VariableArray::listVariableNames(void){makeVarListCSV(getVarName())}; +// This generates a comma separated list of variable units +// String VariableArray::listVariableUnits(void){makeVarListCSV(getVarUnit())}; +// This generates a comma separated list of variable codes +// String VariableArray::listVariableCodes(void){makeVarListCSV(getVarCode())}; +// This generates a comma separated list of variable UUID's +// String VariableArray::listVariableUUIDs(void){makeVarListCSV(getVarUUID())}; + + +// These generate some helpful comma-separated lists of variable information +// This is a PRE-PROCESSOR MACRO to speed up generating header rows +// Again, THIS IS NOT A FUNCTION, it is a pre-processor macro +// #define streamVarListCSV(function) \ +// { \ +// for (uint8_t i = 0; i < _variableCount; i++) \ +// { \ +// stream->print(arrayOfVars[i]->function); \ +// if (i + 1 != _variableCount) \ +// { \ +// stream->print(','); \ +// } \ +// } \ +// } +// void VariableArray::streamSensorDataCSV(Stream *stream){streamVarListCSV(getValueString())}; +// void VariableArray::streamParentSensorNames(Stream *stream){streamVarListCSV(getParentSensorName())}; +// void VariableArray::streamVariableNames(Stream *stream){streamVarListCSV(getVarName())}; +// void VariableArray::streamVariableUnits(Stream *stream){streamVarListCSV(getVarUnit())}; +// void VariableArray::streamVariableCodes(Stream *stream){streamVarListCSV(getVarCode())}; +// void VariableArray::streamVariableUUIDs(Stream *stream){streamVarListCSV(getVarUUID())}; + +// Check for unique sensors +bool VariableArray::isLastVarFromSensor(int arrayIndex) { - String csvString = F(""); + // MS_DBG(F("Checking if "), arrayOfVars[arrayIndex]->getVarName(), F(" ("), + // arrayIndex, F(") is the last variable from a sensor...")); - for (uint8_t i = 0; i < _variableCount; i++) + // Calculated variables are never the last variable from a sensor, simply + // because the don't come from a sensor at all. + if (arrayOfVars[arrayIndex]->isCalculated) { - csvString += _variableList[i]->getValueString(); - if (i + 1 != _variableCount) - { - csvString += F(","); - } + // MS_DBG(F(" ... Nope, it's calculated!\n")); + return false; } - return csvString; -} - - -bool VariableArray::isLastVarFromSensor(int arrayIndex) -{ - // Check for unique sensors - String sensName = _variableList[arrayIndex]->parentSensor->getSensorName(); - String sensLoc = _variableList[arrayIndex]->parentSensor->getSensorLocation(); - bool unique = true; - for (int j = arrayIndex + 1; j < _variableCount; j++) + else { - if (sensName == _variableList[j]->parentSensor->getSensorName() && - sensLoc == _variableList[j]->parentSensor->getSensorLocation()) + String sensName = arrayOfVars[arrayIndex]->getParentSensorName(); + String sensLoc = arrayOfVars[arrayIndex]->getParentSensorLocation(); + bool unique = true; + for (int j = arrayIndex + 1; j < _variableCount; j++) { - unique = false; - break; + if (sensName == arrayOfVars[j]->getParentSensorName() && + sensLoc == arrayOfVars[j]->getParentSensorLocation()) + { + unique = false; + // MS_DBG(F(" ... Nope, there are others after it!\n")); + break; + } } + // if (unique) MS_DBG(F(" ... Yes, it is!\n")); + return unique; } - return unique; } +// Count the maximum number of readings needed from a single sensor for the +// requested averaging uint8_t VariableArray::countMaxToAverage(void) { int numReps = 0; @@ -486,7 +572,7 @@ uint8_t VariableArray::countMaxToAverage(void) { if (isLastVarFromSensor(i)) // Skip non-unique sensors { - numReps = max(numReps, _variableList[i]->parentSensor->getNumberMeasurementsToAverage()); + numReps = max(numReps, arrayOfVars[i]->parentSensor->getNumberMeasurementsToAverage()); } } MS_DBG(F("The largest number of measurements to average will be "), numReps, F(".\n")); diff --git a/src/VariableArray.h b/src/VariableArray.h index 78d3fe666..18680edb4 100644 --- a/src/VariableArray.h +++ b/src/VariableArray.h @@ -24,13 +24,19 @@ class VariableArray public: // Initialization - cannot do this in constructor arduino has issues creating // instances of classes with non-empty constructors - virtual void init(int variableCount, Variable *variableList[]); + VariableArray(int variableCount, Variable *variableList[]); + + // Leave the internal variable list public + Variable **arrayOfVars; // Functions to return information about the list - // This just returns the number of variables + // This just returns the number of variables (as input in the constructor) int getVariableCount(void){return _variableCount;} + // This counts and returns the number of calculated variables + int getCalculatedVariableCount(void); + // This counts and returns the number of sensors int getSensorCount(void); @@ -56,13 +62,31 @@ class VariableArray // This function prints out the results for any connected sensors to a stream void printSensorData(Stream *stream = &Serial); + // These generate some helpful comma-separated lists of variable information // This generates a comma separated list of sensor values WITHOUT TIME STAMP - String generateSensorDataCSV(void); + // String generateSensorDataCSV(void); + // This generates a comma separated list of parent sensor names + // String listParentSensorNames(void); + // This generates a comma separated list of variable names + // String listVariableNames(void); + // This generates a comma separated list of variable units + // String listVariableUnits(void); + // This generates a comma separated list of variable codes + // String listVariableCodes(void); + // This generates a comma separated list of variable UUID's + // String listVariableUUIDs(void); + // These are identical to the above, except they directly send the data to + // an arduino stream to avoid passing around long strings. + // void streamSensorDataCSV(Stream *stream); + // void streamParentSensorNames(Stream *stream); + // void streamVariableNames(Stream *stream); + // void streamVariableUnits(Stream *stream); + // void streamVariableCodes(Stream *stream); + // void streamVariableUUIDs(Stream *stream); protected: uint8_t _variableCount; uint8_t _sensorCount; - Variable **_variableList; uint8_t _maxSamplestoAverage; private: diff --git a/src/VariableBase.cpp b/src/VariableBase.cpp index 9eea1a21f..7633361cf 100644 --- a/src/VariableBase.cpp +++ b/src/VariableBase.cpp @@ -14,13 +14,18 @@ // The class and functions for interfacing with a specific variable. // ============================================================================ -// The constructor +const char* Variable::VAR_BASE_UNKNOWN = "Unknown"; + +// The constructor for a measured variable - that is, one whose values are +// updated by a sensor. Variable::Variable(Sensor *parentSense, int varNum, - String varName, String varUnit, + const char *varName, const char *varUnit, unsigned int decimalResolution, - String defaultVarCode, - String UUID, String customVarCode) + const char *defaultVarCode, + const char *UUID, const char *customVarCode) { + isCalculated = false; + _calcFxn = NULL; parentSensor = parentSense; _varNum = varNum; _varName = varName; @@ -35,26 +40,81 @@ Variable::Variable(Sensor *parentSense, int varNum, _currentValue = -9999; } -void Variable::attachSensor(int varNum, Sensor *parentSense) { - MS_DBG(F("Attempting to register "), getVarName()); - MS_DBG(F(" to "), parentSense->getSensorName()); - MS_DBG(F(" attached at "), parentSense->getSensorLocation(), F("... ")); - parentSense->registerVariable(varNum, this); +// The constructor for a calculated variable - that is, one whose value is +// calculated by the calcFxn which returns a float. +// NOTE: ALL arguments are required! +Variable::Variable(float (*calcFxn)(), + const char *varName, const char *varUnit, + unsigned int decimalResolution, + const char *UUID, const char *customVarCode) +{ + isCalculated = true; + _calcFxn = calcFxn; + parentSensor = NULL; + _varNum = 0; + _varName = varName; + _varUnit = varUnit; + _decimalResolution = decimalResolution; + _defaultVarCode = ""; + _customCode = customVarCode; + _UUID = UUID; + + // When we create the variable, we also want to initialize it with a current + // value of -9999 (ie, a bad result). + _currentValue = -9999; } -bool Variable::setup(void) + +// This notifies the parent sensor that it has an observing variable +// This function should never be called for a calculated variable +void Variable::attachSensor(int varNum, Sensor *parentSense) { - attachSensor(_varNum, parentSensor); - return true; + if (!isCalculated) + { + MS_DBG(F("Attempting to register "), getVarName()); + MS_DBG(F(" to "), parentSense->getSensorName()); + MS_DBG(F(" attached at "), parentSense->getSensorLocation(), F("... \n")); + parentSense->registerVariable(varNum, this); + } } + +// This is the function called by the parent sensor's notifyVariables() function +// This function should never be called for a calculated variable void Variable::onSensorUpdate(Sensor *parentSense) { - _currentValue = parentSense->sensorValues[_varNum]; - MS_DBG(F("... received "), sensorValue, F("\n")); + if (!isCalculated) + { + _currentValue = parentSense->sensorValues[_varNum]; + MS_DBG(F("... received "), _currentValue, F("\n")); + } } -String Variable::getVarUUID(void) {return _UUID;} + +// This is a helper - it returns the name of the parent sensor, if applicable +// This is needed for dealing with variables in arrays +String Variable::getParentSensorLocation(void) +{ + if (!isCalculated) return parentSensor->getSensorLocation(); + else return "Calculated"; +} + + +// This is a helper - it returns the name of the parent sensor, if applicable +// This is needed for dealing with variables in arrays +String Variable::getParentSensorName(void) +{ + if (!isCalculated) return parentSensor->getSensorName(); + else return "Calculated"; +} + + +// This sets up the variable (generally attaching it to its parent) +bool Variable::setup(void) +{ + if (!isCalculated) attachSensor(_varNum, parentSensor); + return true; +} // This returns the variable's name using http://vocabulary.odm2.org/variablename/ String Variable::getVarName(void){return _varName;} @@ -65,17 +125,33 @@ String Variable::getVarUnit(void){return _varUnit;} // This returns a customized code for the variable, if one is given, and a default if not String Variable::getVarCode(void) { - if (_customCode != "") return _customCode; + if (strcmp(_customCode,"") != 0) return _customCode; else return _defaultVarCode; } +// This returns the variable UUID, if one has been assigned +String Variable::getVarUUID(void) {return _UUID;} + + // This returns the current value of the variable as a float float Variable::getValue(bool updateValue) { - if (updateValue) parentSensor->update(); - return _currentValue; + if (isCalculated) + { + // NOTE: We cannot "update" the parent sensor's values before doing + // the calculation because we don't know which sensors those are. + // Make sure you update the parent sensors manually for a calculated + // variable!! + return _calcFxn(); + } + else + { + if (updateValue) parentSensor->update(); + return _currentValue; + } } + // This returns the current value of the variable as a string // with the correct number of significant figures String Variable::getValueString(bool updateValue) diff --git a/src/VariableBase.h b/src/VariableBase.h index 0e71c1780..16187d034 100644 --- a/src/VariableBase.h +++ b/src/VariableBase.h @@ -20,15 +20,34 @@ class Sensor; // Forward declaration class Variable { public: + // The constructor for a measured variable - that is, one whose values are + // updated by a sensor. Variable(Sensor *parentSense, int varNum, - String varName = "Unknown", String varUnit = "Unknown", + const char *varName = VAR_BASE_UNKNOWN, const char *varUnit = VAR_BASE_UNKNOWN, unsigned int decimalResolution = 0, - String defaultVarCode = "Unknown", - String UUID = "", String customVarCode = ""); + const char *defaultVarCode = VAR_BASE_UNKNOWN, + const char *UUID = "", const char *customVarCode = ""); + + // The constructor for a calculated variable - that is, one whose value is + // calculated by the calcFxn which returns a float. + // NOTE: ALL arguments are required! + Variable(float (*calcFxn)(), + const char *varName, const char *varUnit, + unsigned int decimalResolution, + const char *UUID, const char *customVarCode); // These functions tie the variable and sensor together + // They should never be called for a calculated variable + // This notifies the parent sensor that it has an observing variable void attachSensor(int varNum, Sensor *parentSense); + // This is the function called by the parent sensor's notifyVariables() function virtual void onSensorUpdate(Sensor *parentSense); + // This is a helper - it returns the name of the parent sensor, if applicable + // This is needed for dealing with variables in arrays + String getParentSensorName(void); + // This is needed for dealing with variables in arrays + // This is a helper - it returns the "location" of the parent sensor, if applicable + String getParentSensorLocation(void); // This sets up the variable (generally attaching it to its parent) virtual bool setup(void); @@ -44,23 +63,27 @@ class Variable // This returns the current value of the variable as a float float getValue(bool updateValue = false); - // This returns the current value of the variable as a string with the correct number of significant figures + // This returns the current value of the variable as a string with the + // correct number of significant figures String getValueString(bool updateValue = false); // This is the parent sensor for the variable Sensor *parentSensor; + bool isCalculated; protected: float _currentValue; private: + float (*_calcFxn)(void); uint8_t _varNum; - String _varName; - String _varUnit; + const char *_varName; + const char *_varUnit; unsigned int _decimalResolution; - String _defaultVarCode; - String _customCode; - String _UUID; + const char *_defaultVarCode; + const char *_customCode; + const char *_UUID; + static const char* VAR_BASE_UNKNOWN; }; #endif diff --git a/src/YosemitechParent.cpp b/src/YosemitechParent.cpp index 503f22440..43848ca0f 100644 --- a/src/YosemitechParent.cpp +++ b/src/YosemitechParent.cpp @@ -18,7 +18,7 @@ // The constructor - need the sensor type, modbus address, power pin, stream for data, and number of readings to average YosemitechParent::YosemitechParent(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin, uint8_t measurementsToAverage, - yosemitechModel model, String sensName, int numVariables, + yosemitechModel model, const char *sensName, int numVariables, uint32_t warmUpTime_ms, uint32_t stabilizationTime_ms, uint32_t measurementTime_ms) : Sensor(sensName, numVariables, warmUpTime_ms, stabilizationTime_ms, measurementTime_ms, @@ -31,7 +31,7 @@ YosemitechParent::YosemitechParent(byte modbusAddress, Stream* stream, } YosemitechParent::YosemitechParent(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin, uint8_t measurementsToAverage, - yosemitechModel model, String sensName, int numVariables, + yosemitechModel model, const char *sensName, int numVariables, uint32_t warmUpTime_ms, uint32_t stabilizationTime_ms, uint32_t measurementTime_ms) : Sensor(sensName, numVariables, warmUpTime_ms, stabilizationTime_ms, measurementTime_ms, @@ -57,7 +57,7 @@ String YosemitechParent::getSensorLocation(void) bool YosemitechParent::setup(void) { bool retVal = Sensor::setup(); // sets time stamp and status bits - if (_RS485EnablePin > 0) pinMode(_RS485EnablePin, OUTPUT); + if (_RS485EnablePin >= 0) pinMode(_RS485EnablePin, OUTPUT); #if defined(DEEP_DEBUGGING_SERIAL_OUTPUT) sensor.setDebugStream(&DEEP_DEBUGGING_SERIAL_OUTPUT); diff --git a/src/YosemitechParent.h b/src/YosemitechParent.h index 036d8f809..b176aa0f5 100644 --- a/src/YosemitechParent.h +++ b/src/YosemitechParent.h @@ -31,11 +31,11 @@ class YosemitechParent : public Sensor public: YosemitechParent(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1, - yosemitechModel model = UNKNOWN, String sensName = "Yosemitech-Sensor", int numVariables = 2, + yosemitechModel model = UNKNOWN, const char *sensName = "Yosemitech-Sensor", int numVariables = 2, uint32_t warmUpTime_ms = 1500, uint32_t stabilizationTime_ms = 20000, uint32_t measurementTime_ms = 2000); YosemitechParent(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1, - yosemitechModel model = UNKNOWN, String sensName = "Yosemitech-Sensor", int numVariables = 2, + yosemitechModel model = UNKNOWN, const char *sensName = "Yosemitech-Sensor", int numVariables = 2, uint32_t warmUpTime_ms = 1500, uint32_t stabilizationTime_ms = 20000, uint32_t measurementTime_ms = 2000); String getSensorLocation(void) override; diff --git a/src/YosemitechY4000.h b/src/YosemitechY4000.h index 6c85ab9ab..b3d31a2ea 100644 --- a/src/YosemitechY4000.h +++ b/src/YosemitechY4000.h @@ -106,13 +106,13 @@ class YosemitechY4000 : public YosemitechParent YosemitechY4000(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y4000, F("YosemitechY4000"), Y4000_NUM_VARIABLES, + Y4000, "YosemitechY4000", Y4000_NUM_VARIABLES, Y4000_WARM_UP_TIME_MS, Y4000_STABILIZATION_TIME_MS, Y4000_MEASUREMENT_TIME_MS) {} YosemitechY4000(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y4000, F("YosemitechY4000"), Y4000_NUM_VARIABLES, + Y4000, "YosemitechY4000", Y4000_NUM_VARIABLES, Y4000_WARM_UP_TIME_MS, Y4000_STABILIZATION_TIME_MS, Y4000_MEASUREMENT_TIME_MS) {} }; @@ -123,11 +123,11 @@ class YosemitechY4000_DOmgL : public Variable { public: YosemitechY4000_DOmgL(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y4000_DOMGL_VAR_NUM, - F("oxygenDissolved"), F("milligramPerLiter"), + "oxygenDissolved", "milligramPerLiter", Y4000_DOMGL_RESOLUTION, - F("Y4000DOmgL"), UUID, customVarCode) + "Y4000DOmgL", UUID, customVarCode) {} }; @@ -135,11 +135,11 @@ class YosemitechY4000_DOmgL : public Variable class YosemitechY4000_Turbidity : public Variable { public: - YosemitechY4000_Turbidity(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY4000_Turbidity(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y4000_TURB_VAR_NUM, - F("turbidity"), F("nephelometricTurbidityUnit"), + "turbidity", "nephelometricTurbidityUnit", Y4000_TURB_RESOLUTION, - F("Y4000Turbidity"), UUID, customVarCode) + "Y4000Turbidity", UUID, customVarCode) {} }; @@ -147,11 +147,11 @@ class YosemitechY4000_Turbidity : public Variable class YosemitechY4000_Cond : public Variable { public: - YosemitechY4000_Cond(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY4000_Cond(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y4000_COND_VAR_NUM, - F("specificConductance"), F("microsiemenPerCentimeter"), + "specificConductance", "microsiemenPerCentimeter", Y4000_COND_RESOLUTION, - F("Y4000Cond"), UUID, customVarCode) + "Y4000Cond", UUID, customVarCode) {} }; @@ -160,11 +160,11 @@ class YosemitechY4000_pH : public Variable { public: YosemitechY4000_pH(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y4000_PH_VAR_NUM, - F("pH"), F("pH"), + "pH", "pH", Y4000_PH_RESOLUTION, - F("Y4000pH"), UUID, customVarCode) + "Y4000pH", UUID, customVarCode) {} }; @@ -173,11 +173,11 @@ class YosemitechY4000_Temp : public Variable { public: YosemitechY4000_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y4000_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y4000_TEMP_RESOLUTION, - F("Y4000temp"), UUID, customVarCode) + "Y4000temp", UUID, customVarCode) {} }; @@ -186,11 +186,11 @@ class YosemitechY4000_ORP : public Variable { public: YosemitechY4000_ORP(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y4000_ORP_VAR_NUM, - F("ORP"), F("millivolt"), + "ORP", "millivolt", Y4000_ORP_RESOLUTION, - F("Y4000Potential"), UUID, customVarCode) + "Y4000Potential", UUID, customVarCode) {} }; @@ -199,11 +199,11 @@ class YosemitechY4000_Chlorophyll : public Variable { public: YosemitechY4000_Chlorophyll(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y4000_CHLORO_VAR_NUM, - F("chlorophyllFluorescence"), F("microgramPerLiter"), + "chlorophyllFluorescence", "microgramPerLiter", Y4000_CHLORO_RESOLUTION, - F("Y4000Chloro"), UUID, customVarCode) + "Y4000Chloro", UUID, customVarCode) {} }; @@ -212,11 +212,11 @@ class YosemitechY4000_BGA : public Variable { public: YosemitechY4000_BGA(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y4000_BGA_VAR_NUM, - F("blueGreenAlgaeCyanobacteriaPhycocyanin"), F("microgramPerLiter"), + "blueGreenAlgaeCyanobacteriaPhycocyanin", "microgramPerLiter", Y4000_BGA_RESOLUTION, - F("Y4000BGA"), UUID, customVarCode) + "Y4000BGA", UUID, customVarCode) {} }; diff --git a/src/YosemitechY504.h b/src/YosemitechY504.h index bd4c08747..ca293b40d 100644 --- a/src/YosemitechY504.h +++ b/src/YosemitechY504.h @@ -55,13 +55,13 @@ class YosemitechY504 : public YosemitechParent YosemitechY504(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y504, F("YosemitechY504"), Y504_NUM_VARIABLES, + Y504, "YosemitechY504", Y504_NUM_VARIABLES, Y504_WARM_UP_TIME_MS, Y504_STABILIZATION_TIME_MS, Y504_MEASUREMENT_TIME_MS) {} YosemitechY504(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y504, F("YosemitechY504"), Y504_NUM_VARIABLES, + Y504, "YosemitechY504", Y504_NUM_VARIABLES, Y504_WARM_UP_TIME_MS, Y504_STABILIZATION_TIME_MS, Y504_MEASUREMENT_TIME_MS) {} }; @@ -72,11 +72,11 @@ class YosemitechY504_DOpct : public Variable { public: YosemitechY504_DOpct(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y504_DOPCT_VAR_NUM, - F("oxygenDissolvedPercentOfSaturation"), F("percent"), + "oxygenDissolvedPercentOfSaturation", "percent", Y504_DOPCT_RESOLUTION, - F("Y504DOpct"), UUID, customVarCode) + "Y504DOpct", UUID, customVarCode) {} }; @@ -86,11 +86,11 @@ class YosemitechY504_Temp : public Variable { public: YosemitechY504_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y504_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y504_TEMP_RESOLUTION, - F("Y504temp"), UUID, customVarCode) + "Y504temp", UUID, customVarCode) {} }; @@ -100,11 +100,11 @@ class YosemitechY504_DOmgL : public Variable { public: YosemitechY504_DOmgL(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y504_DOMGL_VAR_NUM, - F("oxygenDissolved"), F("milligramPerLiter"), + "oxygenDissolved", "milligramPerLiter", Y504_DOMGL_RESOLUTION, - F("Y504DOmgL"), UUID, customVarCode) + "Y504DOmgL", UUID, customVarCode) {} }; diff --git a/src/YosemitechY510.h b/src/YosemitechY510.h index 887c411ee..ac1845baa 100644 --- a/src/YosemitechY510.h +++ b/src/YosemitechY510.h @@ -52,13 +52,13 @@ class YosemitechY510 : public YosemitechParent YosemitechY510(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y510, F("YosemitechY510"), Y510_NUM_VARIABLES, + Y510, "YosemitechY510", Y510_NUM_VARIABLES, Y510_WARM_UP_TIME_MS, Y510_STABILIZATION_TIME_MS, Y510_MEASUREMENT_TIME_MS) {} YosemitechY510(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y510, F("YosemitechY510"), Y510_NUM_VARIABLES, + Y510, "YosemitechY510", Y510_NUM_VARIABLES, Y510_WARM_UP_TIME_MS, Y510_STABILIZATION_TIME_MS, Y510_MEASUREMENT_TIME_MS) {} }; @@ -68,11 +68,11 @@ class YosemitechY510 : public YosemitechParent class YosemitechY510_Turbidity : public Variable { public: - YosemitechY510_Turbidity(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY510_Turbidity(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y510_TURB_VAR_NUM, - F("turbidity"), F("nephelometricTurbidityUnit"), + "turbidity", "nephelometricTurbidityUnit", Y510_TURB_RESOLUTION, - F("Y510Turbidity"), UUID, customVarCode) + "Y510Turbidity", UUID, customVarCode) {} }; @@ -81,11 +81,11 @@ class YosemitechY510_Turbidity : public Variable class YosemitechY510_Temp : public Variable { public: - YosemitechY510_Temp(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY510_Temp(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y510_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y510_TEMP_RESOLUTION, - F("Y510temp"), UUID, customVarCode) + "Y510temp", UUID, customVarCode) {} }; diff --git a/src/YosemitechY511.h b/src/YosemitechY511.h index b0e2f6bb3..afa5f9bbf 100644 --- a/src/YosemitechY511.h +++ b/src/YosemitechY511.h @@ -52,13 +52,13 @@ class YosemitechY511 : public YosemitechParent YosemitechY511(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y511, F("YosemitechY511"), Y511_NUM_VARIABLES, + Y511, "YosemitechY511", Y511_NUM_VARIABLES, Y511_WARM_UP_TIME_MS, Y511_STABILIZATION_TIME_MS, Y511_MEASUREMENT_TIME_MS) {} YosemitechY511(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream,powerPin, enablePin, measurementsToAverage, - Y511, F("YosemitechY511"), Y511_NUM_VARIABLES, + Y511, "YosemitechY511", Y511_NUM_VARIABLES, Y511_WARM_UP_TIME_MS, Y511_STABILIZATION_TIME_MS, Y511_MEASUREMENT_TIME_MS) {} }; @@ -68,11 +68,11 @@ class YosemitechY511 : public YosemitechParent class YosemitechY511_Turbidity : public Variable { public: - YosemitechY511_Turbidity(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY511_Turbidity(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y511_TURB_VAR_NUM, - F("turbidity"), F("nephelometricTurbidityUnit"), + "turbidity", "nephelometricTurbidityUnit", Y511_TURB_RESOLUTION, - F("Y511Turbidity"), UUID, customVarCode) + "Y511Turbidity", UUID, customVarCode) {} }; @@ -81,11 +81,11 @@ class YosemitechY511_Turbidity : public Variable class YosemitechY511_Temp : public Variable { public: - YosemitechY511_Temp(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY511_Temp(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y511_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y511_TEMP_RESOLUTION, - F("Y511temp"), UUID, customVarCode) + "Y511temp", UUID, customVarCode) {} }; diff --git a/src/YosemitechY514.h b/src/YosemitechY514.h index 40f3944e9..94020ea1e 100644 --- a/src/YosemitechY514.h +++ b/src/YosemitechY514.h @@ -53,13 +53,13 @@ class YosemitechY514 : public YosemitechParent YosemitechY514(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y514, F("YosemitechY514"), Y514_NUM_VARIABLES, + Y514, "YosemitechY514", Y514_NUM_VARIABLES, Y514_WARM_UP_TIME_MS, Y514_STABILIZATION_TIME_MS, Y514_MEASUREMENT_TIME_MS) {} YosemitechY514(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y514, ("YosemitechY514"), Y514_NUM_VARIABLES, + Y514, "YosemitechY514", Y514_NUM_VARIABLES, Y514_WARM_UP_TIME_MS, Y514_STABILIZATION_TIME_MS, Y514_MEASUREMENT_TIME_MS) {} }; @@ -70,11 +70,11 @@ class YosemitechY514_Chlorophyll : public Variable { public: YosemitechY514_Chlorophyll(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y514_CHLORO_VAR_NUM, - F("chlorophyllFluorescence"), F("microgramPerLiter"), + "chlorophyllFluorescence", "microgramPerLiter", Y514_CHLORO_RESOLUTION, - F("Y514Chloro"), UUID, customVarCode) + "Y514Chloro", UUID, customVarCode) {} }; @@ -84,11 +84,11 @@ class YosemitechY514_Temp : public Variable { public: YosemitechY514_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y514_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y514_TEMP_RESOLUTION, - F("Y514temp"), UUID, customVarCode) + "Y514temp", UUID, customVarCode) {} }; #endif diff --git a/src/YosemitechY520.h b/src/YosemitechY520.h index 027deb7e5..f1d89e8c9 100644 --- a/src/YosemitechY520.h +++ b/src/YosemitechY520.h @@ -53,13 +53,13 @@ class YosemitechY520 : public YosemitechParent YosemitechY520(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y520, F("YosemitechY520"), Y520_NUM_VARIABLES, + Y520, "YosemitechY520", Y520_NUM_VARIABLES, Y520_WARM_UP_TIME_MS, Y520_STABILIZATION_TIME_MS, Y520_MEASUREMENT_TIME_MS) {} YosemitechY520(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y520, F("YosemitechY520"), Y520_NUM_VARIABLES, + Y520, "YosemitechY520", Y520_NUM_VARIABLES, Y520_WARM_UP_TIME_MS, Y520_STABILIZATION_TIME_MS, Y520_MEASUREMENT_TIME_MS) {} }; @@ -69,11 +69,11 @@ class YosemitechY520 : public YosemitechParent class YosemitechY520_Cond : public Variable { public: - YosemitechY520_Cond(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY520_Cond(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y520_COND_VAR_NUM, - F("specificConductance"), F("microsiemenPerCentimeter"), + "specificConductance", "microsiemenPerCentimeter", Y520_COND_RESOLUTION, - F("Y520Cond"), UUID, customVarCode) + "Y520Cond", UUID, customVarCode) {} }; @@ -82,11 +82,11 @@ class YosemitechY520_Cond : public Variable class YosemitechY520_Temp : public Variable { public: - YosemitechY520_Temp(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY520_Temp(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y520_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y520_TEMP_RESOLUTION, - F("Y520temp"), UUID, customVarCode) + "Y520temp", UUID, customVarCode) {} }; diff --git a/src/YosemitechY532.h b/src/YosemitechY532.h index f59423984..7675eb0aa 100644 --- a/src/YosemitechY532.h +++ b/src/YosemitechY532.h @@ -56,13 +56,13 @@ class YosemitechY532 : public YosemitechParent YosemitechY532(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y532, F("YosemitechY532"), Y532_NUM_VARIABLES, + Y532, "YosemitechY532", Y532_NUM_VARIABLES, Y532_WARM_UP_TIME_MS, Y532_STABILIZATION_TIME_MS, Y532_MEASUREMENT_TIME_MS) {} YosemitechY532(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y532, F("YosemitechY532"), Y532_NUM_VARIABLES, + Y532, "YosemitechY532", Y532_NUM_VARIABLES, Y532_WARM_UP_TIME_MS, Y532_STABILIZATION_TIME_MS, Y532_MEASUREMENT_TIME_MS) {} }; @@ -73,11 +73,11 @@ class YosemitechY532_pH : public Variable { public: YosemitechY532_pH(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y532_PH_VAR_NUM, - F("pH"), F("pH"), + "pH", "pH", Y532_PH_RESOLUTION, - F("Y532pH"), UUID, customVarCode) + "Y532pH", UUID, customVarCode) {} }; @@ -87,11 +87,11 @@ class YosemitechY532_Temp : public Variable { public: YosemitechY532_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y532_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y532_TEMP_RESOLUTION, - F("Y532temp"), UUID, customVarCode) + "Y532temp", UUID, customVarCode) {} }; @@ -101,11 +101,11 @@ class YosemitechY532_Voltage : public Variable { public: YosemitechY532_Voltage(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y532_VOLT_VAR_NUM, - F("voltage"), F("millivolt"), + "voltage", "millivolt", Y532_VOLT_RESOLUTION, - F("Y532Potential"), UUID, customVarCode) + "Y532Potential", UUID, customVarCode) {} }; diff --git a/src/YosemitechY533.h b/src/YosemitechY533.h index 9cfe07935..8e49e72dd 100644 --- a/src/YosemitechY533.h +++ b/src/YosemitechY533.h @@ -61,13 +61,13 @@ class YosemitechY533 : public YosemitechParent YosemitechY533(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y533, F("YosemitechY533"), Y533_NUM_VARIABLES, + Y533, "YosemitechY533", Y533_NUM_VARIABLES, Y533_WARM_UP_TIME_MS, Y533_STABILIZATION_TIME_MS, Y533_MEASUREMENT_TIME_MS) {} YosemitechY533(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y533, F("YosemitechY533"), Y533_NUM_VARIABLES, + Y533, "YosemitechY533", Y533_NUM_VARIABLES, Y533_WARM_UP_TIME_MS, Y533_STABILIZATION_TIME_MS, Y533_MEASUREMENT_TIME_MS) {} }; @@ -78,11 +78,11 @@ class YosemitechY533_pH : public Variable { public: YosemitechY533_pH(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y533_PH_VAR_NUM, - F("pH"), F("pH"), + "pH", "pH", Y533_PH_RESOLUTION, - F("Y533pH"), UUID, customVarCode) + "Y533pH", UUID, customVarCode) {} }; @@ -92,11 +92,11 @@ class YosemitechY533_Temp : public Variable { public: YosemitechY533_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y533_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y533_TEMP_RESOLUTION, - F("Y533temp"), UUID, customVarCode) + "Y533temp", UUID, customVarCode) {} }; @@ -106,11 +106,11 @@ class YosemitechY533_Voltage : public Variable { public: YosemitechY533_Voltage(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y533_VOLT_VAR_NUM, - F("voltage"), F("millivolt"), + "voltage", "millivolt", Y533_VOLT_RESOLUTION, - F("Y533Potential"), UUID, customVarCode) + "Y533Potential", UUID, customVarCode) {} }; diff --git a/src/YosemitechY550.h b/src/YosemitechY550.h index 0427478d1..9b246789f 100644 --- a/src/YosemitechY550.h +++ b/src/YosemitechY550.h @@ -59,13 +59,13 @@ class YosemitechY550 : public YosemitechParent YosemitechY550(byte modbusAddress, Stream* stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream, powerPin, enablePin, measurementsToAverage, - Y550, F("YosemitechY550"), Y550_NUM_VARIABLES, + Y550, "YosemitechY550", Y550_NUM_VARIABLES, Y550_WARM_UP_TIME_MS, Y550_STABILIZATION_TIME_MS, Y550_MEASUREMENT_TIME_MS) {} YosemitechY550(byte modbusAddress, Stream& stream, int8_t powerPin, int8_t enablePin = -1, uint8_t measurementsToAverage = 1) : YosemitechParent(modbusAddress, stream,powerPin, enablePin, measurementsToAverage, - Y550, F("YosemitechY550"), Y550_NUM_VARIABLES, + Y550, "YosemitechY550", Y550_NUM_VARIABLES, Y550_WARM_UP_TIME_MS, Y550_STABILIZATION_TIME_MS, Y550_MEASUREMENT_TIME_MS) {} }; @@ -75,11 +75,11 @@ class YosemitechY550 : public YosemitechParent class YosemitechY550_COD : public Variable { public: - YosemitechY550_COD(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY550_COD(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y550_COD_VAR_NUM, - F("COD"), F("milligramPerLiter"), + "COD", "milligramPerLiter", Y550_COD_RESOLUTION, - F("Y550COD"), UUID, customVarCode) + "Y550COD", UUID, customVarCode) {} }; @@ -88,11 +88,11 @@ class YosemitechY550_COD : public Variable class YosemitechY550_Temp : public Variable { public: - YosemitechY550_Temp(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY550_Temp(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y550_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", Y550_TEMP_RESOLUTION, - F("Y550temp"), UUID, customVarCode) + "Y550temp", UUID, customVarCode) {} }; @@ -101,11 +101,11 @@ class YosemitechY550_Temp : public Variable class YosemitechY550_Turbidity : public Variable { public: - YosemitechY550_Turbidity(Sensor *parentSense, String UUID = "", String customVarCode = "") + YosemitechY550_Turbidity(Sensor *parentSense, const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, Y550_TURB_VAR_NUM, - F("turbidity"), F("nephelometricTurbidityUnit"), + "turbidity", "nephelometricTurbidityUnit", Y550_TURB_RESOLUTION, - F("Y550Turbidity"), UUID, customVarCode) + "Y550Turbidity", UUID, customVarCode) {} }; diff --git a/src/ZebraTechDOpto.h b/src/ZebraTechDOpto.h index 8a39b5d5f..88c525812 100644 --- a/src/ZebraTechDOpto.h +++ b/src/ZebraTechDOpto.h @@ -60,17 +60,17 @@ class ZebraTechDOpto : public SDI12Sensors // Constructors with overloads ZebraTechDOpto(char SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("ZebraTech D-Opto"), DOPTO_NUM_VARIABLES, + "ZebraTech D-Opto", DOPTO_NUM_VARIABLES, DOPTO_WARM_UP_TIME_MS, DOPTO_STABILIZATION_TIME_MS, DOPTO_MEASUREMENT_TIME_MS) {} ZebraTechDOpto(char *SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("ZebraTech D-Opto"), DOPTO_NUM_VARIABLES, + "ZebraTech D-Opto", DOPTO_NUM_VARIABLES, DOPTO_WARM_UP_TIME_MS, DOPTO_STABILIZATION_TIME_MS, DOPTO_MEASUREMENT_TIME_MS) {} ZebraTechDOpto(int SDI12address, int8_t powerPin, int8_t dataPin, uint8_t measurementsToAverage = 1) : SDI12Sensors(SDI12address, powerPin, dataPin, measurementsToAverage, - F("ZebraTech D-Opto"), DOPTO_NUM_VARIABLES, + "ZebraTech D-Opto", DOPTO_NUM_VARIABLES, DOPTO_WARM_UP_TIME_MS, DOPTO_STABILIZATION_TIME_MS, DOPTO_MEASUREMENT_TIME_MS) {} @@ -82,11 +82,11 @@ class ZebraTechDOpto_Temp : public Variable { public: ZebraTechDOpto_Temp(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, DOPTO_TEMP_VAR_NUM, - F("temperature"), F("degreeCelsius"), + "temperature", "degreeCelsius", DOPTO_TEMP_RESOLUTION, - F("DOtempC"), UUID, customVarCode) + "DOtempC", UUID, customVarCode) {} }; @@ -96,11 +96,11 @@ class ZebraTechDOpto_DOpct : public Variable { public: ZebraTechDOpto_DOpct(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, DOPTO_DOPCT_VAR_NUM, - F("oxygenDissolvedPercentOfSaturation"), F("percent"), + "oxygenDissolvedPercentOfSaturation", "percent", DOPTO_DOPCT_RESOLUTION, - F("DOpercent"), UUID, customVarCode) + "DOpercent", UUID, customVarCode) {} }; @@ -110,11 +110,11 @@ class ZebraTechDOpto_DOmgL : public Variable { public: ZebraTechDOpto_DOmgL(Sensor *parentSense, - String UUID = "", String customVarCode = "") + const char *UUID = "", const char *customVarCode = "") : Variable(parentSense, DOPTO_DOMGL_VAR_NUM, - F("oxygenDissolved"), F("milligramPerLiter"), + "oxygenDissolved", "milligramPerLiter", DOPTO_DOMGL_RESOLUTION, - F("DOppm"), UUID, customVarCode) + "DOppm", UUID, customVarCode) {} };