dfplayer_async/
lib.rs

1//! A no_std async library for interfacing with DFPlayer Mini MP3 modules
2//!
3//! This crate provides an async interface to control DFPlayer Mini MP3 modules
4//! using embedded-hal-async compatible serial interfaces. It handles the binary
5//! protocol, error checking, and timeout management required when communicating
6//! with these devices.
7//!
8//! ## Features
9//!
10//! - Async/await API for embedded systems
11//! - Full command support for DFPlayer Mini and compatible modules
12//! - Proper error handling and timeout management
13//! - no_std compatible
14//! - Robust initialization sequence with fallback mechanisms
15//! - Efficient non-blocking I/O patterns for embedded environments
16//!
17//! ## Example
18//!
19//! ```rust,no_run
20//! use dfplayer_async::{DfPlayer, PlayBackSource, TimeSource};
21//! use embassy_time::{Duration, Instant, Delay};
22//!
23//! // Define a time source for the DFPlayer
24//! struct MyTimeSource;
25//! impl TimeSource for MyTimeSource {
26//!     type Instant = Instant;
27//!     fn now(&self) -> Self::Instant { Instant::now() }
28//!     fn is_elapsed(&self, since: Self::Instant, timeout_ms: u64) -> bool {
29//!         Instant::now().duration_since(since) >= Duration::from_millis(timeout_ms)
30//!     }
31//! }
32//!
33//! // In your async function:
34//! async fn example(mut uart: impl embedded_io_async::Read + embedded_io_async::Write + embedded_io_async::ReadReady) {
35//!     let mut dfplayer = DfPlayer::try_new(
36//!         &mut uart,      // UART port (9600 baud, 8N1)
37//!         false,          // feedback_enable
38//!         1000,           // timeout_ms
39//!         MyTimeSource,   // time source
40//!         Delay,          // delay provider
41//!         None,           // reset_duration_override
42//!     ).await.expect("Failed to initialize DFPlayer");
43//!
44//!     // Play first track
45//!     dfplayer.play(1).await.expect("Failed to play track");
46//! }
47//! ```
48//!
49//! This crate optionally supports logging via the defmt framework.
50//! Enable the "defmt" feature to activate logging.
51
52#![no_std]
53
54use embedded_hal_async::delay::DelayNs;
55use embedded_io_async::{Read, ReadReady, Write};
56
57#[cfg(feature = "defmt")]
58use defmt::{info, Debug2Format};
59
60// Protocol constants
61const START_BYTE: u8 = 0x7E;
62const END_BYTE: u8 = 0xEF;
63const VERSION: u8 = 0xFF;
64const MSG_LEN: u8 = 0x06;
65const DATA_FRAME_SIZE: usize = 10;
66
67// Message byte indices
68const INDEX_VERSION: usize = 1;
69const INDEX_CMD: usize = 3;
70const INDEX_FEEDBACK_ENABLE: usize = 4;
71const INDEX_PARAM_H: usize = 5;
72const INDEX_PARAM_L: usize = 6;
73const INDEX_CHECKSUM_H: usize = 7;
74const INDEX_CHECKSUM_L: usize = 8;
75
76/// Minimal time provider trait for timeout tracking. Implement this for your platform.
77pub trait TimeSource {
78    /// Monotonic time point type
79    type Instant: Copy + Clone + PartialEq + PartialOrd;
80
81    /// Get the current time
82    fn now(&self) -> Self::Instant;
83
84    /// Check if a timeout has occurred
85    fn is_elapsed(&self, since: Self::Instant, timeout_ms: u64) -> bool;
86}
87
88/// Represents available media sources on the DFPlayer
89#[derive(Debug, Clone, Copy)]
90#[repr(u8)]
91#[cfg_attr(feature = "defmt", derive(defmt::Format))]
92pub enum Source {
93    /// Internal USB flash storage
94    USBFlash = 0b001,
95    /// SD card inserted in the module
96    SDCard = 0b010,
97    /// External USB device connected to the module
98    USBHost = 0b100,
99}
100
101impl TryFrom<u8> for Source {
102    type Error = ();
103    fn try_from(value: u8) -> Result<Self, Self::Error> {
104        match value {
105            0b001 => Ok(Source::USBFlash),
106            0b010 => Ok(Source::SDCard),
107            0b100 => Ok(Source::USBHost),
108            _ => Err(()),
109        }
110    }
111}
112
113/// Error codes reported by the DFPlayer module
114#[derive(Debug, Clone, Copy)]
115#[repr(u8)]
116#[cfg_attr(feature = "defmt", derive(defmt::Format))]
117pub enum ModuleError {
118    /// Module is currently busy
119    Busy = 1,
120    /// Module is in sleep mode
121    Sleeping = 2,
122    /// Serial receive error occurred
123    SerialRxError = 3,
124    /// Checksum validation failed
125    Checksum = 4,
126    /// Requested track is out of valid range
127    TrackNotInScope = 5,
128    /// Track was not found on the media
129    TrackNotFound = 6,
130    /// Error inserting file/track
131    InsertionError = 7,
132    /// Module entering sleep mode
133    EnterSleep = 8,
134}
135
136impl TryFrom<u8> for ModuleError {
137    type Error = ();
138    fn try_from(value: u8) -> Result<Self, Self::Error> {
139        match value {
140            1 => Ok(ModuleError::Busy),
141            2 => Ok(ModuleError::Sleeping),
142            3 => Ok(ModuleError::SerialRxError),
143            4 => Ok(ModuleError::Checksum),
144            5 => Ok(ModuleError::TrackNotInScope),
145            6 => Ok(ModuleError::TrackNotFound),
146            7 => Ok(ModuleError::InsertionError),
147            8 => Ok(ModuleError::EnterSleep),
148            _ => Err(()),
149        }
150    }
151}
152
153/// Errors that can occur when operating the DFPlayer
154#[derive(Debug, Clone, Copy)]
155#[cfg_attr(feature = "defmt", derive(defmt::Format))]
156pub enum Error<SerialError> {
157    /// Initialization failed
158    Init,
159    /// Serial port communication error
160    SerialPort(SerialError),
161    /// Delay implementation failed
162    DelayError,
163    /// Command was not acknowledged when feedback was enabled
164    FailedAck,
165    /// Connection to the module was lost
166    Connection,
167    /// Error reported by the module itself
168    ModuleError(ModuleError),
169    /// Unknown error occurred
170    Unknown,
171    /// Received message was corrupted or incomplete
172    BrokenMessage,
173    /// Operation timed out
174    UserTimeout,
175    /// Command parameter was invalid
176    BadParameter,
177}
178
179/// Data structure representing a message to/from the DFPlayer
180#[derive(PartialEq, Debug, Clone, Copy)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182pub struct MessageData {
183    command: Command,
184    param_h: u8,
185    param_l: u8,
186}
187
188impl MessageData {
189    /// Create a new message with the specified command and parameters
190    pub const fn new(command: Command, param_h: u8, param_l: u8) -> Self {
191        Self {
192            command,
193            param_h,
194            param_l,
195        }
196    }
197}
198
199/// Message used for acknowledging commands
200const ACK_MESSAGE_DATA: MessageData =
201    MessageData::new(Command::NotifyReply, 0, 0);
202
203/// Commands supported by the DFPlayer module
204#[repr(u8)]
205#[derive(PartialEq, Debug, Clone, Copy)]
206#[cfg_attr(feature = "defmt", derive(defmt::Format))]
207pub enum Command {
208    /// Play next file
209    Next = 0x01,
210    /// Play previous file
211    Previous = 0x02,
212    /// Specify Track number to play (1-2999)
213    PlayTrack = 0x03,
214    /// Increase Volume
215    VolumeUp = 0x04,
216    /// Decrease Volume
217    VolumeDown = 0x05,
218    /// Set specific volume value, 0-30
219    SetVolume = 0x06,
220    /// Select equalizer
221    SetEQ = 0x07,
222    /// Select track to play in loop
223    PlayLoopTrack = 0x08,
224    /// Select the Playback source (USDB/SD)
225    SetPlaybackSource = 0x09,
226    /// Enter Sleep/StandBy Mode
227    EnterSleepMode = 0x0A,
228    /// Normal mode per DFRobot, but it's reported it does nothing
229    EnterNormalMode = 0x0B,
230    /// Reset the device
231    Reset = 0x0C,
232    /// Start Playback
233    Play = 0x0D,
234    /// Pause Current Playback
235    Pause = 0x0E,
236    /// Specify Track to play in a folder, 99 folders 255 tracks each max
237    PlayTrackInFolder = 0x0F,
238    /// Configure the audio amplifier gain settings, MSB enables amp, LS sets
239    /// gain 0-31
240    ConfigAudioAmp = 0x10,
241    /// Play all tracks in a loop
242    PlayLoopAll = 0x11,
243    /// Play track number in MP3 folder, max 65536 tracks, 3000 recommended max
244    PlayTrackInMp3Folder = 0x12,
245    /// Play track number in ADVERT folder, max 3000 tracks
246    StartAdvertisement = 0x13,
247    /// Play track of large folder, 1-3000 valid names
248    PlayTrackLargeFolder = 0x14,
249    /// Stop playing ADVERT track if one is active
250    StopAdvertisement = 0x15,
251    /// Stop all playback including advertisement
252    Stop = 0x16,
253    /// Play tracks from a folder on repeat, max 99 folders, 255 files each
254    PlayLoopFolder = 0x17,
255    /// Play random tracks from all media available in the current source
256    PlayRandom = 0x18,
257    /// If a track is playing, control loop playback enable (0x1 enable, 0x0 disable)
258    LoopCurrentTrack = 0x19,
259    /// Control whether the DAC is powered on or off
260    SetDAC = 0x1a,
261    /// Only sent by module when media is connected
262    NotifyPushMedia = 0x3A,
263    /// Only sent by module when media is removed
264    NotifyPullOutMedia = 0x3B,
265    /// Only sent by module when track in USB Flash finished playing
266    NotifyFinishTrackUSBFlash = 0x3C,
267    /// Only sent by module when track in SD card finished playing
268    NotifyFinishTrackSD = 0x3D,
269    /// Only sent by module when track in USB Host link stopped playing
270    NotifyFinishTrackUSBHost = 0x3E,
271    /// List the available sources. For the DFPlayer Mini, it's essentially
272    /// only the SD card
273    QueryAvailableSources = 0x3F,
274    /// Only sent by module when an error occurs
275    NotifyError = 0x40,
276    /// Sent as ACK response when feedback is enabled
277    NotifyReply = 0x41,
278    /// Returns status of module
279    QueryStatus = 0x42,
280    /// Returns current volume setting
281    QueryVolume = 0x43,
282    /// Returns current EQ setting
283    QueryEQ = 0x44,
284    /// Returns current playback mode
285    ReservedQueryPlaybackMode = 0x45,
286    /// Returns software version
287    ReservedQuerySwVersion = 0x46,
288    /// Returns number of tracks on USB storage
289    QueryTrackCntUSB = 0x47,
290    /// Returns number of tracks on SD card
291    QueryTrackCntSD = 0x48,
292    /// Returns number of tracks on PC
293    ReservedQueryTrackCntPC = 0x49,
294    /// Query the keep-on setting
295    ReservedQueryKeepOn = 0x4A,
296    /// Returns current track number on USB flash
297    QueryCurrentTrackUSBFlash = 0x4B,
298    /// Returns current track number on SD card
299    QueryCurrentTrackSD = 0x4C,
300    /// Returns current track number on USB host
301    QueryCurrentTrackUSBHost = 0x4D,
302    /// Returns number of tracks in current folder
303    QueryFolderTrackCnt = 0x4E,
304    /// Returns number of folders available
305    QueryFolderCnt = 0x4F,
306}
307
308impl TryFrom<u8> for Command {
309    type Error = ();
310    fn try_from(value: u8) -> Result<Self, Self::Error> {
311        match value {
312            0x01 => Ok(Command::Next),
313            0x02 => Ok(Command::Previous),
314            0x03 => Ok(Command::PlayTrack),
315            0x04 => Ok(Command::VolumeUp),
316            0x05 => Ok(Command::VolumeDown),
317            0x06 => Ok(Command::SetVolume),
318            0x07 => Ok(Command::SetEQ),
319            0x08 => Ok(Command::PlayLoopTrack),
320            0x09 => Ok(Command::SetPlaybackSource),
321            0x0A => Ok(Command::EnterSleepMode),
322            0x0B => Ok(Command::EnterNormalMode),
323            0x0C => Ok(Command::Reset),
324            0x0D => Ok(Command::Play),
325            0x0E => Ok(Command::Pause),
326            0x0F => Ok(Command::PlayTrackInFolder),
327            0x10 => Ok(Command::ConfigAudioAmp),
328            0x11 => Ok(Command::PlayLoopAll),
329            0x12 => Ok(Command::PlayTrackInMp3Folder),
330            0x13 => Ok(Command::StartAdvertisement),
331            0x14 => Ok(Command::PlayTrackLargeFolder),
332            0x15 => Ok(Command::StopAdvertisement),
333            0x16 => Ok(Command::Stop),
334            0x17 => Ok(Command::PlayLoopFolder),
335            0x18 => Ok(Command::PlayRandom),
336            0x19 => Ok(Command::LoopCurrentTrack),
337            0x1A => Ok(Command::SetDAC),
338            0x3A => Ok(Command::NotifyPushMedia),
339            0x3B => Ok(Command::NotifyPullOutMedia),
340            0x3C => Ok(Command::NotifyFinishTrackUSBFlash),
341            0x3D => Ok(Command::NotifyFinishTrackSD),
342            0x3E => Ok(Command::NotifyFinishTrackUSBHost),
343            0x3F => Ok(Command::QueryAvailableSources),
344            0x40 => Ok(Command::NotifyError),
345            0x41 => Ok(Command::NotifyReply),
346            0x42 => Ok(Command::QueryStatus),
347            0x43 => Ok(Command::QueryVolume),
348            0x44 => Ok(Command::QueryEQ),
349            0x45 => Ok(Command::ReservedQueryPlaybackMode),
350            0x46 => Ok(Command::ReservedQuerySwVersion),
351            0x47 => Ok(Command::QueryTrackCntUSB),
352            0x48 => Ok(Command::QueryTrackCntSD),
353            0x49 => Ok(Command::ReservedQueryTrackCntPC),
354            0x4A => Ok(Command::ReservedQueryKeepOn),
355            0x4B => Ok(Command::QueryCurrentTrackUSBFlash),
356            0x4C => Ok(Command::QueryCurrentTrackSD),
357            0x4D => Ok(Command::QueryCurrentTrackUSBHost),
358            0x4E => Ok(Command::QueryFolderTrackCnt),
359            0x4F => Ok(Command::QueryFolderCnt),
360            _ => Err(()),
361        }
362    }
363}
364
365/// Equalizer settings available on the DFPlayer
366#[repr(u8)]
367#[derive(Clone, Copy, Debug)]
368#[cfg_attr(feature = "defmt", derive(defmt::Format))]
369pub enum Equalizer {
370    /// Normal (flat) equalizer setting
371    Normal = 0x0,
372    /// Pop music equalizer preset
373    Pop = 0x1,
374    /// Rock music equalizer preset
375    Rock = 0x2,
376    /// Jazz music equalizer preset
377    Jazz = 0x3,
378    /// Classical music equalizer preset
379    Classic = 0x4,
380    /// Bass boost equalizer preset
381    Bass = 0x5,
382}
383
384/// Playback modes supported by the DFPlayer
385#[repr(u8)]
386#[derive(Clone, Copy, Debug)]
387#[cfg_attr(feature = "defmt", derive(defmt::Format))]
388pub enum PlayBackMode {
389    /// Repeat all tracks
390    Repeat = 0x0,
391    /// Repeat tracks in current folder
392    FolderRepeat = 0x1,
393    /// Repeat single track
394    SingleRepeat = 0x2,
395    /// Play tracks in random order
396    Random = 0x3,
397}
398
399/// Media sources supported by the DFPlayer
400#[repr(u8)]
401#[derive(Clone, Copy, Debug)]
402#[cfg_attr(feature = "defmt", derive(defmt::Format))]
403pub enum PlayBackSource {
404    /// USB storage device
405    USB = 0x0,
406    /// SD card
407    SDCard = 0x1,
408    /// Auxiliary input
409    Aux = 0x2,
410    /// Sleep mode (no source)
411    Sleep = 0x3,
412    /// Flash memory
413    Flash = 0x4,
414}
415
416/// Calculate the checksum for a DFPlayer message
417///
418/// The checksum is calculated by summing all bytes from version to parameters
419/// and then taking the two's complement of the sum.
420pub fn checksum(buffer: &[u8]) -> u16 {
421    let mut checksum = 0;
422    for &b in buffer {
423        checksum += u16::from(b);
424    }
425    if buffer[2] == 0x0 {
426        checksum += 2
427    };
428    0u16.wrapping_sub(checksum)
429}
430
431/// Main driver for interfacing with DFPlayer Mini modules
432pub struct DfPlayer<'a, S, T, D>
433where
434    S: Read + Write + ReadReady,
435    T: TimeSource,
436    D: DelayNs,
437{
438    port: &'a mut S,
439    feedback_enable: bool,
440    last_command: MessageData,
441    last_response: MessageData,
442    last_cmd_acknowledged: bool,
443    timeout_ms: u64,
444    time_source: T,
445    delay: D,
446}
447
448/// Structure for interacting with the device
449impl<'a, S, T, D> DfPlayer<'a, S, T, D>
450where
451    S: Read + Write + ReadReady,
452    T: TimeSource,
453    D: DelayNs,
454{
455    /// Create a new DFPlayer interface
456    ///
457    /// This initializes the driver and performs a robust startup sequence for the DFPlayer module.
458    /// The serial port must be configured with 9600 baud, 8N1 format before calling this function.
459    ///
460    /// The initialization sequence:
461    /// 1. Clears any pending data in the receive buffer
462    /// 2. Sends a reset command and waits for the device to restart
463    /// 3. Configures SD card as the default media source
464    /// 4. Sets the volume to a moderate level (15 out of 30)
465    ///
466    /// The function will attempt to continue even if certain initialization steps fail,
467    /// making it more resilient to communication issues common with these modules.
468    ///
469    /// # Arguments
470    /// * `port` - Serial port connected to the DFPlayer module
471    /// * `feedback_enable` - Whether to enable command acknowledgement (set to false if you're having reliability issues)
472    /// * `timeout_ms` - Timeout for operations in milliseconds
473    /// * `time_source` - Source of time for timeout tracking
474    /// * `delay` - Delay provider for timing operations
475    /// * `reset_duration_override` - Optional override for reset delay duration (ms)
476    pub async fn try_new(
477        port: &'a mut S,
478        feedback_enable: bool,
479        timeout_ms: u64,
480        time_source: T,
481        delay: D,
482        reset_duration_override: Option<u64>,
483    ) -> Result<Self, Error<S::Error>> {
484        let mut player = Self {
485            port,
486            feedback_enable,
487            last_command: MessageData::new(Command::EnterNormalMode, 0, 0),
488            last_response: MessageData::new(Command::EnterNormalMode, 0, 0),
489            last_cmd_acknowledged: false,
490            timeout_ms,
491            time_source,
492            delay,
493        };
494
495        // Clear any pending data in the receive buffer first
496        #[cfg(feature = "defmt")]
497        info!("Clearing initial receive buffer");
498        let _ = player.clear_receive_buffer().await;
499
500        // Send reset command with longer timeout for initialization
501        #[cfg(feature = "defmt")]
502        info!("Sending reset command");
503
504        // Store original timeout and use a longer one for reset
505        let original_timeout = player.timeout_ms;
506        player.timeout_ms = 2000; // Longer timeout for reset
507
508        // Send the reset command using the special init version that won't hang
509        let _reset_result = player
510            .send_command_init(MessageData::new(Command::Reset, 0, 0))
511            .await;
512
513        // Wait for device reset regardless of command result
514        let wait_ms = reset_duration_override.unwrap_or(1500);
515        #[cfg(feature = "defmt")]
516        info!("Waiting {}ms for device reset", wait_ms);
517        player.delay.delay_ms(wait_ms as u32).await;
518
519        // Clear any data that might have arrived during reset
520        let _ = player.clear_receive_buffer().await;
521
522        // Restore original timeout
523        player.timeout_ms = original_timeout;
524
525        // Continue even if reset command had issues
526        if let Err(_e) = _reset_result {
527            #[cfg(feature = "defmt")]
528            info!("Reset error: {:?} - continuing anyway", Debug2Format(&_e));
529        }
530
531        // Configure SD card as the default media source
532        #[cfg(feature = "defmt")]
533        info!("Setting playback source to SD card");
534
535        // Use the special init command here too
536        let _source_result = player
537            .send_command_init(MessageData::new(
538                Command::SetPlaybackSource,
539                0,
540                PlayBackSource::SDCard as u8,
541            ))
542            .await;
543
544        if let Err(_e) = _source_result {
545            #[cfg(feature = "defmt")]
546            info!(
547                "Source select warning: {:?} - continuing anyway",
548                Debug2Format(&_e)
549            );
550        }
551
552        // Add a delay after source selection
553        player.delay.delay_ms(200).await;
554
555        // Set initial volume to a moderate level
556        #[cfg(feature = "defmt")]
557        info!("Setting initial volume");
558
559        // Use the special init command here too
560        let _vol_result = player
561            .send_command_init(MessageData::new(Command::SetVolume, 0, 15))
562            .await;
563
564        if let Err(_e) = _vol_result {
565            #[cfg(feature = "defmt")]
566            info!(
567                "Volume set warning: {:?} - continuing anyway",
568                Debug2Format(&_e)
569            );
570        }
571
572        #[cfg(feature = "defmt")]
573        info!("DFPlayer initialization complete");
574
575        Ok(player)
576    }
577
578    /// Read and process a message from the DFPlayer module
579    ///
580    /// This function handles the binary protocol parsing, message validation,
581    /// and stores the last response. It uses a robust state machine to assemble
582    /// complete messages from potentially fragmented reads, with proper timeout
583    /// handling and error recovery.
584    ///
585    /// Special handling is provided for reset commands and 8-byte response formats
586    /// that sometimes occur in feedback mode.
587    ///
588    /// Returns `Ok(())` if a valid message was received and processed, or an error.
589    /// If a module error response was received, returns that specific error.
590    pub async fn read_last_message(&mut self) -> Result<(), Error<S::Error>> {
591        let timeout_start = self.time_source.now();
592
593        // State tracking for message assembly
594        let mut current_index = 0;
595        let mut message = [0u8; DATA_FRAME_SIZE];
596        let mut receive_buffer = [0u8; 32];
597
598        // Continue trying to read until timeout
599        while !self.time_source.is_elapsed(timeout_start, self.timeout_ms) {
600            // Try to read data with graceful fallback if read_ready isn't reliable
601            let bytes_read = match self.port.read_ready() {
602                Ok(true) => match self.port.read(&mut receive_buffer).await {
603                    Ok(n) => n,
604                    Err(_e) => {
605                        #[cfg(feature = "defmt")]
606                        info!(
607                            "Read error, will retry: {:?}",
608                            Debug2Format(&_e)
609                        );
610                        self.delay.delay_ms(10).await;
611                        continue;
612                    }
613                },
614                // If read_ready says not ready or errors, we'll still try a read
615                // This helps with UART implementations where read_ready isn't reliable
616                _ => {
617                    match self.port.read(&mut receive_buffer).await {
618                        Ok(n) => {
619                            if n == 0 {
620                                // No data available, wait briefly and retry
621                                self.delay.delay_ms(10).await;
622                                continue;
623                            }
624                            n
625                        }
626                        Err(_e) => {
627                            #[cfg(feature = "defmt")]
628                            info!("Read error: {:?}", Debug2Format(&_e));
629                            self.delay.delay_ms(10).await;
630                            continue;
631                        }
632                    }
633                }
634            };
635
636            #[cfg(feature = "defmt")]
637            if bytes_read > 0 {
638                info!(
639                    "Read {} bytes: {:?}",
640                    bytes_read,
641                    &receive_buffer[..bytes_read]
642                );
643            }
644
645            // Special pattern for feedback mode, second responses: 8-byte response format
646            // The DFPlayer sometimes sends truncated 8-byte messages instead of the standard
647            // 10-byte format, particularly for second responses when feedback is enabled.
648            // This appears to be an undocumented protocol quirk of the module.
649            if bytes_read == 8 && receive_buffer[0] == 0x06 {
650                // This looks like a truncated response with the command at index 1
651                let cmd_byte = receive_buffer[1];
652
653                // Try to convert command byte
654                if let Ok(cmd) = Command::try_from(cmd_byte) {
655                    self.last_response.command = cmd;
656                    self.last_response.param_h = receive_buffer[3];
657                    self.last_response.param_l = receive_buffer[4];
658
659                    #[cfg(feature = "defmt")]
660                    info!(
661                        "Parsed 8-byte response: cmd={:?}, params={},{}",
662                        cmd,
663                        self.last_response.param_h,
664                        self.last_response.param_l
665                    );
666
667                    return Ok(());
668                }
669            }
670
671            // Process each byte through our state machine
672            for i in 0..bytes_read {
673                let byte = receive_buffer[i];
674
675                match current_index {
676                    0 => {
677                        // State 0: Looking for start byte (0x7E)
678                        if byte == START_BYTE {
679                            message[current_index] = byte;
680                            current_index = 1;
681                        }
682                    }
683                    9 => {
684                        // State 9: We have 9 bytes and are looking for the end byte (0xEF)
685                        message[current_index] = byte;
686
687                        if byte == END_BYTE {
688                            // Complete message received, validate and process
689                            #[cfg(feature = "defmt")]
690                            info!("Complete message: {:?}", message);
691
692                            // Validate checksum
693                            let read_checksum = ((message[7] as u16) << 8)
694                                | (message[8] as u16);
695                            let calc_checksum = checksum(&message[1..7]);
696
697                            if read_checksum == calc_checksum {
698                                // Valid message - extract command and parameters
699                                if let Ok(cmd) = Command::try_from(message[3]) {
700                                    self.last_response.command = cmd;
701                                    self.last_response.param_h = message[5];
702                                    self.last_response.param_l = message[6];
703
704                                    // Check if this is an ACK message
705                                    if self.last_response == ACK_MESSAGE_DATA {
706                                        self.last_cmd_acknowledged = true;
707                                    }
708
709                                    // Check if this is an error response
710                                    if self.last_response.command
711                                        == Command::NotifyError
712                                    {
713                                        if let Ok(err) = ModuleError::try_from(
714                                            self.last_response.param_l,
715                                        ) {
716                                            return Err(Error::ModuleError(
717                                                err,
718                                            ));
719                                        } else {
720                                            return Err(Error::Unknown);
721                                        }
722                                    }
723
724                                    // Valid message processed successfully
725                                    return Ok(());
726                                }
727                            } else {
728                                #[cfg(feature = "defmt")]
729                                info!(
730                                    "Checksum mismatch: expected {:04X}, got {:04X}",
731                                    calc_checksum, read_checksum
732                                );
733                            }
734                        }
735
736                        // Reset parser state whether valid or not
737                        current_index = 0;
738                    }
739                    _ => {
740                        // States 1-8: Collecting the message body
741                        message[current_index] = byte;
742                        current_index += 1;
743                    }
744                }
745            }
746        }
747
748        // If we reached here, we timed out with no valid response
749        #[cfg(feature = "defmt")]
750        info!("Timeout waiting for response");
751
752        // Special case for reset command - just return success
753        if self.last_command.command == Command::Reset {
754            #[cfg(feature = "defmt")]
755            info!("Ignoring timeout for reset command");
756            return Ok(());
757        }
758
759        // Timeout error for other commands
760        Err(Error::UserTimeout)
761    }
762
763    /// Special version of send_command that won't fail if responses aren't received
764    ///
765    /// Used during initialization to improve reliability when the module is first starting up.
766    /// Unlike the regular send_command, this method:
767    /// - Uses shorter timeouts
768    /// - Continues even if responses aren't received
769    /// - Employs simplified error handling
770    ///
771    /// # Arguments
772    /// * `command_data` - The command and parameters to send
773    async fn send_command_init(
774        &mut self,
775        command_data: MessageData,
776    ) -> Result<(), Error<S::Error>> {
777        // Format the command message according to protocol
778        let mut out_buffer = [
779            START_BYTE, VERSION, MSG_LEN, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
780            END_BYTE,
781        ];
782
783        // Set feedback flag if enabled
784        if self.feedback_enable {
785            out_buffer[INDEX_FEEDBACK_ENABLE] = 0x1;
786        }
787
788        // Set command and parameters
789        out_buffer[INDEX_CMD] = command_data.command as u8;
790        out_buffer[INDEX_PARAM_H] = command_data.param_h;
791        out_buffer[INDEX_PARAM_L] = command_data.param_l;
792
793        // Calculate and set checksum
794        let checksum = checksum(&out_buffer[INDEX_VERSION..INDEX_CHECKSUM_H]);
795        out_buffer[INDEX_CHECKSUM_H] = (checksum >> 8) as u8;
796        out_buffer[INDEX_CHECKSUM_L] = checksum as u8;
797
798        // Log the message being sent
799        #[cfg(feature = "defmt")]
800        info!("tx {}", out_buffer);
801
802        // Send the message to the device
803        self.port
804            .write_all(&out_buffer)
805            .await
806            .map_err(Error::SerialPort)?;
807
808        // Store the command for reference
809        self.last_command = command_data;
810        self.last_cmd_acknowledged = false;
811
812        // If feedback is disabled, don't even try to read a response during initialization
813        if !self.feedback_enable {
814            #[cfg(feature = "defmt")]
815            info!(
816                "Skipping response wait during initialization (feedback disabled)"
817            );
818
819            // Still need a small delay to let the command be processed
820            self.delay.delay_ms(50).await;
821            return Ok(());
822        }
823
824        // For feedback mode, use a short timeout for reading during initialization
825        let original_timeout = self.timeout_ms;
826        self.timeout_ms = 200; // Short timeout for init
827
828        // Try to read a response but don't fail if we timeout
829        let result = self.read_last_message().await;
830
831        // Restore original timeout
832        self.timeout_ms = original_timeout;
833
834        // During initialization, continue even if we get a timeout
835        match result {
836            Ok(_) => {
837                #[cfg(feature = "defmt")]
838                info!("Initialization command received response");
839            }
840            Err(Error::UserTimeout) => {
841                #[cfg(feature = "defmt")]
842                info!("Initialization command timed out (continuing anyway)");
843            }
844            Err(_e) => {
845                #[cfg(feature = "defmt")]
846                info!("Initialization command error: {:?}", Debug2Format(&_e));
847            }
848        }
849
850        Ok(())
851    }
852
853    /// Send a command to the DFPlayer module
854    ///
855    /// This constructs a properly formatted message, sends it to the device,
856    /// and then waits for a response or acknowledgement if feedback is enabled.
857    /// For query commands in non-feedback mode, attempts to read and process responses
858    /// with multiple retries if needed.
859    ///
860    /// The method calculates the appropriate checksum and handles all aspects of
861    /// the binary communication protocol.
862    ///
863    /// # Arguments
864    /// * `command_data` - The command and parameters to send
865    pub async fn send_command(
866        &mut self,
867        command_data: MessageData,
868    ) -> Result<(), Error<S::Error>> {
869        // Format the command message according to protocol
870        let mut out_buffer = [
871            START_BYTE, VERSION, MSG_LEN, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
872            END_BYTE,
873        ];
874
875        // Set feedback flag if enabled
876        if self.feedback_enable {
877            out_buffer[INDEX_FEEDBACK_ENABLE] = 0x1;
878        }
879
880        // Set command and parameters
881        out_buffer[INDEX_CMD] = command_data.command as u8;
882        out_buffer[INDEX_PARAM_H] = command_data.param_h;
883        out_buffer[INDEX_PARAM_L] = command_data.param_l;
884
885        // Calculate and set checksum
886        let checksum = checksum(&out_buffer[INDEX_VERSION..INDEX_CHECKSUM_H]);
887        out_buffer[INDEX_CHECKSUM_H] = (checksum >> 8) as u8;
888        out_buffer[INDEX_CHECKSUM_L] = checksum as u8;
889
890        // Log the message being sent
891        #[cfg(feature = "defmt")]
892        info!("tx {}", out_buffer);
893
894        // Send the message to the device
895        self.port
896            .write_all(&out_buffer)
897            .await
898            .map_err(Error::SerialPort)?;
899
900        // Store the command for reference
901        self.last_command = command_data;
902        self.last_cmd_acknowledged = false;
903
904        // Determine if this is a query command
905        let is_query_command = matches!(
906            command_data.command,
907            Command::QueryTrackCntSD
908                | Command::QueryVolume
909                | Command::QueryEQ
910                | Command::QueryAvailableSources
911                | Command::QueryStatus
912                | Command::QueryTrackCntUSB
913                | Command::QueryCurrentTrackUSBFlash
914                | Command::QueryCurrentTrackSD
915                | Command::QueryCurrentTrackUSBHost
916                | Command::QueryFolderTrackCnt
917                | Command::QueryFolderCnt
918        );
919
920        // Different handling based on feedback mode
921        if self.feedback_enable {
922            // With feedback enabled, we expect an ACK followed by data for queries
923
924            // First read for ACK
925            match self.read_last_message().await {
926                Ok(_) if self.last_cmd_acknowledged => {
927                    // ACK received, now we need the data response for queries
928                    if is_query_command {
929                        #[cfg(feature = "defmt")]
930                        info!("Reading data response after ACK for query");
931
932                        // Read the data response - use a short delay if needed
933                        self.delay.delay_ms(20).await;
934
935                        match self.read_last_message().await {
936                            Ok(_) => {
937                                #[cfg(feature = "defmt")]
938                                info!("Received data response for query");
939                            }
940                            Err(e) => {
941                                #[cfg(feature = "defmt")]
942                                info!(
943                                    "Error reading data response: {:?}",
944                                    Debug2Format(&e)
945                                );
946                                return Err(e);
947                            }
948                        }
949                    }
950                }
951                Ok(_) => {
952                    // Response but no ACK
953                    if command_data.command != Command::Reset {
954                        #[cfg(feature = "defmt")]
955                        info!("Expected ACK not received");
956                        return Err(Error::FailedAck);
957                    }
958                }
959                Err(e) => return Err(e),
960            }
961        } else {
962            // In non-feedback mode, we need to read the response for query commands
963
964            if is_query_command {
965                // Wait a bit longer for the device to respond
966                self.delay.delay_ms(100).await;
967
968                // Try to read the complete response with retries for fragmented messages
969                let mut attempts = 0;
970                let max_attempts = 3;
971
972                while attempts < max_attempts {
973                    attempts += 1;
974
975                    // Try to read a response
976                    let mut buffer = [0u8; 32]; // Larger buffer to catch more data
977                    let bytes_read = match self.port.read(&mut buffer).await {
978                        Ok(n) => n,
979                        Err(e) => {
980                            #[cfg(feature = "defmt")]
981                            info!("Read error: {:?}", Debug2Format(&e));
982                            if attempts == max_attempts {
983                                return Err(Error::SerialPort(e));
984                            }
985                            self.delay.delay_ms(50).await;
986                            continue;
987                        }
988                    };
989
990                    #[cfg(feature = "defmt")]
991                    if bytes_read > 0 {
992                        info!(
993                            "Response (attempt {}): {:?}",
994                            attempts,
995                            &buffer[..bytes_read]
996                        );
997                    }
998
999                    // Check if we have a complete message
1000                    if bytes_read >= DATA_FRAME_SIZE
1001                        && buffer[0] == START_BYTE
1002                        && buffer[DATA_FRAME_SIZE - 1] == END_BYTE
1003                    {
1004                        // Try to extract command and check
1005                        if let Ok(cmd) = Command::try_from(buffer[INDEX_CMD]) {
1006                            // Update last response
1007                            self.last_response.command = cmd;
1008                            self.last_response.param_h = buffer[INDEX_PARAM_H];
1009                            self.last_response.param_l = buffer[INDEX_PARAM_L];
1010
1011                            // For track count, accept any valid response
1012                            if command_data.command == Command::QueryTrackCntSD
1013                            {
1014                                #[cfg(feature = "defmt")]
1015                                info!(
1016                                    "Got response for track count query: {:?}, value={}",
1017                                    cmd, buffer[INDEX_PARAM_L]
1018                                );
1019                                return Ok(());
1020                            }
1021
1022                            // For other commands, verify it matches what we sent
1023                            if cmd == command_data.command {
1024                                #[cfg(feature = "defmt")]
1025                                info!(
1026                                    "Got matching response: {:?}, value={}",
1027                                    cmd, buffer[INDEX_PARAM_L]
1028                                );
1029                                return Ok(());
1030                            } else {
1031                                #[cfg(feature = "defmt")]
1032                                info!(
1033                                    "Response command mismatch: expected {:?}, got {:?}",
1034                                    command_data.command, cmd
1035                                );
1036                            }
1037                        }
1038                    }
1039
1040                    // If we didn't get a complete message, wait and try again
1041                    if attempts < max_attempts {
1042                        self.delay.delay_ms(50).await;
1043                    }
1044                }
1045
1046                // Special case: for track count query, don't fail if we'll check for delayed response
1047                if command_data.command == Command::QueryTrackCntSD {
1048                    #[cfg(feature = "defmt")]
1049                    info!(
1050                        "No immediate track count response, will check for delayed response"
1051                    );
1052                    return Ok(());
1053                }
1054
1055                // If we get here, we didn't get a proper response
1056                #[cfg(feature = "defmt")]
1057                info!(
1058                    "Failed to get proper response after {} attempts",
1059                    max_attempts
1060                );
1061                return Err(Error::BrokenMessage);
1062            } else {
1063                // For non-query commands in non-feedback mode, we don't need to wait for a response
1064            }
1065        }
1066
1067        Ok(())
1068    }
1069
1070    /// Clear any pending data in the receive buffer
1071    ///
1072    /// This method safely reads and discards any data waiting in the receive buffer
1073    /// without blocking indefinitely. It uses non-blocking I/O patterns to avoid
1074    /// hanging when no data is available.
1075    ///
1076    /// This is critical during initialization and after certain commands to ensure
1077    /// a clean communication state and prevent misinterpreting stale data as responses
1078    /// to new commands.
1079    async fn clear_receive_buffer(&mut self) -> Result<(), Error<S::Error>> {
1080        let mut buffer = [0u8; 32];
1081        let start = self.time_source.now();
1082
1083        // Try reading a few times with timeouts
1084        for _ in 0..5 {
1085            // First check if data is available to avoid blocking
1086            let has_data = match self.port.read_ready() {
1087                Ok(ready) => ready,
1088                Err(_) => false, // Assume no data on error
1089            };
1090
1091            if !has_data {
1092                // No data available, don't block
1093                #[cfg(feature = "defmt")]
1094                info!("No data to clear");
1095
1096                // Short delay and continue
1097                self.delay.delay_ms(10).await;
1098                continue;
1099            }
1100
1101            // Data is available, read and discard it
1102            match self.port.read(&mut buffer).await {
1103                Ok(0) => {
1104                    // No data was actually read
1105                    self.delay.delay_ms(10).await;
1106                }
1107                Ok(_n) => {
1108                    #[cfg(feature = "defmt")]
1109                    info!("Cleared {} bytes: {:?}", n, &buffer[.._n]);
1110                    // Short delay and try again
1111                    self.delay.delay_ms(10).await;
1112                }
1113                Err(_e) => {
1114                    #[cfg(feature = "defmt")]
1115                    info!("Clear buffer read error: {:?}", Debug2Format(&_e));
1116                    self.delay.delay_ms(10).await;
1117                }
1118            }
1119
1120            // Check if we've been trying too long
1121            if self.time_source.is_elapsed(start, 100) {
1122                break;
1123            }
1124        }
1125
1126        Ok(())
1127    }
1128
1129    /// Play the next track
1130    ///
1131    /// Sends the command to play the next track in sequence.
1132    pub async fn next(&mut self) -> Result<(), Error<S::Error>> {
1133        self.send_command(MessageData::new(Command::Next, 0, 0))
1134            .await
1135    }
1136
1137    /// Reset the DFPlayer module
1138    ///
1139    /// Sends a reset command to the module and waits for it to restart.
1140    /// The reset command typically causes the module to restart its processor
1141    /// and reinitialize its state.
1142    ///
1143    /// Special handling is provided for the reset command, as it often won't
1144    /// receive a response from the module.
1145    ///
1146    /// # Arguments
1147    /// * `reset_duration_override` - Optional override for reset delay duration (ms)
1148    pub async fn reset(
1149        &mut self,
1150        reset_duration_override: Option<u64>,
1151    ) -> Result<(), Error<S::Error>> {
1152        // Send reset command
1153        self.send_command(MessageData::new(Command::Reset, 0, 0))
1154            .await?;
1155
1156        // Wait for the device to complete the reset
1157        let wait_ms = reset_duration_override.unwrap_or(1500); // Default timeout based on typical M16P init time
1158        self.delay.delay_ms(wait_ms as u32).await;
1159
1160        // Try to read a response (though one may not come after reset)
1161        let _ = self.read_last_message().await;
1162
1163        Ok(())
1164    }
1165
1166    /// Set the media source for playback
1167    ///
1168    /// Configures which media source the DFPlayer should use for audio files.
1169    /// This method includes an additional delay after sending the command to
1170    /// allow the module time to switch sources and initialize the file system.
1171    ///
1172    /// # Arguments
1173    /// * `playback_source` - The media source to use (SD card, USB, etc.)
1174    pub async fn set_playback_source(
1175        &mut self,
1176        playback_source: PlayBackSource,
1177    ) -> Result<(), Error<S::Error>> {
1178        // Send the command to set playback source
1179        let cmd_result = self
1180            .send_command(MessageData::new(
1181                Command::SetPlaybackSource,
1182                0,
1183                playback_source as u8,
1184            ))
1185            .await;
1186
1187        // Wait for the module to initialize the source
1188        // This delay is needed regardless of command success
1189        self.delay.delay_ms(200).await;
1190
1191        cmd_result
1192    }
1193
1194    /// Set the volume level (0-30)
1195    ///
1196    /// Configures the output volume of the DFPlayer module.
1197    /// Valid range is 0 (silent) to 30 (maximum volume).
1198    ///
1199    /// # Arguments
1200    /// * `volume` - Volume level from 0 (silent) to 30 (maximum)
1201    ///
1202    /// # Errors
1203    /// Returns `Error::BadParameter` if the volume is greater than 30.
1204    pub async fn set_volume(
1205        &mut self,
1206        volume: u8,
1207    ) -> Result<(), Error<S::Error>> {
1208        if volume > 30 {
1209            return Err(Error::BadParameter);
1210        }
1211        self.send_command(MessageData::new(Command::SetVolume, 0, volume))
1212            .await
1213    }
1214
1215    /// Play a specific track by its index number
1216    ///
1217    /// Starts playback of a track by its numerical index.
1218    /// Track numbers typically start at 1, not 0.
1219    ///
1220    /// # Arguments
1221    /// * `track` - Track number (1-2999)
1222    ///
1223    /// # Errors
1224    /// Returns `Error::BadParameter` if the track number is greater than 2999.
1225    pub async fn play(&mut self, track: u16) -> Result<(), Error<S::Error>> {
1226        if track > 2999 {
1227            return Err(Error::BadParameter);
1228        }
1229        self.send_command(MessageData::new(
1230            Command::PlayTrack,
1231            (track >> 8) as u8,
1232            track as u8,
1233        ))
1234        .await
1235    }
1236
1237    /// Set the equalizer mode
1238    ///
1239    /// Configures the audio equalizer preset on the DFPlayer.
1240    ///
1241    /// # Arguments
1242    /// * `equalizer` - Equalizer preset to use
1243    pub async fn set_equalizer(
1244        &mut self,
1245        equalizer: Equalizer,
1246    ) -> Result<(), Error<S::Error>> {
1247        self.send_command(MessageData::new(Command::SetEQ, 0, equalizer as u8))
1248            .await
1249    }
1250
1251    /// Set whether to loop all tracks
1252    ///
1253    /// Enables or disables looping through all tracks.
1254    ///
1255    /// # Arguments
1256    /// * `enable` - Whether to enable looping of all tracks
1257    pub async fn set_loop_all(
1258        &mut self,
1259        enable: bool,
1260    ) -> Result<(), Error<S::Error>> {
1261        self.send_command(MessageData::new(
1262            Command::PlayLoopAll,
1263            0,
1264            if enable { 1 } else { 0 },
1265        ))
1266        .await
1267    }
1268
1269    /// Pause the current playback
1270    ///
1271    /// Pauses any currently playing track.
1272    pub async fn pause(&mut self) -> Result<(), Error<S::Error>> {
1273        self.send_command(MessageData::new(Command::Pause, 0, 0))
1274            .await
1275    }
1276
1277    /// Resume playback
1278    ///
1279    /// Resumes playback of a paused track.
1280    pub async fn resume(&mut self) -> Result<(), Error<S::Error>> {
1281        self.send_command(MessageData::new(Command::Play, 0, 0))
1282            .await
1283    }
1284
1285    /// Play the previous track
1286    ///
1287    /// Plays the track before the current one in sequence.
1288    pub async fn previous(&mut self) -> Result<(), Error<S::Error>> {
1289        self.send_command(MessageData::new(Command::Previous, 0, 0))
1290            .await
1291    }
1292
1293    /// Stop all playback
1294    ///
1295    /// Stops any current playback, including advertisements.
1296    pub async fn stop(&mut self) -> Result<(), Error<S::Error>> {
1297        self.send_command(MessageData::new(Command::Stop, 0, 0))
1298            .await
1299    }
1300
1301    /// Play a track from a specific folder
1302    ///
1303    /// The DFPlayer supports organizing tracks in folders.
1304    /// This command plays a specific track from a specific folder.
1305    ///
1306    /// # Arguments
1307    /// * `folder` - Folder number (1-99)
1308    /// * `track` - Track number within the folder (1-255)
1309    ///
1310    /// # Errors
1311    /// Returns `Error::BadParameter` if parameters are out of range.
1312    pub async fn play_from_folder(
1313        &mut self,
1314        folder: u8,
1315        track: u8,
1316    ) -> Result<(), Error<S::Error>> {
1317        if folder == 0 || folder > 99 || track == 0 {
1318            return Err(Error::BadParameter);
1319        }
1320
1321        self.send_command(MessageData::new(
1322            Command::PlayTrackInFolder,
1323            folder,
1324            track,
1325        ))
1326        .await
1327    }
1328
1329    /// Play tracks in random order
1330    ///
1331    /// Starts playback in random order from the current source.
1332    pub async fn play_random(&mut self) -> Result<(), Error<S::Error>> {
1333        self.send_command(MessageData::new(Command::PlayRandom, 0, 0))
1334            .await
1335    }
1336
1337    /// Set whether to loop the current track
1338    ///
1339    /// Enables or disables looping of the currently playing track.
1340    ///
1341    /// # Arguments
1342    /// * `enable` - Whether to enable looping of the current track
1343    pub async fn set_loop_current_track(
1344        &mut self,
1345        enable: bool,
1346    ) -> Result<(), Error<S::Error>> {
1347        self.send_command(MessageData::new(
1348            Command::LoopCurrentTrack,
1349            0,
1350            if enable { 1 } else { 0 },
1351        ))
1352        .await
1353    }
1354
1355    /// Query the total number of tracks on the SD card
1356    ///
1357    /// Returns the number of tracks on the SD card.
1358    ///
1359    /// This method implements a robust strategy for obtaining track counts:
1360    /// - Sends the query command and processes immediate responses
1361    /// - For non-feedback mode, waits for delayed responses with multiple attempts
1362    /// - Returns 0 if no tracks are found or the count couldn't be retrieved
1363    ///
1364    /// The track count retrieval is one of the most timing-sensitive operations
1365    /// of the DFPlayer module and may require multiple attempts.
1366    pub async fn query_tracks_sd(&mut self) -> Result<u16, Error<S::Error>> {
1367        // Send the command
1368        let result = self
1369            .send_command(MessageData::new(Command::QueryTrackCntSD, 0, 0))
1370            .await;
1371
1372        // If we got a command error that wasn't BrokenMessage, return it
1373        if let Err(e) = result {
1374            match e {
1375                Error::BrokenMessage => {
1376                    // We'll continue and try to get a delayed response
1377                    #[cfg(feature = "defmt")]
1378                    info!(
1379                        "Initial track count query failed, trying delayed response"
1380                    );
1381                }
1382                _ => return Err(e),
1383            }
1384        }
1385
1386        // If we're in non-feedback mode, wait for delayed response
1387        if !self.feedback_enable {
1388            // Wait longer for delayed response - often track count takes a while
1389            self.delay.delay_ms(500).await;
1390
1391            // Try to read the delayed response with multiple attempts
1392            for attempt in 1..=3 {
1393                let mut buffer = [0u8; 32];
1394                let bytes_read = match self.port.read(&mut buffer).await {
1395                    Ok(n) => n,
1396                    Err(_) => {
1397                        // If we can't read, wait and try again
1398                        self.delay.delay_ms(100).await;
1399                        continue;
1400                    }
1401                };
1402
1403                #[cfg(feature = "defmt")]
1404                if bytes_read > 0 {
1405                    info!(
1406                        "Delayed response (attempt {}): {:?}",
1407                        attempt,
1408                        &buffer[..bytes_read]
1409                    );
1410                }
1411
1412                // Check for track count response in the buffer
1413                for i in 0..bytes_read.saturating_sub(9) {
1414                    if buffer[i] == START_BYTE
1415                        && buffer[i + INDEX_CMD]
1416                            == Command::QueryTrackCntSD as u8
1417                        && buffer[i + 9] == END_BYTE
1418                    {
1419                        // Found track count response
1420                        let track_count = buffer[i + INDEX_PARAM_L];
1421
1422                        #[cfg(feature = "defmt")]
1423                        info!("Found delayed track count: {}", track_count);
1424
1425                        // Update last_response
1426                        self.last_response.command = Command::QueryTrackCntSD;
1427                        self.last_response.param_h = buffer[i + INDEX_PARAM_H];
1428                        self.last_response.param_l = track_count;
1429
1430                        return Ok(track_count as u16);
1431                    }
1432                }
1433
1434                // If we didn't find a response, wait and try again
1435                if attempt < 3 {
1436                    self.delay.delay_ms(150).await;
1437                }
1438            }
1439        }
1440
1441        // If we have a track count response, use it
1442        if self.last_response.command == Command::QueryTrackCntSD {
1443            return Ok(self.last_response.param_l as u16);
1444        }
1445
1446        // No valid track count found
1447        #[cfg(feature = "defmt")]
1448        info!("Failed to get track count after multiple attempts");
1449
1450        // For the special case of no tracks found, return 0
1451        Ok(0)
1452    }
1453
1454    /// Query the current volume setting
1455    ///
1456    /// Returns the current volume level (0-30) or an error.
1457    pub async fn query_volume(&mut self) -> Result<u8, Error<S::Error>> {
1458        self.send_command(MessageData::new(Command::QueryVolume, 0, 0))
1459            .await?;
1460        Ok(self.last_response.param_l)
1461    }
1462
1463    /// Query the current equalizer setting
1464    ///
1465    /// Returns the current equalizer setting (0-5) or an error.
1466    pub async fn query_eq(&mut self) -> Result<u8, Error<S::Error>> {
1467        self.send_command(MessageData::new(Command::QueryEQ, 0, 0))
1468            .await?;
1469        Ok(self.last_response.param_l)
1470    }
1471}