diff --git a/.gitignore b/.gitignore index e1bada2..bdbabf6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ *.map test/*.md test/env/* -test/*.py \ No newline at end of file +test/*.py +TODO.md \ No newline at end of file diff --git a/README.md b/README.md index 4529d56..f4fd0aa 100644 --- a/README.md +++ b/README.md @@ -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 converting between SG/Plato in device. * 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 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 @@ -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 ``` -gravity,host=,device=,format=SG value=1.0004 -angle,host=,device= value=45.45 -temp,host=,device=,format= value=20.1 -battery,host=,device= value=3.96 -rssi,host=,device= value=-18 +measurement,host=,device=,temp-format=,gravity-format=SG gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18 ``` __TODO: Update image for push settings.__ diff --git a/bin/firmware-debug.bin b/bin/firmware-debug.bin index 6648e93..f9909b9 100644 Binary files a/bin/firmware-debug.bin and b/bin/firmware-debug.bin differ diff --git a/bin/firmware.bin b/bin/firmware.bin index 832f534..1004918 100644 Binary files a/bin/firmware.bin and b/bin/firmware.bin differ diff --git a/platformio.ini b/platformio.ini index 9415a7a..315be10 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,10 +21,14 @@ board = d1_mini build_unflags = build_flags = #-O0 -Wl,-Map,output.map -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 DOUBLERESETDETECTOR_DEBUG 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_PWD=\""\"" # =\""mypwd\"" -D CFG_APPVER="\"0.3.5\"" @@ -38,6 +42,7 @@ lib_deps = https://github.com/bblanchon/ArduinoJson https://github.com/PaulStoffregen/OneWire https://github.com/milesburton/Arduino-Temperature-Control-Library + https://github.com/wollewald/INA219_WE # For measuring power consumption [env:gravity-debug] upload_speed = ${common_env_data.upload_speed} diff --git a/src/gyro.cpp b/src/gyro.cpp index 21a0836..62e4bd3 100644 --- a/src/gyro.cpp +++ b/src/gyro.cpp @@ -52,7 +52,7 @@ bool GyroSensor::setup() { Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR)); sensorConnected = true; - // Configure ethe sensor + // Configure the sensor accelgyro.setTempSensorEnabled(true); accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); accelgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_250); @@ -91,6 +91,7 @@ void GyroSensor::readSensor(RawGyroData &raw, const int noIterations, const int #if LOG_LEVEL==6 Log.verbose(F("GYRO: Reading sensor with %d iterations %d us delay." CR), noIterations, delayTime ); #endif + // Set some initial values #if defined( GYRO_SHOW_MINMAX ) RawGyroData min, max; @@ -99,7 +100,6 @@ void GyroSensor::readSensor(RawGyroData &raw, const int noIterations, const int min.temp = accelgyro.getTemperature(); max = min; #endif - for(int cnt = 0; cnt < noIterations ; cnt ++) { accelgyro.getRotation( &raw.gx, &raw.gy, &raw.gz ); accelgyro.getAcceleration( &raw.ax, &raw.ay, &raw.az ); diff --git a/src/helper.cpp b/src/helper.cpp index 9bec992..88b7625 100644 --- a/src/helper.cpp +++ b/src/helper.cpp @@ -26,6 +26,10 @@ SOFTWARE. #include #include +#if defined( COLLECT_PERFDATA ) +#include // For measuring power consumption +#endif + SerialDebug mySerial; BatteryVoltage myBatteryVoltage; @@ -95,27 +99,64 @@ void BatteryVoltage::read() { #endif } - #if defined( COLLECT_PERFDATA ) 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 // void PerfLogging::clear() { + // Clear the measurements if( first == 0 ) return; PerfEntry* pe = first; do { - PerfEntry* p = pe; + pe->max = 0; + pe->start = 0; + pe->end = 0; + pe->mA = 0; + pe->V = 0; pe = pe->next; - delete p; } while( pe != 0 ); - - first = 0; } // @@ -137,8 +178,10 @@ void PerfLogging::stop( const char* key ) { unsigned long t = pe->end - pe->start; - if( t > pe->max ) + if( t > pe->max ) pe->max = t; + + readPowerSensor( pe ); } } @@ -175,11 +218,19 @@ void PerfLogging::pushInflux() { String body; PerfEntry* pe = first; + char buf[100]; + sprintf( &buf[0], "perf,host=%s,device=%s ", myConfig.getMDNS(), myConfig.getID() ); + body += &buf[0]; while( pe != 0 ) { - char buf[100]; - sprintf( &buf[0], "%s,host=%s,device=%s value=%ld\n", pe->key, myConfig.getMDNS(), myConfig.getID(), pe->max); - body += &buf[0]; + if( 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]; + } pe = pe->next; } diff --git a/src/helper.h b/src/helper.h index 08cc0b7..5235c61 100644 --- a/src/helper.h +++ b/src/helper.h @@ -66,10 +66,15 @@ class PerfLogging { unsigned long end; // millis() unsigned long max; // max time in ms const char* key; // measurement - PerfEntry* next; // + + PerfEntry* next; // Next in the linked list + + float mA; // Power consumption + float V; // Power consumption }; PerfEntry* first = 0; + bool measurePower = false; PerfEntry* find( const char* k ) { if( first == 0 ) @@ -112,7 +117,10 @@ class PerfLogging { return pe; }; + void readPowerSensor(PerfEntry* pe); + public: + PerfLogging(); void clear(); void start( const char* key ); void stop( const char* key ); diff --git a/src/main.cpp b/src/main.cpp index a147c75..e9c618e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,7 +35,6 @@ SOFTWARE. #define ESP8266_DRD_USE_RTC true #define DRD_TIMEOUT 2 #define DRD_ADDRESS 0 -#define DOUBLERESETDETECTOR_DEBUG true #include DoubleResetDetector *drd; @@ -47,10 +46,12 @@ bool sleepModeAlwaysSkip = true; // Web interface can override normal const int interval = 200; // ms, time to wait between changes to output bool sleepModeAlwaysSkip = false; // Web interface can override normal behaviour #endif -unsigned long lastMillis = 0; -unsigned long startMillis; -bool sleepModeActive = false; -int loopCounter = 0; +unsigned long loopMillis = 0; // Used for main loop to run the code every _interval_ +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 goToSleep = false; +int loopCounter = 0; // // Check if we should be in sleep mode @@ -78,7 +79,7 @@ void checkSleepMode( float angle, float volt ) { } // 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 //sleepModeActive = ( angle<85 && angle>5 ) ? true : false; @@ -93,7 +94,7 @@ void checkSleepMode( float angle, float volt ) { void setup() { LOG_PERF_START("run-time"); LOG_PERF_START("main-setup"); - startMillis = millis(); + runtimeMillis = millis(); drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS); bool dt = drd->detectDoubleReset(); @@ -118,10 +119,10 @@ void setup() { LOG_PERF_STOP("main-temp-setup"); // 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() ) Log.error(F("Main: Failed to initialize the gyro." CR)); - LOG_PERF_STOP("main-gyro-setup"); + //LOG_PERF_STOP("main-gyro-setup"); if( dt ) Log.notice(F("Main: Detected doubletap on reset." CR)); @@ -148,16 +149,15 @@ void setup() { LOG_PERF_STOP("main-wifi-ota"); #endif if( !sleepModeActive ) { - LOG_PERF_START("main-webserver-setup"); + //LOG_PERF_START("main-webserver-setup"); // Takes less than 4ms , so skip this measurment myWebServer.setupWebServer(); - LOG_PERF_STOP("main-webserver-setup"); + //LOG_PERF_STOP("main-webserver-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)); + stableGyroMillis = millis(); // Put it here so we dont include time for wifi connection } // @@ -166,26 +166,31 @@ void setup() { void loop() { drd->loop(); - if( sleepModeActive || abs(millis() - lastMillis) > interval ) { - float angle = 90; + if( sleepModeActive || abs(millis() - loopMillis) > interval ) { + float angle = 0; float volt = myBatteryVoltage.getVoltage(); float sensorTemp = 0; loopCounter++; #if LOG_LEVEL==6 Log.verbose(F("Main: Entering main loop." CR) ); #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( myGyro.hasValue() ) { angle = myGyro.getAngle(); // Gyro angle sensorTemp = myGyro.getSensorTempC(); // Temp in the Gyro + stableGyroMillis = millis(); // Reset timer + LOG_PERF_START("loop-temp-read"); float temp = myTempSensor.getValueCelcius(); // The code is build around using C for temp. 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 ); - LOG_PERF_STOP("loop-gravity-calc"); + //LOG_PERF_STOP("loop-gravity-calc"); #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 ); @@ -202,49 +207,62 @@ void loop() { // Limit the printout when sleep mode is not active. 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_PERF_PRINT(); } - unsigned long runTime = millis() - startMillis; - 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"); + // If we have completed the update lets go to sleep + if( sleepModeActive ) + goToSleep = true; } else { 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 Log.verbose(F("Main: Sleep mode not active." CR) ); #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) + // checkSleepMode( angle, volt ); LOG_PERF_START("loop-gyro-read"); myGyro.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(); - LOG_PERF_STOP("loop-batt-read"); + //LOG_PERF_STOP("loop-batt-read"); - lastMillis = millis(); + loopMillis = millis(); #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: HeapFrag %d %%." CR), ESP.getHeapFragmentation() ); diff --git a/src/pushtarget.cpp b/src/pushtarget.cpp index 898cf4e..936b3a1 100644 --- a/src/pushtarget.cpp +++ b/src/pushtarget.cpp @@ -23,6 +23,7 @@ SOFTWARE. */ #include "pushtarget.h" #include "config.h" +#include "gyro.h" // For testing the tempsensor in the gyro PushTarget myPushTarget; @@ -86,17 +87,11 @@ void PushTarget::sendInfluxDb2(float angle, float gravity, float temp, float run // Create body for influxdb2 char buf[1024]; - sprintf( &buf[0], "gravity,host=%s,device=%s,format=%s value=%.4f\n" - "angle,host=%s,device=%s value=%.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", + sprintf( &buf[0], "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s " + "gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,rssi=%d,temp2=%.2f\n", // TODO: Add support for plato format - myConfig.getMDNS(), myConfig.getID(), "SG", gravity, - myConfig.getMDNS(), myConfig.getID(), angle, - myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), temp, - myConfig.getMDNS(), myConfig.getID(), myBatteryVoltage.getVoltage(), - myConfig.getMDNS(), myConfig.getID(), WiFi.RSSI() ); + myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG", + gravity, angle, temp, myBatteryVoltage.getVoltage(), WiFi.RSSI(), myGyro.getSensorTempC() ); // For comparing gyro tempsensor vs DSB1820 #if LOG_LEVEL==6 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(); - } // diff --git a/src/webserver.cpp b/src/webserver.cpp index 09e9cfe..d08e561 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -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 SOFTWARE. */ -//#define DEBUG_ESP_HTTP_SERVER #include "webserver.h" #include "config.h" #include "helper.h" @@ -31,7 +30,6 @@ SOFTWARE. #include #include #include -//#define DEBUG_ESP_HTTP_SERVER #include #include #include