Web improvements, NFC config

This commit is contained in:
Stefan Ostermann 2023-10-15 00:24:58 +02:00
parent a3be4cbb3c
commit 578281d9d1
5 changed files with 111 additions and 66 deletions

View File

@ -111,14 +111,23 @@ Place these sounds in the folder `system` on the SD card:
sleep.mp3 will be played just before the timed shutdown, start.mp3 will be played if the box started and initialized successfully. sleep.mp3 will be played just before the timed shutdown, start.mp3 will be played if the box started and initialized successfully.
## Battery Voltage
The battery voltage is measured by the ADC of pin 13. A voltage divider halves the voltage so that the pin can not be overloaded (max 3.3V).
The voltage divider consists of two 100kOHM resistors, the voltage is measured between them. One side is connected to the battery and the other to ground.
Pin 13s ADC (ADC2) can not be used while WIFI is on... damn.
## TODOs ## TODOs
* Card Reader with SPI. Configure hspi, second SPI channel? Or does SD Card + RFID run at the same channel? * Card Reader with SPI. Configure hspi, second SPI channel? Or does SD Card + RFID run at the same channel?
![](images/wemos-d1-mini-esp32.png) ![](images/wemos-d1-mini-esp32.png)
## Schematic without modules ## Schematic without modules
### Charging circuit ### Charging circuit
https://ww1.microchip.com/downloads/en/AppNotes/01149c.pdf https://ww1.microchip.com/downloads/en/AppNotes/01149c.pdf
MCP73837 ? MCP73837 ?

View File

@ -9,6 +9,8 @@ const char index_html[] PROGMEM = R"rawliteral(
<body> <body>
<h1>HannaBox</h1> <h1>HannaBox</h1>
<span id="state"></span><br/><br/> <span id="state"></span><br/><br/>
<span id="voltage"></span><br/>
<span id="uid"></span><br/>
<div> <div>
<button class="prev-button" onclick="simpleGetCall('prev');""></button> <button class="prev-button" onclick="simpleGetCall('prev');""></button>
@ -16,16 +18,17 @@ const char index_html[] PROGMEM = R"rawliteral(
<button class="next-button" onclick="simpleGetCall('next');"></button><br/><br/> <button class="next-button" onclick="simpleGetCall('next');"></button><br/><br/>
</div> </div>
<div class="slidecontainer"> <div class="slidecontainer">
<label for="progress" id="progressLabel"></label>
<input name="progress" type="range" min="0" max="100" value="0" class="slider" id="progressSlider" <input name="progress" type="range" min="0" max="100" value="0" class="slider" id="progressSlider"
onmouseup="postValue('progress',document.getElementById('progressSlider').value);" onchange="postValue('progress',document.getElementById('progressSlider').value);lastChange = Date.now()"
ontouchend="postValue('progress',document.getElementById('progressSlider').value);"> >
<label for="progress" id="progressLabel"></label>
</div> </div>
<div class="slidecontainer"> <div class="slidecontainer">
<label for="volume">Vol</label>
<input name="volume" type="range" min="0" max="15" value="7" class="slider" id="volumeSlider" <input name="volume" type="range" min="0" max="15" value="7" class="slider" id="volumeSlider"
onmouseup="postValue('volume',document.getElementById('volumeSlider').value);" onchange="postValue('volume',document.getElementById('volumeSlider').value);lastChange = Date.now()"
ontouchend="postValue('volume',document.getElementById('volumeSlider').value);"> >
<label for="volume">Vol</label>
</div> </div>
<!-- <!--
<button onmouseup="simpleGetCall('stop');" ontouchend="simpleGetCall('stop');">Stop</button>&nbsp; <button onmouseup="simpleGetCall('stop');" ontouchend="simpleGetCall('stop');">Stop</button>&nbsp;
@ -42,6 +45,8 @@ const char index_html[] PROGMEM = R"rawliteral(
// Get the <li> elements // Get the <li> elements
var liElements = document.querySelectorAll('ul li'); var liElements = document.querySelectorAll('ul li');
var lastChange = 0;
// Add click event listener to each <li> element // Add click event listener to each <li> element
liElements.forEach(function(li) { liElements.forEach(function(li) {
li.addEventListener('click', function() { li.addEventListener('click', function() {
@ -81,6 +86,8 @@ const char index_html[] PROGMEM = R"rawliteral(
function displayState(state) { function displayState(state) {
document.getElementById("state").innerHTML = state['title']; document.getElementById("state").innerHTML = state['title'];
document.getElementById("progressLabel").innerHTML = state['time']; document.getElementById("progressLabel").innerHTML = state['time'];
document.getElementById("voltage").innerHTML = state['voltage']+' mV';
document.getElementById("uid").innerHTML = 'Last NFC ID: '+state['uid'];
var elements = document.getElementsByClassName('play-button'); var elements = document.getElementsByClassName('play-button');
var btn = elements[0]; var btn = elements[0];
@ -89,12 +96,16 @@ const char index_html[] PROGMEM = R"rawliteral(
} else { } else {
btn.classList.remove('paused'); btn.classList.remove('paused');
} }
if (Date.now()-lastChange>1200) {
var progress = document.getElementById('progressSlider'); var progress = document.getElementById('progressSlider');
progress.value = state['time']; progress.value = state['time'];
progress.max = state['length']; progress.max = state['length'];
var volume = document.getElementById('volumeSlider'); var volume = document.getElementById('volumeSlider');
volume.value = state['volume']; volume.value = state['volume'];
}
} }

View File

@ -65,4 +65,8 @@ li {
box-shadow: -10px 0 0 0 darkgrey; box-shadow: -10px 0 0 0 darkgrey;
} }
.slider {
width: 80%;
}
)rawliteral"; )rawliteral";

View File

@ -52,6 +52,7 @@ void writeFile(fs::FS &fs, const char * path, const char * message){
file.close(); file.close();
} }
unsigned long lastStart = 0; unsigned long lastStart = 0;
unsigned long lastInteraction = 0; unsigned long lastInteraction = 0;
@ -64,12 +65,20 @@ boolean continuousMode = false;
uint8_t buttontoignore = 0; uint8_t buttontoignore = 0;
uint32_t lastVoltage = 0;
const int startDelay = 250; const int startDelay = 250;
String lastUid = "";
const String sleep_sound = "sleep.mp3"; const String sleep_sound = "sleep.mp3";
const String startup_sound = "start.mp3"; const String startup_sound = "start.mp3";
const String mapping_file = "/mapping.txt";
std::map<String, String> rfid_map;
/* /*
const long sleepMessageDelay = 28000; const long sleepMessageDelay = 28000;

View File

@ -31,6 +31,8 @@
#define CS_SDCARD 22 #define CS_SDCARD 22
#define BAT_VOLTAGE_PIN 13
#define RFID_LOOP_INTERVAL 25 #define RFID_LOOP_INTERVAL 25
#include "globals.h" #include "globals.h"
@ -75,9 +77,12 @@ bool asyncNext = false;
bool asyncPrev = false; bool asyncPrev = false;
/*
std::map<String, String> rfid_map{{"67 152 204 14", "01-The_Box_Tops-The_Letter.mp3"}, std::map<String, String> rfid_map{{"67 152 204 14", "01-The_Box_Tops-The_Letter.mp3"},
{"67 175 148 160", "068-Der_Schatz_im_Bergsee"}}; {"67 175 148 160", "068-Der_Schatz_im_Bergsee"}};
*/
void activateSD() void activateSD()
{ {
digitalWrite(CS_SDCARD, LOW); digitalWrite(CS_SDCARD, LOW);
@ -98,6 +103,12 @@ void deactivateRFID()
digitalWrite(CS_RFID, HIGH); digitalWrite(CS_RFID, HIGH);
} }
uint32_t getBatteryVoltageMv() {
uint32_t voltage = analogReadMilliVolts(BAT_VOLTAGE_PIN);
return voltage*2;//*2 because of the voltage divider.
}
void playSongByName(String song) void playSongByName(String song)
{ {
currentNode = rootNode.advanceToMP3(&song); currentNode = rootNode.advanceToMP3(&song);
@ -117,10 +128,14 @@ void playSongByPath(String path)
void playSongByRFID(String id) void playSongByRFID(String id)
{ {
String song = rfid_map[id]; auto songit = rfid_map.find(id);
if (songit==rfid_map.end()) {
Serial.println("song for uid not found.");
return;
}
Serial.println("searching for "); Serial.println("searching for ");
Serial.println(song); Serial.println(songit->second);
playSongByName(song); playSongByName(songit->second);
} }
/** /**
@ -200,11 +215,41 @@ String getState()
jsonState["time"] = audio.getAudioCurrentTime(); jsonState["time"] = audio.getAudioCurrentTime();
jsonState["volume"] = audio.getVolume(); jsonState["volume"] = audio.getVolume();
jsonState["length"] = audio.getAudioFileDuration(); jsonState["length"] = audio.getAudioFileDuration();
jsonState["voltage"] = lastVoltage;
jsonState["uid"] = lastUid;
String output; String output;
serializeJson(jsonState, output); serializeJson(jsonState, output);
jsonState.clear();
jsonState.garbageCollect();
return output; return output;
} }
std::map<String, String> readDataFromFile(const char *filename) {
File file = SD.open(filename);
if (file) {
while (file.available()) {
// Read key and value from the file
String line = file.readStringUntil('\n');
int separatorIndex = line.indexOf('=');
if (separatorIndex != -1) {
// Extract key and value
String key = line.substring(0, separatorIndex).c_str();
String value = line.substring(separatorIndex + 1).c_str();
// Add key-value pair to the map
rfid_map[key] = value;
}
}
file.close();
} else {
Serial.print("Error opening file ");
Serial.println(filename);
}
return rfid_map;
}
String processor(const String &var) String processor(const String &var)
{ {
if (var == "DIRECTORY") if (var == "DIRECTORY")
@ -273,56 +318,13 @@ void IRAM_ATTR rfid_interrupt()
void readRFID() void readRFID()
{ {
rfid.PICC_ReadCardSerial(); rfid.PICC_ReadCardSerial();
Serial.print("Tag UID: "); Serial.print("Card UID: ");
String uidString = getRFIDString(rfid.uid.uidByte); lastUid = getRFIDString(rfid.uid.uidByte);
Serial.println(uidString); Serial.println(lastUid);
//rfid.PICC_DumpDetailsToSerial(&(rfid.uid));
// Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. playSongByRFID(lastUid);
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
//some variables we need
byte buffer[42]; //data transfer buffer (16+2 bytes data+CRC)
byte size = sizeof(buffer);
uint8_t pageAddr = 0x05; //In this example we will write/read 16 bytes (page 6,7,8 and 9).
//Ultraligth mem = 16 pages. 4 bytes per page.
//Pages 0 to 4 are for special functions.
MFRC522::StatusCode status;
rfid.PICC_DumpDetailsToSerial(&(rfid.uid));
//------------------------------------------- GET FIRST NAME
/*
status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 4, &key, &(rfid.uid)); //line 834 of MFRC522.cpp file
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(rfid.GetStatusCodeName(status));
return;
}
Serial.println("Auth");
*/
// works only with mifare ultralight:
status = rfid.MIFARE_Read(pageAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(rfid.GetStatusCodeName(status));
return;
}
//Dump a byte array to Serial
for (byte i = 0; i < size; i++) {
Serial.write(buffer[i]);
}
Serial.println();
// ------------------------------
rfid.PICC_HaltA();
playSongByRFID(uidString);
lastInteraction = millis(); lastInteraction = millis();
} }
@ -361,6 +363,8 @@ void setup()
rootNode.buildDirectoryTree("/"); rootNode.buildDirectoryTree("/");
rootNode.printDirectoryTree(); rootNode.printDirectoryTree();
readDataFromFile(mapping_file.c_str());
deactivateSD(); deactivateSD();
activateRFID(); activateRFID();
Serial.println("RFID"); Serial.println("RFID");
@ -384,11 +388,19 @@ void setup()
Serial.println("Audio initialized."); Serial.println("Audio initialized.");
lastVoltage = getBatteryVoltageMv();
Serial.print("Battery Voltage in mV: ");
Serial.println(lastVoltage);
// first parameter is name of access point, second is the password // first parameter is name of access point, second is the password
AsyncWiFiManager wifiManager(&server, &dns); AsyncWiFiManager wifiManager(&server, &dns);
wifiManager.setTimeout(180); wifiManager.setTimeout(180);
Serial.println("Deactivating Brownout detector...");
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
if (wifiManager.autoConnect("HannaBox")) if (wifiManager.autoConnect("HannaBox"))
{ {
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
@ -438,6 +450,9 @@ void setup()
Serial.println("Wifi timed out. Fallback no Wifi."); Serial.println("Wifi timed out. Fallback no Wifi.");
} }
Serial.println("Activating Brownout detector...");
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector
xTaskCreatePinnedToCore( xTaskCreatePinnedToCore(
loop2, /* Function to implement the task */ loop2, /* Function to implement the task */
"RFIDTask", /* Name of the task */ "RFIDTask", /* Name of the task */
@ -459,7 +474,6 @@ void named_song_action(AsyncWebServerRequest *request)
for (int i = 0; i < params; i++) for (int i = 0; i < params; i++)
{ {
AsyncWebParameter *p = request->getParam(i); AsyncWebParameter *p = request->getParam(i);
Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
if (p->name() == "title") if (p->name() == "title")
{ {
playSongByName(p->value()); playSongByName(p->value());
@ -477,7 +491,6 @@ void progress_action(AsyncWebServerRequest *request)
for (int i = 0; i < params; i++) for (int i = 0; i < params; i++)
{ {
AsyncWebParameter *p = request->getParam(i); AsyncWebParameter *p = request->getParam(i);
Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
if (p->name() == "value") if (p->name() == "value")
{ {
audio.setAudioPlayPosition(atoi(p->value().c_str())); audio.setAudioPlayPosition(atoi(p->value().c_str()));
@ -495,7 +508,6 @@ void volume_action(AsyncWebServerRequest *request)
for (int i = 0; i < params; i++) for (int i = 0; i < params; i++)
{ {
AsyncWebParameter *p = request->getParam(i); AsyncWebParameter *p = request->getParam(i);
Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
if (p->name() == "value") if (p->name() == "value")
{ {
audio.setVolume(atoi(p->value().c_str())); audio.setVolume(atoi(p->value().c_str()));