Software Explanation and Overview
- 1 Boot and WAKE timing
- 2 General Software Organization
- 2.1 ESP32 and Deep Sleep
- 2.1.1 Data Structures
- 2.1.2 Notes about deep sleep
- 2.2 Low Battery Mode
- 2.3 Rainfall Measurement
- 2.4 Wind Speed Measurement
- 2.4.1 Rough math
- 2.4.2 Theory of Operation
- 2.5 Wind Direction Measurement
- 2.5.1 Wind Vane Calibration
- 2.6 EEPROM Module (v 1.2 or greater supports this hardware)
- 2.1 ESP32 and Deep Sleep
- 3 MQTT Topics
- 4 Sensor Datasheets
Boot and WAKE timing
Using my desired setting for waking up every 15 min as an example.
Initial BOOT has no frame of reference on timing yet, so system will WAKE in 3 seconds
1st WAKE will the calibrate itself to happen on an orderly time boundary. In my example( 15 after, 30 after, 15 til hour, top of the hour). Note: WAKE time is pulled forward 60 seconds to ensure data is refreshed slightly ahead of my e-ink display (MQTT subscriber)
Subsequent WAKEs will be at time interval on the boundaries described above.
General Software Organization
This project can grow quickly. I determined early that placing the project in one .cpp file might turn out to be a bad idea for me and I wanted to break up the project to avoid a single, massive file that I’d regret maintaining. I like to steer clear of technical debt.
ESP32 and Deep Sleep
Deep sleep on the ESP32 basically results in a reboot of the microcontroller. We do have basic visibility as to the reason we woke up (timer event or pin interrupt, for instance). We have access to a special SRAM location in the RTC module that we can use to store critical values we need to persist. Normal variables will be lost.
When creating a project or application that uses deep sleep, you must determine what data needs to persist when the device reboots. Rainfall amount is a must. All other sensor values are read, but not stored, just transmitted to a data storage location for further evaluation. One might think that temperature high/low data should be kept, but I chose to move that data analysis off the sensor station responsibilities.
Data Structures
The sensorData
structure is a wrapper to keep all the sensor data values. This structure is not persistent in RTC SRAM.
The historicalData
structure is used to hold hourly and 24h rainfall data. Any variables with the RTC_DATA_ATTR
store the variable in RTC SRAM and can be accessed on after a WAKE event. RTC memory is cleared on RESET.
bootCount
was used extensively in my debug efforts on the unintentional RESET issues.
//===========================================
// Custom structures
//===========================================
struct sensorData
{
float temperatureC;
float temperatureF;
float windSpeed;
float windDirection;
float barometricPressure;
float BMEtemperature;
float humidity;
float UVIndex;
float lux;
float batteryVoltage;
int batteryADC;
};
//rainfall is stored here for historical data uses RTC
struct historicalData
{
unsigned int hourlyRainfall[24];
unsigned int current60MinRainfall[12];
};
//===========================================
// RTC Memory storage
//===========================================
RTC_DATA_ATTR volatile int rainTicks = 0;
RTC_DATA_ATTR int lastHour = 0;
RTC_DATA_ATTR time_t nextUpdate;
RTC_DATA_ATTR struct historicalData rainfall;
RTC_DATA_ATTR int bootCount = 0;
Notes about deep sleep
As I researched regarding people not finding the deep_sleep header file, I found it may not be needed.
Comment out the header file #include statement (or remove it)
//===========================================
// Includes
//===========================================
//#include "esp_deep_sleep.h"
2. change the sleep timer call from
esp_deep_sleep_enable_timer_wakeup
to
esp_sleep_enable_timer_wakeup
void sleepyTime(long UpdateIntervalModified)
{
Serial.println("\n\n\nGoing to sleep now...");
Serial.printf("Waking in %i seconds\n\n\n\n\n\n\n\n\n\n", UpdateIntervalModified);
//updateWake();
esp_sleep_enable_timer_wakeup(UpdateIntervalModified * SEC);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_25, 0);
esp_deep_sleep_start();
}
This change will be part of my next master version (v1.3)
Low Battery Mode
When measured battery is > 15% remaining a flag is set to multiply the wake time interval by 4. In my case, 15 min will turn into 60 min for data reporting.
Rainfall Measurement
Hourly subtotals are stored hour by hour and added to report last 24 hour totals. The system also reports the current hour rainfall.
One tip in the rainfall gauge is 0.011" (0.2794 mm) of rain.
Look for a software update: rainfall is reporting current hour, not the last 60 minutes. Also, technically, the last 23 hours are summed, not last 24 hours.
Wind Speed Measurement
This routine is still in testing, but currently uses multiple switch closures to measure wind speed on the anemometer. Not fully certain that this is bug free.
Rough math
Vendor of anemometer states “A wind speed of 2.4km/h causes the switch to close once per second.“, so this is our calibration target. They never state the measurement in terms of revolutions per second.
If they expect x1 per revolution then…
cup path circumference = .666m = 2(3.14)(.106m), so radius of arm is .106m, or 4.17 inches
If they expect x2 per revolution then…
cup path circumference = 1.333m = 2(3.14)(.211m), so radius of arm is .211m, or 8.31 inches
The math supports an arm radius of .106m. The datasheet is giving us the impression that x1 closure of relay per second is the basis for their calibration information. This is why I make the assertion that my calculations are off/high by 2x and subsequently divide by 2.
Speed Mi/h) | Speed (km/h) | Speed (m/s) | Angular Velocity (rev/sec) @1x | Angular Period (ms/rev) @1x | Angular Period (ms/rev) @2x |
---|---|---|---|---|---|
1.5 | 2.4 | .667 | 1 | 1000 | 500 |
14.9 | 24 | 6.667 | 10 | 100 | 50 |
31.1 | 50 | 13.889 | 20.9 | 47 | 24 |
62.1 | 100 | 27.778 | 41.7 | 23 | 12 |
149.1 | 240 | 66.667 | 100 | 10 | 5 |
Theory of Operation
I make us of the time to negotiate the WiFi connection to measure wind speed in the background with an ISR. I collect ∆t information in an array and allow for up to 6 samples to average. I reject ∆t measurements below 10ms as a software debounce.
Wind Direction Measurement
It interests me that the lowest ADC value is 263, but we are looking for values below 150. While I refactored the original code, I did not adjust that target. I’m guessing the ADC has non-linearities that are the cause of this.
Wind Vane Calibration
I’m showing North as the wind vane bullet points to the anemometer. Adjust mast accordingly.
EEPROM Module (v 1.2 or greater supports this hardware)
This is an optional module I threw together for those that want to be 100% certain they do not lose rainfall data due to RESET events. I have access to many serial NVMs at work and used a 512Kb or 64KB NVM. I only currently am using 144B for storage. A 24C512 from Atmel/Microchip was used in my case.
Hardware RESET events are covered by LDO upgrade
Still see very intermittent RESET events (MQTT broker goes off line, no internet connectivity, stuff out of my control, etc)
If this scenario concerns you, then you may wish to build this module
I could have certainly used ESP32 onboard EEPROM, but in the unlikely event of write endurance failure, I just replace the module, not the entire ESP32.
The EEPROM routine stores rainfall history structure to NVM in parallel with the RTC memory values. The routine is written to only execute writes to memory if something has changed. This conserves write endurance, although the memory I use has 1M cycle endurance.
There is a compiler directive to enable or disable this functionality.
Initial Evaluation Results
I was able to force RESET events and 24hour rainfall totals remain. Rainfall (hourly) totals RESET at the top of the hour as they should.
MQTT Topics
Topic Name | Purpose |
---|---|
batteryADC | ADC reading of VBAT divider |
batteryVoltage | Calculated VBAT |
boot | boot counter |
caseTemperature | temperature at PCB |
ESPCoreC | ESP internal core temperature in deg C |
ESPCoreF | ESP internal core temperature in deg F |
lowBattery | bool indicator of <15% battery remaining |
lux | light level |
photoresistor | ADC value for photoresistor |
pressure | barometric pressure |
rainfall | current hour rainfall |
rainfall24 | current 24 hour rainfall |
relHum | relative humidity |
temperatureC | temperatureC value at DS1820B probe |
temperatureF | temperatureF value at DS1820B probe |
UVIndex | UVIndex |
windDirection | wind heading |
windCardinalDirection | cardinal direction |
windSpeed | wind speed indicator |