219 lines
5.2 KiB
C++
219 lines
5.2 KiB
C++
#ifndef MAIN_H_
|
|
#define MAIN_H_
|
|
|
|
// define pins for RFID
|
|
#define CS_RFID 32 // SIC, tried 4 and 32 but only this worked!
|
|
#define RST_RFID 33
|
|
// this does not work as the irq pin is input only:
|
|
#define IRQ_RFID 34
|
|
|
|
// Audio DAC
|
|
#define I2S_DOUT 26 // connect to DAC pin DIN
|
|
#define I2S_BCLK 27 // connect to DAC pin BCK
|
|
#define I2S_LRC 25 // connect to DAC pin LCK
|
|
|
|
#define BTN_START_STOP 4 // Button on XX and GND
|
|
#define BTN_NEXT 17
|
|
#define BTN_PREV 16
|
|
|
|
#define CS_SDCARD 22
|
|
|
|
#define BAT_VOLTAGE_PIN 35
|
|
|
|
#define RFID_LOOP_INTERVAL 25
|
|
|
|
#define VOLTAGE_LOOP_INTERVAL 5000
|
|
|
|
#define VOLTAGE_THRESHOLD 0
|
|
|
|
#define SHORT_PRESS_TIME 250
|
|
|
|
#define LONG_PRESS_TIME 1000
|
|
|
|
#define MAX_WEBREQUEST_BLOCKINGS 10000
|
|
|
|
#define MAX_VOL 15
|
|
|
|
File root;
|
|
File mp3File;
|
|
|
|
Audio audio;
|
|
|
|
uint volume = 7;
|
|
|
|
// Folder-play tracking: flattened list of files inside a mapped folder and current index
|
|
// Used when a mapping targets a folder (play folder once or loop folder)
|
|
static std::vector<std::pair<DirectoryNode *, int>> folderFlatList;
|
|
static int folderFlatIndex = -1;
|
|
static String folderRootPath = "";
|
|
// Pointer to the root DirectoryNode for active folder-mode playback
|
|
DirectoryNode *folderRootNode = nullptr;
|
|
|
|
AsyncWebServer server(80);
|
|
DNSServer dns;
|
|
|
|
// static variable has to be instantiated outside of class definition:
|
|
uint16_t DirectoryNode::idCounter = 0;
|
|
|
|
DirectoryNode rootNode("/");
|
|
DirectoryNode *currentNode = nullptr;
|
|
|
|
volatile bool newRfidInt = false;
|
|
volatile bool playButtonDown = false;
|
|
// Track if play button hold is active and if volume was adjusted during this hold
|
|
volatile bool playHoldActive = false;
|
|
volatile bool volumeAdjustedDuringHold = false;
|
|
volatile uint8_t sd_lock_flag = 0;
|
|
|
|
MFRC522 rfid(CS_RFID, RST_RFID); // instatiate a MFRC522 reader object.
|
|
|
|
TaskHandle_t RfidTask;
|
|
|
|
bool asyncStop = false;
|
|
|
|
bool asyncStart = false;
|
|
|
|
bool asyncTogglePlayPause = false;
|
|
|
|
bool asyncNext = false;
|
|
|
|
bool asyncPrev = false;
|
|
|
|
bool SDActive = false;
|
|
|
|
bool RFIDActive = false;
|
|
|
|
|
|
// Web request concurrency counter and helpers (atomic via GCC builtins)
|
|
volatile uint32_t webreq_cnt = 0;
|
|
static inline void webreq_enter() { __sync_add_and_fetch(&webreq_cnt, 1); }
|
|
static inline void webreq_exit() { __sync_sub_and_fetch(&webreq_cnt, 1); }
|
|
|
|
uint16_t voltage_threshold_counter = 0;
|
|
|
|
size_t free_heap = 0;
|
|
|
|
void stop();
|
|
|
|
void start();
|
|
|
|
bool playFile(const char* filename, uint32_t resumeFilePos = 0);
|
|
|
|
void loop2(void* parameter);
|
|
|
|
void id_song_action(AsyncWebServerRequest *request);
|
|
|
|
void progress_action(AsyncWebServerRequest *request);
|
|
|
|
void volume_action(AsyncWebServerRequest *request);
|
|
|
|
void init_webserver();
|
|
|
|
boolean buttonPressed(const uint8_t pin);
|
|
|
|
const String getSysDir(const String filename);
|
|
|
|
/**
|
|
* Helper routine to dump a byte array as hex values to Serial.
|
|
*/
|
|
void dump_byte_array(byte *buffer, byte bufferSize)
|
|
{
|
|
for (byte i = 0; i < bufferSize; i++)
|
|
{
|
|
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
|
|
Serial.print(buffer[i], HEX);
|
|
}
|
|
}
|
|
|
|
/* Simple spinlock using older GCC sync builtins (no libatomic required).
|
|
sd_lock_acquire() will block (with a small delay) until the lock is free.
|
|
sd_lock_release() releases the lock. This is sufficient for short SD ops. */
|
|
static inline void sd_lock_acquire()
|
|
{
|
|
while (__sync_lock_test_and_set(&sd_lock_flag, 1))
|
|
{
|
|
delay(1);
|
|
}
|
|
}
|
|
|
|
static inline void sd_lock_release()
|
|
{
|
|
__sync_lock_release(&sd_lock_flag);
|
|
}
|
|
|
|
|
|
String getRFIDString(byte uidByte[10])
|
|
{
|
|
String uidString = String(uidByte[0]) + " " + String(uidByte[1]) + " " +
|
|
String(uidByte[2]) + " " + String(uidByte[3]);
|
|
return uidString;
|
|
}
|
|
|
|
void writeFile(fs::FS &fs, const char * path, const char * message){
|
|
Serial.printf("Writing file: %s\n", path);
|
|
|
|
File file = fs.open(path, FILE_WRITE);
|
|
if(!file){
|
|
Serial.println("Failed to open file for writing");
|
|
return;
|
|
}
|
|
if(file.print(message)){
|
|
Serial.println("File written");
|
|
} else {
|
|
Serial.println("Write failed");
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
unsigned long lastStart = 0;
|
|
|
|
unsigned long lastInteraction = 0;
|
|
|
|
boolean sleepSoundPlayed = false;
|
|
|
|
boolean startupSoundPlayed = false;
|
|
|
|
boolean continuousMode = false;
|
|
|
|
uint8_t buttontoignore = 0;
|
|
|
|
uint32_t lastVoltage = 0;
|
|
|
|
uint loopCounter = 0;
|
|
|
|
String lastUid = "";
|
|
|
|
uint16_t currentSongId = 0;
|
|
|
|
uint32_t currentSongSeconds = 0;
|
|
|
|
boolean continuePlaying = false;
|
|
|
|
boolean prepareSleepMode = false;
|
|
|
|
class DirectoryNode;
|
|
|
|
/* Mapping entry that stores target (file or folder) and playback mode:
|
|
* 's' = single (default) - play only the selected song (or single file in folder)
|
|
* 'f' = folder - play files inside the selected folder, then stop
|
|
* 'r' = random-folder - play files inside the selected folder in random order, then stop
|
|
* 'c' = continuous - continuously play (like previous continuousMode)
|
|
*/
|
|
struct MappingEntry {
|
|
String target;
|
|
char mode;
|
|
MappingEntry() : target(""), mode('s') {}
|
|
MappingEntry(const String& t, char m) : target(t), mode(m) {}
|
|
};
|
|
|
|
std::map<String, MappingEntry> rfid_map;
|
|
|
|
// Folder-play helper: when a mapping requests "folder only" playback we keep
|
|
// track of the folder root node so EOF handling can advance only inside that folder.
|
|
bool folderModeActive = false;
|
|
|
|
bool pendingSeek = false;
|
|
uint32_t pendingSeekSeconds = 0;
|
|
|
|
#endif
|