Refactored to free up heap for SSL
This commit is contained in:
parent
9bea54b703
commit
545f274a47
20
src/calc.cpp
20
src/calc.cpp
@ -49,7 +49,8 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||
#endif
|
||||
|
||||
if (!noAngles) {
|
||||
myLastErrors.addEntry(F("CALC: Not enough values for deriving formula"));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CALC: Not enough values for deriving formula"));
|
||||
return ERR_FORMULA_NOTENOUGHVALUES;
|
||||
} else {
|
||||
double coeffs[order + 1];
|
||||
@ -103,7 +104,10 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
myLastErrors.addEntry(F("CALC: Error validating created formula. Deviation to large, formula rejected."));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(
|
||||
F("CALC: Error validating created formula. Deviation to large, "
|
||||
"formula rejected."));
|
||||
return ERR_FORMULA_UNABLETOFFIND;
|
||||
}
|
||||
|
||||
@ -112,7 +116,8 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||
}
|
||||
}
|
||||
|
||||
myLastErrors.addEntry(F("CALC: Internal error finding formula."));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CALC: Internal error finding formula."));
|
||||
return ERR_FORMULA_INTERNAL;
|
||||
}
|
||||
|
||||
@ -157,7 +162,8 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
|
||||
return g;
|
||||
}
|
||||
|
||||
myLastErrors.addEntry("CALC: Failed to parse gravity expression " + String(err));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("CALC: Failed to parse gravity expression " + String(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -203,8 +209,10 @@ double gravityTemperatureCorrectionC(double gravity, double tempC,
|
||||
return g;
|
||||
}
|
||||
|
||||
myLastErrors.addEntry(
|
||||
"CALC: Failed to parse expression for gravity temperature correction " + String(err));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(
|
||||
"CALC: Failed to parse expression for gravity temperature correction " +
|
||||
String(err));
|
||||
return gravity;
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,8 @@ bool Config::saveFile() {
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "w");
|
||||
|
||||
if (!configFile) {
|
||||
myLastErrors.addEntry(F("CFG : Failed to save configuration."));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CFG : Failed to save configuration."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -164,14 +165,16 @@ bool Config::loadFile() {
|
||||
#endif
|
||||
|
||||
if (!LittleFS.exists(CFG_FILENAME)) {
|
||||
myLastErrors.addEntry(F("CFG : Configuration file does not exist."));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CFG : Configuration file does not exist."));
|
||||
return false;
|
||||
}
|
||||
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
||||
|
||||
if (!configFile) {
|
||||
myLastErrors.addEntry(F("CFG : Failed to load configuration."));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CFG : Failed to load configuration."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -187,7 +190,8 @@ bool Config::loadFile() {
|
||||
configFile.close();
|
||||
|
||||
if (err) {
|
||||
myLastErrors.addEntry(F("CFG : Failed to parse configuration (json)"));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CFG : Failed to parse configuration (json)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -328,7 +332,8 @@ bool HardwareConfig::saveFile() {
|
||||
File configFile = LittleFS.open(CFG_HW_FILENAME, "w");
|
||||
|
||||
if (!configFile) {
|
||||
myLastErrors.addEntry(F("CFG : Failed to write hardware configuration "));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CFG : Failed to write hardware configuration "));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -371,7 +376,8 @@ bool HardwareConfig::loadFile() {
|
||||
File configFile = LittleFS.open(CFG_HW_FILENAME, "r");
|
||||
|
||||
if (!configFile) {
|
||||
myLastErrors.addEntry(F("CFG : Failed to read hardware configuration "));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CFG : Failed to read hardware configuration "));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -387,8 +393,8 @@ bool HardwareConfig::loadFile() {
|
||||
configFile.close();
|
||||
|
||||
if (err) {
|
||||
myLastErrors.addEntry(
|
||||
F("CFG : Failed to parse hardware configuration (json)"));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("CFG : Failed to parse hardware configuration (json)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -154,6 +154,7 @@ class Config {
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isOtaActive() { return _otaURL.length() ? true : false; }
|
||||
bool isOtaSSL() { return _otaURL.startsWith("https://"); }
|
||||
|
||||
const char* getWifiSSID() { return _wifiSSID.c_str(); }
|
||||
void setWifiSSID(String s) {
|
||||
@ -195,6 +196,7 @@ class Config {
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive() { return _httpUrl.length() ? true : false; }
|
||||
bool isHttpSSL() { return _httpUrl.startsWith("https://"); }
|
||||
|
||||
const char* getHttp2Url() { return _http2Url.c_str(); }
|
||||
void setHttp2Url(String s) {
|
||||
@ -206,7 +208,8 @@ class Config {
|
||||
_http2Header[idx] = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive2() { return _http2Url.length() ? true : false; }
|
||||
bool isHttp2Active() { return _http2Url.length() ? true : false; }
|
||||
bool isHttp2SSL() { return _http2Url.startsWith("https://"); }
|
||||
|
||||
// InfluxDB2
|
||||
const char* getInfluxDb2PushUrl() { return _influxDb2Url.c_str(); }
|
||||
@ -232,12 +235,14 @@ class Config {
|
||||
}
|
||||
|
||||
// MQTT
|
||||
bool isMqttActive() { return _mqttUrl.length() ? true : false; }
|
||||
const char* getMqttUrl() { return _mqttUrl.c_str(); }
|
||||
void setMqttUrl(String s) {
|
||||
_mqttUrl = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isMqttActive() { return _mqttUrl.length() ? true : false; }
|
||||
bool isMqttSSL() { return _mqttPort > 8000 ? true : false; }
|
||||
|
||||
int getMqttPort() { return _mqttPort; }
|
||||
void setMqttPort(String s) {
|
||||
_mqttPort = s.toInt();
|
||||
|
11
src/gyro.cpp
11
src/gyro.cpp
@ -44,7 +44,8 @@ bool GyroSensor::setup() {
|
||||
// compilation difficulties
|
||||
|
||||
if (!accelgyro.testConnection()) {
|
||||
myLastErrors.addEntry(F("GYRO: Failed to connect to gyro, is it connected?"));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("GYRO: Failed to connect to gyro, is it connected?"));
|
||||
_sensorConnected = false;
|
||||
} else {
|
||||
#if !defined(GYRO_DISABLE_LOGGING)
|
||||
@ -240,14 +241,14 @@ bool GyroSensor::read() {
|
||||
// If the sensor is unstable we return false to signal we dont have valid
|
||||
// value
|
||||
if (isSensorMoving(_lastGyroData)) {
|
||||
#if !defined(GYRO_DISABLE_LOGGING)
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.notice(F("GYRO: Sensor is moving." CR));
|
||||
#endif
|
||||
_validValue = false;
|
||||
} else {
|
||||
_validValue = true;
|
||||
_angle = calculateAngle(_lastGyroData);
|
||||
#if !defined(GYRO_DISABLE_LOGGING)
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.verbose(F("GYRO: Sensor values %d,%d,%d\t%F" CR), _lastGyroData.ax,
|
||||
_lastGyroData.ay, _lastGyroData.az, _angle);
|
||||
#endif
|
||||
@ -286,7 +287,9 @@ void GyroSensor::applyCalibration() {
|
||||
if ((_calibrationOffset.ax + _calibrationOffset.ay + _calibrationOffset.az +
|
||||
_calibrationOffset.gx + _calibrationOffset.gy + _calibrationOffset.gz) ==
|
||||
0) {
|
||||
myLastErrors.addEntry(F("GYRO: No valid calibration values, please calibrate the device."));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(
|
||||
F("GYRO: No valid calibration values, please calibrate the device."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -37,9 +37,16 @@ SOFTWARE.
|
||||
#include <wifi.hpp>
|
||||
|
||||
SerialDebug mySerial;
|
||||
ErrorFileLog myLastErrors;
|
||||
BatteryVoltage myBatteryVoltage;
|
||||
|
||||
// tcp cleanup, to avoid memory crash.
|
||||
struct tcp_pcb;
|
||||
extern struct tcp_pcb* tcp_tw_pcbs;
|
||||
extern "C" void tcp_abort(struct tcp_pcb* pcb);
|
||||
void tcp_cleanup() {
|
||||
while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs);
|
||||
}
|
||||
|
||||
//
|
||||
// Convert sg to plato
|
||||
//
|
||||
@ -61,7 +68,7 @@ float convertCtoF(float c) { return (c * 1.8) + 32.0; }
|
||||
float convertFtoC(float f) { return (f - 32.0) / 1.8; }
|
||||
|
||||
//
|
||||
//
|
||||
// Load error log from disk
|
||||
//
|
||||
ErrorFileLog::ErrorFileLog() {
|
||||
File errFile = LittleFS.open(ERR_FILENAME, "r");
|
||||
@ -76,7 +83,7 @@ ErrorFileLog::ErrorFileLog() {
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Add new entry to top of error log
|
||||
//
|
||||
void ErrorFileLog::addEntry(String err) {
|
||||
for (int i = (ERR_COUNT - 1); i > 0; i--) {
|
||||
@ -88,7 +95,7 @@ void ErrorFileLog::addEntry(String err) {
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Save error log
|
||||
//
|
||||
void ErrorFileLog::save() {
|
||||
File errFile = LittleFS.open(ERR_FILENAME, "w");
|
||||
@ -101,13 +108,9 @@ void ErrorFileLog::save() {
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Load history log of floats
|
||||
//
|
||||
FloatHistoryLog::FloatHistoryLog(String fName) {
|
||||
/*File debug = LittleFS.open(fName, "r");
|
||||
String s = debug.readString();
|
||||
Serial.println( s.c_str() );
|
||||
debug.close();*/
|
||||
_fName = fName;
|
||||
|
||||
File runFile = LittleFS.open(_fName, "r");
|
||||
@ -125,7 +128,7 @@ FloatHistoryLog::FloatHistoryLog(String fName) {
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Add entry to top of log
|
||||
//
|
||||
void FloatHistoryLog::addEntry(float time) {
|
||||
for (int i = (10 - 1); i > 0; i--) {
|
||||
@ -136,7 +139,7 @@ void FloatHistoryLog::addEntry(float time) {
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Save log
|
||||
//
|
||||
void FloatHistoryLog::save() {
|
||||
File runFile = LittleFS.open(_fName, "w");
|
||||
@ -151,12 +154,16 @@ void FloatHistoryLog::save() {
|
||||
//
|
||||
// Print the heap information.
|
||||
//
|
||||
void printHeap() {
|
||||
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
|
||||
void printHeap(String prefix) {
|
||||
#if LOG_LEVEL == 6 || LOG_LEVEL == 5
|
||||
#if defined(ESP8266)
|
||||
Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR),
|
||||
ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(),
|
||||
ESP.getFreeSketchSpace() / 1024);
|
||||
Log.notice(
|
||||
F("%s: Free-heap %d kb, Heap-rag %d %%, Max-block %d kb Stack=%d b." CR),
|
||||
prefix.c_str(), ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(),
|
||||
ESP.getMaxFreeBlockSize() / 1024, ESP.getFreeContStack());
|
||||
// Log.notice(F("%s: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR),
|
||||
// prefix.c_str(), ESP.getFreeHeap() / 1024,
|
||||
// ESP.getHeapFragmentation(), ESP.getFreeSketchSpace() / 1024);
|
||||
#else // defined (ESP32)
|
||||
Log.verbose(F("HELP: Heap %d kb, FreeSketch %d kb." CR),
|
||||
ESP.getFreeHeap() / 1024, ESP.getFreeSketchSpace() / 1024);
|
||||
@ -306,13 +313,14 @@ void PerfLogging::print() {
|
||||
void PerfLogging::pushInflux() {
|
||||
if (!myConfig.isInfluxDb2Active()) return;
|
||||
|
||||
WiFiClient wifi;
|
||||
HTTPClient http;
|
||||
String serverPath =
|
||||
String(myConfig.getInfluxDb2PushUrl()) +
|
||||
"/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) +
|
||||
"&bucket=" + String(myConfig.getInfluxDb2PushBucket());
|
||||
|
||||
http.begin(myWifi.getWifiClient(), serverPath);
|
||||
http.begin(wifi, serverPath);
|
||||
|
||||
// Create body for influxdb2, format used
|
||||
// key,host=mdns value=0.0
|
||||
@ -375,7 +383,8 @@ void PerfLogging::pushInflux() {
|
||||
}
|
||||
|
||||
http.end();
|
||||
myWifi.closeWifiClient();
|
||||
wifi.stop();
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
#endif // COLLECT_PERFDATA
|
||||
|
@ -32,6 +32,9 @@ SOFTWARE.
|
||||
|
||||
#define RUNTIME_FILENAME "/runtime.log"
|
||||
|
||||
// tcp cleanup
|
||||
void tcp_cleanup();
|
||||
|
||||
// Sleep mode
|
||||
void deepSleep(int t);
|
||||
|
||||
@ -55,7 +58,7 @@ float reduceFloatPrecision(float f, int dec = 2);
|
||||
// Logging via serial
|
||||
void printTimestamp(Print* _logOutput, int _logLevel);
|
||||
void printNewline(Print* _logOutput);
|
||||
void printHeap();
|
||||
void printHeap(String prefix = "HELP");
|
||||
|
||||
// Classes
|
||||
class SerialDebug {
|
||||
@ -83,7 +86,7 @@ class FloatHistoryLog {
|
||||
void save();
|
||||
|
||||
public:
|
||||
FloatHistoryLog(String fName);
|
||||
explicit FloatHistoryLog(String fName);
|
||||
void addEntry(float time);
|
||||
float getAverage() { return _average; }
|
||||
};
|
||||
@ -185,7 +188,6 @@ extern PerfLogging myPerfLogging;
|
||||
|
||||
// Global instance created
|
||||
extern SerialDebug mySerial;
|
||||
extern ErrorFileLog myLastErrors;
|
||||
extern BatteryVoltage myBatteryVoltage;
|
||||
|
||||
#endif // SRC_HELPER_HPP_
|
||||
|
26
src/main.cpp
26
src/main.cpp
@ -40,6 +40,7 @@ int interval = 200; // ms, time to wait between changes to output
|
||||
bool sleepModeAlwaysSkip =
|
||||
false; // Flag set in web interface to override normal behaviour
|
||||
uint32_t loopMillis = 0; // Used for main loop to run the code every _interval_
|
||||
uint32_t pushMillis = 0; // Used to control how often we will send push data
|
||||
uint32_t runtimeMillis; // Used to calculate the total time since start/wakeup
|
||||
uint32_t stableGyroMillis; // Used to calculate the total time since last
|
||||
// stable gyro reading
|
||||
@ -173,7 +174,9 @@ void setup() {
|
||||
LOG_PERF_STOP("main-temp-setup");
|
||||
|
||||
if (!myGyro.setup()) {
|
||||
myLastErrors.addEntry(F("MAIN: Failed to initialize the gyro, is it connected?"));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(
|
||||
F("MAIN: Failed to initialize the gyro, is it connected?"));
|
||||
} else {
|
||||
LOG_PERF_START("main-gyro-read");
|
||||
myGyro.read();
|
||||
@ -207,7 +210,8 @@ void setup() {
|
||||
|
||||
LOG_PERF_STOP("main-setup");
|
||||
Log.notice(F("Main: Setup completed." CR));
|
||||
stableGyroMillis = millis(); // Dont include time for wifi connection
|
||||
pushMillis = stableGyroMillis =
|
||||
millis(); // Dont include time for wifi connection
|
||||
}
|
||||
|
||||
//
|
||||
@ -245,10 +249,15 @@ bool loopReadGravity() {
|
||||
#endif
|
||||
|
||||
LOG_PERF_START("loop-push");
|
||||
// Force the transmission if we are going to sleep
|
||||
myPushTarget.send(angle, gravitySG, corrGravitySG, tempC,
|
||||
(millis() - runtimeMillis) / 1000,
|
||||
runMode == RunMode::gravityMode ? true : false);
|
||||
bool pushExpired = (abs((int32_t)(millis() - pushMillis)) >
|
||||
(myConfig.getSleepInterval() * 1000));
|
||||
|
||||
if (pushExpired || runMode == RunMode::gravityMode) {
|
||||
pushMillis = millis();
|
||||
PushTarget push;
|
||||
push.send(angle, gravitySG, corrGravitySG, tempC,
|
||||
(millis() - runtimeMillis) / 1000);
|
||||
}
|
||||
LOG_PERF_STOP("loop-push");
|
||||
return true;
|
||||
} else {
|
||||
@ -265,7 +274,7 @@ void loopGravityOnInterval() {
|
||||
if (abs((int32_t)(millis() - loopMillis)) > interval) {
|
||||
loopReadGravity();
|
||||
loopMillis = millis();
|
||||
printHeap();
|
||||
// printHeap("MAIN");
|
||||
LOG_PERF_START("loop-gyro-read");
|
||||
myGyro.read();
|
||||
LOG_PERF_STOP("loop-gyro-read");
|
||||
@ -310,8 +319,7 @@ void loop() {
|
||||
loopGravityOnInterval();
|
||||
|
||||
// If we switched mode, dont include this in the log.
|
||||
if (runMode!=RunMode::configurationMode)
|
||||
skipRunTimeLog = true;
|
||||
if (runMode != RunMode::configurationMode) skipRunTimeLog = true;
|
||||
break;
|
||||
|
||||
case RunMode::gravityMode:
|
||||
|
@ -32,40 +32,14 @@ SOFTWARE.
|
||||
#include <pushtarget.hpp>
|
||||
#include <wifi.hpp>
|
||||
|
||||
PushTarget myPushTarget;
|
||||
|
||||
//
|
||||
// Send the data to targets
|
||||
//
|
||||
void PushTarget::send(float angle, float gravitySG, float corrGravitySG,
|
||||
float tempC, float runTime, bool force) {
|
||||
uint32_t timePassed = abs((int32_t)(millis() - _ms));
|
||||
uint32_t interval = myConfig.getSleepInterval() * 1000;
|
||||
|
||||
if ((timePassed < interval) && !force) {
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: Timer has not expired %l vs %l." CR), timePassed,
|
||||
interval);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
_ms = millis();
|
||||
|
||||
#if defined(ESP8266)
|
||||
if (ESP.getFreeContStack() < 1500) {
|
||||
if (!_memErrorReported) {
|
||||
myLastErrors.addEntry("PUSH: Low on memory, skipping push " +
|
||||
String(ESP.getFreeContStack()));
|
||||
} else {
|
||||
Log.error(F("PUSH: Low on memory, skipping push %d" CR),
|
||||
ESP.getFreeContStack());
|
||||
}
|
||||
_memErrorReported = true; // Dont report this again unti restarted.
|
||||
myWifi.closeWifiClient();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
float tempC, float runTime) {
|
||||
printHeap("StartPush");
|
||||
http.setReuse(false);
|
||||
httpSecure.setReuse(false);
|
||||
|
||||
TemplatingEngine engine;
|
||||
engine.initialize(angle, gravitySG, corrGravitySG, tempC, runTime);
|
||||
@ -76,15 +50,17 @@ void PushTarget::send(float angle, float gravitySG, float corrGravitySG,
|
||||
LOG_PERF_STOP("push-brewfather");
|
||||
}
|
||||
|
||||
printHeap("http1");
|
||||
if (myConfig.isHttpActive()) {
|
||||
LOG_PERF_START("push-http");
|
||||
sendHttp(engine, 0);
|
||||
sendHttp(engine, myConfig.isHttpSSL(), 0);
|
||||
LOG_PERF_STOP("push-http");
|
||||
}
|
||||
|
||||
if (myConfig.isHttpActive2()) {
|
||||
printHeap("http2");
|
||||
if (myConfig.isHttp2Active()) {
|
||||
LOG_PERF_START("push-http2");
|
||||
sendHttp(engine, 1);
|
||||
sendHttp(engine, myConfig.isHttp2SSL(), 1);
|
||||
LOG_PERF_STOP("push-http2");
|
||||
}
|
||||
|
||||
@ -96,7 +72,7 @@ void PushTarget::send(float angle, float gravitySG, float corrGravitySG,
|
||||
|
||||
if (myConfig.isMqttActive()) {
|
||||
LOG_PERF_START("push-mqtt");
|
||||
sendMqtt(engine);
|
||||
sendMqtt(engine, myConfig.isMqttSSL());
|
||||
LOG_PERF_STOP("push-mqtt");
|
||||
}
|
||||
|
||||
@ -117,15 +93,13 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) {
|
||||
"&bucket=" + String(myConfig.getInfluxDb2PushBucket());
|
||||
String doc = engine.create(TemplatingEngine::TEMPLATE_INFLUX);
|
||||
|
||||
HTTPClient http;
|
||||
http.begin(myWifi.getWifiClient(), serverPath);
|
||||
http.begin(wifi, serverPath);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: data %s." CR), doc.c_str());
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
|
||||
http.addHeader(F("Authorization"), auth.c_str());
|
||||
int httpResponseCode = http.POST(doc);
|
||||
@ -134,12 +108,14 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) {
|
||||
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
myLastErrors.addEntry("PUSH: Influxdb push failed response=" +
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("PUSH: Influxdb push failed response=" +
|
||||
String(httpResponseCode));
|
||||
}
|
||||
|
||||
http.end();
|
||||
myWifi.closeWifiClient();
|
||||
wifi.stop();
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
//
|
||||
@ -153,16 +129,13 @@ void PushTarget::sendBrewfather(TemplatingEngine& engine) {
|
||||
String serverPath = myConfig.getBrewfatherPushUrl();
|
||||
String doc = engine.create(TemplatingEngine::TEMPLATE_BREWFATHER);
|
||||
|
||||
// Your Domain name with URL path or IP address with path
|
||||
HTTPClient http;
|
||||
http.begin(myWifi.getWifiClient(), serverPath);
|
||||
http.begin(wifi, serverPath);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), doc.c_str());
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
http.addHeader(F("Content-Type"), F("application/json"));
|
||||
int httpResponseCode = http.POST(doc);
|
||||
|
||||
@ -170,12 +143,14 @@ void PushTarget::sendBrewfather(TemplatingEngine& engine) {
|
||||
Log.notice(F("PUSH: Brewfather push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
myLastErrors.addEntry("PUSH: Brewfather push failed response=" +
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("PUSH: Brewfather push failed response=" +
|
||||
String(httpResponseCode));
|
||||
}
|
||||
|
||||
http.end();
|
||||
myWifi.closeWifiClient();
|
||||
wifi.stop();
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
//
|
||||
@ -193,21 +168,20 @@ void PushTarget::addHttpHeader(HTTPClient& http, String header) {
|
||||
value.c_str());
|
||||
http.addHeader(name, value);
|
||||
} else {
|
||||
myLastErrors.addEntry("PUSH: Unable to set header, invalid value " + header);
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("PUSH: Unable to set header, invalid value " + header);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendHttp(TemplatingEngine& engine, int index) {
|
||||
void PushTarget::sendHttp(TemplatingEngine& engine, bool isSecure, int index) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(F("PUSH: Sending values to http (%s)" CR),
|
||||
index ? "http2" : "http");
|
||||
#endif
|
||||
|
||||
String serverPath, doc;
|
||||
HTTPClient http;
|
||||
|
||||
if (index == 0) {
|
||||
serverPath = myConfig.getHttpUrl();
|
||||
@ -217,14 +191,30 @@ void PushTarget::sendHttp(TemplatingEngine& engine, int index) {
|
||||
doc = engine.create(TemplatingEngine::TEMPLATE_HTTP2);
|
||||
}
|
||||
|
||||
if (serverPath.startsWith("https://")) {
|
||||
myWifi.getWifiClientSecure().setInsecure();
|
||||
int httpResponseCode;
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), doc.c_str());
|
||||
#endif
|
||||
|
||||
if (isSecure) {
|
||||
Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR));
|
||||
http.begin(myWifi.getWifiClientSecure(), serverPath);
|
||||
wifiSecure.setInsecure();
|
||||
httpSecure.begin(wifiSecure, serverPath);
|
||||
|
||||
if (index == 0) {
|
||||
addHttpHeader(httpSecure, myConfig.getHttpHeader(0));
|
||||
addHttpHeader(httpSecure, myConfig.getHttpHeader(1));
|
||||
} else {
|
||||
http.begin(myWifi.getWifiClient(), serverPath);
|
||||
addHttpHeader(httpSecure, myConfig.getHttp2Header(0));
|
||||
addHttpHeader(httpSecure, myConfig.getHttp2Header(1));
|
||||
}
|
||||
|
||||
httpResponseCode = httpSecure.POST(doc);
|
||||
} else {
|
||||
http.begin(wifi, serverPath);
|
||||
|
||||
if (index == 0) {
|
||||
addHttpHeader(http, myConfig.getHttpHeader(0));
|
||||
addHttpHeader(http, myConfig.getHttpHeader(1));
|
||||
@ -233,32 +223,33 @@ void PushTarget::sendHttp(TemplatingEngine& engine, int index) {
|
||||
addHttpHeader(http, myConfig.getHttp2Header(1));
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), doc.c_str());
|
||||
#endif
|
||||
|
||||
// Send HTTP POST request
|
||||
// http.addHeader(F("Content-Type"), F("application/json"));
|
||||
int httpResponseCode = http.POST(doc);
|
||||
httpResponseCode = http.POST(doc);
|
||||
}
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
Log.notice(F("PUSH: HTTP push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
myLastErrors.addEntry(
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(
|
||||
"PUSH: HTTP push failed response=" + String(httpResponseCode) +
|
||||
String(index == 0 ? " (http)" : " (http2)"));
|
||||
}
|
||||
|
||||
if (isSecure) {
|
||||
httpSecure.end();
|
||||
wifiSecure.stop();
|
||||
} else {
|
||||
http.end();
|
||||
myWifi.closeWifiClient();
|
||||
wifi.stop();
|
||||
}
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendMqtt(TemplatingEngine& engine) {
|
||||
void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(F("PUSH: Sending values to mqtt." CR));
|
||||
#endif
|
||||
@ -268,13 +259,12 @@ void PushTarget::sendMqtt(TemplatingEngine& engine) {
|
||||
String doc = engine.create(TemplatingEngine::TEMPLATE_MQTT);
|
||||
int port = myConfig.getMqttPort();
|
||||
|
||||
if (port > 8000) {
|
||||
// Allow secure channel, but without certificate validation
|
||||
myWifi.getWifiClientSecure().setInsecure();
|
||||
if (myConfig.isMqttSSL()) {
|
||||
Log.notice(F("PUSH: MQTT, SSL enabled without validation." CR));
|
||||
mqtt.begin(url.c_str(), port, myWifi.getWifiClientSecure());
|
||||
wifiSecure.setInsecure();
|
||||
mqtt.begin(url.c_str(), port, wifiSecure);
|
||||
} else {
|
||||
mqtt.begin(myConfig.getMqttUrl(), port, myWifi.getWifiClient());
|
||||
mqtt.begin(myConfig.getMqttUrl(), port, wifi);
|
||||
}
|
||||
|
||||
mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(),
|
||||
@ -310,7 +300,8 @@ void PushTarget::sendMqtt(TemplatingEngine& engine) {
|
||||
if (mqtt.publish(topic, value)) {
|
||||
Log.notice(F("PUSH: MQTT publish successful on %s" CR), topic.c_str());
|
||||
} else {
|
||||
myLastErrors.addEntry("PUSH: MQTT push on " + topic +
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("PUSH: MQTT push on " + topic +
|
||||
" failed error=" + String(mqtt.lastError()));
|
||||
}
|
||||
|
||||
@ -319,7 +310,12 @@ void PushTarget::sendMqtt(TemplatingEngine& engine) {
|
||||
}
|
||||
|
||||
mqtt.disconnect();
|
||||
myWifi.closeWifiClient();
|
||||
if (isSecure) {
|
||||
wifiSecure.stop();
|
||||
} else {
|
||||
wifi.stop();
|
||||
}
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -28,30 +28,29 @@ SOFTWARE.
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#else // defined (ESP32)
|
||||
#include <HTTPClient.h>
|
||||
#endif
|
||||
|
||||
class PushTarget {
|
||||
private:
|
||||
uint32_t _ms; // Used to check that we do not post to often
|
||||
bool _memErrorReported =
|
||||
false; // Avoid filling the error log with memory errors.
|
||||
WiFiClient wifi;
|
||||
WiFiClientSecure wifiSecure;
|
||||
HTTPClient http;
|
||||
HTTPClient httpSecure;
|
||||
|
||||
void sendBrewfather(TemplatingEngine& engine);
|
||||
void sendHttp(TemplatingEngine& engine, int index);
|
||||
void sendHttp(TemplatingEngine& engine, bool isSecure, int index);
|
||||
void sendInfluxDb2(TemplatingEngine& engine);
|
||||
void sendMqtt(TemplatingEngine& engine);
|
||||
void sendMqtt(TemplatingEngine& engine, bool isSecure);
|
||||
void addHttpHeader(HTTPClient& http, String header);
|
||||
|
||||
public:
|
||||
PushTarget() { _ms = millis(); }
|
||||
void send(float angle, float gravitySG, float corrGravitySG, float tempC,
|
||||
float runTime, bool force = false);
|
||||
float runTime);
|
||||
};
|
||||
|
||||
extern PushTarget myPushTarget;
|
||||
|
||||
#endif // SRC_PUSHTARGET_HPP_
|
||||
|
||||
// EOF
|
||||
|
@ -52,7 +52,8 @@ void WebServerHandler::webHandleDevice() {
|
||||
doc[PARAM_MDNS] = myConfig.getMDNS();
|
||||
|
||||
FloatHistoryLog runLog(RUNTIME_FILENAME);
|
||||
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(runLog.getAverage()?runLog.getAverage()/1000:0, 1);
|
||||
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(
|
||||
runLog.getAverage() ? runLog.getAverage() / 1000 : 0, 1);
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
serializeJson(doc, Serial);
|
||||
@ -97,7 +98,8 @@ void WebServerHandler::webHandleConfig() {
|
||||
doc[PARAM_BATTERY] = reduceFloatPrecision(myBatteryVoltage.getVoltage());
|
||||
|
||||
FloatHistoryLog runLog(RUNTIME_FILENAME);
|
||||
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(runLog.getAverage()?runLog.getAverage()/1000:0, 1);
|
||||
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(
|
||||
runLog.getAverage() ? runLog.getAverage() / 1000 : 0, 1);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
serializeJson(doc, Serial);
|
||||
@ -211,7 +213,8 @@ void WebServerHandler::webHandleFactoryDefaults() {
|
||||
Log.notice(F("WEB : webServer callback for /api/factory." CR));
|
||||
|
||||
if (!id.compareTo(myConfig.getID())) {
|
||||
_server->send(200, "text/plain", "Removing configuration and restarting...");
|
||||
_server->send(200, "text/plain",
|
||||
"Removing configuration and restarting...");
|
||||
LittleFS.remove(CFG_FILENAME);
|
||||
LittleFS.remove(CFG_HW_FILENAME);
|
||||
LittleFS.remove(ERR_FILENAME);
|
||||
@ -386,7 +389,8 @@ void WebServerHandler::webHandleConfigPush() {
|
||||
if (_server->hasArg(PARAM_PUSH_INFLUXDB2))
|
||||
myConfig.setInfluxDb2PushUrl(_server->arg(PARAM_PUSH_INFLUXDB2).c_str());
|
||||
if (_server->hasArg(PARAM_PUSH_INFLUXDB2_ORG))
|
||||
myConfig.setInfluxDb2PushOrg(_server->arg(PARAM_PUSH_INFLUXDB2_ORG).c_str());
|
||||
myConfig.setInfluxDb2PushOrg(
|
||||
_server->arg(PARAM_PUSH_INFLUXDB2_ORG).c_str());
|
||||
if (_server->hasArg(PARAM_PUSH_INFLUXDB2_BUCKET))
|
||||
myConfig.setInfluxDb2PushBucket(
|
||||
_server->arg(PARAM_PUSH_INFLUXDB2_BUCKET).c_str());
|
||||
@ -672,7 +676,8 @@ void WebServerHandler::webHandleConfigFormatWrite() {
|
||||
_server->sendHeader("Location", "/format.htm", true);
|
||||
_server->send(302, "text/plain", "Format updated");
|
||||
} else {
|
||||
myLastErrors.addEntry(F("WEB : Unable to store format file"));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("WEB : Unable to store format file"));
|
||||
_server->send(400, "text/plain", "Unable to store format in file.");
|
||||
}
|
||||
|
||||
|
57
src/wifi.cpp
57
src/wifi.cpp
@ -23,11 +23,14 @@ SOFTWARE.
|
||||
*/
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266httpUpdate.h>
|
||||
#else // defined (ESP32)
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#endif
|
||||
#include <incbin.h>
|
||||
|
||||
@ -192,7 +195,8 @@ bool WifiConnection::waitForConnection(int maxTime) {
|
||||
|
||||
if (i++ >
|
||||
(maxTime * 10)) { // Try for maxTime seconds. Since delay is 100ms.
|
||||
myLastErrors.addEntry("WIFI: Failed to connect to wifi " +
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("WIFI: Failed to connect to wifi " +
|
||||
String(WiFi.status()));
|
||||
WiFi.disconnect();
|
||||
Serial.print(CR);
|
||||
@ -237,31 +241,36 @@ bool WifiConnection::updateFirmware() {
|
||||
Log.verbose(F("WIFI: Updating firmware." CR));
|
||||
#endif
|
||||
|
||||
WiFiClient wifi;
|
||||
WiFiClientSecure wifiSecure;
|
||||
HTTPUpdateResult ret;
|
||||
String serverPath = myConfig.getOtaURL();
|
||||
serverPath += "firmware.bin";
|
||||
HTTPUpdateResult ret;
|
||||
|
||||
if (serverPath.startsWith("https://")) {
|
||||
myWifi.getWifiClientSecure().setInsecure();
|
||||
wifiSecure.setInsecure();
|
||||
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
|
||||
ret = ESPhttpUpdate.update(myWifi.getWifiClientSecure(), serverPath);
|
||||
ret = ESPhttpUpdate.update(wifiSecure, serverPath);
|
||||
} else {
|
||||
ret = ESPhttpUpdate.update(myWifi.getWifiClient(), serverPath);
|
||||
ret = ESPhttpUpdate.update(wifi, serverPath);
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
myLastErrors.addEntry("WIFI: OTA update failed " +
|
||||
case HTTP_UPDATE_FAILED: {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("WIFI: OTA update failed " +
|
||||
String(ESPhttpUpdate.getLastError()));
|
||||
break;
|
||||
} break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
case HTTP_UPDATE_OK: {
|
||||
Log.notice("WIFI: OTA Update sucesfull, rebooting.");
|
||||
delay(100);
|
||||
ESP_RESET();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -272,16 +281,18 @@ void WifiConnection::downloadFile(const char *fname) {
|
||||
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
|
||||
Log.verbose(F("WIFI: Download file %s." CR), fname);
|
||||
#endif
|
||||
WiFiClient wifi;
|
||||
WiFiClientSecure wifiSecure;
|
||||
HTTPClient http;
|
||||
String serverPath = myConfig.getOtaURL();
|
||||
serverPath += fname;
|
||||
|
||||
if (serverPath.startsWith("https://")) {
|
||||
myWifi.getWifiClientSecure().setInsecure();
|
||||
if (myConfig.isOtaSSL()) {
|
||||
wifiSecure.setInsecure();
|
||||
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
|
||||
http.begin(myWifi.getWifiClientSecure(), serverPath);
|
||||
http.begin(wifiSecure, serverPath);
|
||||
} else {
|
||||
http.begin(myWifi.getWifiClient(), serverPath);
|
||||
http.begin(wifi, serverPath);
|
||||
}
|
||||
|
||||
int httpResponseCode = http.GET();
|
||||
@ -292,11 +303,11 @@ void WifiConnection::downloadFile(const char *fname) {
|
||||
f.close();
|
||||
Log.notice(F("WIFI: Downloaded file %s." CR), fname);
|
||||
} else {
|
||||
myLastErrors.addEntry("WIFI: Failed to download html-file " +
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("WIFI: Failed to download html-file " +
|
||||
String(httpResponseCode));
|
||||
}
|
||||
http.end();
|
||||
myWifi.closeWifiClient();
|
||||
}
|
||||
|
||||
//
|
||||
@ -306,17 +317,19 @@ bool WifiConnection::checkFirmwareVersion() {
|
||||
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
|
||||
Log.verbose(F("WIFI: Checking if new version exist." CR));
|
||||
#endif
|
||||
WiFiClient wifi;
|
||||
WiFiClientSecure wifiSecure;
|
||||
HTTPClient http;
|
||||
String serverPath = myConfig.getOtaURL();
|
||||
serverPath += "version.json";
|
||||
|
||||
// Your Domain name with URL path or IP address with path
|
||||
if (serverPath.startsWith("https://")) {
|
||||
myWifi.getWifiClientSecure().setInsecure();
|
||||
if (myConfig.isOtaSSL()) {
|
||||
wifiSecure.setInsecure();
|
||||
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
|
||||
http.begin(myWifi.getWifiClientSecure(), serverPath);
|
||||
http.begin(wifiSecure, serverPath);
|
||||
} else {
|
||||
http.begin(myWifi.getWifiClient(), serverPath);
|
||||
http.begin(wifi, serverPath);
|
||||
}
|
||||
|
||||
// Send HTTP GET request
|
||||
@ -332,7 +345,8 @@ bool WifiConnection::checkFirmwareVersion() {
|
||||
DynamicJsonDocument ver(300);
|
||||
DeserializationError err = deserializeJson(ver, payload);
|
||||
if (err) {
|
||||
myLastErrors.addEntry(F("WIFI: Failed to parse version.json"));
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(F("WIFI: Failed to parse version.json"));
|
||||
} else {
|
||||
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
|
||||
Log.verbose(F("WIFI: Project %s version %s." CR),
|
||||
@ -378,11 +392,12 @@ bool WifiConnection::checkFirmwareVersion() {
|
||||
httpResponseCode);
|
||||
}
|
||||
http.end();
|
||||
myWifi.closeWifiClient();
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: OTA found new version %s." CR),
|
||||
_newFirmware ? "true" : "false");
|
||||
#endif
|
||||
|
||||
return _newFirmware;
|
||||
}
|
||||
|
||||
|
24
src/wifi.hpp
24
src/wifi.hpp
@ -24,28 +24,14 @@ SOFTWARE.
|
||||
#ifndef SRC_WIFI_HPP_
|
||||
#define SRC_WIFI_HPP_
|
||||
|
||||
#if defined (ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#else // defined (ESP32)
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
// tcp cleanup, to avoid memory crash.
|
||||
struct tcp_pcb;
|
||||
extern struct tcp_pcb* tcp_tw_pcbs;
|
||||
extern "C" void tcp_abort(struct tcp_pcb* pcb);
|
||||
|
||||
class WifiConnection {
|
||||
private:
|
||||
// WIFI
|
||||
WiFiClient _client;
|
||||
WiFiClientSecure _secureClient;
|
||||
|
||||
// OTA
|
||||
bool _newFirmware = false;
|
||||
bool parseFirmwareVersionString(int (&num)[3], const char* version);
|
||||
@ -67,16 +53,6 @@ class WifiConnection {
|
||||
void startPortal();
|
||||
void loop();
|
||||
|
||||
WiFiClient& getWifiClient() { return _client; }
|
||||
WiFiClientSecure& getWifiClientSecure() { return _secureClient; }
|
||||
void closeWifiClient() {
|
||||
_client.stop();
|
||||
_secureClient.stop();
|
||||
|
||||
// Cleanup memory allocated by open tcp connetions.
|
||||
while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs);
|
||||
}
|
||||
|
||||
// OTA
|
||||
bool updateFirmware();
|
||||
bool checkFirmwareVersion();
|
||||
|
Loading…
Reference in New Issue
Block a user