Skip to content

Commit 1435944

Browse files
authored
Merge pull request #1 from 1-rafael-1/update-driver
2 parents 1b52cc4 + a38df95 commit 1435944

File tree

11 files changed

+1744
-203
lines changed

11 files changed

+1744
-203
lines changed

Cargo.toml

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
[package]
2-
name = "dfplayer-serial"
3-
version = "0.1.0"
2+
name = "dfplayer-async"
3+
version = "0.2.0"
44
edition = "2021"
5-
description = "A simple embedded-hal driver for operating the MAX485 uart to rs485 module in half duplex mode."
6-
keywords = ["no-std", "driver", "embedded-hal", "mp3", "serial"]
5+
description = "A simple embedded-hal-async driver for the DFPlayer mini MP3 module."
6+
keywords = ["no-std", "embedded-hal-async", "mp3", "dfplayer", "driver"]
77
categories = ["embedded", "hardware-support", "no-std"]
8-
#repository = "https://github.com/Laboratorios-Gensokyo/dfplayer-serial"
9-
#documentation= "https://docs.rs/crate/dfplayer-serial/"
8+
repository = "https://github.com/Laboratorios-Gensokyo/dfplayer-serial"
9+
documentation = "https://docs.rs/dfplayer-async/"
1010
license = "MIT"
1111
readme = "README.md"
12-
authors = ["José Morales <[email protected]>"]
12+
authors = [
13+
"Rafael Koch <[email protected]>",
14+
"José Morales <[email protected]>",
15+
]
1316

1417
[dependencies]
1518
embedded-io-async = "0.6"
16-
embassy-time = "0.3"
17-
esp-println = {version = "0.9.1",features = ["esp32c3", "log"]}
19+
embedded-hal-async = "1.0.0"
20+
21+
defmt = { version = "0.3.10", optional = true }
22+
23+
[features]
24+
defmt = ["dep:defmt"]

LICENSE

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
MIT License
22

33
Copyright (c) 2024 José Fernando Morales Vargas
4+
Copyright (c) 2025 Rafael Koch
45

56
Permission is hereby granted, free of charge, to any person obtaining a copy
67
of this software and associated documentation files (the "Software"), to deal

README.md

+77-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,79 @@
1-
# DFPlayer-serial
1+
# DFPlayer-async
22

3-
Driver to communicate with the DFRobot DFPlayer Mini MP3 Player.
3+
An async no_std Rust driver for the DFPlayer Mini MP3 module, designed for embedded systems.
44

5-
Supports only embassy for now.
5+
## About
6+
7+
This driver is based on the excellent work from [dfplayer-serial](https://github.com/Laboratorios-Gensokyo/dfplayer-serial) by José Fernando Morales Vargas. Published with the original author's kind permission as the original repository is currently not maintained.
8+
9+
Based on the original driver, this version has been reworked. See tags for changes, in case You are interested.
10+
11+
## Features
12+
13+
- **Async/await API** for embedded systems
14+
- **(Probaly) full command support** for DFPlayer Mini and compatible modules
15+
- **Proper error handling** and timeout management
16+
- **no_std compatible** for use in bare-metal environments
17+
- **Robust initialization sequence** with fallback mechanisms
18+
- Optional **logging via defmt** (enable the "defmt" feature)
19+
20+
## Hardware Compatibility
21+
22+
This driver has been tested with:
23+
24+
- Original DFPlayer mini by DFRobot
25+
- AZDeliver DFPlayer mini clone
26+
27+
Despite using different chips, both modules apparently follow the same protocol and work with this driver. Other compatible modules should work as well, but your mileage may vary.
28+
29+
## Usage
30+
31+
Add to your `Cargo.toml`:
32+
33+
```toml
34+
[dependencies]
35+
dfplayer-async = "0.2.0"
36+
```
37+
38+
## Examples
39+
40+
The repository contains complete working examples:
41+
42+
- [`examples/src/basic.rs`](examples/src/basic.rs) - Shows how to initialize the driver and control playback, including:
43+
- Setting up the UART connection
44+
- Initializing the DFPlayer
45+
- Setting volume and equalizer
46+
- Playing tracks and handling the busy pin
47+
48+
- [`examples/src/query.rs`](examples/src/query.rs) - Demonstrates how to query information from the device:
49+
- Getting the number of tracks on the SD card
50+
- Reading the current equalizer setting
51+
- Checking the current volume
52+
53+
The examples assume a Raspberry Pi Pico with specific pin connections:
54+
55+
- UART0 TX/RX on GPIO 17/16
56+
- Busy Pin on GPIO 18
57+
58+
## Hardware Setup
59+
60+
To use the DFPlayer Mini:
61+
62+
1. Connect power (VCC and GND)
63+
2. Connect UART TX/RX lines (9600 baud, 8N1)
64+
3. Optionally connect the busy pin
65+
4. Connect a speaker to the output
66+
5. Insert an SD card with MP3 files
67+
68+
## File Organization
69+
70+
The DFPlayer expects MP3 files to be organized in a specific way. For basic usage:
71+
72+
- Place files in the root directory of the SD card
73+
- Name them with a numerical prefix: `0001-song.mp3`, `0002-song.mp3`, etc.
74+
75+
For more advanced organization, you can create folders and use the `play_from_folder` method.
76+
77+
## License
78+
79+
Licensed under the MIT License - see the LICENSE file for details.

examples/.cargo/config.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2+
# pick one of the following options by uncommenting it
3+
# runner = "elf2uf2-rs -d" #and then flash manually
4+
# runner = "picotool load -u -v -x -t elf"
5+
runner = "probe-rs run --chip RP235x --protocol swd --speed 16000 --probe 2e8a:000c:E6633861A3543E38" #remove the --probe option if you have only one probe attached
6+
7+
[build]
8+
target = "thumbv8m.main-none-eabihf"
9+
10+
[env]
11+
DEFMT_LOG = "debug"

examples/Cargo.toml

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[package]
2+
name = "examples"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[[example]]
7+
name = "basic"
8+
path = "src/basic.rs"
9+
10+
[[example]]
11+
name = "query"
12+
path = "src/query.rs"
13+
14+
[dependencies]
15+
embassy-rp = { version = "0.3.1", features = [
16+
"defmt",
17+
"unstable-pac",
18+
"time-driver",
19+
"critical-section-impl",
20+
"rp235xa",
21+
] }
22+
embassy-embedded-hal = { version = "0.3.0", features = ["defmt"] }
23+
embassy-executor = { version = "0.7.0", features = [
24+
"task-arena-size-98304",
25+
"arch-cortex-m",
26+
"executor-thread",
27+
"executor-interrupt",
28+
"defmt",
29+
] }
30+
embassy-time = { version = "0.4.0", features = [
31+
"defmt",
32+
"defmt-timestamp-uptime",
33+
] }
34+
defmt = "0.3"
35+
defmt-rtt = "0.4"
36+
cortex-m-rt = "0.7.5"
37+
critical-section = "1.2.0"
38+
panic-probe = { version = "0.3", features = ["print-defmt"] }
39+
embedded-io-async = "0.6.1"
40+
static_cell = "2.1.0"
41+
42+
# dependency to dfplayer-async crate in the parent directory, with defmt feature enabled
43+
dfplayer-async = { path = "..", features = [] }

examples/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# DFPlayer Mini Examples
2+
3+
This directory contains examples demonstrating use of the DFPlayer driver using Raspberry Pi Pico 2 (rp2350) and Embassy.
4+
5+
## Examples
6+
7+
### [Basic](src/basic.rs)
8+
9+
Basic example showing how to:
10+
11+
- Initialize the module
12+
- Set volume and equalizer
13+
- Play a track
14+
- Use busy pin in a loop to play subsequent tracks once the current track has finished

examples/build.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//! This build script copies the `memory.x` file from the crate root into
2+
//! a directory where the linker can always find it at build time.
3+
//! For many projects this is optional, as the linker always searches the
4+
//! project root directory -- wherever `Cargo.toml` is. However, if you
5+
//! are using a workspace or have a more complicated build setup, this
6+
//! build script becomes required. Additionally, by requesting that
7+
//! Cargo re-run the build script whenever `memory.x` is changed,
8+
//! updating `memory.x` ensures a rebuild of the application with the
9+
//! new memory settings.
10+
11+
use std::env;
12+
use std::fs::File;
13+
use std::io::Write;
14+
use std::path::PathBuf;
15+
16+
fn main() {
17+
// Put `memory.x` in our output directory and ensure it's
18+
// on the linker search path.
19+
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20+
File::create(out.join("memory.x"))
21+
.unwrap()
22+
.write_all(include_bytes!("memory.x"))
23+
.unwrap();
24+
println!("cargo:rustc-link-search={}", out.display());
25+
26+
// By default, Cargo will re-run a build script whenever
27+
// any file in the project changes. By specifying `memory.x`
28+
// here, we ensure the build script is only re-run when
29+
// `memory.x` is changed.
30+
println!("cargo:rerun-if-changed=memory.x");
31+
32+
println!("cargo:rustc-link-arg=--nmagic");
33+
println!("cargo:rustc-link-arg=-Tlink.x");
34+
println!("cargo:rustc-link-arg=-Tdefmt.x");
35+
}

examples/memory.x

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
MEMORY {
2+
/*
3+
* The RP2350 has either external or internal flash.
4+
*
5+
* 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
6+
*/
7+
FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
8+
/*
9+
* RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
10+
* This is usually good for performance, as it distributes load on
11+
* those banks evenly.
12+
*/
13+
RAM : ORIGIN = 0x20000000, LENGTH = 512K
14+
/*
15+
* RAM banks 8 and 9 use a direct mapping. They can be used to have
16+
* memory areas dedicated for some specific job, improving predictability
17+
* of access times.
18+
* Example: Separate stacks for core0 and core1.
19+
*/
20+
SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
21+
SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
22+
}
23+
24+
SECTIONS {
25+
/* ### Boot ROM info
26+
*
27+
* Goes after .vector_table, to keep it in the first 4K of flash
28+
* where the Boot ROM (and picotool) can find it
29+
*/
30+
.start_block : ALIGN(4)
31+
{
32+
__start_block_addr = .;
33+
KEEP(*(.start_block));
34+
} > FLASH
35+
36+
} INSERT AFTER .vector_table;
37+
38+
/* move .text to start /after/ the boot info */
39+
_stext = ADDR(.start_block) + SIZEOF(.start_block);
40+
41+
SECTIONS {
42+
/* ### Picotool 'Binary Info' Entries
43+
*
44+
* Picotool looks through this block (as we have pointers to it in our
45+
* header) to find interesting information.
46+
*/
47+
.bi_entries : ALIGN(4)
48+
{
49+
/* We put this in the header */
50+
__bi_entries_start = .;
51+
/* Here are the entries */
52+
KEEP(*(.bi_entries));
53+
/* Keep this block a nice round size */
54+
. = ALIGN(4);
55+
/* We put this in the header */
56+
__bi_entries_end = .;
57+
} > FLASH
58+
} INSERT AFTER .text;
59+
60+
SECTIONS {
61+
/* ### Boot ROM extra info
62+
*
63+
* Goes after everything in our program, so it can contain a signature.
64+
*/
65+
.end_block : ALIGN(4)
66+
{
67+
__end_block_addr = .;
68+
KEEP(*(.end_block));
69+
} > FLASH
70+
71+
} INSERT AFTER .uninit;
72+
73+
PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
74+
PROVIDE(end_to_start = __start_block_addr - __end_block_addr);

0 commit comments

Comments
 (0)