Browse Source

Introduced backup-file-upload

master
Torsten Stauder 5 years ago
parent
commit
d09814561f
  1. 3
      README.md
  2. 17
      html/website.html
  3. 4
      platformio.ini
  4. 1
      src/logmessages.h
  5. 52
      src/main.cpp
  6. 15
      src/websiteMgmt.h

3
README.md

@ -191,5 +191,8 @@ Everything that can be controlled via RFID-tags and buttons, can also be control
### Supported file/stream-types
Please refer [ESP32-audioI2S](https://github.com/schreibfaul1/ESP32-audioI2S), as this is the library I used for music-decoding.
### Backups
As all assignments between RFID-IDs and actions (playmode, file to play...) is saved in ESP's NVS, the problem is that it's all gone when the ESP is broken. So that's where a backup comes into play. So every time you change or add a new assignment between a RFID-tag and an action via GUI, a backup-file is saved on the uSD-card. The file's name can be changed via `backupFile`. Again using the GUI you can use the upload-form to import such a file. To be honest: Sometimes I had some issues with Firefox doing this whereas Safari turned out to do it right. Don't know why :-(.
## Smarthome (optional)
As already described, MQTT is supported. In order to use it it's necessary to run a MQTT-broker; [Mosquitto](https://mosquitto.org/) for instance. After connecting to it, Tonuino subscribes to all command-topics. State-topics are used to push states to the broker in order to inform others if anything changed (change of volume, new playlist, new track... name it). Others, like openHAB, subscribe to state-topics end send commands via command-topics. So it's not just limited to openHAB. It's just necessary to use a platform, that supports MQTT.

17
html/website.html

@ -38,6 +38,9 @@
<li class="nav-item">
<a class="nav-link" href="#generalConfig">Allgemein</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#importNvs">NVS-Importer</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/restart" style="color: orange">Neustart Tonuino</a>
</li>
@ -68,8 +71,8 @@
<div class="form-group col-md-6">
<label for="rfidIdMusic">RFID-Chip-Nummer (12-stellig)</label>
<input type="text" class="form-control" id="rfidIdMusic" maxlength="12" pattern="[0-9]{12}" placeholder="%RFID_TAG_ID%" name="rfidIdMusic" required>
<label for="fileOrUrl">Datei, Verzeichnis oder URL (URL nur für Webradio)</label>
<input type="text" class="form-control" id="fileOrUrl" maxlength="255" placeholder="z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3" name="fileOrUrl" required>
<label for="fileOrUrl">Datei, Verzeichnis oder URL (^ und # als Zeichen nicht erlaubt)</label>
<input type="text" class="form-control" id="fileOrUrl" maxlength="255" placeholder="z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3" pattern="[^\^#]" name="fileOrUrl" required>
<label for="playMode">Abspielmodus</label>
<select class="form-control" id="playMode" name="playMode">
<option value="1">Einzelner Titel</option>
@ -177,13 +180,13 @@
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="importNvs">
<h2>NVS-Importer (noch nicht funktional!)</h2>
<h2>NVS-Importer</h2>
<form action="/upload" enctype="multipart/form-data" method="POST">
<div class="form-group">
<label for="nvsUpload">NVS-Importer</label>
<label for="nvsUpload">Hier kann eine Backup-Datei importiert werden.</label>
<input type="file" class="form-control-file" id="nvsUpload" name="nvsUpload" accept=".txt">
<button type="submit" class="btn btn-primary">Absenden</button>
</div>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
@ -194,6 +197,10 @@
var socket = new WebSocket("ws://%IPv4%/ws");
function connect() {
socket = new WebSocket("ws://%IPv4%/ws");
}
function ping() {
var myObj = {
"ping": {

4
platformio.ini

@ -27,5 +27,5 @@ lib_deps =
https://github.com/bblanchon/ArduinoJson.git
; Don't forget to run this script if you changed the html-files provided in any way
extra_scripts =
pre:processHtml.py
;extra_scripts =
; pre:processHtml.py

1
src/logmessages.h

@ -15,6 +15,7 @@ static const char noPlaymodeChangeIfIdle[] PROGMEM = "Playmode kann nicht verän
static const char noValidTopic[] PROGMEM = "Kein gültiges Topic";
static const char freePtr[] PROGMEM = "Ptr-Freigabe";
static const char freeMemory[] PROGMEM = "Freier Speicher";
static const char writeEntryToNvs[] PROGMEM = "Schreibe Eintrag in NVS";
static const char freeMemoryAfterFree[] PROGMEM = "Freier Speicher nach Aufräumen";
static const char releaseMemoryOfOldPlaylist[] PROGMEM = "Gebe Speicher der alten Playlist frei.";
static const char dirOrFileDoesNotExist[] PROGMEM = "Datei oder Verzeichnis existiert nicht!";

52
src/main.cpp

@ -188,7 +188,7 @@ uint8_t buttonDebounceInterval = 50; // Interval in ms to sof
uint16_t intervalToLongPress = 700; // Interval in ms to distinguish between short and long press of previous/next-button
// Where to store the backup-file
static const char backupFile[] PROGMEM = "/backup.txt";
static const char backupFile[] PROGMEM = "/backup.txt"; // File is written every time a (new) RFID-assignment via GUI is done
// Don't change anything here unless you know what you're doing
// HELPER //
@ -255,6 +255,7 @@ char mqtt_server[16] = "192.168.2.43"; // IP-address of MQTT-se
char stringDelimiter[] = "#"; // Character used to encapsulate data in linear NVS-strings (don't change)
char stringOuterDelimiter[] = "^"; // Character used to encapsulate encapsulated data along with RFID-ID in backup-file
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
@ -262,6 +263,10 @@ AsyncWebServer wServer(80);
AsyncWebSocket ws("/ws");
AsyncEventSource events("/events");
static const char backupRecoveryWebsite[] PROGMEM = "<p>Das Backup-File wird eingespielt...<br />Zur letzten Seite <a href=\"javascript:history.back()\">zur&uuml;ckkehren</a>.</p>";
static const char restartWebsite[] PROGMEM = "<p>Der Tonuino wird neu gestartet...<br />Zur letzten Seite <a href=\"javascript:history.back()\">zur&uuml;ckkehren</a>.</p>";
// Audio/mp3
SPIClass spiSD(HSPI);
TaskHandle_t mp3Play;
@ -1188,12 +1193,12 @@ void playAudio(void *parameter) {
audio.pauseResume();
trackCommand = 0;
loggerNl((char *) FPSTR(cmndPause), LOGLEVEL_INFO);
playProperties.pausePlay = !playProperties.pausePlay;
if (playProperties.saveLastPlayPosition) {
if (playProperties.saveLastPlayPosition && playProperties.pausePlay) {
snprintf(logBuf, sizeof(logBuf) / sizeof(logBuf[0]), "Titel wurde bei Position %u pausiert.", audio.getFilePos());
loggerNl(logBuf, LOGLEVEL_INFO);
nvsRfidWriteWrapper(playProperties.playRfidTag, *(playProperties.playlist + playProperties.currentTrackNumber), audio.getFilePos(), playProperties.playMode, playProperties.currentTrackNumber, playProperties.numberOfTracks);
}
playProperties.pausePlay = !playProperties.pausePlay;
continue;
case NEXTTRACK:
@ -1731,11 +1736,15 @@ void showLed(void *parameter) {
} else if (!playProperties.pausePlay) { // Hue-rainbow
leds[led].setHue((uint8_t) (((double) 255 / NUM_LEDS) * led));
} else if (playProperties.pausePlay) {
leds[led] = CRGB::Orange;
leds[led % NUM_LEDS] = CRGB::Orange;
leds[(led+NUM_LEDS/4) % NUM_LEDS] = CRGB::Orange;
leds[(led+NUM_LEDS/2) % NUM_LEDS] = CRGB::Orange;
leds[(led+NUM_LEDS/4*3) % NUM_LEDS] = CRGB::Orange;
break;
}
}
}
} else { // ... but to things a little bit different for Webstream as there's no progress available
} else { // ... but do things a little bit different for Webstream as there's no progress available
if (lastSwitchTimestamp == 0 || (millis() - lastSwitchTimestamp >= ledSwitchInterval * 1000) || redrawProgress) {
redrawProgress = false;
lastSwitchTimestamp = millis();
@ -2827,20 +2836,8 @@ bool dumpNvsToSd(char *_namespace, char *_destFile) {
}
// Handles uploaded files. Still in progress...
// Handles uploaded backup-file and writes valid entries into NVS
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
/*if(!index){
Serial.printf("UploadStart: %s\n", filename.c_str());
}
for(size_t i=0; i<len; i++) {
if (data[i] != '\n') {
Serial.write(data[i]);
}
}
if(final){
Serial.printf("UploadEnd: %s, %u B\n", filename.c_str(), index+len);
}*/
char ebuf[290];
uint16_t j=0;
char *token;
@ -2850,7 +2847,6 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
for(size_t i=0; i<len; i++) {
if (data[i] != '\n') {
ebuf[j++] = data[i];
//Serial.write(data[i]);
} else {
ebuf[j] = '\0';
j=0;
@ -2860,22 +2856,18 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
count++;
memcpy(nvsEntry[0].nvsKey, token, strlen(token));
nvsEntry[0].nvsKey[strlen(token)] = '\0';
Serial.printf("Col 1: %s\n", token);
} else if (count == 1) {
count = 0;
memcpy(nvsEntry[0].nvsEntry, token, strlen(token));
nvsEntry[0].nvsEntry[strlen(token)] = '\0';
Serial.printf("Col 2: %s\n", token);
}
//Serial.printf("%s\n", token);
delay(50);
token = strtok(NULL, stringOuterDelimiter);
}
//Serial.printf("%s => %s\n", nvsEntry[0].nvsKey, nvsEntry[0].nvsEntry);
delay(30);
/*if (isNumber(nvsEntry[0].nvsKey) && nvsEntry[0].nvsEntry[0] == '#') {
Serial.println("ok");
}*/
if (isNumber(nvsEntry[0].nvsKey) && nvsEntry[0].nvsEntry[0] == '#') {
snprintf(logBuf, sizeof(logBuf) / sizeof(logBuf[0]), "%s: %s => %s", (char *) FPSTR(writeEntryToNvs), nvsEntry[0].nvsKey, nvsEntry[0].nvsEntry);
loggerNl(logBuf, LOGLEVEL_NOTICE);
prefsRfid.putString(nvsEntry[0].nvsKey, nvsEntry[0].nvsEntry);
}
}
}
}
@ -3132,11 +3124,11 @@ void setup() {
});
wServer.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request){
request->send(200);
request->send_P(200, "text/html", backupRecoveryWebsite);
}, handleUpload);
wServer.on("/restart", HTTP_GET, [] (AsyncWebServerRequest *request) {
request->send(200, "text/html", "<p>Der Tonuino wird neu gestartet...<br />Zur letzten Seite <a href=\"javascript:history.back()\">zur&uuml;ckkehren</a>.</p>");
request->send_P(200, "text/html", restartWebsite);
Serial.flush();
ESP.restart();
});

15
src/websiteMgmt.h

@ -38,6 +38,9 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#generalConfig\">Allgemein</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#importNvs\">NVS-Importer</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"/restart\" style=\"color: orange\">Neustart Tonuino</a>\
</li>\
@ -68,8 +71,8 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<div class=\"form-group col-md-6\">\
<label for=\"rfidIdMusic\">RFID-Chip-Nummer (12-stellig)</label>\
<input type=\"text\" class=\"form-control\" id=\"rfidIdMusic\" maxlength=\"12\" pattern=\"[0-9]{12}\" placeholder=\"%RFID_TAG_ID%\" name=\"rfidIdMusic\" required>\
<label for=\"fileOrUrl\">Datei, Verzeichnis oder URL (URL nur für Webradio)</label>\
<input type=\"text\" class=\"form-control\" id=\"fileOrUrl\" maxlength=\"255\" placeholder=\"z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3\" name=\"fileOrUrl\" required>\
<label for=\"fileOrUrl\">Datei, Verzeichnis oder URL (^ und # als Zeichen nicht erlaubt)</label>\
<input type=\"text\" class=\"form-control\" id=\"fileOrUrl\" maxlength=\"255\" placeholder=\"z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3\" pattern=\"[^\^#]\" name=\"fileOrUrl\" required>\
<label for=\"playMode\">Abspielmodus</label>\
<select class=\"form-control\" id=\"playMode\" name=\"playMode\">\
<option value=\"1\">Einzelner Titel</option>\
@ -180,10 +183,10 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<h2>NVS-Importer</h2>\
<form action=\"/upload\" enctype=\"multipart/form-data\" method=\"POST\">\
<div class=\"form-group\">\
<label for=\"nvsUpload\">NVS-Importer</label>\
<label for=\"nvsUpload\">Hier kann eine Backup-Datei importiert werden.</label>\
<input type=\"file\" class=\"form-control-file\" id=\"nvsUpload\" name=\"nvsUpload\" accept=\".txt\">\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</div>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</form>\
<div class=\"messages col-md-6 my-2\"></div>\
</div>\
@ -193,6 +196,10 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
var okBox = '<div class=\"alert alert-success alert-dismissible fade show\" role=\"alert\">Aktion erfolgreich ausgeführt.<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button></div>';\
\
var socket = new WebSocket(\"ws://%IPv4%/ws\");\
\
function connect() {\
socket = new WebSocket(\"ws://%IPv4%/ws\");\
}\
\
function ping() {\
var myObj = {\

Loading…
Cancel
Save