From b8d4d6bb92a0912e8f4d10afabd5b06ab27f26ff Mon Sep 17 00:00:00 2001 From: Fabian Schlenz Date: Tue, 6 Aug 2019 20:50:11 +0200 Subject: [PATCH] Playing and controlling MP3s works great. \o/ --- include/buttons.h | 14 -- include/config.h | 4 + include/controller.h | 17 ++- include/player.h | 61 +++++++- include/sd_card.h | 8 - include/spi_master.h | 10 +- platformio.ini | 3 +- src/buttons.cpp | 46 ------ src/controller.cpp | 48 +++++- src/main.cpp | 48 ++---- src/player.cpp | 357 ++++++++++++++++++++++++++++++++++++------- src/sd_card.cpp | 9 -- 12 files changed, 432 insertions(+), 193 deletions(-) delete mode 100644 include/buttons.h delete mode 100644 include/sd_card.h delete mode 100644 src/buttons.cpp delete mode 100644 src/sd_card.cpp diff --git a/include/buttons.h b/include/buttons.h deleted file mode 100644 index 2152ab9..0000000 --- a/include/buttons.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "controller.h" - -class Buttons { -private: - unsigned long _debounce_until = 0; - Controller* _controller; - void _debounce(); -public: - Buttons(Controller* c) : _controller(c) {} - void setup(); - void loop(); -}; diff --git a/include/config.h b/include/config.h index 46ef64f..f57bf02 100644 --- a/include/config.h +++ b/include/config.h @@ -34,3 +34,7 @@ #define PIN_BTN_TRACK_PREV D7 #define DEBOUNCE_MILLIS 100 +#define VOLUME_DEFAULT 0xA0 +#define VOLUME_MIN 0x60 +#define VOLUME_MAX 0xC0 +#define VOLUME_STEP 0x08 diff --git a/include/controller.h b/include/controller.h index 0e5cf2c..b09f2c3 100644 --- a/include/controller.h +++ b/include/controller.h @@ -1,17 +1,18 @@ #pragma once +#include +#include "config.h" #include "player.h" -#include "sd_card.h" +#include class Controller { private: + MFRC522* _rfid; + bool _rfid_enabled = true; + void _check_rfid(); + void _check_serial(); Player* _player; - SDCard* _sd_card; - public: - Controller(Player* p, SDCard* c) : _player(p), _sd_card(c) {} - void vol_up(); - void vol_down(); - void track_next(); - void track_prev(); + Controller(Player* p); + void loop(); }; diff --git a/include/player.h b/include/player.h index 7ccd86c..5122349 100644 --- a/include/player.h +++ b/include/player.h @@ -1,29 +1,80 @@ #pragma once -#include "sd_card.h" #include "config.h" +#include +#include +#include +#include #define SCI_MODE 0x00 #define SCI_STATUS 0x01 #define SCI_CLOCKF 0x03 +#define SCI_DECODE_TIME 0x04 +#define SCI_VOL 0x0B +#define SCI_WRAMADDR 0x07 +#define SCI_WRAM 0x06 #define CMD_WRITE 0x02 #define CMD_READ 0x03 +#define ADDR_ENDBYTE 0x1E06 + +#define SM_CANCEL 0x0008 +#define SS_DO_NOT_JUMP 0x8000 + #define XRESET PIN_VS1053_XRESET #define DREQ PIN_VS1053_DREQ +#define XCS PIN_VS1053_XCS +#define XDCS PIN_VS1053_XDCS class Player { private: - SDCard* _sd_card; + enum state { uninitialized, idle, playing, stopping }; + struct album_state { + uint8_t index; + uint32_t position; + }; void _reset(); void _init(); void _wait(); - uint16_t _read_register(uint8_t address, uint32_t spi_speed, uint16_t t);//=SPI_CLOCK_DIV4); - void _write_register(uint8_t address, uint16_t value, uint32_t spi_speed);//=SPI_CLOCK_DIV2); + uint16_t _read_control_register(uint8_t address); + void _write_control_register(uint8_t address, uint16_t value); + void _write_data(uint8_t* data); + uint16_t _read_wram(uint16_t address); + state _state = state::uninitialized; + void _refill(); + void _flush_and_cancel(); + void _flush(uint bytes); + void _set_last_track(const char* album, uint8_t track, uint32_t position); + std::map _last_tracks; + void _finish_playing(); + void _finish_stopping(); + void _mute(); + void _unmute(); + + SPISettings _spi_settings_slow = SPISettings(250000, MSBFIRST, SPI_MODE0); + SPISettings _spi_settings_fast = SPISettings(4000000, MSBFIRST, SPI_MODE0); + SPISettings* _spi_settings = &_spi_settings_slow; + + std::list _files_in_dir(String dir); + File _file; + uint8_t _buffer[32]; + String _playing_album; + uint8_t _playing_index; + uint _refills; + int8_t _end_byte; + uint8_t _volume; + uint16_t _stop_delay; + uint32_t _skip_to; public: - Player(SDCard* c); + Player(); void vol_up(); void vol_down(); void track_next(); void track_prev(); + + bool play_album(String album); + bool play_song(String album, uint8_t song_index, uint32_t offset=0); + void stop(); + bool loop(); + void set_volume(uint8_t vol, bool save = true); }; diff --git a/include/sd_card.h b/include/sd_card.h deleted file mode 100644 index 96c4a30..0000000 --- a/include/sd_card.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include -#include "config.h" - -class SDCard { -public: - SDCard(); -}; diff --git a/include/spi_master.h b/include/spi_master.h index 6f8ffe3..315b51f 100644 --- a/include/spi_master.h +++ b/include/spi_master.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include "config.h" + class SPIMaster { public: static void init() { @@ -16,9 +20,9 @@ public: static void printStatus() { Serial.printf("CS state: SD:%d, VS1053_XCS:%d, VS1053_XDCS:%d\n", - digitalRead(PIN_SD_CS), - digitalRead(PIN_VS1053_XCS), - digitalRead(PIN_VS1053_XDCS)); + digitalRead(PIN_SD_CS), + digitalRead(PIN_VS1053_XCS), + digitalRead(PIN_VS1053_XDCS)); } static void disable() { diff --git a/platformio.ini b/platformio.ini index d33493a..85f2c5a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,5 +12,6 @@ platform = espressif8266 board = esp12e framework = arduino +upload_speed = 512000 +lib_deps = 63 upload_port = /dev/cu.wchusbserial1420 -upload_speed = 921600 diff --git a/src/buttons.cpp b/src/buttons.cpp deleted file mode 100644 index 919beeb..0000000 --- a/src/buttons.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include "buttons.h" -#include "config.h" - -void Buttons::setup() { - pinMode(PIN_BTN_VOL_UP, INPUT); - pinMode(PIN_BTN_VOL_DOWN, INPUT); - pinMode(PIN_BTN_TRACK_NEXT, INPUT); - pinMode(PIN_BTN_TRACK_PREV, INPUT); -} - -void Buttons::_debounce() { - unsigned long now = millis(); - _debounce_until = now + DEBOUNCE_MILLIS; - if (_debounce_until < now) _debounce_until = now; -} - -void Buttons::loop() { - bool vol_up = digitalRead(PIN_BTN_VOL_UP); - bool vol_down = digitalRead(PIN_BTN_VOL_DOWN); - bool track_next = digitalRead(PIN_BTN_TRACK_NEXT); - bool track_prev = digitalRead(PIN_BTN_TRACK_PREV); - - if (_debounce_until > millis()) { - if (vol_up || vol_down || track_next || track_prev) { - _debounce(); - } - return; - } - - if (vol_up) { - _controller->vol_up(); - } else if (vol_down) { - _controller->vol_down(); - } else if (track_next) { - _controller->track_next(); - } else if (track_prev) { - _controller->track_prev(); - } else { - // If we reach this, no button was pressed and we are not debouncing -> do nothing. - return; - } - - // If we reach this, some button was pressed. So enable debouncing. - _debounce(); -} diff --git a/src/controller.cpp b/src/controller.cpp index 4998ad2..50a1ef5 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -1,17 +1,49 @@ #include "controller.h" +#include "spi_master.h" -void Controller::vol_up() { - _player->vol_up(); +Controller::Controller(Player* p) { + _player = p; + _rfid = new MFRC522(PIN_RC522_CS, MFRC522::UNUSED_PIN); + + SPIMaster::enable(PIN_RC522_CS); + Serial.println("Initializing MFRC522..."); + _rfid->PCD_Init(); + _rfid->PCD_DumpVersionToSerial(); + SPIMaster::disable(); } -void Controller::vol_down() { - _player->vol_down(); +void Controller::loop() { + _check_rfid(); + _check_serial(); } -void Controller::track_next() { - _player->track_next(); +void Controller::_check_rfid() { + SPIMaster::enable(PIN_RC522_CS); + if (!_rfid->PICC_IsNewCardPresent()) { + return; + } + if (!_rfid->PICC_ReadCardSerial()) { + return; + } + _rfid->PICC_DumpToSerial(&(_rfid->uid)); + SPIMaster::disable(); } -void Controller::track_prev() { - _player->track_prev(); +void Controller::_check_serial() { + if (Serial.available() > 0) { + char c = Serial.read(); + if (c == 'n') { + _player->track_next(); + } else if (c=='p') { + _player->track_prev(); + } else if (c=='s') { + _player->stop(); + } else if (c=='+') { + _player->vol_up(); + } else if (c=='-') { + _player->vol_down(); + } else if (c==' ') { + _player->play_album("12345678"); + } + } } diff --git a/src/main.cpp b/src/main.cpp index 7376551..ed884b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,42 +1,16 @@ #include #include +#include #include "config.h" -#include "buttons.h" #include "controller.h" #include "player.h" -#include "sd_card.h" #include "spi_master.h" -Buttons* buttons; Controller* controller; Player* player; -SDCard* sd; - -void printDirectory(File dir, int numTabs) { - while (true) { - - File entry = dir.openNextFile(); - if (! entry) { - // no more files - break; - } - for (uint8_t i = 0; i < numTabs; i++) { - Serial.print('\t'); - } - Serial.print(entry.name()); - if (entry.isDirectory()) { - Serial.println("/"); - printDirectory(entry, numTabs + 1); - } else { - // files have sizes, directories do not - Serial.print("\t\t"); - Serial.println(entry.size(), DEC); - } - entry.close(); - } -} void setup() { + delay(500); Serial.begin(74880); Serial.println("Starting."); @@ -44,12 +18,22 @@ void setup() { SPI.begin(); SPIMaster::init(); - sd = new SDCard(); - player = new Player(sd); - controller = new Controller(player, sd); - buttons = new Buttons(controller); + SPIMaster::enable(PIN_SD_CS); + if (SD.begin(PIN_SD_CS)) { + Serial.println("SD card initialized."); + } else { + Serial.println("Could not initialize SD card. Halting."); + while(1); + } + player = new Player(); + controller = new Controller(player); + + //player->play_album("12345678"); } void loop() { + bool more_data_needed = player->loop(); + if (more_data_needed) return; + controller->loop(); } diff --git a/src/player.cpp b/src/player.cpp index c06496b..026c47f 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3,7 +3,9 @@ #include "player.h" #include "spi_master.h" -Player::Player(SDCard* c) { +//Player::_spi_settings + +Player::Player() { pinMode(XRESET, OUTPUT); digitalWrite(XRESET, HIGH); pinMode(DREQ, INPUT); @@ -13,87 +15,324 @@ Player::Player(SDCard* c) { void Player::_reset() { digitalWrite(XRESET, LOW); - delay(50); + delay(100); digitalWrite(XRESET, HIGH); - delay(50); + delay(100); + _state = uninitialized; + _spi_settings = &_spi_settings_slow; // After reset, communication has to be slow } void Player::_init() { - SPI.setClockDivider(SPI_CLOCK_DIV16); - + SPIMaster::disable(); _reset(); - uint16_t result; - for(uint8_t x=0; x<255; x++) { - result = _read_register(SCI_MODE, 0, x); - if (result != 0) Serial.printf("Try %3d: 0x%04X\n", x, result); + + uint16_t result = _read_control_register(SCI_MODE); + Serial.printf("SCI_MODE: 0x%04X\n", result); + if (result != 0x4800) { + Serial.printf("Error: SCI_MODE was 0x%04X, expected was 0x4800.\n", result); + return; } - delay(10); - /*_read_register(SCI_MODE, SPI_CLOCK_DIV16); // First read fails for some unknown reason... - _read_register(SCI_MODE, SPI_CLOCK_DIV16); - _read_register(SCI_STATUS, SPI_CLOCK_DIV16); - - _read_register(SCI_STATUS, SPI_CLOCK_DIV16); - - - _read_register(SCI_MODE, SPI_CLOCK_DIV16); - _read_register(SCI_MODE, SPI_CLOCK_DIV16); - uint16_t response = _read_register(SCI_MODE, SPI_CLOCK_DIV16); - if (response != 0x4800) { - Serial.printf("Initialization failed. SCI_MODE was: 0x%04X. Expected: 0x4800\n", response); + result = _read_control_register(SCI_STATUS); + Serial.printf("SCI_STATUS: 0x%04X\n", result); + if (result != 0x0040 && result != 0x0048) { + Serial.printf("Error: SCI_STATUS was 0x%04X, expected was 0x0040 or 0x0048.\n", result); return; - }*/ - /*_write_register(SCI_CLOCKF, 0x6000); // Set multiplier to 3x + } + result = _read_control_register(SCI_CLOCKF); + Serial.printf("SCI_CLOCKF: 0x%04X\n", result); + + Serial.println("VS1053 Init looking good."); + Serial.println("Upping VS1053 multiplier..."); + + _write_control_register(SCI_CLOCKF, 0x6000); delay(10); - response = _read_register(SCI_CLOCKF); - if (response != 0x6000) { - Serial.printf("Initialization failed. SCI_CLOCKF was: 0x%04X. Expected: 0x6000\n", response); + + _spi_settings = &_spi_settings_fast; + + result = _read_control_register(SCI_CLOCKF); + Serial.printf("SCI_CLOCKF: 0x%04X\n", result); + if (result != 0x6000) { + Serial.printf("Error: SCI_CLOCKF was 0x%04X, expected was 0x6000.\n", result); return; - }*/ - //Serial.println(result, HEX); + } + + _end_byte = (int8_t)(_read_wram(ADDR_ENDBYTE) & 0xFF); + + set_volume(VOLUME_DEFAULT); + + Serial.println("VS1053 initialization completed."); + _state = idle; } inline void Player::_wait() { - delayMicroseconds(100); - //Serial.print("Waiting for DREQ..."); while(!digitalRead(DREQ)); - //Serial.printf(" done (%d cycles).\n", i); } -uint16_t Player::_read_register(uint8_t address, uint32_t spi_speed, uint16_t t) { - Serial.printf("Try %03d, querying register 0x%02X...", t, address); - //SPI.setClockDivider(spi_speed); - if (t & (1<<1)) _wait(); - SPIMaster::enable(PIN_VS1053_XCS); - if (t & (1<<2)) _wait(); - //_wait(); +uint16_t Player::_read_control_register(uint8_t address) { + _wait(); + SPIMaster::enable(XCS); + SPI.beginTransaction(*_spi_settings); SPI.transfer(CMD_READ); - if (t & (1<<3)) _wait(); SPI.transfer(address); - if (t & (1<<4)) _wait(); uint8_t b1 = SPI.transfer(0xFF); - if (t & (1<<5)) _wait(); + _wait(); uint8_t b2 = SPI.transfer(0xFF); - if (t & (1<<6)) _wait(); + _wait(); + SPI.endTransaction(); + SPIMaster::disable(); - uint16_t result = b1 << 8 | b2; - Serial.printf("Response: 0x%02X 0x%02X -> 0x%04X\n", b1, b2, result); - if (t & (1<<7)) delay(10); + return (b1 << 8) | b2; +} - //_wait(); - //SPIMaster::disable(); +void Player::_write_control_register(uint8_t address, uint16_t value) { + uint8_t b1 = value >> 8; + uint8_t b2 = value & 0xFF; + _wait(); + SPIMaster::enable(XCS); + SPI.beginTransaction(*_spi_settings); + SPI.transfer(CMD_WRITE); + SPI.transfer(address); + SPI.transfer(b1); + SPI.transfer(b2); + _wait(); + SPI.endTransaction(); + SPIMaster::disable(); +} + +void Player::_write_data(uint8_t* buffer) { + SPIMaster::enable(XDCS); + SPI.beginTransaction(*_spi_settings); + for (uint i=0; i VOLUME_MAX) set_volume(VOLUME_MAX); + else set_volume(_volume + VOLUME_STEP); +} + +void Player::vol_down() { + if (_volume >= VOLUME_MIN + VOLUME_STEP) set_volume(_volume - VOLUME_STEP); + else set_volume(VOLUME_MIN); +} + +void Player::_mute() { + Serial.println("Muting."); + set_volume(0, false); +} + +void Player::_unmute() { + Serial.println("Unmuting."); + set_volume(_volume, false); +} + +void Player::track_next() { + if (_state != playing) return; + stop(); + play_song(_playing_album, _playing_index + 1); +} + +void Player::track_prev() { + if (_state != playing) return; + if (_playing_index == 0) _playing_index=1; + stop(); + play_song(_playing_album, _playing_index - 1); +} + +std::list Player::_files_in_dir(String path) { + Serial.printf("Examining folder %s...\n", path.c_str()); + if (!path.startsWith("/")) path = String("/") + path; + if (!path.endsWith("/")) path.concat("/"); + std::list result; + if (!SD.exists(path)) return result; + File dir = SD.open(path); + File entry; + while (entry = dir.openNextFile()) { + String filename = entry.name(); + if (!entry.isDirectory() && !filename.startsWith(".") && filename.endsWith(".mp3")) { + //Serial.printf("Adding file %s\n", filename.c_str()); + result.push_back(path + filename); + } else { + //Serial.printf("Ignoring entry %s\n", filename.c_str()); + } + entry.close(); + } + dir.close(); + result.sort(); return result; } -void Player::_write_register(uint8_t address, uint16_t value, uint32_t spi_speed) { - Serial.printf("Writing to register 0x%02X...", address); - SPI.setClockDivider(spi_speed); - SPIMaster::enable(PIN_VS1053_XCS); - SPI.transfer(CMD_WRITE); - SPI.transfer(address); - SPI.transfer(value >> 8); - SPI.transfer(value & 0xFF); - _wait(); - SPIMaster::disable(); - Serial.println(" done."); +bool Player::play_album(String album) { + album_state s = _last_tracks[album.c_str()]; + Serial.printf("Last index for album %s was %d,%d\n", album.c_str(), s.index, s.position); + return play_song(album, s.index, s.position); +} + +bool Player::play_song(String album, uint8_t index, uint32_t skip_to) { + if (_state != idle) return false; + Serial.printf("Playing song at index %d, offset %d of album %s\n", index, skip_to, album.c_str()); + std::list files = _files_in_dir(album); + Serial.printf("Found %d songs in album\n", files.size()); + if (index >= files.size()) { + Serial.println("No matching file found - not playing."); + return false; + } + //std::list::iterator it = files.begin(); + //std::advance(it, index); + String file = *(std::next(files.begin(), index)); + Serial.printf("Opening file %s for playback...\n", file.c_str()); + + _file = SD.open(file); + + Serial.println("Resetting SCI_DECODE_TIME..."); + _write_control_register(SCI_DECODE_TIME, 0); + Serial.println("Resetting SS_DO_NOT_JUMP..."); + _write_control_register(SCI_STATUS, _read_control_register(SCI_STATUS) & ~SS_DO_NOT_JUMP); + delay(100); + _state = playing; + _playing_album = album; + _playing_index = index; + _refills = 0; + _skip_to = skip_to; + if (_skip_to>0) _mute(); + Serial.println("Now playing."); + _set_last_track(album.c_str(), index, 0); + return true; +} + +void Player::_flush(uint bytes) { + SPIMaster::enable(XDCS); + SPI.beginTransaction(*_spi_settings); + for(uint i=0; i 2048) { + init(); + break; + } + _stop_delay++; + } +} + +void Player::_finish_stopping() { + _state = idle; + if (_file) { + _file.close(); + } + Serial.println("Stopped."); +} + +void Player::_refill() { + SPIMaster::enable(PIN_SD_CS); + _refills++; + if (_refills % 1000 == 0) Serial.print("."); + uint8_t result = _file.read(_buffer, sizeof(_buffer)); + if (result == 0) { + // File is over. + Serial.println("EOF reached."); + _finish_playing(); + _finish_stopping(); + bool result = play_song(_playing_album, _playing_index + 1); + if (!result) { + _set_last_track(_playing_album.c_str(), 0, 0); + } + return; + } + _write_data(_buffer); + + if (_skip_to > 0) { + if (_skip_to > _file.position()) { + uint16_t status = _read_control_register(SCI_STATUS); + if ((status & SS_DO_NOT_JUMP) == 0) { + Serial.printf("Skipping to %d.\n", _skip_to); + _flush(2048); + _file.seek(_skip_to); + _skip_to = 0; + _unmute(); + } + } else { + _skip_to = 0; + _unmute(); + } + } +} + +bool Player::loop() { + if (digitalRead(DREQ) && (_state==playing || _state==stopping)) { + _refill(); + return true; + } + return false; +} + +void Player::_set_last_track(const char* album, uint8_t index, uint32_t position) { + Serial.printf("Setting _last_track[%s]=%d,%d.\n", album, index, position); + _last_tracks[album] = {index, position}; } diff --git a/src/sd_card.cpp b/src/sd_card.cpp deleted file mode 100644 index d6cecb3..0000000 --- a/src/sd_card.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "sd_card.h" - -SDCard::SDCard() { - if (!SD.begin(PIN_SD_CS)) { - Serial.println("Could not initialize SD card."); - } else { - Serial.println("SD card successfully initialized."); - } -}