mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-01-12 00:02:53 +00:00
Initial implementation of custom upgrade code
This commit is contained in:
parent
9a4a8a10f2
commit
054fd43a0d
7
custom_partition.csv
Normal file
7
custom_partition.csv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
|
otadata, data, ota, 0xe000, 0x2000,
|
||||||
|
app0, app, ota_0, 0x10000, 0x1D0000,
|
||||||
|
app1, app, ota_1, 0x1E0000,0x1D0000,
|
||||||
|
spiffs, data, spiffs, 0x3B0000,0x40000,
|
||||||
|
coredump, data, coredump,0x3F0000,0x10000,
|
||||||
|
@ -238,7 +238,10 @@ struct UpgradeInformation {
|
|||||||
char toVersion[8];
|
char toVersion[8];
|
||||||
int16_t exitCode;
|
int16_t exitCode;
|
||||||
int16_t errorCode;
|
int16_t errorCode;
|
||||||
}; // 20
|
uint32_t size;
|
||||||
|
uint16_t block_position;
|
||||||
|
uint8_t retry_count;
|
||||||
|
}; // 27
|
||||||
|
|
||||||
struct CloudConfig {
|
struct CloudConfig {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
|||||||
91
lib/AmsFirmwareUpdater/include/AmsFirmwareUpdater.h
Normal file
91
lib/AmsFirmwareUpdater/include/AmsFirmwareUpdater.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <Print.h>
|
||||||
|
#include "HwTools.h"
|
||||||
|
#include "AmsData.h"
|
||||||
|
#include "AmsConfiguration.h"
|
||||||
|
|
||||||
|
#if defined(ESP32)
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
#include "LittleFS.h"
|
||||||
|
#include "WiFi.h"
|
||||||
|
#include "HTTPClient.h"
|
||||||
|
|
||||||
|
#define AMS_PARTITION_TABLE_OFFSET 0x8000
|
||||||
|
#define AMS_PARTITION_APP0_OFFSET 0x10000
|
||||||
|
#define AMS_PARTITION_APP_SIZE 0x1D0000
|
||||||
|
#define AMS_PARTITION_SPIFFS_SIZE 0x40000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define AMS_UPDATE_ERR_OK 0
|
||||||
|
#define AMS_UPDATE_ERR_DETAILS 1
|
||||||
|
#define AMS_UPDATE_ERR_FETCH 2
|
||||||
|
#define AMS_UPDATE_ERR_ERASE 3
|
||||||
|
#define AMS_UPDATE_ERR_WRITE 4
|
||||||
|
#define AMS_UPDATE_ERR_READ 5
|
||||||
|
#define AMS_UPDATE_ERR_MD5 6
|
||||||
|
#define AMS_UPDATE_ERR_ACTIVATE 7
|
||||||
|
|
||||||
|
#define UPDATE_BUF_SIZE 4096
|
||||||
|
|
||||||
|
class AmsFirmwareUpdater {
|
||||||
|
public:
|
||||||
|
AmsFirmwareUpdater(Print* debugger, HwTools* hw, AmsData* meterState);
|
||||||
|
bool relocateOrRepartitionIfNecessary();
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
char* getNextVersion();
|
||||||
|
bool setTargetVersion(const char* version);
|
||||||
|
void getUpgradeInformation(UpgradeInformation&);
|
||||||
|
float getProgress();
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if defined(ESP8266)
|
||||||
|
char chipType[10] = "esp8266";
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
char chipType[10] = "esp32s2";
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
char chipType[10] = "esp32s3";
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
char chipType[10] = "esp32c3";
|
||||||
|
#elif defined(ESP32)
|
||||||
|
#if defined(CONFIG_FREERTOS_UNICORE)
|
||||||
|
char chipType[10] = "esp32solo";
|
||||||
|
#else
|
||||||
|
char chipType[10] = "esp32";
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Print* debugger;
|
||||||
|
HwTools* hw;
|
||||||
|
AmsData* meterState;
|
||||||
|
|
||||||
|
UpgradeInformation updateStatus = {"","",0,0,0,0,0};
|
||||||
|
uint8_t blocksWritten = 0;
|
||||||
|
String md5;
|
||||||
|
|
||||||
|
uint32_t lastVersionCheck = 0;
|
||||||
|
uint8_t firmwareVariant;
|
||||||
|
bool autoUpgrade;
|
||||||
|
char nextVersion[10];
|
||||||
|
|
||||||
|
|
||||||
|
bool fetchNextVersion();
|
||||||
|
bool fetchVersionDetails();
|
||||||
|
bool fetchFirmwareChunk(HTTPClient& http);
|
||||||
|
bool writeBufferToFlash(size_t bytes);
|
||||||
|
bool verifyChecksum();
|
||||||
|
bool activateNewFirmware();
|
||||||
|
bool writeUpdateStatus();
|
||||||
|
uint32_t sketchSize(sketchSize_t response);
|
||||||
|
|
||||||
|
#if defined(ESP32)
|
||||||
|
uint32_t updateHandle = 0;
|
||||||
|
char* buf = NULL;
|
||||||
|
|
||||||
|
bool readPartition(uint8_t num, const esp_partition_info_t* partition);
|
||||||
|
bool writePartition(uint8_t num, const esp_partition_info_t* partition);
|
||||||
|
bool copyData(const esp_partition_info_t* src, esp_partition_info_t* dst);
|
||||||
|
bool copyFile(fs::LittleFSFS* src, fs::LittleFSFS* dst, const char* filename);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
490
lib/AmsFirmwareUpdater/src/AmsFirmwareUpdater.cpp
Normal file
490
lib/AmsFirmwareUpdater/src/AmsFirmwareUpdater.cpp
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
#include "AmsFirmwareUpdater.h"
|
||||||
|
#include "AmsStorage.h"
|
||||||
|
#include "FirmwareVersion.h"
|
||||||
|
|
||||||
|
#if defined(ESP32)
|
||||||
|
#include "esp_ota_ops.h"
|
||||||
|
#include "driver/spi_common.h"
|
||||||
|
#include "esp_flash_spi_init.h"
|
||||||
|
#include "MD5Builder.h"
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#include ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AmsFirmwareUpdater::AmsFirmwareUpdater(Print* debugger, HwTools* hw, AmsData* meterState) {
|
||||||
|
this->debugger = debugger;
|
||||||
|
this->hw = hw;
|
||||||
|
this->meterState = meterState;
|
||||||
|
memset(nextVersion, 0, sizeof(nextVersion));
|
||||||
|
firmwareVariant = 0;
|
||||||
|
autoUpgrade = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* AmsFirmwareUpdater::getNextVersion() {
|
||||||
|
return nextVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::setTargetVersion(const char* version) {
|
||||||
|
if(strcmp(version, FirmwareVersion::VersionString) == 0) {
|
||||||
|
memset(updateStatus.toVersion, 0, sizeof(updateStatus.toVersion));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(strcmp(version, updateStatus.toVersion) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
strcpy(updateStatus.fromVersion, FirmwareVersion::VersionString);
|
||||||
|
strcpy(updateStatus.toVersion, version);
|
||||||
|
updateStatus.size = 0;
|
||||||
|
updateStatus.retry_count = 0;
|
||||||
|
updateStatus.block_position = 0;
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_OK;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmsFirmwareUpdater::getUpgradeInformation(UpgradeInformation& upinfo) {
|
||||||
|
memcpy(&upinfo, &updateStatus, sizeof(upinfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
float AmsFirmwareUpdater::getProgress() {
|
||||||
|
if(strlen(updateStatus.toVersion) == 0 || updateStatus.size == 0) return -1.0;
|
||||||
|
return min((float) 100.0, ((((float) updateStatus.block_position) * UPDATE_BUF_SIZE) / updateStatus.size) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmsFirmwareUpdater::loop() {
|
||||||
|
if(strlen(updateStatus.toVersion) > 0) {
|
||||||
|
if(updateStatus.errorCode > 0) return;
|
||||||
|
|
||||||
|
if(!hw->isVoltageOptimal(0.1)) {
|
||||||
|
writeUpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long start = 0, end = 0;
|
||||||
|
|
||||||
|
if(buf == NULL) buf = (char*) malloc(UPDATE_BUF_SIZE);
|
||||||
|
if(updateStatus.size == 0) {
|
||||||
|
start = millis();
|
||||||
|
if(!fetchVersionDetails()) {
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_DETAILS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
end = millis();
|
||||||
|
debugger->printf_P(PSTR("fetch details took %lums\n"), end-start);
|
||||||
|
updateStatus.retry_count = 0;
|
||||||
|
updateStatus.block_position = 0;
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_OK;
|
||||||
|
} else if(updateStatus.block_position * UPDATE_BUF_SIZE < updateStatus.size) {
|
||||||
|
HTTPClient http;
|
||||||
|
start = millis();
|
||||||
|
if(!fetchFirmwareChunk(http)) {
|
||||||
|
if(updateStatus.retry_count++ == 3) {
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_FETCH;
|
||||||
|
}
|
||||||
|
writeUpdateStatus();
|
||||||
|
http.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
end = millis();
|
||||||
|
debugger->printf_P(PSTR("fetch chunk took %lums\n"), end-start);
|
||||||
|
|
||||||
|
start = millis();
|
||||||
|
WiFiClient* client = http.getStreamPtr();
|
||||||
|
updateStatus.retry_count = 0;
|
||||||
|
if(!client->available()) {
|
||||||
|
http.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
end = millis();
|
||||||
|
debugger->printf_P(PSTR("get ptr took %lums (%d) \n"), end-start, client->available());
|
||||||
|
size_t bytes = UPDATE_BUF_SIZE; // To start first loop
|
||||||
|
while(bytes > 0 && client->available() > 0) {
|
||||||
|
start = millis();
|
||||||
|
bytes = client->readBytes(buf, UPDATE_BUF_SIZE);
|
||||||
|
end = millis();
|
||||||
|
debugger->printf_P(PSTR("read buffer took %lums (%lu bytes, %d left)\n"), end-start, bytes, client->available());
|
||||||
|
if(bytes > 0) {
|
||||||
|
start = millis();
|
||||||
|
if(!writeBufferToFlash(bytes)) {
|
||||||
|
http.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
end = millis();
|
||||||
|
debugger->printf_P(PSTR("write buffer took %lums\n"), end-start);
|
||||||
|
}
|
||||||
|
start = millis();
|
||||||
|
if(!hw->isVoltageOptimal(0.2)) {
|
||||||
|
writeUpdateStatus();
|
||||||
|
}
|
||||||
|
end = millis();
|
||||||
|
debugger->printf_P(PSTR("check voltage took %lums\n"), end-start);
|
||||||
|
}
|
||||||
|
start = millis();
|
||||||
|
http.end();
|
||||||
|
end = millis();
|
||||||
|
debugger->printf_P(PSTR("http end took %lums\n"), end-start);
|
||||||
|
} else if(updateStatus.block_position * UPDATE_BUF_SIZE >= updateStatus.size) {
|
||||||
|
if(!verifyChecksum()) {
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_MD5;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!activateNewFirmware()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t seconds = millis() / 1000.0;
|
||||||
|
if((lastVersionCheck == 0 && seconds > 20) || seconds - lastVersionCheck > 86400) {
|
||||||
|
fetchNextVersion();
|
||||||
|
lastVersionCheck = seconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::fetchNextVersion() {
|
||||||
|
HTTPClient http;
|
||||||
|
const char * headerkeys[] = { "x-version" };
|
||||||
|
http.collectHeaders(headerkeys, 1);
|
||||||
|
|
||||||
|
char firmwareVariant[10] = "stable";
|
||||||
|
|
||||||
|
char url[128];
|
||||||
|
snprintf_P(url, 128, PSTR("http://hub.amsleser.no/hub/firmware/%s/%s/next"), chipType, firmwareVariant);
|
||||||
|
if(http.begin(url)) {
|
||||||
|
http.useHTTP10(true);
|
||||||
|
http.setTimeout(30000);
|
||||||
|
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
||||||
|
http.setUserAgent("AMS-Firmware-Updater");
|
||||||
|
http.addHeader(F("Cache-Control"), "no-cache");
|
||||||
|
http.addHeader(F("x-AMS-version"), FirmwareVersion::VersionString);
|
||||||
|
int status = http.GET();
|
||||||
|
if(status == 204) {
|
||||||
|
String nextVersion = http.header("x-version");
|
||||||
|
strcpy(this->nextVersion, nextVersion.c_str());
|
||||||
|
if(autoUpgrade && strcmp(updateStatus.toVersion, this->nextVersion) != 0) {
|
||||||
|
strcpy(updateStatus.toVersion, this->nextVersion);
|
||||||
|
updateStatus.size = 0;
|
||||||
|
}
|
||||||
|
http.end();
|
||||||
|
return strlen(this->nextVersion) > 0;
|
||||||
|
} else if(status == 200) {
|
||||||
|
memset(this->nextVersion, 0, sizeof(this->nextVersion));
|
||||||
|
}
|
||||||
|
http.end();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::fetchVersionDetails() {
|
||||||
|
HTTPClient http;
|
||||||
|
const char * headerkeys[] = { "x-size" };
|
||||||
|
http.collectHeaders(headerkeys, 1);
|
||||||
|
|
||||||
|
char firmwareVariant[10] = "stable";
|
||||||
|
|
||||||
|
char url[128];
|
||||||
|
snprintf_P(url, 128, PSTR("http://hub.amsleser.no/hub/firmware/%s/%s/%s/details"), chipType, firmwareVariant, updateStatus.toVersion);
|
||||||
|
if(http.begin(url)) {
|
||||||
|
http.useHTTP10(true);
|
||||||
|
http.setTimeout(30000);
|
||||||
|
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
||||||
|
http.setUserAgent("AMS-Firmware-Updater");
|
||||||
|
http.addHeader(F("Cache-Control"), "no-cache");
|
||||||
|
http.addHeader(F("x-AMS-STA-MAC"), WiFi.macAddress());
|
||||||
|
http.addHeader(F("x-AMS-AP-MAC"), WiFi.softAPmacAddress());
|
||||||
|
http.addHeader(F("x-AMS-free-space"), String(ESP.getFreeSketchSpace()));
|
||||||
|
http.addHeader(F("x-AMS-sketch-size"), String(ESP.getSketchSize()));
|
||||||
|
String sketchMD5 = ESP.getSketchMD5();
|
||||||
|
if(!sketchMD5.isEmpty()) {
|
||||||
|
http.addHeader(F("x-AMS-sketch-md5"), sketchMD5);
|
||||||
|
}
|
||||||
|
http.addHeader(F("x-AMS-chip-size"), String(ESP.getFlashChipSize()));
|
||||||
|
http.addHeader(F("x-AMS-sdk-version"), ESP.getSdkVersion());
|
||||||
|
http.addHeader(F("x-AMS-mode"), "sketch");
|
||||||
|
http.addHeader(F("x-AMS-version"), FirmwareVersion::VersionString);
|
||||||
|
http.addHeader(F("x-AMS-board-type"), String(hw->getBoardType(), 10));
|
||||||
|
if(meterState->getMeterType() != AmsTypeAutodetect) {
|
||||||
|
http.addHeader(F("x-AMS-meter-mfg"), String(meterState->getMeterType(), 10));
|
||||||
|
}
|
||||||
|
if(!meterState->getMeterModel().isEmpty()) {
|
||||||
|
http.addHeader(F("x-AMS-meter-model"), meterState->getMeterModel());
|
||||||
|
}
|
||||||
|
int status = http.GET();
|
||||||
|
if(status == 204) {
|
||||||
|
String size = http.header("x-size");
|
||||||
|
updateStatus.size = size.toInt();
|
||||||
|
http.end();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
http.end();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::fetchFirmwareChunk(HTTPClient& http) {
|
||||||
|
const char * headerkeys[] = { "x-MD5" };
|
||||||
|
http.collectHeaders(headerkeys, 1);
|
||||||
|
|
||||||
|
uint32_t start = updateStatus.block_position * UPDATE_BUF_SIZE;
|
||||||
|
uint32_t end = start + (UPDATE_BUF_SIZE * 1);
|
||||||
|
char range[24];
|
||||||
|
snprintf_P(range, 24, PSTR("bytes=%lu-%lu"), start, end);
|
||||||
|
|
||||||
|
char firmwareVariant[10] = "stable";
|
||||||
|
|
||||||
|
char url[128];
|
||||||
|
snprintf_P(url, 128, PSTR("http://hub.amsleser.no/hub/firmware/%s/%s/%s/chunk"), chipType, firmwareVariant, updateStatus.toVersion);
|
||||||
|
if(http.begin(url)) {
|
||||||
|
http.useHTTP10(true);
|
||||||
|
http.setTimeout(30000);
|
||||||
|
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
||||||
|
http.setUserAgent("AMS-Firmware-Updater");
|
||||||
|
http.addHeader(F("Cache-Control"), "no-cache");
|
||||||
|
http.addHeader(F("x-AMS-version"), FirmwareVersion::VersionString);
|
||||||
|
http.addHeader(F("Range"), range);
|
||||||
|
if(http.GET() == 206) {
|
||||||
|
this->md5 = http.header("x-MD5");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::writeUpdateStatus() {
|
||||||
|
return false; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ESP32)
|
||||||
|
bool AmsFirmwareUpdater::writeBufferToFlash(size_t bytes) {
|
||||||
|
uint32_t offset = updateStatus.block_position * UPDATE_BUF_SIZE;
|
||||||
|
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
|
||||||
|
esp_err_t eraseErr = esp_partition_erase_range(partition, offset, UPDATE_BUF_SIZE);
|
||||||
|
if(eraseErr != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("esp_partition_erase_range(%s, %lu, %lu) failed with %d\n"), partition->label, offset, UPDATE_BUF_SIZE, eraseErr);
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_ERASE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
esp_err_t writeErr = esp_partition_write(partition, offset, buf, bytes);
|
||||||
|
if(writeErr != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("esp_partition_write(%s, %lu, buf, %lu) failed with %d\n"), partition->label, offset, bytes, writeErr);
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_WRITE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
updateStatus.block_position++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::activateNewFirmware() {
|
||||||
|
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
|
||||||
|
if(esp_ota_set_boot_partition(partition) != ESP_OK) {
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_ACTIVATE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP.restart();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::relocateOrRepartitionIfNecessary() {
|
||||||
|
const esp_partition_t* active = esp_ota_get_running_partition();
|
||||||
|
debugger->printf_P(PSTR("Firmware currently running from %s\n"), active->label);
|
||||||
|
if(active->type != ESP_PARTITION_TYPE_APP) {
|
||||||
|
debugger->printf_P(PSTR("Not running on APP partition?\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(active->size >= AMS_PARTITION_APP_SIZE) {
|
||||||
|
debugger->printf_P(PSTR("Partition is large enough, no change\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buf == NULL) buf = (char*) malloc(UPDATE_BUF_SIZE);
|
||||||
|
esp_partition_info_t p_nvs, p_ota, p_app0, p_app1, p_spiffs, p_coredump;
|
||||||
|
readPartition(0, &p_nvs);
|
||||||
|
readPartition(1, &p_ota);
|
||||||
|
readPartition(2, &p_app0);
|
||||||
|
readPartition(3, &p_app1);
|
||||||
|
readPartition(4, &p_spiffs);
|
||||||
|
readPartition(5, &p_coredump);
|
||||||
|
|
||||||
|
const esp_partition_t* app0 = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_MIN, NULL);
|
||||||
|
if(active->subtype != ESP_PARTITION_SUBTYPE_APP_OTA_MIN) {
|
||||||
|
debugger->printf_P(PSTR("Relocating %s to %s\n"), active->label, p_app0.label);
|
||||||
|
if(!copyData(&p_app1, &p_app0)) {
|
||||||
|
debugger->printf_P(PSTR("Unable to copy app0 to app1\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(esp_ota_set_boot_partition(app0) != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("Unable to set app0 active\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugger->printf_P(PSTR("Small partition, repartitioning\n"));
|
||||||
|
|
||||||
|
p_app0.pos.offset = AMS_PARTITION_APP0_OFFSET;
|
||||||
|
p_app0.pos.size = AMS_PARTITION_APP_SIZE;
|
||||||
|
p_app1.pos.offset = p_app0.pos.offset + p_app0.pos.size;
|
||||||
|
p_app1.pos.size = AMS_PARTITION_APP_SIZE;
|
||||||
|
p_spiffs.pos.offset = p_app1.pos.offset + p_app1.pos.size;
|
||||||
|
p_spiffs.pos.size = AMS_PARTITION_SPIFFS_SIZE;
|
||||||
|
|
||||||
|
esp_err_t p_erase_err = esp_flash_erase_region(NULL, AMS_PARTITION_TABLE_OFFSET, 4096);
|
||||||
|
if(p_erase_err != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("Unable to erase partition table (%d)\n"), p_erase_err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
writePartition(0, &p_nvs);
|
||||||
|
writePartition(1, &p_ota);
|
||||||
|
writePartition(2, &p_app0);
|
||||||
|
writePartition(3, &p_app1);
|
||||||
|
writePartition(4, &p_spiffs);
|
||||||
|
writePartition(5, &p_coredump);
|
||||||
|
|
||||||
|
uint32_t md5pos = 0;
|
||||||
|
esp_partition_info_t part;
|
||||||
|
for(uint8_t i = 0; i < 10; i++) {
|
||||||
|
uint16_t size = sizeof(part);
|
||||||
|
uint32_t pos = i * size;
|
||||||
|
readPartition(i, &part);
|
||||||
|
if(part.magic == ESP_PARTITION_MAGIC) {
|
||||||
|
debugger->printf_P(PSTR("Partition %d, magic: %04X, offset: %X, size: %d, type: %d:%d, label: %s, flags: %04X\n"), i, part.magic, part.pos.offset, part.pos.size, part.type, part.subtype, part.label, part.flags);
|
||||||
|
} else {
|
||||||
|
md5pos = pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(buf, 0, UPDATE_BUF_SIZE);
|
||||||
|
MD5Builder md5;
|
||||||
|
md5.begin();
|
||||||
|
md5.add((uint8_t*) &p_nvs, sizeof(p_nvs));
|
||||||
|
md5.add((uint8_t*) &p_ota, sizeof(p_ota));
|
||||||
|
md5.add((uint8_t*) &p_app0, sizeof(p_app0));
|
||||||
|
md5.add((uint8_t*) &p_app1, sizeof(p_app1));
|
||||||
|
md5.add((uint8_t*) &p_spiffs, sizeof(p_spiffs));
|
||||||
|
md5.add((uint8_t*) &p_coredump, sizeof(p_coredump));
|
||||||
|
md5.calculate();
|
||||||
|
md5.getChars(buf);
|
||||||
|
debugger->printf_P(PSTR("Writing MD5 %s to position %d\n"), buf, md5pos);
|
||||||
|
|
||||||
|
part.magic = ESP_PARTITION_MAGIC_MD5;
|
||||||
|
if(esp_flash_write(NULL, (uint8_t*) &part, AMS_PARTITION_TABLE_OFFSET + md5pos, 2) != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("Unable to write md5 header\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
md5.getBytes((uint8_t*) buf);
|
||||||
|
if(esp_flash_write(NULL, buf, AMS_PARTITION_TABLE_OFFSET + md5pos + ESP_PARTITION_MD5_OFFSET, ESP_ROM_MD5_DIGEST_LEN) != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("Unable to write md5\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(esp_flash_erase_region(NULL, p_app1.pos.offset, p_app1.pos.size) != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("Unable to erase app1\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t eraseForFsStart = p_spiffs.pos.offset + p_spiffs.pos.size - UPDATE_BUF_SIZE;
|
||||||
|
if(esp_flash_erase_region(NULL, eraseForFsStart, UPDATE_BUF_SIZE) == ESP_OK) {
|
||||||
|
fs::LittleFSFS newFs;
|
||||||
|
if(newFs.begin(true, "/newfs", 10, (char*) p_spiffs.label)) {
|
||||||
|
copyFile(&LittleFS, &newFs, FILE_MQTT_CA);
|
||||||
|
copyFile(&LittleFS, &newFs, FILE_MQTT_CERT);
|
||||||
|
copyFile(&LittleFS, &newFs, FILE_MQTT_KEY);
|
||||||
|
copyFile(&LittleFS, &newFs, FILE_DAYPLOT);
|
||||||
|
copyFile(&LittleFS, &newFs, FILE_MONTHPLOT);
|
||||||
|
copyFile(&LittleFS, &newFs, FILE_ENERGYACCOUNTING);
|
||||||
|
copyFile(&LittleFS, &newFs, FILE_PRICE_CONF);
|
||||||
|
} else {
|
||||||
|
debugger->printf_P(PSTR("Unable to start spiffs on new location\n"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debugger->printf_P(PSTR("Unable to erase fs\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_image_header_t h_app0;
|
||||||
|
if(esp_flash_read(NULL, buf, p_app0.pos.offset, sizeof(&h_app0)) == ESP_OK) {
|
||||||
|
if(esp_flash_write(NULL, buf, p_app1.pos.offset, sizeof(&h_app0)) != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("Unable to write header to app1\n"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debugger->printf_P(PSTR("Unable to read header from app0\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::readPartition(uint8_t num, const esp_partition_info_t* partition) {
|
||||||
|
uint32_t pos = num * sizeof(*partition);
|
||||||
|
if(esp_flash_read(NULL, (uint8_t*) partition, AMS_PARTITION_TABLE_OFFSET + pos, sizeof(*partition)) != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("Unable to read partition %d\n"), num);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::writePartition(uint8_t num, const esp_partition_info_t* partition) {
|
||||||
|
uint32_t pos = num * sizeof(*partition);
|
||||||
|
if(esp_flash_write(NULL, (uint8_t*) partition, AMS_PARTITION_TABLE_OFFSET + pos, sizeof(*partition)) != ESP_OK) {
|
||||||
|
debugger->printf_P(PSTR("Unable to write partition %d\n"), num);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::copyData(const esp_partition_info_t* src, esp_partition_info_t* dst) {
|
||||||
|
if(esp_flash_erase_region(NULL, dst->pos.offset, dst->pos.size) != ESP_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t pos = 0;
|
||||||
|
while(pos < dst->pos.size) {
|
||||||
|
if(esp_flash_read(NULL, buf, src->pos.offset + pos, UPDATE_BUF_SIZE) != ESP_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(esp_flash_write(NULL, buf, dst->pos.offset + pos, UPDATE_BUF_SIZE) != ESP_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pos += UPDATE_BUF_SIZE;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::copyFile(fs::LittleFSFS* srcFs, fs::LittleFSFS* dstFs, const char* filename) {
|
||||||
|
if(srcFs->exists(filename)) {
|
||||||
|
File src = srcFs->open(filename, "r");
|
||||||
|
File dst = dstFs->open(filename, "w");
|
||||||
|
|
||||||
|
size_t size;
|
||||||
|
while((size = src.readBytes(buf, UPDATE_BUF_SIZE)) > 0) {
|
||||||
|
dst.write((uint8_t*) buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.close();
|
||||||
|
src.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmsFirmwareUpdater::verifyChecksum() {
|
||||||
|
const esp_partition_t *partition = esp_ota_get_next_update_partition(NULL);
|
||||||
|
if (!partition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MD5Builder md5;
|
||||||
|
md5.begin();
|
||||||
|
uint32_t offset = 0;
|
||||||
|
uint32_t lengthLeft = updateStatus.size;
|
||||||
|
while( lengthLeft > 0) {
|
||||||
|
size_t bytes = (lengthLeft < UPDATE_BUF_SIZE) ? lengthLeft : UPDATE_BUF_SIZE;
|
||||||
|
if(esp_partition_read(partition, offset, buf, bytes) != ESP_OK) {
|
||||||
|
updateStatus.errorCode = AMS_UPDATE_ERR_READ;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
md5.add((uint8_t*) buf, bytes);
|
||||||
|
lengthLeft -= bytes;
|
||||||
|
offset += bytes;
|
||||||
|
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
md5.calculate();
|
||||||
|
return !this->md5.isEmpty() && this->md5.equals(md5.toString());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -43,7 +43,7 @@ struct AdcConfig {
|
|||||||
class HwTools {
|
class HwTools {
|
||||||
public:
|
public:
|
||||||
bool applyBoardConfig(uint8_t boardType, GpioConfig& gpioConfig, MeterConfig& meterConfig, uint8_t hanPin);
|
bool applyBoardConfig(uint8_t boardType, GpioConfig& gpioConfig, MeterConfig& meterConfig, uint8_t hanPin);
|
||||||
void setup(GpioConfig*);
|
void setup(SystemConfig* sys, GpioConfig* gpio);
|
||||||
float getVcc();
|
float getVcc();
|
||||||
uint8_t getTempSensorCount();
|
uint8_t getTempSensorCount();
|
||||||
TempSensorData* getTempSensorData(uint8_t);
|
TempSensorData* getTempSensorData(uint8_t);
|
||||||
@ -56,15 +56,24 @@ public:
|
|||||||
bool ledOff(uint8_t color);
|
bool ledOff(uint8_t color);
|
||||||
bool ledBlink(uint8_t color, uint8_t blink);
|
bool ledBlink(uint8_t color, uint8_t blink);
|
||||||
void setBootSuccessful(bool value);
|
void setBootSuccessful(bool value);
|
||||||
|
bool isVoltageOptimal(float range = 0.4);
|
||||||
|
uint8_t getBoardType();
|
||||||
|
|
||||||
HwTools() {};
|
HwTools() {};
|
||||||
private:
|
private:
|
||||||
|
uint8_t boardType;
|
||||||
|
uint8_t ledPin, redPin, greenPin, bluePin, tempPin, atempPin;
|
||||||
|
uint8_t ledDisablePin, ledBehaviour;
|
||||||
|
bool ledInvert, rgbInvert;
|
||||||
|
uint8_t vccPin, vccGnd_r, vccVcc_r;
|
||||||
|
float vccOffset, vccMultiplier;
|
||||||
|
float maxVcc = 2.9;
|
||||||
|
|
||||||
uint16_t analogRange = 1024;
|
uint16_t analogRange = 1024;
|
||||||
AdcConfig voltAdc, tempAdc;
|
AdcConfig voltAdc, tempAdc;
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
esp_adc_cal_characteristics_t* voltAdcChar, tempAdcChar;
|
esp_adc_cal_characteristics_t* voltAdcChar, tempAdcChar;
|
||||||
#endif
|
#endif
|
||||||
GpioConfig* config;
|
|
||||||
bool tempSensorInit;
|
bool tempSensorInit;
|
||||||
OneWire *oneWire = NULL;
|
OneWire *oneWire = NULL;
|
||||||
DallasTemperature *sensorApi = NULL;
|
DallasTemperature *sensorApi = NULL;
|
||||||
|
|||||||
@ -144,8 +144,8 @@ bool HwTools::applyBoardConfig(uint8_t boardType, GpioConfig& gpioConfig, MeterC
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HwTools::setup(GpioConfig* config) {
|
void HwTools::setup(SystemConfig* sys, GpioConfig* config) {
|
||||||
this->config = config;
|
this->boardType = sys->boardType;
|
||||||
this->tempSensorInit = false;
|
this->tempSensorInit = false;
|
||||||
if(sensorApi != NULL)
|
if(sensorApi != NULL)
|
||||||
delete sensorApi;
|
delete sensorApi;
|
||||||
@ -153,8 +153,9 @@ void HwTools::setup(GpioConfig* config) {
|
|||||||
delete oneWire;
|
delete oneWire;
|
||||||
if(config->tempSensorPin > 0 && config->tempSensorPin < 40) {
|
if(config->tempSensorPin > 0 && config->tempSensorPin < 40) {
|
||||||
pinMode(config->tempSensorPin, INPUT);
|
pinMode(config->tempSensorPin, INPUT);
|
||||||
|
tempPin = config->tempSensorPin;
|
||||||
} else {
|
} else {
|
||||||
config->tempSensorPin = 0xFF;
|
tempPin = config->tempSensorPin = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
@ -196,47 +197,63 @@ void HwTools::setup(GpioConfig* config) {
|
|||||||
#else
|
#else
|
||||||
pinMode(config->vccPin, INPUT);
|
pinMode(config->vccPin, INPUT);
|
||||||
#endif
|
#endif
|
||||||
|
vccPin = config->vccPin;
|
||||||
|
vccOffset = config->vccOffset / 100.0;
|
||||||
|
vccMultiplier = config->vccMultiplier / 1000.0;
|
||||||
|
vccGnd_r = config->vccResistorGnd;
|
||||||
|
vccVcc_r = config->vccResistorVcc;
|
||||||
} else {
|
} else {
|
||||||
voltAdc.unit = 0xFF;
|
voltAdc.unit = 0xFF;
|
||||||
voltAdc.channel = 0xFF;
|
voltAdc.channel = 0xFF;
|
||||||
config->vccPin = 0xFF;
|
vccPin = config->vccPin = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config->tempAnalogSensorPin > 0 && config->tempAnalogSensorPin < 40) {
|
if(config->tempAnalogSensorPin > 0 && config->tempAnalogSensorPin < 40) {
|
||||||
pinMode(config->tempAnalogSensorPin, INPUT);
|
pinMode(config->tempAnalogSensorPin, INPUT);
|
||||||
|
atempPin = config->tempAnalogSensorPin;
|
||||||
} else {
|
} else {
|
||||||
config->tempAnalogSensorPin = 0xFF;
|
atempPin = config->tempAnalogSensorPin = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config->ledPin > 0 && config->ledPin < 40) {
|
if(config->ledPin > 0 && config->ledPin < 40) {
|
||||||
pinMode(config->ledPin, OUTPUT);
|
pinMode(config->ledPin, OUTPUT);
|
||||||
|
ledPin = config->ledPin;
|
||||||
|
ledInvert = config->ledInverted;
|
||||||
ledOff(LED_INTERNAL);
|
ledOff(LED_INTERNAL);
|
||||||
} else {
|
} else {
|
||||||
config->ledPin = 0xFF;
|
ledPin = config->ledPin = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config->ledPinRed > 0 && config->ledPinRed < 40) {
|
if(config->ledPinRed > 0 && config->ledPinRed < 40) {
|
||||||
pinMode(config->ledPinRed, OUTPUT);
|
pinMode(config->ledPinRed, OUTPUT);
|
||||||
|
redPin = config->ledPinRed;
|
||||||
ledOff(LED_RED);
|
ledOff(LED_RED);
|
||||||
} else {
|
} else {
|
||||||
config->ledPinRed = 0xFF;
|
redPin = config->ledPinRed = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config->ledPinGreen > 0 && config->ledPinGreen < 40) {
|
if(config->ledPinGreen > 0 && config->ledPinGreen < 40) {
|
||||||
pinMode(config->ledPinGreen, OUTPUT);
|
pinMode(config->ledPinGreen, OUTPUT);
|
||||||
|
greenPin = config->ledPinGreen;
|
||||||
ledOff(LED_GREEN);
|
ledOff(LED_GREEN);
|
||||||
} else {
|
} else {
|
||||||
config->ledPinGreen = 0xFF;
|
greenPin = config->ledPinGreen = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config->ledPinBlue > 0 && config->ledPinBlue < 40) {
|
if(config->ledPinBlue > 0 && config->ledPinBlue < 40) {
|
||||||
pinMode(config->ledPinBlue, OUTPUT);
|
pinMode(config->ledPinBlue, OUTPUT);
|
||||||
|
bluePin = config->ledPinBlue;
|
||||||
ledOff(LED_BLUE);
|
ledOff(LED_BLUE);
|
||||||
} else {
|
} else {
|
||||||
config->ledPinBlue = 0xFF;
|
bluePin = config->ledPinBlue = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rgbInvert = config->ledRgbInverted;
|
||||||
|
|
||||||
if(config->ledDisablePin > 0 && config->ledDisablePin < 40) {
|
if(config->ledDisablePin > 0 && config->ledDisablePin < 40) {
|
||||||
pinMode(config->ledDisablePin, OUTPUT_OPEN_DRAIN);
|
pinMode(config->ledDisablePin, OUTPUT_OPEN_DRAIN);
|
||||||
|
ledDisablePin = config->ledDisablePin;
|
||||||
|
ledBehaviour = config->ledBehaviour;
|
||||||
setBootSuccessful(false);
|
setBootSuccessful(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -362,7 +379,7 @@ void HwTools::getAdcChannel(uint8_t pin, AdcConfig& config) {
|
|||||||
|
|
||||||
float HwTools::getVcc() {
|
float HwTools::getVcc() {
|
||||||
float volts = 0.0;
|
float volts = 0.0;
|
||||||
if(config->vccPin != 0xFF) {
|
if(vccPin != 0xFF) {
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
if(voltAdc.unit != 0xFF) {
|
if(voltAdc.unit != 0xFF) {
|
||||||
uint32_t x = 0;
|
uint32_t x = 0;
|
||||||
@ -385,7 +402,7 @@ float HwTools::getVcc() {
|
|||||||
} else {
|
} else {
|
||||||
uint32_t x = 0;
|
uint32_t x = 0;
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
x += analogRead(config->vccPin);
|
x += analogRead(vccPin);
|
||||||
}
|
}
|
||||||
volts = (x * 3.3) / 10.0 / analogRange;
|
volts = (x * 3.3) / 10.0 / analogRange;
|
||||||
}
|
}
|
||||||
@ -403,13 +420,10 @@ float HwTools::getVcc() {
|
|||||||
}
|
}
|
||||||
if(volts == 0.0) return 0.0;
|
if(volts == 0.0) return 0.0;
|
||||||
|
|
||||||
if(config->vccResistorGnd > 0 && config->vccResistorVcc > 0) {
|
if(vccGnd_r > 0 && vccVcc_r > 0) {
|
||||||
volts *= ((float) (config->vccResistorGnd + config->vccResistorVcc) / config->vccResistorGnd);
|
volts *= ((float) (vccGnd_r + vccVcc_r) / vccGnd_r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float vccOffset = config->vccOffset / 100.0;
|
|
||||||
float vccMultiplier = config->vccMultiplier / 1000.0;
|
|
||||||
return vccOffset + (volts > 0.0 ? volts * vccMultiplier : 0.0);
|
return vccOffset + (volts > 0.0 ? volts * vccMultiplier : 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,9 +439,9 @@ TempSensorData* HwTools::getTempSensorData(uint8_t i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HwTools::updateTemperatures() {
|
bool HwTools::updateTemperatures() {
|
||||||
if(config->tempSensorPin != 0xFF) {
|
if(tempPin != 0xFF) {
|
||||||
if(!tempSensorInit) {
|
if(!tempSensorInit) {
|
||||||
oneWire = new OneWire(config->tempSensorPin);
|
oneWire = new OneWire(tempPin);
|
||||||
sensorApi = new DallasTemperature(this->oneWire);
|
sensorApi = new DallasTemperature(this->oneWire);
|
||||||
sensorApi->begin();
|
sensorApi->begin();
|
||||||
delay(100);
|
delay(100);
|
||||||
@ -513,9 +527,9 @@ float HwTools::getTemperature() {
|
|||||||
return c == 0 ? DEVICE_DISCONNECTED_C : ret/c;
|
return c == 0 ? DEVICE_DISCONNECTED_C : ret/c;
|
||||||
}
|
}
|
||||||
float HwTools::getTemperatureAnalog() {
|
float HwTools::getTemperatureAnalog() {
|
||||||
if(config->tempAnalogSensorPin != 0xFF) {
|
if(atempPin != 0xFF) {
|
||||||
float adcCalibrationFactor = 1.06587;
|
float adcCalibrationFactor = 1.06587;
|
||||||
int volts = ((float) analogRead(config->tempAnalogSensorPin) / analogRange) * 3.3;
|
int volts = ((float) analogRead(atempPin) / analogRange) * 3.3;
|
||||||
return ((volts * adcCalibrationFactor) - 0.4) / 0.0195;
|
return ((volts * adcCalibrationFactor) - 0.4) / 0.0195;
|
||||||
}
|
}
|
||||||
return DEVICE_DISCONNECTED_C;
|
return DEVICE_DISCONNECTED_C;
|
||||||
@ -529,42 +543,42 @@ int HwTools::getWifiRssi() {
|
|||||||
void HwTools::setBootSuccessful(bool value) {
|
void HwTools::setBootSuccessful(bool value) {
|
||||||
if(bootSuccessful && value) return;
|
if(bootSuccessful && value) return;
|
||||||
bootSuccessful = value;
|
bootSuccessful = value;
|
||||||
if(config->ledDisablePin > 0 && config->ledDisablePin < 40) {
|
if(ledDisablePin > 0 && ledDisablePin < 40) {
|
||||||
switch(config->ledBehaviour) {
|
switch(ledBehaviour) {
|
||||||
case LED_BEHAVIOUR_ERROR_ONLY:
|
case LED_BEHAVIOUR_ERROR_ONLY:
|
||||||
case LED_BEHAVIOUR_OFF:
|
case LED_BEHAVIOUR_OFF:
|
||||||
digitalWrite(config->ledDisablePin, LOW);
|
digitalWrite(ledDisablePin, LOW);
|
||||||
break;
|
break;
|
||||||
case LED_BEHAVIOUR_BOOT:
|
case LED_BEHAVIOUR_BOOT:
|
||||||
if(bootSuccessful) {
|
if(bootSuccessful) {
|
||||||
digitalWrite(config->ledDisablePin, LOW);
|
digitalWrite(ledDisablePin, LOW);
|
||||||
} else {
|
} else {
|
||||||
digitalWrite(config->ledDisablePin, HIGH);
|
digitalWrite(ledDisablePin, HIGH);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
digitalWrite(config->ledDisablePin, HIGH);
|
digitalWrite(ledDisablePin, HIGH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HwTools::ledOn(uint8_t color) {
|
bool HwTools::ledOn(uint8_t color) {
|
||||||
if(config->ledBehaviour == LED_BEHAVIOUR_OFF) return false;
|
if(ledBehaviour == LED_BEHAVIOUR_OFF) return false;
|
||||||
if(config->ledBehaviour == LED_BEHAVIOUR_ERROR_ONLY && color != LED_RED) return false;
|
if(ledBehaviour == LED_BEHAVIOUR_ERROR_ONLY && color != LED_RED) return false;
|
||||||
if(config->ledBehaviour == LED_BEHAVIOUR_BOOT && color != LED_RED && bootSuccessful) return false;
|
if(ledBehaviour == LED_BEHAVIOUR_BOOT && color != LED_RED && bootSuccessful) return false;
|
||||||
|
|
||||||
if(color == LED_INTERNAL) {
|
if(color == LED_INTERNAL) {
|
||||||
return writeLedPin(color, config->ledInverted ? LOW : HIGH);
|
return writeLedPin(color, ledInvert ? LOW : HIGH);
|
||||||
} else {
|
} else {
|
||||||
return writeLedPin(color, config->ledRgbInverted ? LOW : HIGH);
|
return writeLedPin(color, rgbInvert ? LOW : HIGH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HwTools::ledOff(uint8_t color) {
|
bool HwTools::ledOff(uint8_t color) {
|
||||||
if(color == LED_INTERNAL) {
|
if(color == LED_INTERNAL) {
|
||||||
return writeLedPin(color, config->ledInverted ? HIGH : LOW);
|
return writeLedPin(color, ledInvert ? HIGH : LOW);
|
||||||
} else {
|
} else {
|
||||||
return writeLedPin(color, config->ledRgbInverted ? HIGH : LOW);
|
return writeLedPin(color, rgbInvert ? HIGH : LOW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,8 +595,8 @@ bool HwTools::ledBlink(uint8_t color, uint8_t blink) {
|
|||||||
bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
||||||
switch(color) {
|
switch(color) {
|
||||||
case LED_INTERNAL: {
|
case LED_INTERNAL: {
|
||||||
if(config->ledPin != 0xFF) {
|
if(ledPin != 0xFF) {
|
||||||
digitalWrite(config->ledPin, state);
|
digitalWrite(ledPin, state);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -590,8 +604,8 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LED_RED: {
|
case LED_RED: {
|
||||||
if(config->ledPinRed != 0xFF) {
|
if(redPin != 0xFF) {
|
||||||
digitalWrite(config->ledPinRed, state);
|
digitalWrite(redPin, state);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -599,8 +613,8 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LED_GREEN: {
|
case LED_GREEN: {
|
||||||
if(config->ledPinGreen != 0xFF) {
|
if(greenPin != 0xFF) {
|
||||||
digitalWrite(config->ledPinGreen, state);
|
digitalWrite(greenPin, state);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -608,8 +622,8 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LED_BLUE: {
|
case LED_BLUE: {
|
||||||
if(config->ledPinBlue != 0xFF) {
|
if(bluePin != 0xFF) {
|
||||||
digitalWrite(config->ledPinBlue, state);
|
digitalWrite(bluePin, state);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -617,9 +631,9 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LED_YELLOW: {
|
case LED_YELLOW: {
|
||||||
if(config->ledPinRed != 0xFF && config->ledPinGreen != 0xFF) {
|
if(redPin != 0xFF && greenPin != 0xFF) {
|
||||||
digitalWrite(config->ledPinRed, state);
|
digitalWrite(redPin, state);
|
||||||
digitalWrite(config->ledPinGreen, state);
|
digitalWrite(greenPin, state);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -629,3 +643,22 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HwTools::isVoltageOptimal(float range) {
|
||||||
|
if(boardType >= 5 && boardType <= 7 && maxVcc > 2.8) { // Pow-*
|
||||||
|
float vcc = getVcc();
|
||||||
|
if(vcc > 3.4 || vcc < 2.8) {
|
||||||
|
maxVcc = 0; // Voltage is outside the operating range, we have to assume voltage is OK
|
||||||
|
} else if(vcc > maxVcc) {
|
||||||
|
maxVcc = vcc;
|
||||||
|
} else {
|
||||||
|
float diff = min(maxVcc, (float) 3.3) - vcc;
|
||||||
|
return diff < range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HwTools::getBoardType() {
|
||||||
|
return boardType;
|
||||||
|
}
|
||||||
2
lib/SvelteUi/app/dist/index.css
vendored
2
lib/SvelteUi/app/dist/index.css
vendored
File diff suppressed because one or more lines are too long
12
lib/SvelteUi/app/dist/index.js
vendored
12
lib/SvelteUi/app/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Router, Route, navigate } from "svelte-navigator";
|
import { Router, Route, navigate } from "svelte-navigator";
|
||||||
import { getTariff, tariffStore, sysinfoStore, dataStore, pricesStore, dayPlotStore, monthPlotStore, temperaturesStore } from './lib/DataStores.js';
|
import { getTariff, tariffStore, sysinfoStore, dataStore, pricesStore, dayPlotStore, monthPlotStore, temperaturesStore, getSysinfo } from './lib/DataStores.js';
|
||||||
import { translationsStore, getTranslations } from "./lib/TranslationService.js";
|
import { translationsStore, getTranslations } from "./lib/TranslationService.js";
|
||||||
import Favicon from './assets/favicon.svg'; // Need this for the build
|
import Favicon from './assets/favicon.svg'; // Need this for the build
|
||||||
import Header from './lib/Header.svelte';
|
import Header from './lib/Header.svelte';
|
||||||
@ -44,6 +44,8 @@
|
|||||||
translations = update;
|
translations = update;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let sito;
|
||||||
|
let data = {};
|
||||||
let sysinfo = {};
|
let sysinfo = {};
|
||||||
sysinfoStore.subscribe(update => {
|
sysinfoStore.subscribe(update => {
|
||||||
sysinfo = update;
|
sysinfo = update;
|
||||||
@ -70,9 +72,11 @@
|
|||||||
if(sysinfo.ui.lang && sysinfo.ui.lang != translations?.language?.code) {
|
if(sysinfo.ui.lang && sysinfo.ui.lang != translations?.language?.code) {
|
||||||
getTranslations(sysinfo.ui.lang);
|
getTranslations(sysinfo.ui.lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(sito) clearTimeout(sito);
|
||||||
|
sito = setTimeout(getSysinfo, !data || !data.u || data.u < 30 || sysinfo?.upgrading ? 10000 : 300000);
|
||||||
});
|
});
|
||||||
|
|
||||||
let data = {};
|
|
||||||
dataStore.subscribe(update => {
|
dataStore.subscribe(update => {
|
||||||
data = update;
|
data = update;
|
||||||
updateRealtime(update);
|
updateRealtime(update);
|
||||||
@ -126,9 +130,7 @@
|
|||||||
</Route>
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
||||||
{#if sysinfo.upgrading}
|
{#if sysinfo.booting}
|
||||||
<Mask active=true message="Device is upgrading, please wait"/>
|
|
||||||
{:else if sysinfo.booting}
|
|
||||||
{#if sysinfo.trying}
|
{#if sysinfo.trying}
|
||||||
<Mask active=true message="Device is booting, please wait. Trying to reach it on {sysinfo.trying}"/>
|
<Mask active=true message="Device is booting, please wait. Trying to reach it on {sysinfo.trying}"/>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@ -46,6 +46,7 @@ function updateSysinfo(url) {
|
|||||||
let tries = 0;
|
let tries = 0;
|
||||||
let lastTemp = -127;
|
let lastTemp = -127;
|
||||||
let lastPrice = null;
|
let lastPrice = null;
|
||||||
|
let lastUp = 0;
|
||||||
let data = {};
|
let data = {};
|
||||||
export const dataStore = readable(data, (set) => {
|
export const dataStore = readable(data, (set) => {
|
||||||
let timeout;
|
let timeout;
|
||||||
@ -63,7 +64,7 @@ export const dataStore = readable(data, (set) => {
|
|||||||
lastPrice = data.p;
|
lastPrice = data.p;
|
||||||
getPrices();
|
getPrices();
|
||||||
}
|
}
|
||||||
if(sysinfo.upgrading) {
|
if(sysinfo.upgrading && lastUp > data.u) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
} else if(!sysinfo || !sysinfo.chip || sysinfo.booting || (tries > 1 && !isBusPowered(sysinfo.board))) {
|
} else if(!sysinfo || !sysinfo.chip || sysinfo.booting || (tries > 1 && !isBusPowered(sysinfo.board))) {
|
||||||
getSysinfo();
|
getSysinfo();
|
||||||
@ -72,6 +73,7 @@ export const dataStore = readable(data, (set) => {
|
|||||||
if(monthPlotTimeout) clearTimeout(monthPlotTimeout);
|
if(monthPlotTimeout) clearTimeout(monthPlotTimeout);
|
||||||
monthPlotTimeout = setTimeout(getMonthPlot, 3000);
|
monthPlotTimeout = setTimeout(getMonthPlot, 3000);
|
||||||
}
|
}
|
||||||
|
lastUp = data.u;
|
||||||
if(!dayPlotTimeout) dayPlotTimeout = getDayPlot();
|
if(!dayPlotTimeout) dayPlotTimeout = getDayPlot();
|
||||||
if(!monthPlotTimeout) monthPlotTimeout = getMonthPlot();
|
if(!monthPlotTimeout) monthPlotTimeout = getMonthPlot();
|
||||||
|
|
||||||
@ -212,12 +214,3 @@ export async function getTariff() {
|
|||||||
export const tariffStore = writable(tariff, (set) => {
|
export const tariffStore = writable(tariff, (set) => {
|
||||||
return function stop() {}
|
return function stop() {}
|
||||||
});
|
});
|
||||||
|
|
||||||
let releases = [];
|
|
||||||
export const gitHubReleaseStore = writable(releases);
|
|
||||||
|
|
||||||
export async function getGitHubReleases() {
|
|
||||||
const response = await fetchWithTimeout("https://api.github.com/repos/UtilitechAS/amsreader-firmware/releases");
|
|
||||||
releases = (await response.json())
|
|
||||||
gitHubReleaseStore.set(releases);
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Link } from "svelte-navigator";
|
import { Link } from "svelte-navigator";
|
||||||
import { sysinfoStore, getGitHubReleases, gitHubReleaseStore } from './DataStores.js';
|
import { sysinfoStore } from './DataStores.js';
|
||||||
import { upgrade, getNextVersion, upgradeWarningText } from './UpgradeHelper';
|
import { upgrade, upgradeWarningText } from './UpgradeHelper';
|
||||||
import { boardtype, isBusPowered, wiki, bcol } from './Helpers.js';
|
import { boardtype, isBusPowered, wiki, bcol } from './Helpers.js';
|
||||||
import { translationsStore } from "./TranslationService.js";
|
import { translationsStore } from "./TranslationService.js";
|
||||||
import FavIco from './../assets/favicon.svg';
|
import FavIco from './../assets/favicon.svg';
|
||||||
@ -16,34 +16,30 @@
|
|||||||
export let data = {};
|
export let data = {};
|
||||||
let sysinfo = {};
|
let sysinfo = {};
|
||||||
|
|
||||||
let nextVersion = {};
|
|
||||||
|
|
||||||
function askUpgrade() {
|
function askUpgrade() {
|
||||||
if(confirm((translations.header?.upgrade ?? "Upgrade to {0}?").replace('{0}',nextVersion.tag_name))) {
|
if(confirm((translations.header?.upgrade ?? "Upgrade to {0}?").replace('{0}',sysinfo.upgrade.n))) {
|
||||||
if(!isBusPowered(sysinfo.board) || confirm(upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board)))) {
|
upgrade(sysinfo.upgrade.n);
|
||||||
sysinfoStore.update(s => {
|
sysinfoStore.update(s => {
|
||||||
s.upgrading = true;
|
s.upgrade.t = sysinfo.upgrade.n;
|
||||||
return s;
|
s.upgrading = true;
|
||||||
});
|
return s;
|
||||||
upgrade(nextVersion.tag_name);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let progress;
|
||||||
sysinfoStore.subscribe(update => {
|
sysinfoStore.subscribe(update => {
|
||||||
sysinfo = update;
|
sysinfo = update;
|
||||||
if(update.fwconsent === 1) {
|
|
||||||
getGitHubReleases();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
gitHubReleaseStore.subscribe(releases => {
|
|
||||||
nextVersion = getNextVersion(sysinfo.version, releases);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let translations = {};
|
let translations = {};
|
||||||
translationsStore.subscribe(update => {
|
translationsStore.subscribe(update => {
|
||||||
translations = update;
|
translations = update;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$: {
|
||||||
|
progress = Math.max(0, sysinfo.upgrade.p);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav class="hdr">
|
<nav class="hdr">
|
||||||
@ -91,12 +87,14 @@
|
|||||||
<div class="flex-none px-1 mt-1" title={translations.header?.doc ?? ""}>
|
<div class="flex-none px-1 mt-1" title={translations.header?.doc ?? ""}>
|
||||||
<a href={wiki('')} target='_blank' rel="noreferrer"><HelpIcon/></a>
|
<a href={wiki('')} target='_blank' rel="noreferrer"><HelpIcon/></a>
|
||||||
</div>
|
</div>
|
||||||
{#if sysinfo.fwconsent === 1 && nextVersion}
|
{#if sysinfo.upgrading}
|
||||||
<div class="flex-none mr-3 text-yellow-500" title={(translations.header?.new_version ?? "New version") + ': ' + nextVersion.tag_name}>
|
<div class="flex-none mr-3 mt-1 text-yellow-300">Upgrading to {sysinfo.upgrade.t}, {progress.toFixed(1)}%</div>
|
||||||
|
{:else if sysinfo.fwconsent === 1 && sysinfo.upgrade.n}
|
||||||
|
<div class="flex-none mr-3 text-yellow-500" title={(translations.header?.new_version ?? "New version") + ': ' + sysinfo.upgrade.n}>
|
||||||
{#if sysinfo.security == 0 || data.a}
|
{#if sysinfo.security == 0 || data.a}
|
||||||
<button on:click={askUpgrade} class="flex"><span class="mt-1">{translations.header?.new_version ?? "New version"}: {nextVersion.tag_name}</span></button>
|
<button on:click={askUpgrade} class="flex"><span class="mt-1">{translations.header?.new_version ?? "New version"}: {sysinfo.upgrade.n}</span></button>
|
||||||
{:else}
|
{:else}
|
||||||
<span>{translations.header?.new_version ?? "New version"}: {nextVersion.tag_name}</span>
|
<span>{translations.header?.new_version ?? "New version"}: {sysinfo.upgrade.n}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { metertype, boardtype, isBusPowered, getBaseChip } from './Helpers.js';
|
import { metertype, boardtype, isBusPowered, getBaseChip } from './Helpers.js';
|
||||||
import { getSysinfo, gitHubReleaseStore, sysinfoStore } from './DataStores.js';
|
import { getSysinfo, sysinfoStore } from './DataStores.js';
|
||||||
import { upgrade, getNextVersion, upgradeWarningText } from './UpgradeHelper';
|
import { upgrade, upgradeWarningText } from './UpgradeHelper';
|
||||||
import { translationsStore } from './TranslationService.js';
|
import { translationsStore } from './TranslationService.js';
|
||||||
import { Link } from 'svelte-navigator';
|
import { Link } from 'svelte-navigator';
|
||||||
import Clock from './Clock.svelte';
|
import Clock from './Clock.svelte';
|
||||||
@ -41,24 +41,15 @@
|
|||||||
translationsStore.subscribe(update => {
|
translationsStore.subscribe(update => {
|
||||||
translations = update;
|
translations = update;
|
||||||
});
|
});
|
||||||
|
|
||||||
let nextVersion = {};
|
|
||||||
gitHubReleaseStore.subscribe(releases => {
|
|
||||||
nextVersion = getNextVersion(sysinfo.version, releases);
|
|
||||||
if(!nextVersion) {
|
|
||||||
nextVersion = releases[0];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function askUpgrade() {
|
function askUpgrade() {
|
||||||
if(confirm((translations.header?.upgrade ?? "Upgrade to {0}?").replace('{0}',nextVersion.tag_name))) {
|
if(confirm((translations.header?.upgrade ?? "Upgrade to {0}?").replace('{0}',sysinfo.upgrade.n))) {
|
||||||
if((sysinfo.board != 2 && sysinfo.board != 4 && sysinfo.board != 7) || confirm(upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board)))) {
|
upgrade(sysinfo.upgrade.n);
|
||||||
sysinfoStore.update(s => {
|
sysinfoStore.update(s => {
|
||||||
s.upgrading = true;
|
s.upgrade.t = sysinfo.upgrade.n;
|
||||||
return s;
|
s.upgrading = true;
|
||||||
});
|
return s;
|
||||||
upgrade(nextVersion.tag_name);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +147,7 @@
|
|||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
{translations.status?.device?.last_boot ?? "Last boot"}:
|
{translations.status?.device?.last_boot ?? "Last boot"}:
|
||||||
{#if data.u > 0}
|
{#if data.u > 0}
|
||||||
<Clock timestamp={new Date(new Date().getTime() - (data.u * 1000))} fullTimeColor="" />
|
<Clock timestamp={new Date(new Date().getTime() - (data.u * 1000))} fullTimeColor="" offset={sysinfo.clock_offset}/>
|
||||||
{:else}
|
{:else}
|
||||||
-
|
-
|
||||||
{/if}
|
{/if}
|
||||||
@ -228,11 +219,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if nextVersion}
|
{#if sysinfo.upgrade.n}
|
||||||
<div class="my-2 flex">
|
<div class="my-2 flex">
|
||||||
{translations.status?.firmware?.latest ?? "Latest"}:
|
{translations.status?.firmware?.latest ?? "Latest"}:
|
||||||
<a href={nextVersion.html_url} class="ml-2 text-blue-600 hover:text-blue-800" target='_blank' rel="noreferrer">{nextVersion.tag_name}</a>
|
<a href={"https://github.com/UtilitechAS/amsreader-firmware/releases/tag/" + sysinfo.upgrade.n} class="ml-2 text-blue-600 hover:text-blue-800" target='_blank' rel="noreferrer">{sysinfo.upgrade.n}</a>
|
||||||
{#if (sysinfo.security == 0 || data.a) && sysinfo.fwconsent === 1 && nextVersion && nextVersion.tag_name != sysinfo.version}
|
{#if (sysinfo.security == 0 || data.a) && sysinfo.fwconsent === 1 && sysinfo.upgrade.n && sysinfo.upgrade.n != sysinfo.version}
|
||||||
<div class="flex-none ml-2 text-green-500" title={translations.status?.firmware?.install ?? "Install"}>
|
<div class="flex-none ml-2 text-green-500" title={translations.status?.firmware?.install ?? "Install"}>
|
||||||
<button on:click={askUpgrade}>⇓</button>
|
<button on:click={askUpgrade}>⇓</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<option value={-1}>disabled</option>
|
||||||
{#if chip == 'esp8266'}
|
{#if chip == 'esp8266'}
|
||||||
<option value={3}>UART0</option>
|
<option value={3}>UART0</option>
|
||||||
<option value={113}>UART2</option>
|
<option value={113}>UART2</option>
|
||||||
|
|||||||
@ -6,61 +6,5 @@ export async function upgrade(expected_version) {
|
|||||||
const response = await fetch('upgrade?expected_version='+expected_version, {
|
const response = await fetch('upgrade?expected_version='+expected_version, {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
await response.json();
|
return await response.json();
|
||||||
}
|
|
||||||
|
|
||||||
export function getNextVersion(currentVersion, releases_orig) {
|
|
||||||
if(!releases_orig || releases_orig.message) return;
|
|
||||||
if(/^v\d{1,2}\.\d{1,2}\.\d{1,2}$/.test(currentVersion)) {
|
|
||||||
let v = currentVersion.substring(1).split('.');
|
|
||||||
let v_major = parseInt(v[0]);
|
|
||||||
let v_minor = parseInt(v[1]);
|
|
||||||
let v_patch = parseInt(v[2]);
|
|
||||||
|
|
||||||
let releases = [...releases_orig];
|
|
||||||
releases.reverse();
|
|
||||||
let next_patch;
|
|
||||||
let next_minor;
|
|
||||||
let next_major;
|
|
||||||
for(let i = 0; i < releases.length; i++) {
|
|
||||||
let release = releases[i];
|
|
||||||
let ver2 = release.tag_name;
|
|
||||||
let v2 = ver2.substring(1).split('.');
|
|
||||||
let v2_major = parseInt(v2[0]);
|
|
||||||
let v2_minor = parseInt(v2[1]);
|
|
||||||
let v2_patch = parseInt(v2[2]);
|
|
||||||
|
|
||||||
if(v2_major == v_major) {
|
|
||||||
if(v2_minor == v_minor) {
|
|
||||||
if(v2_patch > v_patch) {
|
|
||||||
next_patch = release;
|
|
||||||
}
|
|
||||||
} else if(v2_minor == v_minor+1) {
|
|
||||||
next_minor = release;
|
|
||||||
}
|
|
||||||
} else if(v2_major == v_major+1) {
|
|
||||||
if(next_major) {
|
|
||||||
let mv = next_major.tag_name.substring(1).split('.');
|
|
||||||
let mv_major = parseInt(mv[0]);
|
|
||||||
let mv_minor = parseInt(mv[1]);
|
|
||||||
let mv_patch = parseInt(mv[2]);
|
|
||||||
if(v2_minor == mv_minor) {
|
|
||||||
next_major = release;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
next_major = release;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if(next_minor) {
|
|
||||||
return next_minor;
|
|
||||||
} else if(next_major) {
|
|
||||||
return next_major;
|
|
||||||
} else if(next_patch) {
|
|
||||||
return next_patch;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return releases_orig[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,25 +17,25 @@ export default defineConfig({
|
|||||||
plugins: [svelte()],
|
plugins: [svelte()],
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
"/data.json": "http://192.168.233.49",
|
"/data.json": "http://192.168.233.154",
|
||||||
"/energyprice.json": "http://192.168.233.49",
|
"/energyprice.json": "http://192.168.233.154",
|
||||||
"/dayplot.json": "http://192.168.233.49",
|
"/dayplot.json": "http://192.168.233.154",
|
||||||
"/monthplot.json": "http://192.168.233.49",
|
"/monthplot.json": "http://192.168.233.154",
|
||||||
"/temperature.json": "http://192.168.233.49",
|
"/temperature.json": "http://192.168.233.154",
|
||||||
"/sysinfo.json": "http://192.168.233.49",
|
"/sysinfo.json": "http://192.168.233.154",
|
||||||
"/configuration.json": "http://192.168.233.49",
|
"/configuration.json": "http://192.168.233.154",
|
||||||
"/tariff.json": "http://192.168.233.49",
|
"/tariff.json": "http://192.168.233.154",
|
||||||
"/realtime.json": "http://192.168.233.49",
|
"/realtime.json": "http://192.168.233.154",
|
||||||
"/priceconfig.json": "http://192.168.233.49",
|
"/priceconfig.json": "http://192.168.233.154",
|
||||||
"/cloudkey.json": "http://192.168.233.49",
|
"/cloudkey.json": "http://192.168.233.154",
|
||||||
"/save": "http://192.168.233.49",
|
"/save": "http://192.168.233.154",
|
||||||
"/reboot": "http://192.168.233.49",
|
"/reboot": "http://192.168.233.154",
|
||||||
"/configfile": "http://192.168.233.49",
|
"/configfile": "http://192.168.233.154",
|
||||||
"/upgrade": "http://192.168.233.49",
|
"/upgrade": "http://192.168.233.154",
|
||||||
"/mqtt-ca": "http://192.168.233.49",
|
"/mqtt-ca": "http://192.168.233.154",
|
||||||
"/mqtt-cert": "http://192.168.233.49",
|
"/mqtt-cert": "http://192.168.233.154",
|
||||||
"/mqtt-key": "http://192.168.233.49",
|
"/mqtt-key": "http://192.168.233.154",
|
||||||
"/logo.svg": "http://192.168.233.49",
|
"/logo.svg": "http://192.168.233.154",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include "AmsData.h"
|
#include "AmsData.h"
|
||||||
#include "AmsStorage.h"
|
#include "AmsStorage.h"
|
||||||
#include "AmsDataStorage.h"
|
#include "AmsDataStorage.h"
|
||||||
|
#include "AmsFirmwareUpdater.h"
|
||||||
#include "EnergyAccounting.h"
|
#include "EnergyAccounting.h"
|
||||||
#include "Uptime.h"
|
#include "Uptime.h"
|
||||||
#if defined(AMS_REMOTE_DEBUG)
|
#if defined(AMS_REMOTE_DEBUG)
|
||||||
@ -53,7 +54,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
AmsWebServer(uint8_t* buf, Stream* Debug, HwTools* hw, ResetDataContainer* rdc);
|
AmsWebServer(uint8_t* buf, Stream* Debug, HwTools* hw, ResetDataContainer* rdc);
|
||||||
#endif
|
#endif
|
||||||
void setup(AmsConfiguration*, GpioConfig*, AmsData*, AmsDataStorage*, EnergyAccounting*, RealtimePlot*);
|
void setup(AmsConfiguration*, GpioConfig*, AmsData*, AmsDataStorage*, EnergyAccounting*, RealtimePlot*, AmsFirmwareUpdater*);
|
||||||
void loop();
|
void loop();
|
||||||
#if defined(_CLOUDCONNECTOR_H)
|
#if defined(_CLOUDCONNECTOR_H)
|
||||||
void setCloud(CloudConnector* cloud);
|
void setCloud(CloudConnector* cloud);
|
||||||
@ -88,6 +89,7 @@ private:
|
|||||||
AmsDataStorage* ds;
|
AmsDataStorage* ds;
|
||||||
EnergyAccounting* ea = NULL;
|
EnergyAccounting* ea = NULL;
|
||||||
RealtimePlot* rtp = NULL;
|
RealtimePlot* rtp = NULL;
|
||||||
|
AmsFirmwareUpdater* updater = NULL;
|
||||||
AmsMqttHandler* mqttHandler = NULL;
|
AmsMqttHandler* mqttHandler = NULL;
|
||||||
ConnectionHandler* ch = NULL;
|
ConnectionHandler* ch = NULL;
|
||||||
#if defined(_CLOUDCONNECTOR_H)
|
#if defined(_CLOUDCONNECTOR_H)
|
||||||
@ -139,7 +141,6 @@ private:
|
|||||||
void handleSave();
|
void handleSave();
|
||||||
void reboot();
|
void reboot();
|
||||||
void upgrade();
|
void upgrade();
|
||||||
void upgradeFromUrl(String url, String nextVersion);
|
|
||||||
void firmwareHtml();
|
void firmwareHtml();
|
||||||
void firmwarePost();
|
void firmwarePost();
|
||||||
void firmwareUpload();
|
void firmwareUpload();
|
||||||
@ -167,7 +168,6 @@ private:
|
|||||||
void robotstxt();
|
void robotstxt();
|
||||||
void ssdpSchema();
|
void ssdpSchema();
|
||||||
|
|
||||||
void updaterRequestCallback(HTTPClient*);
|
|
||||||
void addConditionalCloudHeaders();
|
void addConditionalCloudHeaders();
|
||||||
void optionsGet();
|
void optionsGet();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -56,7 +56,9 @@
|
|||||||
"x": %d,
|
"x": %d,
|
||||||
"e": %d,
|
"e": %d,
|
||||||
"f": "%s",
|
"f": "%s",
|
||||||
"t": "%s"
|
"t": "%s",
|
||||||
|
"n": "%s",
|
||||||
|
"p": %.1f
|
||||||
},
|
},
|
||||||
"last_month": {
|
"last_month": {
|
||||||
"u" : %.2f,
|
"u" : %.2f,
|
||||||
|
|||||||
@ -70,13 +70,14 @@ AmsWebServer::AmsWebServer(uint8_t* buf, Stream* Debug, HwTools* hw, ResetDataCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, AmsData* meterState, AmsDataStorage* ds, EnergyAccounting* ea, RealtimePlot* rtp) {
|
void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, AmsData* meterState, AmsDataStorage* ds, EnergyAccounting* ea, RealtimePlot* rtp, AmsFirmwareUpdater* updater) {
|
||||||
this->config = config;
|
this->config = config;
|
||||||
this->gpioConfig = gpioConfig;
|
this->gpioConfig = gpioConfig;
|
||||||
this->meterState = meterState;
|
this->meterState = meterState;
|
||||||
this->ds = ds;
|
this->ds = ds;
|
||||||
this->ea = ea;
|
this->ea = ea;
|
||||||
this->rtp = rtp;
|
this->rtp = rtp;
|
||||||
|
this->updater = updater;
|
||||||
|
|
||||||
String context;
|
String context;
|
||||||
config->getWebConfig(webConfig);
|
config->getWebConfig(webConfig);
|
||||||
@ -371,7 +372,7 @@ void AmsWebServer::sysinfoJson() {
|
|||||||
config->getUiConfig(ui);
|
config->getUiConfig(ui);
|
||||||
|
|
||||||
UpgradeInformation upinfo;
|
UpgradeInformation upinfo;
|
||||||
config->getUpgradeInformation(upinfo);
|
updater->getUpgradeInformation(upinfo);
|
||||||
|
|
||||||
String meterModel = meterState->getMeterModel();
|
String meterModel = meterState->getMeterModel();
|
||||||
if(!meterModel.isEmpty())
|
if(!meterModel.isEmpty())
|
||||||
@ -417,7 +418,7 @@ void AmsWebServer::sysinfoJson() {
|
|||||||
sys.dataCollectionConsent,
|
sys.dataCollectionConsent,
|
||||||
hostname.c_str(),
|
hostname.c_str(),
|
||||||
performRestart ? "true" : "false",
|
performRestart ? "true" : "false",
|
||||||
rebootForUpgrade ? "true" : "false",
|
updater->getProgress() > 0.0 ? "true" : "false",
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
localIp.isSet() ? localIp.toString().c_str() : "",
|
localIp.isSet() ? localIp.toString().c_str() : "",
|
||||||
subnet.isSet() ? subnet.toString().c_str() : "",
|
subnet.isSet() ? subnet.toString().c_str() : "",
|
||||||
@ -470,6 +471,8 @@ void AmsWebServer::sysinfoJson() {
|
|||||||
upinfo.errorCode,
|
upinfo.errorCode,
|
||||||
upinfo.fromVersion,
|
upinfo.fromVersion,
|
||||||
upinfo.toVersion,
|
upinfo.toVersion,
|
||||||
|
updater->getNextVersion(),
|
||||||
|
updater->getProgress(),
|
||||||
ea->getUseLastMonth(),
|
ea->getUseLastMonth(),
|
||||||
ea->getCostLastMonth(),
|
ea->getCostLastMonth(),
|
||||||
ea->getProducedLastMonth(),
|
ea->getProducedLastMonth(),
|
||||||
@ -1173,6 +1176,9 @@ void AmsWebServer::handleSave() {
|
|||||||
if(!checkSecurity(1))
|
if(!checkSecurity(1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
SystemConfig sys;
|
||||||
|
config->getSystemConfig(sys);
|
||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
if(server.hasArg(F("v")) && server.arg(F("v")) == F("true")) {
|
if(server.hasArg(F("v")) && server.arg(F("v")) == F("true")) {
|
||||||
int boardType = server.arg(F("vb")).toInt();
|
int boardType = server.arg(F("vb")).toInt();
|
||||||
@ -1189,8 +1195,6 @@ void AmsWebServer::handleSave() {
|
|||||||
config->setGpioConfig(*gpioConfig);
|
config->setGpioConfig(*gpioConfig);
|
||||||
config->setMeterConfig(meterConfig);
|
config->setMeterConfig(meterConfig);
|
||||||
|
|
||||||
SystemConfig sys;
|
|
||||||
config->getSystemConfig(sys);
|
|
||||||
sys.boardType = success ? boardType : 0xFF;
|
sys.boardType = success ? boardType : 0xFF;
|
||||||
sys.vendorConfigured = success;
|
sys.vendorConfigured = success;
|
||||||
config->setSystemConfig(sys);
|
config->setSystemConfig(sys);
|
||||||
@ -1198,8 +1202,6 @@ void AmsWebServer::handleSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(server.hasArg(F("s")) && server.arg(F("s")) == F("true")) {
|
if(server.hasArg(F("s")) && server.arg(F("s")) == F("true")) {
|
||||||
SystemConfig sys;
|
|
||||||
config->getSystemConfig(sys);
|
|
||||||
MeterConfig meterConfig;
|
MeterConfig meterConfig;
|
||||||
config->getMeterConfig(meterConfig);
|
config->getMeterConfig(meterConfig);
|
||||||
|
|
||||||
@ -1267,8 +1269,6 @@ void AmsWebServer::handleSave() {
|
|||||||
performRestart = true;
|
performRestart = true;
|
||||||
}
|
}
|
||||||
} else if(server.hasArg(F("sf")) && !server.arg(F("sf")).isEmpty()) {
|
} else if(server.hasArg(F("sf")) && !server.arg(F("sf")).isEmpty()) {
|
||||||
SystemConfig sys;
|
|
||||||
config->getSystemConfig(sys);
|
|
||||||
sys.dataCollectionConsent = server.hasArg(F("sf")) && (server.arg(F("sf")) == F("true") || server.arg(F("sf")) == F("1")) ? 1 : 2;
|
sys.dataCollectionConsent = server.hasArg(F("sf")) && (server.arg(F("sf")) == F("true") || server.arg(F("sf")) == F("1")) ? 1 : 2;
|
||||||
config->setSystemConfig(sys);
|
config->setSystemConfig(sys);
|
||||||
}
|
}
|
||||||
@ -1564,8 +1564,6 @@ void AmsWebServer::handleSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(server.hasArg(F("c")) && server.arg(F("c")) == F("true")) {
|
if(server.hasArg(F("c")) && server.arg(F("c")) == F("true")) {
|
||||||
SystemConfig sys;
|
|
||||||
config->getSystemConfig(sys);
|
|
||||||
sys.energyspeedometer = server.hasArg(F("ces")) && server.arg(F("ces")) == F("true") ? 7 : 0;
|
sys.energyspeedometer = server.hasArg(F("ces")) && server.arg(F("ces")) == F("true") ? 7 : 0;
|
||||||
config->setSystemConfig(sys);
|
config->setSystemConfig(sys);
|
||||||
|
|
||||||
@ -1651,7 +1649,7 @@ debugger->printf_P(PSTR("Successfully saved.\n"));
|
|||||||
if(config->isNetworkConfigChanged() || performRestart) {
|
if(config->isNetworkConfigChanged() || performRestart) {
|
||||||
performRestart = true;
|
performRestart = true;
|
||||||
} else {
|
} else {
|
||||||
hw->setup(gpioConfig);
|
hw->setup(&sys, gpioConfig);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
success = false;
|
success = false;
|
||||||
@ -1726,85 +1724,8 @@ void AmsWebServer::upgrade() {
|
|||||||
server.handleClient();
|
server.handleClient();
|
||||||
delay(250);
|
delay(250);
|
||||||
|
|
||||||
if(server.hasArg(F("url"))) {
|
String version = server.arg(F("expected_version"));
|
||||||
customFirmwareUrl = server.arg(F("url"));
|
updater->setTargetVersion(version.c_str());
|
||||||
}
|
|
||||||
|
|
||||||
String url = customFirmwareUrl.isEmpty() || !customFirmwareUrl.startsWith(F("http")) ? F("http://hub.amsleser.no/hub/firmware/update") : customFirmwareUrl;
|
|
||||||
|
|
||||||
if(server.hasArg(F("version"))) {
|
|
||||||
url += "/" + server.arg(F("version"));
|
|
||||||
}
|
|
||||||
upgradeFromUrl(url, server.arg(F("expected_version")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AmsWebServer::upgradeFromUrl(String url, String nextVersion) {
|
|
||||||
config->setUpgradeInformation(0xFF, 0xFF, FirmwareVersion::VersionString, nextVersion.c_str());
|
|
||||||
|
|
||||||
WiFiClient client;
|
|
||||||
#if defined(ESP8266)
|
|
||||||
String chipType = F("esp8266");
|
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
|
||||||
String chipType = F("esp32s2");
|
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
String chipType = F("esp32s3");
|
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
||||||
String chipType = F("esp32c3");
|
|
||||||
#elif defined(ESP32)
|
|
||||||
#if defined(CONFIG_FREERTOS_UNICORE)
|
|
||||||
String chipType = F("esp32solo");
|
|
||||||
#else
|
|
||||||
String chipType = F("esp32");
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ESP8266)
|
|
||||||
ESP8266HTTPUpdate httpUpdate = ESP8266HTTPUpdate(60000);
|
|
||||||
String currentVersion = FirmwareVersion::VersionString;
|
|
||||||
ESP.wdtEnable(300000);
|
|
||||||
#elif defined(ESP32)
|
|
||||||
HTTPUpdate httpUpdate = HTTPUpdate(60000);
|
|
||||||
String currentVersion = String(FirmwareVersion::VersionString) + "-" + chipType;
|
|
||||||
esp_task_wdt_init(300, true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
httpUpdate.rebootOnUpdate(false);
|
|
||||||
httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
|
||||||
#if defined(ESP32)
|
|
||||||
HTTPUpdateResult ret = httpUpdate.update(client, url, currentVersion, std::bind(&AmsWebServer::updaterRequestCallback, this, std::placeholders::_1));
|
|
||||||
#else
|
|
||||||
HTTPUpdateResult ret = httpUpdate.update(client, url, currentVersion);
|
|
||||||
#endif
|
|
||||||
int lastError = httpUpdate.getLastError();
|
|
||||||
|
|
||||||
config->setUpgradeInformation(ret, ret == HTTP_UPDATE_OK ? 0 : lastError, FirmwareVersion::VersionString, nextVersion.c_str());
|
|
||||||
switch(ret) {
|
|
||||||
case HTTP_UPDATE_FAILED:
|
|
||||||
debugger->printf_P(PSTR("Update failed\n"));
|
|
||||||
break;
|
|
||||||
case HTTP_UPDATE_NO_UPDATES:
|
|
||||||
debugger->printf_P(PSTR("No Update\n"));
|
|
||||||
break;
|
|
||||||
case HTTP_UPDATE_OK:
|
|
||||||
debugger->printf_P(PSTR("Update OK\n"));
|
|
||||||
debugger->flush();
|
|
||||||
rdc->cause = 4;
|
|
||||||
ESP.restart();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AmsWebServer::updaterRequestCallback(HTTPClient* http) {
|
|
||||||
SystemConfig sys;
|
|
||||||
if(config->getSystemConfig(sys)) {
|
|
||||||
http->addHeader(F("x-AMS-board-type"), String(sys.boardType, 10));
|
|
||||||
if(meterState->getMeterType() != AmsTypeAutodetect) {
|
|
||||||
http->addHeader(F("x-AMS-meter-mfg"), String(meterState->getMeterType(), 10));
|
|
||||||
}
|
|
||||||
if(!meterState->getMeterModel().isEmpty()) {
|
|
||||||
http->addHeader(F("x-AMS-meter-model"), meterState->getMeterModel());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1828,14 +1749,6 @@ void AmsWebServer::firmwarePost() {
|
|||||||
server.send(200);
|
server.send(200);
|
||||||
} else {
|
} else {
|
||||||
config->setUpgradeInformation(0xFF, 0xFF, FirmwareVersion::VersionString, "");
|
config->setUpgradeInformation(0xFF, 0xFF, FirmwareVersion::VersionString, "");
|
||||||
if(server.hasArg(F("url"))) {
|
|
||||||
String url = server.arg(F("url"));
|
|
||||||
if(!url.isEmpty() && (url.startsWith(F("http://")) || url.startsWith(F("https://")))) {
|
|
||||||
upgradeFromUrl(url, "");
|
|
||||||
server.send(200, MIME_PLAIN, "OK");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server.sendHeader(HEADER_LOCATION,F("/firmware"));
|
server.sendHeader(HEADER_LOCATION,F("/firmware"));
|
||||||
server.send(303);
|
server.send(303);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
extra_configs = platformio-user.ini
|
extra_configs = platformio-user.ini
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.2, OneWireNg@0.10.0, DallasTemperature@3.9.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1, Timezone@1.2.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, PriceService, EnergyAccounting, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, PassthroughMqttHandler, RealtimePlot, ConnectionHandler, MeterCommunicators
|
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.2, OneWireNg@0.10.0, DallasTemperature@3.9.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1, Timezone@1.2.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, PriceService, EnergyAccounting, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, PassthroughMqttHandler, RealtimePlot, ConnectionHandler, MeterCommunicators, AmsFirmwareUpdater
|
||||||
lib_ignore = OneWire
|
lib_ignore = OneWire
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
pre:scripts/addversion.py
|
pre:scripts/addversion.py
|
||||||
|
|||||||
@ -118,6 +118,8 @@ HardwareSerial Debug = Serial;
|
|||||||
|
|
||||||
#include "Timezones.h"
|
#include "Timezones.h"
|
||||||
|
|
||||||
|
#include "AmsFirmwareUpdater.h"
|
||||||
|
|
||||||
uint8_t commonBuffer[BUF_SIZE_COMMON];
|
uint8_t commonBuffer[BUF_SIZE_COMMON];
|
||||||
|
|
||||||
HwTools hw;
|
HwTools hw;
|
||||||
@ -178,6 +180,8 @@ bool ntpEnabled = false;
|
|||||||
|
|
||||||
bool mdnsEnabled = false;
|
bool mdnsEnabled = false;
|
||||||
|
|
||||||
|
AmsFirmwareUpdater updater(&Debug, &hw, &meterState);
|
||||||
|
|
||||||
AmsDataStorage ds(&Debug);
|
AmsDataStorage ds(&Debug);
|
||||||
#if defined(_CLOUDCONNECTOR_H)
|
#if defined(_CLOUDCONNECTOR_H)
|
||||||
CloudConnector *cloud = NULL;
|
CloudConnector *cloud = NULL;
|
||||||
@ -317,7 +321,7 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delay(1);
|
delay(1);
|
||||||
hw.setup(&gpioConfig);
|
hw.setup(&sysConfig, &gpioConfig);
|
||||||
|
|
||||||
if(gpioConfig.apPin >= 0) {
|
if(gpioConfig.apPin >= 0) {
|
||||||
pinMode(gpioConfig.apPin, INPUT_PULLUP);
|
pinMode(gpioConfig.apPin, INPUT_PULLUP);
|
||||||
@ -411,6 +415,10 @@ void setup() {
|
|||||||
|
|
||||||
debugI_P(PSTR("AMS bridge started"));
|
debugI_P(PSTR("AMS bridge started"));
|
||||||
debugI_P(PSTR("Voltage: %.2fV"), vcc);
|
debugI_P(PSTR("Voltage: %.2fV"), vcc);
|
||||||
|
if(updater.relocateOrRepartitionIfNecessary()) {
|
||||||
|
ESP.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
float vccBootLimit = gpioConfig.vccBootLimit == 0 ? 0 : min(3.29, gpioConfig.vccBootLimit / 10.0); // Make sure it is never above 3.3v
|
float vccBootLimit = gpioConfig.vccBootLimit == 0 ? 0 : min(3.29, gpioConfig.vccBootLimit / 10.0); // Make sure it is never above 3.3v
|
||||||
if(vccBootLimit > 2.5 && vccBootLimit < 3.3 && (gpioConfig.apPin == 0xFF || digitalRead(gpioConfig.apPin) == HIGH)) { // Skip if user is holding AP button while booting (HIGH = button is released)
|
if(vccBootLimit > 2.5 && vccBootLimit < 3.3 && (gpioConfig.apPin == 0xFF || digitalRead(gpioConfig.apPin) == HIGH)) { // Skip if user is holding AP button while booting (HIGH = button is released)
|
||||||
@ -530,7 +538,7 @@ void setup() {
|
|||||||
ea.setup(&ds, eac);
|
ea.setup(&ds, eac);
|
||||||
ea.load();
|
ea.load();
|
||||||
ea.setPriceService(ps);
|
ea.setPriceService(ps);
|
||||||
ws.setup(&config, &gpioConfig, &meterState, &ds, &ea, &rtp);
|
ws.setup(&config, &gpioConfig, &meterState, &ds, &ea, &rtp, &updater);
|
||||||
|
|
||||||
UiConfig ui;
|
UiConfig ui;
|
||||||
if(config.getUiConfig(ui)) {
|
if(config.getUiConfig(ui)) {
|
||||||
@ -727,6 +735,12 @@ void loop() {
|
|||||||
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
||||||
debugW_P(PSTR("Used %dms to handle language update"), end-start);
|
debugW_P(PSTR("Used %dms to handle language update"), end-start);
|
||||||
}
|
}
|
||||||
|
start = millis();
|
||||||
|
updater.loop();
|
||||||
|
end = millis();
|
||||||
|
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
||||||
|
debugW_P(PSTR("Used %dms to handle firmware updater"), end-start);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
if(now - lastVoltageCheck > 1000) {
|
if(now - lastVoltageCheck > 1000) {
|
||||||
@ -962,7 +976,6 @@ void handleEnergyAccountingChanged() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char ntpServerName[64] = "";
|
char ntpServerName[64] = "";
|
||||||
float maxVcc = 2.9;
|
|
||||||
|
|
||||||
void handleNtpChange() {
|
void handleNtpChange() {
|
||||||
NtpConfig ntp;
|
NtpConfig ntp;
|
||||||
@ -1025,23 +1038,12 @@ void handleSystem(unsigned long now) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool handleVoltageCheck() {
|
bool handleVoltageCheck() {
|
||||||
if(sysConfig.boardType >= 5 && sysConfig.boardType <= 7 && maxVcc > 2.8) { // Pow-*
|
if(!hw.isVoltageOptimal()) {
|
||||||
float vcc = hw.getVcc();
|
if(WiFi.getMode() == WIFI_STA) {
|
||||||
if(vcc > 3.4 || vcc < 2.8) {
|
debugW_P(PSTR("Vcc dropped below limit, disconnecting WiFi for 5 seconds to preserve power"));
|
||||||
maxVcc = 0;
|
ch->disconnect(5000);
|
||||||
} else if(vcc > maxVcc) {
|
|
||||||
debugD_P(PSTR("Setting new max Vcc to %.2f"), vcc);
|
|
||||||
maxVcc = vcc;
|
|
||||||
} else {
|
|
||||||
float diff = min(maxVcc, (float) 3.3)-vcc;
|
|
||||||
if(diff > 0.4) {
|
|
||||||
if(WiFi.getMode() == WIFI_STA) {
|
|
||||||
debugW_P(PSTR("Vcc dropped to %.2f, disconnecting WiFi for 5 seconds to preserve power"), vcc);
|
|
||||||
ch->disconnect(5000);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user