@ -152,8 +152,8 @@ typedef struct { // Bit field 
		
	
		
			
				playProps  playProperties ; playProps  playProperties ;  
		
	
		
			
				
 
		
	
		
			
				typedef  struct  { typedef  struct  {  
		
	
		
			
				    char  * nvsKey ;  
		
	
		
			
				    char  * nvsEntry ;  
		
	
		
			
				    char  nvsKey [ 13 ] ;  
		
	
		
			
				    char  nvsEntry [ 275 ] ;  
		
	
		
			
				}  nvs_t ; }  nvs_t ;  
		
	
		
			
				
 
		
	
		
			
				// Configuration of initial values (for the first start) goes here....
 // Configuration of initial values (for the first start) goes here....
  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -187,6 +187,8 @@ char ftpPassword[15] = "esp32";                         // FTP-password 
		
	
		
			
				uint8_t  buttonDebounceInterval  =  50 ;                     // Interval in ms to software-debounce buttons
 uint8_t  buttonDebounceInterval  =  50 ;                     // Interval in ms to software-debounce buttons
  
		
	
		
			
				uint16_t  intervalToLongPress  =  700 ;                      // Interval in ms to distinguish between short and long press of previous/next-button
 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 " ;  
		
	
		
			
				
 
		
	
		
			
				// Don't change anything here unless you know what you're doing
 // Don't change anything here unless you know what you're doing
  
		
	
		
			
				// HELPER //
 // HELPER //
  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -251,6 +253,7 @@ char mqtt_server[16] = "192.168.2.43";                  // IP-address of MQTT-se 
		
	
		
			
				# endif 
 # endif 
  
		
	
		
			
				
 
		
	
		
			
				char  stringDelimiter [ ]  =  " # " ;                                // Character used to encapsulate data in linear NVS-strings (don't change)
 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 )  { void  notFound ( AsyncWebServerRequest  * request )  {  
		
	
		
			
				    request - > send ( 404 ,  " text/plain " ,  " Not found " ) ;     request - > send ( 404 ,  " text/plain " ,  " Not found " ) ;  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -387,7 +390,7 @@ int countChars(const char* string, char ch) { 
		
	
		
			
				
 
		
	
		
			
				
 
		
	
		
			
				// Used to print content of sd-card (currently not used, maybe later :-))
 // Used to print content of sd-card (currently not used, maybe later :-))
  
		
	
		
			
				void  printSdContent ( File  dir ,  uint16_t  allocSize ,  uint8_t  allocCount ,  char  * sdContent ,  uint8_t  depth )  {  
		
	
		
			
				/*void printSdContent(File dir, uint16_t allocSize, uint8_t allocCount, char *sdContent, uint8_t depth) {
  
		
	
		
			
				    while  ( true )  {     while  ( true )  {  
		
	
		
			
				        File  entry  =  dir . openNextFile ( ) ;         File  entry  =  dir . openNextFile ( ) ;  
		
	
		
			
				        if  ( ! entry )  {         if  ( ! entry )  {  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -417,7 +420,7 @@ void printSdContent(File dir, uint16_t allocSize, uint8_t allocCount, char *sdCo 
		
	
		
			
				        }         }  
		
	
		
			
				        entry . close ( ) ;         entry . close ( ) ;  
		
	
		
			
				    }     }  
		
	
		
			
				}  
		
	
		
			
				} */  
		
	
		
			
				
 
		
	
		
			
				void  IRAM_ATTR  onTimer ( )  { void  IRAM_ATTR  onTimer ( )  {  
		
	
		
			
				  xSemaphoreGiveFromISR ( timerSemaphore ,  NULL ) ;   xSemaphoreGiveFromISR ( timerSemaphore ,  NULL ) ;  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -1145,12 +1148,13 @@ void playAudio(void *parameter) { 
		
	
		
			
				                }                 }  
		
	
		
			
				                if  ( playProperties . saveLastPlayPosition )  {      // Don't save for AUDIOBOOK_LOOP because not necessary
                 if  ( playProperties . saveLastPlayPosition )  {      // Don't save for AUDIOBOOK_LOOP because not necessary
  
		
	
		
			
				                    if  ( playProperties . currentTrackNumber  +  1  <  playProperties . numberOfTracks )  {                     if  ( playProperties . currentTrackNumber  +  1  <  playProperties . numberOfTracks )  {  
		
	
		
			
				                        // Only save if there's another track, otherwise it will be saved at end of playlist
  
		
	
		
			
				                        // Only save if there's another track, otherwise it will be saved at end of playlist anyway 
  
		
	
		
			
				                        nvsRfidWriteWrapper ( playProperties . playRfidTag ,  * ( playProperties . playlist  +  playProperties . currentTrackNumber ) ,  0 ,  playProperties . playMode ,  playProperties . currentTrackNumber + 1 ,  playProperties . numberOfTracks ) ;                         nvsRfidWriteWrapper ( playProperties . playRfidTag ,  * ( playProperties . playlist  +  playProperties . currentTrackNumber ) ,  0 ,  playProperties . playMode ,  playProperties . currentTrackNumber + 1 ,  playProperties . numberOfTracks ) ;  
		
	
		
			
				                    }                     }  
		
	
		
			
				                }                 }  
		
	
		
			
				                if  ( playProperties . sleepAfterCurrentTrack )  {   // Go to sleep if "sleep after track" was requested
                 if  ( playProperties . sleepAfterCurrentTrack )  {   // Go to sleep if "sleep after track" was requested
  
		
	
		
			
				                    gotoSleep  =  true ;                     gotoSleep  =  true ;  
		
	
		
			
				                    break ;  
		
	
		
			
				                }                 }  
		
	
		
			
				                if  ( ! playProperties . repeatCurrentTrack )  {    // If endless-loop requested, track-number will not be incremented
                 if  ( ! playProperties . repeatCurrentTrack )  {    // If endless-loop requested, track-number will not be incremented
  
		
	
		
			
				                    playProperties . currentTrackNumber + + ;                     playProperties . currentTrackNumber + + ;  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -1795,6 +1799,7 @@ void deepSleepManager(void) { 
		
	
		
			
				        # endif 
         # endif 
  
		
	
		
			
				        SPI . end ( ) ;         SPI . end ( ) ;  
		
	
		
			
				        spiSD . end ( ) ;         spiSD . end ( ) ;  
		
	
		
			
				        digitalWrite ( POWER ,  LOW ) ;  
		
	
		
			
				        delay ( 200 ) ;         delay ( 200 ) ;  
		
	
		
			
				        esp_deep_sleep_start ( ) ;         esp_deep_sleep_start ( ) ;  
		
	
		
			
				    }     }  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -2628,7 +2633,7 @@ bool processJsonRequest(char *_serialJson) { 
		
	
		
			
				        if  ( s . compareTo ( rfidString ) )  {         if  ( s . compareTo ( rfidString ) )  {  
		
	
		
			
				            return  false ;             return  false ;  
		
	
		
			
				        }         }  
		
	
		
			
				        dumpNvsToSd ( " rfidTags " ,  " /backup.txt " ) ;  
		
	
		
			
				        dumpNvsToSd ( " rfidTags " ,  ( char  * )  FPSTR ( backupFile ) ) ;         // Store backup-file every time when a new rfid-tag is programmed
   
		
	
		
			
				
 
		
	
		
			
				    }  else  if  ( doc . containsKey ( " rfidAssign " ) )  {     }  else  if  ( doc . containsKey ( " rfidAssign " ) )  {  
		
	
		
			
				        const  char  * _rfidIdAssinId  =  object [ " rfidAssign " ] [ " rfidIdMusic " ] ;         const  char  * _rfidIdAssinId  =  object [ " rfidAssign " ] [ " rfidIdMusic " ] ;  
		
	
	
		
			
				
					
					
					
						
							 
					
				 
				@ -2644,7 +2649,7 @@ bool processJsonRequest(char *_serialJson) { 
		
	
		
			
				        if  ( s . compareTo ( rfidString ) )  {         if  ( s . compareTo ( rfidString ) )  {  
		
	
		
			
				            return  false ;             return  false ;  
		
	
		
			
				        }         }  
		
	
		
			
				        dumpNvsToSd ( " rfidTags " ,  " /backup.txt " ) ;  
		
	
		
			
				        dumpNvsToSd ( " rfidTags " ,  ( char  * )  FPSTR ( backupFile ) ) ;                      // Store backup-file every time when a new rfid-tag is programmed
   
		
	
		
			
				
 
		
	
		
			
				    }  else  if  ( doc . containsKey ( " wifiConfig " ) )  {     }  else  if  ( doc . containsKey ( " wifiConfig " ) )  {  
		
	
		
			
				        const  char  * _ssid  =  object [ " wifiConfig " ] [ " ssid " ] ;         const  char  * _ssid  =  object [ " wifiConfig " ] [ " ssid " ] ;  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -2805,7 +2810,7 @@ bool dumpNvsToSd(char *_namespace, char *_destFile) { 
		
	
		
			
				                    ( buf . Entry [ i ] . Ns  = =  namespace_ID ) )  {            // otherwise just my namespace
                     ( buf . Entry [ i ] . Ns  = =  namespace_ID ) )  {            // otherwise just my namespace
  
		
	
		
			
				                    if  ( isNumber ( buf . Entry [ i ] . Key ) )  {                     if  ( isNumber ( buf . Entry [ i ] . Key ) )  {  
		
	
		
			
				                        String  s  =  prefsRfid . getString ( ( const  char  * ) buf . Entry [ i ] . Key ) ;                         String  s  =  prefsRfid . getString ( ( const  char  * ) buf . Entry [ i ] . Key ) ;  
		
	
		
			
				                        backupFile . printf ( " %s^ %s \n " ,  buf . Entry [ i ] . Key ,  s . c_str ( ) ) ;  
		
	
		
			
				                        backupFile . printf ( " %s%s%s %s \n " ,  stringOuterDelimiter ,  buf . Entry [ i ] . Key ,  stringOuterDelimiter ,  s . c_str ( ) ) ;  
		
	
		
			
				                    }                     }  
		
	
		
			
				                }                 }  
		
	
		
			
				                i  + =  buf . Entry [ i ] . Span ;                               // Next entry
                 i  + =  buf . Entry [ i ] . Span ;                               // Next entry
  
		
	
	
		
			
				
					
					
					
						
							 
					
				 
				@ -2822,6 +2827,60 @@ bool dumpNvsToSd(char *_namespace, char *_destFile) { 
		
	
		
			
				} }  
		
	
		
			
				
 
		
	
		
			
				
 
		
	
		
			
				// Handles uploaded files. Still in progress...
  
		
	
		
			
				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 ;  
		
	
		
			
				    uint8_t  count = 0 ;  
		
	
		
			
				    nvs_t  nvsEntry [ 1 ] ;  
		
	
		
			
				
 
		
	
		
			
				    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 ;  
		
	
		
			
				            token  =  strtok ( ebuf ,  stringOuterDelimiter ) ;  
		
	
		
			
				            while  ( token  ! =  NULL )  {  
		
	
		
			
				                if  ( ! count )  {  
		
	
		
			
				                    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 " ) ;  
		
	
		
			
				            } */  
		
	
		
			
				        }  
		
	
		
			
				    }  
		
	
		
			
				}  
		
	
		
			
				
 
		
	
		
			
				
 
		
	
		
			
				void  setup ( )  { void  setup ( )  {  
		
	
		
			
				    Serial . begin ( 115200 ) ;     Serial . begin ( 115200 ) ;  
		
	
		
			
				    srand ( esp_random ( ) ) ;     srand ( esp_random ( ) ) ;  
		
	
	
		
			
				
					
						
							 
					
					
						
							 
					
					
				 
				@ -3061,14 +3120,21 @@ void setup() { 
		
	
		
			
				    lastTimeActiveTimestamp  =  millis ( ) ;      // initial set after boot
     lastTimeActiveTimestamp  =  millis ( ) ;      // initial set after boot
  
		
	
		
			
				
 
		
	
		
			
				    if  ( wifiManager ( )  = =  WL_CONNECTED )  {     if  ( wifiManager ( )  = =  WL_CONNECTED )  {  
		
	
		
			
				        // Webs ocket for Mgmt-Interface
  
		
	
		
			
				        // attach AsyncWebS ocket for Mgmt-Interface
  
		
	
		
			
				        ws . onEvent ( onWebsocketEvent ) ;         ws . onEvent ( onWebsocketEvent ) ;  
		
	
		
			
				        wServer . addHandler ( & ws ) ;         wServer . addHandler ( & ws ) ;  
		
	
		
			
				
 
		
	
		
			
				        // attach AsyncEventSource
  
		
	
		
			
				        wServer . addHandler ( & events ) ;  
		
	
		
			
				
 
		
	
		
			
				        wServer . on ( " / " ,  HTTP_GET ,  [ ] ( AsyncWebServerRequest  * request )  {         wServer . on ( " / " ,  HTTP_GET ,  [ ] ( AsyncWebServerRequest  * request )  {  
		
	
		
			
				            request - > send_P ( 200 ,  " text/html " ,  mgtWebsite ,  templateProcessor ) ;             request - > send_P ( 200 ,  " text/html " ,  mgtWebsite ,  templateProcessor ) ;  
		
	
		
			
				        } ) ;         } ) ;  
		
	
		
			
				
 
		
	
		
			
				        wServer . on ( " /upload " ,  HTTP_POST ,  [ ] ( AsyncWebServerRequest  * request ) {  
		
	
		
			
				            request - > send ( 200 ) ;  
		
	
		
			
				        } ,  handleUpload ) ;  
		
	
		
			
				
 
		
	
		
			
				        wServer . on ( " /restart " ,  HTTP_GET ,  [ ]  ( AsyncWebServerRequest  * request )  {         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ückkehren</a>.</p> " ) ;             request - > send ( 200 ,  " text/html " ,  " <p>Der Tonuino wird neu gestartet...<br />Zur letzten Seite <a href= \" javascript:history.back() \" >zurückkehren</a>.</p> " ) ;  
		
	
		
			
				            Serial . flush ( ) ;             Serial . flush ( ) ;