I'm using an ESP32 + baro sensor (MS5611), and looking to trigger an action when the altitude over last few minutes changes above a threshold. I can imagine doing this by storing individual sample values over time. However storing samples at 60/min for last 20-30 minutes will consume too much memory.
Memory unfortunately is a constrain. Is there a way to achieve this with using less amount of memory?
Do you want to know the actual min and max readings over the previous x minutes, or if the reading has exceeded a specific threshold within the previous x minutes? The former would likely require that all samples be saved, the latter would only need to know the time of the most recent reading that exceeded the threshold (so that you could tell when that reading dropped out of the x minute window).
I'm looking at change in altitude over time. As an example — given the barometer can go up and down — in the past say 10 minutes, have we seen an altitude change of more than 15 meters, given a sample rate of 1 sample per second. Absolute altitude does not really matter, for this case.
I can't really think of any way to get around saving all the samples. If you saved just the min and max reading, and the time those occurred, you would still need all the other readings to determine the new min/max when one of those dropped out of the 10 minute window.
How much deviation do you expect in the pressure readings? You might be able to save memory by storing the readings as the deviation from some nominal value, instead of the actual pressure, although this would work best if you used the raw 24-bit data from the MS5611 instead of the float from the pressure calculation.
So at X millis() store a value and the millis() value. Over the course of X time check each new value against the stored value. At the end of X time, store a new millis() value and once again for a duration check the values.
Though for an ESP32 I'd not use millis() due to its limitations over using the ESP32's own micro cycle count.
this function runs a pump for x amount of time during which time values are checked and if out of spec the pump stops.
void fDoMoistureDetector1( void * parameter )
{
//wait for a mqtt connection
while ( !MQTTclient.connected() )
{
vTaskDelay( 250 );
}
int TimeToPublish = 5000000; //5000000uS
int TimeForADreading = 100 * 1000; // 100mS
uint64_t TimePastPublish = esp_timer_get_time(); // used by publish
uint64_t TimeADreading = esp_timer_get_time();
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = 10; //delay for 10mS
float RemainingMoisture = 100.0f; //prevents pump turn on during start up
bool pumpOn = false;
uint64_t PumpOnTime = esp_timer_get_time();
int PumpRunTime = 11000000;
uint64_t PumpOffWait = esp_timer_get_time();
uint64_t PumpOffWaitFor = 60000000; //one minute
float lowMoisture = 23.0f;
float highMoisture = 40.0f;
for (;;)
{
xSemaphoreTake( sema_WaterCactus, portMAX_DELAY );
//read AD values every 100mS.
if ( (esp_timer_get_time() - TimeADreading) >= TimeForADreading )
{
xEventGroupSetBits( eg, evtADCreading0 );
TimeADreading = esp_timer_get_time();
}
xQueueReceive(xQ_RM, &RemainingMoisture, 0 ); //receive queue stuff no waiting
//read gpio 0 is water level good. Yes: OK to run pump : no pump off. remaining moisture good, denergize water pump otherwise energize water pump.
if ( RemainingMoisture >= highMoisture )
{
WaterPump0_off();
}
if ( !pumpOn )
{
log_i( "not pump on ");
if ( gpio_get_level( GPIO_NUM_0 ) )
{
if ( RemainingMoisture <= lowMoisture )
{
//has one minute passed since last pump energize, if so then allow motor to run
if ( (esp_timer_get_time() - PumpOffWait) >= PumpOffWaitFor )
{
gpio_set_level( GPIO_NUM_5, HIGH); //open valve
WaterPump0_on();
log_i( "pump on " );
pumpOn = !pumpOn;
PumpOnTime = esp_timer_get_time();
}
}
//xSemaphoreGive( sema_RemainingMoisture );
} else {
log_i( "water level bad " );
WaterPump0_off();
gpio_set_level( GPIO_NUM_5, LOW); //denergize/close valve
PumpOffWait = esp_timer_get_time();
}
} else {
/*
pump goes on runs for X seconds then turn off, then wait PumpOffWaitTime before being allowed to energize again
*/
if ( (esp_timer_get_time() - PumpOnTime) >= PumpRunTime )
{
log_i( "pump off " );
WaterPump0_off(); // after 5 seconds turn pump off
gpio_set_level( GPIO_NUM_5, LOW); //denergize/close valve
pumpOn = !pumpOn;
PumpOffWait = esp_timer_get_time();
}
}
// publish to MQTT every 5000000uS
if ( (esp_timer_get_time() - TimePastPublish) >= TimeToPublish )
{
xQueueOverwrite( xQ_RemainingMoistureMQTT, (void *) &RemainingMoisture );// data for mqtt publish
TimePastPublish = esp_timer_get_time(); // get next publish time
}
xSemaphoreGive( sema_WaterCactus );
xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil( &xLastWakeTime, xFrequency );
}
vTaskDelete( NULL );
}// end fDoMoistureDetector1()
MY ESP is mounted on a fixed wing RC plane. Expected deviation will be in the few hundred meters over a few minutes as the plane climbs and descends. My use-case is to implement an auto-shutdown when there's no "significant motion" detected. Significant motion being defined as an arbitrary "less than m altitude change in last minutes"
Sounds more like you need a rolling average of the amount of change between readings. When the altitude changes ceased the average would drop toward zero.
Why an altimeter instead of an accelerometer?
My shortest RC plane flight was 4 seconds when I started with my ailerons plugged in reverse, resulting in a "rapid unscheduled disassembly" at the end of the runway.
Unless you plan to make such short flights, would it be sufficient to log your altitude every 10 seconds in a array with a rolling index? you would get 120 values to assess.
Still a bit puzzled about your use case. How do you prevent that your autoshutdown, does not just kick in when, after a 19 minute coffee break, you scream down the runway for your second flight, and you just passed your 20 minutes auto shutdown timewindow at that point?