diff --git a/html/calibration.htm b/html/calibration.htm index 7a6e1f8..7a8bf2d 100644 --- a/html/calibration.htm +++ b/html/calibration.htm @@ -86,19 +86,20 @@
+
- Here you can create your gravity formula by entering angles/tilt and the corresponding gravity (SG). These values + Here you can create your gravity formula by entering angles/tilt and the corresponding gravity. These values will be saved for future use. Angles with 0 (zero) will be skipped. The values below will be used to check the - formula and if the deviation is more than 1.5 SG then the forumla will be rejected. On the bottom of the page you can - see a graph over the entered values + values calcualated by the formula. + formula and if the deviation is more than 1.5SG / 0.38P on any of the provided points then the forumla will be + rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula.
- +
@@ -107,7 +108,7 @@
- +
@@ -117,7 +118,7 @@
- +
@@ -127,7 +128,7 @@
- +
@@ -137,7 +138,7 @@
- +
@@ -147,7 +148,7 @@
- +
@@ -233,28 +234,40 @@

Here you can create your gravity formula by entering angles/tilt and the corresponding gravity (SG). These values will be saved for future use. Angles with 0 (zero) will be skipped. The values below will be used to check the formula and if the deviation is more than 1.5 SG then the forumla will be rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula.



Here you can create your gravity formula by entering angles/tilt and the corresponding gravity. These values will be saved for future use. Angles with 0 (zero) will be skipped. The values below will be used to check the formula and if the deviation is more than 1.5SG / 0.38P on any of the provided points then the forumla will be rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula.


(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file + var myChart = 0;
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/config.htm b/html/config.htm index 8f8b3e9..1785ee9 100644 --- a/html/config.htm +++ b/html/config.htm @@ -270,8 +270,25 @@
+
+ Gravity Format: +
+
+ + +
+
+ + +
+
+
- +
@@ -416,10 +433,10 @@ $("#id3").val(cfg["id"]); $("#id4").val(cfg["id"]); $("#mdns").val(cfg["mdns"]); - if( cfg["temp-format"] == "C" ) - $("#temp-format-c").click(); - else - $("#temp-format-f").click(); + if( cfg["temp-format"] == "C" ) $("#temp-format-c").click(); + else $("#temp-format-f").click(); + if( cfg["gravity-format"] == "G" ) $("#gravity-format-g").click(); + else $("#gravity-format-p").click(); $("#ota-url").val(cfg["ota-url"]); $("#http-push").val(cfg["http-push"]); $("#http-push2").val(cfg["http-push2"]); diff --git a/html/config.min.htm b/html/config.min.htm index 39418a1..5063a45 100644 --- a/html/config.min.htm +++ b/html/config.min.htm @@ -1 +1 @@ -Beer Gravity Monitor

Temperature Format:





(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file +Beer Gravity Monitor

Temperature Format:




Gravity Format:


(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/index.htm b/html/index.htm index 052cb1e..66c5219 100644 --- a/html/index.htm +++ b/html/index.htm @@ -123,13 +123,19 @@ console.log( cfg ); $("#id").text(cfg["id"]); $("#angle").text(cfg["angle"]); - $("#gravity").text(cfg["gravity"] + " SG"); + + if( cfg["gravity-format"] == "G") + $("#gravity").text(cfg["gravity"] + " SG"); + else + $("#gravity").text(cfg["gravity"] + " °P"); + $("#battery").text(cfg["battery"] + " V"); + if( cfg["temp-format"] == "C") $("#temp").text(cfg["temp-c"] + " C"); else $("#temp").text(cfg["temp-f"] + " F"); - //console.log(cfg["sleep-mode"] ); + if( cfg["sleep-mode"] ) $("#sleep-mode").attr("checked", true ); else diff --git a/html/index.min.htm b/html/index.min.htm index 1822cec..9104210 100644 --- a/html/index.min.htm +++ b/html/index.min.htm @@ -1 +1 @@ -Beer Gravity Monitor

Gravity:
Loading...
Temperature:
Loading...
Angle/Tilt:
Loading...
Battery:
Loading...

(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file +Beer Gravity Monitor

Gravity:
Loading...
Temperature:
Loading...
Angle/Tilt:
Loading...
Battery:
Loading...

(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 2185184..1c6e1da 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,19 +27,14 @@ build_flags = #-D DEBUG_ESP_HTTP_SERVER #-D DEBUG_ESP_PORT=Serial #-D DEBUG_ESP_WIFI + #-D DEBUG_ESP_SSL #-D DEBUG_ESP_CORE #-D SKIP_SLEEPMODE - -D CFG_DISABLE_LOGGING # Turn off verbose logging for some of the parts (too much will cause a crash) but also add space - -D GYRO_DISABLE_LOGGING - -D PUSH_DISABLE_LOGGING - -D TSEN_DISABLE_LOGGING - -D WEB_DISABLE_LOGGING - -D MAIN_DISABLE_LOGGING -D USE_LITTLEFS=true -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.6.0\"" + -D CFG_APPVER="\"0.6.1\"" lib_deps = # Switched to forks for better version control. # Using local copy of this library #https://github.com/jrowberg/i2cdevlib.git# @@ -65,20 +60,26 @@ extra_scripts = script/create_versionjson.py build_unflags = ${common_env_data.build_unflags} -# -D MAIN_DISABLE_LOGGING - -D WEB_DISABLE_LOGGING - -D PUSH_DISABLE_LOGGING build_flags = ${common_env_data.build_flags} - -D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS + #-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS #-D SKIP_SLEEPMODE - -D DOUBLERESETDETECTOR_DEBUG=true - -D COLLECT_PERFDATA # This option will collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb - -D LOG_LEVEL=6 # Maximum log level for the debug build. + #-D DOUBLERESETDETECTOR_DEBUG=true + -D COLLECT_PERFDATA # This option will collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb + -D LOG_LEVEL=6 # Maximum log level for the debug build. + -D CFG_DISABLE_LOGGING # Turn off verbose/notice logging to reduce size and dont overload uart. + -D GYRO_DISABLE_LOGGING + -D CALC_DISABLE_LOGGING + -D HELPER_DISABLE_LOGGING + -D PUSH_DISABLE_LOGGING + -D TSEN_DISABLE_LOGGING + -D WIFI_DISABLE_LOGGING + -D WEB_DISABLE_LOGGING + -D MAIN_DISABLE_LOGGING lib_deps = ${common_env_data.lib_deps} board = ${common_env_data.board} -#build_type = debug # Using debug type crashes my devkit... +#build_type = debug build_type = release board_build.filesystem = littlefs monitor_filters = esp8266_exception_decoder diff --git a/src/calc.cpp b/src/calc.cpp index e14ced4..15118a6 100644 --- a/src/calc.cpp +++ b/src/calc.cpp @@ -29,7 +29,7 @@ SOFTWARE. #include #include -#define FORMULA_MAX_DEVIATION 1.5 +#define FORMULA_MAX_DEVIATION 1.6 // // Use values to derive a formula @@ -46,7 +46,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer, else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0) noAngles = 3; -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) Log.verbose( F("CALC: Trying to create formula using order = %d, found %d angles" CR), order, noAngles); @@ -62,7 +62,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer, // Returned value is 0 if no error if (ret == 0) { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) Log.verbose(F("CALC: Finshied processing data points." CR)); #endif @@ -83,7 +83,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer, coeffs[1]); } -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) Log.verbose(F("CALC: Formula: %s" CR), formulaBuffer); #endif @@ -96,7 +96,14 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer, double dev = (g - fd.g[i]) < 0 ? (fd.g[i] - g) : (g - fd.g[i]); // If the deviation is more than 2 degress we mark it as failed. - if (dev * 1000 > FORMULA_MAX_DEVIATION) valid = false; + if (dev * 1000 > FORMULA_MAX_DEVIATION) { +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) + char s[10]; + snprintf(&s[0], sizeof(s), "%.8f", dev); + Log.verbose(F("CALC: Deviation is: %s" CR), &s[0]); +#endif + valid = false; + } } if (!valid) { @@ -121,13 +128,13 @@ double calculateGravity(double angle, double temp, const char *tempFormula) { const char *formula = myConfig.getGravityFormula(); if (tempFormula != 0) { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) Log.verbose(F("CALC: Using temporary formula." CR)); #endif formula = tempFormula; } -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) Log.verbose(F("CALC: Calculating gravity for angle %F, temp %F." CR), angle, temp); Log.verbose(F("CALC: Formula %s." CR), formula); @@ -146,8 +153,10 @@ double calculateGravity(double angle, double temp, const char *tempFormula) { double g = te_eval(expr); te_free(expr); -#if LOG_LEVEL == 6 - Log.verbose(F("CALC: Calculated gravity is %F." CR), g); +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) + char s[10]; + snprintf(&s[0], sizeof(s), "%.8f", g); + Log.verbose(F("CALC: Calculated gravity is %s." CR), &s[0]); #endif return g; } @@ -158,18 +167,20 @@ double calculateGravity(double angle, double temp, const char *tempFormula) { // // Do a standard gravity temperature correction. This is a simple way to adjust -// for differnt worth temperatures +// for differnt worth temperatures. This function uses C as temperature. // -double gravityTemperatureCorrection(double gravity, double temp, - char tempFormat, double calTemp) { -#if LOG_LEVEL == 6 +// Source: https://homebrewacademy.com/hydrometer-temperature-correction/ +// +double gravityTemperatureCorrectionC(double gravity, double tempC, + double calTempC) { +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, " "temp %F, calTemp %F." CR), - gravity, temp, calTemp); + gravity, tempC, calTempC); #endif + // float tempF = convertCtoF(tempC); + // float calTempF = convertCtoF(calTempC); - if (tempFormat == 'C') temp = convertCtoF(temp); - double calTempF = convertCtoF(calTemp); // calTemp is in C const char *formula = "gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0." "00000000232820948*temp^3)/" @@ -178,7 +189,7 @@ double gravityTemperatureCorrection(double gravity, double temp, // Store variable names and pointers. te_variable vars[] = { - {"gravity", &gravity}, {"temp", &temp}, {"cal", &calTempF}}; + {"gravity", &gravity}, {"temp", &tempC}, {"cal", &calTempC}}; int err; // Compile the expression with variables. @@ -188,8 +199,10 @@ double gravityTemperatureCorrection(double gravity, double temp, double g = te_eval(expr); te_free(expr); -#if LOG_LEVEL == 6 - Log.verbose(F("CALC: Corrected gravity is %F." CR), g); +#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) + char s[10]; + snprintf(&s[0], sizeof(s), "%.8f", g); + Log.verbose(F("CALC: Corrected gravity is %s." CR), &s[0]); #endif return g; } diff --git a/src/calc.hpp b/src/calc.hpp index a6ea40d..854d39d 100644 --- a/src/calc.hpp +++ b/src/calc.hpp @@ -26,16 +26,16 @@ SOFTWARE. // Includes #include -#include #define ERR_FORMULA_NOTENOUGHVALUES -1 #define ERR_FORMULA_INTERNAL -2 #define ERR_FORMULA_UNABLETOFFIND -3 // Functions -double calculateGravity(double angle, double temp, const char *tempFormula = 0); -double gravityTemperatureCorrection(double gravity, double temp, - char tempFormat, double calTemp = 20); +double calculateGravity(double angle, double tempC, + const char *tempFormula = 0); +double gravityTemperatureCorrectionC(double gravity, double tempC, + double calTempC = 20); int createFormula(RawFormulaData &fd, char *formulaBuffer, int formulaBufferSize, int order); diff --git a/src/config.cpp b/src/config.cpp index 323efd9..545d8c3 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -48,7 +48,7 @@ Config::Config() { setGravityFormat('G'); setSleepInterval(900); // 15 minutes setVoltageFactor(1.59); // Conversion factor for battery - setTempSensorAdj(0.0); + setTempSensorAdjC(0.0); setGravityTempAdj(false); gyroCalibration = {0, 0, 0, 0, 0, 0}; formulaData = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}}; @@ -82,7 +82,7 @@ void Config::createJson(DynamicJsonDocument& doc) { doc[CFG_PARAM_VOLTAGEFACTOR] = getVoltageFactor(); doc[CFG_PARAM_GRAVITY_FORMULA] = getGravityFormula(); doc[CFG_PARAM_GRAVITY_FORMAT] = String(getGravityFormat()); - doc[CFG_PARAM_TEMP_ADJ] = getTempSensorAdj(); + doc[CFG_PARAM_TEMP_ADJ] = getTempSensorAdjC(); doc[CFG_PARAM_GRAVITY_TEMP_ADJ] = isGravityTempAdj(); doc[CFG_PARAM_GYRO_TEMP] = isGyroTemp(); @@ -239,7 +239,7 @@ bool Config::loadFile() { setGravityFormat(s.charAt(0)); } if (!doc[CFG_PARAM_TEMP_ADJ].isNull()) - setTempSensorAdj(doc[CFG_PARAM_TEMP_ADJ].as()); + setTempSensorAdjC(doc[CFG_PARAM_TEMP_ADJ].as()); if (!doc[CFG_PARAM_GYRO_CALIBRATION]["ax"].isNull()) gyroCalibration.ax = doc[CFG_PARAM_GYRO_CALIBRATION]["ax"]; @@ -318,7 +318,7 @@ void Config::debug() { Log.verbose(F("CFG : Sleep interval; %d." CR), getSleepInterval()); Log.verbose(F("CFG : OTA; '%s'." CR), getOtaURL()); Log.verbose(F("CFG : Temp Format; %c." CR), getTempFormat()); - Log.verbose(F("CFG : Temp Adj; %F." CR), getTempSensorAdj()); + Log.verbose(F("CFG : Temp Adj; %F." CR), getTempSensorAdjC()); Log.verbose(F("CFG : VoltageFactor; %F." CR), getVoltageFactor()); Log.verbose(F("CFG : Gravity formula; '%s'." CR), getGravityFormula()); Log.verbose(F("CFG : Gravity format; '%c'." CR), getGravityFormat()); diff --git a/src/config.hpp b/src/config.hpp index 9b85505..a00d90d 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -89,6 +89,7 @@ SOFTWARE. #define CFG_PARAM_BATTERY "battery" #define CFG_PARAM_SLEEP_MODE "sleep-mode" #define CFG_PARAM_RSSI "rssi" +#define CFG_PARAM_ERROR "error" // Used for holding sensordata or sensoroffsets struct RawGyroData { @@ -118,41 +119,40 @@ class Config { String id; String mDNS; String otaURL; - char tempFormat; // C, F + char tempFormat; float voltageFactor; - float tempSensorAdj; // This value will be added to the read sensor value + float tempSensorAdjC; int sleepInterval; - bool gyroTemp; // Experimental feature + bool gyroTemp; // Wifi Config String wifiSSID; String wifiPASS; // Push target settings - String brewfatherPushUrl; // URL For brewfather + String brewfatherPushUrl; - String httpPushUrl; // URL 1 for standard http - String httpPushUrl2; // URL 2 for standard http + String httpPushUrl; + String httpPushUrl2; - String influxDb2Url; // URL for InfluxDB v2 - String influxDb2Org; // Organisation for InfluxDB v2 - String influxDb2Bucket; // Bucket for InfluxDB v2 - String influxDb2Token; // Auth Token for InfluxDB v2 + String influxDb2Url; + String influxDb2Org; + String influxDb2Bucket; + String influxDb2Token; - String mqttUrl; // Server name + String mqttUrl; String mqttTopic; String mqttUser; String mqttPass; // Gravity and temperature calculations String gravityFormula; - bool gravityTempAdj; // true, false - char gravityFormat; // G, P + bool gravityTempAdj; + char gravityFormat; - // Gyro calibration data - RawGyroData - gyroCalibration; // Holds the gyro calibration constants (6 * int16_t) - RawFormulaData formulaData; // Used for creating formula + // Gyro calibration and formula calculation data + RawGyroData gyroCalibration; + RawFormulaData formulaData; void debug(); void formatFileSystem(); @@ -273,11 +273,13 @@ class Config { char getTempFormat() { return tempFormat; } void setTempFormat(char c) { - tempFormat = c; - saveNeeded = true; + if (c == 'C' || c == 'F') { + tempFormat = c; + saveNeeded = true; + } } - bool isTempC() { return tempFormat == 'C' ? false : true; } - bool isTempF() { return tempFormat == 'F' ? false : true; } + bool isTempC() { return tempFormat == 'C'; } + bool isTempF() { return tempFormat == 'F'; } float getVoltageFactor() { return voltageFactor; } void setVoltageFactor(float f) { @@ -289,13 +291,17 @@ class Config { saveNeeded = true; } - float getTempSensorAdj() { return tempSensorAdj; } - void setTempSensorAdj(float f) { - tempSensorAdj = f; + float getTempSensorAdjC() { return tempSensorAdjC; } + void setTempSensorAdjC(float f) { + tempSensorAdjC = f; saveNeeded = true; } - void setTempSensorAdj(String s) { - tempSensorAdj = s.toFloat(); + void setTempSensorAdjC(String s) { + tempSensorAdjC = s.toFloat(); + saveNeeded = true; + } + void setTempSensorAdjF(String s) { + tempSensorAdjC = convertFtoC(s.toFloat()); saveNeeded = true; } @@ -313,11 +319,13 @@ class Config { char getGravityFormat() { return gravityFormat; } void setGravityFormat(char c) { - gravityFormat = c; - saveNeeded = true; + if (c == 'G' || c == 'P') { + gravityFormat = c; + saveNeeded = true; + } } - bool isGravitySG() { return gravityFormat == 'G' ? false : true; } - bool isGravityPlato() { return gravityFormat == 'P' ? false : true; } + bool isGravitySG() { return gravityFormat == 'G'; } + bool isGravityPlato() { return gravityFormat == 'P'; } const RawGyroData& getGyroCalibration() { return gyroCalibration; } void setGyroCalibration(const RawGyroData& r) { diff --git a/src/helper.cpp b/src/helper.cpp index 9785334..75654b5 100644 --- a/src/helper.cpp +++ b/src/helper.cpp @@ -24,19 +24,39 @@ SOFTWARE. #include #include -#include #include #include #include +#include SerialDebug mySerial; BatteryVoltage myBatteryVoltage; +// +// Convert sg to plato +// +double convertToPlato(double sg) { return 259 - (259 / sg); } + +// +// Convert plato to sg +// +double convertToSG(double plato) { return 259 / (259 - plato); } + +// +// Conversion to F +// +float convertCtoF(float c) { return (c * 1.8) + 32.0; } + +// +// Conversion to C +// +float convertFtoC(float f) { return (f - 32.0) / 1.8; } + // // Print the heap information. // void printHeap() { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING) Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR), ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(), ESP.getFreeSketchSpace() / 1024); @@ -47,7 +67,7 @@ void printHeap() { // Enter deep sleep for the defined duration (Argument is seconds) // void deepSleep(int t) { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING) Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t); #endif uint32_t wake = t * 1000000; @@ -106,7 +126,7 @@ void BatteryVoltage::read() { float factor = myConfig.getVoltageFactor(); // Default value is 1.63 int v = analogRead(A0); batteryLevel = ((3.3 / 1023) * v) * factor; -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING) Log.verbose( F("BATT: Reading voltage level. Factor=%F Value=%d, Voltage=%F." CR), factor, v, batteryLevel); @@ -177,14 +197,13 @@ void PerfLogging::print() { void PerfLogging::pushInflux() { if (!myConfig.isInfluxDb2Active()) return; - WiFiClient client; HTTPClient http; String serverPath = String(myConfig.getInfluxDb2PushUrl()) + "/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) + "&bucket=" + String(myConfig.getInfluxDb2PushBucket()); - http.begin(client, serverPath); + http.begin(myWifi.getWifiClient(), serverPath); // Create body for influxdb2, format used // key,host=mdns value=0.0 @@ -225,7 +244,7 @@ void PerfLogging::pushInflux() { // Log.notice(F("PERF: data %s." CR), body.c_str() ); -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING) Log.verbose(F("PERF: url %s." CR), serverPath.c_str()); Log.verbose(F("PERF: data %s." CR), body.c_str()); #endif @@ -236,15 +255,18 @@ void PerfLogging::pushInflux() { int httpResponseCode = http.POST(body); if (httpResponseCode == 204) { +#if !defined(HELPER_DISABLE_LOGGING) Log.notice( F("PERF: InfluxDB2 push performance data successful, response=%d" CR), httpResponseCode); +#endif } else { Log.error(F("PERF: InfluxDB2 push performance data failed, response=%d" CR), httpResponseCode); } http.end(); + myWifi.closeWifiClient(); } #endif // COLLECT_PERFDATA diff --git a/src/helper.hpp b/src/helper.hpp index 072d8f1..755f484 100644 --- a/src/helper.hpp +++ b/src/helper.hpp @@ -33,6 +33,12 @@ void deepSleep(int t); // Show build options void printBuildOptions(); +// Data conversion +double convertToPlato(double sg); +double convertToSG(double plato); +float convertCtoF(float c); +float convertFtoC(float f); + // Float to String char* convertFloatToString(float f, char* buf, int dec = 2); float reduceFloatPrecision(float f, int dec = 2); diff --git a/src/main.cpp b/src/main.cpp index 1872ae9..34fba25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -63,14 +63,18 @@ void checkSleepMode(float angle, float volt) { if (!g.ax && !g.ay && !g.az && !g.gx && !g.gy && !g.gz) { // Will not enter sleep mode if: no calibration data +#if !defined(MAIN_DISABLE_LOGGING) Log.notice( F("MAIN: Missing calibration data, so forcing webserver to be " "active." CR)); +#endif runMode = RunMode::configurationMode; } else if (sleepModeAlwaysSkip) { // Check if the flag from the UI has been set, the we force configuration // mode. +#if !defined(MAIN_DISABLE_LOGGING) Log.notice(F("MAIN: Sleep mode disabled from web interface." CR)); +#endif runMode = RunMode::configurationMode; } else if ((volt < 4.15 && (angle > 85 && angle < 95)) || (volt > 4.15)) { runMode = RunMode::configurationMode; @@ -80,14 +84,18 @@ void checkSleepMode(float angle, float volt) { switch (runMode) { case RunMode::configurationMode: +#if !defined(MAIN_DISABLE_LOGGING) Log.notice(F("MAIN: run mode CONFIG (angle=%F volt=%F)." CR), angle, volt); +#endif break; case RunMode::wifiSetupMode: break; case RunMode::gravityMode: +#if !defined(MAIN_DISABLE_LOGGING) Log.notice(F("MAIN: run mode GRAVITY (angle=%F volt=%F)." CR), angle, volt); +#endif break; } } @@ -207,22 +215,21 @@ bool loopReadGravity() { stableGyroMillis = millis(); // Reset timer LOG_PERF_START("loop-temp-read"); - float temp = myTempSensor.getTempC(myConfig.isGyroTemp()); + float tempC = myTempSensor.getTempC(myConfig.isGyroTemp()); LOG_PERF_STOP("loop-temp-read"); - float gravity = calculateGravity(angle, temp); - float corrGravity = - gravityTemperatureCorrection(gravity, temp, myConfig.getTempFormat()); + float gravity = calculateGravity(angle, tempC); + float corrGravity = gravityTemperatureCorrectionC(gravity, tempC); #if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING) - Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%F, gravity=%F, " - "corr=%F." CR), - angle, temp, gravity, corrGravity); + Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%FC, gravity=%F, " + "corr_gravity=%F." CR), + angle, tempC, gravity, corrGravity); #endif LOG_PERF_START("loop-push"); // Force the transmission if we are going to sleep - myPushTarget.send(angle, gravity, corrGravity, temp, + myPushTarget.send(angle, gravity, corrGravity, tempC, (millis() - runtimeMillis) / 1000, runMode == RunMode::gravityMode ? true : false); LOG_PERF_STOP("loop-push"); diff --git a/src/pushtarget.cpp b/src/pushtarget.cpp index f57abc5..2baa505 100644 --- a/src/pushtarget.cpp +++ b/src/pushtarget.cpp @@ -21,19 +21,21 @@ 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. */ +#include #include #include #include #include +#include PushTarget myPushTarget; // -// Send the pressure value +// Send the data to targets // -void PushTarget::send(float angle, float gravity, float corrGravity, float temp, - float runTime, bool force) { +void PushTarget::send(float angle, float gravity, float corrGravity, + float tempC, float runTime, bool force) { uint32_t timePassed = abs((int32_t)(millis() - ms)); uint32_t interval = myConfig.getSleepInterval() * 1000; @@ -45,41 +47,37 @@ void PushTarget::send(float angle, float gravity, float corrGravity, float temp, return; } -#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) - Log.verbose(F("PUSH: Sending data." CR)); -#endif - ms = millis(); if (myConfig.isBrewfatherActive()) { LOG_PERF_START("push-brewfather"); - sendBrewfather(angle, gravity, corrGravity, temp); + sendBrewfather(angle, gravity, corrGravity, tempC); LOG_PERF_STOP("push-brewfather"); } if (myConfig.isHttpActive()) { LOG_PERF_START("push-http"); - sendHttp(myConfig.getHttpPushUrl(), angle, gravity, corrGravity, temp, + sendHttp(myConfig.getHttpPushUrl(), angle, gravity, corrGravity, tempC, runTime); LOG_PERF_STOP("push-http"); } if (myConfig.isHttpActive2()) { LOG_PERF_START("push-http2"); - sendHttp(myConfig.getHttpPushUrl2(), angle, gravity, corrGravity, temp, + sendHttp(myConfig.getHttpPushUrl2(), angle, gravity, corrGravity, tempC, runTime); LOG_PERF_STOP("push-http2"); } if (myConfig.isInfluxDb2Active()) { LOG_PERF_START("push-influxdb2"); - sendInfluxDb2(angle, gravity, corrGravity, temp, runTime); + sendInfluxDb2(angle, gravity, corrGravity, tempC, runTime); LOG_PERF_STOP("push-influxdb2"); } if (myConfig.isMqttActive()) { LOG_PERF_START("push-mqtt"); - sendMqtt(angle, gravity, corrGravity, temp, runTime); + sendMqtt(angle, gravity, corrGravity, tempC, runTime); LOG_PERF_STOP("push-mqtt"); } } @@ -88,32 +86,43 @@ void PushTarget::send(float angle, float gravity, float corrGravity, float temp, // Send to influx db v2 // void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity, - float temp, float runTime) { + float tempC, float runTime) { #if !defined(PUSH_DISABLE_LOGGING) Log.notice( F("PUSH: Sending values to influxdb2 angle=%F, gravity=%F, temp=%F." CR), - angle, gravity, temp); + angle, gravity, tempC); #endif - WiFiClient client; HTTPClient http; String serverPath = String(myConfig.getInfluxDb2PushUrl()) + "/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) + "&bucket=" + String(myConfig.getInfluxDb2PushBucket()); - http.begin(client, serverPath); + http.begin(myWifi.getWifiClient(), serverPath); + + float temp = myConfig.isTempC() ? tempC : convertCtoF(tempC); + gravity = myConfig.isGravityTempAdj() ? corrGravity : gravity; // Create body for influxdb2 char buf[1024]; - snprintf(&buf[0], sizeof(buf), - "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s " - "gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f," - "rssi=%d\n", - // TODO: Add support for plato format - myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG", - myConfig.isGravityTempAdj() ? corrGravity : gravity, corrGravity, - angle, temp, myBatteryVoltage.getVoltage(), WiFi.RSSI()); + if (myConfig.isGravitySG()) { + snprintf(&buf[0], sizeof(buf), + "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s " + "gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f," + "rssi=%d\n", + myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), + "G", gravity, corrGravity, angle, temp, + myBatteryVoltage.getVoltage(), WiFi.RSSI()); + } else { + snprintf(&buf[0], sizeof(buf), + "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s " + "gravity=%.1f,corr-gravity=%.1f,angle=%.2f,temp=%.2f,battery=%.2f," + "rssi=%d\n", + myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), + "G", convertToPlato(gravity), convertToPlato(corrGravity), angle, + convertCtoF(temp), myBatteryVoltage.getVoltage(), WiFi.RSSI()); + } #if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) Log.verbose(F("PUSH: url %s." CR), serverPath.c_str()); @@ -134,17 +143,18 @@ void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity, } http.end(); + myWifi.closeWifiClient(); } // // Send data to brewfather // void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity, - float temp) { + float tempC) { #if !defined(PUSH_DISABLE_LOGGING) - Log.notice( - F("PUSH: Sending values to brewfather angle=%F, gravity=%F, temp=%F." CR), - angle, gravity, temp); + Log.notice(F("PUSH: Sending values to brewfather angle=%F, gravity=%F, " + "corr-gravity=%F, temp=%F." CR), + angle, gravity, corrGravity, tempC); #endif DynamicJsonDocument doc(300); @@ -165,21 +175,26 @@ void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity, // "battery": 4.98 // } // + float temp = myConfig.isTempC() ? tempC : convertCtoF(tempC); + doc["name"] = myConfig.getMDNS(); doc["temp"] = reduceFloatPrecision(temp, 1); doc["temp_unit"] = String(myConfig.getTempFormat()); doc["battery"] = reduceFloatPrecision(myBatteryVoltage.getVoltage(), 2); - // TODO: Add support for plato format - doc["gravity"] = reduceFloatPrecision( - myConfig.isGravityTempAdj() ? corrGravity : gravity, 4); + if (myConfig.isGravitySG()) { + doc["gravity"] = reduceFloatPrecision( + myConfig.isGravityTempAdj() ? corrGravity : gravity, 4); + } else { + doc["gravity"] = reduceFloatPrecision( + convertToPlato(myConfig.isGravityTempAdj() ? corrGravity : gravity), 1); + } doc["gravity_unit"] = myConfig.isGravitySG() ? "G" : "P"; - WiFiClient client; HTTPClient http; String serverPath = myConfig.getBrewfatherPushUrl(); // Your Domain name with URL path or IP address with path - http.begin(client, serverPath); + http.begin(myWifi.getWifiClient(), serverPath); String json; serializeJson(doc, json); #if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) @@ -200,6 +215,7 @@ void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity, } http.end(); + myWifi.closeWifiClient(); } // @@ -207,24 +223,31 @@ void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity, // void PushTarget::createIspindleFormat(DynamicJsonDocument &doc, float angle, float gravity, float corrGravity, - float temp, float runTime) { + float tempC, float runTime) { + float temp = myConfig.isTempC() ? tempC : convertCtoF(tempC); + // Use iSpindle format for compatibility doc["name"] = myConfig.getMDNS(); doc["ID"] = myConfig.getID(); - doc["token"] = "gravmon"; + doc["token"] = "gravitmon"; doc["interval"] = myConfig.getSleepInterval(); doc["temperature"] = reduceFloatPrecision(temp, 1); doc["temp-units"] = String(myConfig.getTempFormat()); - // TODO: Add support for plato format - doc["gravity"] = reduceFloatPrecision( - myConfig.isGravityTempAdj() ? corrGravity : gravity, 4); - doc["corr-gravity"] = reduceFloatPrecision(corrGravity, 4); + if (myConfig.isGravitySG()) { + doc["gravity"] = reduceFloatPrecision( + myConfig.isGravityTempAdj() ? corrGravity : gravity, 4); + doc["corr-gravity"] = reduceFloatPrecision(corrGravity, 4); + } else { + doc["gravity"] = reduceFloatPrecision( + convertToPlato(myConfig.isGravityTempAdj() ? corrGravity : gravity), 1); + doc["corr-gravity"] = reduceFloatPrecision(convertToPlato(corrGravity), 1); + } doc["angle"] = reduceFloatPrecision(angle, 2); doc["battery"] = reduceFloatPrecision(myBatteryVoltage.getVoltage(), 2); doc["rssi"] = WiFi.RSSI(); // Some additional information - doc["gravity-units"] = "SG"; + doc["gravity-unit"] = myConfig.isGravitySG() ? "G" : "P"; doc["run-time"] = reduceFloatPrecision(runTime, 2); } @@ -232,21 +255,26 @@ void PushTarget::createIspindleFormat(DynamicJsonDocument &doc, float angle, // Send data to http target // void PushTarget::sendHttp(String serverPath, float angle, float gravity, - float corrGravity, float temp, float runTime) { + float corrGravity, float tempC, float runTime) { #if !defined(PUSH_DISABLE_LOGGING) - Log.notice( - F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR), - angle, gravity, temp); + Log.notice(F("PUSH: Sending values to http angle=%F, gravity=%F, " + "corr-gravity=%F, temp=%F." CR), + angle, gravity, corrGravity, tempC); #endif DynamicJsonDocument doc(256); - createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime); + createIspindleFormat(doc, angle, gravity, corrGravity, tempC, runTime); - WiFiClient client; HTTPClient http; - // Your Domain name with URL path or IP address with path - http.begin(client, serverPath); + if (serverPath.startsWith("https://")) { + myWifi.getWifiClientSecure().setInsecure(); + Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR)); + http.begin(myWifi.getWifiClientSecure(), serverPath); + } else { + http.begin(myWifi.getWifiClient(), serverPath); + } + String json; serializeJson(doc, json); #if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) @@ -266,26 +294,36 @@ void PushTarget::sendHttp(String serverPath, float angle, float gravity, } http.end(); + myWifi.closeWifiClient(); } // // Send data to http target // void PushTarget::sendMqtt(float angle, float gravity, float corrGravity, - float temp, float runTime) { + float tempC, float runTime) { #if !defined(PUSH_DISABLE_LOGGING) - Log.notice( - F("PUSH: Sending values to mqtt angle=%F, gravity=%F, temp=%F." CR), - angle, gravity, temp); + Log.notice(F("PUSH: Sending values to mqtt angle=%F, gravity=%F, " + "corr-gravity=%F, temp=%F." CR), + angle, gravity, corrGravity, tempC); #endif DynamicJsonDocument doc(256); - createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime); + createIspindleFormat(doc, angle, gravity, corrGravity, tempC, runTime); - WiFiClient client; MQTTClient mqtt(512); // Maximum message size + String url = myConfig.getMqttUrl(); + + if (url.endsWith(":8883")) { + // Allow secure channel, but without certificate validation + myWifi.getWifiClientSecure().setInsecure(); + Log.notice(F("PUSH: MQTT, SSL enabled without validation." CR)); + url.replace(":8883", ""); + mqtt.begin(url.c_str(), 8883, myWifi.getWifiClientSecure()); + } else { + mqtt.begin(myConfig.getMqttUrl(), myWifi.getWifiClient()); + } - mqtt.begin(myConfig.getMqttUrl(), client); mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(), myConfig.getMqttPass()); @@ -306,6 +344,7 @@ void PushTarget::sendMqtt(float angle, float gravity, float corrGravity, } mqtt.disconnect(); + myWifi.closeWifiClient(); } // EOF diff --git a/src/pushtarget.hpp b/src/pushtarget.hpp index 330fc8c..56c5389 100644 --- a/src/pushtarget.hpp +++ b/src/pushtarget.hpp @@ -27,8 +27,6 @@ SOFTWARE. // Includes #include #include -#include -#include #include @@ -38,15 +36,15 @@ class PushTarget { uint32_t ms; // Used to check that we do not post to often void sendBrewfather(float angle, float gravity, float corrGravity, - float temp); + float tempC); void sendHttp(String serverPath, float angle, float gravity, - float corrGravity, float temp, float runTime); - void sendInfluxDb2(float angle, float gravity, float corrGravity, float temp, + float corrGravity, float tempC, float runTime); + void sendInfluxDb2(float angle, float gravity, float corrGravity, float tempC, float runTime); - void sendMqtt(float angle, float gravity, float corrGravity, float temp, + void sendMqtt(float angle, float gravity, float corrGravity, float tempC, float runTime); void createIspindleFormat(DynamicJsonDocument &doc, float angle, - float gravity, float corrGravity, float temp, + float gravity, float corrGravity, float tempC, float runTime); public: diff --git a/src/tempsensor.cpp b/src/tempsensor.cpp index d8f722a..54af5b7 100644 --- a/src/tempsensor.cpp +++ b/src/tempsensor.cpp @@ -30,11 +30,6 @@ SOFTWARE. #include #include -// -// Conversion between C and F -// -float convertCtoF(float t) { return (t * 1.8) + 32.0; } - OneWire myOneWire(D6); DallasTemperature mySensors(&myOneWire); #define TEMPERATURE_PRECISION 9 @@ -63,20 +58,12 @@ void TempSensor::setup() { mySensors.setResolution(TEMPERATURE_PRECISION); } - float t = myConfig.getTempSensorAdj(); - // Set the temp sensor adjustment values - if (myConfig.isTempC()) { - tempSensorAdjF = t * 1.8; // Convert the adjustment value to C - tempSensorAdjC = t; - } else { - tempSensorAdjF = t; - tempSensorAdjC = t * 0.556; // Convert the adjustent value to F - } + tempSensorAdjC = myConfig.getTempSensorAdjC(); #if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING) - Log.verbose(F("TSEN: Adjustment values for temp sensor %F C, %F F." CR), - tempSensorAdjC, tempSensorAdjF); + Log.verbose(F("TSEN: Adjustment values for temp sensor %F C." CR), + tempSensorAdjC); #endif } @@ -101,7 +88,9 @@ float TempSensor::getValue(bool useGyro) { // If we dont have sensors just return 0 if (!mySensors.getDS18Count()) { - Log.error(F("TSEN: No temperature sensors found. Skipping read." CR)); +#if !defined(TSEN_DISABLE_LOGGING) + Log.notice(F("TSEN: No temperature sensors found. Skipping read." CR)); +#endif return -273; } diff --git a/src/tempsensor.hpp b/src/tempsensor.hpp index 112d2af..16ccc23 100644 --- a/src/tempsensor.hpp +++ b/src/tempsensor.hpp @@ -25,13 +25,11 @@ SOFTWARE. #define SRC_TEMPSENSOR_HPP_ // definitions -float convertCtoF(float t); // classes class TempSensor { private: bool hasSensor = false; - float tempSensorAdjF = 0; float tempSensorAdjC = 0; float getValue(bool useGyro); @@ -41,9 +39,6 @@ class TempSensor { float getTempC(bool useGyro = false) { return getValue(useGyro) + tempSensorAdjC; } - float getTempF(bool useGyro = false) { - return convertCtoF(getValue(useGyro)) + tempSensorAdjF; - } }; // Global instance created diff --git a/src/webserver.cpp b/src/webserver.cpp index dd60bb6..d5f8171 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -72,16 +72,23 @@ void WebServer::webHandleConfig() { doc[CFG_PARAM_PASS] = ""; // dont show the wifi password double angle = myGyro.getAngle(); - double temp = myTempSensor.getTempC(myConfig.isGyroTemp()); - double gravity = calculateGravity(angle, temp); + double tempC = myTempSensor.getTempC(myConfig.isGyroTemp()); + double gravity = calculateGravity(angle, tempC); doc[CFG_PARAM_ANGLE] = reduceFloatPrecision(angle); - if (myConfig.isGravityTempAdj()) - doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision( - gravityTemperatureCorrection(gravity, temp, myConfig.getTempFormat()), - 4); - else + doc[CFG_PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat()); + + if (myConfig.isGravityTempAdj()) { + gravity = + gravityTemperatureCorrectionC(gravity, tempC, myConfig.getTempFormat()); + } + + if (myConfig.isGravityPlato()) { + doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1); + } else { doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(gravity, 4); + } + doc[CFG_PARAM_BATTERY] = reduceFloatPrecision(myBatteryVoltage.getVoltage()); #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) @@ -215,22 +222,24 @@ void WebServer::webHandleStatus() { DynamicJsonDocument doc(256); double angle = myGyro.getAngle(); - double temp = myTempSensor.getTempC(myConfig.isGyroTemp()); - double gravity = calculateGravity(angle, temp); + double tempC = myTempSensor.getTempC(myConfig.isGyroTemp()); + double gravity = calculateGravity(angle, tempC); doc[CFG_PARAM_ID] = myConfig.getID(); doc[CFG_PARAM_ANGLE] = reduceFloatPrecision(angle); - if (myConfig.isGravityTempAdj()) - doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision( - gravityTemperatureCorrection(gravity, temp, myConfig.getTempFormat()), - 4); - else + if (myConfig.isGravityTempAdj()) { + gravity = gravityTemperatureCorrectionC(gravity, tempC); // + } + if (myConfig.isGravityPlato()) { + doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1); + } else { doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(gravity, 4); - doc[CFG_PARAM_TEMP_C] = reduceFloatPrecision(temp, 1); - doc[CFG_PARAM_TEMP_F] = - reduceFloatPrecision(myTempSensor.getTempF(myConfig.isGyroTemp()), 1); + } + doc[CFG_PARAM_TEMP_C] = reduceFloatPrecision(tempC, 1); + doc[CFG_PARAM_TEMP_F] = reduceFloatPrecision(convertCtoF(tempC), 1); doc[CFG_PARAM_BATTERY] = reduceFloatPrecision(myBatteryVoltage.getVoltage()); doc[CFG_PARAM_TEMPFORMAT] = String(myConfig.getTempFormat()); + doc[CFG_PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat()); doc[CFG_PARAM_SLEEP_MODE] = sleepModeAlwaysSkip; doc[CFG_PARAM_RSSI] = WiFi.RSSI(); @@ -280,8 +289,7 @@ void WebServer::webHandleStatusSleepmode() { } #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) - Log.verbose(F("WEB : sleep-mode=%s." CR), - server->arg(CFG_PARAM_SLEEP_MODE).c_str()); + Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str()); #endif if (server->arg(CFG_PARAM_SLEEP_MODE).equalsIgnoreCase("true")) @@ -309,9 +317,7 @@ void WebServer::webHandleConfigDevice() { } #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) - Log.verbose(F("WEB : mdns=%s, temp-format=%s." CR), - server->arg(CFG_PARAM_MDNS).c_str(), - server->arg(CFG_PARAM_TEMPFORMAT).c_str()); + Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str()); #endif myConfig.setMDNS(server->arg(CFG_PARAM_MDNS).c_str()); @@ -339,14 +345,7 @@ void WebServer::webHandleConfigPush() { return; } #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) - Log.verbose(F("WEB : http=%s,%s, bf=%s influx2=%s, %s, %s, %s." CR), - server->arg(CFG_PARAM_PUSH_HTTP).c_str(), - server->arg(CFG_PARAM_PUSH_HTTP2).c_str(), - server->arg(CFG_PARAM_PUSH_BREWFATHER).c_str(), - server->arg(CFG_PARAM_PUSH_INFLUXDB2).c_str(), - server->arg(CFG_PARAM_PUSH_INFLUXDB2_ORG).c_str(), - server->arg(CFG_PARAM_PUSH_INFLUXDB2_BUCKET).c_str(), - server->arg(CFG_PARAM_PUSH_INFLUXDB2_AUTH).c_str()); + Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str()); #endif myConfig.setHttpPushUrl(server->arg(CFG_PARAM_PUSH_HTTP).c_str()); @@ -369,6 +368,25 @@ void WebServer::webHandleConfigPush() { LOG_PERF_STOP("webserver-api-config-push"); } +// +// Get string with all received arguments. Used for debugging only. +// +String WebServer::getRequestArguments() { + String debug; + + for (int i = 0; i < server->args(); i++) { + if (!server->argName(i).equals( + "plain")) { // this contains all the arguments, we dont need that. + if (debug.length()) debug += ", "; + + debug += server->argName(i); + debug += "="; + debug += server->arg(i); + } + } + return debug; +} + // // Update gravity settings. // @@ -386,11 +404,10 @@ void WebServer::webHandleConfigGravity() { } #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) - Log.verbose(F("WEB : formula=%s, temp-corr=%s." CR), - server->arg(CFG_PARAM_GRAVITY_FORMULA).c_str(), - server->arg(CFG_PARAM_GRAVITY_TEMP_ADJ).c_str()); + Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str()); #endif + myConfig.setGravityFormat(server->arg(CFG_PARAM_GRAVITY_FORMAT).charAt(0)); myConfig.setGravityFormula(server->arg(CFG_PARAM_GRAVITY_FORMULA).c_str()); myConfig.setGravityTempAdj( server->arg(CFG_PARAM_GRAVITY_TEMP_ADJ).equalsIgnoreCase("on") ? true @@ -418,15 +435,15 @@ void WebServer::webHandleConfigHardware() { } #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) - Log.verbose(F("WEB : vf=%s, tempadj=%s, ota=%s gyrotemp=%s." CR), - server->arg(CFG_PARAM_VOLTAGEFACTOR).c_str(), - server->arg(CFG_PARAM_TEMP_ADJ).c_str(), - server->arg(CFG_PARAM_OTA).c_str(), - server->arg(CFG_PARAM_GYRO_TEMP).c_str()); + Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str()); #endif myConfig.setVoltageFactor(server->arg(CFG_PARAM_VOLTAGEFACTOR).toFloat()); - myConfig.setTempSensorAdj(server->arg(CFG_PARAM_TEMP_ADJ).toFloat()); + if (myConfig.isTempC()) { + myConfig.setTempSensorAdjC(server->arg(CFG_PARAM_TEMP_ADJ)); + } else { + myConfig.setTempSensorAdjF(server->arg(CFG_PARAM_TEMP_ADJ)); + } myConfig.setOtaURL(server->arg(CFG_PARAM_OTA).c_str()); myConfig.setGyroTemp( server->arg(CFG_PARAM_GYRO_TEMP).equalsIgnoreCase("on") ? true : false); @@ -447,24 +464,24 @@ void WebServer::webHandleFormulaRead() { const RawFormulaData& fd = myConfig.getFormulaData(); #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) - Log.verbose(F("WEB : %F %F %F %F %F." CR), fd.a[0], fd.a[1], fd.a[2], fd.a[3], - fd.a[4]); - Log.verbose(F("WEB : %F %F %F %F %F." CR), fd.g[0], fd.g[1], fd.g[2], fd.g[3], - fd.g[4]); + Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str()); #endif doc[CFG_PARAM_ID] = myConfig.getID(); doc[CFG_PARAM_ANGLE] = reduceFloatPrecision(myGyro.getAngle()); + doc[CFG_PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat()); + doc[CFG_PARAM_GRAVITY_FORMULA] = ""; + doc[CFG_PARAM_ERROR] = ""; switch (lastFormulaCreateError) { case ERR_FORMULA_INTERNAL: - doc[CFG_PARAM_GRAVITY_FORMULA] = "Internal error creating formula."; + doc[CFG_PARAM_ERROR] = "Internal error creating formula."; break; case ERR_FORMULA_NOTENOUGHVALUES: - doc[CFG_PARAM_GRAVITY_FORMULA] = "Not enough values to create formula."; + doc[CFG_PARAM_ERROR] = "Not enough values to create formula."; break; case ERR_FORMULA_UNABLETOFFIND: - doc[CFG_PARAM_GRAVITY_FORMULA] = + doc[CFG_PARAM_ERROR] = "Unable to find an accurate formula based on input."; break; default: @@ -478,11 +495,19 @@ void WebServer::webHandleFormulaRead() { doc["a4"] = reduceFloatPrecision(fd.a[3], 2); doc["a5"] = reduceFloatPrecision(fd.a[4], 2); - doc["g1"] = reduceFloatPrecision(fd.g[0], 4); - doc["g2"] = reduceFloatPrecision(fd.g[1], 4); - doc["g3"] = reduceFloatPrecision(fd.g[2], 4); - doc["g4"] = reduceFloatPrecision(fd.g[3], 4); - doc["g5"] = reduceFloatPrecision(fd.g[4], 4); + if (myConfig.isGravityPlato()) { + doc["g1"] = reduceFloatPrecision(convertToPlato(fd.g[0]), 1); + doc["g2"] = reduceFloatPrecision(convertToPlato(fd.g[1]), 1); + doc["g3"] = reduceFloatPrecision(convertToPlato(fd.g[2]), 1); + doc["g4"] = reduceFloatPrecision(convertToPlato(fd.g[3]), 1); + doc["g5"] = reduceFloatPrecision(convertToPlato(fd.g[4]), 1); + } else { + doc["g1"] = reduceFloatPrecision(fd.g[0], 4); + doc["g2"] = reduceFloatPrecision(fd.g[1], 4); + doc["g3"] = reduceFloatPrecision(fd.g[2], 4); + doc["g4"] = reduceFloatPrecision(fd.g[3], 4); + doc["g5"] = reduceFloatPrecision(fd.g[4], 4); + } #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) serializeJson(doc, Serial); @@ -512,13 +537,7 @@ void WebServer::webHandleFormulaWrite() { } #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) - Log.verbose(F("WEB : angles=%F,%F,%F,%F,%F." CR), server->arg("a1").toFloat(), - server->arg("a2").toFloat(), server->arg("a3").toFloat(), - server->arg("a4").toFloat(), server->arg("a5").toFloat()); - Log.verbose(F("WEB : gravity=%F,%F,%F,%F,%F." CR), - server->arg("g1").toFloat(), server->arg("g2").toFloat(), - server->arg("g3").toFloat(), server->arg("g4").toFloat(), - server->arg("g5").toFloat()); + Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str()); #endif RawFormulaData fd; @@ -527,11 +546,21 @@ void WebServer::webHandleFormulaWrite() { fd.a[2] = server->arg("a3").toDouble(); fd.a[3] = server->arg("a4").toDouble(); fd.a[4] = server->arg("a5").toDouble(); - fd.g[0] = server->arg("g1").toDouble(); - fd.g[1] = server->arg("g2").toDouble(); - fd.g[2] = server->arg("g3").toDouble(); - fd.g[3] = server->arg("g4").toDouble(); - fd.g[4] = server->arg("g5").toDouble(); + + if (myConfig.isGravityPlato()) { + fd.g[0] = convertToSG(server->arg("g1").toDouble()); + fd.g[1] = convertToSG(server->arg("g2").toDouble()); + fd.g[2] = convertToSG(server->arg("g3").toDouble()); + fd.g[3] = convertToSG(server->arg("g4").toDouble()); + fd.g[4] = convertToSG(server->arg("g5").toDouble()); + } else { + fd.g[0] = server->arg("g1").toDouble(); + fd.g[1] = server->arg("g2").toDouble(); + fd.g[2] = server->arg("g3").toDouble(); + fd.g[3] = server->arg("g4").toDouble(); + fd.g[4] = server->arg("g5").toDouble(); + } + myConfig.setFormulaData(fd); int e; @@ -727,9 +756,8 @@ bool WebServer::setupWebServer() { // called from main loop // void WebServer::loop() { - // Dont put serial debug output in this call - server->handleClient(); MDNS.update(); + server->handleClient(); } // EOF diff --git a/src/webserver.hpp b/src/webserver.hpp index 9051d23..d8a9d6c 100644 --- a/src/webserver.hpp +++ b/src/webserver.hpp @@ -65,6 +65,8 @@ class WebServer { void webHandleDevice(); void webHandlePageNotFound(); + String getRequestArguments(); + // Inline functions. void webReturnOK() { server->send(200); } #if defined(EMBED_HTML) diff --git a/src/wifi.cpp b/src/wifi.cpp index e040735..ddf4de4 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -110,6 +110,8 @@ String WifiConnection::getIPAddress() { return WiFi.localIP().toString(); } // Additional method to detect double reset. // bool WifiConnection::isDoubleResetDetected() { + if (strlen(userSSID)) + return false; // Ignore this if we have hardcoded settings. return myDRD->detectDoubleReset(); } @@ -230,11 +232,17 @@ bool WifiConnection::updateFirmware() { Log.verbose(F("WIFI: Updating firmware." CR)); #endif - WiFiClient client; String serverPath = myConfig.getOtaURL(); serverPath += "firmware.bin"; + HTTPUpdateResult ret; - HTTPUpdateResult ret = ESPhttpUpdate.update(client, serverPath); + if (serverPath.startsWith("https://")) { + myWifi.getWifiClientSecure().setInsecure(); + Log.notice(F("WIFI: OTA, SSL enabled without validation." CR)); + ret = ESPhttpUpdate.update(myWifi.getWifiClientSecure(), serverPath); + } else { + ret = ESPhttpUpdate.update(myWifi.getWifiClient(), serverPath); + } switch (ret) { case HTTP_UPDATE_FAILED: @@ -257,16 +265,21 @@ bool WifiConnection::updateFirmware() { // Download and save file // void WifiConnection::downloadFile(const char *fname) { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) Log.verbose(F("WIFI: Download file %s." CR), fname); #endif - WiFiClient client; HTTPClient http; String serverPath = myConfig.getOtaURL(); serverPath += fname; - // Your Domain name with URL path or IP address with path - http.begin(client, serverPath); + if (serverPath.startsWith("https://")) { + myWifi.getWifiClientSecure().setInsecure(); + Log.notice(F("WIFI: OTA, SSL enabled without validation." CR)); + http.begin(myWifi.getWifiClientSecure(), serverPath); + } else { + http.begin(myWifi.getWifiClient(), serverPath); + } + int httpResponseCode = http.GET(); if (httpResponseCode == 200) { @@ -279,22 +292,28 @@ void WifiConnection::downloadFile(const char *fname) { httpResponseCode); } http.end(); + myWifi.closeWifiClient(); } // // Check what firmware version is available over OTA // bool WifiConnection::checkFirmwareVersion() { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) Log.verbose(F("WIFI: Checking if new version exist." CR)); #endif - WiFiClient client; HTTPClient http; String serverPath = myConfig.getOtaURL(); serverPath += "version.json"; // Your Domain name with URL path or IP address with path - http.begin(client, serverPath); + if (serverPath.startsWith("https://")) { + myWifi.getWifiClientSecure().setInsecure(); + Log.notice(F("WIFI: OTA, SSL enabled without validation." CR)); + http.begin(myWifi.getWifiClientSecure(), serverPath); + } else { + http.begin(myWifi.getWifiClient(), serverPath); + } // Send HTTP GET request int httpResponseCode = http.GET(); @@ -303,7 +322,7 @@ bool WifiConnection::checkFirmwareVersion() { Log.notice(F("WIFI: Found version.json, response=%d" CR), httpResponseCode); String payload = http.getString(); -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) Log.verbose(F("WIFI: Payload %s." CR), payload.c_str()); #endif DynamicJsonDocument ver(300); @@ -311,7 +330,7 @@ bool WifiConnection::checkFirmwareVersion() { if (err) { Log.error(F("WIFI: Failed to parse version.json, %s" CR), err); } else { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) Log.verbose(F("WIFI: Project %s version %s." CR), (const char *)ver["project"], (const char *)ver["version"]); #endif @@ -320,7 +339,7 @@ bool WifiConnection::checkFirmwareVersion() { if (parseFirmwareVersionString(newVer, (const char *)ver["version"])) { if (parseFirmwareVersionString(curVer, CFG_APPVER)) { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) Log.verbose(F("WIFI: OTA checking new=%d.%d.%d cur=%d.%d.%d" CR), newVer[0], newVer[1], newVer[2], curVer[0], curVer[1], curVer[2]); @@ -355,6 +374,7 @@ bool WifiConnection::checkFirmwareVersion() { httpResponseCode); } http.end(); + myWifi.closeWifiClient(); #if LOG_LEVEL == 6 Log.verbose(F("WIFI: OTA found new version %s." CR), newFirmware ? "true" : "false"); @@ -367,7 +387,7 @@ bool WifiConnection::checkFirmwareVersion() { // bool WifiConnection::parseFirmwareVersionString(int (&num)[3], const char *version) { -#if LOG_LEVEL == 6 +#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) Log.verbose(F("WIFI: Parsing version number string %s." CR), version); #endif char temp[80]; diff --git a/src/wifi.hpp b/src/wifi.hpp index 4854ffb..fa9c2f3 100644 --- a/src/wifi.hpp +++ b/src/wifi.hpp @@ -26,16 +26,24 @@ SOFTWARE. // Include #include +#include + +// tcp cleanup, to avoid memory crash. +struct tcp_pcb; +extern struct tcp_pcb* tcp_tw_pcbs; +extern "C" void tcp_abort(struct tcp_pcb* pcb); // classes class WifiConnection { private: // WIFI + WiFiClient _client; + WiFiClientSecure _secureClient; // OTA bool newFirmware = false; - bool parseFirmwareVersionString(int (&num)[3], const char *version); - void downloadFile(const char *fname); + bool parseFirmwareVersionString(int (&num)[3], const char* version); + void downloadFile(const char* fname); void connectAsync(); bool waitForConnection(int maxTime = 20); @@ -53,6 +61,16 @@ class WifiConnection { void startPortal(); void loop(); + WiFiClient& getWifiClient() { return _client; } + WiFiClientSecure& getWifiClientSecure() { return _secureClient; } + void closeWifiClient() { + _client.stopAll(); + _secureClient.stopAll(); + + // Cleanup memory allocated by open tcp connetions. + while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs); + } + // OTA bool updateFirmware(); bool checkFirmwareVersion(); diff --git a/src_docs/source/compiling.rst b/src_docs/source/compiling.rst index d8dbfbf..19a4030 100644 --- a/src_docs/source/compiling.rst +++ b/src_docs/source/compiling.rst @@ -83,19 +83,9 @@ This is a list of C++ defines that is used to enable/disable functions in the co * - ACTIVATE_OTA - Enables the OTA functionallity in the code * - SKIP_SLEEPMODE - - THe device never goes into sleep mode, useful when developing. - * - CFG_DISABLE_LOGGING - - Done include verbose logging in Config class. Excessive logging may crash device. - * - GYRO_DISABLE_LOGGING - - Done include verbose logging in Gyro class. Excessive logging may crash device. - * - PUSH_DISABLE_LOGGING - - Done include verbose logging in PushTarget class. Excessive logging may crash device. - * - TSEN_DISABLE_LOGGING - - Done include verbose logging in TempSensor class. Excessive logging may crash device. - * - WEB_DISABLE_LOGGING - - Done include verbose logging in WebServer class. Excessive logging may crash device. - * - MAIN_DISABLE_LOGGING - - Done include verbose logging in Main class. Excessive logging may crash device. + - The device never goes into sleep mode, useful when developing. + * - xxx_DISABLE_LOGGING + - Done include verbose logging in the corresponding class. Excessive logging may crash device. * - USE_LITTLEFS - Use the new filesystem in Ardurino * - EMBED_HTML diff --git a/src_docs/source/configuration.rst b/src_docs/source/configuration.rst index f8c0b97..01df80c 100644 --- a/src_docs/source/configuration.rst +++ b/src_docs/source/configuration.rst @@ -12,6 +12,7 @@ One of the following conditions will place the device in ``configuration mode``: - Placed in horizontal mode 85-90 degrees - Charger connected >4.15V + Status ====== @@ -97,22 +98,36 @@ Push Settings :width: 800 :alt: Push Settings +.. note:: + + When enabling SSL this will not validate the root CA of the remote service, this is a design decision based on two aspects. Enabling CA validation will take 3-4s extra on each connection which means way less + battery life, so the decision is to prioritize battery life over security. The data transmitted is not really that sensitive anyway so I belive this is a good balance. + + * **HTTP URL 1:** Endpoint to send data via http. Format used Format used :ref:`data-formats-ispindle` + If you add the prefix `https://` then the device will use SSL when sending data. + * **HTTP URL 2:** Endpoint to send data via http. Format used :ref:`data-formats-ispindle` + If you add the prefix `https://` then the device will use SSL when sending data. + * **Brewfather URL:** Endpoint to send data via http to brewfather. Format used :ref:`data-formats-brewfather` + SSL is not supported for this target. + * **Influx DB v2 URL:** Endpoint to send data via http to InfluxDB. Format used :ref:`data-formats-influxdb2` + SSL is not supported for this target. Raise a issue on github if this is wanted. + * **Influx DB v2 Organisation:** Name of organisation in Influx. @@ -129,6 +144,8 @@ Push Settings IP or name of server to send data to. Format used :ref:`data-formats-ispindle` + If you add the suffix `:8883` to the server name, then the device will use SSL when sending data. + * **MQTT Topic:** Name of topic to publish sensor readings to, iSpindle format is used. @@ -149,6 +166,10 @@ Gravity Settings :width: 800 :alt: Gravity Settings +* **Gravity format:** + + Gravity format can be eihter `SG` or `Plato`. The device will use SG Internally and convert to Plato when displaying data. + * **Gravity formula:** Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. You can also use @@ -159,9 +180,9 @@ Gravity Settings Will apply a temperature calibration formula to the gravity as a second step. .. warning:: - This formula assumes that the calibration has been done at 20C. + This formula assumes that the calibration has been done at 20°C / 68°F. -Formula used in temperature correction: +Formula used in temperature correction. :: @@ -199,6 +220,8 @@ Hardware Settings For the OTA to work, place the following files (version.json + firmware.bin) at the location that you pointed out in OTA URL. If the version number in the json file is newer than in the code the update will be done during startup. + If you have the previx `https://` then the device will use secure transfer without CA validation. + Example; OTA URL (don't forget trailing dash), the name of the file should be firmware.bin .. code-block:: @@ -325,7 +348,7 @@ GET: /api/config/formula Retrive the data used for formula calculation data via an HTTP GET command. Payload is in JSON format. * ``a1``-``a4`` are the angles/tilt readings (up to 5 are currently supported) -* ``g1``-``g4`` are the corresponding gravity reaadings (in SG) +* ``g1``-``g4`` are the corresponding gravity reaadings in SG or Plato depending on the device-format. .. code-block:: json @@ -340,8 +363,9 @@ Retrive the data used for formula calculation data via an HTTP GET command. Payl "g2": 1.053, "g3": 1.062, "g4": 1, - "g5": 1 - "gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436", + "g5": 1, + "gravity-format": "G", + "gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436" } @@ -350,7 +374,7 @@ POST: /api/config/device Used to update device settings via an HTTP POST command. Payload is in JSON format. -* ``temp-format`` can be either ``C`` or ``F`` +* ``temp-format`` can be either ``C`` (Celcius) or ``F`` (Farenheight) .. code-block:: json @@ -391,6 +415,7 @@ POST: /api/config/gravity Used to update gravity settings via an HTTP POST command. Payload is in JSON format. * ``gravity-formula`` keywords ``temp`` and ``tilt`` are supported. +* ``gravity-format`` can be either ``G`` (SG) or ``P`` (PLATO) .. note:: ``gravity-temp-adjustment`` is defined as "on" or "off" when posting since this is the output values @@ -401,6 +426,7 @@ Used to update gravity settings via an HTTP POST command. Payload is in JSON for { "id": "ee1bfc", "gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436", + "gravity-format": "P", "gravity-temp-adjustment": "off" } @@ -500,6 +526,7 @@ present or the API call will fail. url = "http://" + host + "/api/config/gravity" json = { "id": id, "gravity-formula": "", + "gravity-format": "P", "gravity-temp-adjustment": "off" # Adjust gravity (on/off) } set_config( url, json ) @@ -542,6 +569,7 @@ iSpindle format This is the format used for standard http posts. * ``corr-gravity`` is an extended parameter containing a temperature corrected gravity reading. +* ``gravity-format`` is an extended parameter containing the gravity format (G or P). * ``run-time`` is an extended parameter containing the number of seconds the execution took. .. code-block:: json @@ -554,10 +582,12 @@ This is the format used for standard http posts. "temperature": 20.5, "temp-units": "C", "gravity": 1.0050, - "corr-gravity": 1.0050, "angle": 45.34, "battery": 3.67, "rssi": -12, + + "corr-gravity": 1.0050, + "gravity-unit": "G", "run-time": 6 } @@ -567,14 +597,14 @@ This is the format used for standard http posts. Brewfather format ================= -This is the format for Brewfather +This is the format for Brewfather. See: `Brewfather API docs `_ .. code-block:: json { "name" : "gravmon", "temp": 20.5, - "temp-unit": "C", + "temp_unit": "C", "battery": 3.67, "gravity": 1.0050, "gravity_unit": "G", diff --git a/src_docs/source/images/config3.png b/src_docs/source/images/config3.png index 568e17c..8fcd6f4 100644 Binary files a/src_docs/source/images/config3.png and b/src_docs/source/images/config3.png differ diff --git a/src_docs/source/index.rst b/src_docs/source/index.rst index eeb3b33..1954666 100644 --- a/src_docs/source/index.rst +++ b/src_docs/source/index.rst @@ -7,7 +7,7 @@ Welcome to GravityMon's documentation! ###################################### .. note:: - This documentation reflects **v0.6**. Last updated 2022-01-15 + This documentation reflects **v0.7**. Last updated 2022-01-18 GravityMon is a replacement firmare for the iSpindle firmware, it uses the same hardware configuration so @@ -23,9 +23,6 @@ The hardware design comes from the fantastic iSpindle project so that is not cov My approach to this software is a little different from that the original ispindle firmware. The github repository can be found here; `GravityMon on Github `_ - - - .. note:: This software is in the early stages even though its more than one year old so if you find issues, please open a ticket on github. @@ -46,6 +43,7 @@ The main differences: * Visual graph showing how formula will be interpreted * Using the temperature sensor in gyro instead of DS18B20 (faster) * Built in performance measurements (used to optimise code) +* SSL support in standard HTTP and MQTT connections. For a complete breakdown see the :ref:`functionallity` diff --git a/src_docs/source/releases.rst b/src_docs/source/releases.rst index 9eddc78..b571d83 100644 --- a/src_docs/source/releases.rst +++ b/src_docs/source/releases.rst @@ -3,6 +3,24 @@ Releases ######## +v0.7.0 +------ + +Development version (dev branch) + +* SSL support for HTTP targets (no validation of CA) +* SSL support for MQTT targets (no validation of CA) +* SSL support for OTA (no validation of CA) +* Breaking change: To simplify the internal structure the + temp sensor adjustment is now stored in C. So if you have + enabled this function using F you will need to go into + the configuration and update the factor again. +* Added error handling for calibration page. + +TODO: +Update docs, MQTT ssl is enabled using :8883 at end, http targets enables using prefix https:// +Note! Brewfather don't support SSL. + v0.6.0 ------