#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> 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 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