Modes still not working
This commit is contained in:
parent
8d70f55548
commit
80018fc5d5
78
esp32/include/RunningMedian.h
Normal file
78
esp32/include/RunningMedian.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
//
|
||||||
|
// FILE: RunningMedian.h
|
||||||
|
// AUTHOR: Rob dot Tillaart at gmail dot com
|
||||||
|
// PURPOSE: RunningMedian library for Arduino
|
||||||
|
// VERSION: 0.2.1
|
||||||
|
// URL: https://github.com/RobTillaart/RunningMedian
|
||||||
|
// URL: http://arduino.cc/playground/Main/RunningMedian
|
||||||
|
// HISTORY: See RunningMedian.cpp
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#define RUNNING_MEDIAN_VERSION "0.2.1"
|
||||||
|
|
||||||
|
// prepare for dynamic version
|
||||||
|
// not tested ==> use at own risk :)
|
||||||
|
// #define RUNNING_MEDIAN_USE_MALLOC
|
||||||
|
|
||||||
|
|
||||||
|
// should at least be 5 to be practical,
|
||||||
|
// odd sizes results in a 'real' middle element and will be a bit faster.
|
||||||
|
// even sizes takes the average of the two middle elements as median
|
||||||
|
#define MEDIAN_MIN_SIZE 5
|
||||||
|
#define MEDIAN_MAX_SIZE 19
|
||||||
|
|
||||||
|
|
||||||
|
class RunningMedian
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// # elements in the internal buffer
|
||||||
|
explicit RunningMedian(const uint8_t size);
|
||||||
|
~RunningMedian();
|
||||||
|
|
||||||
|
// resets internal buffer and var
|
||||||
|
void clear();
|
||||||
|
// adds a new value to internal buffer, optionally replacing the oldest element.
|
||||||
|
void add(const float value);
|
||||||
|
// returns the median == middle element
|
||||||
|
float getMedian();
|
||||||
|
|
||||||
|
// returns average of the values in the internal buffer
|
||||||
|
float getAverage();
|
||||||
|
// returns average of the middle nMedian values, removes noise from outliers
|
||||||
|
float getAverage(uint8_t nMedian);
|
||||||
|
|
||||||
|
float getHighest() { return getSortedElement(_cnt - 1); };
|
||||||
|
float getLowest() { return getSortedElement(0); };
|
||||||
|
|
||||||
|
// get n'th element from the values in time order
|
||||||
|
float getElement(const uint8_t n);
|
||||||
|
// get n'th element from the values in size order
|
||||||
|
float getSortedElement(const uint8_t n);
|
||||||
|
// predict the max change of median after n additions
|
||||||
|
float predict(const uint8_t n);
|
||||||
|
|
||||||
|
uint8_t getSize() { return _size; };
|
||||||
|
// returns current used elements, getCount() <= getSize()
|
||||||
|
uint8_t getCount() { return _cnt; };
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
boolean _sorted;
|
||||||
|
uint8_t _size;
|
||||||
|
uint8_t _cnt;
|
||||||
|
uint8_t _idx;
|
||||||
|
|
||||||
|
#ifdef RUNNING_MEDIAN_USE_MALLOC
|
||||||
|
float * _ar;
|
||||||
|
uint8_t * _p;
|
||||||
|
#else
|
||||||
|
float _ar[MEDIAN_MAX_SIZE];
|
||||||
|
uint8_t _p[MEDIAN_MAX_SIZE];
|
||||||
|
#endif
|
||||||
|
void sort();
|
||||||
|
};
|
||||||
|
|
||||||
|
// END OF FILE
|
170
esp32/src/RunningMedian.cpp
Normal file
170
esp32/src/RunningMedian.cpp
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
//
|
||||||
|
// FILE: RunningMedian.cpp
|
||||||
|
// AUTHOR: Rob.Tillaart at gmail.com
|
||||||
|
// VERSION: 0.2.1
|
||||||
|
// PURPOSE: RunningMedian library for Arduino
|
||||||
|
//
|
||||||
|
// HISTORY:
|
||||||
|
// 0.1.00 - 2011-02-16 initial version
|
||||||
|
// 0.1.01 - 2011-02-22 added remarks from CodingBadly
|
||||||
|
// 0.1.02 - 2012-03-15 added
|
||||||
|
// 0.1.03 - 2013-09-30 added _sorted flag, minor refactor
|
||||||
|
// 0.1.04 - 2013-10-17 added getAverage(uint8_t) - kudo's to Sembazuru
|
||||||
|
// 0.1.05 - 2013-10-18 fixed bug in sort; removes default constructor; dynamic memory
|
||||||
|
// 0.1.06 - 2013-10-19 faster sort, dynamic arrays, replaced sorted float array with indirection array
|
||||||
|
// 0.1.07 - 2013-10-19 add correct median if _cnt is even.
|
||||||
|
// 0.1.08 - 2013-10-20 add getElement(), add getSottedElement() add predict()
|
||||||
|
// 0.1.09 - 2014-11-25 float to double (support ARM)
|
||||||
|
// 0.1.10 - 2015-03-07 fix clear
|
||||||
|
// 0.1.11 - 2015-03-29 undo 0.1.10 fix clear
|
||||||
|
// 0.1.12 - 2015-07-12 refactor constructor + const
|
||||||
|
// 0.1.13 - 2015-10-30 fix getElement(n) - kudos to Gdunge
|
||||||
|
// 0.1.14 - 2017-07-26 revert double to float - issue #33
|
||||||
|
// 0.1.15 - 2018-08-24 make runningMedian Configurable #110
|
||||||
|
// 0.2.0 2020-04-16 refactor.
|
||||||
|
// 0.2.1 2020-06-19 fix library.json
|
||||||
|
|
||||||
|
#include "RunningMedian.h"
|
||||||
|
|
||||||
|
RunningMedian::RunningMedian(const uint8_t size)
|
||||||
|
{
|
||||||
|
_size = constrain(size, MEDIAN_MIN_SIZE, MEDIAN_MAX_SIZE);
|
||||||
|
|
||||||
|
#ifdef RUNNING_MEDIAN_USE_MALLOC
|
||||||
|
_ar = (float *) malloc(_size * sizeof(float));
|
||||||
|
_p = (uint8_t *) malloc(_size * sizeof(uint8_t));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
RunningMedian::~RunningMedian()
|
||||||
|
{
|
||||||
|
#ifdef RUNNING_MEDIAN_USE_MALLOC
|
||||||
|
free(_ar);
|
||||||
|
free(_p);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// resets all counters
|
||||||
|
void RunningMedian::clear()
|
||||||
|
{
|
||||||
|
_cnt = 0;
|
||||||
|
_idx = 0;
|
||||||
|
_sorted = false;
|
||||||
|
for (uint8_t i = 0; i < _size; i++)
|
||||||
|
{
|
||||||
|
_p[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds a new value to the data-set
|
||||||
|
// or overwrites the oldest if full.
|
||||||
|
void RunningMedian::add(float value)
|
||||||
|
{
|
||||||
|
_ar[_idx++] = value;
|
||||||
|
if (_idx >= _size) _idx = 0; // wrap around
|
||||||
|
if (_cnt < _size) _cnt++;
|
||||||
|
_sorted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float RunningMedian::getMedian()
|
||||||
|
{
|
||||||
|
if (_cnt == 0) return NAN;
|
||||||
|
|
||||||
|
if (_sorted == false) sort();
|
||||||
|
|
||||||
|
if (_cnt & 0x01) // is it odd sized?
|
||||||
|
{
|
||||||
|
return _ar[_p[_cnt / 2]];
|
||||||
|
}
|
||||||
|
return (_ar[_p[_cnt / 2]] + _ar[_p[_cnt / 2 - 1]]) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
float RunningMedian::getAverage()
|
||||||
|
{
|
||||||
|
if (_cnt == 0) return NAN;
|
||||||
|
|
||||||
|
float sum = 0;
|
||||||
|
for (uint8_t i = 0; i < _cnt; i++)
|
||||||
|
{
|
||||||
|
sum += _ar[i];
|
||||||
|
}
|
||||||
|
return sum / _cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
float RunningMedian::getAverage(uint8_t nMedians)
|
||||||
|
{
|
||||||
|
if ((_cnt == 0) || (nMedians == 0)) return NAN;
|
||||||
|
|
||||||
|
if (_cnt < nMedians) nMedians = _cnt; // when filling the array for first time
|
||||||
|
uint8_t start = ((_cnt - nMedians) / 2);
|
||||||
|
uint8_t stop = start + nMedians;
|
||||||
|
|
||||||
|
if (_sorted == false) sort();
|
||||||
|
|
||||||
|
float sum = 0;
|
||||||
|
for (uint8_t i = start; i < stop; i++)
|
||||||
|
{
|
||||||
|
sum += _ar[_p[i]];
|
||||||
|
}
|
||||||
|
return sum / nMedians;
|
||||||
|
}
|
||||||
|
|
||||||
|
float RunningMedian::getElement(const uint8_t n)
|
||||||
|
{
|
||||||
|
if ((_cnt == 0) || (n >= _cnt)) return NAN;
|
||||||
|
|
||||||
|
uint8_t pos = _idx + n;
|
||||||
|
if (pos >= _cnt) // faster than %
|
||||||
|
{
|
||||||
|
pos -= _cnt;
|
||||||
|
}
|
||||||
|
return _ar[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
float RunningMedian::getSortedElement(const uint8_t n)
|
||||||
|
{
|
||||||
|
if ((_cnt == 0) || (n >= _cnt)) return NAN;
|
||||||
|
|
||||||
|
if (_sorted == false) sort();
|
||||||
|
return _ar[_p[n]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// n can be max <= half the (filled) size
|
||||||
|
float RunningMedian::predict(const uint8_t n)
|
||||||
|
{
|
||||||
|
if ((_cnt == 0) || (n >= _cnt / 2)) return NAN;
|
||||||
|
|
||||||
|
float med = getMedian(); // takes care of sorting !
|
||||||
|
if (_cnt & 0x01)
|
||||||
|
{
|
||||||
|
return max(med - _ar[_p[_cnt / 2 - n]], _ar[_p[_cnt / 2 + n]] - med);
|
||||||
|
}
|
||||||
|
float f1 = (_ar[_p[_cnt / 2 - n]] + _ar[_p[_cnt / 2 - n - 1]]) / 2;
|
||||||
|
float f2 = (_ar[_p[_cnt / 2 + n]] + _ar[_p[_cnt / 2 + n - 1]]) / 2;
|
||||||
|
return max(med - f1, f2 - med) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunningMedian::sort()
|
||||||
|
{
|
||||||
|
// bubble sort with flag
|
||||||
|
for (uint8_t i = 0; i < _cnt - 1; i++)
|
||||||
|
{
|
||||||
|
bool flag = true;
|
||||||
|
for (uint8_t j = 1; j < _cnt - i; j++)
|
||||||
|
{
|
||||||
|
if (_ar[_p[j - 1]] > _ar[_p[j]])
|
||||||
|
{
|
||||||
|
uint8_t t = _p[j - 1];
|
||||||
|
_p[j - 1] = _p[j];
|
||||||
|
_p[j] = t;
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) break;
|
||||||
|
}
|
||||||
|
_sorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- END OF FILE --
|
@ -24,7 +24,11 @@ const unsigned long TEMPREADCYCLE = 30000; /**< Check temperature all half minut
|
|||||||
#define TEMP_INIT_VALUE -999.0f
|
#define TEMP_INIT_VALUE -999.0f
|
||||||
#define TEMP_MAX_VALUE 85.0f
|
#define TEMP_MAX_VALUE 85.0f
|
||||||
|
|
||||||
RTC_DATA_ATTR bool coldBoot = false;
|
RTC_DATA_ATTR bool coldBoot = true;
|
||||||
|
bool warmBoot = true;
|
||||||
|
bool mode2Active = false;
|
||||||
|
bool mode3Active = false;
|
||||||
|
|
||||||
|
|
||||||
bool mLoopInited = false;
|
bool mLoopInited = false;
|
||||||
bool mDeepSleep = false;
|
bool mDeepSleep = false;
|
||||||
@ -326,9 +330,15 @@ bool switch3Handler(const HomieRange& range, const String& value) {
|
|||||||
return switchGeneralPumpHandler(2, range, value);
|
return switchGeneralPumpHandler(2, range, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void systemInit(){
|
void systemInit(){
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
|
Homie_setFirmware("PlantControl", FIRMWARE_VERSION);
|
||||||
|
Homie.setLoopFunction(loopHandler);
|
||||||
|
Homie.setup();
|
||||||
|
|
||||||
|
mConfigured = Homie.isConfigured();
|
||||||
|
|
||||||
// Set default values
|
// Set default values
|
||||||
deepSleepTime.setDefaultValue(300000); /* 5 minutes in milliseconds */
|
deepSleepTime.setDefaultValue(300000); /* 5 minutes in milliseconds */
|
||||||
deepSleepNightTime.setDefaultValue(0);
|
deepSleepNightTime.setDefaultValue(0);
|
||||||
@ -408,18 +418,32 @@ void systemInit(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void mode1(){
|
bool mode1(){
|
||||||
|
Serial.println("Init mode 1");
|
||||||
readSensors();
|
readSensors();
|
||||||
|
//TODO evaluate if something is to do
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mode2(){
|
void mode2(){
|
||||||
WiFi.mode(WIFI_STA);
|
Serial.println("Init mode 2");
|
||||||
Homie.setup();
|
mode2Active = true;
|
||||||
|
|
||||||
|
|
||||||
|
systemInit();
|
||||||
|
|
||||||
|
/* Jump into Mode 3, if not configured */
|
||||||
|
if (!mConfigured) {
|
||||||
|
mode2Active = false;
|
||||||
|
mode3Active = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mode3(){
|
void mode3(){
|
||||||
WiFi.mode(WIFI_STA);
|
Serial.println("Init mode 3");
|
||||||
Homie.setup();
|
mode3Active = true;
|
||||||
|
|
||||||
|
systemInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -460,19 +484,12 @@ void setup() {
|
|||||||
Serial << " | Update Limits.hpp : MAX_JSON_CONFIG_FILE_SIZE to 5000" << endl;
|
Serial << " | Update Limits.hpp : MAX_JSON_CONFIG_FILE_SIZE to 5000" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
Homie_setFirmware("PlantControl", FIRMWARE_VERSION);
|
|
||||||
Homie.setLoopFunction(loopHandler);
|
|
||||||
|
|
||||||
mConfigured = Homie.isConfigured();
|
|
||||||
systemInit();
|
|
||||||
|
|
||||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
|
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
|
||||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
|
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
|
||||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
|
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
|
||||||
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL,ESP_PD_OPTION_ON);
|
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL,ESP_PD_OPTION_ON);
|
||||||
|
|
||||||
|
// Big TODO use here the settings in RTC_Memory
|
||||||
mode2();
|
|
||||||
|
|
||||||
// Configure Deep Sleep:
|
// Configure Deep Sleep:
|
||||||
if (mConfigured && (deepSleepNightTime.get() > 0) &&
|
if (mConfigured && (deepSleepNightTime.get() > 0) &&
|
||||||
@ -503,50 +520,55 @@ void setup() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if(!coldBoot){
|
if(coldBoot){
|
||||||
coldBoot = true;
|
coldBoot = false;
|
||||||
delay(1000);
|
delay(1000);
|
||||||
|
if (digitalRead(BUTTON) == LOW){
|
||||||
|
for(int i = 0;i<10;i++){
|
||||||
digitalWrite(OUTPUT_SENSOR, LOW);
|
digitalWrite(OUTPUT_SENSOR, LOW);
|
||||||
|
delay(50);
|
||||||
|
digitalWrite(OUTPUT_SENSOR, HIGH);
|
||||||
|
delay(50);
|
||||||
}
|
}
|
||||||
if (!mDeepSleep || !mConfigured) {
|
|
||||||
if ((digitalRead(BUTTON) == LOW) && (mButtonClicks % 2) == 0) {
|
|
||||||
mButtonClicks++;
|
|
||||||
|
|
||||||
switch(mButtonClicks) {
|
|
||||||
case 1:
|
|
||||||
case 3:
|
|
||||||
case 5:
|
|
||||||
mode3();
|
mode3();
|
||||||
break;
|
return;
|
||||||
default:
|
} else {
|
||||||
Serial << "No further tests! Please reboot" << endl;
|
|
||||||
}
|
|
||||||
Serial.flush();
|
|
||||||
}else if (mButtonClicks > 0 && (digitalRead(BUTTON) == HIGH) && (mButtonClicks % 2) == 1) {
|
|
||||||
Serial << "Self Test Ended" << endl;
|
|
||||||
mButtonClicks++;
|
|
||||||
/* Always reset all outputs */
|
|
||||||
digitalWrite(OUTPUT_SENSOR, LOW);
|
digitalWrite(OUTPUT_SENSOR, LOW);
|
||||||
for(int i=0; i < MAX_PLANTS; i++) {
|
|
||||||
digitalWrite(mPlants[i].getPumpPin(), LOW);
|
|
||||||
}
|
}
|
||||||
digitalWrite(OUTPUT_PUMP4, LOW);
|
|
||||||
} else if (mButtonClicks == 0) {
|
|
||||||
Homie.loop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Perform the active modes (non mode1) */
|
||||||
|
if (mode3Active || mode2Active) {
|
||||||
|
Homie.loop();
|
||||||
|
if(!mode3Active){
|
||||||
|
/* Upgrade to mode 3 via reset */
|
||||||
|
if (digitalRead(BUTTON) == LOW){
|
||||||
|
coldBoot=true;
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!mAlive) {
|
/* Check which mode shall be selected */
|
||||||
Serial << (millis()/ 1000) << "s running; sleeeping ..." << endl;
|
if(warmBoot){
|
||||||
|
warmBoot = false;
|
||||||
|
if(mode1()){
|
||||||
|
mode2();
|
||||||
|
} else {
|
||||||
|
/* Upgrade to mode 3 via reset */
|
||||||
|
if (digitalRead(BUTTON) == LOW){
|
||||||
|
coldBoot=true;
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
Serial.println("Nothing to do back to sleep");
|
||||||
Serial.flush();
|
Serial.flush();
|
||||||
esp_deep_sleep_start();
|
esp_deep_sleep_start();
|
||||||
} else {
|
}
|
||||||
mDeepSleep = false;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (((millis()) % 10000) == 0) {
|
if(millis() > 30000 && !mode3Active){
|
||||||
/* tell everybody how long we are awoken */
|
Serial << (millis()/ 1000) << "s running; going to suicide ..." << endl;
|
||||||
stayAlive.setProperty("alive").send( String(millis()/ 1000) );
|
Serial.flush();
|
||||||
}
|
esp_deep_sleep_start();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user