Update esp32c3 support

This commit is contained in:
Magnus Persson
2022-10-17 20:19:20 +02:00
parent a9347c7e03
commit 6262cec27d
10 changed files with 150 additions and 59 deletions

View File

@ -26,9 +26,9 @@ SOFTWARE.
#if defined(ESP32) && !defined(ESP32S2) #if defined(ESP32) && !defined(ESP32S2)
#include <Arduino.h>
#include <NimBLEBeacon.h> #include <NimBLEBeacon.h>
#include <NimBLEDevice.h> #include <NimBLEDevice.h>
#include <Arduino.h>
class BleSender { class BleSender {
private: private:

View File

@ -143,7 +143,15 @@ class Config {
String _mDNS = ""; String _mDNS = "";
String _otaURL = ""; String _otaURL = "";
char _tempFormat = 'C'; char _tempFormat = 'C';
#if defined(ESP8266)
float _voltageFactor = 1.59; float _voltageFactor = 1.59;
#elif defined(ESP32C3)
float _voltageFactor = 1.3;
#elif defined(ESP32S2)
float _voltageFactor = 1.3;
#else // ESP32
float _voltageFactor = 1.3;
#endif
float _voltageConfig = 4.15; float _voltageConfig = 4.15;
float _tempSensorAdjC = 0; float _tempSensorAdjC = 0;
int _sleepInterval = 900; int _sleepInterval = 900;

View File

@ -61,32 +61,67 @@ void checkResetReason() {
writeErrorLog(&s[0]); writeErrorLog(&s[0]);
} }
#else // defined (ESP32) #else // defined (ESP32)
RESET_REASON r = rtc_get_reset_reason(0); // We only check cpu0 since we dont use cpu1 on the esp32 RESET_REASON r = rtc_get_reset_reason(
0); // We only check cpu0 since we dont use cpu1 on the esp32
String rStr; String rStr;
switch (r) { switch (r) {
case 0 : rStr = F("None"); break; case 0:
case 1 : rStr = F("vbat power on reset"); break; rStr = F("None");
case 3 : rStr = F("software reset digital core"); break; break;
case 4 : rStr = F("legacy watch dog reset digital core"); break; case 1:
case 5 : rStr = F("deep Sleep reset digital core"); break; rStr = F("vbat power on reset");
case 6 : rStr = F("reset by SLC module, reset digital core"); break; break;
case 7 : rStr = F("timer Group0 Watch dog reset digital core"); break; case 3:
case 8 : rStr = F("timer Group1 Watch dog reset digital core"); break; rStr = F("software reset digital core");
case 9 : rStr = F("RTC Watch dog Reset digital core"); break; break;
case 10 : rStr = F("instrusion tested to reset CPU"); break; case 4:
case 11 : rStr = F("time Group reset CPU"); break; rStr = F("legacy watch dog reset digital core");
case 12 : rStr = F("software reset CPU"); break; break;
case 13 : rStr = F("RTC Watch dog Reset CPU"); break; case 5:
case 14 : rStr = F("for APP CPU, reseted by PRO CPU"); break; rStr = F("deep Sleep reset digital core");
case 15 : rStr = F("reset when the vdd voltage is not stable"); break; break;
case 16 : rStr = F("RTC Watch dog reset digital core and rtc module"); break; case 6:
default : rStr = F("unknown reset reason"); break; rStr = F("reset by SLC module, reset digital core");
break;
case 7:
rStr = F("timer Group0 Watch dog reset digital core");
break;
case 8:
rStr = F("timer Group1 Watch dog reset digital core");
break;
case 9:
rStr = F("RTC Watch dog Reset digital core");
break;
case 10:
rStr = F("instrusion tested to reset CPU");
break;
case 11:
rStr = F("time Group reset CPU");
break;
case 12:
rStr = F("software reset CPU");
break;
case 13:
rStr = F("RTC Watch dog Reset CPU");
break;
case 14:
rStr = F("for APP CPU, reseted by PRO CPU");
break;
case 15:
rStr = F("reset when the vdd voltage is not stable");
break;
case 16:
rStr = F("RTC Watch dog reset digital core and rtc module");
break;
default:
rStr = F("unknown reset reason");
break;
} }
Log.notice(F("HELP: Last reset cause '%s' (%d)" CR), rStr.c_str(), r); Log.notice(F("HELP: Last reset cause '%s' (%d)" CR), rStr.c_str(), r);
#warning "TODO: Implement logging of crashes for esp32" #warning "TODO: Implement logging of crashes for esp32"
#endif #endif
} }
@ -216,6 +251,14 @@ void printTimestamp(Print* _logOutput, int _logLevel) {
_logOutput->print(c); _logOutput->print(c);
} }
BatteryVoltage::BatteryVoltage() {
#if defined(ESP8266)
pinMode(PIN_A0, INPUT);
#else
pinMode(PIN_A0, INPUT_PULLDOWN);
#endif
}
void BatteryVoltage::read() { void BatteryVoltage::read() {
// The analog pin can only handle 3.3V maximum voltage so we need to reduce // The analog pin can only handle 3.3V maximum voltage so we need to reduce
// the voltage (from max 5V) // the voltage (from max 5V)
@ -363,7 +406,7 @@ void PerfLogging::pushInflux() {
// Send HTTP POST request // Send HTTP POST request
String auth = "Token " + String(myConfig.getInfluxDb2PushToken()); String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
http.addHeader(F("Authorization"), auth.c_str()); http.addHeader(F("Authorization"), auth.c_str());
http.setTimeout(myAdvancedConfig.getPushTimeout()); http.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
int httpResponseCode = http.POST(body); int httpResponseCode = http.POST(body);
if (httpResponseCode == 204) { if (httpResponseCode == 204) {

View File

@ -88,9 +88,10 @@ class FloatHistoryLog {
class BatteryVoltage { class BatteryVoltage {
private: private:
float _batteryLevel; float _batteryLevel = 0;
public: public:
BatteryVoltage();
void read(); void read();
float getVoltage() { return _batteryLevel; } float getVoltage() { return _batteryLevel; }
}; };

View File

@ -121,12 +121,8 @@ void checkSleepMode(float angle, float volt) {
Log.notice( Log.notice(
F("Main: Storage mode entered, going to sleep for maximum time." CR)); F("Main: Storage mode entered, going to sleep for maximum time." CR));
#if defined(ESP8266) #if defined(ESP8266)
// ESP.deepSleep(ESP.deepSleepMax());
ESP.deepSleep(0); // indefinite sleep ESP.deepSleep(0); // indefinite sleep
#else #else
#warning "Check and test the max deep sleep for esp32"
// deepSleep(70 * 60); // quick search on internet suggest max time is 70
// min
ESP.deepSleep(0); // indefinite sleep ESP.deepSleep(0); // indefinite sleep
#endif #endif
} }
@ -141,8 +137,8 @@ void setup() {
// Add a delay so that serial is started. // Add a delay so that serial is started.
// delay(3000); // delay(3000);
#endif #endif
// Main startup
// Main startup
#if defined(ESP8266) #if defined(ESP8266)
Log.notice(F("Main: Started setup for %s." CR), Log.notice(F("Main: Started setup for %s." CR),
String(ESP.getChipId(), HEX).c_str()); String(ESP.getChipId(), HEX).c_str());
@ -161,7 +157,7 @@ void setup() {
LOG_PERF_START("main-config-load"); LOG_PERF_START("main-config-load");
myConfig.checkFileSystem(); myConfig.checkFileSystem();
//checkResetReason(); checkResetReason();
myConfig.loadFile(); myConfig.loadFile();
myWifi.init(); myWifi.init();
myAdvancedConfig.loadFile(); myAdvancedConfig.loadFile();
@ -209,6 +205,8 @@ void setup() {
myBatteryVoltage.read(); myBatteryVoltage.read();
checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage()); checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage());
Log.notice(F("Main: Battery %F V, Gyro=%F, Run-mode=%d." CR),
myBatteryVoltage.getVoltage(), myGyro.getAngle(), runMode);
#if defined(ESP32) #if defined(ESP32)
if (!myConfig.isWifiPushActive() && runMode == RunMode::gravityMode) { if (!myConfig.isWifiPushActive() && runMode == RunMode::gravityMode) {
@ -318,7 +316,7 @@ bool loopReadGravity() {
ble.sendData(convertCtoF(tempC), gravitySG); ble.sendData(convertCtoF(tempC), gravitySG);
Log.notice(F("MAIN: Broadcast data over bluetooth." CR)); Log.notice(F("MAIN: Broadcast data over bluetooth." CR));
} }
#endif // ESP32 && !ESP32S2 #endif // ESP32 && !ESP32S2
if (myWifi.isConnected()) { // no need to try if there is no wifi if (myWifi.isConnected()) { // no need to try if there is no wifi
// connection. // connection.

View File

@ -45,9 +45,10 @@ extern RunMode runMode;
#define PIN_DS D6 #define PIN_DS D6
#define PIN_LED 2 #define PIN_LED 2
// #define PIN_A0 A0 // #define PIN_A0 A0
#elif defined(ESP32C3) #elif defined(ESP32C3)
#include <FS.h> #include <FS.h>
#include <LittleFS.h> #include <LittleFS.h>
#include "esp32c3/rom/rtc.h" #include "esp32c3/rom/rtc.h"
#define ESPhttpUpdate httpUpdate #define ESPhttpUpdate httpUpdate
#define ESP_RESET ESP.restart #define ESP_RESET ESP.restart
@ -62,12 +63,14 @@ extern RunMode runMode;
#endif #endif
#define PIN_DS A3 #define PIN_DS A3
#define PIN_A0 A0 #define PIN_A0 A0
// This should be the LED_BUILTIN, but that is also connected SDA (Gyro) so we cannot use both. So we point LED to pin 8 which is not used. // This should be the LED_BUILTIN, but that is also connected SDA (Gyro) so we
// cannot use both. So we point LED to pin 8 which is not used.
#define PIN_LED 8 #define PIN_LED 8
/* /*
#elif defined(ESP32S2) #elif defined(ESP32S2)
#include <FS.h> #include <FS.h>
#include <LittleFS.h> #include <LittleFS.h>
#include "esp32s2/rom/rtc.h" #include "esp32s2/rom/rtc.h"
#define ESPhttpUpdate httpUpdate #define ESPhttpUpdate httpUpdate
#define ESP_RESET ESP.restart #define ESP_RESET ESP.restart
@ -81,6 +84,7 @@ extern RunMode runMode;
#else // defined (ESP32) #else // defined (ESP32)
#include <FS.h> #include <FS.h>
#include <LittleFS.h> #include <LittleFS.h>
#include "esp32/rom/rtc.h" #include "esp32/rom/rtc.h"
#define ESPhttpUpdate httpUpdate #define ESPhttpUpdate httpUpdate
#define ESP_RESET ESP.restart #define ESP_RESET ESP.restart

View File

@ -24,6 +24,7 @@ SOFTWARE.
#if defined(ESP8266) #if defined(ESP8266)
#define INCBIN_OUTPUT_SECTION ".irom.text" #define INCBIN_OUTPUT_SECTION ".irom.text"
#include <incbin.h> #include <incbin.h>
#include <resources.hpp> #include <resources.hpp>
INCBIN(IndexHtm, "data/index.min.htm"); INCBIN(IndexHtm, "data/index.min.htm");
INCBIN(ConfigHtm, "data/config.min.htm"); INCBIN(ConfigHtm, "data/config.min.htm");

View File

@ -91,7 +91,7 @@ void WebServerHandler::webHandleConfig() {
doc[PARAM_PLATFORM] = "esp32c3"; doc[PARAM_PLATFORM] = "esp32c3";
#elif defined(ESP32S2) #elif defined(ESP32S2)
doc[PARAM_PLATFORM] = "esp32s3"; doc[PARAM_PLATFORM] = "esp32s3";
#else // esp32 mini #else // esp32 mini
doc[PARAM_PLATFORM] = "esp32"; doc[PARAM_PLATFORM] = "esp32";
#endif #endif
@ -108,7 +108,6 @@ void WebServerHandler::webHandleConfig() {
LOG_PERF_STOP("webserver-api-config"); LOG_PERF_STOP("webserver-api-config");
} }
void WebServerHandler::webHandleUploadFile() { void WebServerHandler::webHandleUploadFile() {
LOG_PERF_START("webserver-api-upload-file"); LOG_PERF_START("webserver-api-upload-file");
Log.verbose(F("WEB : webServer callback for /api/upload(post)." CR)); Log.verbose(F("WEB : webServer callback for /api/upload(post)." CR));
@ -116,9 +115,9 @@ void WebServerHandler::webHandleUploadFile() {
String f = upload.filename; String f = upload.filename;
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose( Log.verbose(F("WEB : webServer callback for /api/upload, receiving file %s, "
F("WEB : webServer callback for /api/upload, receiving file %s, %d(%d)." CR), "%d(%d)." CR),
f.c_str(), upload.currentSize, upload.totalSize); f.c_str(), upload.currentSize, upload.totalSize);
#endif #endif
// Handle firmware update, hardcode since function return wrong value. // Handle firmware update, hardcode since function return wrong value.
@ -128,21 +127,21 @@ void WebServerHandler::webHandleUploadFile() {
if (upload.status == UPLOAD_FILE_START) { if (upload.status == UPLOAD_FILE_START) {
_uploadReturn = 200; _uploadReturn = 200;
Log.notice(F("WEB : Start firmware upload, max sketch size %d kb." CR), Log.notice(F("WEB : Start firmware upload, max sketch size %d kb." CR),
maxSketchSpace / 1024); maxSketchSpace / 1024);
if (!Update.begin(maxSketchSpace, U_FLASH, PIN_LED)) { if (!Update.begin(maxSketchSpace, U_FLASH, PIN_LED)) {
writeErrorLog("WEB : Not enough space to store for this firmware."); writeErrorLog("WEB : Not enough space to store for this firmware.");
_uploadReturn = 500; _uploadReturn = 500;
} }
} else if (upload.status == UPLOAD_FILE_WRITE) { } else if (upload.status == UPLOAD_FILE_WRITE) {
Log.notice(F("WEB : Writing firmware upload %d (%d)." CR), Log.notice(F("WEB : Writing firmware upload %d (%d)." CR), upload.totalSize,
upload.totalSize, maxSketchSpace); maxSketchSpace);
if (upload.totalSize > maxSketchSpace) { if (upload.totalSize > maxSketchSpace) {
Log.error(F("WEB : Firmware file is to large." CR)); Log.error(F("WEB : Firmware file is to large." CR));
_uploadReturn = 500; _uploadReturn = 500;
} else if (Update.write(upload.buf, upload.currentSize) != } else if (Update.write(upload.buf, upload.currentSize) !=
upload.currentSize) { upload.currentSize) {
Log.warning(F("WEB : Firmware write was unsuccessful." CR)); Log.warning(F("WEB : Firmware write was unsuccessful." CR));
_uploadReturn = 500; _uploadReturn = 500;
} }
@ -275,7 +274,7 @@ void WebServerHandler::webHandleStatus() {
doc[PARAM_PLATFORM] = "esp32c3"; doc[PARAM_PLATFORM] = "esp32c3";
#elif defined(ESP32S2) #elif defined(ESP32S2)
doc[PARAM_PLATFORM] = "esp32s3"; doc[PARAM_PLATFORM] = "esp32s3";
#else // esp32 mini #else // esp32 mini
doc[PARAM_PLATFORM] = "esp32"; doc[PARAM_PLATFORM] = "esp32";
#endif #endif
@ -1026,10 +1025,31 @@ void WebServerHandler::webHandlePageNotFound() {
_server->send(404, "text/plain", F("URL not found")); _server->send(404, "text/plain", F("URL not found"));
} }
int indexHtmLength = 0;
int configHtmLength = 0;
int calibrationHtmLength = 0;
int formatHtmLength = 0;
int testHtmLength = 0;
int aboutHtmLength = 0;
int firmwareHtmLength = 0;
bool WebServerHandler::setupWebServer() { bool WebServerHandler::setupWebServer() {
Log.notice(F("WEB : Configuring web server." CR)); Log.notice(F("WEB : Configuring web server." CR));
_server = new ESP8266WebServer(); _server = new ESP8266WebServer();
indexHtmLength = strlen(reinterpret_cast<const char*>(&indexHtmStart[0]));
configHtmLength = strlen(reinterpret_cast<const char*>(&configHtmStart[0]));
calibrationHtmLength =
strlen(reinterpret_cast<const char*>(&calibrationHtmStart[0]));
formatHtmLength = strlen(reinterpret_cast<const char*>(&formatHtmStart[0]));
testHtmLength = strlen(reinterpret_cast<const char*>(&testHtmStart[0]));
aboutHtmLength = strlen(reinterpret_cast<const char*>(&aboutHtmStart[0]));
firmwareHtmLength =
strlen(reinterpret_cast<const char*>(&firmwareHtmStart[0]));
Log.notice(F("WEB : Embedded HTML size; index=%d, config=%d, calibration=%d, "
"format=%d, test=%d, about=%d, firmware=%d." CR),
indexHtmLength, configHtmLength, calibrationHtmLength,
formatHtmLength, testHtmLength, aboutHtmLength, firmwareHtmLength);
MDNS.begin(myConfig.getMDNS()); MDNS.begin(myConfig.getMDNS());
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);
@ -1049,7 +1069,7 @@ bool WebServerHandler::setupWebServer() {
LittleFS.remove(dir.fileName().c_str()); LittleFS.remove(dir.fileName().c_str());
} }
} }
#else #else // ESP32
File root = LittleFS.open("/"); File root = LittleFS.open("/");
File f = root.openNextFile(); File f = root.openNextFile();
while (f) { while (f) {

View File

@ -51,16 +51,26 @@ extern const uint8_t indexHtmStart[] asm("_binary_html_index_min_htm_start");
extern const uint8_t indexHtmEnd[] asm("_binary_html_index_min_htm_end"); extern const uint8_t indexHtmEnd[] asm("_binary_html_index_min_htm_end");
extern const uint8_t configHtmStart[] asm("_binary_html_config_min_htm_start"); extern const uint8_t configHtmStart[] asm("_binary_html_config_min_htm_start");
extern const uint8_t configHtmEnd[] asm("_binary_html_config_min_htm_end"); extern const uint8_t configHtmEnd[] asm("_binary_html_config_min_htm_end");
extern const uint8_t calibrationHtmStart[] asm("_binary_html_calibration_min_htm_start"); extern const uint8_t calibrationHtmStart[] asm(
extern const uint8_t calibrationHtmEnd[] asm("_binary_html_calibration_min_htm_end"); "_binary_html_calibration_min_htm_start");
extern const uint8_t calibrationHtmEnd[] asm(
"_binary_html_calibration_min_htm_end");
extern const uint8_t formatHtmStart[] asm("_binary_html_format_min_htm_start"); extern const uint8_t formatHtmStart[] asm("_binary_html_format_min_htm_start");
extern const uint8_t formatHtmEnd[] asm("_binary_html_format_min_htm_end"); extern const uint8_t formatHtmEnd[] asm("_binary_html_format_min_htm_end");
extern const uint8_t testHtmStart[] asm("_binary_html_test_min_htm_start"); extern const uint8_t testHtmStart[] asm("_binary_html_test_min_htm_start");
extern const uint8_t testHtmEnd[] asm("_binary_html_test_min_htm_end"); extern const uint8_t testHtmEnd[] asm("_binary_html_test_min_htm_end");
extern const uint8_t aboutHtmStart[] asm("_binary_html_about_min_htm_start"); extern const uint8_t aboutHtmStart[] asm("_binary_html_about_min_htm_start");
extern const uint8_t aboutHtmEnd[] asm("_binary_html_about_min_htm_end"); extern const uint8_t aboutHtmEnd[] asm("_binary_html_about_min_htm_end");
extern const uint8_t firmwareHtmStart[] asm("_binary_html_firmware_min_htm_start"); extern const uint8_t firmwareHtmStart[] asm(
"_binary_html_firmware_min_htm_start");
extern const uint8_t firmwareHtmEnd[] asm("_binary_html_firmware_min_htm_end"); extern const uint8_t firmwareHtmEnd[] asm("_binary_html_firmware_min_htm_end");
extern int indexHtmLength;
extern int configHtmLength;
extern int calibrationHtmLength;
extern int formatHtmLength;
extern int testHtmLength;
extern int aboutHtmLength;
extern int firmwareHtmLength;
#endif #endif
class WebServerHandler { class WebServerHandler {
@ -128,25 +138,31 @@ class WebServerHandler {
} }
#else #else
void webReturnIndexHtm() { void webReturnIndexHtm() {
_server->send_P(200, "text/html", (const char*)indexHtmStart, indexHtmEnd-indexHtmStart); _server->send_P(200, "text/html", (const char*)indexHtmStart,
indexHtmLength);
} }
void webReturnConfigHtm() { void webReturnConfigHtm() {
_server->send_P(200, "text/html", (const char*)configHtmStart, configHtmEnd-configHtmStart); _server->send_P(200, "text/html", (const char*)configHtmStart,
configHtmLength);
} }
void webReturnCalibrationHtm() { void webReturnCalibrationHtm() {
_server->send_P(200, "text/html", (const char*)calibrationHtmStart, calibrationHtmEnd-calibrationHtmStart); _server->send_P(200, "text/html", (const char*)calibrationHtmStart,
calibrationHtmLength);
} }
void webReturnFormatHtm() { void webReturnFormatHtm() {
_server->send_P(200, "text/html", (const char*)formatHtmStart, formatHtmEnd-formatHtmStart); _server->send_P(200, "text/html", (const char*)formatHtmStart,
formatHtmLength);
} }
void webReturnAboutHtm() { void webReturnAboutHtm() {
_server->send_P(200, "text/html", (const char*)aboutHtmStart, aboutHtmEnd-aboutHtmStart); _server->send_P(200, "text/html", (const char*)aboutHtmStart,
aboutHtmLength);
} }
void webReturnTestHtm() { void webReturnTestHtm() {
_server->send_P(200, "text/html", (const char*)testHtmStart, testHtmEnd-testHtmStart); _server->send_P(200, "text/html", (const char*)testHtmStart, testHtmLength);
} }
void webReturnFirmwareHtm() { void webReturnFirmwareHtm() {
_server->send_P(200, "text/html", (const char*)firmwareHtmStart, firmwareHtmEnd-firmwareHtmStart); _server->send_P(200, "text/html", (const char*)firmwareHtmStart,
firmwareHtmLength);
} }
#endif #endif

View File

@ -120,7 +120,7 @@ void WifiConnection::startPortal() {
#if defined(ESP32C3) #if defined(ESP32C3)
Log.notice(F("WIFI: Reducing wifi power for c3 chip." CR)); Log.notice(F("WIFI: Reducing wifi power for c3 chip." CR));
WiFi.setTxPower(WIFI_POWER_8_5dBm); // Required for ESP32C3 Mini WiFi.setTxPower(WIFI_POWER_8_5dBm); // Required for ESP32C3 Mini
#endif #endif
myWifiManager->startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD); myWifiManager->startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD);
@ -159,7 +159,7 @@ void WifiConnection::connectAsync(int wifiIndex) {
#if defined(ESP32C3) #if defined(ESP32C3)
Log.notice(F("WIFI: Reducing wifi power for c3 chip." CR)); Log.notice(F("WIFI: Reducing wifi power for c3 chip." CR));
WiFi.setTxPower(WIFI_POWER_8_5dBm); // Required for ESP32C3 Mini WiFi.setTxPower(WIFI_POWER_8_5dBm); // Required for ESP32C3 Mini
#endif #endif
if (strlen(userSSID)) { if (strlen(userSSID)) {
@ -303,7 +303,7 @@ bool WifiConnection::updateFirmware() {
serverPath += "firmware32c3.bin"; serverPath += "firmware32c3.bin";
#elif defined(ESP32S2) #elif defined(ESP32S2)
serverPath += "firmware32s2.bin"; serverPath += "firmware32s2.bin";
#else // defined (ESP32) #else // defined (ESP32)
serverPath += "firmware32.bin"; serverPath += "firmware32.bin";
#endif #endif
@ -456,6 +456,6 @@ bool WifiConnection::parseFirmwareVersionString(int (&num)[3],
return true; return true;
} }
#endif // ACTIVATE_OTA #endif // ACTIVATE_OTA
// EOF // EOF