Skip to content

Commit

Permalink
invoke "/rfid" endpoint (#260)
Browse files Browse the repository at this point in the history
* invoke "/rfid" endpoint

* list rfid tags to JSON directly avoid using SD card - Web_DumpNvsToSd()

* stop playback before delete nvs assignment
  • Loading branch information
tueddy authored Aug 31, 2023
1 parent 787a587 commit 639cb3f
Showing 1 changed file with 213 additions and 69 deletions.
282 changes: 213 additions & 69 deletions src/Web.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ static void handleGetWiFiConfig(AsyncWebServerRequest *request);
static void handlePostWiFiConfig(AsyncWebServerRequest *request, JsonVariant &json);
static void handleCoverImageRequest(AsyncWebServerRequest *request);
static void handleWiFiScanRequest(AsyncWebServerRequest *request);
static void handleGetRFIDRequest(AsyncWebServerRequest *request);
static void handlePostRFIDRequest(AsyncWebServerRequest *request, JsonVariant &json);
static void handleDeleteRFIDRequest(AsyncWebServerRequest *request);
static void handleGetInfo(AsyncWebServerRequest *request);
static void handleGetSettings(AsyncWebServerRequest *request);
static void handlePostSettings(AsyncWebServerRequest *request, JsonVariant &json);


static bool Web_DumpNvsToSd(const char *_namespace, const char *_destFile);

static void onWebsocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
static void settingsToJSON(JsonObject obj, String section);
static bool JSONToSettings(JsonObject obj);
Expand Down Expand Up @@ -155,6 +155,85 @@ class OneParamRewrite : public AsyncWebRewrite
};


// List all key in NVS for a given namespace
// callback function is called for every key with userdefined data object
bool listNVSKeys(const char *_namespace, void* data, bool (*callback)(const char * key, void* data)) {
Led_SetPause(true); // Workaround to prevent exceptions due to Neopixel-signalisation while NVS-write
esp_partition_iterator_t pi; // Iterator for find
const esp_partition_t *nvs; // Pointer to partition struct
esp_err_t result = ESP_OK;
const char *partname = "nvs";
uint8_t pagenr = 0; // Page number in NVS
uint8_t i; // Index in Entry 0..125
uint8_t bm; // Bitmap for an entry
uint32_t offset = 0; // Offset in nvs partition
uint8_t namespace_ID; // Namespace ID found

pi = esp_partition_find(ESP_PARTITION_TYPE_DATA, // Get partition iterator for
ESP_PARTITION_SUBTYPE_ANY, // this partition
partname);
if (pi) {
nvs = esp_partition_get(pi); // Get partition struct
esp_partition_iterator_release(pi); // Release the iterator
Log_Printf(LOGLEVEL_DEBUG, "Partition %s found, %d bytes", partname, nvs->size);
} else {
Log_Printf(LOGLEVEL_ERROR, "Partition %s not found!", partname);
return NULL;
}
namespace_ID = FindNsID(nvs, _namespace); // Find ID of our namespace in NVS
while (offset < nvs->size) {
result = esp_partition_read(nvs, offset, // Read 1 page in nvs partition
&buf,
sizeof(nvs_page));
if (result != ESP_OK) {
Log_Println("Error reading NVS!", LOGLEVEL_ERROR);
return false;
}

i = 0;

while (i < 126) {
bm = (buf.Bitmap[i / 4] >> ((i % 4) * 2)) & 0x03; // Get bitmap for this entry
if (bm == 2) {
if ((namespace_ID == 0xFF) || // Show all if ID = 0xFF
(buf.Entry[i].Ns == namespace_ID)) { // otherwise just my namespace
if (isNumber(buf.Entry[i].Key)) {
if (!callback(buf.Entry[i].Key, data)) {
return false;
}
}
}
i += buf.Entry[i].Span; // Next entry
} else {
i++;
}
}
offset += sizeof(nvs_page); // Prepare to read next page in nvs
pagenr++;
}
Led_SetPause(false);

return true;
}

// callback for writing a NVS entry to file
bool DumpNvsToSdCallback(const char * key, void* data) {
String s = gPrefsRfid.getString(key);
File *file = (File *) data;
file->printf("%s%s%s%s\n", stringOuterDelimiter, key, stringOuterDelimiter, s.c_str());
return true;
}

// Dumps all RFID-entries from NVS into a file on SD-card
bool Web_DumpNvsToSd(const char *_namespace, const char *_destFile) {
File file = gFSystem.open(_destFile, FILE_WRITE);
if (!file) {
return false;
}
bool success = listNVSKeys(_namespace, &file, DumpNvsToSdCallback);
file.close();
return success;
}

// First request will return 0 results unless you start scan from somewhere else (loop/setup)
// Do not request more often than 3-5 seconds
Expand Down Expand Up @@ -335,6 +414,11 @@ void webserverStart(void) {
System_UpdateActivityTimer();
});

// RFID
wServer.on("/rfid", HTTP_GET, handleGetRFIDRequest);
wServer.addHandler(new AsyncCallbackJsonWebHandler("/rfid", handlePostRFIDRequest));
wServer.addRewrite(new OneParamRewrite("/rfid/{id}", "/rfid?id={id}") );
wServer.on("/rfid", HTTP_DELETE, handleDeleteRFIDRequest);

// WiFi scan
wServer.on("/wifiscan", HTTP_GET, handleWiFiScanRequest);
Expand Down Expand Up @@ -1490,6 +1574,132 @@ void handlePostWiFiConfig(AsyncWebServerRequest *request, JsonVariant &json){
}
}

static bool tagIdToJSON(String tagId, JsonObject entry) {
String s = gPrefsRfid.getString(tagId.c_str(), "-1"); // Try to lookup rfidId in NVS
if (!s.compareTo("-1")) {
return false;
}
char _file[255];
uint32_t _lastPlayPos = 0;
uint16_t _trackLastPlayed = 0;
uint32_t _mode = 1;
char *token;
uint8_t i = 1;
token = strtok((char *)s.c_str(), stringDelimiter);
while (token != NULL) { // Try to extract data from string after lookup
if (i == 1) {
strncpy(_file, token, sizeof(_file) / sizeof(_file[0]));
} else if (i == 2) {
_lastPlayPos = strtoul(token, NULL, 10);
} else if (i == 3) {
_mode = strtoul(token, NULL, 10);
} else if (i == 4) {
_trackLastPlayed = strtoul(token, NULL, 10);
}
i++;
token = strtok(NULL, stringDelimiter);
}
entry["id"] = tagId;
if (_mode >= 100) {
entry["modId"] = _mode;
} else {
entry["fileOrUrl"] = _file;
entry["playMode"] = _mode;
entry["lastPlayPos"] = _lastPlayPos;
entry["trackLastPlayed"] = _trackLastPlayed;
}
return true;
}

// callback for writing a NVS entry to JSON
bool DumpNvsToJSONCallback(const char * key, void* data) {
JsonArray *myArr = (JsonArray*) data;
JsonObject obj = myArr->createNestedObject();
return tagIdToJSON(key, obj);
}

static void handleGetRFIDRequest(AsyncWebServerRequest *request) {
String tagId = "";
if (request->hasParam("id")) {
tagId = request->getParam("id")->value();
}
if (tagId == "") {
// return all RFID assignments
AsyncJsonResponse *response = new AsyncJsonResponse(true);
JsonArray Arr = response->getRoot();
if (listNVSKeys("rfidTags", &Arr, DumpNvsToJSONCallback)) {
response->setLength();
request->send(response);
} else {
request->send(500, "error reading entries from NVS");
}
} else {
if (gPrefsRfid.isKey(tagId.c_str())) {
// return single RFID assignment
AsyncJsonResponse *response = new AsyncJsonResponse(false);
JsonObject obj = response->getRoot();
tagIdToJSON(tagId, obj);
response->setLength();
request->send(response);
} else {
// RFID assignment not found
request->send(404);
}
}
}

static void handlePostRFIDRequest(AsyncWebServerRequest *request, JsonVariant &json) {
const JsonObject& jsonObj = json.as<JsonObject>();

String tagId = jsonObj["id"];
String fileOrUrl = jsonObj["fileOrUrl"];
if (fileOrUrl.isEmpty()) {
fileOrUrl = "0";
}
char _fileOrUrlAscii[MAX_FILEPATH_LENTGH];
convertFilenameToAscii(fileOrUrl, _fileOrUrlAscii);
uint8_t _playModeOrModId;
if (jsonObj.containsKey("modId")) {
_playModeOrModId = jsonObj["modId"];
} else {
_playModeOrModId = jsonObj["playMode"];
}
if (_playModeOrModId <= 0) {
Log_Println("rfidAssign: Invalid playMode or modId", LOGLEVEL_ERROR);
request->send(500, "text/plain; charset=utf-8", "rfidAssign: Invalid playMode or modId");
return;
}
char rfidString[275];
snprintf(rfidString, sizeof(rfidString) / sizeof(rfidString[0]), "%s%s%s0%s%u%s0", stringDelimiter, _fileOrUrlAscii, stringDelimiter, stringDelimiter, _playModeOrModId, stringDelimiter);
gPrefsRfid.putString(tagId.c_str(), rfidString);

String s = gPrefsRfid.getString(tagId.c_str(), "-1");
if (s.compareTo(rfidString)) {
request->send(500, "text/plain; charset=utf-8", "rfidAssign: cannot save assignment to NVS");
return;
}
Web_DumpNvsToSd("rfidTags", backupFile); // Store backup-file every time when a new rfid-tag is programmed
}

static void handleDeleteRFIDRequest(AsyncWebServerRequest *request) {
String tagId = "";
if (request->hasParam("id")) {
tagId = request->getParam("id")->value();
}
if ((tagId != "") && (gPrefsRfid.isKey(tagId.c_str()))) {
// stop playback, tag to delete might be in use
Cmd_Action(CMD_STOP);
if (gPrefsRfid.remove(tagId.c_str())) {
request->send(200, "text/plain; charset=utf-8", tagId + " removed successfuly");
} else {
request->send(500, "text/plain; charset=utf-8", "error removing tag from NVS");
}
} else {
request->send(404, "text/plain; charset=utf-8", "error removing tag from NVS: Tag not exists");
}
}



// Takes stream from file-upload and writes payload into a temporary sd-file.
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
Expand Down Expand Up @@ -1577,72 +1787,6 @@ void Web_DumpSdToNvs(const char *_filename) {
gFSystem.remove(_filename);
}

// Dumps all RFID-entries from NVS into a file on SD-card
bool Web_DumpNvsToSd(const char *_namespace, const char *_destFile) {
Led_SetPause(true); // Workaround to prevent exceptions due to Neopixel-signalisation while NVS-write
esp_partition_iterator_t pi; // Iterator for find
const esp_partition_t *nvs; // Pointer to partition struct
esp_err_t result = ESP_OK;
const char *partname = "nvs";
uint8_t pagenr = 0; // Page number in NVS
uint8_t i; // Index in Entry 0..125
uint8_t bm; // Bitmap for an entry
uint32_t offset = 0; // Offset in nvs partition
uint8_t namespace_ID; // Namespace ID found

pi = esp_partition_find(ESP_PARTITION_TYPE_DATA, // Get partition iterator for
ESP_PARTITION_SUBTYPE_ANY, // this partition
partname);
if (pi) {
nvs = esp_partition_get(pi); // Get partition struct
esp_partition_iterator_release(pi); // Release the iterator
Log_Printf(LOGLEVEL_DEBUG, "Partition %s found, %d bytes", partname, nvs->size);
} else {
Log_Printf(LOGLEVEL_ERROR, "Partition %s not found!", partname);
return NULL;
}
namespace_ID = FindNsID(nvs, _namespace); // Find ID of our namespace in NVS
File backupFile = gFSystem.open(_destFile, FILE_WRITE);
if (!backupFile) {
return false;
}
while (offset < nvs->size) {
result = esp_partition_read(nvs, offset, // Read 1 page in nvs partition
&buf,
sizeof(nvs_page));
if (result != ESP_OK) {
Log_Println("Error reading NVS!", LOGLEVEL_ERROR);
return false;
}

i = 0;

while (i < 126) {
bm = (buf.Bitmap[i / 4] >> ((i % 4) * 2)) & 0x03; // Get bitmap for this entry
if (bm == 2) {
if ((namespace_ID == 0xFF) || // Show all if ID = 0xFF
(buf.Entry[i].Ns == namespace_ID)) { // otherwise just my namespace
if (isNumber(buf.Entry[i].Key)) {
String s = gPrefsRfid.getString((const char *)buf.Entry[i].Key);
backupFile.printf("%s%s%s%s\n", stringOuterDelimiter, buf.Entry[i].Key, stringOuterDelimiter, s.c_str());
}
}
i += buf.Entry[i].Span; // Next entry
} else {
i++;
}
}
offset += sizeof(nvs_page); // Prepare to read next page in nvs
pagenr++;
}

backupFile.close();
Led_SetPause(false);

return true;
}


// handle album cover image request
static void handleCoverImageRequest(AsyncWebServerRequest *request) {

Expand Down

0 comments on commit 639cb3f

Please sign in to comment.