diff --git a/drivers/Makefile.include b/drivers/Makefile.include index c4dccaf3d0..b03eddf888 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -90,6 +90,10 @@ endif ifneq (,$(filter dfplayer,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/dfplayer/include + ifneq (,$(filter arch_avr8 arch_msp430,$(FEATURES_USED))) + # no strerror() on AVR and MSP430 + CFLAGS += -DDFPLAYER_NO_STRERROR + endif endif ifneq (,$(filter dht,$(USEMODULE))) diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 242f40b1d9..fa7be4a572 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -38,4 +38,11 @@ ifneq (,$(filter trace,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter shell_commands,$(USEMODULE))) + ifneq (,$(filter dfplayer,$(USEMODULE))) + USEMODULE += auto_init_multimedia + USEMODULE += fmt + endif +endif + include $(RIOTBASE)/sys/test_utils/Makefile.dep diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index a8403d4aa2..d4a6f8d486 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -5,6 +5,9 @@ SRC = shell_commands.c sc_sys.c ifneq (,$(filter app_metadata,$(USEMODULE))) SRC += sc_app_metadata.c endif +ifneq (,$(filter dfplayer,$(USEMODULE))) + SRC += sc_dfplayer.c +endif ifneq (,$(filter mci,$(USEMODULE))) SRC += sc_disk.c endif diff --git a/sys/shell/commands/sc_dfplayer.c b/sys/shell/commands/sc_dfplayer.c new file mode 100644 index 0000000000..8c5a54040b --- /dev/null +++ b/sys/shell/commands/sc_dfplayer.c @@ -0,0 +1,621 @@ +/* + * Copyright 2019 Marian Buschsieweke + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_shell_commands + * @{ + * + * @file + * @brief Provides a shell command to control a DFPlayer Mini + * + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include +#include +#include + +#include "dfplayer.h" +#include "dfplayer_internal.h" +#include "fmt.h" + +static const char *_equalizers[] = { + [DFPLAYER_EQ_NORMAL] = "normal", + [DFPLAYER_EQ_POP] = "pop", + [DFPLAYER_EQ_ROCK] = "rock", + [DFPLAYER_EQ_JAZZ] = "jazz", + [DFPLAYER_EQ_CLASSIC] = "classic", + [DFPLAYER_EQ_BASE] = "base", + [DFPLAYER_EQ_NUMOF] = NULL +}; + +static const char *_modes[] = { + [DFPLAYER_MODE_UNKOWN] = "unknown", + [DFPLAYER_MODE_REPEAT_DIR] = "repeat folder", + [DFPLAYER_MODE_REPEAT] = "repeat", + [DFPLAYER_MODE_RANDOM] = "random", + [DFPLAYER_MODE_NORMAL] = "normal", + [DFPLAYER_MODE_NUMOF] = NULL +}; + +static const char *_states[] = { + [DFPLAYER_STATE_PLAYING] = "playing", + [DFPLAYER_STATE_PAUSED] = "paused", + [DFPLAYER_STATE_STOPPED] = "stopped", + [DFPLAYER_STATE_NUMOF] = NULL +}; + +static void _print_error(int retval) +{ + print_str("Error: "); +#ifdef DFPLAYER_NO_STRERROR + print_s32_dec(retval); +#else + print_str(strerror(-retval)); +#endif + print("\n", 1); +} + +static void _print_state(dfplayer_t *dev) +{ + dfplayer_state_t state; + print_str("State: "); + int retval = dfplayer_get_state(dev, &state); + if (retval) { + _print_error(retval); + } + else { + print_str(_states[state]); + print("\n", 1); + } +} + +static void _print_tracks(dfplayer_t *dev) +{ + dfplayer_source_set_t srcs = dfplayer_get_sources(dev); + print_str("Selected file numbers (in file system):\n"); + + if (dfplayer_source_set_contains(srcs, DFPLAYER_SOURCE_USB)) { + uint16_t fileno; + int retval = dfplayer_get_fileno_usb(dev, &fileno); + print_str(" - USB: "); + if (retval) { + _print_error(retval); + } + else { + print_u32_dec(fileno); + print("\n", 1); + } + } + + if (dfplayer_source_set_contains(srcs, DFPLAYER_SOURCE_SDCARD)) { + uint16_t fileno; + int retval = dfplayer_get_fileno_sdcard(dev, &fileno); + print_str(" - SD card: "); + if (retval) { + _print_error(retval); + } + else { + print_u32_dec(fileno); + print("\n", 1); + } + } + + if (dfplayer_source_set_contains(srcs, DFPLAYER_SOURCE_FLASH)) { + uint16_t fileno; + int retval = dfplayer_get_fileno_sdcard(dev, &fileno); + print_str(" - NOR flash: "); + if (retval) { + _print_error(retval); + } + else { + print_u32_dec(fileno); + print("\n", 1); + } + } + + print_str("Current track: "); + dfplayer_file_t file = dfplayer_get_played_file(dev); + if (file.scheme == DFPLAYER_SCHEME_FOLDER_FILE) { + print_u32_dec(file.folder); + print_str("/"); + print_u32_dec(file.file); + } + else { + print_u32_dec(file.number); + } + + print("\n", 1); +} + +static void _print_volume(dfplayer_t *dev) +{ + uint8_t volume = 0; + int retval = dfplayer_get_volume(dev, &volume); + print_str("Volume: "); + if (retval) { + _print_error(retval); + } + else { + print_u32_dec(volume); + print("\n", 1); + } +} + +static void _print_equalizer(dfplayer_t *dev) +{ + dfplayer_eq_t equalizer = 0; + int retval = dfplayer_get_equalizer(dev, &equalizer); + print_str("Equalizer: "); + if (retval) { + _print_error(retval); + } + else { + if ((unsigned)equalizer >= (unsigned)DFPLAYER_EQ_NUMOF) { + print_str("Invalid response!?!\n"); + } + else { + print_str(_equalizers[equalizer]); + print("\n", 1); + } + } +} + +static void _print_mode(dfplayer_t *dev) +{ + dfplayer_mode_t mode; + int retval = dfplayer_get_mode(dev, &mode); + if (retval) { + _print_error(retval); + } + else { + print_str("Playback mode: "); + print_str(_modes[mode]); + print("\n", 1); + } +} + +static void _print_files(dfplayer_t *dev) +{ + dfplayer_source_set_t srcs = dfplayer_get_sources(dev); + uint16_t files; + int retval; + + print_str("USB: "); + if (dfplayer_source_set_contains(srcs, DFPLAYER_SOURCE_USB)) { + retval = dfplayer_files_usb(dev, &files); + if (retval) { + _print_error(retval); + } + else { + print_u32_dec(files); + print("\n", 1); + } + } + else { + print_str("Not present\n"); + } + + print_str("SD card: "); + if (dfplayer_source_set_contains(srcs, DFPLAYER_SOURCE_SDCARD)) { + retval = dfplayer_files_sdcard(dev, &files); + if (retval) { + _print_error(retval); + } + else { + print_u32_dec(files); + print("\n", 1); + } + } + else { + print_str("Not present\n"); + } + + print_str("Flash: "); + if (dfplayer_source_set_contains(srcs, DFPLAYER_SOURCE_FLASH)) { + retval = dfplayer_files_flash(dev, &files); + if (retval) { + _print_error(retval); + } + else { + print_u32_dec(files); + print("\n", 1); + } + } + else { + print_str("Not present\n"); + } +} + +static void _print_status(dfplayer_t *dev) +{ + uint16_t version; + int retval; + + print_str("Software version: "); + retval = dfplayer_get_version(dev, &version); + if (retval) { + _print_error(retval); + } + else { + print_u32_dec(version); + print("\n", 1); + } + + _print_state(dev); + _print_volume(dev); + _print_tracks(dev); + _print_mode(dev); + _print_files(dev); +} + +static void _print_usage(const char *progname) +{ + print_str("Usage: "); + print_str(progname); + print_str(" [-d ] [CMD [PARAMS]]\n"); +} + +static void _print_help(const char *progname) +{ + _print_usage(progname); + print_str("Run "); + print_str(progname); + print_str(" without command to query the current status\n"); + print_str( + "\n" + "Commands:\n" + " play Start playback\n" + " pause Pause playing\n" + " next Start playing next song\n" + " prev Start playing previous song\n" + " track Print the currently played track\n" + " track Select track with number \n" + " files Print the number of files present\n" + " file Play file number in folder \n" + " (E.g. \"09/042.mp3\" is selected with \"" + ); + print_str(progname); + print_str(" file 9 42\")\n"); + print_str( + " volume Print current volume\n" + " volume Set volume to (0 - 30)\n" + " source Set source to (USB, SD, FLASH)\n" + " equalizer Print current equalizer setting\n" + " equalizer Set equalizer to \n" + " mode Print the current playback mode\n" + " repeat <1 / 0> Enable (1) or disable (0) repeat\n" + " repeat-folder Play and repeat folder number \n" + " state Print the DFPlayer's state\n" + " shuffle Play all files at random\n" + " mp3 Play file number from \"MP3\" folder\n" + " advert Play file number from \"ADVERT\" folder\n" + " (Only works during playback, previous playback\n" + " is resumed afterwards)\n" + " stop-advert During advert, resume previous playback now\n" + " cmd [[P1] P2] Run command with parameters P1 and P2\n" + ); +} + +int _sc_dfplayer(int argc, char **argv) +{ + unsigned dev_num = 0; + int pos = 1; + dfplayer_t *dev; + + if ((argc > 1) && (!strcmp("-d", argv[1]))) { + if (argc < 3) { + print_str("Missing device number for parameter \"-d\"\n\n"); + _print_usage(argv[0]); + return 1; + } + dev_num = atoi(argv[2]); + pos = 3; + } + + dev = dfplayer_get(dev_num); + + if (!dev) { + print_str("Error: No DFPlayer Mini device with number "); + print_u32_dec(dev_num); + print("\n", 1); + return 1; + } + + if (argc == pos) { + _print_status(dev); + return 0; + } + + if (!strcmp("play", argv[pos])) { + int retval = dfplayer_play(dev); + if (retval) { + _print_error(retval); + return 1; + } + return 0; + } + + if (!strcmp("pause", argv[pos])) { + int retval = dfplayer_pause(dev); + if (retval) { + _print_error(retval); + return 1; + } + return 0; + } + + if (!strcmp("next", argv[pos])) { + int retval = dfplayer_next(dev); + if (retval) { + _print_error(retval); + return 1; + } + return 0; + } + + if (!strcmp("prev", argv[pos])) { + int retval = dfplayer_prev(dev); + if (retval) { + _print_error(retval); + return 1; + } + return 0; + } + + if (!strcmp("track", argv[pos])) { + _print_tracks(dev); + return 0; + } + + if (!strcmp("files", argv[pos])) { + if (++pos != argc) { + print_str("Error: Expected no parameter for argument \"files\"\n"); + return 1; + } + + _print_files(dev); + return 0; + } + + if (!strcmp("file", argv[pos])) { + if (pos + 2 >= argc) { + print_str("Error: Missing folder / file number\n\n"); + _print_help(argv[0]); + return 1; + } + + uint8_t folder = atoi(argv[++pos]); + uint8_t file = atoi(argv[++pos]); + int retval = dfplayer_play_file(dev, folder, file); + if (retval) { + _print_error(retval); + return 1; + } + return 0; + } + + if (!strcmp("volume", argv[pos])) { + if (argc == ++pos) { + _print_volume(dev); + } + else { + uint8_t volume = atoi(argv[pos]); + int retval = dfplayer_set_volume(dev, volume); + if (retval) { + _print_error(retval); + return -1; + } + } + return 0; + } + + if (!strcmp("source", argv[pos])) { + if (argc < ++pos) { + print_str("Error: Missing argument for command \"source\"\n\n"); + _print_help(argv[0]); + return 1; + } + + dfplayer_source_t src; + if (!strcmp("usb", argv[pos]) || !strcmp("USB", argv[pos])) { + src = DFPLAYER_SOURCE_USB; + } + else if (!strcmp("sd", argv[pos]) || !strcmp("SD", argv[pos])) { + src = DFPLAYER_SOURCE_SDCARD; + } + else if (!strcmp("flash", argv[pos]) || !strcmp("FLASH", argv[pos])) { + src = DFPLAYER_SOURCE_FLASH; + } + else { + print_str("Error: Unknown source type. (Valid: USB, SD, FLASH)\n"); + return 1; + } + + dfplayer_source_set_t srcs = dfplayer_get_sources(dev); + + if (!dfplayer_source_set_contains(srcs, src)) { + print_str("Error: Specified playback source not connected\n"); + return 1; + } + + int retval = dfplayer_set_source(dev, src); + if (retval) { + _print_error(retval); + return 1; + } + return 0; + } + + if (!strcmp("equalizer", argv[pos])) { + if (++pos == argc) { + _print_equalizer(dev); + } + else { + dfplayer_eq_t eq = DFPLAYER_EQ_NUMOF; + for (unsigned i = 0; _equalizers[i] != NULL; i++) { + if (!strcmp(_equalizers[i], argv[pos])) { + eq = (dfplayer_eq_t)i; + break; + } + } + + if (eq == DFPLAYER_EQ_NUMOF) { + print_str("Error: Unknown equalizer\n"); + return 1; + } + + int retval = dfplayer_set_equalizer(dev, eq); + if (retval) { + _print_error(retval); + return 1; + } + } + return 0; + } + + if (!strcmp("mode", argv[pos])) { + _print_mode(dev); + return 0; + } + + if (!strcmp("repeat", argv[pos])) { + if (++pos == argc) { + print_str("Error: Missing parameter for command \"repeat\"\n"); + return 1; + } + else { + bool repeat = (atoi(argv[pos])) ? true : false; + int retval = dfplayer_repeat(dev, repeat); + if (retval) { + _print_error(retval); + return 1; + } + } + return 0; + } + + if (!strcmp("repeat-folder", argv[pos])) { + if (++pos == argc) { + print_str("Error: Missing argument for command \"repeat\"\n"); + return 1; + } + uint8_t folder = atoi(argv[pos]); + int retval = dfplayer_repeat_folder(dev, folder); + if (retval) { + _print_error(retval); + return 1; + } + + return 0; + } + + if (!strcmp("state", argv[pos])) { + _print_state(dev); + return 0; + } + + if (!strcmp("shuffle", argv[pos])) { + int retval = dfplayer_shuffle_all(dev); + if (retval) { + _print_error(retval); + return 1; + } + return 0; + } + + if (!strcmp("mp3", argv[pos])) { + if (++pos == argc) { + print_str("Error: Missing parameter for command \"mp3\"\n"); + return 1; + } + uint16_t file = atoi(argv[pos]); + int retval = dfplayer_play_from_mp3(dev, file); + if (retval) { + _print_error(retval); + return 1; + } + + return 0; + } + + if (!strcmp("advert", argv[pos])) { + if (++pos == argc) { + print_str("Error: Missing parameter for command \"advert\"\n"); + return 1; + } + uint16_t file = atoi(argv[pos]); + int retval = dfplayer_play_from_advert(dev, file); + if (retval) { + _print_error(retval); + return 1; + } + + return 0; + } + + if (!strcmp("stop-advert", argv[pos])) { + int retval = dfplayer_stop_advert(dev); + if (retval) { + _print_error(retval); + return 1; + } + return 0; + } + + if (!strcmp("cmd", argv[pos])) { + uint8_t cmd, p1, p2; + uint16_t resp; + p1 = p2 = 0; + + if (++pos == argc) { + print_str("Error: Missing for command \"cmd\"\n"); + } + + cmd = (uint8_t)scn_u32_hex(argv[pos], 2); + if (++pos < argc) { + p2 = (uint8_t)scn_u32_hex(argv[pos], 2); + } + + if (++pos < argc) { + p1 = p2; + p2 = (uint8_t)scn_u32_hex(argv[pos], 2); + } + + print_str("Sending cmd = 0x"); + print_u32_hex(cmd); + print_str(" with params = 0x"); + print_u32_hex(p1); + print_str(", 0x"); + print_u32_hex(p2); + print_str(": "); + int retval = dfplayer_transceive(dev, &resp, cmd, p1, p2); + if (retval) { + _print_error(retval); + return 1; + } + else { + print_str("Response = 0x"); + print_u32_hex(resp); + print("\n", 1); + } + return 0; + } + + if (strcmp("-h", argv[pos]) && strcmp("--help", argv[pos])) { + print_str("Failed to parse command \""); + print_str(argv[pos]); + print_str("\"\n"); + } + + _print_help(argv[0]); + return 1; +} diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index a4da347bf0..c9466a1290 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -30,6 +30,10 @@ extern int _version_handler(int argc, char **argv); extern int _id_handler(int argc, char **argv); #endif +#ifdef MODULE_DFPLAYER +extern int _sc_dfplayer(int argc, char **argv); +#endif + #ifdef MODULE_HEAP_CMD extern int _heap_handler(int argc, char **argv); #endif @@ -301,6 +305,9 @@ const shell_command_t _shell_command_list[] = { #endif #ifdef MODULE_CRYPTOAUTHLIB { "cryptoauth", "Commands for Microchip CryptoAuth devices", _cryptoauth }, +#endif +#ifdef MODULE_DFPLAYER + {"dfplayer", "Control a DFPlayer Mini MP3 player", _sc_dfplayer}, #endif {NULL, NULL, NULL} };