Test version for fermentation

This commit is contained in:
Magnus 2021-04-07 20:16:40 +02:00
parent 7f782ad7bd
commit 876718602f
11 changed files with 146 additions and 73 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
test/*.md test/*.md
test/env/* test/env/*
test/*.py test/*.py
TODO.md

View File

@ -7,9 +7,11 @@ I started this project out of curiosity for how a motion sensor is working and s
* Add support for Plato in device (today it assumes that formula is in SG). * Add support for Plato in device (today it assumes that formula is in SG).
* Add support for converting between SG/Plato in device. * Add support for converting between SG/Plato in device.
* Add support for Blynk as endpoint * Add support for Blynk as endpoint
* Add support for https connections (push) * Add support for https connections (push) - [need to reduce memory usage for this to work, gets out of memory error]
* Add support for https web server (will require certificates to be created as part of build process) * Add support for https web server (will require certificates to be created as part of build process)
* Add iSpindle 3D print cradle + small PCB (what I use for my builds) * Add iSpindle 3D print cradle + small PCB (what I use for my builds)
* Validate max sleep time to 70 min (max time for ESP)
* Check how much movement there is during fermentack (check run time) if we should go into sleep mode for a shorter time...
# Functionallity # Functionallity
@ -96,11 +98,7 @@ The second section contains the push settings, two URL's for http posts, Brewfat
### This is the format for InfluxDB v2 ### This is the format for InfluxDB v2
``` ```
gravity,host=<mdns>,device=<id>,format=SG value=1.0004 measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18
angle,host=<mdns>,device=<id> value=45.45
temp,host=<mdns>,device=<id>,format=<C|F> value=20.1
battery,host=<mdns>,device=<id> value=3.96
rssi,host=<mdns>,device=<id> value=-18
``` ```
__TODO: Update image for push settings.__ __TODO: Update image for push settings.__

Binary file not shown.

Binary file not shown.

View File

@ -22,9 +22,13 @@ build_unflags =
build_flags = #-O0 -Wl,-Map,output.map build_flags = #-O0 -Wl,-Map,output.map
-D BAUD=${common_env_data.monitor_speed} -D BAUD=${common_env_data.monitor_speed}
-D ACTIVATE_OTA -D ACTIVATE_OTA
#-D DEBUG_ESP_HTTP_CLIENT
#-D DEBUG_ESP_HTTP_SERVER
#-D DEBUG_ESP_PORT=Serial
#-D SKIP_SLEEPMODE #-D SKIP_SLEEPMODE
#-D DOUBLERESETDETECTOR_DEBUG true
-D USE_LITTLEFS=true -D USE_LITTLEFS=true
-D EMBED_HTML -D EMBED_HTML # If this is not used the html files needs to be on the file system (can be uploaded)
-D USER_SSID=\""\"" # =\""myssid\"" -D USER_SSID=\""\"" # =\""myssid\""
-D USER_SSID_PWD=\""\"" # =\""mypwd\"" -D USER_SSID_PWD=\""\"" # =\""mypwd\""
-D CFG_APPVER="\"0.3.5\"" -D CFG_APPVER="\"0.3.5\""
@ -38,6 +42,7 @@ lib_deps =
https://github.com/bblanchon/ArduinoJson https://github.com/bblanchon/ArduinoJson
https://github.com/PaulStoffregen/OneWire https://github.com/PaulStoffregen/OneWire
https://github.com/milesburton/Arduino-Temperature-Control-Library https://github.com/milesburton/Arduino-Temperature-Control-Library
https://github.com/wollewald/INA219_WE # For measuring power consumption
[env:gravity-debug] [env:gravity-debug]
upload_speed = ${common_env_data.upload_speed} upload_speed = ${common_env_data.upload_speed}

View File

@ -52,7 +52,7 @@ bool GyroSensor::setup() {
Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR)); Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR));
sensorConnected = true; sensorConnected = true;
// Configure ethe sensor // Configure the sensor
accelgyro.setTempSensorEnabled(true); accelgyro.setTempSensorEnabled(true);
accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
accelgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_250); accelgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
@ -91,6 +91,7 @@ void GyroSensor::readSensor(RawGyroData &raw, const int noIterations, const int
#if LOG_LEVEL==6 #if LOG_LEVEL==6
Log.verbose(F("GYRO: Reading sensor with %d iterations %d us delay." CR), noIterations, delayTime ); Log.verbose(F("GYRO: Reading sensor with %d iterations %d us delay." CR), noIterations, delayTime );
#endif #endif
// Set some initial values // Set some initial values
#if defined( GYRO_SHOW_MINMAX ) #if defined( GYRO_SHOW_MINMAX )
RawGyroData min, max; RawGyroData min, max;
@ -99,7 +100,6 @@ void GyroSensor::readSensor(RawGyroData &raw, const int noIterations, const int
min.temp = accelgyro.getTemperature(); min.temp = accelgyro.getTemperature();
max = min; max = min;
#endif #endif
for(int cnt = 0; cnt < noIterations ; cnt ++) { for(int cnt = 0; cnt < noIterations ; cnt ++) {
accelgyro.getRotation( &raw.gx, &raw.gy, &raw.gz ); accelgyro.getRotation( &raw.gx, &raw.gy, &raw.gz );
accelgyro.getAcceleration( &raw.ax, &raw.ay, &raw.az ); accelgyro.getAcceleration( &raw.ax, &raw.ay, &raw.az );

View File

@ -26,6 +26,10 @@ SOFTWARE.
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h> #include <ESP8266HTTPClient.h>
#if defined( COLLECT_PERFDATA )
#include <INA219_WE.h> // For measuring power consumption
#endif
SerialDebug mySerial; SerialDebug mySerial;
BatteryVoltage myBatteryVoltage; BatteryVoltage myBatteryVoltage;
@ -95,27 +99,64 @@ void BatteryVoltage::read() {
#endif #endif
} }
#if defined( COLLECT_PERFDATA ) #if defined( COLLECT_PERFDATA )
PerfLogging myPerfLogging; PerfLogging myPerfLogging;
INA219_WE ina219(0x40); // For measuring power consumption
//
// Initialize
//
PerfLogging::PerfLogging() {
if( ina219.init() )
measurePower = true;
Log.notice( F("PERF: Performance logging enabled. Power sensor %s" CR), measurePower?"found":"not found");
}
//
// Initialize
//
void PerfLogging::readPowerSensor(PerfEntry *pe) {
pe->mA = 0;
pe->V = 0;
if( !measurePower )
return;
if( ina219.getOverflow() )
Log.error( F("PERF: Voltage sensor overflow detected." CR));
/*
shuntVoltage_mV = ina219.getShuntVoltage_mV();
busVoltage_V = ina219.getBusVoltage_V();
current_mA = ina219.getCurrent_mA();
power_mW = ina219.getBusPower();
loadVoltage_V = busVoltage_V + (shuntVoltage_mV/1000);
*/
pe->mA = ina219.getCurrent_mA();
pe->V = ina219.getBusVoltage_V() + (ina219.getShuntVoltage_mV()/1000);
}
// //
// Clear the current cache // Clear the current cache
// //
void PerfLogging::clear() { void PerfLogging::clear() {
// Clear the measurements
if( first == 0 ) if( first == 0 )
return; return;
PerfEntry* pe = first; PerfEntry* pe = first;
do { do {
PerfEntry* p = pe; pe->max = 0;
pe->start = 0;
pe->end = 0;
pe->mA = 0;
pe->V = 0;
pe = pe->next; pe = pe->next;
delete p;
} while( pe != 0 ); } while( pe != 0 );
first = 0;
} }
// //
@ -139,6 +180,8 @@ void PerfLogging::stop( const char* key ) {
if( t > pe->max ) if( t > pe->max )
pe->max = t; pe->max = t;
readPowerSensor( pe );
} }
} }
@ -175,11 +218,19 @@ void PerfLogging::pushInflux() {
String body; String body;
PerfEntry* pe = first; PerfEntry* pe = first;
char buf[100];
sprintf( &buf[0], "perf,host=%s,device=%s ", myConfig.getMDNS(), myConfig.getID() );
body += &buf[0];
while( pe != 0 ) { while( pe != 0 ) {
char buf[100]; if( pe->max ) {
sprintf( &buf[0], "%s,host=%s,device=%s value=%ld\n", pe->key, myConfig.getMDNS(), myConfig.getID(), pe->max); if( pe->next )
sprintf( &buf[0], "%s=%ld,", pe->key, pe->max);
else
sprintf( &buf[0], "%s=%ld", pe->key, pe->max);
body += &buf[0]; body += &buf[0];
}
pe = pe->next; pe = pe->next;
} }

View File

@ -66,10 +66,15 @@ class PerfLogging {
unsigned long end; // millis() unsigned long end; // millis()
unsigned long max; // max time in ms unsigned long max; // max time in ms
const char* key; // measurement const char* key; // measurement
PerfEntry* next; //
PerfEntry* next; // Next in the linked list
float mA; // Power consumption
float V; // Power consumption
}; };
PerfEntry* first = 0; PerfEntry* first = 0;
bool measurePower = false;
PerfEntry* find( const char* k ) { PerfEntry* find( const char* k ) {
if( first == 0 ) if( first == 0 )
@ -112,7 +117,10 @@ class PerfLogging {
return pe; return pe;
}; };
void readPowerSensor(PerfEntry* pe);
public: public:
PerfLogging();
void clear(); void clear();
void start( const char* key ); void start( const char* key );
void stop( const char* key ); void stop( const char* key );

View File

@ -35,7 +35,6 @@ SOFTWARE.
#define ESP8266_DRD_USE_RTC true #define ESP8266_DRD_USE_RTC true
#define DRD_TIMEOUT 2 #define DRD_TIMEOUT 2
#define DRD_ADDRESS 0 #define DRD_ADDRESS 0
#define DOUBLERESETDETECTOR_DEBUG true
#include <ESP_DoubleResetDetector.h> #include <ESP_DoubleResetDetector.h>
DoubleResetDetector *drd; DoubleResetDetector *drd;
@ -47,9 +46,11 @@ bool sleepModeAlwaysSkip = true; // Web interface can override normal
const int interval = 200; // ms, time to wait between changes to output const int interval = 200; // ms, time to wait between changes to output
bool sleepModeAlwaysSkip = false; // Web interface can override normal behaviour bool sleepModeAlwaysSkip = false; // Web interface can override normal behaviour
#endif #endif
unsigned long lastMillis = 0; unsigned long loopMillis = 0; // Used for main loop to run the code every _interval_
unsigned long startMillis; unsigned long runtimeMillis; // Used to calculate the total time since start/wakeup
unsigned long stableGyroMillis; // Used to calculate the total time since last stable gyro reading
bool sleepModeActive = false; bool sleepModeActive = false;
bool goToSleep = false;
int loopCounter = 0; int loopCounter = 0;
// //
@ -78,7 +79,7 @@ void checkSleepMode( float angle, float volt ) {
} }
// Will not enter sleep mode if: charger is connected // Will not enter sleep mode if: charger is connected
sleepModeActive = (volt<4.15 && angle>85) || (volt>4.15) ? false : true; sleepModeActive = (volt<4.15 && (angle>85 && angle<95)) || (volt>4.15) ? false : true;
// sleep mode active when flat // sleep mode active when flat
//sleepModeActive = ( angle<85 && angle>5 ) ? true : false; //sleepModeActive = ( angle<85 && angle>5 ) ? true : false;
@ -93,7 +94,7 @@ void checkSleepMode( float angle, float volt ) {
void setup() { void setup() {
LOG_PERF_START("run-time"); LOG_PERF_START("run-time");
LOG_PERF_START("main-setup"); LOG_PERF_START("main-setup");
startMillis = millis(); runtimeMillis = millis();
drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS); drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
bool dt = drd->detectDoubleReset(); bool dt = drd->detectDoubleReset();
@ -118,10 +119,10 @@ void setup() {
LOG_PERF_STOP("main-temp-setup"); LOG_PERF_STOP("main-temp-setup");
// Setup Gyro // Setup Gyro
LOG_PERF_START("main-gyro-setup"); //LOG_PERF_START("main-gyro-setup"); // Takes less than 5ms, so skip this measurment
if( !myGyro.setup() ) if( !myGyro.setup() )
Log.error(F("Main: Failed to initialize the gyro." CR)); Log.error(F("Main: Failed to initialize the gyro." CR));
LOG_PERF_STOP("main-gyro-setup"); //LOG_PERF_STOP("main-gyro-setup");
if( dt ) if( dt )
Log.notice(F("Main: Detected doubletap on reset." CR)); Log.notice(F("Main: Detected doubletap on reset." CR));
@ -148,16 +149,15 @@ void setup() {
LOG_PERF_STOP("main-wifi-ota"); LOG_PERF_STOP("main-wifi-ota");
#endif #endif
if( !sleepModeActive ) { if( !sleepModeActive ) {
LOG_PERF_START("main-webserver-setup"); //LOG_PERF_START("main-webserver-setup"); // Takes less than 4ms , so skip this measurment
myWebServer.setupWebServer(); myWebServer.setupWebServer();
LOG_PERF_STOP("main-webserver-setup"); //LOG_PERF_STOP("main-webserver-setup");
} }
} }
LOG_PERF_STOP("main-setup"); LOG_PERF_STOP("main-setup");
LOG_PERF_PRINT(); // Dump data to serial
LOG_PERF_PUSH(); // Dump data to influx
Log.notice(F("Main: Setup completed." CR)); Log.notice(F("Main: Setup completed." CR));
stableGyroMillis = millis(); // Put it here so we dont include time for wifi connection
} }
// //
@ -166,26 +166,31 @@ void setup() {
void loop() { void loop() {
drd->loop(); drd->loop();
if( sleepModeActive || abs(millis() - lastMillis) > interval ) { if( sleepModeActive || abs(millis() - loopMillis) > interval ) {
float angle = 90; float angle = 0;
float volt = myBatteryVoltage.getVoltage(); float volt = myBatteryVoltage.getVoltage();
float sensorTemp = 0; float sensorTemp = 0;
loopCounter++; loopCounter++;
#if LOG_LEVEL==6 #if LOG_LEVEL==6
Log.verbose(F("Main: Entering main loop." CR) ); Log.verbose(F("Main: Entering main loop." CR) );
#endif #endif
// Process the sensor values and push data to targets.
// ------------------------------------------------------------------------------------------------
// If we dont get any readings we just skip this and try again the next interval. // If we dont get any readings we just skip this and try again the next interval.
//
if( myGyro.hasValue() ) { if( myGyro.hasValue() ) {
angle = myGyro.getAngle(); // Gyro angle angle = myGyro.getAngle(); // Gyro angle
sensorTemp = myGyro.getSensorTempC(); // Temp in the Gyro sensorTemp = myGyro.getSensorTempC(); // Temp in the Gyro
stableGyroMillis = millis(); // Reset timer
LOG_PERF_START("loop-temp-read"); LOG_PERF_START("loop-temp-read");
float temp = myTempSensor.getValueCelcius(); // The code is build around using C for temp. float temp = myTempSensor.getValueCelcius(); // The code is build around using C for temp.
LOG_PERF_STOP("loop-temp-read"); LOG_PERF_STOP("loop-temp-read");
LOG_PERF_START("loop-gravity-calc"); //LOG_PERF_START("loop-gravity-calc"); // Takes less than 2ms , so skip this measurment
float gravity = calculateGravity( angle, temp ); float gravity = calculateGravity( angle, temp );
LOG_PERF_STOP("loop-gravity-calc"); //LOG_PERF_STOP("loop-gravity-calc");
#if LOG_LEVEL==6 #if LOG_LEVEL==6
Log.verbose(F("Main: Sensor values gyro angle=%F gyro temp=%F, temp=%F, gravity=%F." CR), angle, sensorTemp, temp, gravity ); Log.verbose(F("Main: Sensor values gyro angle=%F gyro temp=%F, temp=%F, gravity=%F." CR), angle, sensorTemp, temp, gravity );
@ -202,49 +207,62 @@ void loop() {
// Limit the printout when sleep mode is not active. // Limit the printout when sleep mode is not active.
if( loopCounter%10 == 0 || sleepModeActive ) { if( loopCounter%10 == 0 || sleepModeActive ) {
Log.notice(F("Main: gyro angle=%F, gyro temp=%F, DS18B20 temp=%F, gravity=%F, batt=%F." CR), angle, sensorTemp, temp, gravity, volt ); Log.notice(F("Main: gyro angle=%F, gyro temp=%F, DS18B20 temp=%F, gravity=%F, batt=%F." CR), angle, sensorTemp, temp, gravity, volt );
LOG_PERF_PRINT();
} }
unsigned long runTime = millis() - startMillis;
LOG_PERF_START("loop-push"); LOG_PERF_START("loop-push");
myPushTarget.send( angle, gravity, temp, runTime/1000, sleepModeActive ); // Force the transmission if we are going to sleep myPushTarget.send( angle, gravity, temp, (millis()-runtimeMillis)/1000, sleepModeActive ); // Force the transmission if we are going to sleep
LOG_PERF_STOP("loop-push"); LOG_PERF_STOP("loop-push");
// If we have completed the update lets go to sleep
if( sleepModeActive )
goToSleep = true;
} else { } else {
Log.error(F("Main: No gyro value." CR) ); Log.error(F("Main: No gyro value." CR) );
} }
if( sleepModeActive ) {
unsigned long runTime = millis() - startMillis;
// Enter sleep mode...
Log.notice(F("MAIN: Entering deep sleep for %d s, run time %l s, battery=%F V." CR), myConfig.getSleepInterval(), runTime/1000, volt );
LittleFS.end();
myGyro.enterSleep();
drd->stop();
LOG_PERF_STOP("run-time");
LOG_PERF_PUSH();
LOG_PERF_PRINT();
delay(100);
deepSleep( myConfig.getSleepInterval() );
}
#if LOG_LEVEL==6 #if LOG_LEVEL==6
Log.verbose(F("Main: Sleep mode not active." CR) ); Log.verbose(F("Main: Sleep mode not active." CR) );
#endif #endif
int sleepInterval = myConfig.getSleepInterval();
// If the sensor is moving and we are not getting a clear reading, we enter sleep for a short time to conserve battery.
// ------------------------------------------------------------------------------------------------
//
if( sleepModeActive && ((millis()-stableGyroMillis)>10000L) ) { // 10s since last stable gyro reading
Log.notice(F("MAIN: Unable to get a stable reading for 10s, sleeping for 60s." CR) );
sleepInterval = 60; // 60s
goToSleep = true;
}
// Enter sleep mode if the conditions are right
// ------------------------------------------------------------------------------------------------
//
if( goToSleep ) {
Log.notice(F("MAIN: Entering deep sleep for %d s, run time %l s, battery=%F V." CR), sleepInterval, (millis()-runtimeMillis)/1000, volt );
LittleFS.end();
myGyro.enterSleep();
drd->stop();
LOG_PERF_STOP("run-time");
LOG_PERF_PUSH();
delay(100);
deepSleep( sleepInterval );
}
// If we are running in normal mode we just continue
// ------------------------------------------------------------------------------------------------
// Do these checks if we are running in normal mode (not sleep mode) // Do these checks if we are running in normal mode (not sleep mode)
//
checkSleepMode( angle, volt ); checkSleepMode( angle, volt );
LOG_PERF_START("loop-gyro-read"); LOG_PERF_START("loop-gyro-read");
myGyro.read(); myGyro.read();
LOG_PERF_STOP("loop-gyro-read"); LOG_PERF_STOP("loop-gyro-read");
LOG_PERF_START("loop-batt-read"); //LOG_PERF_START("loop-batt-read"); // Takes less than 2ms , so skip this measurment
myBatteryVoltage.read(); myBatteryVoltage.read();
LOG_PERF_STOP("loop-batt-read"); //LOG_PERF_STOP("loop-batt-read");
lastMillis = millis(); loopMillis = millis();
#if LOG_LEVEL==6 #if LOG_LEVEL==6
Log.verbose(F("Main: Heap %d kb FreeSketch %d kb." CR), ESP.getFreeHeap()/1024, ESP.getFreeSketchSpace()/1024 ); Log.verbose(F("Main: Heap %d kb FreeSketch %d kb." CR), ESP.getFreeHeap()/1024, ESP.getFreeSketchSpace()/1024 );
Log.verbose(F("Main: HeapFrag %d %%." CR), ESP.getHeapFragmentation() ); Log.verbose(F("Main: HeapFrag %d %%." CR), ESP.getHeapFragmentation() );

View File

@ -23,6 +23,7 @@ SOFTWARE.
*/ */
#include "pushtarget.h" #include "pushtarget.h"
#include "config.h" #include "config.h"
#include "gyro.h" // For testing the tempsensor in the gyro
PushTarget myPushTarget; PushTarget myPushTarget;
@ -86,17 +87,11 @@ void PushTarget::sendInfluxDb2(float angle, float gravity, float temp, float run
// Create body for influxdb2 // Create body for influxdb2
char buf[1024]; char buf[1024];
sprintf( &buf[0], "gravity,host=%s,device=%s,format=%s value=%.4f\n" sprintf( &buf[0], "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s "
"angle,host=%s,device=%s value=%.2f\n" "gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,rssi=%d,temp2=%.2f\n",
"temp,host=%s,device=%s,format=%c value=%.1f\n"
"battery,host=%s,device=%s value=%.2f\n"
"rssi,host=%s,device=%s value=%d\n",
// TODO: Add support for plato format // TODO: Add support for plato format
myConfig.getMDNS(), myConfig.getID(), "SG", gravity, myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG",
myConfig.getMDNS(), myConfig.getID(), angle, gravity, angle, temp, myBatteryVoltage.getVoltage(), WiFi.RSSI(), myGyro.getSensorTempC() ); // For comparing gyro tempsensor vs DSB1820
myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), temp,
myConfig.getMDNS(), myConfig.getID(), myBatteryVoltage.getVoltage(),
myConfig.getMDNS(), myConfig.getID(), WiFi.RSSI() );
#if LOG_LEVEL==6 #if LOG_LEVEL==6
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str()); Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
@ -115,7 +110,6 @@ void PushTarget::sendInfluxDb2(float angle, float gravity, float temp, float run
} }
http.end(); http.end();
} }
// //

View File

@ -21,7 +21,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
//#define DEBUG_ESP_HTTP_SERVER
#include "webserver.h" #include "webserver.h"
#include "config.h" #include "config.h"
#include "helper.h" #include "helper.h"
@ -31,7 +30,6 @@ SOFTWARE.
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <incbin.h> #include <incbin.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
//#define DEBUG_ESP_HTTP_SERVER
#include <ESP8266WebServer.h> #include <ESP8266WebServer.h>
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#include <LittleFS.h> #include <LittleFS.h>