Merge pull request #18 from nlimper/development

bugfix and better handling of AP crash
This commit is contained in:
Nic Limper
2023-02-19 11:20:29 +01:00
committed by GitHub
12 changed files with 122 additions and 83 deletions

View File

@@ -52,8 +52,9 @@
<div class="actionbox">
<div>
<div>Currently active tags:</div>
<div class="rebootbtn"><span id="rebootbutton">reboot AP</span></div>
<div class="editbtn"><a href="/edit" target="littlefs" class="filebutton">edit littleFS</a></div>
Currently active tags:<br>
</div>
</div>

View File

@@ -52,28 +52,32 @@ label {
.actionbox>div:first-child {
padding: 10px;
background-color: white;
margin: 5px;
background-color: white;
margin: 5px;
}
.actionbox p {
padding: 5px;
.actionbox>div:first-child>div:first-child {
flex-grow: 2;
}
.actionbox .columns {
.actionbox>div {
display:flex;
flex-wrap: wrap;
gap: 20px;
}
.filebutton {
padding:2px 5px;
#rebootbutton {
padding: 2px 5px;
background-color: #cccccc;
text-decoration: none;
color: black;
cursor: pointer;
}
.editbtn {
float:right;
.filebutton {
padding: 2px 5px;
background-color: #cccccc;
text-decoration: none;
color: black;
}
.columns div {

View File

@@ -259,7 +259,28 @@ $('#cfgsave').onclick = function () {
}
$('#cfgdelete').onclick = function () {
let mac = $('#cfgmac').dataset.mac;
let formData = new FormData();
formData.append("mac", $('#cfgmac').dataset.mac);
fetch("/delete_cfg", {
method: "POST",
body: formData
})
.then(response => response.text())
.then(data => {
var div = $('#tag' + $('#cfgmac').dataset.mac);
div.remove();
showMessage(data);
})
.catch(error => showMessage('Error: ' + error));
$('#configbox').style.display = 'none';
}
$('#rebootbutton').onclick = function () {
showMessage("rebooting AP....",true);
fetch("/reboot", {
method: "POST"
});
socket.close();
}
function contentselected() {

View File

@@ -9,4 +9,5 @@ extern bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, u
extern void processJoinNetwork(struct espJoinNetwork* xjn);
extern void processXferComplete(struct espXferComplete* xfc);
extern void processXferTimeout(struct espXferComplete* xfc);
extern void processDataReq(struct espAvailDataReq* adr);
extern void processDataReq(struct espAvailDataReq* adr);
void refreshAllPending();

View File

@@ -8,9 +8,7 @@
class pendingdata {
public:
String filename;
//uint8_t dst[8];
uint64_t ver;
String md5;
uint32_t timeout;
uint8_t datatimeout;
uint8_t* data = nullptr;

View File

@@ -43,6 +43,7 @@ class tagRecord {
extern std::vector<tagRecord*> tagDB;
String tagDBtoJson(uint8_t mac[6] = nullptr, uint8_t startPos = 0);
bool deleteRecord(uint8_t mac[6]);
void fillNode(JsonObject &tag, tagRecord* &taginfo);
void saveDB(String filename);
void loadDB(String filename);

View File

@@ -200,7 +200,7 @@ void initSprite(TFT_eSprite &spr, int w, int h) {
spr.setColorDepth(8);
spr.createSprite(w, h);
if (spr.getPointer() == nullptr) {
wsErr("Failed to create sprite in drawNumber");
wsErr("Failed to create sprite");
}
spr.fillSprite(TFT_WHITE);
}

View File

@@ -44,10 +44,10 @@ void setup() {
init_web();
loadDB("/current/tagDB.json");
xTaskCreate(timeTask, "timed tasks", 10000, NULL, 2, NULL);
xTaskCreate(zbsRxTask, "zbsRX Process", 10000, NULL, 2, NULL);
xTaskCreate(garbageCollection, "pending-data cleanup", 5000, NULL, 1, NULL);
xTaskCreate(webSocketSendProcess, "ws", 5000, NULL,configMAX_PRIORITIES-10, NULL);
xTaskCreate(timeTask, "timed tasks", 10000, NULL, 2, NULL);
}
void loop() {

View File

@@ -63,7 +63,6 @@ void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) {
char buffer[64];
uint8_t src[8];
*((uint64_t*)src) = swap64(*((uint64_t*)dst));
sprintf(buffer, "idle request %02X%02X%02X%02X%02X%02X %d minutes\n\0", src[2], src[3], src[4], src[5], src[6], src[7], nextCheckin);
Serial.print(buffer);
sendDataAvail(&pending);
@@ -135,7 +134,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
if (taginfo != nullptr) {
if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) {
wsLog("new image is the same as current image. not updating tag.");
wsLog("new image is the same as current or already pending image. not updating tag.");
wsSendTaginfo(mac);
return true;
}
@@ -147,7 +146,6 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
lut = EPD_LUT_DEFAULT; // full update once a day
taginfo->lastfullupdate = now;
}
Serial.println("last midnight: "+String(last_midnight)+" last full: "+String(taginfo->lastfullupdate) + " -> lut: " + String(lut));
} else {
wsErr("Tag not found, this shouldn't happen.");
}
@@ -266,10 +264,14 @@ void processXferComplete(struct espXferComplete* xfc) {
sprintf(src_path, "/current/%02X%02X%02X%02X%02X%02X.pending\0", src[2], src[3], src[4], src[5], src[6], src[7]);
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X.bmp\0", src[2], src[3], src[4], src[5], src[6], src[7]);
sprintf(tmp_path, "/temp/%02X%02X%02X%02X%02X%02X.bmp\0", src[2], src[3], src[4], src[5], src[6], src[7]);
if (LittleFS.exists(dst_path)) {
if (LittleFS.exists(dst_path) && LittleFS.exists(src_path)) {
LittleFS.remove(dst_path);
}
LittleFS.rename(src_path, dst_path);
if (LittleFS.exists(src_path)) {
LittleFS.rename(src_path, dst_path);
} else {
wsErr("hm, weird, no pending image found after xfercomplete.");
}
if (LittleFS.exists(tmp_path)) {
LittleFS.remove(tmp_path);
}
@@ -362,11 +364,23 @@ void processDataReq(struct espAvailDataReq* eadr) {
taginfo->capabilities = eadr->adr.capabilities;
}
Serial.printf("t=%d, lqi=%d, rssi=%d, ", eadr->adr.temperature, eadr->adr.lastPacketLQI, eadr->adr.lastPacketRSSI);
Serial.printf("hwtype=%d, reason=%d, volt=%d", eadr->adr.hwType,eadr->adr.wakeupReason,eadr->adr.batteryMv);
sprintf(buffer, "<ADR %02X%02X%02X%02X%02X%02X\n\0", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.print(buffer);
wsSendTaginfo(mac);
digitalWrite(ONBOARD_LED, HIGH);
}
void refreshAllPending() {
for (int16_t c = 0; c < tagDB.size(); c++) {
tagRecord* taginfo = nullptr;
taginfo = tagDB.at(c);
if (taginfo->pending) {
taginfo->pending = false;
taginfo->nextupdate = 0;
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
wsSendTaginfo(taginfo->mac);
}
}
};

View File

@@ -251,6 +251,8 @@ void zbsRxTask(void* parameter) {
Serial.println("I wasn't able to connect to a ZBS tag, trying to reboot the tag.");
Serial.println("If this problem persists, please check wiring and definitions in the settings.h file, and presence of the right firmware");
simplePowerOn();
wsErr("The AP tag crashed. Restarting tag, regenerating all pending info.");
refreshAllPending();
}
}

View File

@@ -20,6 +20,19 @@ tagRecord* tagRecord::findByMAC(uint8_t mac[6]) {
return nullptr;
}
bool deleteRecord(uint8_t mac[6]) {
for (int16_t c = 0; c < tagDB.size(); c++) {
tagRecord* tag = nullptr;
tag = tagDB.at(c);
if (memcmp(tag->mac, mac, 6) == 0) {
delete tagDB[c];
tagDB.erase(tagDB.begin() + c);
return true;
}
}
return false;
}
String tagDBtoJson(uint8_t mac[6], uint8_t startPos) {
DynamicJsonDocument doc(2500);
JsonArray tags = doc.createNestedArray("tags");
@@ -55,9 +68,12 @@ void fillNode(JsonObject &tag, tagRecord* &taginfo) {
char buffer[16];
sprintf(buffer, "%02X%02X%02X%02X%02X%02X\0", taginfo->mac[0], taginfo->mac[1], taginfo->mac[2], taginfo->mac[3], taginfo->mac[4], taginfo->mac[5]);
tag["mac"] = (String)buffer;
char hex[7];
sprintf(hex, "%02x%02x%02x\0", taginfo->md5[0], taginfo->md5[1], taginfo->md5[2]);
tag["hash"] = hex;
char hex[33];
for (uint8_t i = 0; i < 16; i++) {
sprintf(hex + (i * 2), "%02x", taginfo->md5[i]);
}
tag["hash"] = (String)hex;
tag["lastseen"] = taginfo->lastseen;
tag["nextupdate"] = taginfo->nextupdate;
tag["nextcheckin"] = taginfo->expectedNextCheckin;
@@ -140,8 +156,14 @@ void loadDB(String filename) {
memcpy(taginfo->mac, mac, sizeof(taginfo->mac));
tagDB.push_back(taginfo);
}
String md5 = tag["hash"].as<String>();
if (md5.length() >= 32) {
for (int i = 0; i < 16; i++) {
taginfo->md5[i] = strtoul(md5.substring(i * 2, i * 2 + 2).c_str(), NULL, 16);
}
}
memcpy(taginfo->md5pending, taginfo->md5, sizeof(taginfo->md5));
taginfo->lastseen = (uint32_t)tag["lastseen"];
//taginfo->lastseen = 0;
taginfo->nextupdate = (uint32_t)tag["nextupdate"];
taginfo->expectedNextCheckin = (uint16_t)tag["nextcheckin"];
if (taginfo->expectedNextCheckin < now - 1800) {

View File

@@ -42,30 +42,11 @@ uint64_t swap64(uint64_t x) {
}
void webSocketSendProcess(void *parameter) {
uint32_t ulNotificationValue;
Serial.print("websocket thread started\n");
websocketUpdater = xTaskGetCurrentTaskHandle();
wsMutex = xSemaphoreCreateMutex();
while (true) {
ulNotificationValue = ulTaskNotifyTake(pdTRUE, 1000 / portTICK_RATE_MS);
if (ulNotificationValue == 0) { // timeout, so every 1s
ws.cleanupClients();
} else {
// if (ws.count())
// sendStatus(STATUS_WIFI_ACTIVITY);
DynamicJsonDocument doc(1500);
if (ulNotificationValue & 2) { // WS_SEND_MODE_STATUS) {
}
/*
JsonArray statusframes = doc.createNestedArray("frames");
}*/
size_t len = measureJson(doc);
xSemaphoreTake(wsMutex, portMAX_DELAY);
auto buffer = std::make_shared<std::vector<uint8_t>>(len);
serializeJson(doc, buffer->data(), len);
// ws.textAll((char*)buffer->data());
xSemaphoreGive(wsMutex);
}
ws.cleanupClients();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
@@ -146,17 +127,17 @@ void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType
void wsLog(String text) {
StaticJsonDocument<500> doc;
doc["logMsg"] = text;
xSemaphoreTake(wsMutex, portMAX_DELAY);
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as<String>());
xSemaphoreGive(wsMutex);
if (wsMutex) xSemaphoreGive(wsMutex);
}
void wsErr(String text) {
StaticJsonDocument<500> doc;
doc["errMsg"] = text;
xSemaphoreTake(wsMutex, portMAX_DELAY);
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as<String>());
xSemaphoreGive(wsMutex);
if (wsMutex) xSemaphoreGive(wsMutex);
}
void wsSendSysteminfo() {
@@ -214,6 +195,12 @@ void init_web() {
server.on("/reboot", HTTP_POST, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "OK Reboot");
wsErr("REBOOTING");
ws.enable(false);
refreshAllPending();
saveDB("/current/tagDB.json");
ws.closeAll();
delay(100);
ESP.restart();
});
@@ -226,34 +213,6 @@ void init_web() {
},
doImageUpload);
server.on("/req_checkin", HTTP_POST, [](AsyncWebServerRequest *request) {
String filename;
String dst;
if (request->hasParam("dst", true)) {
dst = request->getParam("dst", true)->value();
uint8_t mac_addr[12]; // I expected this to return like 8 values, but if I make the array 8 bytes long, things die.
mac_addr[0] = 0x00;
mac_addr[1] = 0x00;
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X",
&mac_addr[2],
&mac_addr[3],
&mac_addr[4],
&mac_addr[5],
&mac_addr[6],
&mac_addr[7]) != 6) {
request->send(200, "text/plain", "Something went wrong trying to parse the mac address");
} else {
*((uint64_t *)mac_addr) = swap64(*((uint64_t *)mac_addr));
if (prepareDataAvail(&filename, DATATYPE_NOUPDATE, mac_addr,0)) {
request->send(200, "text/plain", "Sending check-in request to " + dst);
}
}
return;
}
request->send(200, "text/plain", "Didn't get the required params");
return;
});
server.on("/get_db", HTTP_GET, [](AsyncWebServerRequest *request) {
String json = "";
if (request->hasParam("mac")) {
@@ -284,8 +243,8 @@ void init_web() {
taginfo->modeConfigJson = request->getParam("modecfgjson", true)->value();
taginfo->contentMode = atoi(request->getParam("contentmode", true)->value().c_str());
taginfo->nextupdate = 0;
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
//memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
//memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
wsSendTaginfo(mac);
saveDB("/current/tagDB.json");
request->send(200, "text/plain", "Ok, saved");
@@ -297,6 +256,22 @@ void init_web() {
request->send(200, "text/plain", "Ok, saved");
});
server.on("/delete_cfg", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("mac", true)) {
String dst = request->getParam("mac", true)->value();
uint8_t mac[6];
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
if (deleteRecord(mac)) {
request->send(200, "text/plain", "Ok, deleted");
} else {
request->send(200, "text/plain", "Error while saving: mac not found");
}
}
} else {
request->send(500, "text/plain", "no mac");
}
});
server.onNotFound([](AsyncWebServerRequest *request) {
if (request->url() == "/" || request->url() == "index.htm") {
request->send(200, "text/html", "-");