Applied precommit cpp checks
This commit is contained in:
parent
88bd971b73
commit
ed53182c29
389
src/calc.cpp
389
src/calc.cpp
@ -1,186 +1,203 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 "src/calc.h"
|
||||
#include <tinyexpr.h>
|
||||
#include <curveFitting.h>
|
||||
#include "src/helper.h"
|
||||
#include "src/config.h"
|
||||
#include "src/tempsensor.h"
|
||||
|
||||
#define FORMULA_MAX_DEVIATION 1.5
|
||||
|
||||
//
|
||||
// Use values to derive a formula
|
||||
//
|
||||
int createFormula(RawFormulaData& fd, char *formulaBuffer, int formulaBufferSize, int order) {
|
||||
|
||||
int noAngles = 0;
|
||||
|
||||
// Check how many valid values we have got
|
||||
if ( fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0 && fd.a[4] > 0 )
|
||||
noAngles = 5;
|
||||
else if ( fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0 )
|
||||
noAngles = 4;
|
||||
else if ( fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 )
|
||||
noAngles = 3;
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Trying to create formula using order = %d, found %d angles" CR), order, noAngles);
|
||||
#endif
|
||||
|
||||
if ( !noAngles ) {
|
||||
Log.error(F("CALC: Not enough values for deriving formula" CR));
|
||||
return ERR_FORMULA_NOTENOUGHVALUES;
|
||||
} else {
|
||||
|
||||
double coeffs[order+1];
|
||||
int ret = fitCurve(order, noAngles, fd.a, fd.g, sizeof(coeffs)/sizeof(double), coeffs);
|
||||
|
||||
// Returned value is 0 if no error
|
||||
if ( ret == 0 ) {
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Finshied processing data points." CR));
|
||||
#endif
|
||||
|
||||
// Print the formula based on 'order'
|
||||
if ( order == 4 ) {
|
||||
snprintf(formulaBuffer, formulaBufferSize, "%.8f*tilt^4+%.8f*tilt^3+%.8f*tilt^2+%.8f*tilt+%.8f", coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4]);
|
||||
} else if ( order == 3 ) {
|
||||
snprintf(formulaBuffer, formulaBufferSize, "%.8f*tilt^3+%.8f*tilt^2+%.8f*tilt+%.8f", coeffs[0], coeffs[1], coeffs[2], coeffs[3]);
|
||||
} else if ( order == 2 ) {
|
||||
snprintf(formulaBuffer, formulaBufferSize, "%.8f*tilt^2+%.8f*tilt+%.8f", coeffs[0], coeffs[1], coeffs[2]);
|
||||
} else { // order == 1
|
||||
snprintf(formulaBuffer, formulaBufferSize, "%.8f*tilt+%.8f", coeffs[0], coeffs[1]);
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Formula: %s" CR), formulaBuffer );
|
||||
#endif
|
||||
|
||||
bool valid = true;
|
||||
|
||||
for ( int i = 0; i < 5; i++ ) {
|
||||
if ( fd.a[i] == 0 && valid )
|
||||
break;
|
||||
|
||||
double g = calculateGravity(fd.a[i], 0, 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 ( !valid ) {
|
||||
Log.error(F("CALC: Deviation to large, formula rejected." CR));
|
||||
return ERR_FORMULA_UNABLETOFFIND;
|
||||
}
|
||||
|
||||
Log.info(F("CALC: Found formula '%s'." CR), formulaBuffer);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Log.error(F("CALC: Internal error finding formula." CR));
|
||||
return ERR_FORMULA_INTERNAL;
|
||||
}
|
||||
|
||||
//
|
||||
// Calculates gravity according to supplied formula, compatible with iSpindle/Fermentrack formula
|
||||
//
|
||||
double calculateGravity(double angle, double temp, const char *tempFormula) {
|
||||
const char* formula = myConfig.getGravityFormula();
|
||||
|
||||
if ( tempFormula != 0 ) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Using temporary formula." CR));
|
||||
#endif
|
||||
formula = tempFormula;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Calculating gravity for angle %F, temp %F." CR), angle, temp);
|
||||
Log.verbose(F("CALC: Formula %s." CR), formula);
|
||||
#endif
|
||||
|
||||
if ( strlen(formula) == 0 )
|
||||
return 0.0;
|
||||
|
||||
// Store variable names and pointers.
|
||||
te_variable vars[] = {{"tilt", &angle}, {"temp", &temp}};
|
||||
|
||||
int err;
|
||||
// Compile the expression with variables.
|
||||
te_expr *expr = te_compile(formula, vars, 2, &err);
|
||||
|
||||
if ( expr ) {
|
||||
double g = te_eval(expr);
|
||||
te_free(expr);
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Calculated gravity is %F." CR), g);
|
||||
#endif
|
||||
return g;
|
||||
}
|
||||
|
||||
Log.error(F("CALC: Failed to parse expression %d." CR), err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Do a standard gravity temperature correction. This is a simple way to adjust for differnt worth temperatures
|
||||
//
|
||||
double gravityTemperatureCorrection(double gravity, double temp, char tempFormat, double calTemp) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, temp %F, calTemp %F." CR), gravity, temp, calTemp);
|
||||
#endif
|
||||
|
||||
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)/(1.00130346-0.000134722124*cal+0.00000204052596*cal^2-0.00000000232820948*cal^3))";
|
||||
|
||||
// Store variable names and pointers.
|
||||
te_variable vars[] = {{"gravity", &gravity}, {"temp", &temp}, {"cal", &calTempF}};
|
||||
|
||||
int err;
|
||||
// Compile the expression with variables.
|
||||
te_expr *expr = te_compile(formula, vars, 3, &err);
|
||||
|
||||
if ( expr ) {
|
||||
double g = te_eval(expr);
|
||||
te_free(expr);
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Corrected gravity is %F." CR), g);
|
||||
#endif
|
||||
return g;
|
||||
}
|
||||
|
||||
Log.error(F("CALC: Failed to parse expression %d, no correction has been made." CR), err);
|
||||
return gravity;
|
||||
}
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 <config.h>
|
||||
#include <curveFitting.h>
|
||||
#include <tinyexpr.h>
|
||||
|
||||
#include <calc.hpp>
|
||||
#include <helper.hpp>
|
||||
#include <tempsensor.hpp>
|
||||
|
||||
#define FORMULA_MAX_DEVIATION 1.5
|
||||
|
||||
//
|
||||
// Use values to derive a formula
|
||||
//
|
||||
int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||
int formulaBufferSize, int order) {
|
||||
int noAngles = 0;
|
||||
|
||||
// Check how many valid values we have got
|
||||
if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0 && fd.a[4] > 0)
|
||||
noAngles = 5;
|
||||
else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0)
|
||||
noAngles = 4;
|
||||
else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0)
|
||||
noAngles = 3;
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(
|
||||
F("CALC: Trying to create formula using order = %d, found %d angles" CR),
|
||||
order, noAngles);
|
||||
#endif
|
||||
|
||||
if (!noAngles) {
|
||||
Log.error(F("CALC: Not enough values for deriving formula" CR));
|
||||
return ERR_FORMULA_NOTENOUGHVALUES;
|
||||
} else {
|
||||
double coeffs[order + 1];
|
||||
int ret = fitCurve(order, noAngles, fd.a, fd.g,
|
||||
sizeof(coeffs) / sizeof(double), coeffs);
|
||||
|
||||
// Returned value is 0 if no error
|
||||
if (ret == 0) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Finshied processing data points." CR));
|
||||
#endif
|
||||
|
||||
// Print the formula based on 'order'
|
||||
if (order == 4) {
|
||||
snprintf(formulaBuffer, formulaBufferSize,
|
||||
"%.8f*tilt^4+%.8f*tilt^3+%.8f*tilt^2+%.8f*tilt+%.8f",
|
||||
coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4]);
|
||||
} else if (order == 3) {
|
||||
snprintf(formulaBuffer, formulaBufferSize,
|
||||
"%.8f*tilt^3+%.8f*tilt^2+%.8f*tilt+%.8f", coeffs[0], coeffs[1],
|
||||
coeffs[2], coeffs[3]);
|
||||
} else if (order == 2) {
|
||||
snprintf(formulaBuffer, formulaBufferSize, "%.8f*tilt^2+%.8f*tilt+%.8f",
|
||||
coeffs[0], coeffs[1], coeffs[2]);
|
||||
} else { // order == 1
|
||||
snprintf(formulaBuffer, formulaBufferSize, "%.8f*tilt+%.8f", coeffs[0],
|
||||
coeffs[1]);
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Formula: %s" CR), formulaBuffer);
|
||||
#endif
|
||||
|
||||
bool valid = true;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (fd.a[i] == 0 && valid) break;
|
||||
|
||||
double g = calculateGravity(fd.a[i], 0, 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 (!valid) {
|
||||
Log.error(F("CALC: Deviation to large, formula rejected." CR));
|
||||
return ERR_FORMULA_UNABLETOFFIND;
|
||||
}
|
||||
|
||||
Log.info(F("CALC: Found formula '%s'." CR), formulaBuffer);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Log.error(F("CALC: Internal error finding formula." CR));
|
||||
return ERR_FORMULA_INTERNAL;
|
||||
}
|
||||
|
||||
//
|
||||
// Calculates gravity according to supplied formula, compatible with
|
||||
// iSpindle/Fermentrack formula
|
||||
//
|
||||
double calculateGravity(double angle, double temp, const char *tempFormula) {
|
||||
const char *formula = myConfig.getGravityFormula();
|
||||
|
||||
if (tempFormula != 0) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Using temporary formula." CR));
|
||||
#endif
|
||||
formula = tempFormula;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Calculating gravity for angle %F, temp %F." CR), angle,
|
||||
temp);
|
||||
Log.verbose(F("CALC: Formula %s." CR), formula);
|
||||
#endif
|
||||
|
||||
if (strlen(formula) == 0) return 0.0;
|
||||
|
||||
// Store variable names and pointers.
|
||||
te_variable vars[] = {{"tilt", &angle}, {"temp", &temp}};
|
||||
|
||||
int err;
|
||||
// Compile the expression with variables.
|
||||
te_expr *expr = te_compile(formula, vars, 2, &err);
|
||||
|
||||
if (expr) {
|
||||
double g = te_eval(expr);
|
||||
te_free(expr);
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Calculated gravity is %F." CR), g);
|
||||
#endif
|
||||
return g;
|
||||
}
|
||||
|
||||
Log.error(F("CALC: Failed to parse expression %d." CR), err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Do a standard gravity temperature correction. This is a simple way to adjust
|
||||
// for differnt worth temperatures
|
||||
//
|
||||
double gravityTemperatureCorrection(double gravity, double temp,
|
||||
char tempFormat, double calTemp) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, "
|
||||
"temp %F, calTemp %F." CR),
|
||||
gravity, temp, calTemp);
|
||||
#endif
|
||||
|
||||
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)/"
|
||||
"(1.00130346-0.000134722124*cal+0.00000204052596*cal^2-0."
|
||||
"00000000232820948*cal^3))";
|
||||
|
||||
// Store variable names and pointers.
|
||||
te_variable vars[] = {
|
||||
{"gravity", &gravity}, {"temp", &temp}, {"cal", &calTempF}};
|
||||
|
||||
int err;
|
||||
// Compile the expression with variables.
|
||||
te_expr *expr = te_compile(formula, vars, 3, &err);
|
||||
|
||||
if (expr) {
|
||||
double g = te_eval(expr);
|
||||
te_free(expr);
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CALC: Corrected gravity is %F." CR), g);
|
||||
#endif
|
||||
return g;
|
||||
}
|
||||
|
||||
Log.error(
|
||||
F("CALC: Failed to parse expression %d, no correction has been made." CR),
|
||||
err);
|
||||
return gravity;
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -1,42 +1,44 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_CALC_H_
|
||||
#define SRC_CALC_H_
|
||||
|
||||
// Includes
|
||||
#include "src/helper.h"
|
||||
#include "src/config.h"
|
||||
|
||||
#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);
|
||||
int createFormula(RawFormulaData& fd, char *formulaBuffer, int formulaBufferSize, int order);
|
||||
|
||||
#endif // SRC_CALC_H_
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_CALC_HPP_
|
||||
#define SRC_CALC_HPP_
|
||||
|
||||
// Includes
|
||||
#include <config.hpp>
|
||||
#include <helper.hpp>
|
||||
|
||||
#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);
|
||||
int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||
int formulaBufferSize, int order);
|
||||
|
||||
#endif // SRC_CALC_HPP_
|
||||
|
||||
// EOF
|
464
src/config.cpp
464
src/config.cpp
@ -1,7 +1,7 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@ -21,296 +21,304 @@ 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 "src/config.h"
|
||||
#include "src/helper.h"
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include <config.hpp>
|
||||
#include <helper.hpp>
|
||||
|
||||
Config myConfig;
|
||||
|
||||
//
|
||||
// Create the config class with default settings.
|
||||
//
|
||||
Config::Config() {
|
||||
// Assiging default values
|
||||
char buf[30];
|
||||
snprintf(&buf[0], sizeof(buf) "%6x", (unsigned int) ESP.getChipId());
|
||||
id = String(&buf[0]);
|
||||
snprintf(&buf[0], sizeof(buf), "" WIFI_MDNS "%s", getID());
|
||||
mDNS = String(&buf[0]);
|
||||
// Assiging default values
|
||||
char buf[30];
|
||||
snprintf(&buf[0], sizeof(buf), "%6x", (unsigned int)ESP.getChipId());
|
||||
id = String(&buf[0]);
|
||||
snprintf(&buf[0], sizeof(buf), "" WIFI_MDNS "%s", getID());
|
||||
mDNS = String(&buf[0]);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined( CFG_DISABLE_LOGGING )
|
||||
Log.verbose(F("CFG : Created config for %s (%s)." CR), id.c_str(), mDNS.c_str() );
|
||||
#endif
|
||||
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||
Log.verbose(F("CFG : Created config for %s (%s)." CR), id.c_str(),
|
||||
mDNS.c_str());
|
||||
#endif
|
||||
|
||||
setTempFormat('C');
|
||||
setGravityFormat('G');
|
||||
setSleepInterval(900); // 15 minutes
|
||||
setVoltageFactor(1.59); // Conversion factor for battery
|
||||
setTempSensorAdj(0.0);
|
||||
setGravityTempAdj(false);
|
||||
gyroCalibration = { 0 , 0 , 0 , 0 , 0 , 0 };
|
||||
formulaData = {{ 0 , 0 , 0 , 0 , 0 } , { 1 , 1 , 1 , 1 , 1 }};
|
||||
saveNeeded = false;
|
||||
setTempFormat('C');
|
||||
setGravityFormat('G');
|
||||
setSleepInterval(900); // 15 minutes
|
||||
setVoltageFactor(1.59); // Conversion factor for battery
|
||||
setTempSensorAdj(0.0);
|
||||
setGravityTempAdj(false);
|
||||
gyroCalibration = {0, 0, 0, 0, 0, 0};
|
||||
formulaData = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}};
|
||||
saveNeeded = false;
|
||||
}
|
||||
|
||||
//
|
||||
// Populate the json document with all configuration parameters (used in both web and saving to file)
|
||||
// Populate the json document with all configuration parameters (used in both
|
||||
// web and saving to file)
|
||||
//
|
||||
void Config::createJson(DynamicJsonDocument& doc) {
|
||||
doc[ CFG_PARAM_MDNS ] = getMDNS();
|
||||
doc[ CFG_PARAM_ID ] = getID();
|
||||
doc[ CFG_PARAM_OTA ] = getOtaURL();
|
||||
doc[ CFG_PARAM_SSID ] = getWifiSSID();
|
||||
doc[ CFG_PARAM_PASS ] = getWifiPass();
|
||||
doc[ CFG_PARAM_TEMPFORMAT ] = String(getTempFormat());
|
||||
doc[ CFG_PARAM_PUSH_BREWFATHER ] = getBrewfatherPushUrl();
|
||||
doc[ CFG_PARAM_PUSH_HTTP ] = getHttpPushUrl();
|
||||
doc[ CFG_PARAM_PUSH_HTTP2 ] = getHttpPushUrl2();
|
||||
doc[ CFG_PARAM_PUSH_INFLUXDB2 ] = getInfluxDb2PushUrl();
|
||||
doc[ CFG_PARAM_PUSH_INFLUXDB2_ORG ] = getInfluxDb2PushOrg();
|
||||
doc[ CFG_PARAM_PUSH_INFLUXDB2_BUCKET ] = getInfluxDb2PushBucket();
|
||||
doc[ CFG_PARAM_PUSH_INFLUXDB2_AUTH ] = getInfluxDb2PushToken();
|
||||
doc[ CFG_PARAM_SLEEP_INTERVAL ] = getSleepInterval();
|
||||
// doc[ CFG_PARAM_PUSH_INTERVAL ] = getSleepInterval(); // TODO: @deprecated
|
||||
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_GRAVITY_TEMP_ADJ ] = isGravityTempAdj();
|
||||
doc[CFG_PARAM_MDNS] = getMDNS();
|
||||
doc[CFG_PARAM_ID] = getID();
|
||||
doc[CFG_PARAM_OTA] = getOtaURL();
|
||||
doc[CFG_PARAM_SSID] = getWifiSSID();
|
||||
doc[CFG_PARAM_PASS] = getWifiPass();
|
||||
doc[CFG_PARAM_TEMPFORMAT] = String(getTempFormat());
|
||||
doc[CFG_PARAM_PUSH_BREWFATHER] = getBrewfatherPushUrl();
|
||||
doc[CFG_PARAM_PUSH_HTTP] = getHttpPushUrl();
|
||||
doc[CFG_PARAM_PUSH_HTTP2] = getHttpPushUrl2();
|
||||
doc[CFG_PARAM_PUSH_INFLUXDB2] = getInfluxDb2PushUrl();
|
||||
doc[CFG_PARAM_PUSH_INFLUXDB2_ORG] = getInfluxDb2PushOrg();
|
||||
doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET] = getInfluxDb2PushBucket();
|
||||
doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH] = getInfluxDb2PushToken();
|
||||
doc[CFG_PARAM_SLEEP_INTERVAL] = getSleepInterval();
|
||||
// doc[ CFG_PARAM_PUSH_INTERVAL ] = getSleepInterval(); //
|
||||
// TODO: @deprecated
|
||||
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_GRAVITY_TEMP_ADJ] = isGravityTempAdj();
|
||||
|
||||
JsonObject cal = doc.createNestedObject(CFG_PARAM_GYRO_CALIBRATION);
|
||||
cal["ax"] = gyroCalibration.ax;
|
||||
cal["ay"] = gyroCalibration.ay;
|
||||
cal["az"] = gyroCalibration.az;
|
||||
cal["gx"] = gyroCalibration.gx;
|
||||
cal["gy"] = gyroCalibration.gy;
|
||||
cal["gz"] = gyroCalibration.gz;
|
||||
JsonObject cal = doc.createNestedObject(CFG_PARAM_GYRO_CALIBRATION);
|
||||
cal["ax"] = gyroCalibration.ax;
|
||||
cal["ay"] = gyroCalibration.ay;
|
||||
cal["az"] = gyroCalibration.az;
|
||||
cal["gx"] = gyroCalibration.gx;
|
||||
cal["gy"] = gyroCalibration.gy;
|
||||
cal["gz"] = gyroCalibration.gz;
|
||||
|
||||
JsonObject cal2 = doc.createNestedObject(CFG_PARAM_FORMULA_DATA);
|
||||
cal2[ "a1" ] = reduceFloatPrecision(formulaData.a[0], 2);
|
||||
cal2[ "a2" ] = reduceFloatPrecision(formulaData.a[1], 2);
|
||||
cal2[ "a3" ] = reduceFloatPrecision(formulaData.a[2], 2);
|
||||
cal2[ "a4" ] = reduceFloatPrecision(formulaData.a[3], 2);
|
||||
cal2[ "a5" ] = reduceFloatPrecision(formulaData.a[4], 2);
|
||||
JsonObject cal2 = doc.createNestedObject(CFG_PARAM_FORMULA_DATA);
|
||||
cal2["a1"] = reduceFloatPrecision(formulaData.a[0], 2);
|
||||
cal2["a2"] = reduceFloatPrecision(formulaData.a[1], 2);
|
||||
cal2["a3"] = reduceFloatPrecision(formulaData.a[2], 2);
|
||||
cal2["a4"] = reduceFloatPrecision(formulaData.a[3], 2);
|
||||
cal2["a5"] = reduceFloatPrecision(formulaData.a[4], 2);
|
||||
|
||||
cal2[ "g1" ] = reduceFloatPrecision(formulaData.g[0], 4);
|
||||
cal2[ "g2" ] = reduceFloatPrecision(formulaData.g[1], 4);
|
||||
cal2[ "g3" ] = reduceFloatPrecision(formulaData.g[2], 4);
|
||||
cal2[ "g4" ] = reduceFloatPrecision(formulaData.g[3], 4);
|
||||
cal2[ "g5" ] = reduceFloatPrecision(formulaData.g[4], 4);
|
||||
cal2["g1"] = reduceFloatPrecision(formulaData.g[0], 4);
|
||||
cal2["g2"] = reduceFloatPrecision(formulaData.g[1], 4);
|
||||
cal2["g3"] = reduceFloatPrecision(formulaData.g[2], 4);
|
||||
cal2["g4"] = reduceFloatPrecision(formulaData.g[3], 4);
|
||||
cal2["g5"] = reduceFloatPrecision(formulaData.g[4], 4);
|
||||
}
|
||||
|
||||
//
|
||||
// Save json document to file
|
||||
//
|
||||
bool Config::saveFile() {
|
||||
if ( !saveNeeded ) {
|
||||
#if LOG_LEVEL == 6 && !defined( CFG_DISABLE_LOGGING )
|
||||
Log.verbose(F("CFG : Skipping save, not needed." CR));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined( CFG_DISABLE_LOGGING )
|
||||
Log.verbose(F("CFG : Saving configuration to file." CR));
|
||||
#endif
|
||||
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "w");
|
||||
|
||||
if ( !configFile ) {
|
||||
Log.error(F("CFG : Failed to open file " CFG_FILENAME " for save." CR));
|
||||
return false;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
||||
createJson(doc);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined( CFG_DISABLE_LOGGING )
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(CR);
|
||||
#endif
|
||||
|
||||
serializeJson(doc, configFile);
|
||||
configFile.flush();
|
||||
configFile.close();
|
||||
|
||||
saveNeeded = false;
|
||||
myConfig.debug();
|
||||
Log.notice(F("CFG : Configuration saved to " CFG_FILENAME "." CR));
|
||||
if (!saveNeeded) {
|
||||
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||
Log.verbose(F("CFG : Skipping save, not needed." CR));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||
Log.verbose(F("CFG : Saving configuration to file." CR));
|
||||
#endif
|
||||
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "w");
|
||||
|
||||
if (!configFile) {
|
||||
Log.error(F("CFG : Failed to open file " CFG_FILENAME " for save." CR));
|
||||
return false;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
||||
createJson(doc);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(CR);
|
||||
#endif
|
||||
|
||||
serializeJson(doc, configFile);
|
||||
configFile.flush();
|
||||
configFile.close();
|
||||
|
||||
saveNeeded = false;
|
||||
myConfig.debug();
|
||||
Log.notice(F("CFG : Configuration saved to " CFG_FILENAME "." CR));
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Load config file from disk
|
||||
//
|
||||
bool Config::loadFile() {
|
||||
#if LOG_LEVEL == 6 && !defined( CFG_DISABLE_LOGGING )
|
||||
Log.verbose(F("CFG : Loading configuration from file." CR));
|
||||
#endif
|
||||
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||
Log.verbose(F("CFG : Loading configuration from file." CR));
|
||||
#endif
|
||||
|
||||
if ( !LittleFS.exists(CFG_FILENAME) ) {
|
||||
Log.error(F("CFG : Configuration file does not exist " CFG_FILENAME "." CR));
|
||||
return false;
|
||||
}
|
||||
if (!LittleFS.exists(CFG_FILENAME)) {
|
||||
Log.error(
|
||||
F("CFG : Configuration file does not exist " CFG_FILENAME "." CR));
|
||||
return false;
|
||||
}
|
||||
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
||||
|
||||
if ( !configFile ) {
|
||||
Log.error(F("CFG : Failed to open " CFG_FILENAME "." CR));
|
||||
return false;
|
||||
}
|
||||
if (!configFile) {
|
||||
Log.error(F("CFG : Failed to open " CFG_FILENAME "." CR));
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.notice(F("CFG : Size of configuration file=%d bytes." CR), configFile.size() );
|
||||
Log.notice(F("CFG : Size of configuration file=%d bytes." CR),
|
||||
configFile.size());
|
||||
|
||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
||||
DeserializationError err = deserializeJson(doc, configFile);
|
||||
#if LOG_LEVEL == 6
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(CR);
|
||||
#endif
|
||||
configFile.close();
|
||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
||||
DeserializationError err = deserializeJson(doc, configFile);
|
||||
#if LOG_LEVEL == 6
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(CR);
|
||||
#endif
|
||||
configFile.close();
|
||||
|
||||
if ( err ) {
|
||||
Log.error(F("CFG : Failed to parse " CFG_FILENAME " file, Err: %s, %d." CR), err.c_str(), doc.capacity());
|
||||
return false;
|
||||
}
|
||||
if (err) {
|
||||
Log.error(F("CFG : Failed to parse " CFG_FILENAME " file, Err: %s, %d." CR),
|
||||
err.c_str(), doc.capacity());
|
||||
return false;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("CFG : Parsed configuration file." CR));
|
||||
Log.verbose(F("CFG : Parsed configuration file." CR));
|
||||
#endif
|
||||
if ( !doc[ CFG_PARAM_OTA ].isNull() )
|
||||
setOtaURL(doc[ CFG_PARAM_OTA ]);
|
||||
if ( !doc[ CFG_PARAM_MDNS ].isNull() )
|
||||
setMDNS(doc[ CFG_PARAM_MDNS ] );
|
||||
if ( !doc[ CFG_PARAM_SSID ].isNull() )
|
||||
setWifiSSID(doc[ CFG_PARAM_SSID ]);
|
||||
if ( !doc[ CFG_PARAM_PASS ].isNull() )
|
||||
setWifiPass(doc[ CFG_PARAM_PASS ]);
|
||||
if ( !doc[ CFG_PARAM_TEMPFORMAT ].isNull() ) {
|
||||
String s = doc[ CFG_PARAM_TEMPFORMAT ];
|
||||
setTempFormat(s.charAt(0));
|
||||
}
|
||||
if ( !doc[ CFG_PARAM_PUSH_BREWFATHER ].isNull() )
|
||||
setBrewfatherPushUrl(doc[ CFG_PARAM_PUSH_BREWFATHER ]);
|
||||
if ( !doc[ CFG_PARAM_PUSH_HTTP ].isNull() )
|
||||
setHttpPushUrl(doc[ CFG_PARAM_PUSH_HTTP ]);
|
||||
if ( !doc[ CFG_PARAM_PUSH_HTTP2 ].isNull() )
|
||||
setHttpPushUrl2(doc[ CFG_PARAM_PUSH_HTTP2 ]);
|
||||
if ( !doc[ CFG_PARAM_PUSH_INFLUXDB2 ].isNull() )
|
||||
setInfluxDb2PushUrl(doc[ CFG_PARAM_PUSH_INFLUXDB2 ]);
|
||||
if ( !doc[ CFG_PARAM_PUSH_INFLUXDB2_ORG ].isNull() )
|
||||
setInfluxDb2PushOrg(doc[ CFG_PARAM_PUSH_INFLUXDB2_ORG ]);
|
||||
if ( !doc[ CFG_PARAM_PUSH_INFLUXDB2_BUCKET ].isNull() )
|
||||
setInfluxDb2PushBucket(doc[ CFG_PARAM_PUSH_INFLUXDB2_BUCKET ]);
|
||||
if ( !doc[ CFG_PARAM_PUSH_INFLUXDB2_AUTH ].isNull() )
|
||||
setInfluxDb2PushToken(doc[ CFG_PARAM_PUSH_INFLUXDB2_AUTH ]);
|
||||
if ( !doc[ CFG_PARAM_SLEEP_INTERVAL ].isNull() )
|
||||
setSleepInterval(doc[ CFG_PARAM_SLEEP_INTERVAL ].as<int>());
|
||||
if ( !doc[ CFG_PARAM_PUSH_INTERVAL ].isNull() ) // TODO: @deprecated
|
||||
setSleepInterval(doc[ CFG_PARAM_PUSH_INTERVAL ].as<int>()); // TODO: @deprecated
|
||||
if ( !doc[ CFG_PARAM_VOLTAGEFACTOR ].isNull() )
|
||||
setVoltageFactor(doc[ CFG_PARAM_VOLTAGEFACTOR ].as<float>());
|
||||
if ( !doc[ CFG_PARAM_GRAVITY_FORMULA ].isNull() )
|
||||
setGravityFormula(doc[ CFG_PARAM_GRAVITY_FORMULA ]);
|
||||
if ( !doc[ CFG_PARAM_GRAVITY_TEMP_ADJ ].isNull() )
|
||||
setGravityTempAdj(doc[ CFG_PARAM_GRAVITY_TEMP_ADJ ].as<bool>());
|
||||
if ( !doc[ CFG_PARAM_GRAVITY_FORMAT ].isNull() ) {
|
||||
String s = doc[ CFG_PARAM_GRAVITY_FORMAT ];
|
||||
setGravityFormat(s.charAt(0));
|
||||
}
|
||||
if ( !doc[ CFG_PARAM_TEMP_ADJ ].isNull() )
|
||||
setTempSensorAdj(doc[ CFG_PARAM_TEMP_ADJ ].as<float>());
|
||||
if (!doc[CFG_PARAM_OTA].isNull()) setOtaURL(doc[CFG_PARAM_OTA]);
|
||||
if (!doc[CFG_PARAM_MDNS].isNull()) setMDNS(doc[CFG_PARAM_MDNS]);
|
||||
if (!doc[CFG_PARAM_SSID].isNull()) setWifiSSID(doc[CFG_PARAM_SSID]);
|
||||
if (!doc[CFG_PARAM_PASS].isNull()) setWifiPass(doc[CFG_PARAM_PASS]);
|
||||
if (!doc[CFG_PARAM_TEMPFORMAT].isNull()) {
|
||||
String s = doc[CFG_PARAM_TEMPFORMAT];
|
||||
setTempFormat(s.charAt(0));
|
||||
}
|
||||
if (!doc[CFG_PARAM_PUSH_BREWFATHER].isNull())
|
||||
setBrewfatherPushUrl(doc[CFG_PARAM_PUSH_BREWFATHER]);
|
||||
if (!doc[CFG_PARAM_PUSH_HTTP].isNull())
|
||||
setHttpPushUrl(doc[CFG_PARAM_PUSH_HTTP]);
|
||||
if (!doc[CFG_PARAM_PUSH_HTTP2].isNull())
|
||||
setHttpPushUrl2(doc[CFG_PARAM_PUSH_HTTP2]);
|
||||
if (!doc[CFG_PARAM_PUSH_INFLUXDB2].isNull())
|
||||
setInfluxDb2PushUrl(doc[CFG_PARAM_PUSH_INFLUXDB2]);
|
||||
if (!doc[CFG_PARAM_PUSH_INFLUXDB2_ORG].isNull())
|
||||
setInfluxDb2PushOrg(doc[CFG_PARAM_PUSH_INFLUXDB2_ORG]);
|
||||
if (!doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET].isNull())
|
||||
setInfluxDb2PushBucket(doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET]);
|
||||
if (!doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH].isNull())
|
||||
setInfluxDb2PushToken(doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH]);
|
||||
if (!doc[CFG_PARAM_SLEEP_INTERVAL].isNull())
|
||||
setSleepInterval(doc[CFG_PARAM_SLEEP_INTERVAL].as<int>());
|
||||
if (!doc[CFG_PARAM_PUSH_INTERVAL].isNull()) // TODO: @deprecated
|
||||
setSleepInterval(
|
||||
doc[CFG_PARAM_PUSH_INTERVAL].as<int>()); // TODO: @deprecated
|
||||
if (!doc[CFG_PARAM_VOLTAGEFACTOR].isNull())
|
||||
setVoltageFactor(doc[CFG_PARAM_VOLTAGEFACTOR].as<float>());
|
||||
if (!doc[CFG_PARAM_GRAVITY_FORMULA].isNull())
|
||||
setGravityFormula(doc[CFG_PARAM_GRAVITY_FORMULA]);
|
||||
if (!doc[CFG_PARAM_GRAVITY_TEMP_ADJ].isNull())
|
||||
setGravityTempAdj(doc[CFG_PARAM_GRAVITY_TEMP_ADJ].as<bool>());
|
||||
if (!doc[CFG_PARAM_GRAVITY_FORMAT].isNull()) {
|
||||
String s = doc[CFG_PARAM_GRAVITY_FORMAT];
|
||||
setGravityFormat(s.charAt(0));
|
||||
}
|
||||
if (!doc[CFG_PARAM_TEMP_ADJ].isNull())
|
||||
setTempSensorAdj(doc[CFG_PARAM_TEMP_ADJ].as<float>());
|
||||
|
||||
if ( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["ax"].isNull() )
|
||||
gyroCalibration.ax = doc[ CFG_PARAM_GYRO_CALIBRATION ]["ax"];
|
||||
if ( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["ay"].isNull() )
|
||||
gyroCalibration.ay = doc[ CFG_PARAM_GYRO_CALIBRATION ]["ay"];
|
||||
if ( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["az"].isNull() )
|
||||
gyroCalibration.az = doc[ CFG_PARAM_GYRO_CALIBRATION ]["az"];
|
||||
if ( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["gx"].isNull() )
|
||||
gyroCalibration.gx = doc[ CFG_PARAM_GYRO_CALIBRATION ]["gx"];
|
||||
if ( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["gy"].isNull() )
|
||||
gyroCalibration.gy = doc[ CFG_PARAM_GYRO_CALIBRATION ]["gy"];
|
||||
if ( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["gz"].isNull() )
|
||||
gyroCalibration.gz = doc[ CFG_PARAM_GYRO_CALIBRATION ]["gz"];
|
||||
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["ax"].isNull())
|
||||
gyroCalibration.ax = doc[CFG_PARAM_GYRO_CALIBRATION]["ax"];
|
||||
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["ay"].isNull())
|
||||
gyroCalibration.ay = doc[CFG_PARAM_GYRO_CALIBRATION]["ay"];
|
||||
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["az"].isNull())
|
||||
gyroCalibration.az = doc[CFG_PARAM_GYRO_CALIBRATION]["az"];
|
||||
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["gx"].isNull())
|
||||
gyroCalibration.gx = doc[CFG_PARAM_GYRO_CALIBRATION]["gx"];
|
||||
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["gy"].isNull())
|
||||
gyroCalibration.gy = doc[CFG_PARAM_GYRO_CALIBRATION]["gy"];
|
||||
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["gz"].isNull())
|
||||
gyroCalibration.gz = doc[CFG_PARAM_GYRO_CALIBRATION]["gz"];
|
||||
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "a1" ].isNull() )
|
||||
formulaData.a[0] = doc[ CFG_PARAM_FORMULA_DATA ][ "a1" ].as<double>();
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "a2" ].isNull() )
|
||||
formulaData.a[1] = doc[ CFG_PARAM_FORMULA_DATA ][ "a2" ].as<double>();
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "a3" ].isNull() )
|
||||
formulaData.a[2] = doc[ CFG_PARAM_FORMULA_DATA ][ "a3" ].as<double>();
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "a4" ].isNull() )
|
||||
formulaData.a[3] = doc[ CFG_PARAM_FORMULA_DATA ][ "a4" ].as<double>();
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "a5" ].isNull() )
|
||||
formulaData.a[4] = doc[ CFG_PARAM_FORMULA_DATA ][ "a5" ].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["a1"].isNull())
|
||||
formulaData.a[0] = doc[CFG_PARAM_FORMULA_DATA]["a1"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["a2"].isNull())
|
||||
formulaData.a[1] = doc[CFG_PARAM_FORMULA_DATA]["a2"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["a3"].isNull())
|
||||
formulaData.a[2] = doc[CFG_PARAM_FORMULA_DATA]["a3"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["a4"].isNull())
|
||||
formulaData.a[3] = doc[CFG_PARAM_FORMULA_DATA]["a4"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["a5"].isNull())
|
||||
formulaData.a[4] = doc[CFG_PARAM_FORMULA_DATA]["a5"].as<double>();
|
||||
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "g1" ].isNull() )
|
||||
formulaData.g[0] = doc[ CFG_PARAM_FORMULA_DATA ][ "g1" ].as<double>();
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "g2" ].isNull() )
|
||||
formulaData.g[1] = doc[ CFG_PARAM_FORMULA_DATA ][ "g2" ].as<double>();
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "g3" ].isNull() )
|
||||
formulaData.g[2] = doc[ CFG_PARAM_FORMULA_DATA ][ "g3" ].as<double>();
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "g4" ].isNull() )
|
||||
formulaData.g[3] = doc[ CFG_PARAM_FORMULA_DATA ][ "g4" ].as<double>();
|
||||
if ( !doc[ CFG_PARAM_FORMULA_DATA ][ "g5" ].isNull() )
|
||||
formulaData.g[4] = doc[ CFG_PARAM_FORMULA_DATA ][ "g5" ];
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["g1"].isNull())
|
||||
formulaData.g[0] = doc[CFG_PARAM_FORMULA_DATA]["g1"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["g2"].isNull())
|
||||
formulaData.g[1] = doc[CFG_PARAM_FORMULA_DATA]["g2"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["g3"].isNull())
|
||||
formulaData.g[2] = doc[CFG_PARAM_FORMULA_DATA]["g3"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["g4"].isNull())
|
||||
formulaData.g[3] = doc[CFG_PARAM_FORMULA_DATA]["g4"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["g5"].isNull())
|
||||
formulaData.g[4] = doc[CFG_PARAM_FORMULA_DATA]["g5"];
|
||||
|
||||
myConfig.debug();
|
||||
saveNeeded = false; // Reset save flag
|
||||
Log.notice(F("CFG : Configuration file " CFG_FILENAME " loaded." CR));
|
||||
return true;
|
||||
myConfig.debug();
|
||||
saveNeeded = false; // Reset save flag
|
||||
Log.notice(F("CFG : Configuration file " CFG_FILENAME " loaded." CR));
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Check if file system can be mounted, if not we format it.
|
||||
//
|
||||
void Config::formatFileSystem() {
|
||||
Log.notice(F("CFG : Formating filesystem." CR));
|
||||
LittleFS.format();
|
||||
Log.notice(F("CFG : Formating filesystem." CR));
|
||||
LittleFS.format();
|
||||
}
|
||||
|
||||
//
|
||||
// Check if file system can be mounted, if not we format it.
|
||||
//
|
||||
void Config::checkFileSystem() {
|
||||
#if LOG_LEVEL == 6 && !defined( CFG_DISABLE_LOGGING )
|
||||
Log.verbose(F("CFG : Checking if filesystem is valid." CR));
|
||||
#endif
|
||||
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||
Log.verbose(F("CFG : Checking if filesystem is valid." CR));
|
||||
#endif
|
||||
|
||||
if ( LittleFS.begin() ) {
|
||||
Log.notice(F("CFG : Filesystem mounted." CR));
|
||||
} else {
|
||||
Log.error(F("CFG : Unable to mount file system, formatting..." CR));
|
||||
LittleFS.format();
|
||||
}
|
||||
if (LittleFS.begin()) {
|
||||
Log.notice(F("CFG : Filesystem mounted." CR));
|
||||
} else {
|
||||
Log.error(F("CFG : Unable to mount file system, formatting..." CR));
|
||||
LittleFS.format();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Dump the configuration to the serial port
|
||||
//
|
||||
void Config::debug() {
|
||||
#if LOG_LEVEL == 6 && !defined( CFG_DISABLE_LOGGING )
|
||||
Log.verbose(F("CFG : Dumping configration " CFG_FILENAME "." CR));
|
||||
Log.verbose(F("CFG : ID; '%s'." CR), getID());
|
||||
Log.verbose(F("CFG : WIFI; '%s', '%s'." CR), getWifiSSID(), getWifiPass() );
|
||||
Log.verbose(F("CFG : mDNS; '%s'." CR), getMDNS() );
|
||||
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 : VoltageFactor; %F." CR), getVoltageFactor() );
|
||||
Log.verbose(F("CFG : Gravity formula; '%s'." CR), getGravityFormula() );
|
||||
Log.verbose(F("CFG : Gravity format; '%c'." CR), getGravityFormat() );
|
||||
Log.verbose(F("CFG : Gravity temp adj; %s." CR), isGravityTempAdj()?"true":"false" );
|
||||
Log.verbose(F("CFG : Push brewfather; '%s'." CR), getBrewfatherPushUrl() );
|
||||
Log.verbose(F("CFG : Push http; '%s'." CR), getHttpPushUrl() );
|
||||
Log.verbose(F("CFG : Push http2; '%s'." CR), getHttpPushUrl2() );
|
||||
Log.verbose(F("CFG : InfluxDb2; '%s', '%s', '%s', '%s'." CR), getInfluxDb2PushUrl(), getInfluxDb2PushOrg(),
|
||||
getInfluxDb2PushBucket(), getInfluxDb2PushToken() );
|
||||
// Log.verbose(F("CFG : Accel offset\t%d\t%d\t%d" CR), gyroCalibration.ax, gyroCalibration.ay, gyroCalibration.az );
|
||||
// Log.verbose(F("CFG : Gyro offset \t%d\t%d\t%d" CR), gyroCalibration.gx, gyroCalibration.gy, gyroCalibration.gz );
|
||||
#endif
|
||||
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||
Log.verbose(F("CFG : Dumping configration " CFG_FILENAME "." CR));
|
||||
Log.verbose(F("CFG : ID; '%s'." CR), getID());
|
||||
Log.verbose(F("CFG : WIFI; '%s', '%s'." CR), getWifiSSID(), getWifiPass());
|
||||
Log.verbose(F("CFG : mDNS; '%s'." CR), getMDNS());
|
||||
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 : VoltageFactor; %F." CR), getVoltageFactor());
|
||||
Log.verbose(F("CFG : Gravity formula; '%s'." CR), getGravityFormula());
|
||||
Log.verbose(F("CFG : Gravity format; '%c'." CR), getGravityFormat());
|
||||
Log.verbose(F("CFG : Gravity temp adj; %s." CR),
|
||||
isGravityTempAdj() ? "true" : "false");
|
||||
Log.verbose(F("CFG : Push brewfather; '%s'." CR), getBrewfatherPushUrl());
|
||||
Log.verbose(F("CFG : Push http; '%s'." CR), getHttpPushUrl());
|
||||
Log.verbose(F("CFG : Push http2; '%s'." CR), getHttpPushUrl2());
|
||||
Log.verbose(F("CFG : InfluxDb2; '%s', '%s', '%s', '%s'." CR),
|
||||
getInfluxDb2PushUrl(), getInfluxDb2PushOrg(),
|
||||
getInfluxDb2PushBucket(), getInfluxDb2PushToken());
|
||||
// Log.verbose(F("CFG : Accel offset\t%d\t%d\t%d" CR), gyroCalibration.ax,
|
||||
// gyroCalibration.ay, gyroCalibration.az ); Log.verbose(F("CFG : Gyro offset
|
||||
// \t%d\t%d\t%d" CR), gyroCalibration.gx, gyroCalibration.gy,
|
||||
// gyroCalibration.gz );
|
||||
#endif
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
230
src/config.h
230
src/config.h
@ -1,230 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_CONFIG_H_
|
||||
#define SRC_CONFIG_H_
|
||||
|
||||
// Includes
|
||||
#include <stdlib.h>
|
||||
#include "src/helper.h"
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// defintions
|
||||
#define CFG_JSON_BUFSIZE 3192
|
||||
|
||||
#define CFG_APPNAME "GravityMon " // Name of firmware
|
||||
#define CFG_FILENAME "/gravitymon.json" // Name of config file
|
||||
|
||||
#define WIFI_DEFAULT_SSID "GravityMon" // Name of created SSID
|
||||
#define WIFI_DEFAULT_PWD "password" // Password for created SSID
|
||||
#define WIFI_MDNS "gravitymon" // Prefix for MDNS name
|
||||
#define WIFI_PORTAL_TIMEOUT 120 // Number of seconds until the config portal is closed
|
||||
|
||||
// These are used in API + Savefile
|
||||
#define CFG_PARAM_ID "id"
|
||||
#define CFG_PARAM_MDNS "mdns" // Device name
|
||||
#define CFG_PARAM_OTA "ota-url" // Base URL for OTA
|
||||
#define CFG_PARAM_SSID "wifi-ssid" // WIFI
|
||||
#define CFG_PARAM_PASS "wifi-pass" // WIFI
|
||||
|
||||
#define CFG_PARAM_PUSH_BREWFATHER "brewfather-push" // URL (brewfather format)
|
||||
#define CFG_PARAM_PUSH_HTTP "http-push" // URL (iSpindle format)
|
||||
#define CFG_PARAM_PUSH_HTTP2 "http-push2" // URL (iSpindle format)
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2 "influxdb2-push" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_AUTH "influxdb2-auth" // URL
|
||||
#define CFG_PARAM_SLEEP_INTERVAL "sleep-interval" // Sleep interval
|
||||
// TODO: @deprecated setting
|
||||
#define CFG_PARAM_PUSH_INTERVAL "push-interval" // Time between push
|
||||
#define CFG_PARAM_TEMPFORMAT "temp-format" // C or F
|
||||
#define CFG_PARAM_VOLTAGEFACTOR "voltage-factor" // Factor to calculate the battery voltage
|
||||
#define CFG_PARAM_GRAVITY_FORMULA "gravity-formula" // Formula for calculating gravity
|
||||
#define CFG_PARAM_GRAVITY_FORMAT "gravity-format" // Gravity format G or P
|
||||
#define CFG_PARAM_GRAVITY_TEMP_ADJ "gravity-temp-adjustment" // True/False. Adjust gravity for temperature
|
||||
#define CFG_PARAM_TEMP_ADJ "temp-adjustment-value" // Correction value for temp sensor
|
||||
#define CFG_PARAM_GYRO_CALIBRATION "gyro-calibration-data" // READ ONLY
|
||||
|
||||
#define CFG_PARAM_FORMULA_DATA "formula-calculation-data" // Raw data for the formula calculation
|
||||
|
||||
// These are used in API's
|
||||
#define CFG_PARAM_APP_NAME "app-name"
|
||||
#define CFG_PARAM_APP_VER "app-ver"
|
||||
#define CFG_PARAM_ANGLE "angle"
|
||||
#define CFG_PARAM_GRAVITY "gravity"
|
||||
#define CFG_PARAM_TEMP_C "temp-c"
|
||||
#define CFG_PARAM_TEMP_F "temp-f"
|
||||
#define CFG_PARAM_BATTERY "battery"
|
||||
#define CFG_PARAM_SLEEP_MODE "sleep-mode"
|
||||
#define CFG_PARAM_RSSI "rssi"
|
||||
|
||||
// Used for holding sensordata or sensoroffsets
|
||||
struct RawGyroData {
|
||||
int16_t ax; // Raw Acceleration
|
||||
int16_t ay;
|
||||
int16_t az;
|
||||
|
||||
int16_t gx; // Raw Position
|
||||
int16_t gy;
|
||||
int16_t gz;
|
||||
|
||||
int16_t temp; // Only for information (temperature of chip)
|
||||
};
|
||||
|
||||
// Used for holding formulaData (used for calculating formula on device)
|
||||
struct RawFormulaData {
|
||||
double a[5];
|
||||
double g[5];
|
||||
};
|
||||
|
||||
// Main configuration class
|
||||
class Config {
|
||||
private:
|
||||
bool saveNeeded;
|
||||
|
||||
// Device configuration
|
||||
String id;
|
||||
String mDNS;
|
||||
String otaURL;
|
||||
char tempFormat; // C, F
|
||||
float voltageFactor;
|
||||
float tempSensorAdj; // This value will be added to the read sensor value
|
||||
int sleepInterval;
|
||||
|
||||
// Wifi Config
|
||||
String wifiSSID;
|
||||
String wifiPASS;
|
||||
|
||||
// Push target settings
|
||||
String brewfatherPushUrl; // URL For brewfather
|
||||
|
||||
String httpPushUrl; // URL 1 for standard http
|
||||
String httpPushUrl2; // URL 2 for standard http
|
||||
|
||||
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
|
||||
|
||||
// Gravity and temperature calculations
|
||||
String gravityFormula;
|
||||
bool gravityTempAdj; // true, false
|
||||
char gravityFormat; // G, P
|
||||
|
||||
// Gyro calibration data
|
||||
RawGyroData gyroCalibration; // Holds the gyro calibration constants (6 * int16_t)
|
||||
RawFormulaData formulaData; // Used for creating formula
|
||||
|
||||
void debug();
|
||||
void formatFileSystem();
|
||||
|
||||
public:
|
||||
Config();
|
||||
const char* getID() { return id.c_str(); }
|
||||
|
||||
const char* getMDNS() { return mDNS.c_str(); }
|
||||
void setMDNS(String s) { mDNS = s; saveNeeded = true; }
|
||||
|
||||
const char* getOtaURL() { return otaURL.c_str(); }
|
||||
void setOtaURL(String s) { otaURL = s; saveNeeded = true; }
|
||||
bool isOtaActive() { return otaURL.length() ? true : false; }
|
||||
|
||||
const char* getWifiSSID() { return wifiSSID.c_str(); }
|
||||
void setWifiSSID(String s) { wifiSSID = s; saveNeeded = true; }
|
||||
const char* getWifiPass() { return wifiPASS.c_str(); }
|
||||
void setWifiPass(String s) { wifiPASS = s; saveNeeded = true; }
|
||||
|
||||
// Brewfather
|
||||
const char* getBrewfatherPushUrl() { return brewfatherPushUrl.c_str(); }
|
||||
void setBrewfatherPushUrl(String s) { brewfatherPushUrl = s; saveNeeded = true; }
|
||||
bool isBrewfatherActive() { return brewfatherPushUrl.length()?true:false; }
|
||||
|
||||
// Standard HTTP
|
||||
const char* getHttpPushUrl() { return httpPushUrl.c_str(); }
|
||||
void setHttpPushUrl(String s) { httpPushUrl = s; saveNeeded = true; }
|
||||
bool isHttpActive() { return httpPushUrl.length()?true:false; }
|
||||
const char* getHttpPushUrl2() { return httpPushUrl2.c_str(); }
|
||||
void setHttpPushUrl2(String s) { httpPushUrl2 = s; saveNeeded = true; }
|
||||
bool isHttpActive2() { return httpPushUrl2.length()?true:false; }
|
||||
|
||||
// InfluxDB2
|
||||
const char* getInfluxDb2PushUrl() { return influxDb2Url.c_str(); }
|
||||
void setInfluxDb2PushUrl(String s) { influxDb2Url = s; saveNeeded = true; }
|
||||
bool isInfluxDb2Active() { return influxDb2Url.length()?true:false; }
|
||||
const char* getInfluxDb2PushOrg() { return influxDb2Org.c_str(); }
|
||||
void setInfluxDb2PushOrg(String s) { influxDb2Org = s; saveNeeded = true; }
|
||||
const char* getInfluxDb2PushBucket() { return influxDb2Bucket.c_str(); }
|
||||
void setInfluxDb2PushBucket(String s) { influxDb2Bucket = s; saveNeeded = true; }
|
||||
const char* getInfluxDb2PushToken() { return influxDb2Token.c_str(); }
|
||||
void setInfluxDb2PushToken(String s) { influxDb2Token = s; saveNeeded = true; }
|
||||
|
||||
int getSleepInterval() { return sleepInterval; }
|
||||
void setSleepInterval(int v) { sleepInterval = v; saveNeeded = true; }
|
||||
void setSleepInterval(String s) { sleepInterval = s.toInt(); saveNeeded = true; }
|
||||
|
||||
char getTempFormat() { return tempFormat; }
|
||||
void setTempFormat(char c) { tempFormat = c; saveNeeded = true; }
|
||||
bool isTempC() { return tempFormat == 'C' ? false : true; }
|
||||
bool isTempF() { return tempFormat == 'F' ? false : true; }
|
||||
|
||||
float getVoltageFactor() { return voltageFactor; }
|
||||
void setVoltageFactor(float f) { voltageFactor = f; saveNeeded = true; }
|
||||
void setVoltageFactor(String s) { voltageFactor = s.toFloat(); saveNeeded = true; }
|
||||
|
||||
float getTempSensorAdj() { return tempSensorAdj; }
|
||||
void setTempSensorAdj(float f) { tempSensorAdj = f; saveNeeded = true; }
|
||||
void setTempSensorAdj(String s) { tempSensorAdj = s.toFloat(); saveNeeded = true; }
|
||||
|
||||
const char* getGravityFormula() { return gravityFormula.c_str(); }
|
||||
void setGravityFormula(String s) { gravityFormula = s; saveNeeded = true; }
|
||||
|
||||
bool isGravityTempAdj() { return gravityTempAdj; }
|
||||
void setGravityTempAdj(bool b) { gravityTempAdj = b; saveNeeded = true; }
|
||||
|
||||
char getGravityFormat() { return gravityFormat; }
|
||||
void setGravityFormat(char c) { gravityFormat = c; saveNeeded = true; }
|
||||
bool isGravitySG() { return gravityFormat == 'G' ? false : true; }
|
||||
bool isGravityPlato() { return gravityFormat == 'P' ? false : true; }
|
||||
|
||||
const RawGyroData& getGyroCalibration() { return gyroCalibration; }
|
||||
void setGyroCalibration(const RawGyroData &r) { gyroCalibration = r; saveNeeded = true; }
|
||||
|
||||
const RawFormulaData& getFormulaData() { return formulaData; }
|
||||
void setFormulaData(const RawFormulaData &r) { formulaData = r; saveNeeded = true; }
|
||||
|
||||
// IO functions
|
||||
void createJson(DynamicJsonDocument& doc);
|
||||
bool saveFile();
|
||||
bool loadFile();
|
||||
void checkFileSystem();
|
||||
bool isSaveNeeded() { return saveNeeded; }
|
||||
void setSaveNeeded() { saveNeeded = true; }
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern Config myConfig;
|
||||
|
||||
#endif // SRC_CONFIG_H_
|
||||
|
||||
// EOF
|
309
src/config.hpp
Normal file
309
src/config.hpp
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_CONFIG_HPP_
|
||||
#define SRC_CONFIG_HPP_
|
||||
|
||||
// Includes
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <helper.hpp>
|
||||
|
||||
// defintions
|
||||
#define CFG_JSON_BUFSIZE 3192
|
||||
|
||||
#define CFG_APPNAME "GravityMon " // Name of firmware
|
||||
#define CFG_FILENAME "/gravitymon.json" // Name of config file
|
||||
|
||||
#define WIFI_DEFAULT_SSID "GravityMon" // Name of created SSID
|
||||
#define WIFI_DEFAULT_PWD "password" // Password for created SSID
|
||||
#define WIFI_MDNS "gravitymon" // Prefix for MDNS name
|
||||
#define WIFI_PORTAL_TIMEOUT \
|
||||
120 // Number of seconds until the config portal is closed
|
||||
|
||||
// These are used in API + Savefile
|
||||
#define CFG_PARAM_ID "id"
|
||||
#define CFG_PARAM_MDNS "mdns" // Device name
|
||||
#define CFG_PARAM_OTA "ota-url" // Base URL for OTA
|
||||
#define CFG_PARAM_SSID "wifi-ssid" // WIFI
|
||||
#define CFG_PARAM_PASS "wifi-pass" // WIFI
|
||||
|
||||
#define CFG_PARAM_PUSH_BREWFATHER "brewfather-push" // URL (brewfather format)
|
||||
#define CFG_PARAM_PUSH_HTTP "http-push" // URL (iSpindle format)
|
||||
#define CFG_PARAM_PUSH_HTTP2 "http-push2" // URL (iSpindle format)
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2 "influxdb2-push" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_AUTH "influxdb2-auth" // URL
|
||||
#define CFG_PARAM_SLEEP_INTERVAL "sleep-interval" // Sleep interval
|
||||
// TODO: @deprecated setting
|
||||
#define CFG_PARAM_PUSH_INTERVAL "push-interval" // Time between push
|
||||
#define CFG_PARAM_TEMPFORMAT "temp-format" // C or F
|
||||
#define CFG_PARAM_VOLTAGEFACTOR \
|
||||
"voltage-factor" // Factor to calculate the battery voltage
|
||||
#define CFG_PARAM_GRAVITY_FORMULA \
|
||||
"gravity-formula" // Formula for calculating gravity
|
||||
#define CFG_PARAM_GRAVITY_FORMAT "gravity-format" // Gravity format G or P
|
||||
#define CFG_PARAM_GRAVITY_TEMP_ADJ \
|
||||
"gravity-temp-adjustment" // True/False. Adjust gravity for temperature
|
||||
#define CFG_PARAM_TEMP_ADJ \
|
||||
"temp-adjustment-value" // Correction value for temp sensor
|
||||
#define CFG_PARAM_GYRO_CALIBRATION "gyro-calibration-data" // READ ONLY
|
||||
|
||||
#define CFG_PARAM_FORMULA_DATA \
|
||||
"formula-calculation-data" // Raw data for the formula calculation
|
||||
|
||||
// These are used in API's
|
||||
#define CFG_PARAM_APP_NAME "app-name"
|
||||
#define CFG_PARAM_APP_VER "app-ver"
|
||||
#define CFG_PARAM_ANGLE "angle"
|
||||
#define CFG_PARAM_GRAVITY "gravity"
|
||||
#define CFG_PARAM_TEMP_C "temp-c"
|
||||
#define CFG_PARAM_TEMP_F "temp-f"
|
||||
#define CFG_PARAM_BATTERY "battery"
|
||||
#define CFG_PARAM_SLEEP_MODE "sleep-mode"
|
||||
#define CFG_PARAM_RSSI "rssi"
|
||||
|
||||
// Used for holding sensordata or sensoroffsets
|
||||
struct RawGyroData {
|
||||
int16_t ax; // Raw Acceleration
|
||||
int16_t ay;
|
||||
int16_t az;
|
||||
|
||||
int16_t gx; // Raw Position
|
||||
int16_t gy;
|
||||
int16_t gz;
|
||||
|
||||
int16_t temp; // Only for information (temperature of chip)
|
||||
};
|
||||
|
||||
// Used for holding formulaData (used for calculating formula on device)
|
||||
struct RawFormulaData {
|
||||
double a[5];
|
||||
double g[5];
|
||||
};
|
||||
|
||||
// Main configuration class
|
||||
class Config {
|
||||
private:
|
||||
bool saveNeeded;
|
||||
|
||||
// Device configuration
|
||||
String id;
|
||||
String mDNS;
|
||||
String otaURL;
|
||||
char tempFormat; // C, F
|
||||
float voltageFactor;
|
||||
float tempSensorAdj; // This value will be added to the read sensor value
|
||||
int sleepInterval;
|
||||
|
||||
// Wifi Config
|
||||
String wifiSSID;
|
||||
String wifiPASS;
|
||||
|
||||
// Push target settings
|
||||
String brewfatherPushUrl; // URL For brewfather
|
||||
|
||||
String httpPushUrl; // URL 1 for standard http
|
||||
String httpPushUrl2; // URL 2 for standard http
|
||||
|
||||
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
|
||||
|
||||
// Gravity and temperature calculations
|
||||
String gravityFormula;
|
||||
bool gravityTempAdj; // true, false
|
||||
char gravityFormat; // G, P
|
||||
|
||||
// Gyro calibration data
|
||||
RawGyroData
|
||||
gyroCalibration; // Holds the gyro calibration constants (6 * int16_t)
|
||||
RawFormulaData formulaData; // Used for creating formula
|
||||
|
||||
void debug();
|
||||
void formatFileSystem();
|
||||
|
||||
public:
|
||||
Config();
|
||||
const char* getID() { return id.c_str(); }
|
||||
|
||||
const char* getMDNS() { return mDNS.c_str(); }
|
||||
void setMDNS(String s) {
|
||||
mDNS = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
const char* getOtaURL() { return otaURL.c_str(); }
|
||||
void setOtaURL(String s) {
|
||||
otaURL = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isOtaActive() { return otaURL.length() ? true : false; }
|
||||
|
||||
const char* getWifiSSID() { return wifiSSID.c_str(); }
|
||||
void setWifiSSID(String s) {
|
||||
wifiSSID = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
const char* getWifiPass() { return wifiPASS.c_str(); }
|
||||
void setWifiPass(String s) {
|
||||
wifiPASS = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
// Brewfather
|
||||
const char* getBrewfatherPushUrl() { return brewfatherPushUrl.c_str(); }
|
||||
void setBrewfatherPushUrl(String s) {
|
||||
brewfatherPushUrl = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isBrewfatherActive() {
|
||||
return brewfatherPushUrl.length() ? true : false;
|
||||
}
|
||||
|
||||
// Standard HTTP
|
||||
const char* getHttpPushUrl() { return httpPushUrl.c_str(); }
|
||||
void setHttpPushUrl(String s) {
|
||||
httpPushUrl = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive() { return httpPushUrl.length() ? true : false; }
|
||||
const char* getHttpPushUrl2() { return httpPushUrl2.c_str(); }
|
||||
void setHttpPushUrl2(String s) {
|
||||
httpPushUrl2 = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive2() { return httpPushUrl2.length() ? true : false; }
|
||||
|
||||
// InfluxDB2
|
||||
const char* getInfluxDb2PushUrl() { return influxDb2Url.c_str(); }
|
||||
void setInfluxDb2PushUrl(String s) {
|
||||
influxDb2Url = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isInfluxDb2Active() { return influxDb2Url.length() ? true : false; }
|
||||
const char* getInfluxDb2PushOrg() { return influxDb2Org.c_str(); }
|
||||
void setInfluxDb2PushOrg(String s) {
|
||||
influxDb2Org = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
const char* getInfluxDb2PushBucket() { return influxDb2Bucket.c_str(); }
|
||||
void setInfluxDb2PushBucket(String s) {
|
||||
influxDb2Bucket = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
const char* getInfluxDb2PushToken() { return influxDb2Token.c_str(); }
|
||||
void setInfluxDb2PushToken(String s) {
|
||||
influxDb2Token = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
int getSleepInterval() { return sleepInterval; }
|
||||
void setSleepInterval(int v) {
|
||||
sleepInterval = v;
|
||||
saveNeeded = true;
|
||||
}
|
||||
void setSleepInterval(String s) {
|
||||
sleepInterval = s.toInt();
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
char getTempFormat() { return tempFormat; }
|
||||
void setTempFormat(char c) {
|
||||
tempFormat = c;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isTempC() { return tempFormat == 'C' ? false : true; }
|
||||
bool isTempF() { return tempFormat == 'F' ? false : true; }
|
||||
|
||||
float getVoltageFactor() { return voltageFactor; }
|
||||
void setVoltageFactor(float f) {
|
||||
voltageFactor = f;
|
||||
saveNeeded = true;
|
||||
}
|
||||
void setVoltageFactor(String s) {
|
||||
voltageFactor = s.toFloat();
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
float getTempSensorAdj() { return tempSensorAdj; }
|
||||
void setTempSensorAdj(float f) {
|
||||
tempSensorAdj = f;
|
||||
saveNeeded = true;
|
||||
}
|
||||
void setTempSensorAdj(String s) {
|
||||
tempSensorAdj = s.toFloat();
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
const char* getGravityFormula() { return gravityFormula.c_str(); }
|
||||
void setGravityFormula(String s) {
|
||||
gravityFormula = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
bool isGravityTempAdj() { return gravityTempAdj; }
|
||||
void setGravityTempAdj(bool b) {
|
||||
gravityTempAdj = b;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
char getGravityFormat() { return gravityFormat; }
|
||||
void setGravityFormat(char c) {
|
||||
gravityFormat = c;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isGravitySG() { return gravityFormat == 'G' ? false : true; }
|
||||
bool isGravityPlato() { return gravityFormat == 'P' ? false : true; }
|
||||
|
||||
const RawGyroData& getGyroCalibration() { return gyroCalibration; }
|
||||
void setGyroCalibration(const RawGyroData& r) {
|
||||
gyroCalibration = r;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
const RawFormulaData& getFormulaData() { return formulaData; }
|
||||
void setFormulaData(const RawFormulaData& r) {
|
||||
formulaData = r;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
// IO functions
|
||||
void createJson(DynamicJsonDocument& doc);
|
||||
bool saveFile();
|
||||
bool loadFile();
|
||||
void checkFileSystem();
|
||||
bool isSaveNeeded() { return saveNeeded; }
|
||||
void setSaveNeeded() { saveNeeded = true; }
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern Config myConfig;
|
||||
|
||||
#endif // SRC_CONFIG_HPP_
|
||||
|
||||
// EOF
|
777
src/gyro.cpp
777
src/gyro.cpp
@ -1,368 +1,409 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 "gyro.h"
|
||||
#include "helper.h"
|
||||
|
||||
GyroSensor myGyro;
|
||||
|
||||
#define GYRO_USE_INTERRUPT // Use interrupt to detect when new sample is ready
|
||||
#define SENSOR_MOVING_THREASHOLD 500
|
||||
#define SENSOR_READ_COUNT 50
|
||||
#define SENSOR_READ_DELAY 3150 // us, empirical, to hold sampling to 200 Hz
|
||||
|
||||
#define GYRO_SHOW_MINMAX // Will calculate the min/max values when doing calibration
|
||||
//#define GYRO_CALIBRATE_STARTUP // Will calibrate sensor at startup
|
||||
|
||||
//
|
||||
// Initialize the sensor chip.
|
||||
//
|
||||
bool GyroSensor::setup() {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Setting up hardware." CR));
|
||||
#endif
|
||||
Wire.begin(D3, D4);
|
||||
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
|
||||
accelgyro.initialize();
|
||||
|
||||
if( !accelgyro.testConnection() ) {
|
||||
Log.error(F("GYRO: Failed to connect to MPU6050 (gyro)." CR));
|
||||
sensorConnected = false;
|
||||
} else {
|
||||
|
||||
#if !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR));
|
||||
#endif
|
||||
sensorConnected = true;
|
||||
|
||||
// Configure the sensor
|
||||
accelgyro.setTempSensorEnabled(true);
|
||||
//accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); // Set in .initalize()
|
||||
//accelgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_250); // Set in .initalize()
|
||||
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
||||
#if defined( GYRO_USE_INTERRUPT )
|
||||
// Alternative method to read data, let the MPU signal when sampling is done.
|
||||
accelgyro.setRate(17);
|
||||
accelgyro.setInterruptDrive(1);
|
||||
accelgyro.setInterruptMode(1);
|
||||
accelgyro.setInterruptLatch(0);
|
||||
accelgyro.setIntDataReadyEnabled(true);
|
||||
#endif
|
||||
|
||||
#if defined ( GYRO_CALIBRATE_STARTUP )
|
||||
// Run the calibration at start, useful for testing.
|
||||
calibrateSensor();
|
||||
#endif
|
||||
|
||||
// Once we have calibration values stored we just apply them from the config.
|
||||
calibrationOffset = myConfig.getGyroCalibration();
|
||||
applyCalibration();
|
||||
}
|
||||
return sensorConnected;
|
||||
}
|
||||
|
||||
//
|
||||
// Set sensor in sleep mode to conserve battery
|
||||
//
|
||||
void GyroSensor::enterSleep() {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Setting up hardware." CR));
|
||||
#endif
|
||||
accelgyro.setSleepEnabled( true );
|
||||
}
|
||||
|
||||
//
|
||||
// Do a number of reads to get a more stable value.
|
||||
//
|
||||
void GyroSensor::readSensor(RawGyroData &raw, const int noIterations, const int delayTime) {
|
||||
RawGyroDataL average = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
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;
|
||||
//accelgyro.getRotation( &min.gx, &min.gy, &min.gz );
|
||||
accelgyro.getAcceleration( &min.ax, &min.ay, &min.az );
|
||||
min.temp = accelgyro.getTemperature();
|
||||
max = min;
|
||||
#endif
|
||||
for(int cnt = 0; cnt < noIterations ; cnt ++) {
|
||||
|
||||
#if defined( GYRO_USE_INTERRUPT )
|
||||
while( accelgyro.getIntDataReadyStatus() == 0) {
|
||||
delayMicroseconds( 1 );
|
||||
}
|
||||
#endif
|
||||
|
||||
//accelgyro.getRotation( &raw.gx, &raw.gy, &raw.gz );
|
||||
//accelgyro.getAcceleration( &raw.ax, &raw.ay, &raw.az );
|
||||
accelgyro.getMotion6(&raw.ax, &raw.ay, &raw.az, &raw.gx, &raw.gy, &raw.gz);
|
||||
raw.temp = accelgyro.getTemperature();
|
||||
|
||||
average.ax += raw.ax;
|
||||
average.ay += raw.ay;
|
||||
average.az += raw.az;
|
||||
average.gx += raw.gx;
|
||||
average.gy += raw.gy;
|
||||
average.gz += raw.gz;
|
||||
average.temp += raw.temp;
|
||||
|
||||
// Log what the minium value is
|
||||
#if defined( GYRO_SHOW_MINMAX )
|
||||
if( raw.ax < min.ax ) min.ax = raw.ax;
|
||||
if( raw.ay < min.ay ) min.ay = raw.ay;
|
||||
if( raw.az < min.az ) min.az = raw.az;
|
||||
if( raw.gx < min.gx ) min.gx = raw.gx;
|
||||
if( raw.gy < min.gy ) min.gy = raw.gy;
|
||||
if( raw.gz < min.gz ) min.gz = raw.gz;
|
||||
if( raw.temp < min.temp ) min.temp = raw.temp;
|
||||
|
||||
// Log what the maximum value is
|
||||
if( raw.ax > max.ax ) max.ax = raw.ax;
|
||||
if( raw.ay > max.ay ) max.ay = raw.ay;
|
||||
if( raw.az > max.az ) max.az = raw.az;
|
||||
if( raw.gx > max.gx ) max.gx = raw.gx;
|
||||
if( raw.gy > max.gy ) max.gy = raw.gy;
|
||||
if( raw.gz > max.gz ) max.gz = raw.gz;
|
||||
if( raw.temp > max.temp ) max.temp = raw.temp;
|
||||
#endif
|
||||
|
||||
#if !defined( GYRO_USE_INTERRUPT )
|
||||
delayMicroseconds( delayTime );
|
||||
#endif
|
||||
}
|
||||
|
||||
raw.ax = average.ax/noIterations;
|
||||
raw.ay = average.ay/noIterations;
|
||||
raw.az = average.az/noIterations;
|
||||
raw.gx = average.gx/noIterations;
|
||||
raw.gy = average.gy/noIterations;
|
||||
raw.gz = average.gz/noIterations;
|
||||
raw.temp = average.temp/noIterations;
|
||||
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
#if defined( GYRO_SHOW_MINMAX )
|
||||
Log.verbose(F("GYRO: Min \t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), min.ax, min.ay, min.az, min.gx, min.gy, min.gz, min.temp );
|
||||
Log.verbose(F("GYRO: Max \t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), max.ax, max.ay, max.az, max.gx, max.gy, max.gz, max.temp );
|
||||
#endif
|
||||
Log.verbose(F("GYRO: Average\t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), raw.ax, raw.ay, raw.az, raw.gx, raw.gy, raw.gz, raw.temp );
|
||||
//Log.verbose(F("GYRO: Result \t%d\t%d\t%d\t%d\t%d\t%d." CR), average.ax/noIterations, average.ay/noIterations, average.az/noIterations,
|
||||
// average.gx/noIterations, average.gy/noIterations, average.gz/noIterations );
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Calcuate the angles (tilt)
|
||||
//
|
||||
float GyroSensor::calculateAngle(RawGyroData &raw) {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Calculating the angle." CR) );
|
||||
#endif
|
||||
|
||||
// Smooth out the readings to we can have a more stable angle/tilt.
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
// Accelerometer full scale range of +/- 2g with Sensitivity Scale Factor of 16,384 LSB(Count)/g.
|
||||
// Gyroscope full scale range of +/- 250 °/s with Sensitivity Scale Factor of 131 LSB (Count)/°/s.
|
||||
float ax = ((float) raw.ax)/16384,
|
||||
ay = ((float) raw.ay)/16384,
|
||||
az = ((float) raw.az)/16384;
|
||||
|
||||
// Source: https://www.nxp.com/docs/en/application-note/AN3461.pdf
|
||||
float v = (acos( ay / sqrt( ax*ax + ay*ay + az*az ) ) *180.0 / PI);
|
||||
//Log.notice(F("GYRO: angle = %F." CR), v );
|
||||
//double v = (acos( raw.az / sqrt( raw.ax*raw.ax + raw.ay*raw.ay + raw.az*raw.az ) ) *180.0 / PI);
|
||||
//Log.notice(F("GYRO: angle = %F." CR), v );
|
||||
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: angle = %F." CR), v );
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
|
||||
//
|
||||
// Check if the values are high that indicate that the sensor is moving.
|
||||
//
|
||||
bool GyroSensor::isSensorMoving(RawGyroData &raw) {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Checking for sensor movement." CR) );
|
||||
#endif
|
||||
|
||||
int x = abs(raw.gx), y = abs(raw.gy), z = abs(raw.gz);
|
||||
|
||||
if( x>SENSOR_MOVING_THREASHOLD || y>SENSOR_MOVING_THREASHOLD || z>SENSOR_MOVING_THREASHOLD ) {
|
||||
Log.notice(F("GYRO: Movement detected (%d)\t%d\t%d\t%d." CR), SENSOR_MOVING_THREASHOLD, x, y, z);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Read the tilt angle from the gyro.
|
||||
//
|
||||
bool GyroSensor::read() {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Getting new gyro position." CR) );
|
||||
#endif
|
||||
|
||||
if( !sensorConnected )
|
||||
return false;
|
||||
|
||||
readSensor( lastGyroData, SENSOR_READ_COUNT, SENSOR_READ_DELAY ); // Last param is unused if GYRO_USE_INTERRUPT is defined.
|
||||
|
||||
// If the sensor is unstable we return false to signal we dont have valid value
|
||||
if( isSensorMoving(lastGyroData) ) {
|
||||
#if !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.notice(F("GYRO: Sensor is moving." CR) );
|
||||
#endif
|
||||
validValue = false;
|
||||
} else {
|
||||
validValue = true;
|
||||
angle = calculateAngle( lastGyroData );
|
||||
#if !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.notice(F("GYRO: Sensor values %d,%d,%d\t%F" CR), lastGyroData.ax, lastGyroData.ay, lastGyroData.az, angle );
|
||||
#endif
|
||||
}
|
||||
|
||||
sensorTemp = ((float) lastGyroData.temp) / 340 + 36.53;
|
||||
|
||||
// The first read value is close to the DS18 value according to my tests, if more reads are
|
||||
// done then the gyro temp will increase to much
|
||||
if( initialSensorTemp == INVALID_TEMPERATURE )
|
||||
initialSensorTemp = sensorTemp;
|
||||
|
||||
return validValue;
|
||||
}
|
||||
|
||||
//
|
||||
// Dump the stored calibration values.
|
||||
//
|
||||
void GyroSensor::dumpCalibration() {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Accel offset\t%d\t%d\t%d" CR), calibrationOffset.ax, calibrationOffset.ay, calibrationOffset.az );
|
||||
Log.verbose(F("GYRO: Gyro offset \t%d\t%d\t%d" CR), calibrationOffset.gx, calibrationOffset.gy, calibrationOffset.gz );
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Update the sensor with out calculated offsets.
|
||||
//
|
||||
void GyroSensor::applyCalibration() {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Applying calibration offsets to sensor." CR) );
|
||||
#endif
|
||||
|
||||
if( ( calibrationOffset.ax + calibrationOffset.ay + calibrationOffset.az + calibrationOffset.gx + calibrationOffset.gy + calibrationOffset.gz ) == 0 ) {
|
||||
Log.error(F("GYRO: No valid calibraion values exist, aborting." CR) );
|
||||
return;
|
||||
}
|
||||
|
||||
accelgyro.setXAccelOffset( calibrationOffset.ax );
|
||||
accelgyro.setYAccelOffset( calibrationOffset.ay );
|
||||
accelgyro.setZAccelOffset( calibrationOffset.az );
|
||||
accelgyro.setXGyroOffset( calibrationOffset.gx );
|
||||
accelgyro.setYGyroOffset( calibrationOffset.gy );
|
||||
accelgyro.setZGyroOffset( calibrationOffset.gz );
|
||||
}
|
||||
|
||||
//
|
||||
// Calculate the offsets for calibration.
|
||||
//
|
||||
void GyroSensor::calibrateSensor() {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Calibrating sensor" CR) );
|
||||
#endif
|
||||
//accelgyro.PrintActiveOffsets();
|
||||
//Serial.print( CR );
|
||||
|
||||
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
||||
accelgyro.CalibrateAccel(6); // 6 = 600 readings
|
||||
accelgyro.CalibrateGyro(6);
|
||||
|
||||
accelgyro.PrintActiveOffsets();
|
||||
Serial.print( CR );
|
||||
|
||||
calibrationOffset.ax = accelgyro.getXAccelOffset();
|
||||
calibrationOffset.ay = accelgyro.getYAccelOffset();
|
||||
calibrationOffset.az = accelgyro.getZAccelOffset();
|
||||
calibrationOffset.gx = accelgyro.getXGyroOffset();
|
||||
calibrationOffset.gy = accelgyro.getYGyroOffset();
|
||||
calibrationOffset.gz = accelgyro.getZGyroOffset();
|
||||
|
||||
// Save the calibrated values
|
||||
myConfig.setGyroCalibration( calibrationOffset );
|
||||
myConfig.saveFile();
|
||||
}
|
||||
|
||||
//
|
||||
// Calibrate the device.
|
||||
//
|
||||
void GyroSensor::debug() {
|
||||
#if LOG_LEVEL==6 && !defined( GYRO_DISABLE_LOGGING )
|
||||
Log.verbose(F("GYRO: Debug - Clock src %d." CR), accelgyro.getClockSource() );
|
||||
Log.verbose(F("GYRO: Debug - Device ID %d." CR), accelgyro.getDeviceID() );
|
||||
Log.verbose(F("GYRO: Debug - DHPF Mode %d." CR), accelgyro.getDHPFMode() );
|
||||
Log.verbose(F("GYRO: Debug - DMP on %s." CR), accelgyro.getDMPEnabled()?"on":"off" );
|
||||
Log.verbose(F("GYRO: Debug - Acc range %d." CR), accelgyro.getFullScaleAccelRange() );
|
||||
Log.verbose(F("GYRO: Debug - Gyr range %d." CR), accelgyro.getFullScaleGyroRange() );
|
||||
Log.verbose(F("GYRO: Debug - Int %s." CR), accelgyro.getIntEnabled()?"on":"off" );
|
||||
Log.verbose(F("GYRO: Debug - Clock %d." CR), accelgyro.getMasterClockSpeed() );
|
||||
Log.verbose(F("GYRO: Debug - Rate %d." CR), accelgyro.getRate() );
|
||||
Log.verbose(F("GYRO: Debug - Gyro range %d." CR), accelgyro.getFullScaleGyroRange() );
|
||||
// Log.verbose(F("GYRO: Debug - I2C bypass %s." CR), accelgyro.getI2CBypassEnabled()?"on":"off" );
|
||||
// Log.verbose(F("GYRO: Debug - I2C master %s." CR), accelgyro.getI2CMasterModeEnabled()?"on":"off" );
|
||||
Log.verbose(F("GYRO: Debug - Acc FactX %d." CR), accelgyro.getAccelXSelfTestFactoryTrim() );
|
||||
Log.verbose(F("GYRO: Debug - Acc FactY %d." CR), accelgyro.getAccelYSelfTestFactoryTrim() );
|
||||
Log.verbose(F("GYRO: Debug - Acc FactZ %d." CR), accelgyro.getAccelZSelfTestFactoryTrim() );
|
||||
Log.verbose(F("GYRO: Debug - Gyr FactX %d." CR), accelgyro.getGyroXSelfTestFactoryTrim() );
|
||||
Log.verbose(F("GYRO: Debug - Gyr FactY %d." CR), accelgyro.getGyroYSelfTestFactoryTrim() );
|
||||
Log.verbose(F("GYRO: Debug - Gyr FactZ %d." CR), accelgyro.getGyroZSelfTestFactoryTrim() );
|
||||
|
||||
switch( accelgyro.getFullScaleAccelRange() ) {
|
||||
case 0:
|
||||
Log.verbose(F("GYRO: Debug - Accel range +/- 2g." CR));
|
||||
break;
|
||||
case 1:
|
||||
Log.verbose(F("GYRO: Debug - Accel range +/- 4g." CR));
|
||||
break;
|
||||
case 2:
|
||||
Log.verbose(F("GYRO: Debug - Accel range +/- 8g." CR));
|
||||
break;
|
||||
case 3:
|
||||
Log.verbose(F("GYRO: Debug - Accel range +/- 16g." CR));
|
||||
break;
|
||||
}
|
||||
|
||||
Log.verbose(F("GYRO: Debug - Acc OffX %d\t%d." CR), accelgyro.getXAccelOffset(), calibrationOffset.az );
|
||||
Log.verbose(F("GYRO: Debug - Acc OffY %d\t%d." CR), accelgyro.getYAccelOffset(), calibrationOffset.ay );
|
||||
Log.verbose(F("GYRO: Debug - Acc OffZ %d\t%d." CR), accelgyro.getZAccelOffset(), calibrationOffset.az );
|
||||
Log.verbose(F("GYRO: Debug - Gyr OffX %d\t%d." CR), accelgyro.getXGyroOffset(), calibrationOffset.gx );
|
||||
Log.verbose(F("GYRO: Debug - Gyr OffY %d\t%d." CR), accelgyro.getYGyroOffset(), calibrationOffset.gy );
|
||||
Log.verbose(F("GYRO: Debug - Gyr OffZ %d\t%d." CR), accelgyro.getZGyroOffset(), calibrationOffset.gz );
|
||||
#endif
|
||||
}
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 <gyro.hpp>
|
||||
#include <helper.hpp>
|
||||
|
||||
GyroSensor myGyro;
|
||||
|
||||
#define GYRO_USE_INTERRUPT // Use interrupt to detect when new sample is ready
|
||||
#define SENSOR_MOVING_THREASHOLD 500
|
||||
#define SENSOR_READ_COUNT 50
|
||||
#define SENSOR_READ_DELAY 3150 // us, empirical, to hold sampling to 200 Hz
|
||||
|
||||
#define GYRO_SHOW_MINMAX // Will calculate the min/max values when doing
|
||||
// calibration
|
||||
// #define GYRO_CALIBRATE_STARTUP // Will calibrate sensor at startup
|
||||
|
||||
//
|
||||
// Initialize the sensor chip.
|
||||
//
|
||||
bool GyroSensor::setup() {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Setting up hardware." CR));
|
||||
#endif
|
||||
Wire.begin(D3, D4);
|
||||
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having
|
||||
// compilation difficulties
|
||||
accelgyro.initialize();
|
||||
|
||||
if (!accelgyro.testConnection()) {
|
||||
Log.error(F("GYRO: Failed to connect to MPU6050 (gyro)." CR));
|
||||
sensorConnected = false;
|
||||
} else {
|
||||
#if !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR));
|
||||
#endif
|
||||
sensorConnected = true;
|
||||
|
||||
// Configure the sensor
|
||||
accelgyro.setTempSensorEnabled(true);
|
||||
// accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); // Set in
|
||||
// .initalize() accelgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_250); //
|
||||
// Set in .initalize()
|
||||
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
||||
#if defined(GYRO_USE_INTERRUPT)
|
||||
// Alternative method to read data, let the MPU signal when sampling is
|
||||
// done.
|
||||
accelgyro.setRate(17);
|
||||
accelgyro.setInterruptDrive(1);
|
||||
accelgyro.setInterruptMode(1);
|
||||
accelgyro.setInterruptLatch(0);
|
||||
accelgyro.setIntDataReadyEnabled(true);
|
||||
#endif
|
||||
|
||||
#if defined(GYRO_CALIBRATE_STARTUP)
|
||||
// Run the calibration at start, useful for testing.
|
||||
calibrateSensor();
|
||||
#endif
|
||||
|
||||
// Once we have calibration values stored we just apply them from the
|
||||
// config.
|
||||
calibrationOffset = myConfig.getGyroCalibration();
|
||||
applyCalibration();
|
||||
}
|
||||
return sensorConnected;
|
||||
}
|
||||
|
||||
//
|
||||
// Set sensor in sleep mode to conserve battery
|
||||
//
|
||||
void GyroSensor::enterSleep() {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Setting up hardware." CR));
|
||||
#endif
|
||||
accelgyro.setSleepEnabled(true);
|
||||
}
|
||||
|
||||
//
|
||||
// Do a number of reads to get a more stable value.
|
||||
//
|
||||
void GyroSensor::readSensor(RawGyroData &raw, const int noIterations,
|
||||
const int delayTime) {
|
||||
RawGyroDataL average = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
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;
|
||||
// accelgyro.getRotation( &min.gx, &min.gy, &min.gz );
|
||||
accelgyro.getAcceleration(&min.ax, &min.ay, &min.az);
|
||||
min.temp = accelgyro.getTemperature();
|
||||
max = min;
|
||||
#endif
|
||||
for (int cnt = 0; cnt < noIterations; cnt++) {
|
||||
#if defined(GYRO_USE_INTERRUPT)
|
||||
while (accelgyro.getIntDataReadyStatus() == 0) {
|
||||
delayMicroseconds(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// accelgyro.getRotation( &raw.gx, &raw.gy, &raw.gz );
|
||||
// accelgyro.getAcceleration( &raw.ax, &raw.ay, &raw.az );
|
||||
accelgyro.getMotion6(&raw.ax, &raw.ay, &raw.az, &raw.gx, &raw.gy, &raw.gz);
|
||||
raw.temp = accelgyro.getTemperature();
|
||||
|
||||
average.ax += raw.ax;
|
||||
average.ay += raw.ay;
|
||||
average.az += raw.az;
|
||||
average.gx += raw.gx;
|
||||
average.gy += raw.gy;
|
||||
average.gz += raw.gz;
|
||||
average.temp += raw.temp;
|
||||
|
||||
// Log what the minium value is
|
||||
#if defined(GYRO_SHOW_MINMAX)
|
||||
if (raw.ax < min.ax) min.ax = raw.ax;
|
||||
if (raw.ay < min.ay) min.ay = raw.ay;
|
||||
if (raw.az < min.az) min.az = raw.az;
|
||||
if (raw.gx < min.gx) min.gx = raw.gx;
|
||||
if (raw.gy < min.gy) min.gy = raw.gy;
|
||||
if (raw.gz < min.gz) min.gz = raw.gz;
|
||||
if (raw.temp < min.temp) min.temp = raw.temp;
|
||||
|
||||
// Log what the maximum value is
|
||||
if (raw.ax > max.ax) max.ax = raw.ax;
|
||||
if (raw.ay > max.ay) max.ay = raw.ay;
|
||||
if (raw.az > max.az) max.az = raw.az;
|
||||
if (raw.gx > max.gx) max.gx = raw.gx;
|
||||
if (raw.gy > max.gy) max.gy = raw.gy;
|
||||
if (raw.gz > max.gz) max.gz = raw.gz;
|
||||
if (raw.temp > max.temp) max.temp = raw.temp;
|
||||
#endif
|
||||
|
||||
#if !defined(GYRO_USE_INTERRUPT)
|
||||
delayMicroseconds(delayTime);
|
||||
#endif
|
||||
}
|
||||
|
||||
raw.ax = average.ax / noIterations;
|
||||
raw.ay = average.ay / noIterations;
|
||||
raw.az = average.az / noIterations;
|
||||
raw.gx = average.gx / noIterations;
|
||||
raw.gy = average.gy / noIterations;
|
||||
raw.gz = average.gz / noIterations;
|
||||
raw.temp = average.temp / noIterations;
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
#if defined(GYRO_SHOW_MINMAX)
|
||||
Log.verbose(F("GYRO: Min \t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), min.ax,
|
||||
min.ay, min.az, min.gx, min.gy, min.gz, min.temp);
|
||||
Log.verbose(F("GYRO: Max \t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), max.ax,
|
||||
max.ay, max.az, max.gx, max.gy, max.gz, max.temp);
|
||||
#endif
|
||||
Log.verbose(F("GYRO: Average\t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), raw.ax,
|
||||
raw.ay, raw.az, raw.gx, raw.gy, raw.gz, raw.temp);
|
||||
// Log.verbose(F("GYRO: Result \t%d\t%d\t%d\t%d\t%d\t%d." CR),
|
||||
// average.ax/noIterations, average.ay/noIterations, average.az/noIterations,
|
||||
// average.gx/noIterations,
|
||||
// average.gy/noIterations,
|
||||
// average.gz/noIterations
|
||||
// );
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Calcuate the angles (tilt)
|
||||
//
|
||||
float GyroSensor::calculateAngle(RawGyroData &raw) {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Calculating the angle." CR));
|
||||
#endif
|
||||
|
||||
// Smooth out the readings to we can have a more stable angle/tilt.
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
// Accelerometer full scale range of +/- 2g with Sensitivity Scale Factor of
|
||||
// 16,384 LSB(Count)/g. Gyroscope full scale range of +/- 250 °/s with
|
||||
// Sensitivity Scale Factor of 131 LSB (Count)/°/s.
|
||||
float ax = (static_cast<float>(raw.ax)) / 16384,
|
||||
ay = (static_cast<float>(raw.ay)) / 16384,
|
||||
az = (static_cast<float>(raw.az)) / 16384;
|
||||
|
||||
// Source: https://www.nxp.com/docs/en/application-note/AN3461.pdf
|
||||
float v = (acos(ay / sqrt(ax * ax + ay * ay + az * az)) * 180.0 / PI);
|
||||
// Log.notice(F("GYRO: angle = %F." CR), v );
|
||||
// double v = (acos( raw.az / sqrt( raw.ax*raw.ax + raw.ay*raw.ay +
|
||||
// raw.az*raw.az ) ) *180.0 / PI); Log.notice(F("GYRO: angle = %F." CR), v );
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: angle = %F." CR), v);
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
|
||||
//
|
||||
// Check if the values are high that indicate that the sensor is moving.
|
||||
//
|
||||
bool GyroSensor::isSensorMoving(RawGyroData &raw) {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Checking for sensor movement." CR));
|
||||
#endif
|
||||
|
||||
int x = abs(raw.gx), y = abs(raw.gy), z = abs(raw.gz);
|
||||
|
||||
if (x > SENSOR_MOVING_THREASHOLD || y > SENSOR_MOVING_THREASHOLD ||
|
||||
z > SENSOR_MOVING_THREASHOLD) {
|
||||
Log.notice(F("GYRO: Movement detected (%d)\t%d\t%d\t%d." CR),
|
||||
SENSOR_MOVING_THREASHOLD, x, y, z);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Read the tilt angle from the gyro.
|
||||
//
|
||||
bool GyroSensor::read() {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Getting new gyro position." CR));
|
||||
#endif
|
||||
|
||||
if (!sensorConnected) return false;
|
||||
|
||||
readSensor(lastGyroData, SENSOR_READ_COUNT,
|
||||
SENSOR_READ_DELAY); // Last param is unused if GYRO_USE_INTERRUPT
|
||||
// is defined.
|
||||
|
||||
// If the sensor is unstable we return false to signal we dont have valid
|
||||
// value
|
||||
if (isSensorMoving(lastGyroData)) {
|
||||
#if !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.notice(F("GYRO: Sensor is moving." CR));
|
||||
#endif
|
||||
validValue = false;
|
||||
} else {
|
||||
validValue = true;
|
||||
angle = calculateAngle(lastGyroData);
|
||||
#if !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.notice(F("GYRO: Sensor values %d,%d,%d\t%F" CR), lastGyroData.ax,
|
||||
lastGyroData.ay, lastGyroData.az, angle);
|
||||
#endif
|
||||
}
|
||||
|
||||
sensorTemp = (static_cast<float>(lastGyroData.temp)) / 340 + 36.53;
|
||||
|
||||
// The first read value is close to the DS18 value according to my tests, if
|
||||
// more reads are done then the gyro temp will increase to much
|
||||
if (initialSensorTemp == INVALID_TEMPERATURE) initialSensorTemp = sensorTemp;
|
||||
|
||||
return validValue;
|
||||
}
|
||||
|
||||
//
|
||||
// Dump the stored calibration values.
|
||||
//
|
||||
void GyroSensor::dumpCalibration() {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Accel offset\t%d\t%d\t%d" CR), calibrationOffset.ax,
|
||||
calibrationOffset.ay, calibrationOffset.az);
|
||||
Log.verbose(F("GYRO: Gyro offset \t%d\t%d\t%d" CR), calibrationOffset.gx,
|
||||
calibrationOffset.gy, calibrationOffset.gz);
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Update the sensor with out calculated offsets.
|
||||
//
|
||||
void GyroSensor::applyCalibration() {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Applying calibration offsets to sensor." CR));
|
||||
#endif
|
||||
|
||||
if ((calibrationOffset.ax + calibrationOffset.ay + calibrationOffset.az +
|
||||
calibrationOffset.gx + calibrationOffset.gy + calibrationOffset.gz) ==
|
||||
0) {
|
||||
Log.error(F("GYRO: No valid calibraion values exist, aborting." CR));
|
||||
return;
|
||||
}
|
||||
|
||||
accelgyro.setXAccelOffset(calibrationOffset.ax);
|
||||
accelgyro.setYAccelOffset(calibrationOffset.ay);
|
||||
accelgyro.setZAccelOffset(calibrationOffset.az);
|
||||
accelgyro.setXGyroOffset(calibrationOffset.gx);
|
||||
accelgyro.setYGyroOffset(calibrationOffset.gy);
|
||||
accelgyro.setZGyroOffset(calibrationOffset.gz);
|
||||
}
|
||||
|
||||
//
|
||||
// Calculate the offsets for calibration.
|
||||
//
|
||||
void GyroSensor::calibrateSensor() {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Calibrating sensor" CR));
|
||||
#endif
|
||||
// accelgyro.PrintActiveOffsets();
|
||||
// Serial.print( CR );
|
||||
|
||||
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
||||
accelgyro.CalibrateAccel(6); // 6 = 600 readings
|
||||
accelgyro.CalibrateGyro(6);
|
||||
|
||||
accelgyro.PrintActiveOffsets();
|
||||
Serial.print(CR);
|
||||
|
||||
calibrationOffset.ax = accelgyro.getXAccelOffset();
|
||||
calibrationOffset.ay = accelgyro.getYAccelOffset();
|
||||
calibrationOffset.az = accelgyro.getZAccelOffset();
|
||||
calibrationOffset.gx = accelgyro.getXGyroOffset();
|
||||
calibrationOffset.gy = accelgyro.getYGyroOffset();
|
||||
calibrationOffset.gz = accelgyro.getZGyroOffset();
|
||||
|
||||
// Save the calibrated values
|
||||
myConfig.setGyroCalibration(calibrationOffset);
|
||||
myConfig.saveFile();
|
||||
}
|
||||
|
||||
//
|
||||
// Calibrate the device.
|
||||
//
|
||||
void GyroSensor::debug() {
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Debug - Clock src %d." CR),
|
||||
accelgyro.getClockSource());
|
||||
Log.verbose(F("GYRO: Debug - Device ID %d." CR), accelgyro.getDeviceID());
|
||||
Log.verbose(F("GYRO: Debug - DHPF Mode %d." CR), accelgyro.getDHPFMode());
|
||||
Log.verbose(F("GYRO: Debug - DMP on %s." CR),
|
||||
accelgyro.getDMPEnabled() ? "on" : "off");
|
||||
Log.verbose(F("GYRO: Debug - Acc range %d." CR),
|
||||
accelgyro.getFullScaleAccelRange());
|
||||
Log.verbose(F("GYRO: Debug - Gyr range %d." CR),
|
||||
accelgyro.getFullScaleGyroRange());
|
||||
Log.verbose(F("GYRO: Debug - Int %s." CR),
|
||||
accelgyro.getIntEnabled() ? "on" : "off");
|
||||
Log.verbose(F("GYRO: Debug - Clock %d." CR),
|
||||
accelgyro.getMasterClockSpeed());
|
||||
Log.verbose(F("GYRO: Debug - Rate %d." CR), accelgyro.getRate());
|
||||
Log.verbose(F("GYRO: Debug - Gyro range %d." CR),
|
||||
accelgyro.getFullScaleGyroRange());
|
||||
// Log.verbose(F("GYRO: Debug - I2C bypass %s." CR),
|
||||
// accelgyro.getI2CBypassEnabled()?"on":"off" ); Log.verbose(F("GYRO: Debug -
|
||||
// I2C master %s." CR), accelgyro.getI2CMasterModeEnabled()?"on":"off" );
|
||||
Log.verbose(F("GYRO: Debug - Acc FactX %d." CR),
|
||||
accelgyro.getAccelXSelfTestFactoryTrim());
|
||||
Log.verbose(F("GYRO: Debug - Acc FactY %d." CR),
|
||||
accelgyro.getAccelYSelfTestFactoryTrim());
|
||||
Log.verbose(F("GYRO: Debug - Acc FactZ %d." CR),
|
||||
accelgyro.getAccelZSelfTestFactoryTrim());
|
||||
Log.verbose(F("GYRO: Debug - Gyr FactX %d." CR),
|
||||
accelgyro.getGyroXSelfTestFactoryTrim());
|
||||
Log.verbose(F("GYRO: Debug - Gyr FactY %d." CR),
|
||||
accelgyro.getGyroYSelfTestFactoryTrim());
|
||||
Log.verbose(F("GYRO: Debug - Gyr FactZ %d." CR),
|
||||
accelgyro.getGyroZSelfTestFactoryTrim());
|
||||
|
||||
switch (accelgyro.getFullScaleAccelRange()) {
|
||||
case 0:
|
||||
Log.verbose(F("GYRO: Debug - Accel range +/- 2g." CR));
|
||||
break;
|
||||
case 1:
|
||||
Log.verbose(F("GYRO: Debug - Accel range +/- 4g." CR));
|
||||
break;
|
||||
case 2:
|
||||
Log.verbose(F("GYRO: Debug - Accel range +/- 8g." CR));
|
||||
break;
|
||||
case 3:
|
||||
Log.verbose(F("GYRO: Debug - Accel range +/- 16g." CR));
|
||||
break;
|
||||
}
|
||||
|
||||
Log.verbose(F("GYRO: Debug - Acc OffX %d\t%d." CR),
|
||||
accelgyro.getXAccelOffset(), calibrationOffset.az);
|
||||
Log.verbose(F("GYRO: Debug - Acc OffY %d\t%d." CR),
|
||||
accelgyro.getYAccelOffset(), calibrationOffset.ay);
|
||||
Log.verbose(F("GYRO: Debug - Acc OffZ %d\t%d." CR),
|
||||
accelgyro.getZAccelOffset(), calibrationOffset.az);
|
||||
Log.verbose(F("GYRO: Debug - Gyr OffX %d\t%d." CR),
|
||||
accelgyro.getXGyroOffset(), calibrationOffset.gx);
|
||||
Log.verbose(F("GYRO: Debug - Gyr OffY %d\t%d." CR),
|
||||
accelgyro.getYGyroOffset(), calibrationOffset.gy);
|
||||
Log.verbose(F("GYRO: Debug - Gyr OffZ %d\t%d." CR),
|
||||
accelgyro.getZGyroOffset(), calibrationOffset.gz);
|
||||
#endif
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
87
src/gyro.h
87
src/gyro.h
@ -1,87 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef _GYRO_H
|
||||
#define _GYRO_H
|
||||
|
||||
#define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE
|
||||
//#define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_SBWIRE
|
||||
|
||||
// Includes
|
||||
#include <Arduino.h>
|
||||
#include "MPU6050.h"
|
||||
#include "config.h"
|
||||
|
||||
// Classes
|
||||
struct RawGyroDataL { // Used for average multiple readings
|
||||
long ax; // Raw Acceleration
|
||||
long ay;
|
||||
long az;
|
||||
|
||||
long gx; // Raw Position
|
||||
long gy;
|
||||
long gz;
|
||||
|
||||
long temp; // Only for information (temperature of chip)
|
||||
};
|
||||
|
||||
#define INVALID_TEMPERATURE -273
|
||||
|
||||
class GyroSensor {
|
||||
private:
|
||||
MPU6050 accelgyro;
|
||||
bool sensorConnected = false;
|
||||
bool validValue = false;
|
||||
float angle = 0;
|
||||
float sensorTemp = 0;
|
||||
float initialSensorTemp = INVALID_TEMPERATURE;
|
||||
RawGyroData calibrationOffset;
|
||||
RawGyroData lastGyroData;
|
||||
|
||||
void debug();
|
||||
void applyCalibration();
|
||||
void dumpCalibration();
|
||||
void readSensor(RawGyroData &raw, const int noIterations = 100, const int delayTime = 1);
|
||||
bool isSensorMoving(RawGyroData &raw);
|
||||
float calculateAngle(RawGyroData &raw);
|
||||
|
||||
public:
|
||||
bool setup();
|
||||
bool read();
|
||||
void calibrateSensor();
|
||||
|
||||
const RawGyroData& getLastGyroData() { return lastGyroData; }
|
||||
float getAngle() { return angle; };
|
||||
float getSensorTempC() { return sensorTemp; };
|
||||
float getInitialSensorTempC() { return initialSensorTemp; };
|
||||
bool isConnected() { return sensorConnected; };
|
||||
bool hasValue() { return validValue; };
|
||||
void enterSleep();
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern GyroSensor myGyro;
|
||||
|
||||
#endif // _GYRO_H
|
||||
|
||||
// EOF
|
89
src/gyro.hpp
Normal file
89
src/gyro.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_GYRO_HPP_
|
||||
#define SRC_GYRO_HPP_
|
||||
|
||||
#define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE
|
||||
// #define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_SBWIRE
|
||||
|
||||
// Includes
|
||||
#include <Arduino.h>
|
||||
#include <MPU6050.h>
|
||||
|
||||
#include <config.hpp>
|
||||
|
||||
// Classes
|
||||
struct RawGyroDataL { // Used for average multiple readings
|
||||
int32_t ax; // Raw Acceleration
|
||||
int32_t ay;
|
||||
int32_t az;
|
||||
|
||||
int32_t gx; // Raw Position
|
||||
int32_t gy;
|
||||
int32_t gz;
|
||||
|
||||
int32_t temp; // Only for information (temperature of chip)
|
||||
};
|
||||
|
||||
#define INVALID_TEMPERATURE -273
|
||||
|
||||
class GyroSensor {
|
||||
private:
|
||||
MPU6050 accelgyro;
|
||||
bool sensorConnected = false;
|
||||
bool validValue = false;
|
||||
float angle = 0;
|
||||
float sensorTemp = 0;
|
||||
float initialSensorTemp = INVALID_TEMPERATURE;
|
||||
RawGyroData calibrationOffset;
|
||||
RawGyroData lastGyroData;
|
||||
|
||||
void debug();
|
||||
void applyCalibration();
|
||||
void dumpCalibration();
|
||||
void readSensor(RawGyroData &raw, const int noIterations = 100,
|
||||
const int delayTime = 1);
|
||||
bool isSensorMoving(RawGyroData &raw);
|
||||
float calculateAngle(RawGyroData &raw);
|
||||
|
||||
public:
|
||||
bool setup();
|
||||
bool read();
|
||||
void calibrateSensor();
|
||||
|
||||
const RawGyroData &getLastGyroData() { return lastGyroData; }
|
||||
float getAngle() { return angle; }
|
||||
float getSensorTempC() { return sensorTemp; }
|
||||
float getInitialSensorTempC() { return initialSensorTemp; }
|
||||
bool isConnected() { return sensorConnected; }
|
||||
bool hasValue() { return validValue; }
|
||||
void enterSleep();
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern GyroSensor myGyro;
|
||||
|
||||
#endif // SRC_GYRO_HPP_
|
||||
|
||||
// EOF
|
528
src/helper.cpp
528
src/helper.cpp
@ -1,256 +1,272 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 "helper.h"
|
||||
#include "config.h"
|
||||
#include "gyro.h"
|
||||
#include "tempsensor.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
SerialDebug mySerial;
|
||||
BatteryVoltage myBatteryVoltage;
|
||||
|
||||
//
|
||||
// Print the heap information.
|
||||
//
|
||||
void printHeap() {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR), ESP.getFreeHeap()/1024, ESP.getHeapFragmentation(), ESP.getFreeSketchSpace()/1024 );
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Enter deep sleep for the defined duration (Argument is seconds)
|
||||
//
|
||||
void deepSleep(int t) {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t );
|
||||
#endif
|
||||
uint64_t wake = t * 1000000;
|
||||
ESP.deepSleep( wake );
|
||||
}
|
||||
|
||||
//
|
||||
// Print the build options used
|
||||
//
|
||||
void printBuildOptions() {
|
||||
Log.notice( F("Build options: %s LOGLEVEL %d "
|
||||
#ifdef SKIP_SLEEPMODE
|
||||
"SKIP_SLEEP "
|
||||
#endif
|
||||
#ifdef EMBED_HTML
|
||||
"EMBED_HTML "
|
||||
#endif
|
||||
#ifdef COLLECT_PERFDATA
|
||||
"PERFDATA "
|
||||
#endif
|
||||
#ifdef ACTIVATE_OTA
|
||||
"OTA "
|
||||
#endif
|
||||
CR), CFG_APPVER, LOG_LEVEL );
|
||||
}
|
||||
|
||||
//
|
||||
// Configure serial debug output
|
||||
//
|
||||
SerialDebug::SerialDebug(const long serialSpeed) {
|
||||
// Start serial with auto-detected rate (default to defined BAUD)
|
||||
Serial.flush();
|
||||
Serial.begin(serialSpeed);
|
||||
|
||||
getLog()->begin(LOG_LEVEL, &Serial, true);
|
||||
getLog()->setPrefix(printTimestamp);
|
||||
getLog()->notice(F("SDBG: Serial logging started at %l." CR), serialSpeed);
|
||||
}
|
||||
|
||||
//
|
||||
// Print the timestamp (ms since start of device)
|
||||
//
|
||||
void printTimestamp(Print* _logOutput, int _logLevel) {
|
||||
char c[12];
|
||||
sprintf(c, "%10lu ", millis());
|
||||
_logOutput->print(c);
|
||||
}
|
||||
|
||||
//
|
||||
// Read and calculate the battery voltage
|
||||
//
|
||||
void BatteryVoltage::read() {
|
||||
// The analog pin can only handle 3.3V maximum voltage so we need to reduce the voltage (from max 5V)
|
||||
float factor = myConfig.getVoltageFactor(); // Default value is 1.63
|
||||
int v = analogRead( A0 );
|
||||
batteryLevel = ((3.3/1023)*v)*factor;
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("BATT: Reading voltage level. Factor=%F Value=%d, Voltage=%F." CR), factor, v, batteryLevel );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined( COLLECT_PERFDATA )
|
||||
|
||||
PerfLogging myPerfLogging;
|
||||
|
||||
//
|
||||
// Clear the current cache
|
||||
//
|
||||
void PerfLogging::clear() {
|
||||
// Clear the measurements
|
||||
if( first == 0 )
|
||||
return;
|
||||
|
||||
PerfEntry* pe = first;
|
||||
|
||||
do {
|
||||
pe->max = 0;
|
||||
pe->start = 0;
|
||||
pe->end = 0;
|
||||
pe->mA = 0;
|
||||
pe->V = 0;
|
||||
pe = pe->next;
|
||||
} while( pe != 0 );
|
||||
}
|
||||
|
||||
//
|
||||
// Start measuring this performance point
|
||||
//
|
||||
void PerfLogging::start( const char* key ) {
|
||||
PerfEntry* pe = add( key );
|
||||
pe->start = millis();
|
||||
}
|
||||
|
||||
//
|
||||
// Finalize measuring of this performance point
|
||||
//
|
||||
void PerfLogging::stop( const char* key ) {
|
||||
PerfEntry* pe = find( key );
|
||||
|
||||
if( pe != 0 ) {
|
||||
pe->end = millis();
|
||||
|
||||
unsigned long t = pe->end - pe->start;
|
||||
|
||||
if( t > pe->max )
|
||||
pe->max = t;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Print the collected performance data
|
||||
//
|
||||
void PerfLogging::print() {
|
||||
PerfEntry* pe = first;
|
||||
|
||||
while( pe != 0 ) {
|
||||
//Log.notice( F("PERF: %s=%l ms (%l, %l)" CR), pe->key, (pe->end - pe->start), pe->start, pe->end );
|
||||
Log.notice( F("PERF: %s %lms" CR), pe->key, pe->max );
|
||||
pe = pe->next;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Push collected performance data to influx (use influx configuration)
|
||||
//
|
||||
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);
|
||||
|
||||
// Create body for influxdb2, format used
|
||||
// key,host=mdns value=0.0
|
||||
String body;
|
||||
|
||||
// Create the payload with performance data.
|
||||
// ------------------------------------------------------------------------------------------
|
||||
PerfEntry* pe = first;
|
||||
char buf[100];
|
||||
sprintf( &buf[0], "perf,host=%s,device=%s ", myConfig.getMDNS(), myConfig.getID() );
|
||||
body += &buf[0];
|
||||
|
||||
while( pe != 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;
|
||||
}
|
||||
|
||||
// Create the payload with debug data for validating sensor stability
|
||||
// ------------------------------------------------------------------------------------------
|
||||
sprintf( &buf[0], "\ndebug,host=%s,device=%s ", myConfig.getMDNS(), myConfig.getID() );
|
||||
body += &buf[0];
|
||||
sprintf( &buf[0], "angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp=%.2f", myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
||||
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az, myGyro.getSensorTempC(), myTempSensor.getTempC() );
|
||||
body += &buf[0];
|
||||
|
||||
// Log.notice(F("PERF: data %s." CR), body.c_str() );
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("PERF: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PERF: data %s." CR), body.c_str() );
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
String auth = "Token " + String( myConfig.getInfluxDb2PushToken() );
|
||||
http.addHeader(F("Authorization"), auth.c_str() );
|
||||
int httpResponseCode = http.POST(body);
|
||||
|
||||
if (httpResponseCode==204) {
|
||||
Log.notice(F("PERF: InfluxDB2 push performance data successful, response=%d" CR), httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PERF: InfluxDB2 push performance data failed, response=%d" CR), httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
#endif // COLLECT_PERFDATA
|
||||
|
||||
//
|
||||
// Convert float to formatted string with n decimals. Buffer should be at least 10 chars.
|
||||
//
|
||||
char* convertFloatToString( float f, char *buffer, int dec ) {
|
||||
dtostrf(f, 6, dec, buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//
|
||||
// Reduce precision to n decimals
|
||||
//
|
||||
float reduceFloatPrecision( float f, int dec ) {
|
||||
char buffer[5];
|
||||
dtostrf(f, 6, dec, &buffer[0]);
|
||||
return atof(&buffer[0]);
|
||||
}
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 <ESP8266HTTPClient.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
#include <config.hpp>
|
||||
#include <gyro.hpp>
|
||||
#include <helper.hpp>
|
||||
#include <tempsensor.hpp>
|
||||
|
||||
SerialDebug mySerial;
|
||||
BatteryVoltage myBatteryVoltage;
|
||||
|
||||
//
|
||||
// Print the heap information.
|
||||
//
|
||||
void printHeap() {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR),
|
||||
ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(),
|
||||
ESP.getFreeSketchSpace() / 1024);
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Enter deep sleep for the defined duration (Argument is seconds)
|
||||
//
|
||||
void deepSleep(int t) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t);
|
||||
#endif
|
||||
uint64_t wake = t * 1000000;
|
||||
ESP.deepSleep(wake);
|
||||
}
|
||||
|
||||
//
|
||||
// Print the build options used
|
||||
//
|
||||
void printBuildOptions() {
|
||||
Log.notice(F("Build options: %s LOGLEVEL %d "
|
||||
#ifdef SKIP_SLEEPMODE
|
||||
"SKIP_SLEEP "
|
||||
#endif
|
||||
#ifdef EMBED_HTML
|
||||
"EMBED_HTML "
|
||||
#endif
|
||||
#ifdef COLLECT_PERFDATA
|
||||
"PERFDATA "
|
||||
#endif
|
||||
#ifdef ACTIVATE_OTA
|
||||
"OTA "
|
||||
#endif
|
||||
CR),
|
||||
CFG_APPVER, LOG_LEVEL);
|
||||
}
|
||||
|
||||
//
|
||||
// Configure serial debug output
|
||||
//
|
||||
SerialDebug::SerialDebug(const int32 serialSpeed) {
|
||||
// Start serial with auto-detected rate (default to defined BAUD)
|
||||
Serial.flush();
|
||||
Serial.begin(serialSpeed);
|
||||
|
||||
getLog()->begin(LOG_LEVEL, &Serial, true);
|
||||
getLog()->setPrefix(printTimestamp);
|
||||
getLog()->notice(F("SDBG: Serial logging started at %l." CR), serialSpeed);
|
||||
}
|
||||
|
||||
//
|
||||
// Print the timestamp (ms since start of device)
|
||||
//
|
||||
void printTimestamp(Print* _logOutput, int _logLevel) {
|
||||
char c[12];
|
||||
snprintf(c, sizeof(c), "%10lu ", millis());
|
||||
_logOutput->print(c);
|
||||
}
|
||||
|
||||
//
|
||||
// Read and calculate the battery voltage
|
||||
//
|
||||
void BatteryVoltage::read() {
|
||||
// The analog pin can only handle 3.3V maximum voltage so we need to reduce
|
||||
// the voltage (from max 5V)
|
||||
float factor = myConfig.getVoltageFactor(); // Default value is 1.63
|
||||
int v = analogRead(A0);
|
||||
batteryLevel = ((3.3 / 1023) * v) * factor;
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(
|
||||
F("BATT: Reading voltage level. Factor=%F Value=%d, Voltage=%F." CR),
|
||||
factor, v, batteryLevel);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(COLLECT_PERFDATA)
|
||||
|
||||
PerfLogging myPerfLogging;
|
||||
|
||||
//
|
||||
// Clear the current cache
|
||||
//
|
||||
void PerfLogging::clear() {
|
||||
// Clear the measurements
|
||||
if (first == 0) return;
|
||||
|
||||
PerfEntry* pe = first;
|
||||
|
||||
do {
|
||||
pe->max = 0;
|
||||
pe->start = 0;
|
||||
pe->end = 0;
|
||||
pe->mA = 0;
|
||||
pe->V = 0;
|
||||
pe = pe->next;
|
||||
} while (pe != 0);
|
||||
}
|
||||
|
||||
//
|
||||
// Start measuring this performance point
|
||||
//
|
||||
void PerfLogging::start(const char* key) {
|
||||
PerfEntry* pe = add(key);
|
||||
pe->start = millis();
|
||||
}
|
||||
|
||||
//
|
||||
// Finalize measuring of this performance point
|
||||
//
|
||||
void PerfLogging::stop(const char* key) {
|
||||
PerfEntry* pe = find(key);
|
||||
|
||||
if (pe != 0) {
|
||||
pe->end = millis();
|
||||
|
||||
uint32_t t = pe->end - pe->start;
|
||||
|
||||
if (t > pe->max) pe->max = t;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Print the collected performance data
|
||||
//
|
||||
void PerfLogging::print() {
|
||||
PerfEntry* pe = first;
|
||||
|
||||
while (pe != 0) {
|
||||
// Log.notice( F("PERF: %s=%l ms (%l, %l)" CR), pe->key, (pe->end -
|
||||
// pe->start), pe->start, pe->end );
|
||||
Log.notice(F("PERF: %s %lms" CR), pe->key, pe->max);
|
||||
pe = pe->next;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Push collected performance data to influx (use influx configuration)
|
||||
//
|
||||
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);
|
||||
|
||||
// Create body for influxdb2, format used
|
||||
// key,host=mdns value=0.0
|
||||
String body;
|
||||
|
||||
// Create the payload with performance data.
|
||||
// ------------------------------------------------------------------------------------------
|
||||
PerfEntry* pe = first;
|
||||
char buf[100];
|
||||
snprintf(&buf[0], sizeof(buf), "perf,host=%s,device=%s ", myConfig.getMDNS(),
|
||||
myConfig.getID());
|
||||
body += &buf[0];
|
||||
|
||||
while (pe != 0) {
|
||||
if (pe->max) {
|
||||
if (pe->next)
|
||||
snprintf(&buf[0], sizeof(buf), "%s=%u,", pe->key, pe->max);
|
||||
else
|
||||
snprintf(&buf[0], sizeof(buf), "%s=%u", pe->key, pe->max);
|
||||
|
||||
body += &buf[0];
|
||||
}
|
||||
pe = pe->next;
|
||||
}
|
||||
|
||||
// Create the payload with debug data for validating sensor stability
|
||||
// ------------------------------------------------------------------------------------------
|
||||
snprintf(&buf[0], sizeof(buf), "\ndebug,host=%s,device=%s ",
|
||||
myConfig.getMDNS(), myConfig.getID());
|
||||
body += &buf[0];
|
||||
snprintf(
|
||||
&buf[0], sizeof(buf),
|
||||
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp=%.2f",
|
||||
myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
||||
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
|
||||
myGyro.getSensorTempC(), myTempSensor.getTempC());
|
||||
body += &buf[0];
|
||||
|
||||
// Log.notice(F("PERF: data %s." CR), body.c_str() );
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("PERF: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PERF: data %s." CR), body.c_str());
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
|
||||
http.addHeader(F("Authorization"), auth.c_str());
|
||||
int httpResponseCode = http.POST(body);
|
||||
|
||||
if (httpResponseCode == 204) {
|
||||
Log.notice(
|
||||
F("PERF: InfluxDB2 push performance data successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PERF: InfluxDB2 push performance data failed, response=%d" CR),
|
||||
httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
#endif // COLLECT_PERFDATA
|
||||
|
||||
//
|
||||
// Convert float to formatted string with n decimals. Buffer should be at least
|
||||
// 10 chars.
|
||||
//
|
||||
char* convertFloatToString(float f, char* buffer, int dec) {
|
||||
dtostrf(f, 6, dec, buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//
|
||||
// Reduce precision to n decimals
|
||||
//
|
||||
float reduceFloatPrecision(float f, int dec) {
|
||||
char buffer[5];
|
||||
dtostrf(f, 6, dec, &buffer[0]);
|
||||
return atof(&buffer[0]);
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
156
src/helper.h
156
src/helper.h
@ -1,156 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef _HELPER_H
|
||||
#define _HELPER_H
|
||||
|
||||
// Includes
|
||||
#include <ArduinoLog.h>
|
||||
|
||||
// Sleep mode
|
||||
void deepSleep(int t);
|
||||
|
||||
// Show build options
|
||||
void printBuildOptions();
|
||||
|
||||
// Float to String
|
||||
char* convertFloatToString( float f, char* buf, int dec = 2);
|
||||
float reduceFloatPrecision( float f, int dec = 2 );
|
||||
|
||||
// Logging via serial
|
||||
void printTimestamp(Print* _logOutput, int _logLevel);
|
||||
void printNewline(Print* _logOutput);
|
||||
void printHeap();
|
||||
|
||||
// Classes
|
||||
class SerialDebug {
|
||||
public:
|
||||
SerialDebug(const long serialSpeed = 115200L);
|
||||
static Logging* getLog() { return &Log; };
|
||||
};
|
||||
|
||||
class BatteryVoltage {
|
||||
private:
|
||||
float batteryLevel;
|
||||
|
||||
public:
|
||||
void read();
|
||||
float getVoltage() { return batteryLevel; };
|
||||
};
|
||||
|
||||
#if defined( COLLECT_PERFDATA )
|
||||
|
||||
class PerfLogging {
|
||||
private:
|
||||
struct PerfEntry {
|
||||
unsigned long start; // millis()
|
||||
unsigned long end; // millis()
|
||||
unsigned long max; // max time in ms
|
||||
const char* key; // measurement
|
||||
|
||||
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 )
|
||||
return 0;
|
||||
|
||||
PerfEntry* pe = first;
|
||||
|
||||
while( strcmp( k, pe->key ) != 0 ) {
|
||||
if( pe->next == 0 )
|
||||
return 0;
|
||||
|
||||
pe = pe->next;
|
||||
}
|
||||
return pe;
|
||||
};
|
||||
|
||||
PerfEntry* add( const char* k ) {
|
||||
if( first == 0 ) {
|
||||
first = new PerfEntry();
|
||||
first->key = k;
|
||||
first->next = 0;
|
||||
first->max = 0;
|
||||
return first;
|
||||
}
|
||||
|
||||
PerfEntry* pe = first;
|
||||
|
||||
while( strcmp( k, pe->key ) != 0 ) {
|
||||
if( pe->next == 0 ) {
|
||||
pe->next = new PerfEntry();
|
||||
pe->next->key = k;
|
||||
pe->next->max = 0;
|
||||
pe->next->next = 0;
|
||||
return pe->next;
|
||||
}
|
||||
|
||||
pe = pe->next;
|
||||
}
|
||||
|
||||
return pe;
|
||||
};
|
||||
|
||||
public:
|
||||
void clear();
|
||||
void start( const char* key );
|
||||
void stop( const char* key );
|
||||
void print();
|
||||
void pushInflux();
|
||||
};
|
||||
|
||||
extern PerfLogging myPerfLogging;
|
||||
|
||||
// Use these to collect performance data from various parts of the code
|
||||
#define LOG_PERF_START(s) myPerfLogging.start(s)
|
||||
#define LOG_PERF_STOP(s) myPerfLogging.stop(s)
|
||||
//#define LOG_PERF_PRINT() myPerfLogging.print()
|
||||
#define LOG_PERF_PRINT()
|
||||
#define LOG_PERF_CLEAR() myPerfLogging.clear()
|
||||
#define LOG_PERF_PUSH() myPerfLogging.pushInflux()
|
||||
|
||||
#else
|
||||
|
||||
// These will disable the performance collection function
|
||||
#define LOG_PERF_START(s)
|
||||
#define LOG_PERF_STOP(s)
|
||||
#define LOG_PERF_PRINT()
|
||||
#define LOG_PERF_CLEAR()
|
||||
#define LOG_PERF_PUSH()
|
||||
|
||||
#endif // COLLECT_PERFDATA
|
||||
|
||||
// Global instance created
|
||||
extern SerialDebug mySerial;
|
||||
extern BatteryVoltage myBatteryVoltage;
|
||||
|
||||
#endif // _HELPER_H
|
||||
|
||||
// EOF
|
153
src/helper.hpp
Normal file
153
src/helper.hpp
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_HELPER_HPP_
|
||||
#define SRC_HELPER_HPP_
|
||||
|
||||
// Includes
|
||||
#include <ArduinoLog.h>
|
||||
|
||||
// Sleep mode
|
||||
void deepSleep(int t);
|
||||
|
||||
// Show build options
|
||||
void printBuildOptions();
|
||||
|
||||
// Float to String
|
||||
char* convertFloatToString(float f, char* buf, int dec = 2);
|
||||
float reduceFloatPrecision(float f, int dec = 2);
|
||||
|
||||
// Logging via serial
|
||||
void printTimestamp(Print* _logOutput, int _logLevel);
|
||||
void printNewline(Print* _logOutput);
|
||||
void printHeap();
|
||||
|
||||
// Classes
|
||||
class SerialDebug {
|
||||
public:
|
||||
explicit SerialDebug(const int32 serialSpeed = 115200L);
|
||||
static Logging* getLog() { return &Log; }
|
||||
};
|
||||
|
||||
class BatteryVoltage {
|
||||
private:
|
||||
float batteryLevel;
|
||||
|
||||
public:
|
||||
void read();
|
||||
float getVoltage() { return batteryLevel; }
|
||||
};
|
||||
|
||||
#if defined(COLLECT_PERFDATA)
|
||||
|
||||
class PerfLogging {
|
||||
private:
|
||||
struct PerfEntry {
|
||||
uint32_t start; // millis()
|
||||
uint32_t end; // millis()
|
||||
uint32_t max; // max time in ms
|
||||
const char* key; // measurement
|
||||
|
||||
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) return 0;
|
||||
|
||||
PerfEntry* pe = first;
|
||||
|
||||
while (strcmp(k, pe->key) != 0) {
|
||||
if (pe->next == 0) return 0;
|
||||
pe = pe->next;
|
||||
}
|
||||
return pe;
|
||||
}
|
||||
|
||||
PerfEntry* add(const char* k) {
|
||||
if (first == 0) {
|
||||
first = new PerfEntry();
|
||||
first->key = k;
|
||||
first->next = 0;
|
||||
first->max = 0;
|
||||
return first;
|
||||
}
|
||||
|
||||
PerfEntry* pe = first;
|
||||
|
||||
while (strcmp(k, pe->key) != 0) {
|
||||
if (pe->next == 0) {
|
||||
pe->next = new PerfEntry();
|
||||
pe->next->key = k;
|
||||
pe->next->max = 0;
|
||||
pe->next->next = 0;
|
||||
return pe->next;
|
||||
}
|
||||
|
||||
pe = pe->next;
|
||||
}
|
||||
|
||||
return pe;
|
||||
}
|
||||
|
||||
public:
|
||||
void clear();
|
||||
void start(const char* key);
|
||||
void stop(const char* key);
|
||||
void print();
|
||||
void pushInflux();
|
||||
};
|
||||
|
||||
extern PerfLogging myPerfLogging;
|
||||
|
||||
// Use these to collect performance data from various parts of the code
|
||||
#define LOG_PERF_START(s) myPerfLogging.start(s)
|
||||
#define LOG_PERF_STOP(s) myPerfLogging.stop(s)
|
||||
// #define LOG_PERF_PRINT() myPerfLogging.print()
|
||||
#define LOG_PERF_PRINT()
|
||||
#define LOG_PERF_CLEAR() myPerfLogging.clear()
|
||||
#define LOG_PERF_PUSH() myPerfLogging.pushInflux()
|
||||
|
||||
#else
|
||||
|
||||
// These will disable the performance collection function
|
||||
#define LOG_PERF_START(s)
|
||||
#define LOG_PERF_STOP(s)
|
||||
#define LOG_PERF_PRINT()
|
||||
#define LOG_PERF_CLEAR()
|
||||
#define LOG_PERF_PUSH()
|
||||
|
||||
#endif // COLLECT_PERFDATA
|
||||
|
||||
// Global instance created
|
||||
extern SerialDebug mySerial;
|
||||
extern BatteryVoltage myBatteryVoltage;
|
||||
|
||||
#endif // SRC_HELPER_HPP_
|
||||
|
||||
// EOF
|
603
src/main.cpp
603
src/main.cpp
@ -1,285 +1,318 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 "helper.h"
|
||||
#include "gyro.h"
|
||||
#include "config.h"
|
||||
#include "wifi.h"
|
||||
#include "webserver.h"
|
||||
#include "calc.h"
|
||||
#include "tempsensor.h"
|
||||
#include "pushtarget.h"
|
||||
#include <LittleFS.h>
|
||||
|
||||
// Settings for double reset detector.
|
||||
#define ESP8266_DRD_USE_RTC true
|
||||
#define DRD_TIMEOUT 2
|
||||
#define DRD_ADDRESS 0
|
||||
#include <ESP_DoubleResetDetector.h>
|
||||
DoubleResetDetector *drd;
|
||||
|
||||
// Define constats for this program
|
||||
#ifdef DEACTIVATE_SLEEPMODE
|
||||
const int interval = 1000; // ms, time to wait between changes to output
|
||||
bool sleepModeAlwaysSkip = true; // Web interface can override normal behaviour
|
||||
#else
|
||||
const int interval = 200; // ms, time to wait between changes to output
|
||||
bool sleepModeAlwaysSkip = false; // Web interface can override normal behaviour
|
||||
#endif
|
||||
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
|
||||
//
|
||||
void checkSleepMode( float angle, float volt ) {
|
||||
|
||||
#if defined( SKIP_SLEEPMODE )
|
||||
sleepModeActive = false;
|
||||
Log.verbose(F("MAIN: Skipping sleep mode (SKIP_SLEEPMODE is defined)." CR) );
|
||||
return;
|
||||
#endif
|
||||
|
||||
const RawGyroData &g = myConfig.getGyroCalibration();
|
||||
|
||||
// Will not enter sleep mode if: no calibration data
|
||||
if( g.ax==0 && g.ay==0 && g.az==0 && g.gx==0 && g.gy==0 && g.gz==0 ) {
|
||||
Log.notice(F("MAIN: Missing calibration data, so forcing webserver to be active." CR) );
|
||||
sleepModeAlwaysSkip = true;
|
||||
}
|
||||
|
||||
if( sleepModeAlwaysSkip ) {
|
||||
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR) );
|
||||
sleepModeActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Will not enter sleep mode if: charger is connected
|
||||
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;
|
||||
Log.notice(F("MAIN: Deep sleep mode %s (angle=%F volt=%F)." CR), sleepModeActive ? "true":"false", angle, volt );
|
||||
}
|
||||
|
||||
//
|
||||
// Setup
|
||||
//
|
||||
void setup() {
|
||||
LOG_PERF_START("run-time");
|
||||
LOG_PERF_START("main-setup");
|
||||
runtimeMillis = millis();
|
||||
|
||||
drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
|
||||
bool dt = drd->detectDoubleReset();
|
||||
#if LOG_LEVEL==6 && !defined( MAIN_DISABLE_LOGGING )
|
||||
Log.verbose(F("Main: Reset reason %s." CR), ESP.getResetInfo().c_str() );
|
||||
#endif
|
||||
// Main startup
|
||||
Log.notice(F("Main: Started setup for %s." CR), String( ESP.getChipId(), HEX).c_str() );
|
||||
printBuildOptions();
|
||||
|
||||
LOG_PERF_START("main-config-load");
|
||||
myConfig.checkFileSystem();
|
||||
myConfig.loadFile();
|
||||
LOG_PERF_STOP("main-config-load");
|
||||
|
||||
// Setup watchdog
|
||||
ESP.wdtDisable();
|
||||
ESP.wdtEnable( interval*2 );
|
||||
|
||||
if( dt ) {
|
||||
Log.notice(F("Main: Detected doubletap on reset. Reset reason=%s" CR), ESP.getResetReason().c_str());
|
||||
}
|
||||
|
||||
#ifdef SKIP_SLEEPMODE
|
||||
// If we are running in debug more we skip this part. makes is hard to debug in case of crash/watchdog reset
|
||||
dt = false;
|
||||
#endif
|
||||
|
||||
LOG_PERF_START("main-wifi-connect");
|
||||
myWifi.connect( dt ); // This will return false if unable to connect to wifi, will be handled in loop()
|
||||
LOG_PERF_STOP("main-wifi-connect");
|
||||
|
||||
LOG_PERF_START("main-temp-setup");
|
||||
myTempSensor.setup();
|
||||
LOG_PERF_STOP("main-temp-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_START("main-gyro-read");
|
||||
myGyro.read();
|
||||
LOG_PERF_STOP("main-gyro-read");
|
||||
|
||||
LOG_PERF_START("main-batt-read");
|
||||
myBatteryVoltage.read();
|
||||
LOG_PERF_STOP("main-batt-read");
|
||||
checkSleepMode( myGyro.getAngle(), myBatteryVoltage.getVoltage() );
|
||||
|
||||
if( myWifi.isConnected() ) {
|
||||
#if defined( ACTIVATE_OTA )
|
||||
LOG_PERF_START("main-wifi-ota");
|
||||
if( !sleepModeActive && myWifi.checkFirmwareVersion() ) {
|
||||
myWifi.updateFirmware();
|
||||
}
|
||||
LOG_PERF_STOP("main-wifi-ota");
|
||||
#endif
|
||||
if( !sleepModeActive ) {
|
||||
//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-setup");
|
||||
Log.notice(F("Main: Setup completed." CR));
|
||||
stableGyroMillis = millis(); // Put it here so we dont include time for wifi connection
|
||||
}
|
||||
|
||||
//
|
||||
// Main loops
|
||||
//
|
||||
void loop() {
|
||||
drd->loop();
|
||||
|
||||
if( sleepModeActive || abs( (long) (millis() - loopMillis)) > interval ) {
|
||||
float angle = 0;
|
||||
float volt = myBatteryVoltage.getVoltage();
|
||||
//float sensorTemp = 0;
|
||||
loopCounter++;
|
||||
|
||||
#if LOG_LEVEL==6 && !defined( MAIN_DISABLE_LOGGING )
|
||||
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
|
||||
|
||||
stableGyroMillis = millis(); // Reset timer
|
||||
|
||||
LOG_PERF_START("loop-temp-read");
|
||||
float temp = myTempSensor.getTempC();
|
||||
LOG_PERF_STOP("loop-temp-read");
|
||||
|
||||
//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_START("loop-gravity-corr"); // Takes less than 2ms , so skip this measurment
|
||||
// Use default correction temperature of 20C
|
||||
float corrGravity = gravityTemperatureCorrection( gravity, temp, myConfig.getTempFormat() );
|
||||
//LOG_PERF_STOP("loop-gravity-corr");
|
||||
|
||||
#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 );
|
||||
#endif
|
||||
|
||||
// Limit the printout when sleep mode is not active.
|
||||
if( loopCounter%10 == 0 || sleepModeActive ) {
|
||||
Log.notice(F("Main: angle=%F, temp=%F, gravity=%F, corrGravity=%F, batt=%F." CR), angle, temp, gravity, corrGravity ,volt );
|
||||
}
|
||||
|
||||
LOG_PERF_START("loop-push");
|
||||
myPushTarget.send( angle, gravity, corrGravity, 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 LOG_LEVEL==6 && !defined( MAIN_DISABLE_LOGGING )
|
||||
Log.verbose(F("Main: Sleep mode not active." CR) );
|
||||
#endif
|
||||
|
||||
int sleepInterval = myConfig.getSleepInterval();
|
||||
|
||||
// If we didnt get a wifi connection, we enter sleep for a short time to conserve battery.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//
|
||||
if( !myWifi.isConnected() ) { // no connection to wifi
|
||||
Log.notice(F("MAIN: No connection to wifi established, sleeping for 60s." CR) );
|
||||
sleepInterval = 60; // 60s
|
||||
goToSleep = true;
|
||||
}
|
||||
|
||||
// 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 && !sleepModeAlwaysSkip ) {
|
||||
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"); // Takes less than 2ms , so skip this measurment
|
||||
myBatteryVoltage.read();
|
||||
//LOG_PERF_STOP("loop-batt-read");
|
||||
|
||||
loopMillis = millis();
|
||||
//#if LOG_LEVEL==6 && !defined( MAIN_DISABLE_LOGGING )
|
||||
Log.verbose(F("Main: Heap %d kb FreeSketch %d kb HeapFrag %d %%." CR), ESP.getFreeHeap()/1024, ESP.getFreeSketchSpace()/1024, ESP.getHeapFragmentation() );
|
||||
//#endif
|
||||
}
|
||||
|
||||
myWebServer.loop();
|
||||
}
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 <LittleFS.h>
|
||||
|
||||
#include <calc.hpp>
|
||||
#include <config.hpp>
|
||||
#include <gyro.hpp>
|
||||
#include <helper.hpp>
|
||||
#include <pushtarget.hpp>
|
||||
#include <tempsensor.hpp>
|
||||
#include <webserver.hpp>
|
||||
#include <wifi.hpp>
|
||||
|
||||
// Settings for double reset detector.
|
||||
#define ESP8266_DRD_USE_RTC true
|
||||
#define DRD_TIMEOUT 2
|
||||
#define DRD_ADDRESS 0
|
||||
#include <ESP_DoubleResetDetector.h>
|
||||
DoubleResetDetector *drd;
|
||||
|
||||
// Define constats for this program
|
||||
#ifdef DEACTIVATE_SLEEPMODE
|
||||
const int interval = 1000; // ms, time to wait between changes to output
|
||||
bool sleepModeAlwaysSkip = true; // Web interface can override normal behaviour
|
||||
#else
|
||||
const int interval = 200; // ms, time to wait between changes to output
|
||||
bool sleepModeAlwaysSkip =
|
||||
false; // Web interface can override normal behaviour
|
||||
#endif
|
||||
uint32_t loopMillis = 0; // Used for main loop to run the code every _interval_
|
||||
uint32_t runtimeMillis; // Used to calculate the total time since start/wakeup
|
||||
uint32_t 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
|
||||
//
|
||||
void checkSleepMode(float angle, float volt) {
|
||||
#if defined(SKIP_SLEEPMODE)
|
||||
sleepModeActive = false;
|
||||
Log.verbose(F("MAIN: Skipping sleep mode (SKIP_SLEEPMODE is defined)." CR));
|
||||
return;
|
||||
#endif
|
||||
|
||||
const RawGyroData &g = myConfig.getGyroCalibration();
|
||||
|
||||
// Will not enter sleep mode if: no calibration data
|
||||
if (g.ax == 0 && g.ay == 0 && g.az == 0 && g.gx == 0 && g.gy == 0 &&
|
||||
g.gz == 0) {
|
||||
Log.notice(
|
||||
F("MAIN: Missing calibration data, so forcing webserver to be "
|
||||
"active." CR));
|
||||
sleepModeAlwaysSkip = true;
|
||||
}
|
||||
|
||||
if (sleepModeAlwaysSkip) {
|
||||
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR));
|
||||
sleepModeActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Will not enter sleep mode if: charger is connected
|
||||
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;
|
||||
Log.notice(F("MAIN: Deep sleep mode %s (angle=%F volt=%F)." CR),
|
||||
sleepModeActive ? "true" : "false", angle, volt);
|
||||
}
|
||||
|
||||
//
|
||||
// Setup
|
||||
//
|
||||
void setup() {
|
||||
LOG_PERF_START("run-time");
|
||||
LOG_PERF_START("main-setup");
|
||||
runtimeMillis = millis();
|
||||
|
||||
drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
|
||||
bool dt = drd->detectDoubleReset();
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
Log.verbose(F("Main: Reset reason %s." CR), ESP.getResetInfo().c_str());
|
||||
#endif
|
||||
// Main startup
|
||||
Log.notice(F("Main: Started setup for %s." CR),
|
||||
String(ESP.getChipId(), HEX).c_str());
|
||||
printBuildOptions();
|
||||
|
||||
LOG_PERF_START("main-config-load");
|
||||
myConfig.checkFileSystem();
|
||||
myConfig.loadFile();
|
||||
LOG_PERF_STOP("main-config-load");
|
||||
|
||||
// Setup watchdog
|
||||
ESP.wdtDisable();
|
||||
ESP.wdtEnable(interval * 2);
|
||||
|
||||
if (dt) {
|
||||
Log.notice(F("Main: Detected doubletap on reset. Reset reason=%s" CR),
|
||||
ESP.getResetReason().c_str());
|
||||
}
|
||||
|
||||
#ifdef SKIP_SLEEPMODE
|
||||
// If we are running in debug more we skip this part. makes is hard to debug
|
||||
// in case of crash/watchdog reset
|
||||
dt = false;
|
||||
#endif
|
||||
|
||||
LOG_PERF_START("main-wifi-connect");
|
||||
myWifi.connect(dt); // This will return false if unable to connect to wifi,
|
||||
// will be handled in loop()
|
||||
LOG_PERF_STOP("main-wifi-connect");
|
||||
|
||||
LOG_PERF_START("main-temp-setup");
|
||||
myTempSensor.setup();
|
||||
LOG_PERF_STOP("main-temp-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_START("main-gyro-read");
|
||||
myGyro.read();
|
||||
LOG_PERF_STOP("main-gyro-read");
|
||||
|
||||
LOG_PERF_START("main-batt-read");
|
||||
myBatteryVoltage.read();
|
||||
LOG_PERF_STOP("main-batt-read");
|
||||
checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage());
|
||||
|
||||
if (myWifi.isConnected()) {
|
||||
#if defined(ACTIVATE_OTA)
|
||||
LOG_PERF_START("main-wifi-ota");
|
||||
if (!sleepModeActive && myWifi.checkFirmwareVersion()) {
|
||||
myWifi.updateFirmware();
|
||||
}
|
||||
LOG_PERF_STOP("main-wifi-ota");
|
||||
#endif
|
||||
if (!sleepModeActive) {
|
||||
// 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-setup");
|
||||
Log.notice(F("Main: Setup completed." CR));
|
||||
stableGyroMillis =
|
||||
millis(); // Put it here so we dont include time for wifi connection
|
||||
}
|
||||
|
||||
//
|
||||
// Main loops
|
||||
//
|
||||
void loop() {
|
||||
drd->loop();
|
||||
|
||||
if (sleepModeActive || abs((int32_t)(millis() - loopMillis)) > interval) {
|
||||
float angle = 0;
|
||||
float volt = myBatteryVoltage.getVoltage();
|
||||
// float sensorTemp = 0;
|
||||
loopCounter++;
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
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
|
||||
|
||||
stableGyroMillis = millis(); // Reset timer
|
||||
|
||||
LOG_PERF_START("loop-temp-read");
|
||||
float temp = myTempSensor.getTempC();
|
||||
LOG_PERF_STOP("loop-temp-read");
|
||||
|
||||
// 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_START("loop-gravity-corr"); // Takes less than 2ms , so skip
|
||||
// this measurment Use default correction temperature of 20C
|
||||
float corrGravity =
|
||||
gravityTemperatureCorrection(gravity, temp, myConfig.getTempFormat());
|
||||
// LOG_PERF_STOP("loop-gravity-corr");
|
||||
|
||||
#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);
|
||||
#endif
|
||||
|
||||
// Limit the printout when sleep mode is not active.
|
||||
if (loopCounter % 10 == 0 || sleepModeActive) {
|
||||
Log.notice(F("Main: angle=%F, temp=%F, gravity=%F, corrGravity=%F, "
|
||||
"batt=%F." CR),
|
||||
angle, temp, gravity, corrGravity, volt);
|
||||
}
|
||||
|
||||
LOG_PERF_START("loop-push");
|
||||
myPushTarget.send(
|
||||
angle, gravity, corrGravity, 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 LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
Log.verbose(F("Main: Sleep mode not active." CR));
|
||||
#endif
|
||||
|
||||
int sleepInterval = myConfig.getSleepInterval();
|
||||
|
||||
// If we didnt get a wifi connection, we enter sleep for a short time to
|
||||
// conserve battery.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//
|
||||
if (!myWifi.isConnected()) { // no connection to wifi
|
||||
Log.notice(
|
||||
F("MAIN: No connection to wifi established, sleeping for 60s." CR));
|
||||
sleepInterval = 60; // 60s
|
||||
goToSleep = true;
|
||||
}
|
||||
|
||||
// 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 && !sleepModeAlwaysSkip) {
|
||||
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"); // Takes less than 2ms , so skip this
|
||||
// measurment
|
||||
myBatteryVoltage.read();
|
||||
// LOG_PERF_STOP("loop-batt-read");
|
||||
|
||||
loopMillis = millis();
|
||||
// #if LOG_LEVEL==6 && !defined( MAIN_DISABLE_LOGGING )
|
||||
Log.verbose(F("Main: Heap %d kb FreeSketch %d kb HeapFrag %d %%." CR),
|
||||
ESP.getFreeHeap() / 1024, ESP.getFreeSketchSpace() / 1024,
|
||||
ESP.getHeapFragmentation());
|
||||
// #endif
|
||||
}
|
||||
|
||||
myWebServer.loop();
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -1,233 +1,258 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 "pushtarget.h"
|
||||
#include "config.h"
|
||||
#include "gyro.h" // For testing the tempsensor in the gyro
|
||||
|
||||
PushTarget myPushTarget;
|
||||
|
||||
//
|
||||
// Send the pressure value
|
||||
//
|
||||
void PushTarget::send(float angle, float gravity, float corrGravity, float temp, float runTime, bool force ) {
|
||||
unsigned long timePassed = abs( (long) (millis() - ms) );
|
||||
unsigned long interval = myConfig.getSleepInterval()*1000;
|
||||
|
||||
if( ( timePassed < interval ) && !force) {
|
||||
#if LOG_LEVEL==6 && !defined( PUSH_DISABLE_LOGGING )
|
||||
Log.verbose(F("PUSH: Timer has not expired %l vs %l." CR), timePassed, interval );
|
||||
#endif
|
||||
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 );
|
||||
LOG_PERF_STOP("push-brewfather");
|
||||
}
|
||||
|
||||
if( myConfig.isHttpActive() ) {
|
||||
LOG_PERF_START("push-http");
|
||||
sendHttp( myConfig.getHttpPushUrl(), angle, gravity, corrGravity, temp, runTime );
|
||||
LOG_PERF_STOP("push-http");
|
||||
}
|
||||
|
||||
if( myConfig.isHttpActive2() ) {
|
||||
LOG_PERF_START("push-http2");
|
||||
sendHttp( myConfig.getHttpPushUrl2(), angle, gravity, corrGravity, temp, runTime );
|
||||
LOG_PERF_STOP("push-http2");
|
||||
}
|
||||
|
||||
if( myConfig.isInfluxDb2Active() ) {
|
||||
LOG_PERF_START("push-influxdb2");
|
||||
sendInfluxDb2( angle, gravity, corrGravity, temp, runTime );
|
||||
LOG_PERF_STOP("push-influxdb2");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Send to influx db v2
|
||||
//
|
||||
void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity, float temp, 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 );
|
||||
#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);
|
||||
|
||||
// Create body for influxdb2
|
||||
char buf[1024];
|
||||
sprintf( &buf[0], "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s "
|
||||
"gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,rssi=%d,temp2=%.2f\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(), myGyro.getSensorTempC() ); // For comparing gyro tempsensor vs DSB1820
|
||||
|
||||
#if LOG_LEVEL==6 && !defined( PUSH_DISABLE_LOGGING )
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: data %s." CR), &buf[0] );
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
String auth = "Token " + String( myConfig.getInfluxDb2PushToken() );
|
||||
http.addHeader(F("Authorization"), auth.c_str() );
|
||||
int httpResponseCode = http.POST(&buf[0]);
|
||||
|
||||
if (httpResponseCode==204) {
|
||||
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR), httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: InfluxDB2 push failed, response=%d" CR), httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to brewfather
|
||||
//
|
||||
void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity, float temp ) {
|
||||
#if !defined( PUSH_DISABLE_LOGGING )
|
||||
Log.notice(F("PUSH: Sending values to brewfather angle=%F, gravity=%F, temp=%F." CR), angle, gravity, temp );
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(300);
|
||||
//
|
||||
// {
|
||||
// "name": "YourDeviceName", // Required field, this will be the ID in Brewfather
|
||||
// "temp": 20.32,
|
||||
// "aux_temp": 15.61, // Fridge Temp
|
||||
// "ext_temp": 6.51, // Room Temp
|
||||
// "temp_unit": "C", // C, F, K
|
||||
// "gravity": 1.042,
|
||||
// "gravity_unit": "G", // G, P
|
||||
// "pressure": 10,
|
||||
// "pressure_unit": "PSI", // PSI, BAR, KPA
|
||||
// "ph": 4.12,
|
||||
// "bpm": 123, // Bubbles Per Minute
|
||||
// "comment": "Hello World",
|
||||
// "beer": "Pale Ale"
|
||||
// "battery": 4.98
|
||||
// }
|
||||
//
|
||||
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 );
|
||||
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);
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
#if LOG_LEVEL==6 && !defined( PUSH_DISABLE_LOGGING )
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
http.addHeader(F("Content-Type"), F("application/json") );
|
||||
int httpResponseCode = http.POST(json);
|
||||
|
||||
if (httpResponseCode==200) {
|
||||
Log.notice(F("PUSH: Brewfather push successful, response=%d" CR), httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: Brewfather push failed, response=%d" CR), httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendHttp( String serverPath, float angle, float gravity, float corrGravity, float temp, 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 );
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(256);
|
||||
|
||||
// Use iSpindle format for compatibility
|
||||
doc["name"] = myConfig.getMDNS();
|
||||
doc["ID"] = myConfig.getID();
|
||||
doc["token"] = "gravmon";
|
||||
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 );
|
||||
doc["angle"] = reduceFloatPrecision( angle, 2);
|
||||
doc["battery"] = reduceFloatPrecision( myBatteryVoltage.getVoltage(), 2 );
|
||||
doc["rssi"] = WiFi.RSSI();
|
||||
|
||||
// Some additional information
|
||||
doc["gravity-units"] = "SG";
|
||||
doc["run-time"] = reduceFloatPrecision( runTime, 2 );
|
||||
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
|
||||
// Your Domain name with URL path or IP address with path
|
||||
http.begin( client, serverPath);
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
#if LOG_LEVEL==6 && !defined( PUSH_DISABLE_LOGGING )
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
http.addHeader(F("Content-Type"), F("application/json") );
|
||||
int httpResponseCode = http.POST(json);
|
||||
|
||||
if (httpResponseCode==200) {
|
||||
Log.notice(F("PUSH: HTTP push successful, response=%d" CR), httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: HTTP push failed, response=%d" CR), httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 <config.h>
|
||||
|
||||
#include <gyro.hpp>
|
||||
#include <pushtarget.hpp>
|
||||
|
||||
PushTarget myPushTarget;
|
||||
|
||||
//
|
||||
// Send the pressure value
|
||||
//
|
||||
void PushTarget::send(float angle, float gravity, float corrGravity, float temp,
|
||||
float runTime, bool force) {
|
||||
uint32_t timePassed = abs((int32_t)(millis() - ms));
|
||||
uint32_t interval = myConfig.getSleepInterval() * 1000;
|
||||
|
||||
if ((timePassed < interval) && !force) {
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: Timer has not expired %l vs %l." CR), timePassed,
|
||||
interval);
|
||||
#endif
|
||||
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);
|
||||
LOG_PERF_STOP("push-brewfather");
|
||||
}
|
||||
|
||||
if (myConfig.isHttpActive()) {
|
||||
LOG_PERF_START("push-http");
|
||||
sendHttp(myConfig.getHttpPushUrl(), angle, gravity, corrGravity, temp,
|
||||
runTime);
|
||||
LOG_PERF_STOP("push-http");
|
||||
}
|
||||
|
||||
if (myConfig.isHttpActive2()) {
|
||||
LOG_PERF_START("push-http2");
|
||||
sendHttp(myConfig.getHttpPushUrl2(), angle, gravity, corrGravity, temp,
|
||||
runTime);
|
||||
LOG_PERF_STOP("push-http2");
|
||||
}
|
||||
|
||||
if (myConfig.isInfluxDb2Active()) {
|
||||
LOG_PERF_START("push-influxdb2");
|
||||
sendInfluxDb2(angle, gravity, corrGravity, temp, runTime);
|
||||
LOG_PERF_STOP("push-influxdb2");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Send to influx db v2
|
||||
//
|
||||
void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity,
|
||||
float temp, 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);
|
||||
#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);
|
||||
|
||||
// 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,temp2=%.2f\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(),
|
||||
myGyro.getSensorTempC()); // For comparing gyro tempsensor vs DSB1820
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: data %s." CR), &buf[0]);
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
|
||||
http.addHeader(F("Authorization"), auth.c_str());
|
||||
int httpResponseCode = http.POST(&buf[0]);
|
||||
|
||||
if (httpResponseCode == 204) {
|
||||
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: InfluxDB2 push failed, response=%d" CR),
|
||||
httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to brewfather
|
||||
//
|
||||
void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity,
|
||||
float temp) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(
|
||||
F("PUSH: Sending values to brewfather angle=%F, gravity=%F, temp=%F." CR),
|
||||
angle, gravity, temp);
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(300);
|
||||
//
|
||||
// {
|
||||
// "name": "YourDeviceName", // Required field, this will be the ID in
|
||||
// Brewfather "temp": 20.32, "aux_temp": 15.61, // Fridge Temp
|
||||
// "ext_temp": 6.51, // Room Temp
|
||||
// "temp_unit": "C", // C, F, K
|
||||
// "gravity": 1.042,
|
||||
// "gravity_unit": "G", // G, P
|
||||
// "pressure": 10,
|
||||
// "pressure_unit": "PSI", // PSI, BAR, KPA
|
||||
// "ph": 4.12,
|
||||
// "bpm": 123, // Bubbles Per Minute
|
||||
// "comment": "Hello World",
|
||||
// "beer": "Pale Ale"
|
||||
// "battery": 4.98
|
||||
// }
|
||||
//
|
||||
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);
|
||||
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);
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
http.addHeader(F("Content-Type"), F("application/json"));
|
||||
int httpResponseCode = http.POST(json);
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
Log.notice(F("PUSH: Brewfather push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: Brewfather push failed, response=%d" CR),
|
||||
httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendHttp(String serverPath, float angle, float gravity,
|
||||
float corrGravity, float temp, 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);
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(256);
|
||||
|
||||
// Use iSpindle format for compatibility
|
||||
doc["name"] = myConfig.getMDNS();
|
||||
doc["ID"] = myConfig.getID();
|
||||
doc["token"] = "gravmon";
|
||||
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);
|
||||
doc["angle"] = reduceFloatPrecision(angle, 2);
|
||||
doc["battery"] = reduceFloatPrecision(myBatteryVoltage.getVoltage(), 2);
|
||||
doc["rssi"] = WiFi.RSSI();
|
||||
|
||||
// Some additional information
|
||||
doc["gravity-units"] = "SG";
|
||||
doc["run-time"] = reduceFloatPrecision(runTime, 2);
|
||||
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
|
||||
// Your Domain name with URL path or IP address with path
|
||||
http.begin(client, serverPath);
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
http.addHeader(F("Content-Type"), F("application/json"));
|
||||
int httpResponseCode = http.POST(json);
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
Log.notice(F("PUSH: HTTP push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: HTTP push failed, response=%d" CR), httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -1,53 +1,57 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef _PUSHTARGET_H
|
||||
#define _PUSHTARGET_H
|
||||
|
||||
// Includes
|
||||
#include "helper.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
// Classes
|
||||
class PushTarget {
|
||||
private:
|
||||
unsigned long ms; // Used to check that we do not post to often
|
||||
|
||||
void sendBrewfather(float angle, float gravity, float corrGravity, float temp );
|
||||
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 runTime);
|
||||
|
||||
public:
|
||||
PushTarget() { ms = millis(); }
|
||||
void send(float angle, float gravity, float corrGravity, float temp, float runTime, bool force = false );
|
||||
};
|
||||
|
||||
extern PushTarget myPushTarget;
|
||||
|
||||
#endif // _PUSHTARGET_H
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_PUSHTARGET_HPP_
|
||||
#define SRC_PUSHTARGET_HPP_
|
||||
|
||||
// Includes
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
#include <helper.hpp>
|
||||
|
||||
// Classes
|
||||
class PushTarget {
|
||||
private:
|
||||
uint32_t ms; // Used to check that we do not post to often
|
||||
|
||||
void sendBrewfather(float angle, float gravity, float corrGravity,
|
||||
float temp);
|
||||
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 runTime);
|
||||
|
||||
public:
|
||||
PushTarget() { ms = millis(); }
|
||||
void send(float angle, float gravity, float corrGravity, float temp,
|
||||
float runTime, bool force = false);
|
||||
};
|
||||
|
||||
extern PushTarget myPushTarget;
|
||||
|
||||
#endif // SRC_PUSHTARGET_HPP_
|
||||
|
||||
// EOF
|
@ -1,43 +1,43 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 INCBIN_OUTPUT_SECTION ".irom.text"
|
||||
#include <incbin.h>
|
||||
|
||||
#if defined( EMBED_HTML )
|
||||
|
||||
// Using minify to reduce memory usage. Reducing RAM memory usage with about 7%
|
||||
INCBIN(IndexHtm, "data/index.min.htm" );
|
||||
INCBIN(DeviceHtm, "data/device.min.htm" );
|
||||
INCBIN(ConfigHtm, "data/config.min.htm" );
|
||||
INCBIN(CalibrationHtm, "data/calibration.min.htm" );
|
||||
INCBIN(AboutHtm, "data/about.min.htm" );
|
||||
|
||||
#else
|
||||
|
||||
// Minium web interface for uploading htm files
|
||||
INCBIN(UploadHtm, "data/upload.min.htm" );
|
||||
|
||||
#endif
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 INCBIN_OUTPUT_SECTION ".irom.text"
|
||||
#include <incbin.h>
|
||||
|
||||
#if defined(EMBED_HTML)
|
||||
|
||||
// Using minify to reduce memory usage. Reducing RAM memory usage with about 7%
|
||||
INCBIN(IndexHtm, "data/index.min.htm");
|
||||
INCBIN(DeviceHtm, "data/device.min.htm");
|
||||
INCBIN(ConfigHtm, "data/config.min.htm");
|
||||
INCBIN(CalibrationHtm, "data/calibration.min.htm");
|
||||
INCBIN(AboutHtm, "data/about.min.htm");
|
||||
|
||||
#else
|
||||
|
||||
// Minium web interface for uploading htm files
|
||||
INCBIN(UploadHtm, "data/upload.min.htm");
|
||||
|
||||
#endif
|
||||
|
||||
// EOF
|
||||
|
@ -1,135 +1,135 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 "tempsensor.h"
|
||||
#include "helper.h"
|
||||
#include "config.h"
|
||||
#include "gyro.h"
|
||||
#include <onewire.h>
|
||||
#include <DallasTemperature.h>
|
||||
#include <Wire.h>
|
||||
|
||||
//
|
||||
// Conversion between C and F
|
||||
//
|
||||
float convertCtoF( float t ) {
|
||||
return (t * 1.8 ) + 32.0;
|
||||
}
|
||||
|
||||
#if !defined( USE_GYRO_TEMP )
|
||||
OneWire myOneWire(D6);
|
||||
DallasTemperature mySensors(&myOneWire);
|
||||
#define TEMPERATURE_PRECISION 9
|
||||
#endif
|
||||
|
||||
TempSensor myTempSensor;
|
||||
|
||||
//
|
||||
// Setup temp sensors
|
||||
//
|
||||
void TempSensor::setup() {
|
||||
|
||||
#if defined( SIMULATE_TEMP )
|
||||
hasSensors = true;
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if defined( USE_GYRO_TEMP )
|
||||
Log.notice(F("TSEN: Using temperature from gyro." CR));
|
||||
#else
|
||||
#if LOG_LEVEL==6 && !defined( TSEN_DISABLE_LOGGING )
|
||||
Log.verbose(F("TSEN: Looking for temp sensors." CR));
|
||||
#endif
|
||||
mySensors.begin();
|
||||
|
||||
if( mySensors.getDS18Count() ) {
|
||||
#if !defined( TSEN_DISABLE_LOGGING )
|
||||
Log.notice(F("TSEN: Found %d temperature sensor(s)." CR), mySensors.getDS18Count());
|
||||
#endif
|
||||
mySensors.setResolution(TEMPERATURE_PRECISION);
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#if LOG_LEVEL==6 && !defined( TSEN_DISABLE_LOGGING )
|
||||
Log.verbose(F("TSEN: Adjustment values for temp sensor %F C, %F F." CR), tempSensorAdjC, tempSensorAdjF );
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieving value from sensor, value is in Celcius
|
||||
//
|
||||
float TempSensor::getValue() {
|
||||
#if defined( SIMULATE_TEMP )
|
||||
return 21;
|
||||
#endif
|
||||
|
||||
#if defined( USE_GYRO_TEMP )
|
||||
// When using the gyro temperature only the first read value will be accurate so we will use this for processing.
|
||||
//LOG_PERF_START("temp-get");
|
||||
float c = myGyro.getInitialSensorTempC();
|
||||
//LOG_PERF_STOP("temp-get");
|
||||
hasSensor = true;
|
||||
return c;
|
||||
#if LOG_LEVEL==6 && !defined( TSEN_DISABLE_LOGGING )
|
||||
Log.verbose(F("TSEN: Reciving temp value for gyro sensor %F C." CR), c);
|
||||
#endif
|
||||
#else
|
||||
// If we dont have sensors just return 0
|
||||
if( !mySensors.getDS18Count() ) {
|
||||
Log.error(F("TSEN: No temperature sensors found. Skipping read." CR));
|
||||
return -273;
|
||||
}
|
||||
|
||||
// Read the sensors
|
||||
//LOG_PERF_START("temp-request");
|
||||
mySensors.requestTemperatures();
|
||||
//LOG_PERF_STOP("temp-request");
|
||||
|
||||
float c = 0;
|
||||
|
||||
if( mySensors.getDS18Count() >= 1) {
|
||||
//LOG_PERF_START("temp-get");
|
||||
c = mySensors.getTempCByIndex(0);
|
||||
//LOG_PERF_STOP("temp-get");
|
||||
|
||||
#if LOG_LEVEL==6 && !defined( TSEN_DISABLE_LOGGING )
|
||||
Log.verbose(F("TSEN: Reciving temp value for sensor %F C." CR), c);
|
||||
#endif
|
||||
hasSensor = true;
|
||||
}
|
||||
return c;
|
||||
#endif
|
||||
}
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 <DallasTemperature.h>
|
||||
#include <Wire.h>
|
||||
#include <onewire.h>
|
||||
|
||||
#include <config.hpp>
|
||||
#include <gyro.hpp>
|
||||
#include <helper.hpp>
|
||||
#include <tempsensor.hpp>
|
||||
|
||||
//
|
||||
// Conversion between C and F
|
||||
//
|
||||
float convertCtoF(float t) { return (t * 1.8) + 32.0; }
|
||||
|
||||
#if !defined(USE_GYRO_TEMP)
|
||||
OneWire myOneWire(D6);
|
||||
DallasTemperature mySensors(&myOneWire);
|
||||
#define TEMPERATURE_PRECISION 9
|
||||
#endif
|
||||
|
||||
TempSensor myTempSensor;
|
||||
|
||||
//
|
||||
// Setup temp sensors
|
||||
//
|
||||
void TempSensor::setup() {
|
||||
#if defined(SIMULATE_TEMP)
|
||||
hasSensors = true;
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if defined(USE_GYRO_TEMP)
|
||||
Log.notice(F("TSEN: Using temperature from gyro." CR));
|
||||
#else
|
||||
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||
Log.verbose(F("TSEN: Looking for temp sensors." CR));
|
||||
#endif
|
||||
mySensors.begin();
|
||||
|
||||
if (mySensors.getDS18Count()) {
|
||||
#if !defined(TSEN_DISABLE_LOGGING)
|
||||
Log.notice(F("TSEN: Found %d temperature sensor(s)." CR),
|
||||
mySensors.getDS18Count());
|
||||
#endif
|
||||
mySensors.setResolution(TEMPERATURE_PRECISION);
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||
Log.verbose(F("TSEN: Adjustment values for temp sensor %F C, %F F." CR),
|
||||
tempSensorAdjC, tempSensorAdjF);
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieving value from sensor, value is in Celcius
|
||||
//
|
||||
float TempSensor::getValue() {
|
||||
#if defined(SIMULATE_TEMP)
|
||||
return 21;
|
||||
#endif
|
||||
|
||||
#if defined(USE_GYRO_TEMP)
|
||||
// When using the gyro temperature only the first read value will be accurate
|
||||
// so we will use this for processing. LOG_PERF_START("temp-get");
|
||||
float c = myGyro.getInitialSensorTempC();
|
||||
// LOG_PERF_STOP("temp-get");
|
||||
hasSensor = true;
|
||||
return c;
|
||||
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||
Log.verbose(F("TSEN: Reciving temp value for gyro sensor %F C." CR), c);
|
||||
#endif
|
||||
#else
|
||||
// If we dont have sensors just return 0
|
||||
if (!mySensors.getDS18Count()) {
|
||||
Log.error(F("TSEN: No temperature sensors found. Skipping read." CR));
|
||||
return -273;
|
||||
}
|
||||
|
||||
// Read the sensors
|
||||
// LOG_PERF_START("temp-request");
|
||||
mySensors.requestTemperatures();
|
||||
// LOG_PERF_STOP("temp-request");
|
||||
|
||||
float c = 0;
|
||||
|
||||
if (mySensors.getDS18Count() >= 1) {
|
||||
// LOG_PERF_START("temp-get");
|
||||
c = mySensors.getTempCByIndex(0);
|
||||
// LOG_PERF_STOP("temp-get");
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||
Log.verbose(F("TSEN: Reciving temp value for sensor %F C." CR), c);
|
||||
#endif
|
||||
hasSensor = true;
|
||||
}
|
||||
return c;
|
||||
#endif
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -1,50 +1,50 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef _TEMPSENSOR_H
|
||||
#define _TEMPSENSOR_H
|
||||
|
||||
// definitions
|
||||
float convertCtoF( float t );
|
||||
|
||||
// classes
|
||||
class TempSensor {
|
||||
private:
|
||||
bool hasSensor = false;
|
||||
float tempSensorAdjF = 0;
|
||||
float tempSensorAdjC = 0;
|
||||
float getValue();
|
||||
|
||||
public:
|
||||
void setup();
|
||||
bool isSensorAttached() { return hasSensor; };
|
||||
float getTempC() { return getValue() + tempSensorAdjC; }
|
||||
float getTempF() { return convertCtoF(getValue()) + tempSensorAdjF; };
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern TempSensor myTempSensor;
|
||||
|
||||
#endif // _TEMPSENSOR_H
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_TEMPSENSOR_HPP_
|
||||
#define SRC_TEMPSENSOR_HPP_
|
||||
|
||||
// definitions
|
||||
float convertCtoF(float t);
|
||||
|
||||
// classes
|
||||
class TempSensor {
|
||||
private:
|
||||
bool hasSensor = false;
|
||||
float tempSensorAdjF = 0;
|
||||
float tempSensorAdjC = 0;
|
||||
float getValue();
|
||||
|
||||
public:
|
||||
void setup();
|
||||
bool isSensorAttached() { return hasSensor; }
|
||||
float getTempC() { return getValue() + tempSensorAdjC; }
|
||||
float getTempF() { return convertCtoF(getValue()) + tempSensorAdjF; }
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern TempSensor myTempSensor;
|
||||
|
||||
#endif // SRC_TEMPSENSOR_HPP_
|
||||
|
||||
// EOF
|
1369
src/webserver.cpp
1369
src/webserver.cpp
File diff suppressed because it is too large
Load Diff
100
src/webserver.h
100
src/webserver.h
@ -1,100 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef _WEBSERVER_H
|
||||
#define _WEBSERVER_H
|
||||
|
||||
// Include
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <incbin.h>
|
||||
|
||||
// Binary resouces
|
||||
#if defined( EMBED_HTML )
|
||||
INCBIN_EXTERN(IndexHtm);
|
||||
INCBIN_EXTERN(DeviceHtm);
|
||||
INCBIN_EXTERN(ConfigHtm);
|
||||
INCBIN_EXTERN(CalibrationHtm);
|
||||
INCBIN_EXTERN(AboutHtm);
|
||||
#else
|
||||
INCBIN_EXTERN(UploadHtm);
|
||||
#endif
|
||||
|
||||
// classes
|
||||
class WebServer {
|
||||
private:
|
||||
ESP8266WebServer *server = 0;
|
||||
File uploadFile;
|
||||
int lastFormulaCreateError = 0;
|
||||
|
||||
void webHandleConfig();
|
||||
void webHandleFormulaWrite();
|
||||
void webHandleFormulaRead();
|
||||
void webHandleConfigHardware();
|
||||
void webHandleConfigGravity();
|
||||
void webHandleConfigPush();
|
||||
void webHandleConfigDevice();
|
||||
void webHandleStatusSleepmode();
|
||||
void webHandleClearWIFI();
|
||||
void webHandleStatus();
|
||||
void webHandleFactoryReset();
|
||||
void webHandleCalibrate();
|
||||
void webHandleUploadFile();
|
||||
void webHandleUpload();
|
||||
void webHandleDevice();
|
||||
void webHandlePageNotFound();
|
||||
|
||||
// Inline functions.
|
||||
void webReturnOK() { server->send(200); }
|
||||
#if defined( EMBED_HTML )
|
||||
void webReturnIndexHtm() { server->send_P(200, "text/html", (const char*) gIndexHtmData, gIndexHtmSize ); }
|
||||
void webReturnDeviceHtm() { server->send_P(200, "text/html", (const char*) gDeviceHtmData, gDeviceHtmSize ); }
|
||||
void webReturnConfigHtm() { server->send_P(200, "text/html", (const char*) gConfigHtmData, gConfigHtmSize ); }
|
||||
void webReturnCalibrationHtm() { server->send_P(200, "text/html", (const char*) gCalibrationHtmData, gCalibrationHtmSize ); }
|
||||
void webReturnAboutHtm() { server->send_P(200, "text/html", (const char*) gAboutHtmData, gAboutHtmSize ); }
|
||||
#else
|
||||
void webReturnUploadHtm() { server->send_P(200, "text/html", (const char*) gUploadHtmData, gUploadHtmSize ); }
|
||||
#endif
|
||||
|
||||
public:
|
||||
enum HtmlFile {
|
||||
HTML_INDEX = 0,
|
||||
HTML_DEVICE = 1,
|
||||
HTML_CONFIG = 2,
|
||||
HTML_ABOUT = 3,
|
||||
HTML_CALIBRATION = 4
|
||||
};
|
||||
|
||||
bool setupWebServer();
|
||||
void loop();
|
||||
bool checkHtmlFile( HtmlFile item );
|
||||
const char* getHtmlFileName( HtmlFile item );
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern WebServer myWebServer;
|
||||
|
||||
#endif // _WEBSERVER_H
|
||||
|
||||
// EOF
|
116
src/webserver.hpp
Normal file
116
src/webserver.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_WEBSERVER_HPP_
|
||||
#define SRC_WEBSERVER_HPP_
|
||||
|
||||
// Include
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <incbin.h>
|
||||
|
||||
// Binary resouces
|
||||
#if defined(EMBED_HTML)
|
||||
INCBIN_EXTERN(IndexHtm);
|
||||
INCBIN_EXTERN(DeviceHtm);
|
||||
INCBIN_EXTERN(ConfigHtm);
|
||||
INCBIN_EXTERN(CalibrationHtm);
|
||||
INCBIN_EXTERN(AboutHtm);
|
||||
#else
|
||||
INCBIN_EXTERN(UploadHtm);
|
||||
#endif
|
||||
|
||||
// classes
|
||||
class WebServer {
|
||||
private:
|
||||
ESP8266WebServer* server = 0;
|
||||
File uploadFile;
|
||||
int lastFormulaCreateError = 0;
|
||||
|
||||
void webHandleConfig();
|
||||
void webHandleFormulaWrite();
|
||||
void webHandleFormulaRead();
|
||||
void webHandleConfigHardware();
|
||||
void webHandleConfigGravity();
|
||||
void webHandleConfigPush();
|
||||
void webHandleConfigDevice();
|
||||
void webHandleStatusSleepmode();
|
||||
void webHandleClearWIFI();
|
||||
void webHandleStatus();
|
||||
void webHandleFactoryReset();
|
||||
void webHandleCalibrate();
|
||||
void webHandleUploadFile();
|
||||
void webHandleUpload();
|
||||
void webHandleDevice();
|
||||
void webHandlePageNotFound();
|
||||
|
||||
// Inline functions.
|
||||
void webReturnOK() { server->send(200); }
|
||||
#if defined(EMBED_HTML)
|
||||
void webReturnIndexHtm() {
|
||||
server->send_P(200, "text/html", (const char*)gIndexHtmData, gIndexHtmSize);
|
||||
}
|
||||
void webReturnDeviceHtm() {
|
||||
server->send_P(200, "text/html", (const char*)gDeviceHtmData,
|
||||
gDeviceHtmSize);
|
||||
}
|
||||
void webReturnConfigHtm() {
|
||||
server->send_P(200, "text/html", (const char*)gConfigHtmData,
|
||||
gConfigHtmSize);
|
||||
}
|
||||
void webReturnCalibrationHtm() {
|
||||
server->send_P(200, "text/html", (const char*)gCalibrationHtmData,
|
||||
gCalibrationHtmSize);
|
||||
}
|
||||
void webReturnAboutHtm() {
|
||||
server->send_P(200, "text/html", (const char*)gAboutHtmData, gAboutHtmSize);
|
||||
}
|
||||
#else
|
||||
void webReturnUploadHtm() {
|
||||
server->send_P(200, "text/html", (const char*)gUploadHtmData,
|
||||
gUploadHtmSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
enum HtmlFile {
|
||||
HTML_INDEX = 0,
|
||||
HTML_DEVICE = 1,
|
||||
HTML_CONFIG = 2,
|
||||
HTML_ABOUT = 3,
|
||||
HTML_CALIBRATION = 4
|
||||
};
|
||||
|
||||
bool setupWebServer();
|
||||
void loop();
|
||||
bool checkHtmlFile(HtmlFile item);
|
||||
const char* getHtmlFileName(HtmlFile item);
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern WebServer myWebServer;
|
||||
|
||||
#endif // SRC_WEBSERVER_HPP_
|
||||
|
||||
// EOF
|
590
src/wifi.cpp
590
src/wifi.cpp
@ -1,284 +1,306 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 "wifi.h"
|
||||
#include "config.h"
|
||||
#include "helper.h"
|
||||
#include "gyro.h"
|
||||
#include "calc.h"
|
||||
#include "tempsensor.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266httpUpdate.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <LittleFS.h>
|
||||
#include <incbin.h>
|
||||
|
||||
Wifi myWifi;
|
||||
|
||||
const char* userSSID= USER_SSID;
|
||||
const char* userPWD = USER_SSID_PWD;
|
||||
|
||||
//
|
||||
// Connect to last known access point or create one if connection is not working.
|
||||
//
|
||||
bool Wifi::connect( bool showPortal ) {
|
||||
|
||||
WiFi.persistent( true );
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
if( !strlen( myConfig.getWifiSSID() ) ) {
|
||||
Log.info(F("WIFI: No SSID seams to be stored, forcing portal to start." CR));
|
||||
showPortal = true;
|
||||
} else {
|
||||
//Log.info(F("WIFI: Using SSID=%s and %s." CR), myConfig.getWifiSSID(), myConfig.getWifiPass() );
|
||||
//Log.info(F("WIFI: Using SSID=%s and %s." CR), myConfig.getWifiSSID(), "*****" );
|
||||
}
|
||||
|
||||
if( strlen(userSSID)==0 && showPortal ) {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Connecting to WIFI via connection manager (portal=%s)." CR), showPortal?"true":"false");
|
||||
#endif
|
||||
WiFiManager myWifiManager;
|
||||
Log.notice(F("WIFI: Starting wifi portal." CR));
|
||||
myWifiManager.setDebugOutput(true);
|
||||
myWifiManager.setClass("invert");
|
||||
myWifiManager.setConfigPortalTimeout( 120 ); // Keep it open for 120 seconds
|
||||
bool f = myWifiManager.startConfigPortal( WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD );
|
||||
if( f ) {
|
||||
//Log.notice(F("WIFI: Success got values from WIFI portal=%s,%s." CR), myWifiManager.getWiFiSSID(), myWifiManager.getWiFiPass() );
|
||||
Log.notice(F("WIFI: Success got values from WIFI portal=%s,%s." CR), myWifiManager.getWiFiSSID(), "*****" );
|
||||
myConfig.setWifiSSID( myWifiManager.getWiFiSSID() );
|
||||
myConfig.setWifiPass( myWifiManager.getWiFiPass() );
|
||||
myConfig.saveFile();
|
||||
} else {
|
||||
Log.notice(F("WIFI: Failure from WIFI portal, rebooting." CR) );
|
||||
delay(200);
|
||||
ESP.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to wifi
|
||||
int i = 0;
|
||||
|
||||
//Log.notice(F("WIFI: Connecting to WIFI, mode=%d,persistent=%d,fhy=%d ." CR), WiFi.getMode(), WiFi.getPersistent(), WiFi.getPhyMode() );
|
||||
WiFi.mode(WIFI_STA);
|
||||
if( strlen(userSSID) ) {
|
||||
Log.notice(F("WIFI: Connecting to wifi using hardcoded settings %s." CR), userSSID);
|
||||
WiFi.begin( userSSID, userPWD );
|
||||
} else {
|
||||
Log.notice(F("WIFI: Connecting to wifi using stored settings %s." CR), myConfig.getWifiSSID());
|
||||
WiFi.begin(myConfig.getWifiSSID(), myConfig.getWifiPass());
|
||||
}
|
||||
|
||||
//WiFi.printDiag(Serial);
|
||||
|
||||
while( WiFi.status() != WL_CONNECTED ) {
|
||||
delay(200);
|
||||
Serial.print( "." );
|
||||
|
||||
if( i++ > 100 ) { // Try for 20 seconds.
|
||||
Log.error(F("WIFI: Failed to connect to wifi %d, aborting %s." CR), WiFi.status(), getIPAddress().c_str() );
|
||||
WiFi.disconnect();
|
||||
return connectedFlag; // Return to main that we have failed to connect.
|
||||
}
|
||||
}
|
||||
Serial.print( CR );
|
||||
connectedFlag = true;
|
||||
Log.notice(F("WIFI: Connected to wifi ip=%s." CR), getIPAddress().c_str() );
|
||||
Log.notice(F("WIFI: Using mDNS name %s." CR), myConfig.getMDNS() );
|
||||
return connectedFlag;
|
||||
}
|
||||
|
||||
//
|
||||
// This will erase the stored credentials and forcing the WIFI manager to AP mode.
|
||||
//
|
||||
bool Wifi::disconnect() {
|
||||
Log.notice(F("WIFI: Erasing stored WIFI credentials." CR));
|
||||
// Erase WIFI credentials
|
||||
return WiFi.disconnect(true);
|
||||
}
|
||||
|
||||
#if defined( ACTIVATE_OTA )
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
bool Wifi::updateFirmware() {
|
||||
if( !newFirmware ) {
|
||||
Log.notice(F("WIFI: No newer version exist, skipping update." CR));
|
||||
return false;
|
||||
}
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Updating firmware." CR));
|
||||
#endif
|
||||
|
||||
WiFiClient client;
|
||||
String serverPath = myConfig.getOtaURL();
|
||||
serverPath += "firmware.bin";
|
||||
|
||||
HTTPUpdateResult ret = ESPhttpUpdate.update(client, serverPath);
|
||||
|
||||
switch(ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Log.error(F("WIFI: OTA update failed %d, %s." CR), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
|
||||
break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Log.notice("WIFI: OTA Update sucesfull, rebooting." );
|
||||
delay(100);
|
||||
ESP.reset();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Download and save file
|
||||
//
|
||||
void Wifi::downloadFile(const char *fname) {
|
||||
#if LOG_LEVEL==6
|
||||
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);
|
||||
int httpResponseCode = http.GET();
|
||||
|
||||
if (httpResponseCode==200) {
|
||||
File f = LittleFS.open( fname, "w" );
|
||||
http.writeToStream( &f );
|
||||
f.close();
|
||||
Log.notice(F("WIFI: Downloaded file %s." CR), fname);
|
||||
} else {
|
||||
Log.error(F("WIFI: Failed to download file, respone=%d" CR), httpResponseCode);
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Check what firmware version is available over OTA
|
||||
//
|
||||
bool Wifi::checkFirmwareVersion() {
|
||||
#if LOG_LEVEL==6
|
||||
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);
|
||||
|
||||
// Send HTTP GET request
|
||||
int httpResponseCode = http.GET();
|
||||
|
||||
if (httpResponseCode==200) {
|
||||
Log.notice(F("WIFI: Found version.json, response=%d" CR), httpResponseCode);
|
||||
|
||||
String payload = http.getString();
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Payload %s." CR), payload.c_str());
|
||||
#endif
|
||||
DynamicJsonDocument ver(300);
|
||||
DeserializationError err = deserializeJson(ver, payload);
|
||||
if( err ) {
|
||||
Log.error(F("WIFI: Failed to parse version.json, %s" CR), err);
|
||||
} else {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Project %s version %s." CR), (const char*) ver["project"], (const char*) ver["version"]);
|
||||
#endif
|
||||
int newVer[3];
|
||||
int curVer[3];
|
||||
|
||||
if( parseFirmwareVersionString( newVer, (const char*) ver["version"] ) ) {
|
||||
if( parseFirmwareVersionString( curVer, CFG_APPVER) ) {
|
||||
#if LOG_LEVEL==6
|
||||
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] );
|
||||
#endif
|
||||
// Compare major version
|
||||
if( newVer[0] > curVer[0] )
|
||||
newFirmware = true;
|
||||
// Compare minor version
|
||||
if( newVer[0] == curVer[0] && newVer[1] > curVer[1] )
|
||||
newFirmware = true;
|
||||
// Compare patch version
|
||||
if( newVer[0] == curVer[0] && newVer[1] == curVer[1] && newVer[2] > curVer[2] )
|
||||
newFirmware = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Download new html files to filesystem if they are present.
|
||||
if( !ver["html"].isNull() && newFirmware ) {
|
||||
Log.notice(F("WIFI: OTA downloading new html files." CR));
|
||||
JsonArray htmlFiles = ver["html"].as<JsonArray>();
|
||||
for(JsonVariant v : htmlFiles) {
|
||||
String s = v;
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: OTA listed html file %s" CR), s.c_str() );
|
||||
#endif
|
||||
downloadFile( s.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.error(F("WIFI: OTA error checking version.json, response=%d" CR), httpResponseCode);
|
||||
}
|
||||
http.end();
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: OTA found new version %s." CR), newFirmware?"true":"false");
|
||||
#endif
|
||||
return newFirmware;
|
||||
}
|
||||
|
||||
//
|
||||
// Parse a version string in the format M.m.p (eg. 1.2.10)
|
||||
//
|
||||
bool Wifi::parseFirmwareVersionString( int (&num)[3], const char *version ) {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Parsing version number string %s." CR), version);
|
||||
#endif
|
||||
char temp[80];
|
||||
char *s;
|
||||
char *p = &temp[0];
|
||||
int i = 0;
|
||||
|
||||
strcpy( &temp[0], version );
|
||||
|
||||
while ((s = strtok_r(p, ".", &p)) != NULL) {
|
||||
num[i++] = atoi( s );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // ACTIVATE_OTA
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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 <ArduinoJson.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266httpUpdate.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <LittleFS.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <incbin.h>
|
||||
|
||||
#include <calc.hpp>
|
||||
#include <config.hpp>
|
||||
#include <gyro.hpp>
|
||||
#include <helper.hpp>
|
||||
#include <tempsensor.hpp>
|
||||
#include <wifi.hpp>
|
||||
|
||||
Wifi myWifi;
|
||||
|
||||
const char *userSSID = USER_SSID;
|
||||
const char *userPWD = USER_SSID_PWD;
|
||||
|
||||
//
|
||||
// Connect to last known access point or create one if connection is not
|
||||
// working.
|
||||
//
|
||||
bool Wifi::connect(bool showPortal) {
|
||||
WiFi.persistent(true);
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
if (!strlen(myConfig.getWifiSSID())) {
|
||||
Log.info(
|
||||
F("WIFI: No SSID seams to be stored, forcing portal to start." CR));
|
||||
showPortal = true;
|
||||
} else {
|
||||
// Log.info(F("WIFI: Using SSID=%s and %s." CR), myConfig.getWifiSSID(),
|
||||
// myConfig.getWifiPass()); Log.info(F("WIFI: Using SSID=%s and %s." CR),
|
||||
// myConfig.getWifiSSID(), "*****");
|
||||
}
|
||||
|
||||
if (strlen(userSSID) == 0 && showPortal) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(
|
||||
F("WIFI: Connecting to WIFI via connection manager (portal=%s)." CR),
|
||||
showPortal ? "true" : "false");
|
||||
#endif
|
||||
WiFiManager myWifiManager;
|
||||
Log.notice(F("WIFI: Starting wifi portal." CR));
|
||||
myWifiManager.setDebugOutput(true);
|
||||
myWifiManager.setClass("invert");
|
||||
myWifiManager.setConfigPortalTimeout(120); // Keep it open for 120 seconds
|
||||
bool f =
|
||||
myWifiManager.startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD);
|
||||
if (f) {
|
||||
// Log.notice(F("WIFI: Success got values from WIFI portal=%s,%s." CR),
|
||||
// myWifiManager.getWiFiSSID(), myWifiManager.getWiFiPass() );
|
||||
Log.notice(F("WIFI: Success got values from WIFI portal=%s,%s." CR),
|
||||
myWifiManager.getWiFiSSID(), "*****");
|
||||
myConfig.setWifiSSID(myWifiManager.getWiFiSSID());
|
||||
myConfig.setWifiPass(myWifiManager.getWiFiPass());
|
||||
myConfig.saveFile();
|
||||
} else {
|
||||
Log.notice(F("WIFI: Failure from WIFI portal, rebooting." CR));
|
||||
delay(200);
|
||||
ESP.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to wifi
|
||||
int i = 0;
|
||||
|
||||
// Log.notice(F("WIFI: Connecting to WIFI, mode=%d,persistent=%d,fhy=%d ."
|
||||
// CR), WiFi.getMode(), WiFi.getPersistent(), WiFi.getPhyMode() );
|
||||
WiFi.mode(WIFI_STA);
|
||||
if (strlen(userSSID)) {
|
||||
Log.notice(F("WIFI: Connecting to wifi using hardcoded settings %s." CR),
|
||||
userSSID);
|
||||
WiFi.begin(userSSID, userPWD);
|
||||
} else {
|
||||
Log.notice(F("WIFI: Connecting to wifi using stored settings %s." CR),
|
||||
myConfig.getWifiSSID());
|
||||
WiFi.begin(myConfig.getWifiSSID(), myConfig.getWifiPass());
|
||||
}
|
||||
|
||||
// WiFi.printDiag(Serial);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(200);
|
||||
Serial.print(".");
|
||||
|
||||
if (i++ > 100) { // Try for 20 seconds.
|
||||
Log.error(F("WIFI: Failed to connect to wifi %d, aborting %s." CR),
|
||||
WiFi.status(), getIPAddress().c_str());
|
||||
WiFi.disconnect();
|
||||
return connectedFlag; // Return to main that we have failed to connect.
|
||||
}
|
||||
}
|
||||
Serial.print(CR);
|
||||
connectedFlag = true;
|
||||
Log.notice(F("WIFI: Connected to wifi ip=%s." CR), getIPAddress().c_str());
|
||||
Log.notice(F("WIFI: Using mDNS name %s." CR), myConfig.getMDNS());
|
||||
return connectedFlag;
|
||||
}
|
||||
|
||||
//
|
||||
// This will erase the stored credentials and forcing the WIFI manager to AP
|
||||
// mode.
|
||||
//
|
||||
bool Wifi::disconnect() {
|
||||
Log.notice(F("WIFI: Erasing stored WIFI credentials." CR));
|
||||
// Erase WIFI credentials
|
||||
return WiFi.disconnect(true);
|
||||
}
|
||||
|
||||
#if defined(ACTIVATE_OTA)
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
bool Wifi::updateFirmware() {
|
||||
if (!newFirmware) {
|
||||
Log.notice(F("WIFI: No newer version exist, skipping update." CR));
|
||||
return false;
|
||||
}
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: Updating firmware." CR));
|
||||
#endif
|
||||
|
||||
WiFiClient client;
|
||||
String serverPath = myConfig.getOtaURL();
|
||||
serverPath += "firmware.bin";
|
||||
|
||||
HTTPUpdateResult ret = ESPhttpUpdate.update(client, serverPath);
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Log.error(F("WIFI: OTA update failed %d, %s." CR),
|
||||
ESPhttpUpdate.getLastError(),
|
||||
ESPhttpUpdate.getLastErrorString().c_str());
|
||||
break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Log.notice("WIFI: OTA Update sucesfull, rebooting.");
|
||||
delay(100);
|
||||
ESP.reset();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Download and save file
|
||||
//
|
||||
void Wifi::downloadFile(const char *fname) {
|
||||
#if LOG_LEVEL == 6
|
||||
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);
|
||||
int httpResponseCode = http.GET();
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
File f = LittleFS.open(fname, "w");
|
||||
http.writeToStream(&f);
|
||||
f.close();
|
||||
Log.notice(F("WIFI: Downloaded file %s." CR), fname);
|
||||
} else {
|
||||
Log.error(F("WIFI: Failed to download file, respone=%d" CR),
|
||||
httpResponseCode);
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Check what firmware version is available over OTA
|
||||
//
|
||||
bool Wifi::checkFirmwareVersion() {
|
||||
#if LOG_LEVEL == 6
|
||||
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);
|
||||
|
||||
// Send HTTP GET request
|
||||
int httpResponseCode = http.GET();
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
Log.notice(F("WIFI: Found version.json, response=%d" CR), httpResponseCode);
|
||||
|
||||
String payload = http.getString();
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: Payload %s." CR), payload.c_str());
|
||||
#endif
|
||||
DynamicJsonDocument ver(300);
|
||||
DeserializationError err = deserializeJson(ver, payload);
|
||||
if (err) {
|
||||
Log.error(F("WIFI: Failed to parse version.json, %s" CR), err);
|
||||
} else {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: Project %s version %s." CR),
|
||||
(const char *)ver["project"], (const char *)ver["version"]);
|
||||
#endif
|
||||
int newVer[3];
|
||||
int curVer[3];
|
||||
|
||||
if (parseFirmwareVersionString(newVer, (const char *)ver["version"])) {
|
||||
if (parseFirmwareVersionString(curVer, CFG_APPVER)) {
|
||||
#if LOG_LEVEL == 6
|
||||
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]);
|
||||
#endif
|
||||
// Compare major version
|
||||
if (newVer[0] > curVer[0]) newFirmware = true;
|
||||
// Compare minor version
|
||||
if (newVer[0] == curVer[0] && newVer[1] > curVer[1])
|
||||
newFirmware = true;
|
||||
// Compare patch version
|
||||
if (newVer[0] == curVer[0] && newVer[1] == curVer[1] &&
|
||||
newVer[2] > curVer[2])
|
||||
newFirmware = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Download new html files to filesystem if they are present.
|
||||
if (!ver["html"].isNull() && newFirmware) {
|
||||
Log.notice(F("WIFI: OTA downloading new html files." CR));
|
||||
JsonArray htmlFiles = ver["html"].as<JsonArray>();
|
||||
for (JsonVariant v : htmlFiles) {
|
||||
String s = v;
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: OTA listed html file %s" CR), s.c_str());
|
||||
#endif
|
||||
downloadFile(s.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.error(F("WIFI: OTA error checking version.json, response=%d" CR),
|
||||
httpResponseCode);
|
||||
}
|
||||
http.end();
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: OTA found new version %s." CR),
|
||||
newFirmware ? "true" : "false");
|
||||
#endif
|
||||
return newFirmware;
|
||||
}
|
||||
|
||||
//
|
||||
// Parse a version string in the format M.m.p (eg. 1.2.10)
|
||||
//
|
||||
bool Wifi::parseFirmwareVersionString(int (&num)[3], const char *version) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: Parsing version number string %s." CR), version);
|
||||
#endif
|
||||
char temp[80];
|
||||
char *s;
|
||||
char *p = &temp[0];
|
||||
int i = 0;
|
||||
|
||||
// strcpy(&temp[0], version);
|
||||
snprintf(&temp[0], sizeof(temp), "%s", version);
|
||||
|
||||
while ((s = strtok_r(p, ".", &p)) != NULL) {
|
||||
num[i++] = atoi(s);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // ACTIVATE_OTA
|
||||
|
||||
// EOF
|
||||
|
@ -1,58 +1,58 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef _WIFI_H
|
||||
#define _WIFI_H
|
||||
|
||||
// Include
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
// classes
|
||||
class Wifi {
|
||||
private:
|
||||
// WIFI
|
||||
bool connectedFlag = false;
|
||||
|
||||
// OTA
|
||||
bool newFirmware = false;
|
||||
bool parseFirmwareVersionString( int (&num)[3], const char *version );
|
||||
void downloadFile(const char *fname);
|
||||
|
||||
public:
|
||||
// WIFI
|
||||
bool connect( bool showPortal = false );
|
||||
bool disconnect();
|
||||
bool isConnected() { return connectedFlag; };
|
||||
String getIPAddress() { return WiFi.localIP().toString(); };
|
||||
|
||||
// OTA
|
||||
bool updateFirmware();
|
||||
bool checkFirmwareVersion();
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern Wifi myWifi;
|
||||
|
||||
#endif // _WIFI_H
|
||||
|
||||
// EOF
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_WIFI_HPP_
|
||||
#define SRC_WIFI_HPP_
|
||||
|
||||
// Include
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
// classes
|
||||
class Wifi {
|
||||
private:
|
||||
// WIFI
|
||||
bool connectedFlag = false;
|
||||
|
||||
// OTA
|
||||
bool newFirmware = false;
|
||||
bool parseFirmwareVersionString(int (&num)[3], const char *version);
|
||||
void downloadFile(const char *fname);
|
||||
|
||||
public:
|
||||
// WIFI
|
||||
bool connect(bool showPortal = false);
|
||||
bool disconnect();
|
||||
bool isConnected() { return connectedFlag; }
|
||||
String getIPAddress() { return WiFi.localIP().toString(); }
|
||||
|
||||
// OTA
|
||||
bool updateFirmware();
|
||||
bool checkFirmwareVersion();
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern Wifi myWifi;
|
||||
|
||||
#endif // SRC_WIFI_HPP_
|
||||
|
||||
// EOF
|
Loading…
Reference in New Issue
Block a user