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];
|
||||
int16_t exitCode;
|
||||
int16_t errorCode;
|
||||
}; // 20
|
||||
uint32_t size;
|
||||
uint16_t block_position;
|
||||
uint8_t retry_count;
|
||||
}; // 27
|
||||
|
||||
struct CloudConfig {
|
||||
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 {
|
||||
public:
|
||||
bool applyBoardConfig(uint8_t boardType, GpioConfig& gpioConfig, MeterConfig& meterConfig, uint8_t hanPin);
|
||||
void setup(GpioConfig*);
|
||||
void setup(SystemConfig* sys, GpioConfig* gpio);
|
||||
float getVcc();
|
||||
uint8_t getTempSensorCount();
|
||||
TempSensorData* getTempSensorData(uint8_t);
|
||||
@ -56,15 +56,24 @@ public:
|
||||
bool ledOff(uint8_t color);
|
||||
bool ledBlink(uint8_t color, uint8_t blink);
|
||||
void setBootSuccessful(bool value);
|
||||
bool isVoltageOptimal(float range = 0.4);
|
||||
uint8_t getBoardType();
|
||||
|
||||
HwTools() {};
|
||||
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;
|
||||
AdcConfig voltAdc, tempAdc;
|
||||
#if defined(ESP32)
|
||||
esp_adc_cal_characteristics_t* voltAdcChar, tempAdcChar;
|
||||
#endif
|
||||
GpioConfig* config;
|
||||
bool tempSensorInit;
|
||||
OneWire *oneWire = NULL;
|
||||
DallasTemperature *sensorApi = NULL;
|
||||
|
||||
@ -144,8 +144,8 @@ bool HwTools::applyBoardConfig(uint8_t boardType, GpioConfig& gpioConfig, MeterC
|
||||
return false;
|
||||
}
|
||||
|
||||
void HwTools::setup(GpioConfig* config) {
|
||||
this->config = config;
|
||||
void HwTools::setup(SystemConfig* sys, GpioConfig* config) {
|
||||
this->boardType = sys->boardType;
|
||||
this->tempSensorInit = false;
|
||||
if(sensorApi != NULL)
|
||||
delete sensorApi;
|
||||
@ -153,8 +153,9 @@ void HwTools::setup(GpioConfig* config) {
|
||||
delete oneWire;
|
||||
if(config->tempSensorPin > 0 && config->tempSensorPin < 40) {
|
||||
pinMode(config->tempSensorPin, INPUT);
|
||||
tempPin = config->tempSensorPin;
|
||||
} else {
|
||||
config->tempSensorPin = 0xFF;
|
||||
tempPin = config->tempSensorPin = 0xFF;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
@ -196,47 +197,63 @@ void HwTools::setup(GpioConfig* config) {
|
||||
#else
|
||||
pinMode(config->vccPin, INPUT);
|
||||
#endif
|
||||
vccPin = config->vccPin;
|
||||
vccOffset = config->vccOffset / 100.0;
|
||||
vccMultiplier = config->vccMultiplier / 1000.0;
|
||||
vccGnd_r = config->vccResistorGnd;
|
||||
vccVcc_r = config->vccResistorVcc;
|
||||
} else {
|
||||
voltAdc.unit = 0xFF;
|
||||
voltAdc.channel = 0xFF;
|
||||
config->vccPin = 0xFF;
|
||||
vccPin = config->vccPin = 0xFF;
|
||||
}
|
||||
|
||||
if(config->tempAnalogSensorPin > 0 && config->tempAnalogSensorPin < 40) {
|
||||
pinMode(config->tempAnalogSensorPin, INPUT);
|
||||
atempPin = config->tempAnalogSensorPin;
|
||||
} else {
|
||||
config->tempAnalogSensorPin = 0xFF;
|
||||
atempPin = config->tempAnalogSensorPin = 0xFF;
|
||||
}
|
||||
|
||||
if(config->ledPin > 0 && config->ledPin < 40) {
|
||||
pinMode(config->ledPin, OUTPUT);
|
||||
ledPin = config->ledPin;
|
||||
ledInvert = config->ledInverted;
|
||||
ledOff(LED_INTERNAL);
|
||||
} else {
|
||||
config->ledPin = 0xFF;
|
||||
ledPin = config->ledPin = 0xFF;
|
||||
}
|
||||
|
||||
if(config->ledPinRed > 0 && config->ledPinRed < 40) {
|
||||
pinMode(config->ledPinRed, OUTPUT);
|
||||
redPin = config->ledPinRed;
|
||||
ledOff(LED_RED);
|
||||
} else {
|
||||
config->ledPinRed = 0xFF;
|
||||
redPin = config->ledPinRed = 0xFF;
|
||||
}
|
||||
|
||||
if(config->ledPinGreen > 0 && config->ledPinGreen < 40) {
|
||||
pinMode(config->ledPinGreen, OUTPUT);
|
||||
greenPin = config->ledPinGreen;
|
||||
ledOff(LED_GREEN);
|
||||
} else {
|
||||
config->ledPinGreen = 0xFF;
|
||||
greenPin = config->ledPinGreen = 0xFF;
|
||||
}
|
||||
|
||||
if(config->ledPinBlue > 0 && config->ledPinBlue < 40) {
|
||||
pinMode(config->ledPinBlue, OUTPUT);
|
||||
bluePin = config->ledPinBlue;
|
||||
ledOff(LED_BLUE);
|
||||
} else {
|
||||
config->ledPinBlue = 0xFF;
|
||||
bluePin = config->ledPinBlue = 0xFF;
|
||||
}
|
||||
|
||||
rgbInvert = config->ledRgbInverted;
|
||||
|
||||
if(config->ledDisablePin > 0 && config->ledDisablePin < 40) {
|
||||
pinMode(config->ledDisablePin, OUTPUT_OPEN_DRAIN);
|
||||
ledDisablePin = config->ledDisablePin;
|
||||
ledBehaviour = config->ledBehaviour;
|
||||
setBootSuccessful(false);
|
||||
}
|
||||
}
|
||||
@ -362,7 +379,7 @@ void HwTools::getAdcChannel(uint8_t pin, AdcConfig& config) {
|
||||
|
||||
float HwTools::getVcc() {
|
||||
float volts = 0.0;
|
||||
if(config->vccPin != 0xFF) {
|
||||
if(vccPin != 0xFF) {
|
||||
#if defined(ESP32)
|
||||
if(voltAdc.unit != 0xFF) {
|
||||
uint32_t x = 0;
|
||||
@ -385,7 +402,7 @@ float HwTools::getVcc() {
|
||||
} else {
|
||||
uint32_t x = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
x += analogRead(config->vccPin);
|
||||
x += analogRead(vccPin);
|
||||
}
|
||||
volts = (x * 3.3) / 10.0 / analogRange;
|
||||
}
|
||||
@ -403,13 +420,10 @@ float HwTools::getVcc() {
|
||||
}
|
||||
if(volts == 0.0) return 0.0;
|
||||
|
||||
if(config->vccResistorGnd > 0 && config->vccResistorVcc > 0) {
|
||||
volts *= ((float) (config->vccResistorGnd + config->vccResistorVcc) / config->vccResistorGnd);
|
||||
if(vccGnd_r > 0 && vccVcc_r > 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -425,9 +439,9 @@ TempSensorData* HwTools::getTempSensorData(uint8_t i) {
|
||||
}
|
||||
|
||||
bool HwTools::updateTemperatures() {
|
||||
if(config->tempSensorPin != 0xFF) {
|
||||
if(tempPin != 0xFF) {
|
||||
if(!tempSensorInit) {
|
||||
oneWire = new OneWire(config->tempSensorPin);
|
||||
oneWire = new OneWire(tempPin);
|
||||
sensorApi = new DallasTemperature(this->oneWire);
|
||||
sensorApi->begin();
|
||||
delay(100);
|
||||
@ -513,9 +527,9 @@ float HwTools::getTemperature() {
|
||||
return c == 0 ? DEVICE_DISCONNECTED_C : ret/c;
|
||||
}
|
||||
float HwTools::getTemperatureAnalog() {
|
||||
if(config->tempAnalogSensorPin != 0xFF) {
|
||||
if(atempPin != 0xFF) {
|
||||
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 DEVICE_DISCONNECTED_C;
|
||||
@ -529,42 +543,42 @@ int HwTools::getWifiRssi() {
|
||||
void HwTools::setBootSuccessful(bool value) {
|
||||
if(bootSuccessful && value) return;
|
||||
bootSuccessful = value;
|
||||
if(config->ledDisablePin > 0 && config->ledDisablePin < 40) {
|
||||
switch(config->ledBehaviour) {
|
||||
if(ledDisablePin > 0 && ledDisablePin < 40) {
|
||||
switch(ledBehaviour) {
|
||||
case LED_BEHAVIOUR_ERROR_ONLY:
|
||||
case LED_BEHAVIOUR_OFF:
|
||||
digitalWrite(config->ledDisablePin, LOW);
|
||||
digitalWrite(ledDisablePin, LOW);
|
||||
break;
|
||||
case LED_BEHAVIOUR_BOOT:
|
||||
if(bootSuccessful) {
|
||||
digitalWrite(config->ledDisablePin, LOW);
|
||||
digitalWrite(ledDisablePin, LOW);
|
||||
} else {
|
||||
digitalWrite(config->ledDisablePin, HIGH);
|
||||
digitalWrite(ledDisablePin, HIGH);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
digitalWrite(config->ledDisablePin, HIGH);
|
||||
digitalWrite(ledDisablePin, HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HwTools::ledOn(uint8_t color) {
|
||||
if(config->ledBehaviour == LED_BEHAVIOUR_OFF) return false;
|
||||
if(config->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_OFF) return false;
|
||||
if(ledBehaviour == LED_BEHAVIOUR_ERROR_ONLY && color != LED_RED) return false;
|
||||
if(ledBehaviour == LED_BEHAVIOUR_BOOT && color != LED_RED && bootSuccessful) return false;
|
||||
|
||||
if(color == LED_INTERNAL) {
|
||||
return writeLedPin(color, config->ledInverted ? LOW : HIGH);
|
||||
return writeLedPin(color, ledInvert ? LOW : HIGH);
|
||||
} else {
|
||||
return writeLedPin(color, config->ledRgbInverted ? LOW : HIGH);
|
||||
return writeLedPin(color, rgbInvert ? LOW : HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
bool HwTools::ledOff(uint8_t color) {
|
||||
if(color == LED_INTERNAL) {
|
||||
return writeLedPin(color, config->ledInverted ? HIGH : LOW);
|
||||
return writeLedPin(color, ledInvert ? HIGH : LOW);
|
||||
} 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) {
|
||||
switch(color) {
|
||||
case LED_INTERNAL: {
|
||||
if(config->ledPin != 0xFF) {
|
||||
digitalWrite(config->ledPin, state);
|
||||
if(ledPin != 0xFF) {
|
||||
digitalWrite(ledPin, state);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -590,8 +604,8 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
||||
break;
|
||||
}
|
||||
case LED_RED: {
|
||||
if(config->ledPinRed != 0xFF) {
|
||||
digitalWrite(config->ledPinRed, state);
|
||||
if(redPin != 0xFF) {
|
||||
digitalWrite(redPin, state);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -599,8 +613,8 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
||||
break;
|
||||
}
|
||||
case LED_GREEN: {
|
||||
if(config->ledPinGreen != 0xFF) {
|
||||
digitalWrite(config->ledPinGreen, state);
|
||||
if(greenPin != 0xFF) {
|
||||
digitalWrite(greenPin, state);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -608,8 +622,8 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
||||
break;
|
||||
}
|
||||
case LED_BLUE: {
|
||||
if(config->ledPinBlue != 0xFF) {
|
||||
digitalWrite(config->ledPinBlue, state);
|
||||
if(bluePin != 0xFF) {
|
||||
digitalWrite(bluePin, state);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -617,9 +631,9 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
||||
break;
|
||||
}
|
||||
case LED_YELLOW: {
|
||||
if(config->ledPinRed != 0xFF && config->ledPinGreen != 0xFF) {
|
||||
digitalWrite(config->ledPinRed, state);
|
||||
digitalWrite(config->ledPinGreen, state);
|
||||
if(redPin != 0xFF && greenPin != 0xFF) {
|
||||
digitalWrite(redPin, state);
|
||||
digitalWrite(greenPin, state);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -629,3 +643,22 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
||||
}
|
||||
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>
|
||||
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 Favicon from './assets/favicon.svg'; // Need this for the build
|
||||
import Header from './lib/Header.svelte';
|
||||
@ -44,6 +44,8 @@
|
||||
translations = update;
|
||||
});
|
||||
|
||||
let sito;
|
||||
let data = {};
|
||||
let sysinfo = {};
|
||||
sysinfoStore.subscribe(update => {
|
||||
sysinfo = update;
|
||||
@ -70,9 +72,11 @@
|
||||
if(sysinfo.ui.lang && sysinfo.ui.lang != translations?.language?.code) {
|
||||
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 => {
|
||||
data = update;
|
||||
updateRealtime(update);
|
||||
@ -126,9 +130,7 @@
|
||||
</Route>
|
||||
</Router>
|
||||
|
||||
{#if sysinfo.upgrading}
|
||||
<Mask active=true message="Device is upgrading, please wait"/>
|
||||
{:else if sysinfo.booting}
|
||||
{#if sysinfo.booting}
|
||||
{#if sysinfo.trying}
|
||||
<Mask active=true message="Device is booting, please wait. Trying to reach it on {sysinfo.trying}"/>
|
||||
{:else}
|
||||
|
||||
@ -46,6 +46,7 @@ function updateSysinfo(url) {
|
||||
let tries = 0;
|
||||
let lastTemp = -127;
|
||||
let lastPrice = null;
|
||||
let lastUp = 0;
|
||||
let data = {};
|
||||
export const dataStore = readable(data, (set) => {
|
||||
let timeout;
|
||||
@ -63,7 +64,7 @@ export const dataStore = readable(data, (set) => {
|
||||
lastPrice = data.p;
|
||||
getPrices();
|
||||
}
|
||||
if(sysinfo.upgrading) {
|
||||
if(sysinfo.upgrading && lastUp > data.u) {
|
||||
window.location.reload();
|
||||
} else if(!sysinfo || !sysinfo.chip || sysinfo.booting || (tries > 1 && !isBusPowered(sysinfo.board))) {
|
||||
getSysinfo();
|
||||
@ -72,6 +73,7 @@ export const dataStore = readable(data, (set) => {
|
||||
if(monthPlotTimeout) clearTimeout(monthPlotTimeout);
|
||||
monthPlotTimeout = setTimeout(getMonthPlot, 3000);
|
||||
}
|
||||
lastUp = data.u;
|
||||
if(!dayPlotTimeout) dayPlotTimeout = getDayPlot();
|
||||
if(!monthPlotTimeout) monthPlotTimeout = getMonthPlot();
|
||||
|
||||
@ -212,12 +214,3 @@ export async function getTariff() {
|
||||
export const tariffStore = writable(tariff, (set) => {
|
||||
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>
|
||||
import { Link } from "svelte-navigator";
|
||||
import { sysinfoStore, getGitHubReleases, gitHubReleaseStore } from './DataStores.js';
|
||||
import { upgrade, getNextVersion, upgradeWarningText } from './UpgradeHelper';
|
||||
import { sysinfoStore } from './DataStores.js';
|
||||
import { upgrade, upgradeWarningText } from './UpgradeHelper';
|
||||
import { boardtype, isBusPowered, wiki, bcol } from './Helpers.js';
|
||||
import { translationsStore } from "./TranslationService.js";
|
||||
import FavIco from './../assets/favicon.svg';
|
||||
@ -16,34 +16,30 @@
|
||||
export let data = {};
|
||||
let sysinfo = {};
|
||||
|
||||
let nextVersion = {};
|
||||
|
||||
function askUpgrade() {
|
||||
if(confirm((translations.header?.upgrade ?? "Upgrade to {0}?").replace('{0}',nextVersion.tag_name))) {
|
||||
if(!isBusPowered(sysinfo.board) || confirm(upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board)))) {
|
||||
sysinfoStore.update(s => {
|
||||
s.upgrading = true;
|
||||
return s;
|
||||
});
|
||||
upgrade(nextVersion.tag_name);
|
||||
}
|
||||
if(confirm((translations.header?.upgrade ?? "Upgrade to {0}?").replace('{0}',sysinfo.upgrade.n))) {
|
||||
upgrade(sysinfo.upgrade.n);
|
||||
sysinfoStore.update(s => {
|
||||
s.upgrade.t = sysinfo.upgrade.n;
|
||||
s.upgrading = true;
|
||||
return s;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let progress;
|
||||
sysinfoStore.subscribe(update => {
|
||||
sysinfo = update;
|
||||
if(update.fwconsent === 1) {
|
||||
getGitHubReleases();
|
||||
}
|
||||
});
|
||||
|
||||
gitHubReleaseStore.subscribe(releases => {
|
||||
nextVersion = getNextVersion(sysinfo.version, releases);
|
||||
});
|
||||
|
||||
let translations = {};
|
||||
translationsStore.subscribe(update => {
|
||||
translations = update;
|
||||
});
|
||||
|
||||
$: {
|
||||
progress = Math.max(0, sysinfo.upgrade.p);
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav class="hdr">
|
||||
@ -91,12 +87,14 @@
|
||||
<div class="flex-none px-1 mt-1" title={translations.header?.doc ?? ""}>
|
||||
<a href={wiki('')} target='_blank' rel="noreferrer"><HelpIcon/></a>
|
||||
</div>
|
||||
{#if sysinfo.fwconsent === 1 && nextVersion}
|
||||
<div class="flex-none mr-3 text-yellow-500" title={(translations.header?.new_version ?? "New version") + ': ' + nextVersion.tag_name}>
|
||||
{#if sysinfo.upgrading}
|
||||
<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}
|
||||
<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}
|
||||
<span>{translations.header?.new_version ?? "New version"}: {nextVersion.tag_name}</span>
|
||||
<span>{translations.header?.new_version ?? "New version"}: {sysinfo.upgrade.n}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { metertype, boardtype, isBusPowered, getBaseChip } from './Helpers.js';
|
||||
import { getSysinfo, gitHubReleaseStore, sysinfoStore } from './DataStores.js';
|
||||
import { upgrade, getNextVersion, upgradeWarningText } from './UpgradeHelper';
|
||||
import { getSysinfo, sysinfoStore } from './DataStores.js';
|
||||
import { upgrade, upgradeWarningText } from './UpgradeHelper';
|
||||
import { translationsStore } from './TranslationService.js';
|
||||
import { Link } from 'svelte-navigator';
|
||||
import Clock from './Clock.svelte';
|
||||
@ -41,24 +41,15 @@
|
||||
translationsStore.subscribe(update => {
|
||||
translations = update;
|
||||
});
|
||||
|
||||
let nextVersion = {};
|
||||
gitHubReleaseStore.subscribe(releases => {
|
||||
nextVersion = getNextVersion(sysinfo.version, releases);
|
||||
if(!nextVersion) {
|
||||
nextVersion = releases[0];
|
||||
}
|
||||
});
|
||||
|
||||
function askUpgrade() {
|
||||
if(confirm((translations.header?.upgrade ?? "Upgrade to {0}?").replace('{0}',nextVersion.tag_name))) {
|
||||
if((sysinfo.board != 2 && sysinfo.board != 4 && sysinfo.board != 7) || confirm(upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board)))) {
|
||||
sysinfoStore.update(s => {
|
||||
s.upgrading = true;
|
||||
return s;
|
||||
});
|
||||
upgrade(nextVersion.tag_name);
|
||||
}
|
||||
if(confirm((translations.header?.upgrade ?? "Upgrade to {0}?").replace('{0}',sysinfo.upgrade.n))) {
|
||||
upgrade(sysinfo.upgrade.n);
|
||||
sysinfoStore.update(s => {
|
||||
s.upgrade.t = sysinfo.upgrade.n;
|
||||
s.upgrading = true;
|
||||
return s;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +147,7 @@
|
||||
<div class="my-2">
|
||||
{translations.status?.device?.last_boot ?? "Last boot"}:
|
||||
{#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}
|
||||
-
|
||||
{/if}
|
||||
@ -228,11 +219,11 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if nextVersion}
|
||||
{#if sysinfo.upgrade.n}
|
||||
<div class="my-2 flex">
|
||||
{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>
|
||||
{#if (sysinfo.security == 0 || data.a) && sysinfo.fwconsent === 1 && nextVersion && nextVersion.tag_name != sysinfo.version}
|
||||
<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 && sysinfo.upgrade.n && sysinfo.upgrade.n != sysinfo.version}
|
||||
<div class="flex-none ml-2 text-green-500" title={translations.status?.firmware?.install ?? "Install"}>
|
||||
<button on:click={askUpgrade}>⇓</button>
|
||||
</div>
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<option value={-1}>disabled</option>
|
||||
{#if chip == 'esp8266'}
|
||||
<option value={3}>UART0</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, {
|
||||
method: 'POST'
|
||||
});
|
||||
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];
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
@ -17,25 +17,25 @@ export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
server: {
|
||||
proxy: {
|
||||
"/data.json": "http://192.168.233.49",
|
||||
"/energyprice.json": "http://192.168.233.49",
|
||||
"/dayplot.json": "http://192.168.233.49",
|
||||
"/monthplot.json": "http://192.168.233.49",
|
||||
"/temperature.json": "http://192.168.233.49",
|
||||
"/sysinfo.json": "http://192.168.233.49",
|
||||
"/configuration.json": "http://192.168.233.49",
|
||||
"/tariff.json": "http://192.168.233.49",
|
||||
"/realtime.json": "http://192.168.233.49",
|
||||
"/priceconfig.json": "http://192.168.233.49",
|
||||
"/cloudkey.json": "http://192.168.233.49",
|
||||
"/save": "http://192.168.233.49",
|
||||
"/reboot": "http://192.168.233.49",
|
||||
"/configfile": "http://192.168.233.49",
|
||||
"/upgrade": "http://192.168.233.49",
|
||||
"/mqtt-ca": "http://192.168.233.49",
|
||||
"/mqtt-cert": "http://192.168.233.49",
|
||||
"/mqtt-key": "http://192.168.233.49",
|
||||
"/logo.svg": "http://192.168.233.49",
|
||||
"/data.json": "http://192.168.233.154",
|
||||
"/energyprice.json": "http://192.168.233.154",
|
||||
"/dayplot.json": "http://192.168.233.154",
|
||||
"/monthplot.json": "http://192.168.233.154",
|
||||
"/temperature.json": "http://192.168.233.154",
|
||||
"/sysinfo.json": "http://192.168.233.154",
|
||||
"/configuration.json": "http://192.168.233.154",
|
||||
"/tariff.json": "http://192.168.233.154",
|
||||
"/realtime.json": "http://192.168.233.154",
|
||||
"/priceconfig.json": "http://192.168.233.154",
|
||||
"/cloudkey.json": "http://192.168.233.154",
|
||||
"/save": "http://192.168.233.154",
|
||||
"/reboot": "http://192.168.233.154",
|
||||
"/configfile": "http://192.168.233.154",
|
||||
"/upgrade": "http://192.168.233.154",
|
||||
"/mqtt-ca": "http://192.168.233.154",
|
||||
"/mqtt-cert": "http://192.168.233.154",
|
||||
"/mqtt-key": "http://192.168.233.154",
|
||||
"/logo.svg": "http://192.168.233.154",
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include "AmsData.h"
|
||||
#include "AmsStorage.h"
|
||||
#include "AmsDataStorage.h"
|
||||
#include "AmsFirmwareUpdater.h"
|
||||
#include "EnergyAccounting.h"
|
||||
#include "Uptime.h"
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
@ -53,7 +54,7 @@ public:
|
||||
#else
|
||||
AmsWebServer(uint8_t* buf, Stream* Debug, HwTools* hw, ResetDataContainer* rdc);
|
||||
#endif
|
||||
void setup(AmsConfiguration*, GpioConfig*, AmsData*, AmsDataStorage*, EnergyAccounting*, RealtimePlot*);
|
||||
void setup(AmsConfiguration*, GpioConfig*, AmsData*, AmsDataStorage*, EnergyAccounting*, RealtimePlot*, AmsFirmwareUpdater*);
|
||||
void loop();
|
||||
#if defined(_CLOUDCONNECTOR_H)
|
||||
void setCloud(CloudConnector* cloud);
|
||||
@ -88,6 +89,7 @@ private:
|
||||
AmsDataStorage* ds;
|
||||
EnergyAccounting* ea = NULL;
|
||||
RealtimePlot* rtp = NULL;
|
||||
AmsFirmwareUpdater* updater = NULL;
|
||||
AmsMqttHandler* mqttHandler = NULL;
|
||||
ConnectionHandler* ch = NULL;
|
||||
#if defined(_CLOUDCONNECTOR_H)
|
||||
@ -139,7 +141,6 @@ private:
|
||||
void handleSave();
|
||||
void reboot();
|
||||
void upgrade();
|
||||
void upgradeFromUrl(String url, String nextVersion);
|
||||
void firmwareHtml();
|
||||
void firmwarePost();
|
||||
void firmwareUpload();
|
||||
@ -167,7 +168,6 @@ private:
|
||||
void robotstxt();
|
||||
void ssdpSchema();
|
||||
|
||||
void updaterRequestCallback(HTTPClient*);
|
||||
void addConditionalCloudHeaders();
|
||||
void optionsGet();
|
||||
};
|
||||
|
||||
@ -56,7 +56,9 @@
|
||||
"x": %d,
|
||||
"e": %d,
|
||||
"f": "%s",
|
||||
"t": "%s"
|
||||
"t": "%s",
|
||||
"n": "%s",
|
||||
"p": %.1f
|
||||
},
|
||||
"last_month": {
|
||||
"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->gpioConfig = gpioConfig;
|
||||
this->meterState = meterState;
|
||||
this->ds = ds;
|
||||
this->ea = ea;
|
||||
this->rtp = rtp;
|
||||
this->updater = updater;
|
||||
|
||||
String context;
|
||||
config->getWebConfig(webConfig);
|
||||
@ -371,7 +372,7 @@ void AmsWebServer::sysinfoJson() {
|
||||
config->getUiConfig(ui);
|
||||
|
||||
UpgradeInformation upinfo;
|
||||
config->getUpgradeInformation(upinfo);
|
||||
updater->getUpgradeInformation(upinfo);
|
||||
|
||||
String meterModel = meterState->getMeterModel();
|
||||
if(!meterModel.isEmpty())
|
||||
@ -417,7 +418,7 @@ void AmsWebServer::sysinfoJson() {
|
||||
sys.dataCollectionConsent,
|
||||
hostname.c_str(),
|
||||
performRestart ? "true" : "false",
|
||||
rebootForUpgrade ? "true" : "false",
|
||||
updater->getProgress() > 0.0 ? "true" : "false",
|
||||
#if defined(ESP8266)
|
||||
localIp.isSet() ? localIp.toString().c_str() : "",
|
||||
subnet.isSet() ? subnet.toString().c_str() : "",
|
||||
@ -470,6 +471,8 @@ void AmsWebServer::sysinfoJson() {
|
||||
upinfo.errorCode,
|
||||
upinfo.fromVersion,
|
||||
upinfo.toVersion,
|
||||
updater->getNextVersion(),
|
||||
updater->getProgress(),
|
||||
ea->getUseLastMonth(),
|
||||
ea->getCostLastMonth(),
|
||||
ea->getProducedLastMonth(),
|
||||
@ -1173,6 +1176,9 @@ void AmsWebServer::handleSave() {
|
||||
if(!checkSecurity(1))
|
||||
return;
|
||||
|
||||
SystemConfig sys;
|
||||
config->getSystemConfig(sys);
|
||||
|
||||
bool success = true;
|
||||
if(server.hasArg(F("v")) && server.arg(F("v")) == F("true")) {
|
||||
int boardType = server.arg(F("vb")).toInt();
|
||||
@ -1189,8 +1195,6 @@ void AmsWebServer::handleSave() {
|
||||
config->setGpioConfig(*gpioConfig);
|
||||
config->setMeterConfig(meterConfig);
|
||||
|
||||
SystemConfig sys;
|
||||
config->getSystemConfig(sys);
|
||||
sys.boardType = success ? boardType : 0xFF;
|
||||
sys.vendorConfigured = success;
|
||||
config->setSystemConfig(sys);
|
||||
@ -1198,8 +1202,6 @@ void AmsWebServer::handleSave() {
|
||||
}
|
||||
|
||||
if(server.hasArg(F("s")) && server.arg(F("s")) == F("true")) {
|
||||
SystemConfig sys;
|
||||
config->getSystemConfig(sys);
|
||||
MeterConfig meterConfig;
|
||||
config->getMeterConfig(meterConfig);
|
||||
|
||||
@ -1267,8 +1269,6 @@ void AmsWebServer::handleSave() {
|
||||
performRestart = true;
|
||||
}
|
||||
} 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;
|
||||
config->setSystemConfig(sys);
|
||||
}
|
||||
@ -1564,8 +1564,6 @@ void AmsWebServer::handleSave() {
|
||||
}
|
||||
|
||||
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;
|
||||
config->setSystemConfig(sys);
|
||||
|
||||
@ -1651,7 +1649,7 @@ debugger->printf_P(PSTR("Successfully saved.\n"));
|
||||
if(config->isNetworkConfigChanged() || performRestart) {
|
||||
performRestart = true;
|
||||
} else {
|
||||
hw->setup(gpioConfig);
|
||||
hw->setup(&sys, gpioConfig);
|
||||
}
|
||||
} else {
|
||||
success = false;
|
||||
@ -1726,85 +1724,8 @@ void AmsWebServer::upgrade() {
|
||||
server.handleClient();
|
||||
delay(250);
|
||||
|
||||
if(server.hasArg(F("url"))) {
|
||||
customFirmwareUrl = server.arg(F("url"));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
String version = server.arg(F("expected_version"));
|
||||
updater->setTargetVersion(version.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1828,14 +1749,6 @@ void AmsWebServer::firmwarePost() {
|
||||
server.send(200);
|
||||
} else {
|
||||
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.send(303);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
extra_configs = platformio-user.ini
|
||||
|
||||
[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
|
||||
extra_scripts =
|
||||
pre:scripts/addversion.py
|
||||
|
||||
@ -118,6 +118,8 @@ HardwareSerial Debug = Serial;
|
||||
|
||||
#include "Timezones.h"
|
||||
|
||||
#include "AmsFirmwareUpdater.h"
|
||||
|
||||
uint8_t commonBuffer[BUF_SIZE_COMMON];
|
||||
|
||||
HwTools hw;
|
||||
@ -178,6 +180,8 @@ bool ntpEnabled = false;
|
||||
|
||||
bool mdnsEnabled = false;
|
||||
|
||||
AmsFirmwareUpdater updater(&Debug, &hw, &meterState);
|
||||
|
||||
AmsDataStorage ds(&Debug);
|
||||
#if defined(_CLOUDCONNECTOR_H)
|
||||
CloudConnector *cloud = NULL;
|
||||
@ -317,7 +321,7 @@ void setup() {
|
||||
}
|
||||
|
||||
delay(1);
|
||||
hw.setup(&gpioConfig);
|
||||
hw.setup(&sysConfig, &gpioConfig);
|
||||
|
||||
if(gpioConfig.apPin >= 0) {
|
||||
pinMode(gpioConfig.apPin, INPUT_PULLUP);
|
||||
@ -411,6 +415,10 @@ void setup() {
|
||||
|
||||
debugI_P(PSTR("AMS bridge started"));
|
||||
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
|
||||
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.load();
|
||||
ea.setPriceService(ps);
|
||||
ws.setup(&config, &gpioConfig, &meterState, &ds, &ea, &rtp);
|
||||
ws.setup(&config, &gpioConfig, &meterState, &ds, &ea, &rtp, &updater);
|
||||
|
||||
UiConfig ui;
|
||||
if(config.getUiConfig(ui)) {
|
||||
@ -727,6 +735,12 @@ void loop() {
|
||||
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
||||
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(now - lastVoltageCheck > 1000) {
|
||||
@ -962,7 +976,6 @@ void handleEnergyAccountingChanged() {
|
||||
}
|
||||
|
||||
char ntpServerName[64] = "";
|
||||
float maxVcc = 2.9;
|
||||
|
||||
void handleNtpChange() {
|
||||
NtpConfig ntp;
|
||||
@ -1025,23 +1038,12 @@ void handleSystem(unsigned long now) {
|
||||
}
|
||||
|
||||
bool handleVoltageCheck() {
|
||||
if(sysConfig.boardType >= 5 && sysConfig.boardType <= 7 && maxVcc > 2.8) { // Pow-*
|
||||
float vcc = hw.getVcc();
|
||||
if(vcc > 3.4 || vcc < 2.8) {
|
||||
maxVcc = 0;
|
||||
} 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;
|
||||
}
|
||||
if(!hw.isVoltageOptimal()) {
|
||||
if(WiFi.getMode() == WIFI_STA) {
|
||||
debugW_P(PSTR("Vcc dropped below limit, disconnecting WiFi for 5 seconds to preserve power"));
|
||||
ch->disconnect(5000);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user