Web improvements, NFC config
This commit is contained in:
parent
a3be4cbb3c
commit
578281d9d1
|
|
@ -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?
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## 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 ?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
<button onmouseup="simpleGetCall('stop');" ontouchend="simpleGetCall('stop');">Stop</button>
|
||||||
|
|
@ -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'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,8 @@ li {
|
||||||
box-shadow: -10px 0 0 0 darkgrey;
|
box-shadow: -10px 0 0 0 darkgrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
)rawliteral";
|
)rawliteral";
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
120
src/main.cpp
120
src/main.cpp
|
|
@ -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()));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue