Initial version for testing
This commit is contained in:
99
src/calc.cpp
Normal file
99
src/calc.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
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 "calc.h"
|
||||
#include "helper.h"
|
||||
#include "config.h"
|
||||
#include "tinyexpr.h"
|
||||
#include "tempsensor.h"
|
||||
|
||||
//#define LOG_LEVEL 5
|
||||
|
||||
//
|
||||
// Calculates gravity according to supplied formula, compatible with iSpindle/Fermentrack formula
|
||||
//
|
||||
double calculateGravity( double angle, double temp ) {
|
||||
const char* formula = myConfig.getGravityFormula();
|
||||
#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, 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
|
||||
|
||||
double tempF = convertCtoF( temp );
|
||||
double calTempF = convertCtoF(calTemp);
|
||||
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", &tempF}, {"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
|
36
src/calc.h
Normal file
36
src/calc.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
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 _CALC_H
|
||||
#define _CALC_H
|
||||
|
||||
// Includes
|
||||
#include "helper.h"
|
||||
|
||||
// Functions
|
||||
double calculateGravity( double angle, double temp );
|
||||
double gravityTemperatureCorrection( double gravity, double temp, double calTemp = 20 );
|
||||
|
||||
#endif // _CALC_H
|
||||
|
||||
// EOF
|
232
src/config.cpp
Normal file
232
src/config.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
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 "config.h"
|
||||
#include "helper.h"
|
||||
#include <LittleFS.h>
|
||||
|
||||
Config myConfig;
|
||||
|
||||
//
|
||||
// Create the config class with default settings.
|
||||
//
|
||||
Config::Config() {
|
||||
// Assiging default values
|
||||
sprintf(&id[0], "%6x", (unsigned int) ESP.getChipId() );
|
||||
sprintf(&mDNS[0], "" WIFI_MDNS "%s", getID() );
|
||||
setTempFormat('C');
|
||||
setPushInterval(900); // 15 minutes
|
||||
setVoltageFactor(1.59); // Conversion factor for battery
|
||||
setTempSensorAdj(0.0);
|
||||
setGravityTempAdj(false);
|
||||
gyroCalibration = { 0, 0, 0, 0, 0 ,0 };
|
||||
saveNeeded = false;
|
||||
}
|
||||
|
||||
//
|
||||
// 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_TEMPFORMAT ] = String( getTempFormat() );
|
||||
doc[ CFG_PARAM_PUSH_BREWFATHER ] = getBrewfatherPushTarget();
|
||||
doc[ CFG_PARAM_PUSH_HTTP ] = getHttpPushTarget();
|
||||
doc[ CFG_PARAM_PUSH_INTERVAL ] = getPushInterval();
|
||||
doc[ CFG_PARAM_VOLTAGEFACTOR ] = getVoltageFactor();
|
||||
doc[ CFG_PARAM_GRAVITY_FORMULA ] = getGravityFormula();
|
||||
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;
|
||||
}
|
||||
|
||||
//
|
||||
// Save json document to file
|
||||
//
|
||||
bool Config::saveFile() {
|
||||
if( !saveNeeded ) {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("CFG : Skipping save, not needed." CR));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
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
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
||||
|
||||
if (!configFile) {
|
||||
Log.error(F("CFG : Failed to open " CFG_FILENAME "." CR));
|
||||
return false;
|
||||
}
|
||||
|
||||
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 LOG_LEVEL==6
|
||||
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_TEMPFORMAT ].isNull() ) {
|
||||
String s = doc[ CFG_PARAM_TEMPFORMAT ];
|
||||
setTempFormat( s.charAt(0) );
|
||||
}
|
||||
if( !doc[ CFG_PARAM_PUSH_BREWFATHER ].isNull() )
|
||||
setBrewfatherPushTarget( doc[ CFG_PARAM_PUSH_BREWFATHER ] );
|
||||
if( !doc[ CFG_PARAM_PUSH_HTTP ].isNull() )
|
||||
setHttpPushTarget( doc[ CFG_PARAM_PUSH_HTTP ] );
|
||||
if( !doc[ CFG_PARAM_PUSH_INTERVAL ].isNull() )
|
||||
setPushInterval( doc[ CFG_PARAM_PUSH_INTERVAL ].as<int>() );
|
||||
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_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"];
|
||||
|
||||
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() {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("CFG : Formating filesystem." CR));
|
||||
#endif
|
||||
LittleFS.format();
|
||||
}
|
||||
|
||||
//
|
||||
// Check if file system can be mounted, if not we format it.
|
||||
//
|
||||
void Config::checkFileSystem() {
|
||||
#if LOG_LEVEL==6
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Dump the configuration to the serial port
|
||||
//
|
||||
void Config::debug() {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("CFG : Dumping configration " CFG_FILENAME "." CR));
|
||||
Log.verbose(F("CFG : ID; '%s'." CR), getID());
|
||||
Log.verbose(F("CFG : mDNS; '%s'." CR), getMDNS() );
|
||||
Log.verbose(F("CFG : OTA; '%s'." CR), getOtaURL() );
|
||||
Log.verbose(F("CFG : Temp; %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 : Push brewfather; '%s'." CR), getBrewfatherPushTarget() );
|
||||
Log.verbose(F("CFG : Push http; '%s'." CR), getHttpPushTarget() );
|
||||
Log.verbose(F("CFG : Push interval; %d." CR), getPushInterval() );
|
||||
// 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
|
175
src/config.h
Normal file
175
src/config.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
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 _CONFIG_H
|
||||
#define _CONFIG_H
|
||||
|
||||
// Includes
|
||||
#include "helper.h"
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// defintions
|
||||
#define CFG_JSON_BUFSIZE 1000
|
||||
|
||||
#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_PUSH_BREWFATHER "brewfather-push" // URL (brewfather format)
|
||||
#define CFG_PARAM_PUSH_HTTP "http-push" // URL (iSpindle format)
|
||||
#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_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
|
||||
|
||||
// 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)
|
||||
};
|
||||
|
||||
// Main configuration class
|
||||
class Config {
|
||||
private:
|
||||
bool saveNeeded;
|
||||
|
||||
// Device configuration
|
||||
char id[10];
|
||||
char mDNS[30];
|
||||
char otaURL[200];
|
||||
char tempFormat; // C, F
|
||||
float voltageFactor;
|
||||
float tempSensorAdj; // This value will be added to the read sensor value
|
||||
|
||||
// Push target settings
|
||||
char brewfatherPushTarget[200];
|
||||
char httpPushTarget[200];
|
||||
int pushInterval;
|
||||
|
||||
// Gravity and temperature calculations
|
||||
char gravityFormula[200];
|
||||
bool gravityTempAdj; // true, false
|
||||
char gravityFormat; // G, P
|
||||
|
||||
// Gyro calibration data
|
||||
RawGyroData gyroCalibration; // Holds the gyro calibration constants (6 * int16_t)
|
||||
|
||||
void debug();
|
||||
void formatFileSystem();
|
||||
|
||||
public:
|
||||
Config();
|
||||
const char* getID() { return &id[0]; };
|
||||
|
||||
const char* getMDNS() { return &mDNS[0]; }
|
||||
void setMDNS( const char* s ) { strncpy( &mDNS[0], s, sizeof(mDNS)-1); saveNeeded = true; }
|
||||
|
||||
const char* getOtaURL() { return &otaURL[0]; }
|
||||
void setOtaURL( const char* s ) { strncpy( &otaURL[0], s, sizeof(otaURL)-1); saveNeeded = true; }
|
||||
bool isOtaActive() { return strlen(&otaURL[0])>0?true:false; }
|
||||
|
||||
const char* getBrewfatherPushTarget() { return &brewfatherPushTarget[0]; }
|
||||
void setBrewfatherPushTarget( const char* s ) { strncpy(&brewfatherPushTarget[0], s, sizeof(brewfatherPushTarget)-1); saveNeeded = true; }
|
||||
bool isBrewfatherActive() { return strlen(&brewfatherPushTarget[0])>0?true:false; }
|
||||
|
||||
const char* getHttpPushTarget() { return &httpPushTarget[0]; }
|
||||
void setHttpPushTarget( const char* s ) { strncpy(&httpPushTarget[0], s, sizeof(httpPushTarget)-1); saveNeeded = true; }
|
||||
bool isHttpActive() { return strlen(&httpPushTarget[0])>0?true:false; }
|
||||
|
||||
int getPushInterval() { return pushInterval; }
|
||||
void setPushInterval( int v ) { pushInterval = v; saveNeeded = true; }
|
||||
void setPushInterval( const char* s ) { pushInterval = atoi(s); 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( const char* s ) { voltageFactor = atof(s); saveNeeded = true; }
|
||||
|
||||
float getTempSensorAdj() { return tempSensorAdj; }
|
||||
void setTempSensorAdj( float f ) { tempSensorAdj = f; saveNeeded = true; }
|
||||
void setTempSensorAdj( const char* s ) { tempSensorAdj = atof(s); saveNeeded = true; }
|
||||
|
||||
const char* getGravityFormula() { return &gravityFormula[0]; }
|
||||
void setGravityFormula( const char* s ) { strncpy(&gravityFormula[0], s, sizeof(gravityFormula)-1); 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; }
|
||||
|
||||
// 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 // _CONFIG_H
|
||||
|
||||
// EOF
|
327
src/gyro.cpp
Normal file
327
src/gyro.cpp
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
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 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
|
||||
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 {
|
||||
|
||||
Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR));
|
||||
sensorConnected = true;
|
||||
|
||||
// Configure ethe sensor
|
||||
accelgyro.setTempSensorEnabled(true);
|
||||
accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
|
||||
accelgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
|
||||
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
||||
accelgyro.setRate(17);
|
||||
|
||||
// For now we run the calibration at start.
|
||||
#if defined ( GYRO_CALIBRATE_STARTUP )
|
||||
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
|
||||
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
|
||||
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 ++) {
|
||||
accelgyro.getRotation( &raw.gx, &raw.gy, &raw.gz );
|
||||
accelgyro.getAcceleration( &raw.ax, &raw.ay, &raw.az );
|
||||
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
|
||||
|
||||
delayMicroseconds( delayTime );
|
||||
}
|
||||
|
||||
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
|
||||
#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)
|
||||
//
|
||||
double GyroSensor::calculateAngle(RawGyroData &raw) {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("GYRO: Calculating the angle." CR) );
|
||||
#endif
|
||||
|
||||
// Source: https://www.nxp.com/docs/en/application-note/AN3461.pdf
|
||||
double v = (acos( raw.ay / sqrt( raw.ax*raw.ax + raw.ay*raw.ay + raw.az*raw.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
|
||||
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
|
||||
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
|
||||
Log.verbose(F("GYRO: Getting new gyro position." CR) );
|
||||
#endif
|
||||
|
||||
RawGyroData raw;
|
||||
readSensor( raw, SENSOR_READ_COUNT, SENSOR_READ_DELAY );
|
||||
|
||||
// If the sensor is unstable we return false to signal we dont have valid value
|
||||
if( isSensorMoving(raw) ) {
|
||||
Log.notice(F("GYRO: Sensor is moving." CR) );
|
||||
validValue = false;
|
||||
} else {
|
||||
validValue = true;
|
||||
angle = calculateAngle( raw );
|
||||
//Log.notice(F("GYRO: Calculated angle %F" CR), angle );
|
||||
}
|
||||
|
||||
return validValue;
|
||||
}
|
||||
|
||||
//
|
||||
// Dump the stored calibration values.
|
||||
//
|
||||
void GyroSensor::dumpCalibration() {
|
||||
#if LOG_LEVEL==6
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
79
src/gyro.h
Normal file
79
src/gyro.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
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)
|
||||
};
|
||||
|
||||
class GyroSensor {
|
||||
private:
|
||||
MPU6050 accelgyro;
|
||||
bool sensorConnected = false;
|
||||
bool validValue = false;
|
||||
double angle = 0;
|
||||
RawGyroData calibrationOffset;
|
||||
|
||||
void debug();
|
||||
void applyCalibration();
|
||||
void dumpCalibration();
|
||||
void readSensor(RawGyroData &raw, const int noIterations = 100, const int delayTime = 1);
|
||||
bool isSensorMoving(RawGyroData &raw);
|
||||
double calculateAngle(RawGyroData &raw);
|
||||
|
||||
public:
|
||||
bool setup();
|
||||
bool read();
|
||||
void calibrateSensor();
|
||||
|
||||
double getAngle() { return angle; };
|
||||
bool isConnected() { return sensorConnected; };
|
||||
bool hasValue() { return validValue; };
|
||||
void enterSleep();
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern GyroSensor myGyro;
|
||||
|
||||
#endif // _GYRO_H
|
||||
|
||||
// EOF
|
110
src/helper.cpp
Normal file
110
src/helper.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
SerialDebug mySerial;
|
||||
BatteryVoltage myBatteryVoltage;
|
||||
|
||||
//
|
||||
// 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 ACTIVATE_PUSH
|
||||
"PUSH "
|
||||
#endif
|
||||
#ifdef SKIP_SLEEPMODE
|
||||
"SKIP_SLEEP "
|
||||
#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) {
|
||||
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
|
||||
}
|
||||
|
||||
//
|
||||
// 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
|
66
src/helper.h
Normal file
66
src/helper.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
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);
|
||||
void printNewline(Print* _logOutput);
|
||||
|
||||
// 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; };
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern SerialDebug mySerial;
|
||||
extern BatteryVoltage myBatteryVoltage;
|
||||
|
||||
#endif // _HELPER_H
|
||||
|
||||
// EOF
|
214
src/main.cpp
Normal file
214
src/main.cpp
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
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 ESP_MRD_USE_LITTLEFS true
|
||||
#define ESP_MRD_USE_SPIFFS false
|
||||
#define ESP_MRD_USE_EEPROM false
|
||||
#define MRD_TIMES 3
|
||||
#define MRD_TIMEOUT 10
|
||||
#define MRD_ADDRESS 0
|
||||
#define MULTIRESETDETECTOR_DEBUG true
|
||||
#include <ESP_MultiResetDetector.h>
|
||||
MultiResetDetector *mrd;
|
||||
|
||||
// Define constats for this program
|
||||
#if LOG_LEVEL==6
|
||||
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 = 100; // ms, time to wait between changes to output
|
||||
bool sleepModeAlwaysSkip = false; // Web interface can override normal behaviour
|
||||
#endif
|
||||
unsigned long lastMillis = 0;
|
||||
unsigned long startMillis;
|
||||
bool sleepModeActive = false;
|
||||
|
||||
//
|
||||
// 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 ? true : false;
|
||||
|
||||
// sleep mode active when flat
|
||||
//sleepModeActive = ( angle<85 && angle>5 ) ? true : false;
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("MAIN: Deep sleep mode %s (angle=%F volt=%F)." CR), sleepModeActive ? "true":"false", angle, volt );
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Setup
|
||||
//
|
||||
void setup() {
|
||||
startMillis = millis();
|
||||
mrd = new MultiResetDetector(MRD_TIMEOUT, MRD_ADDRESS);
|
||||
bool dt = mrd->detectMultiReset();
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
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.notice(F("Main: Loading configuration." CR));
|
||||
myConfig.checkFileSystem();
|
||||
myConfig.loadFile();
|
||||
|
||||
// Setup watchdog
|
||||
ESP.wdtDisable();
|
||||
ESP.wdtEnable( interval*2 );
|
||||
|
||||
myTempSensor.setup();
|
||||
|
||||
// Setup Gyro
|
||||
if( !myGyro.setup() )
|
||||
Log.error(F("Main: Failed to initialize the gyro." CR));
|
||||
|
||||
if( dt )
|
||||
Log.notice(F("Main: Detected doubletap on reset." CR));
|
||||
|
||||
Log.notice(F("Main: Connecting to wifi." CR));
|
||||
myWifi.connect( dt );
|
||||
Log.notice(F("Main: WIFI connected." CR));
|
||||
|
||||
myGyro.read();
|
||||
myBatteryVoltage.read();
|
||||
checkSleepMode( myGyro.getAngle(), myBatteryVoltage.getVoltage() );
|
||||
|
||||
if( myWifi.isConnected() ) {
|
||||
Log.notice(F("Main: Connected to wifi ip=%s." CR), myWifi.getIPAddress().c_str() );
|
||||
|
||||
if( !sleepModeActive )
|
||||
if( myWebServer.setupWebServer() )
|
||||
Log.notice(F("Main: Webserver is running." CR) );
|
||||
}
|
||||
|
||||
#if defined( ACTIVATE_OTA )
|
||||
if( !sleepModeActive && myWifi.isConnected() && myWifi.checkFirmwareVersion() ) {
|
||||
myWifi.updateFirmware();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Main loops
|
||||
//
|
||||
void loop() {
|
||||
mrd->loop();
|
||||
|
||||
if( sleepModeActive || abs(millis() - lastMillis) > interval ) {
|
||||
float angle = 90;
|
||||
float volt = myBatteryVoltage.getVoltage();
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("Main: Entering main loop." CR) );
|
||||
#endif
|
||||
// If we dont get any readings we just skip this and try again the next interval.
|
||||
if( myGyro.hasValue() ) {
|
||||
angle = myGyro.getAngle();
|
||||
float temp = myTempSensor.getValueCelcius(); // The code is build around using C for temp.
|
||||
float gravity = calculateGravity( angle, temp );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("Main: Sensor values gyro=%F, temp=%F, gravity=%F." CR), angle, temp, gravity );
|
||||
#endif
|
||||
if( myConfig.isGravityTempAdj() ) {
|
||||
gravity = gravityTemperatureCorrection( gravity, temp); // Use default correction temperature of 20C
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("Main: Temp adjusted gravity=%F." CR), gravity );
|
||||
#endif
|
||||
}
|
||||
|
||||
Log.notice(F("Main: Gyro angle=%F, temp=%F, gravity=%F, batt=%F." CR), angle, temp, gravity, volt );
|
||||
|
||||
#if defined( ACTIVATE_PUSH )
|
||||
unsigned long runTime = millis() - startMillis;
|
||||
|
||||
myPushTarget.send( angle, gravity, temp, runTime/1000, sleepModeActive ); // Force the transmission if we are going to sleep
|
||||
#endif
|
||||
} else {
|
||||
Log.error(F("Main: No gyro value." CR) );
|
||||
}
|
||||
|
||||
if( sleepModeActive ) {
|
||||
unsigned long runTime = millis() - startMillis;
|
||||
|
||||
// Enter sleep mode...
|
||||
Log.notice(F("MAIN: Entering deep sleep, run time %l s." CR), runTime/1000 );
|
||||
LittleFS.end();
|
||||
myGyro.enterSleep();
|
||||
mrd->stop();
|
||||
delay(100);
|
||||
deepSleep( myConfig.getPushInterval() );
|
||||
}
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("Main: Sleep mode not active." CR) );
|
||||
#endif
|
||||
// Do these checks if we are running in normal mode (not sleep mode)
|
||||
checkSleepMode( angle, volt );
|
||||
myGyro.read();
|
||||
myBatteryVoltage.read();
|
||||
lastMillis = millis();
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("Main: Heap %d kb FreeSketch %d kb." CR), ESP.getFreeHeap()/1024, ESP.getFreeSketchSpace()/1024 );
|
||||
Log.verbose(F("Main: HeapFrag %d %%." CR), ESP.getHeapFragmentation() );
|
||||
#endif
|
||||
}
|
||||
|
||||
myWebServer.loop();
|
||||
}
|
||||
|
||||
// EOF
|
170
src/pushtarget.cpp
Normal file
170
src/pushtarget.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
#if defined( ACTIVATE_PUSH )
|
||||
|
||||
PushTarget myPushTarget;
|
||||
|
||||
//
|
||||
// Send the pressure value
|
||||
//
|
||||
void PushTarget::send(float angle, float gravity, float temp, float runTime, bool force ) {
|
||||
unsigned long timePassed = abs( millis() - ms );
|
||||
unsigned long interval = myConfig.getPushInterval()*1000;
|
||||
|
||||
if( ( timePassed < interval ) && !force) {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("PUSH: Timer has not expired %l vs %l." CR), timePassed, interval );
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("PUSH: Sending data." CR) );
|
||||
#endif
|
||||
ms = millis();
|
||||
|
||||
if( myConfig.isBrewfatherActive() )
|
||||
sendBrewfather( angle, gravity, temp );
|
||||
|
||||
if( myConfig.isHttpActive() )
|
||||
sendHttp( angle, gravity, temp, runTime );
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to brewfather
|
||||
//
|
||||
void PushTarget::sendBrewfather(float angle, float gravity, float temp ) {
|
||||
Log.notice(F("PUSH: Sending values to brewfather angle=%F, gravity=%F, temp=%F." CR), angle, gravity, temp );
|
||||
|
||||
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["aux_temp"] = 0;
|
||||
//doc["ext_temp"] = 0;
|
||||
doc["temp_unit"] = String( myConfig.getTempFormat() );
|
||||
//doc["pressure"] = ;
|
||||
//doc["pressure_unit"] = ;
|
||||
doc["battery"] = reduceFloatPrecision( myBatteryVoltage.getVoltage(), 2 );
|
||||
doc["gravity"] = reduceFloatPrecision( gravity, 4 );
|
||||
doc["gravity_unit"] = myConfig.isGravitySG()?"G":"P";
|
||||
//doc["ph"] = 0;
|
||||
//doc["bpm"] = 0;
|
||||
//doc["comment"] = "";
|
||||
//doc["beer"] = "";
|
||||
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
String serverPath = myConfig.getBrewfatherPushTarget();
|
||||
|
||||
// Your Domain name with URL path or IP address with path
|
||||
http.begin( client, serverPath);
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
#if LOG_LEVEL==6
|
||||
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 Response code %d" CR), httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: HTTP Response code %d" CR), httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendHttp(float angle, float gravity, float temp, float runTime ) {
|
||||
Log.notice(F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR), angle, gravity, temp );
|
||||
|
||||
DynamicJsonDocument doc(256);
|
||||
|
||||
doc["name"] = myConfig.getMDNS();
|
||||
doc["temp"] = reduceFloatPrecision( temp, 1 );
|
||||
doc["temp-unit"] = String( myConfig.getTempFormat() );
|
||||
doc["gravity"] = reduceFloatPrecision( gravity, 4 );
|
||||
doc["angle"] = reduceFloatPrecision( angle, 2);
|
||||
doc["battery"] = reduceFloatPrecision( myBatteryVoltage.getVoltage(), 2 );
|
||||
doc["rssi"] = WiFi.RSSI();
|
||||
|
||||
// Some debug information
|
||||
doc["run-time"] = reduceFloatPrecision( runTime, 2 );
|
||||
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
String serverPath = myConfig.getHttpPushTarget();
|
||||
|
||||
// Your Domain name with URL path or IP address with path
|
||||
http.begin( client, serverPath);
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
#if LOG_LEVEL==6
|
||||
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 Response code %d" CR), httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: HTTP Response code %d" CR), httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
#endif // ACTIVATE_PUSH
|
||||
|
||||
// EOF
|
52
src/pushtarget.h
Normal file
52
src/pushtarget.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 temp );
|
||||
void sendHttp(float angle, float gravity, float temp, float runTime );
|
||||
|
||||
public:
|
||||
PushTarget() { ms = millis(); }
|
||||
void send(float angle, float gravity, float temp, float runTime, bool force = false );
|
||||
};
|
||||
|
||||
extern PushTarget myPushTarget;
|
||||
|
||||
#endif // _PUSHTARGET_H
|
||||
|
||||
// EOF
|
41
src/resources.cpp
Normal file
41
src/resources.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
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 <incbin.h>
|
||||
|
||||
#if defined( EMBED_HTML )
|
||||
|
||||
/*INCBIN(IndexHtm, "data/index.htm" );
|
||||
INCBIN(DeviceHtm, "data/device.htm" );
|
||||
INCBIN(ConfigHtm, "data/config.htm" );
|
||||
INCBIN(AboutHtm, "data/about.htm" );*/
|
||||
|
||||
// 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(AboutHtm, "data/about.min.htm" );
|
||||
|
||||
#endif
|
||||
|
||||
// EOF
|
108
src/tempsensor.cpp
Normal file
108
src/tempsensor.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
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 <onewire.h>
|
||||
#include <DallasTemperature.h>
|
||||
#include <Wire.h>
|
||||
|
||||
//
|
||||
// Conversion between C and F
|
||||
//
|
||||
float convertCtoF( float t ) {
|
||||
return (t * 1.8 ) + 32.0;
|
||||
}
|
||||
|
||||
OneWire myOneWire(D6);
|
||||
DallasTemperature mySensors(&myOneWire);
|
||||
TempSensor myTempSensor;
|
||||
|
||||
#define TEMPERATURE_PRECISION 9
|
||||
|
||||
//
|
||||
// Setup temp sensors
|
||||
//
|
||||
void TempSensor::setup() {
|
||||
|
||||
#if defined( SIMULATE_TEMP )
|
||||
hasSensors = true;
|
||||
return;
|
||||
#endif
|
||||
|
||||
if( mySensors.getDeviceCount() )
|
||||
return;
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("TSEN: Looking for temp sensors." CR));
|
||||
#endif
|
||||
mySensors.begin();
|
||||
|
||||
if( mySensors.getDeviceCount() ) {
|
||||
Log.notice(F("TSEN: Found %d sensors." CR), mySensors.getDeviceCount());
|
||||
mySensors.setResolution(TEMPERATURE_PRECISION);
|
||||
}
|
||||
|
||||
float t = myConfig.getTempSensorAdj();
|
||||
|
||||
// Set the temp sensor adjustment values
|
||||
if( myConfig.isTempC() ) {
|
||||
tempSensorAdjF = t * 1.8; // Convert the adjustment value to C
|
||||
tempSensorAdjC = t;
|
||||
} else {
|
||||
tempSensorAdjF = t;
|
||||
tempSensorAdjC = t * 0.556; // Convert the adjustent value to F
|
||||
}
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("TSEN: Adjustment values for temp sensor %F C, %F F." CR), tempSensorAdjC, tempSensorAdjF );
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieving value from sensor
|
||||
//
|
||||
float TempSensor::getValue() {
|
||||
float c = 0;
|
||||
|
||||
#if defined( SIMULATE_TEMP )
|
||||
return 21;
|
||||
#endif
|
||||
|
||||
// Read the sensors
|
||||
mySensors.requestTemperatures();
|
||||
|
||||
if( mySensors.getDeviceCount() >= 1) {
|
||||
c = mySensors.getTempCByIndex(0);
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("TSEN: Reciving temp value for sensor %F C." CR), c);
|
||||
#endif
|
||||
hasSensor = true;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
// EOF
|
50
src/tempsensor.h
Normal file
50
src/tempsensor.h
Normal file
@ -0,0 +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 getValueCelcius() { return getValue() + tempSensorAdjC; }
|
||||
float getValueFarenheight() { return convertCtoF(getValue()) + tempSensorAdjF; };
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern TempSensor myTempSensor;
|
||||
|
||||
#endif // _TEMPSENSOR_H
|
||||
|
||||
// EOF
|
382
src/webserver.cpp
Normal file
382
src/webserver.cpp
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
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 DEBUG_ESP_HTTP_SERVER
|
||||
#include "webserver.h"
|
||||
#include "config.h"
|
||||
#include "helper.h"
|
||||
#include "gyro.h"
|
||||
#include "calc.h"
|
||||
#include "tempsensor.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <incbin.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
// Binary resouces
|
||||
#if defined( EMBED_HTML )
|
||||
INCBIN_EXTERN(IndexHtm);
|
||||
INCBIN_EXTERN(DeviceHtm);
|
||||
INCBIN_EXTERN(ConfigHtm);
|
||||
INCBIN_EXTERN(AboutHtm);
|
||||
#endif
|
||||
|
||||
WebServer myWebServer;
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
extern bool sleepModeActive;
|
||||
extern bool sleepModeAlwaysSkip;
|
||||
|
||||
//
|
||||
// Callback from webServer when / has been accessed.
|
||||
//
|
||||
void webHandleDevice() {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/config." CR));
|
||||
#endif
|
||||
DynamicJsonDocument doc(100);
|
||||
doc[ CFG_PARAM_ID ] = myConfig.getID();
|
||||
doc[ CFG_PARAM_APP_NAME ] = CFG_APPNAME;
|
||||
doc[ CFG_PARAM_APP_VER ] = CFG_APPVER;
|
||||
doc[ CFG_PARAM_MDNS ] = myConfig.getMDNS();
|
||||
#if LOG_LEVEL==6
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print( CR );
|
||||
#endif
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
server.send(200, "application/json", out.c_str() );
|
||||
}
|
||||
|
||||
//
|
||||
// Callback from webServer when / has been accessed.
|
||||
//
|
||||
void webHandleConfig() {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/config." CR));
|
||||
#endif
|
||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
||||
myConfig.createJson( doc );
|
||||
|
||||
double angle = myGyro.getAngle();
|
||||
double temp = myTempSensor.getValueCelcius();
|
||||
double gravity = calculateGravity( angle, temp );
|
||||
|
||||
doc[ CFG_PARAM_ANGLE ] = reduceFloatPrecision( angle);
|
||||
doc[ CFG_PARAM_GRAVITY ] = reduceFloatPrecision( gravityTemperatureCorrection( gravity, temp ), 4);
|
||||
doc[ CFG_PARAM_BATTERY ] = reduceFloatPrecision( myBatteryVoltage.getVoltage());
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print( CR );
|
||||
#endif
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
server.send(200, "application/json", out.c_str() );
|
||||
}
|
||||
|
||||
//
|
||||
// Callback from webServer when / has been accessed.
|
||||
//
|
||||
void webHandleCalibrate() {
|
||||
String id = server.arg( CFG_PARAM_ID );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/calibrate." CR));
|
||||
#endif
|
||||
if( !id.equalsIgnoreCase( myConfig.getID() ) ) {
|
||||
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(), myConfig.getID());
|
||||
server.send(400, "text/plain", "Invalid ID.");
|
||||
return;
|
||||
}
|
||||
myGyro.calibrateSensor();
|
||||
server.send(200, "text/plain", "Device calibrated" );
|
||||
}
|
||||
|
||||
//
|
||||
// Callback from webServer when / has been accessed.
|
||||
//
|
||||
void webHandleFactoryReset() {
|
||||
String id = server.arg( CFG_PARAM_ID );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/factory." CR));
|
||||
#endif
|
||||
if( !id.compareTo( myConfig.getID() ) ) {
|
||||
server.send(200, "text/plain", "Doing reset...");
|
||||
LittleFS.remove(CFG_FILENAME);
|
||||
LittleFS.end();
|
||||
delay(500);
|
||||
ESP.reset();
|
||||
} else {
|
||||
server.send(400, "text/plain", "Unknown ID.");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Callback from webServer when / has been accessed.
|
||||
//
|
||||
void webHandleStatus() {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/status." CR));
|
||||
#endif
|
||||
DynamicJsonDocument doc(256);
|
||||
|
||||
double angle = myGyro.getAngle();
|
||||
double temp = myTempSensor.getValueCelcius();
|
||||
double gravity = calculateGravity( angle, temp );
|
||||
|
||||
doc[ CFG_PARAM_ID ] = myConfig.getID();
|
||||
doc[ CFG_PARAM_ANGLE ] = reduceFloatPrecision( angle);
|
||||
doc[ CFG_PARAM_GRAVITY ] = reduceFloatPrecision( gravityTemperatureCorrection( gravity, temp ), 4);
|
||||
doc[ CFG_PARAM_TEMP_C ] = reduceFloatPrecision( temp, 1);
|
||||
doc[ CFG_PARAM_TEMP_F ] = reduceFloatPrecision( myTempSensor.getValueFarenheight(), 1);
|
||||
doc[ CFG_PARAM_BATTERY ] = reduceFloatPrecision( myBatteryVoltage.getVoltage());
|
||||
doc[ CFG_PARAM_TEMPFORMAT ] = String( myConfig.getTempFormat() );
|
||||
doc[ CFG_PARAM_SLEEP_MODE ] = sleepModeAlwaysSkip;
|
||||
doc[ CFG_PARAM_RSSI ] = WiFi.RSSI();
|
||||
#if LOG_LEVEL==6
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print( CR );
|
||||
#endif
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
server.send(200, "application/json", out.c_str() );
|
||||
}
|
||||
|
||||
//
|
||||
// Callback from webServer when / has been accessed.
|
||||
//
|
||||
void webHandleClearWIFI() {
|
||||
String id = server.arg( CFG_PARAM_ID );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/clearwifi." CR));
|
||||
#endif
|
||||
if( !id.compareTo( myConfig.getID() ) ) {
|
||||
server.send(200, "text/plain", "Clearing WIFI credentials and doing reset...");
|
||||
delay(1000);
|
||||
WiFi.disconnect(); // Clear credentials
|
||||
ESP.reset();
|
||||
} else {
|
||||
server.send(400, "text/plain", "Unknown ID.");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Used to force the device to never sleep.
|
||||
//
|
||||
void webHandleStatusSleepmode() {
|
||||
String id = server.arg( CFG_PARAM_ID );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/status/sleepmode." CR) );
|
||||
#endif
|
||||
if( !id.equalsIgnoreCase( myConfig.getID() ) ) {
|
||||
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(), myConfig.getID());
|
||||
server.send(400, "text/plain", "Invalid ID.");
|
||||
return;
|
||||
}
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : sleep-mode=%s." CR), server.arg( CFG_PARAM_SLEEP_MODE ).c_str() );
|
||||
#endif
|
||||
if( server.arg( CFG_PARAM_SLEEP_MODE ).equalsIgnoreCase( "true" ) )
|
||||
sleepModeAlwaysSkip = true;
|
||||
else
|
||||
sleepModeAlwaysSkip = false;
|
||||
server.send(200, "text/plain", "Sleep mode updated" );
|
||||
}
|
||||
|
||||
//
|
||||
// Update device settings.
|
||||
//
|
||||
void webHandleConfigDevice() {
|
||||
String id = server.arg( CFG_PARAM_ID );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/config/device." CR) );
|
||||
#endif
|
||||
if( !id.equalsIgnoreCase( myConfig.getID() ) ) {
|
||||
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(), myConfig.getID());
|
||||
server.send(400, "text/plain", "Invalid ID.");
|
||||
return;
|
||||
}
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : mdns=%s, temp-format=%s." CR), server.arg( CFG_PARAM_MDNS ).c_str(), server.arg( CFG_PARAM_TEMPFORMAT ).c_str() );
|
||||
#endif
|
||||
myConfig.setMDNS( server.arg( CFG_PARAM_MDNS ).c_str() );
|
||||
myConfig.setTempFormat( server.arg( CFG_PARAM_TEMPFORMAT ).charAt(0) );
|
||||
myConfig.saveFile();
|
||||
server.sendHeader("Location", "/config.htm#collapseOne", true);
|
||||
server.send(302, "text/plain", "Device config updated" );
|
||||
}
|
||||
|
||||
//
|
||||
// Update push settings.
|
||||
//
|
||||
void webHandleConfigPush() {
|
||||
String id = server.arg( CFG_PARAM_ID );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/config/push." CR) );
|
||||
#endif
|
||||
if( !id.equalsIgnoreCase( myConfig.getID() ) ) {
|
||||
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(), myConfig.getID());
|
||||
server.send(400, "text/plain", "Invalid ID.");
|
||||
return;
|
||||
}
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : http=%s, bf=%s interval=%s." CR), server.arg( CFG_PARAM_PUSH_HTTP ).c_str(), server.arg( CFG_PARAM_PUSH_BREWFATHER ).c_str(), server.arg( CFG_PARAM_PUSH_INTERVAL ).c_str() );
|
||||
#endif
|
||||
myConfig.setHttpPushTarget( server.arg( CFG_PARAM_PUSH_HTTP ).c_str() );
|
||||
myConfig.setBrewfatherPushTarget( server.arg( CFG_PARAM_PUSH_BREWFATHER ).c_str() );
|
||||
myConfig.setPushInterval( server.arg( CFG_PARAM_PUSH_INTERVAL ).c_str() );
|
||||
myConfig.saveFile();
|
||||
server.sendHeader("Location", "/config.htm#collapseTwo", true);
|
||||
server.send(302, "text/plain", "Push config updated" );
|
||||
}
|
||||
|
||||
//
|
||||
// Update gravity settings.
|
||||
//
|
||||
void webHandleConfigGravity() {
|
||||
String id = server.arg( CFG_PARAM_ID );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/config/gravity." CR) );
|
||||
#endif
|
||||
if( !id.equalsIgnoreCase( myConfig.getID() ) ) {
|
||||
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(), myConfig.getID());
|
||||
server.send(400, "text/plain", "Invalid ID.");
|
||||
return;
|
||||
}
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : formula=%s, temp-corr=%s." CR), server.arg( CFG_PARAM_GRAVITY_FORMULA ).c_str(), server.arg( CFG_PARAM_GRAVITY_TEMP_ADJ ).c_str() );
|
||||
#endif
|
||||
myConfig.setGravityFormula( server.arg( CFG_PARAM_GRAVITY_FORMULA ).c_str() );
|
||||
myConfig.setGravityTempAdj( server.arg( CFG_PARAM_GRAVITY_TEMP_ADJ ).equalsIgnoreCase( "on" ) ? true:false);
|
||||
myConfig.saveFile();
|
||||
server.sendHeader("Location", "/config.htm#collapseThree", true);
|
||||
server.send(302, "text/plain", "Gravity config updated" );
|
||||
}
|
||||
|
||||
//
|
||||
// Update hardware settings.
|
||||
//
|
||||
void webHandleConfigHardware() {
|
||||
String id = server.arg( CFG_PARAM_ID );
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : webServer callback for /api/config/hardware." CR) );
|
||||
#endif
|
||||
if( !id.equalsIgnoreCase( myConfig.getID() ) ) {
|
||||
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(), myConfig.getID());
|
||||
server.send(400, "text/plain", "Invalid ID.");
|
||||
return;
|
||||
}
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : vf=%s, tempadj=%s, ota=%s." CR), server.arg( CFG_PARAM_VOLTAGEFACTOR ).c_str(), server.arg( CFG_PARAM_TEMP_ADJ ).c_str(), server.arg( CFG_PARAM_OTA ).c_str() );
|
||||
#endif
|
||||
myConfig.setVoltageFactor( server.arg( CFG_PARAM_VOLTAGEFACTOR ).toFloat() );
|
||||
myConfig.setTempSensorAdj( server.arg( CFG_PARAM_TEMP_ADJ ).toFloat() );
|
||||
myConfig.setOtaURL( server.arg( CFG_PARAM_OTA ).c_str() );
|
||||
myConfig.saveFile();
|
||||
server.sendHeader("Location", "/config.htm#collapseFour", true);
|
||||
server.send(302, "text/plain", "Hardware config updated" );
|
||||
}
|
||||
|
||||
//
|
||||
// Setup the Web Server callbacks and start it
|
||||
//
|
||||
bool WebServer::setupWebServer() {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WEB : Setting up web server." CR));
|
||||
#endif
|
||||
|
||||
Log.notice(F("WEB : Web server setup started." CR));
|
||||
|
||||
MDNS.begin( myConfig.getMDNS() );
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
// Static content
|
||||
#if defined( EMBED_HTML )
|
||||
server.on("/",[]() {
|
||||
server.send_P(200, "text/html", (const char*) gIndexHtmData, gIndexHtmSize );
|
||||
} );
|
||||
server.on("/index.htm",[]() {
|
||||
server.send_P(200, "text/html", (const char*) gIndexHtmData, gIndexHtmSize );
|
||||
} );
|
||||
server.on("/device.htm",[]() {
|
||||
server.send_P(200, "text/html", (const char*) gDeviceHtmData, gDeviceHtmSize );
|
||||
} );
|
||||
server.on("/config.htm",[]() {
|
||||
server.send_P(200, "text/html", (const char*) gConfigHtmData, gConfigHtmSize );
|
||||
} );
|
||||
server.on("/about.htm",[]() {
|
||||
server.send_P(200, "text/html", (const char*) gAboutHtmData, gAboutHtmSize );
|
||||
} );
|
||||
#else
|
||||
// Show files in the filessytem at startup
|
||||
FSInfo fs;
|
||||
LittleFS.info(fs);
|
||||
Log.notice( F("File system: Total=%d, Used=%d." CR), fs.totalBytes, fs.usedBytes );
|
||||
Dir dir = LittleFS.openDir("/");
|
||||
while( dir.next() ) {
|
||||
Log.notice( F("File: %s, %d bytes" CR), dir.fileName().c_str(), dir.fileSize() );
|
||||
}
|
||||
|
||||
server.serveStatic("/", LittleFS, "/index.htm" );
|
||||
server.serveStatic("/index.htm", LittleFS, "/index.htm" );
|
||||
server.serveStatic("/device.htm", LittleFS, "/device.htm" );
|
||||
server.serveStatic("/config.htm", LittleFS, "/config.htm" );
|
||||
server.serveStatic("/about.htm", LittleFS, "/about.htm" );
|
||||
#endif
|
||||
|
||||
// Dynamic content
|
||||
server.on("/api/config", webHandleConfig); // Get config.json
|
||||
server.on("/api/device", webHandleDevice); // Get device.json
|
||||
server.on("/api/calibrate", webHandleCalibrate); // Run calibration routine (param id)
|
||||
server.on("/api/factory", webHandleFactoryReset); // Reset the device
|
||||
server.on("/api/status", webHandleStatus); // Get the status.json
|
||||
server.on("/api/clearwifi", webHandleClearWIFI); // Clear wifi settings
|
||||
|
||||
server.on("/api/status/sleepmode", webHandleStatusSleepmode);
|
||||
server.on("/api/config/device", webHandleConfigDevice);
|
||||
server.on("/api/config/push", webHandleConfigPush);
|
||||
server.on("/api/config/gravity", webHandleConfigGravity);
|
||||
server.on("/api/config/hardware", webHandleConfigHardware);
|
||||
|
||||
server.onNotFound( []() {
|
||||
Log.error(F("WEB : URL not found %s received." CR), server.uri().c_str());
|
||||
server.send(404, "text/plain", F("URL not found") );
|
||||
} );
|
||||
|
||||
server.begin();
|
||||
Log.notice(F("WEB : Web server started." CR));
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// called from main loop
|
||||
//
|
||||
void WebServer::loop() {
|
||||
// Dont put serial debug output in this call
|
||||
server.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
|
||||
// EOF
|
41
src/webserver.h
Normal file
41
src/webserver.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
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
|
||||
|
||||
// classes
|
||||
class WebServer {
|
||||
public:
|
||||
bool setupWebServer();
|
||||
void loop();
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern WebServer myWebServer;
|
||||
|
||||
#endif // _WEBSERVER_H
|
||||
|
||||
// EOF
|
203
src/wifi.cpp
Normal file
203
src/wifi.cpp
Normal file
@ -0,0 +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 "wifi.h"
|
||||
#include "config.h"
|
||||
#include "helper.h"
|
||||
#include "gyro.h"
|
||||
#include "calc.h"
|
||||
#include "tempsensor.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266httpUpdate.h>
|
||||
#include <LittleFS.h>
|
||||
#include <incbin.h>
|
||||
|
||||
Wifi myWifi;
|
||||
WiFiManager myWifiManager;
|
||||
|
||||
// TODO: ADD MDNS setting to WIFI portal.....
|
||||
// TODO: Download html files during OTA update to reduce image size.
|
||||
|
||||
//
|
||||
// Connect to last known access point or create one if connection is not working.
|
||||
//
|
||||
bool Wifi::connect( bool showPortal ) {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Connecting to WIFI via connection manager (portal=%s)." CR), showPortal?"true":"false");
|
||||
myWifiManager.setDebugOutput(true);
|
||||
#else
|
||||
myWifiManager.setDebugOutput(false);
|
||||
#endif
|
||||
unsigned long startMillis = millis();
|
||||
myWifiManager.setConfigPortalTimeout( WIFI_PORTAL_TIMEOUT );
|
||||
if( showPortal ) {
|
||||
Log.notice(F("WIFI: Starting wifi portal." CR));
|
||||
connectedFlag = myWifiManager.startConfigPortal( WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD );
|
||||
}
|
||||
else
|
||||
connectedFlag = myWifiManager.autoConnect( WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD );
|
||||
|
||||
Log.notice( F("WIFI: Connect time %d s" CR), abs(millis() - startMillis)/1000);
|
||||
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Connect returned %s." CR), connectedFlag?"True":"False" );
|
||||
#endif
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#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: Updating failed %d, %s." CR), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
|
||||
break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Log.notice("WIFI: Updated succesfull, rebooting." );
|
||||
ESP.reset();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// 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: HTTP Response code %d" CR), httpResponseCode);
|
||||
|
||||
String payload = http.getString();
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Payload %s." CR), payload.c_str());
|
||||
#endif
|
||||
DynamicJsonDocument ver(256);
|
||||
DeserializationError err = deserializeJson(ver, payload);
|
||||
if( err ) {
|
||||
Log.error(F("WIFI: Failed to parse json" CR));
|
||||
} else {
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: Project %s version %s." CR), ver["project"].as<char*>(), ver["version"].as<char*>());
|
||||
#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("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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.error(F("WIFI: HTTP Response code %d" CR), httpResponseCode);
|
||||
}
|
||||
http.end();
|
||||
#if LOG_LEVEL==6
|
||||
Log.verbose(F("WIFI: 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 %s." CR), version);
|
||||
#endif
|
||||
char temp[80];
|
||||
char *s;
|
||||
char *p = &temp[0];
|
||||
int i = 0;
|
||||
|
||||
strcpy( &temp[0], version );
|
||||
|
||||
// TODO: Do some error checking on the string, lenght etc.
|
||||
|
||||
while ((s = strtok_r(p, ".", &p)) != NULL) {
|
||||
num[i++] = atoi( s );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // ACTIVATE_OTA
|
||||
|
||||
// EOF
|
57
src/wifi.h
Normal file
57
src/wifi.h
Normal file
@ -0,0 +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 _WIFI_H
|
||||
#define _WIFI_H
|
||||
|
||||
// Include
|
||||
#include <WiFiManager.h>
|
||||
|
||||
// classes
|
||||
class Wifi {
|
||||
private:
|
||||
// WIFI
|
||||
bool connectedFlag = false;
|
||||
|
||||
// OTA
|
||||
bool newFirmware = false;
|
||||
bool parseFirmwareVersionString( int (&num)[3], const char *version );
|
||||
|
||||
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
|
Reference in New Issue
Block a user