various fixes

- added last updated time of a tag in tagdb
- day ahead prices choice of whole money units or cents
- optimization of file upload during updates
- show mac address in wifi setup
- fixed reload of display after unexpected reboot of tag
This commit is contained in:
Nic Limper
2024-03-21 20:11:12 +01:00
parent 98d9dc61f3
commit 587d7c86c0
14 changed files with 119 additions and 33 deletions

View File

@@ -33,8 +33,8 @@
"4": {
"location": [ 5, 5, "fonts/bahnschrift30" ],
"wind": [ 280, 5, "fonts/bahnschrift30" ],
"temp": [ 5, 65, "fonts/bahnschrift70" ],
"icon": [ 285, 20, 70, 2 ],
"temp": [ 5, 65, "fonts/calibrib80" ],
"icon": [ 275, 20, 90, 2 ],
"dir": [ 245, -12, 40 ],
"umbrella": [ 190, -50, 25 ]
},

Binary file not shown.

View File

@@ -15,7 +15,7 @@
#define NO_SUBGHZ_CHANNEL 255
class tagRecord {
public:
tagRecord() : mac{0}, version(0), alias(""), lastseen(0), nextupdate(0), contentMode(0), pendingCount(0), md5{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0), updateCount(0) {}
tagRecord() : mac{0}, version(0), alias(""), lastseen(0), nextupdate(0), contentMode(0), pendingCount(0), md5{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0), updateCount(0), updateLast(0) {}
uint8_t mac[8];
uint8_t version;
@@ -45,6 +45,7 @@ class tagRecord {
uint8_t currentChannel;
uint8_t invert;
uint32_t updateCount;
uint32_t updateLast;
uint8_t dataType;
String filename;

View File

@@ -235,9 +235,10 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
imageParams.lut = EPD_LUT_OTA;
}
int32_t interval = 60 * 60;
interval = cfgobj["interval"].as<int>() * 60;
if (interval < 0) {
int32_t interval = cfgobj["interval"].as<int>() * 60;
if (interval == -1440 * 60) {
interval = util::getMidnightTime() - now;
} else if (interval < 0) {
interval = -interval;
unsigned int secondsUntilNext = (interval - (now % interval)) % interval;
interval = secondsUntilNext;
@@ -341,8 +342,8 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
case 8: // Forecast
drawForecast(filename, cfgobj, taginfo, imageParams);
taginfo->nextupdate = now + 3600;
updateTagImage(filename, mac, 15, taginfo, imageParams);
taginfo->nextupdate = now + interval;
updateTagImage(filename, mac, interval / 60, taginfo, imageParams);
break;
case 5: // Firmware
@@ -1513,15 +1514,17 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
int n = doc.size();
int units = cfgobj["units"].as<int>();
if (units == 0) units = 1;
double tarifkwh = cfgobj["tariffkwh"].as<double>();
double tariftax = cfgobj["tarifftax"].as<double>();
double minPrice = (doc[0]["price"].as<double>() / 10 + tarifkwh) * (1 + tariftax / 100);
double minPrice = (doc[0]["price"].as<double>() / 10 + tarifkwh) * (1 + tariftax / 100) / units;
double maxPrice = minPrice;
double prices[n];
for (int i = 0; i < n; i++) {
const JsonObject &obj = doc[i];
const double price = (obj["price"].as<double>() / 10 + tarifkwh) * (1 + tariftax / 100);
const double price = (obj["price"].as<double>() / 10 + tarifkwh) * (1 + tariftax / 100) / units;
minPrice = min(minPrice, price);
maxPrice = max(maxPrice, price);
prices[i] = price;
@@ -1537,7 +1540,7 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
for (double i = minPrice; i <= maxPrice; i += yAxisScale.step) {
int y = mapDouble(i, minPrice, maxPrice, spr.height() - barBottom, spr.height() - barBottom - loc["bars"][2].as<int>());
spr.drawLine(0, y, spr.width(), y, TFT_BLACK);
drawString(spr, String(int(i)), yAxisX, y - 8, loc["yaxis"][0], TL_DATUM, TFT_BLACK);
drawString(spr, String(int(i * units)), yAxisX, y - 8, loc["yaxis"][0], TL_DATUM, TFT_BLACK);
}
uint16_t barwidth = loc["bars"][1].as<int>() / n;
@@ -1552,7 +1555,7 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
struct tm item_timeinfo;
localtime_r(&item_time, &item_timeinfo);
const double price = (obj["price"].as<double>() / 10 + tarifkwh) * (1 + tariftax / 100);
const double price = (obj["price"].as<double>() / 10 + tarifkwh) * (1 + tariftax / 100) / units;
uint16_t barcolor = getPercentileColor(prices, n, price);
uint16_t thisbarh = mapDouble(price, minPrice, maxPrice, 0, loc["bars"][2].as<int>());

View File

@@ -461,6 +461,7 @@ void processXferComplete(struct espXferComplete* xfc, bool local) {
clearPending(taginfo);
memcpy(taginfo->md5, md5bytes, sizeof(md5bytes));
taginfo->updateCount++;
taginfo->updateLast = now;
taginfo->pendingCount = countQueueItem(xfc->src);
taginfo->wakeupReason = 0;
if (taginfo->contentMode == 12 && local == false) {
@@ -568,7 +569,10 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP
if (eadr->adr.lastPacketRSSI != 0) {
if (eadr->adr.wakeupReason >= 0xE0) {
if (taginfo->pendingCount == 0) taginfo->nextupdate = 0;
if (taginfo->pendingCount == 0) {
taginfo->nextupdate = 0;
memset(taginfo->md5, 0, sizeof(taginfo->md5));
}
if (local) {
const char* reason = "";
@@ -643,6 +647,7 @@ void updateContent(const uint8_t* dst) {
tagRecord* taginfo = tagRecord::findByMAC(dst);
if (taginfo != nullptr) {
clearPending(taginfo);
memset(taginfo->md5, 0, sizeof(taginfo->md5));
taginfo->nextupdate = 0;
wsSendTaginfo(taginfo->mac, SYNC_TAGSTATUS);
}

View File

@@ -93,34 +93,80 @@ void handleCheckFile(AsyncWebServerRequest* request) {
request->send(200, "application/json", jsonResponse);
}
#define UPLOAD_BUFFER_SIZE 32768
struct UploadInfo {
String filename;
uint8_t buffer[UPLOAD_BUFFER_SIZE];
size_t bufferSize;
};
void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) {
String uploadfilename;
bool error = false;
if (!index) {
String path;
if (!request->hasParam("path", true)) {
path = "/temp/null.bin";
final = true;
error = true;
} else {
path = request->getParam("path", true)->value();
Serial.println("update " + path);
xSemaphoreTake(fsMutex, portMAX_DELAY);
request->_tempFile = contentFS->open(path, "w", true);
uploadfilename = request->getParam("path", true)->value();
Serial.println("update " + uploadfilename);
File file = contentFS->open(uploadfilename, "w");
file.close();
UploadInfo* uploadInfo = new UploadInfo{uploadfilename, {}, 0};
request->_tempObject = (void*)uploadInfo;
}
}
if (len) {
if (!request->_tempFile.write(data, len)) {
error = true;
final = true;
UploadInfo* uploadInfo = static_cast<UploadInfo*>(request->_tempObject);
if (uploadInfo != nullptr) {
uploadfilename = uploadInfo->filename;
if (len) {
if (uploadInfo->bufferSize + len <= UPLOAD_BUFFER_SIZE) {
memcpy(&uploadInfo->buffer[uploadInfo->bufferSize], data, len);
uploadInfo->bufferSize += len;
} else {
xSemaphoreTake(fsMutex, portMAX_DELAY);
File file = contentFS->open(uploadfilename, "a");
if (file) {
file.write(uploadInfo->buffer, uploadInfo->bufferSize);
file.close();
uploadInfo->bufferSize = 0;
} else {
logLine("Failed to open file for appending: " + uploadfilename);
final = true;
error = true;
}
xSemaphoreGive(fsMutex);
memcpy(uploadInfo->buffer, data, len);
uploadInfo->bufferSize = len;
}
}
}
if (final) {
request->_tempFile.close();
xSemaphoreGive(fsMutex);
if (error) {
request->send(507, "text/plain", "Error. Disk full?");
} else {
request->send(200, "text/plain", "Ok, file written");
if (final) {
if (uploadInfo->bufferSize > 0) {
xSemaphoreTake(fsMutex, portMAX_DELAY);
File file = contentFS->open(uploadfilename, "a");
if (file) {
file.write(uploadInfo->buffer, uploadInfo->bufferSize);
file.close();
} else {
logLine("Failed to open file for appending: " + uploadfilename);
error = true;
}
xSemaphoreGive(fsMutex);
request->_tempObject = nullptr;
delete uploadInfo;
}
if (error) {
request->send(507, "text/plain", "Error. Disk full?");
} else {
request->send(200, "text/plain", "Ok, file written");
}
}
}
}

View File

@@ -120,6 +120,7 @@ void fillNode(JsonObject& tag, const tagRecord* taginfo) {
tag["lut"] = taginfo->lut;
tag["invert"] = taginfo->invert;
tag["updatecount"] = taginfo->updateCount;
tag["updatelast"] = taginfo->updateLast;
tag["ch"] = taginfo->currentChannel;
tag["ver"] = taginfo->tagSoftwareVersion;
}
@@ -228,6 +229,7 @@ bool loadDB(const String& filename) {
taginfo->lut = tag["lut"] | 0;
taginfo->invert = tag["invert"] | 0;
taginfo->updateCount = tag["updatecount"] | 0;
taginfo->updateLast = tag["updatelast"] | 0;
taginfo->currentChannel = tag["ch"] | 0;
taginfo->tagSoftwareVersion = tag["ver"] | 0;
}

View File

@@ -651,6 +651,7 @@ void init_web() {
for (size_t i = 0; i < numKeys; i++) {
doc[keys[i]] = preferences.getString(keys[i], "");
}
doc["mac"] = WiFi.macAddress();
serializeJson(doc, *response);
request->send(response);
});

View File

@@ -146,6 +146,17 @@
"1": "Fahrenheit / mph / millimeters"
}
},
{
"key": "interval",
"name": "Interval",
"desc": "How often the forecast is being refreshed.",
"type": "select",
"options": {
"60": "Every hour",
"180": "-Every three hours",
"-1440": "At midnight"
}
},
{
"key": "#lat",
"name": "Lat",
@@ -380,7 +391,7 @@
{
"key": "tariffkwh",
"name": "Fixed surcharge",
"desc": "Fixed surcharge per kWh, in 1/100 units (cents)",
"desc": "Fixed surcharge per kWh, in 1/100 units (cents/öre/øre)",
"type": "text"
},
{
@@ -388,7 +399,17 @@
"name": "Tax percentage",
"desc": "Percentage to add to the total (for example, 21, for 21% VAT)",
"type": "text"
}
},
{
"key": "units",
"name": "Price units",
"desc": "Display whole units (EUR/NOK/DKK) or cents",
"type": "select",
"options": {
"100": "1/1 units (EUR/NOK/DKK)",
"1": "1/100 units (cents/öre/øre)"
}
}
]
},
{

View File

@@ -9,6 +9,12 @@
<link rel="stylesheet" href="main.css" type="text/css" />
<link rel="icon" type="image/vnd.icon" href="favicon.ico">
<style>
.window {
margin-left: 10px;
}
p {
margin-bottom: 10px;
}
.window input {
width: 120px;
}
@@ -29,6 +35,7 @@
<div class="container">
<div class="window">
<h1>WiFi config</h1>
<p>WiFi mac address: <span id="mac">-</span></p>
<pre>
SSID <input type="text" id="ssid"> <button id="listssid">find SSID</button>
Password <input type="password" id="pw">
@@ -40,7 +47,6 @@ Subnet mask <input type="text" id="mask">
Gateway <input type="text" id="gw">
DNS <input type="text" id="dns">
</pre>
<button id="connect">Save WiFi settings and reboot</button><br>
</div>

View File

@@ -10,6 +10,7 @@ window.addEventListener("load", function () {
$('#mask').value = data.mask || "";
$('#gw').value = data.gw || "";
$('#dns').value = data.dns || "";
$('#mac').innerHTML = data.mac || "";
});
});