diff --git a/.gitignore b/.gitignore index 0aab59b2..a8eeaf96 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,12 @@ Temporary Items # TonUINO /tools/*.pyc +/Release/ +/sc-card_priv/ +/sloeber.ino.cpp +/spec.d +/.cproject +/.project +/.pydevproject +/.sproject +/.settings diff --git a/Tonuino.ino b/Tonuino.ino index c3ce9c9b..94f656e1 100644 --- a/Tonuino.ino +++ b/Tonuino.ino @@ -1,10 +1,9 @@ -#include -#include -#include -#include -#include -#include -#include +#include "src/tonuino.hpp" + +#include "src/settings.hpp" +#include "src/mp3.hpp" +#include "src/buttons.hpp" + /* _____ _____ _____ _____ _____ @@ -17,714 +16,20 @@ Information and contribution at https://tonuino.de. */ -// uncomment the below line to enable five button support -//#define FIVEBUTTONS - -static const uint32_t cardCookie = 322417479; - -// DFPlayer Mini -SoftwareSerial mySoftwareSerial(2, 3); // RX, TX -uint16_t numTracksInFolder; -uint16_t currentTrack; -uint16_t firstTrack; -uint8_t queue[255]; -uint8_t volume; - -struct folderSettings { - uint8_t folder; - uint8_t mode; - uint8_t special; - uint8_t special2; -}; - -// this object stores nfc tag data -struct nfcTagObject { - uint32_t cookie; - uint8_t version; - folderSettings nfcFolderSettings; - // uint8_t folder; - // uint8_t mode; - // uint8_t special; - // uint8_t special2; -}; - -// admin settings stored in eeprom -struct adminSettings { - uint32_t cookie; - byte version; - uint8_t maxVolume; - uint8_t minVolume; - uint8_t initVolume; - uint8_t eq; - bool locked; - long standbyTimer; - bool invertVolumeButtons; - folderSettings shortCuts[4]; - uint8_t adminMenuLocked; - uint8_t adminMenuPin[4]; -}; - -adminSettings mySettings; -nfcTagObject myCard; -folderSettings *myFolder; -unsigned long sleepAtMillis = 0; -static uint16_t _lastTrackFinished; - -static void nextTrack(uint16_t track); -uint8_t voiceMenu(int numberOfOptions, int startMessage, int messageOffset, - bool preview = false, int previewFromFolder = 0, int defaultValue = 0, bool exitWithLongPress = false); -bool isPlaying(); -bool checkTwo ( uint8_t a[], uint8_t b[] ); -void writeCard(nfcTagObject nfcTag); -void dump_byte_array(byte * buffer, byte bufferSize); -void adminMenu(bool fromCard = false); -bool knownCard = false; - -// implement a notification class, -// its member methods will get called -// -class Mp3Notify { - public: - static void OnError(uint16_t errorCode) { - // see DfMp3_Error for code meaning - Serial.println(); - Serial.print("Com Error "); - Serial.println(errorCode); - } - static void PrintlnSourceAction(DfMp3_PlaySources source, const char* action) { - if (source & DfMp3_PlaySources_Sd) Serial.print("SD Karte "); - if (source & DfMp3_PlaySources_Usb) Serial.print("USB "); - if (source & DfMp3_PlaySources_Flash) Serial.print("Flash "); - Serial.println(action); - } - static void OnPlayFinished(DfMp3_PlaySources source, uint16_t track) { - // Serial.print("Track beendet"); - // Serial.println(track); - // delay(100); - nextTrack(track); - } - static void OnPlaySourceOnline(DfMp3_PlaySources source) { - PrintlnSourceAction(source, "online"); - } - static void OnPlaySourceInserted(DfMp3_PlaySources source) { - PrintlnSourceAction(source, "bereit"); - } - static void OnPlaySourceRemoved(DfMp3_PlaySources source) { - PrintlnSourceAction(source, "entfernt"); - } -}; - -static DFMiniMp3 mp3(mySoftwareSerial); - -void shuffleQueue() { - // Queue für die Zufallswiedergabe erstellen - for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1; x++) - queue[x] = x + firstTrack; - // Rest mit 0 auffüllen - for (uint8_t x = numTracksInFolder - firstTrack + 1; x < 255; x++) - queue[x] = 0; - // Queue mischen - for (uint8_t i = 0; i < numTracksInFolder - firstTrack + 1; i++) - { - uint8_t j = random (0, numTracksInFolder - firstTrack + 1); - uint8_t t = queue[i]; - queue[i] = queue[j]; - queue[j] = t; - } - /* Serial.println(F("Queue :")); - for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; x++) - Serial.println(queue[x]); - */ -} - -void writeSettingsToFlash() { - Serial.println(F("=== writeSettingsToFlash()")); - int address = sizeof(myFolder->folder) * 100; - EEPROM.put(address, mySettings); -} - -void resetSettings() { - Serial.println(F("=== resetSettings()")); - mySettings.cookie = cardCookie; - mySettings.version = 2; - mySettings.maxVolume = 25; - mySettings.minVolume = 5; - mySettings.initVolume = 15; - mySettings.eq = 1; - mySettings.locked = false; - mySettings.standbyTimer = 0; - mySettings.invertVolumeButtons = true; - mySettings.shortCuts[0].folder = 0; - mySettings.shortCuts[1].folder = 0; - mySettings.shortCuts[2].folder = 0; - mySettings.shortCuts[3].folder = 0; - mySettings.adminMenuLocked = 0; - mySettings.adminMenuPin[0] = 1; - mySettings.adminMenuPin[1] = 1; - mySettings.adminMenuPin[2] = 1; - mySettings.adminMenuPin[3] = 1; - - writeSettingsToFlash(); -} - -void migrateSettings(int oldVersion) { - if (oldVersion == 1) { - Serial.println(F("=== resetSettings()")); - Serial.println(F("1 -> 2")); - mySettings.version = 2; - mySettings.adminMenuLocked = 0; - mySettings.adminMenuPin[0] = 1; - mySettings.adminMenuPin[1] = 1; - mySettings.adminMenuPin[2] = 1; - mySettings.adminMenuPin[3] = 1; - writeSettingsToFlash(); - } -} - -void loadSettingsFromFlash() { - Serial.println(F("=== loadSettingsFromFlash()")); - int address = sizeof(myFolder->folder) * 100; - EEPROM.get(address, mySettings); - if (mySettings.cookie != cardCookie) - resetSettings(); - migrateSettings(mySettings.version); - - Serial.print(F("Version: ")); - Serial.println(mySettings.version); - - Serial.print(F("Maximal Volume: ")); - Serial.println(mySettings.maxVolume); - - Serial.print(F("Minimal Volume: ")); - Serial.println(mySettings.minVolume); - - Serial.print(F("Initial Volume: ")); - Serial.println(mySettings.initVolume); - - Serial.print(F("EQ: ")); - Serial.println(mySettings.eq); - - Serial.print(F("Locked: ")); - Serial.println(mySettings.locked); - - Serial.print(F("Sleep Timer: ")); - Serial.println(mySettings.standbyTimer); - - Serial.print(F("Inverted Volume Buttons: ")); - Serial.println(mySettings.invertVolumeButtons); - - Serial.print(F("Admin Menu locked: ")); - Serial.println(mySettings.adminMenuLocked); - - Serial.print(F("Admin Menu Pin: ")); - Serial.print(mySettings.adminMenuPin[0]); - Serial.print(mySettings.adminMenuPin[1]); - Serial.print(mySettings.adminMenuPin[2]); - Serial.println(mySettings.adminMenuPin[3]); -} - -class Modifier { - public: - virtual void loop() {} - virtual bool handlePause() { - return false; - } - virtual bool handleNext() { - return false; - } - virtual bool handlePrevious() { - return false; - } - virtual bool handleNextButton() { - return false; - } - virtual bool handlePreviousButton() { - return false; - } - virtual bool handleVolumeUp() { - return false; - } - virtual bool handleVolumeDown() { - return false; - } - virtual bool handleRFID(nfcTagObject *newCard) { - return false; - } - virtual uint8_t getActive() { - return 0; - } - Modifier() { - - } -}; - -Modifier *activeModifier = NULL; - -class SleepTimer: public Modifier { - private: - unsigned long sleepAtMillis = 0; - - public: - void loop() { - if (this->sleepAtMillis != 0 && millis() > this->sleepAtMillis) { - Serial.println(F("=== SleepTimer::loop() -> SLEEP!")); - mp3.pause(); - setstandbyTimer(); - activeModifier = NULL; - delete this; - } - } - - SleepTimer(uint8_t minutes) { - Serial.println(F("=== SleepTimer()")); - Serial.println(minutes); - this->sleepAtMillis = millis() + minutes * 60000; - // if (isPlaying()) - // mp3.playAdvertisement(302); - // delay(500); - } - uint8_t getActive() { - Serial.println(F("== SleepTimer::getActive()")); - return 1; - } -}; - -class FreezeDance: public Modifier { - private: - unsigned long nextStopAtMillis = 0; - const uint8_t minSecondsBetweenStops = 5; - const uint8_t maxSecondsBetweenStops = 30; - - void setNextStopAtMillis() { - uint16_t seconds = random(this->minSecondsBetweenStops, this->maxSecondsBetweenStops + 1); - Serial.println(F("=== FreezeDance::setNextStopAtMillis()")); - Serial.println(seconds); - this->nextStopAtMillis = millis() + seconds * 1000; - } - - public: - void loop() { - if (this->nextStopAtMillis != 0 && millis() > this->nextStopAtMillis) { - Serial.println(F("== FreezeDance::loop() -> FREEZE!")); - if (isPlaying()) { - mp3.playAdvertisement(301); - delay(500); - } - setNextStopAtMillis(); - } - } - FreezeDance(void) { - Serial.println(F("=== FreezeDance()")); - if (isPlaying()) { - delay(1000); - mp3.playAdvertisement(300); - delay(500); - } - setNextStopAtMillis(); - } - uint8_t getActive() { - Serial.println(F("== FreezeDance::getActive()")); - return 2; - } -}; - -class Locked: public Modifier { - public: - virtual bool handlePause() { - Serial.println(F("== Locked::handlePause() -> LOCKED!")); - return true; - } - virtual bool handleNextButton() { - Serial.println(F("== Locked::handleNextButton() -> LOCKED!")); - return true; - } - virtual bool handlePreviousButton() { - Serial.println(F("== Locked::handlePreviousButton() -> LOCKED!")); - return true; - } - virtual bool handleVolumeUp() { - Serial.println(F("== Locked::handleVolumeUp() -> LOCKED!")); - return true; - } - virtual bool handleVolumeDown() { - Serial.println(F("== Locked::handleVolumeDown() -> LOCKED!")); - return true; - } - virtual bool handleRFID(nfcTagObject *newCard) { - Serial.println(F("== Locked::handleRFID() -> LOCKED!")); - return true; - } - Locked(void) { - Serial.println(F("=== Locked()")); - // if (isPlaying()) - // mp3.playAdvertisement(303); - } - uint8_t getActive() { - return 3; - } -}; - -class ToddlerMode: public Modifier { - public: - virtual bool handlePause() { - Serial.println(F("== ToddlerMode::handlePause() -> LOCKED!")); - return true; - } - virtual bool handleNextButton() { - Serial.println(F("== ToddlerMode::handleNextButton() -> LOCKED!")); - return true; - } - virtual bool handlePreviousButton() { - Serial.println(F("== ToddlerMode::handlePreviousButton() -> LOCKED!")); - return true; - } - virtual bool handleVolumeUp() { - Serial.println(F("== ToddlerMode::handleVolumeUp() -> LOCKED!")); - return true; - } - virtual bool handleVolumeDown() { - Serial.println(F("== ToddlerMode::handleVolumeDown() -> LOCKED!")); - return true; - } - ToddlerMode(void) { - Serial.println(F("=== ToddlerMode()")); - // if (isPlaying()) - // mp3.playAdvertisement(304); - } - uint8_t getActive() { - Serial.println(F("== ToddlerMode::getActive()")); - return 4; - } -}; - -class KindergardenMode: public Modifier { - private: - nfcTagObject nextCard; - bool cardQueued = false; - - public: - virtual bool handleNext() { - Serial.println(F("== KindergardenMode::handleNext() -> NEXT")); - //if (this->nextCard.cookie == cardCookie && this->nextCard.nfcFolderSettings.folder != 0 && this->nextCard.nfcFolderSettings.mode != 0) { - //myFolder = &this->nextCard.nfcFolderSettings; - if (this->cardQueued == true) { - this->cardQueued = false; - - myCard = nextCard; - myFolder = &myCard.nfcFolderSettings; - Serial.println(myFolder->folder); - Serial.println(myFolder->mode); - playFolder(); - return true; - } - return false; - } - // virtual bool handlePause() { - // Serial.println(F("== KindergardenMode::handlePause() -> LOCKED!")); - // return true; - // } - virtual bool handleNextButton() { - Serial.println(F("== KindergardenMode::handleNextButton() -> LOCKED!")); - return true; - } - virtual bool handlePreviousButton() { - Serial.println(F("== KindergardenMode::handlePreviousButton() -> LOCKED!")); - return true; - } - virtual bool handleRFID(nfcTagObject * newCard) { // lot of work to do! - Serial.println(F("== KindergardenMode::handleRFID() -> queued!")); - this->nextCard = *newCard; - this->cardQueued = true; - if (!isPlaying()) { - handleNext(); - } - return true; - } - KindergardenMode() { - Serial.println(F("=== KindergardenMode()")); - // if (isPlaying()) - // mp3.playAdvertisement(305); - // delay(500); - } - uint8_t getActive() { - Serial.println(F("== KindergardenMode::getActive()")); - return 5; - } -}; - -class RepeatSingleModifier: public Modifier { - public: - virtual bool handleNext() { - Serial.println(F("== RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); - delay(50); - if (isPlaying()) return true; - if (myFolder->mode == 3 || myFolder->mode == 9){ - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } - else{ - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - _lastTrackFinished = 0; - return true; - } - RepeatSingleModifier() { - Serial.println(F("=== RepeatSingleModifier()")); - } - uint8_t getActive() { - Serial.println(F("== RepeatSingleModifier::getActive()")); - return 6; - } -}; - -// An modifier can also do somethings in addition to the modified action -// by returning false (not handled) at the end -// This simple FeedbackModifier will tell the volume before changing it and -// give some feedback once a RFID card is detected. -class FeedbackModifier: public Modifier { - public: - virtual bool handleVolumeDown() { - if (volume > mySettings.minVolume) { - mp3.playAdvertisement(volume - 1); - } - else { - mp3.playAdvertisement(volume); - } - delay(500); - Serial.println(F("== FeedbackModifier::handleVolumeDown()!")); - return false; - } - virtual bool handleVolumeUp() { - if (volume < mySettings.maxVolume) { - mp3.playAdvertisement(volume + 1); - } - else { - mp3.playAdvertisement(volume); - } - delay(500); - Serial.println(F("== FeedbackModifier::handleVolumeUp()!")); - return false; - } - virtual bool handleRFID(nfcTagObject *newCard) { - Serial.println(F("== FeedbackModifier::handleRFID()")); - return false; - } -}; - -// Leider kann das Modul selbst keine Queue abspielen, daher müssen wir selbst die Queue verwalten -static void nextTrack(uint16_t track) { - Serial.println(track); - if (activeModifier != NULL) - if (activeModifier->handleNext() == true) - return; - - if (track == _lastTrackFinished) { - return; - } - _lastTrackFinished = track; - - if (knownCard == false) - // Wenn eine neue Karte angelernt wird soll das Ende eines Tracks nicht - // verarbeitet werden - return; - - Serial.println(F("=== nextTrack()")); - - if (myFolder->mode == 1 || myFolder->mode == 7) { - Serial.println(F("Hörspielmodus ist aktiv -> keinen neuen Track spielen")); - setstandbyTimer(); - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - } - if (myFolder->mode == 2 || myFolder->mode == 8) { - if (currentTrack != numTracksInFolder) { - currentTrack = currentTrack + 1; - mp3.playFolderTrack(myFolder->folder, currentTrack); - Serial.print(F("Albummodus ist aktiv -> nächster Track: ")); - Serial.print(currentTrack); - } else - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - setstandbyTimer(); - { } - } - if (myFolder->mode == 3 || myFolder->mode == 9) { - if (currentTrack != numTracksInFolder - firstTrack + 1) { - Serial.print(F("Party -> weiter in der Queue ")); - currentTrack++; - } else { - Serial.println(F("Ende der Queue -> beginne von vorne")); - currentTrack = 1; - //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren - // Serial.println(F("Ende der Queue -> mische neu")); - // shuffleQueue(); - } - Serial.println(queue[currentTrack - 1]); - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } - - if (myFolder->mode == 4) { - Serial.println(F("Einzel Modus aktiv -> Strom sparen")); - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - setstandbyTimer(); - } - if (myFolder->mode == 5) { - if (currentTrack != numTracksInFolder) { - currentTrack = currentTrack + 1; - Serial.print(F("Hörbuch Modus ist aktiv -> nächster Track und " - "Fortschritt speichern")); - Serial.println(currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - // Fortschritt im EEPROM abspeichern - EEPROM.update(myFolder->folder, currentTrack); - } else { - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - // Fortschritt zurück setzen - EEPROM.update(myFolder->folder, 1); - setstandbyTimer(); - } - } - delay(500); -} - -static void previousTrack() { - Serial.println(F("=== previousTrack()")); - /* if (myCard.mode == 1 || myCard.mode == 7) { - Serial.println(F("Hörspielmodus ist aktiv -> Track von vorne spielen")); - mp3.playFolderTrack(myCard.folder, currentTrack); - }*/ - if (myFolder->mode == 2 || myFolder->mode == 8) { - Serial.println(F("Albummodus ist aktiv -> vorheriger Track")); - if (currentTrack != firstTrack) { - currentTrack = currentTrack - 1; - } - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - if (myFolder->mode == 3 || myFolder->mode == 9) { - if (currentTrack != 1) { - Serial.print(F("Party Modus ist aktiv -> zurück in der Qeueue ")); - currentTrack--; - } - else - { - Serial.print(F("Anfang der Queue -> springe ans Ende ")); - currentTrack = numTracksInFolder; - } - Serial.println(queue[currentTrack - 1]); - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } - if (myFolder->mode == 4) { - Serial.println(F("Einzel Modus aktiv -> Track von vorne spielen")); - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - if (myFolder->mode == 5) { - Serial.println(F("Hörbuch Modus ist aktiv -> vorheriger Track und " - "Fortschritt speichern")); - if (currentTrack != 1) { - currentTrack = currentTrack - 1; - } - mp3.playFolderTrack(myFolder->folder, currentTrack); - // Fortschritt im EEPROM abspeichern - EEPROM.update(myFolder->folder, currentTrack); - } - delay(1000); -} - -// MFRC522 -#define RST_PIN 9 // Configurable, see typical pin layout above -#define SS_PIN 10 // Configurable, see typical pin layout above -MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 -MFRC522::MIFARE_Key key; -bool successRead; -byte sector = 1; -byte blockAddr = 4; -byte trailerBlock = 7; -MFRC522::StatusCode status; - -#define buttonPause A0 -#define buttonUp A1 -#define buttonDown A2 -#define busyPin 4 -#define shutdownPin 7 -#define openAnalogPin A7 - -#ifdef FIVEBUTTONS -#define buttonFourPin A3 -#define buttonFivePin A4 -#endif - -#define LONG_PRESS 1000 - -Button pauseButton(buttonPause); -Button upButton(buttonUp); -Button downButton(buttonDown); -#ifdef FIVEBUTTONS -Button buttonFour(buttonFourPin); -Button buttonFive(buttonFivePin); -#endif -bool ignorePauseButton = false; -bool ignoreUpButton = false; -bool ignoreDownButton = false; -#ifdef FIVEBUTTONS -bool ignoreButtonFour = false; -bool ignoreButtonFive = false; -#endif +namespace { -/// Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) +const uint8_t openAnalogPin = A7; -void setstandbyTimer() { - Serial.println(F("=== setstandbyTimer()")); - if (mySettings.standbyTimer != 0) - sleepAtMillis = millis() + (mySettings.standbyTimer * 60 * 1000); - else - sleepAtMillis = 0; - Serial.println(sleepAtMillis); } -void disablestandbyTimer() { - Serial.println(F("=== disablestandby()")); - sleepAtMillis = 0; -} - -void checkStandbyAtMillis() { - if (sleepAtMillis != 0 && millis() > sleepAtMillis) { - Serial.println(F("=== power off!")); - // enter sleep state - digitalWrite(shutdownPin, HIGH); - delay(500); - - // http://discourse.voss.earth/t/intenso-s10000-powerbank-automatische-abschaltung-software-only/805 - // powerdown to 27mA (powerbank switches off after 30-60s) - mfrc522.PCD_AntennaOff(); - mfrc522.PCD_SoftPowerDown(); - mp3.sleep(); - - set_sleep_mode(SLEEP_MODE_PWR_DOWN); - cli(); // Disable interrupts - sleep_mode(); - } -} - -bool isPlaying() { - return !digitalRead(busyPin); -} - -void waitForTrackToFinish() { - long currentTime = millis(); -#define TIMEOUT 1000 - do { - mp3.loop(); - } while (!isPlaying() && millis() < currentTime + TIMEOUT); - delay(1000); - do { - mp3.loop(); - } while (isPlaying()); -} void setup() { Serial.begin(115200); // Es gibt ein paar Debug Ausgaben über die serielle Schnittstelle // Wert für randomSeed() erzeugen durch das mehrfache Sammeln von rauschenden LSBs eines offenen Analogeingangs - uint32_t ADC_LSB; - uint32_t ADCSeed; + uint32_t ADC_LSB = 0; + uint32_t ADCSeed = 0; for (uint8_t i = 0; i < 128; i++) { ADC_LSB = analogRead(openAnalogPin) & 0x1; ADCSeed ^= ADC_LSB << (i % 32); @@ -740,1068 +45,11 @@ void setup() { Serial.println(F("created by Thorsten Voß and licensed under GNU/GPL.")); Serial.println(F("Information and contribution at https://tonuino.de.\n")); - // Busy Pin - pinMode(busyPin, INPUT); - - // load Settings from EEPROM - loadSettingsFromFlash(); - - // activate standby timer - setstandbyTimer(); - - // DFPlayer Mini initialisieren - mp3.begin(); - // Zwei Sekunden warten bis der DFPlayer Mini initialisiert ist - delay(2000); - volume = mySettings.initVolume; - mp3.setVolume(volume); - mp3.setEq(mySettings.eq - 1); - // Fix für das Problem mit dem Timeout (ist jetzt in Upstream daher nicht mehr nötig!) - //mySoftwareSerial.setTimeout(10000); - - // NFC Leser initialisieren - SPI.begin(); // Init SPI bus - mfrc522.PCD_Init(); // Init MFRC522 - mfrc522 - .PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader - for (byte i = 0; i < 6; i++) { - key.keyByte[i] = 0xFF; - } - - pinMode(buttonPause, INPUT_PULLUP); - pinMode(buttonUp, INPUT_PULLUP); - pinMode(buttonDown, INPUT_PULLUP); -#ifdef FIVEBUTTONS - pinMode(buttonFourPin, INPUT_PULLUP); - pinMode(buttonFivePin, INPUT_PULLUP); -#endif - pinMode(shutdownPin, OUTPUT); - digitalWrite(shutdownPin, LOW); - - - // RESET --- ALLE DREI KNÖPFE BEIM STARTEN GEDRÜCKT HALTEN -> alle EINSTELLUNGEN werden gelöscht - if (digitalRead(buttonPause) == LOW && digitalRead(buttonUp) == LOW && - digitalRead(buttonDown) == LOW) { - Serial.println(F("Reset -> EEPROM wird gelöscht")); - for (int i = 0; i < EEPROM.length(); i++) { - EEPROM.update(i, 0); - } - loadSettingsFromFlash(); - } - - - // Start Shortcut "at Startup" - e.g. Welcome Sound - playShortCut(3); -} - -void readButtons() { - pauseButton.read(); - upButton.read(); - downButton.read(); -#ifdef FIVEBUTTONS - buttonFour.read(); - buttonFive.read(); -#endif -} - -void volumeUpButton() { - if (activeModifier != NULL) - if (activeModifier->handleVolumeUp() == true) - return; - - Serial.println(F("=== volumeUp()")); - if (volume < mySettings.maxVolume) { - mp3.increaseVolume(); - volume++; - } - Serial.println(volume); -} - -void volumeDownButton() { - if (activeModifier != NULL) - if (activeModifier->handleVolumeDown() == true) - return; - - Serial.println(F("=== volumeDown()")); - if (volume > mySettings.minVolume) { - mp3.decreaseVolume(); - volume--; - } - Serial.println(volume); -} - -void nextButton() { - if (activeModifier != NULL) - if (activeModifier->handleNextButton() == true) - return; - - nextTrack(random(65536)); - delay(1000); -} - -void previousButton() { - if (activeModifier != NULL) - if (activeModifier->handlePreviousButton() == true) - return; - - previousTrack(); - delay(1000); -} - -void playFolder() { - Serial.println(F("== playFolder()")) ; - disablestandbyTimer(); - knownCard = true; - _lastTrackFinished = 0; - numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); - firstTrack = 1; - Serial.print(numTracksInFolder); - Serial.print(F(" Dateien in Ordner ")); - Serial.println(myFolder->folder); - - // Hörspielmodus: eine zufällige Datei aus dem Ordner - if (myFolder->mode == 1) { - Serial.println(F("Hörspielmodus -> zufälligen Track wiedergeben")); - currentTrack = random(1, numTracksInFolder + 1); - Serial.println(currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - // Album Modus: kompletten Ordner spielen - if (myFolder->mode == 2) { - Serial.println(F("Album Modus -> kompletten Ordner wiedergeben")); - currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - // Party Modus: Ordner in zufälliger Reihenfolge - if (myFolder->mode == 3) { - Serial.println( - F("Party Modus -> Ordner in zufälliger Reihenfolge wiedergeben")); - shuffleQueue(); - currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } - // Einzel Modus: eine Datei aus dem Ordner abspielen - if (myFolder->mode == 4) { - Serial.println( - F("Einzel Modus -> eine Datei aus dem Odrdner abspielen")); - currentTrack = myFolder->special; - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken - if (myFolder->mode == 5) { - Serial.println(F("Hörbuch Modus -> kompletten Ordner spielen und " - "Fortschritt merken")); - currentTrack = EEPROM.read(myFolder->folder); - if (currentTrack == 0 || currentTrack > numTracksInFolder) { - currentTrack = 1; - } - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner - if (myFolder->mode == 7) { - Serial.println(F("Spezialmodus Von-Bin: Hörspiel -> zufälligen Track wiedergeben")); - Serial.print(myFolder->special); - Serial.print(F(" bis ")); - Serial.println(myFolder->special2); - numTracksInFolder = myFolder->special2; - currentTrack = random(myFolder->special, numTracksInFolder + 1); - Serial.println(currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - - // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen - if (myFolder->mode == 8) { - Serial.println(F("Spezialmodus Von-Bis: Album: alle Dateien zwischen Start- und Enddatei spielen")); - Serial.print(myFolder->special); - Serial.print(F(" bis ")); - Serial.println(myFolder->special2); - numTracksInFolder = myFolder->special2; - currentTrack = myFolder->special; - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - - // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge - if (myFolder->mode == 9) { - Serial.println( - F("Spezialmodus Von-Bis: Party -> Ordner in zufälliger Reihenfolge wiedergeben")); - firstTrack = myFolder->special; - numTracksInFolder = myFolder->special2; - shuffleQueue(); - currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } -} - -void playShortCut(uint8_t shortCut) { - Serial.println(F("=== playShortCut()")); - Serial.println(shortCut); - if (mySettings.shortCuts[shortCut].folder != 0) { - myFolder = &mySettings.shortCuts[shortCut]; - playFolder(); - disablestandbyTimer(); - delay(1000); - } - else - Serial.println(F("Shortcut not configured!")); + tonuino.setup(); } void loop() { - do { - checkStandbyAtMillis(); - mp3.loop(); - - // Modifier : WIP! - if (activeModifier != NULL) { - activeModifier->loop(); - } - - // Buttons werden nun über JS_Button gehandelt, dadurch kann jede Taste - // doppelt belegt werden - readButtons(); - - // admin menu - if ((pauseButton.pressedFor(LONG_PRESS) || upButton.pressedFor(LONG_PRESS) || downButton.pressedFor(LONG_PRESS)) && pauseButton.isPressed() && upButton.isPressed() && downButton.isPressed()) { - mp3.pause(); - do { - readButtons(); - } while (pauseButton.isPressed() || upButton.isPressed() || downButton.isPressed()); - readButtons(); - adminMenu(); - break; - } - - if (pauseButton.wasReleased()) { - if (activeModifier != NULL) - if (activeModifier->handlePause() == true) - return; - if (ignorePauseButton == false) - if (isPlaying()) { - mp3.pause(); - setstandbyTimer(); - } - else if (knownCard) { - mp3.start(); - disablestandbyTimer(); - } - ignorePauseButton = false; - } else if (pauseButton.pressedFor(LONG_PRESS) && - ignorePauseButton == false) { - if (activeModifier != NULL) - if (activeModifier->handlePause() == true) - return; - if (isPlaying()) { - uint8_t advertTrack; - if (myFolder->mode == 3 || myFolder->mode == 9) { - advertTrack = (queue[currentTrack - 1]); - } - else { - advertTrack = currentTrack; - } - // Spezialmodus Von-Bis für Album und Party gibt die Dateinummer relativ zur Startposition wieder - if (myFolder->mode == 8 || myFolder->mode == 9) { - advertTrack = advertTrack - myFolder->special + 1; - } - mp3.playAdvertisement(advertTrack); - } - else { - playShortCut(0); - } - ignorePauseButton = true; - } - - if (upButton.pressedFor(LONG_PRESS)) { -#ifndef FIVEBUTTONS - if (isPlaying()) { - if (!mySettings.invertVolumeButtons) { - volumeUpButton(); - } - else { - nextButton(); - } - } - else { - playShortCut(1); - } - ignoreUpButton = true; -#endif - } else if (upButton.wasReleased()) { - if (!ignoreUpButton) - if (!mySettings.invertVolumeButtons) { - nextButton(); - } - else { - volumeUpButton(); - } - ignoreUpButton = false; - } - - if (downButton.pressedFor(LONG_PRESS)) { -#ifndef FIVEBUTTONS - if (isPlaying()) { - if (!mySettings.invertVolumeButtons) { - volumeDownButton(); - } - else { - previousButton(); - } - } - else { - playShortCut(2); - } - ignoreDownButton = true; -#endif - } else if (downButton.wasReleased()) { - if (!ignoreDownButton) { - if (!mySettings.invertVolumeButtons) { - previousButton(); - } - else { - volumeDownButton(); - } - } - ignoreDownButton = false; - } -#ifdef FIVEBUTTONS - if (buttonFour.wasReleased()) { - if (isPlaying()) { - if (!mySettings.invertVolumeButtons) { - volumeUpButton(); - } - else { - nextButton(); - } - } - else { - playShortCut(1); - } - } - if (buttonFive.wasReleased()) { - if (isPlaying()) { - if (!mySettings.invertVolumeButtons) { - volumeDownButton(); - } - else { - previousButton(); - } - } - else { - playShortCut(2); - } - } -#endif - // Ende der Buttons - } while (!mfrc522.PICC_IsNewCardPresent()); - - // RFID Karte wurde aufgelegt - - if (!mfrc522.PICC_ReadCardSerial()) - return; - - if (readCard(&myCard) == true) { - if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.folder != 0 && myCard.nfcFolderSettings.mode != 0) { - playFolder(); - } - - // Neue Karte konfigurieren - else if (myCard.cookie != cardCookie) { - knownCard = false; - mp3.playMp3FolderTrack(300); - waitForTrackToFinish(); - setupCard(); - } - } - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); -} - -void adminMenu(bool fromCard = false) { - disablestandbyTimer(); - mp3.pause(); - Serial.println(F("=== adminMenu()")); - knownCard = false; - if (fromCard == false) { - // Admin menu has been locked - it still can be trigged via admin card - if (mySettings.adminMenuLocked == 1) { - return; - } - // Pin check - else if (mySettings.adminMenuLocked == 2) { - uint8_t pin[4]; - mp3.playMp3FolderTrack(991); - if (askCode(pin) == true) { - if (checkTwo(pin, mySettings.adminMenuPin) == false) { - return; - } - } else { - return; - } - } - // Match check - else if (mySettings.adminMenuLocked == 3) { - uint8_t a = random(10, 20); - uint8_t b = random(1, 10); - uint8_t c; - mp3.playMp3FolderTrack(992); - waitForTrackToFinish(); - mp3.playMp3FolderTrack(a); - - if (random(1, 3) == 2) { - // a + b - c = a + b; - waitForTrackToFinish(); - mp3.playMp3FolderTrack(993); - } else { - // a - b - b = random(1, a); - c = a - b; - waitForTrackToFinish(); - mp3.playMp3FolderTrack(994); - } - waitForTrackToFinish(); - mp3.playMp3FolderTrack(b); - Serial.println(c); - uint8_t temp = voiceMenu(255, 0, 0, false); - if (temp != c) { - return; - } - } - } - int subMenu = voiceMenu(12, 900, 900, false, false, 0, true); - if (subMenu == 0) - return; - if (subMenu == 1) { - resetCard(); - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); - } - else if (subMenu == 2) { - // Maximum Volume - mySettings.maxVolume = voiceMenu(30 - mySettings.minVolume, 930, mySettings.minVolume, false, false, mySettings.maxVolume - mySettings.minVolume) + mySettings.minVolume; - } - else if (subMenu == 3) { - // Minimum Volume - mySettings.minVolume = voiceMenu(mySettings.maxVolume - 1, 931, 0, false, false, mySettings.minVolume); - } - else if (subMenu == 4) { - // Initial Volume - mySettings.initVolume = voiceMenu(mySettings.maxVolume - mySettings.minVolume + 1, 932, mySettings.minVolume - 1, false, false, mySettings.initVolume - mySettings.minVolume + 1) + mySettings.minVolume - 1; - } - else if (subMenu == 5) { - // EQ - mySettings.eq = voiceMenu(6, 920, 920, false, false, mySettings.eq); - mp3.setEq(mySettings.eq - 1); - } - else if (subMenu == 6) { - // create modifier card - nfcTagObject tempCard; - tempCard.cookie = cardCookie; - tempCard.version = 1; - tempCard.nfcFolderSettings.folder = 0; - tempCard.nfcFolderSettings.special = 0; - tempCard.nfcFolderSettings.special2 = 0; - tempCard.nfcFolderSettings.mode = voiceMenu(6, 970, 970, false, false, 0, true); - - if (tempCard.nfcFolderSettings.mode != 0) { - if (tempCard.nfcFolderSettings.mode == 1) { - switch (voiceMenu(4, 960, 960)) { - case 1: tempCard.nfcFolderSettings.special = 5; break; - case 2: tempCard.nfcFolderSettings.special = 15; break; - case 3: tempCard.nfcFolderSettings.special = 30; break; - case 4: tempCard.nfcFolderSettings.special = 60; break; - } - } - mp3.playMp3FolderTrack(800); - do { - readButtons(); - if (upButton.wasReleased() || downButton.wasReleased()) { - Serial.println(F("Abgebrochen!")); - mp3.playMp3FolderTrack(802); - return; - } - } while (!mfrc522.PICC_IsNewCardPresent()); - // RFID Karte wurde aufgelegt - if (mfrc522.PICC_ReadCardSerial()) { - Serial.println(F("schreibe Karte...")); - writeCard(tempCard); - delay(100); - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); - waitForTrackToFinish(); - } - } - } - else if (subMenu == 7) { - uint8_t shortcut = voiceMenu(4, 940, 940); - setupFolder(&mySettings.shortCuts[shortcut - 1]); - mp3.playMp3FolderTrack(400); - } - else if (subMenu == 8) { - switch (voiceMenu(5, 960, 960)) { - case 1: mySettings.standbyTimer = 5; break; - case 2: mySettings.standbyTimer = 15; break; - case 3: mySettings.standbyTimer = 30; break; - case 4: mySettings.standbyTimer = 60; break; - case 5: mySettings.standbyTimer = 0; break; - } - } - else if (subMenu == 9) { - // Create Cards for Folder - // Ordner abfragen - nfcTagObject tempCard; - tempCard.cookie = cardCookie; - tempCard.version = 1; - tempCard.nfcFolderSettings.mode = 4; - tempCard.nfcFolderSettings.folder = voiceMenu(99, 301, 0, true); - uint8_t special = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), 321, 0, - true, tempCard.nfcFolderSettings.folder); - uint8_t special2 = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), 322, 0, - true, tempCard.nfcFolderSettings.folder, special); - - mp3.playMp3FolderTrack(936); - waitForTrackToFinish(); - for (uint8_t x = special; x <= special2; x++) { - mp3.playMp3FolderTrack(x); - tempCard.nfcFolderSettings.special = x; - Serial.print(x); - Serial.println(F(" Karte auflegen")); - do { - readButtons(); - if (upButton.wasReleased() || downButton.wasReleased()) { - Serial.println(F("Abgebrochen!")); - mp3.playMp3FolderTrack(802); - return; - } - } while (!mfrc522.PICC_IsNewCardPresent()); - - // RFID Karte wurde aufgelegt - if (mfrc522.PICC_ReadCardSerial()) { - Serial.println(F("schreibe Karte...")); - writeCard(tempCard); - delay(100); - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); - waitForTrackToFinish(); - } - } - } - else if (subMenu == 10) { - // Invert Functions for Up/Down Buttons - int temp = voiceMenu(2, 933, 933, false); - if (temp == 2) { - mySettings.invertVolumeButtons = true; - } - else { - mySettings.invertVolumeButtons = false; - } - } - else if (subMenu == 11) { - Serial.println(F("Reset -> EEPROM wird gelöscht")); - for (int i = 0; i < EEPROM.length(); i++) { - EEPROM.update(i, 0); - } - resetSettings(); - mp3.playMp3FolderTrack(999); - } - // lock admin menu - else if (subMenu == 12) { - int temp = voiceMenu(4, 980, 980, false); - if (temp == 1) { - mySettings.adminMenuLocked = 0; - } - else if (temp == 2) { - mySettings.adminMenuLocked = 1; - } - else if (temp == 3) { - int8_t pin[4]; - mp3.playMp3FolderTrack(991); - if (askCode(pin)) { - memcpy(mySettings.adminMenuPin, pin, 4); - mySettings.adminMenuLocked = 2; - } - } - else if (temp == 4) { - mySettings.adminMenuLocked = 3; - } - - } - writeSettingsToFlash(); - setstandbyTimer(); -} - -bool askCode(uint8_t *code) { - uint8_t x = 0; - while (x < 4) { - readButtons(); - if (pauseButton.pressedFor(LONG_PRESS)) - break; - if (pauseButton.wasReleased()) - code[x++] = 1; - if (upButton.wasReleased()) - code[x++] = 2; - if (downButton.wasReleased()) - code[x++] = 3; - } - return true; -} - -uint8_t voiceMenu(int numberOfOptions, int startMessage, int messageOffset, - bool preview = false, int previewFromFolder = 0, int defaultValue = 0, bool exitWithLongPress = false) { - uint8_t returnValue = defaultValue; - if (startMessage != 0) - mp3.playMp3FolderTrack(startMessage); - Serial.print(F("=== voiceMenu() (")); - Serial.print(numberOfOptions); - Serial.println(F(" Options)")); - do { - if (Serial.available() > 0) { - int optionSerial = Serial.parseInt(); - if (optionSerial != 0 && optionSerial <= numberOfOptions) - return optionSerial; - } - readButtons(); - mp3.loop(); - if (pauseButton.pressedFor(LONG_PRESS)) { - mp3.playMp3FolderTrack(802); - ignorePauseButton = true; - checkStandbyAtMillis(); - return defaultValue; - } - if (pauseButton.wasReleased()) { - if (returnValue != 0) { - Serial.print(F("=== ")); - Serial.print(returnValue); - Serial.println(F(" ===")); - return returnValue; - } - delay(1000); - } - - if (upButton.pressedFor(LONG_PRESS)) { - returnValue = min(returnValue + 10, numberOfOptions); - Serial.println(returnValue); - //mp3.pause(); - mp3.playMp3FolderTrack(messageOffset + returnValue); - waitForTrackToFinish(); - /*if (preview) { - if (previewFromFolder == 0) - mp3.playFolderTrack(returnValue, 1); - else - mp3.playFolderTrack(previewFromFolder, returnValue); - }*/ - ignoreUpButton = true; - } else if (upButton.wasReleased()) { - if (!ignoreUpButton) { - returnValue = min(returnValue + 1, numberOfOptions); - Serial.println(returnValue); - //mp3.pause(); - mp3.playMp3FolderTrack(messageOffset + returnValue); - if (preview) { - waitForTrackToFinish(); - if (previewFromFolder == 0) { - mp3.playFolderTrack(returnValue, 1); - } else { - mp3.playFolderTrack(previewFromFolder, returnValue); - } - delay(1000); - } - } else { - ignoreUpButton = false; - } - } - - if (downButton.pressedFor(LONG_PRESS)) { - returnValue = max(returnValue - 10, 1); - Serial.println(returnValue); - //mp3.pause(); - mp3.playMp3FolderTrack(messageOffset + returnValue); - waitForTrackToFinish(); - /*if (preview) { - if (previewFromFolder == 0) - mp3.playFolderTrack(returnValue, 1); - else - mp3.playFolderTrack(previewFromFolder, returnValue); - }*/ - ignoreDownButton = true; - } else if (downButton.wasReleased()) { - if (!ignoreDownButton) { - returnValue = max(returnValue - 1, 1); - Serial.println(returnValue); - //mp3.pause(); - mp3.playMp3FolderTrack(messageOffset + returnValue); - if (preview) { - waitForTrackToFinish(); - if (previewFromFolder == 0) { - mp3.playFolderTrack(returnValue, 1); - } - else { - mp3.playFolderTrack(previewFromFolder, returnValue); - } - delay(1000); - } - } else { - ignoreDownButton = false; - } - } - } while (true); -} - -void resetCard() { - mp3.playMp3FolderTrack(800); - do { - pauseButton.read(); - upButton.read(); - downButton.read(); - - if (upButton.wasReleased() || downButton.wasReleased()) { - Serial.print(F("Abgebrochen!")); - mp3.playMp3FolderTrack(802); - return; - } - } while (!mfrc522.PICC_IsNewCardPresent()); - - if (!mfrc522.PICC_ReadCardSerial()) - return; - - Serial.print(F("Karte wird neu konfiguriert!")); - setupCard(); -} - -bool setupFolder(folderSettings * theFolder) { - // Ordner abfragen - theFolder->folder = voiceMenu(99, 301, 0, true, 0, 0, true); - if (theFolder->folder == 0) return false; - - // Wiedergabemodus abfragen - theFolder->mode = voiceMenu(9, 310, 310, false, 0, 0, true); - if (theFolder->mode == 0) return false; - - // // Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen - // EEPROM.update(theFolder->folder, 1); - - // Einzelmodus -> Datei abfragen - if (theFolder->mode == 4) - theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), 320, 0, - true, theFolder->folder); - // Admin Funktionen - if (theFolder->mode == 6) { - //theFolder->special = voiceMenu(3, 320, 320); - theFolder->folder = 0; - theFolder->mode = 255; - } - // Spezialmodus Von-Bis - if (theFolder->mode == 7 || theFolder->mode == 8 || theFolder->mode == 9) { - theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), 321, 0, - true, theFolder->folder); - theFolder->special2 = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), 322, 0, - true, theFolder->folder, theFolder->special); - } - return true; -} - -void setupCard() { - mp3.pause(); - Serial.println(F("=== setupCard()")); - nfcTagObject newCard; - if (setupFolder(&newCard.nfcFolderSettings) == true) - { - // Karte ist konfiguriert -> speichern - mp3.pause(); - do { - } while (isPlaying()); - writeCard(newCard); - } - delay(1000); -} -bool readCard(nfcTagObject * nfcTag) { - nfcTagObject tempCard; - // Show some details of the PICC (that is: the tag/card) - Serial.print(F("Card UID:")); - dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); - Serial.println(); - Serial.print(F("PICC type: ")); - MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); - Serial.println(mfrc522.PICC_GetTypeName(piccType)); - - byte buffer[18]; - byte size = sizeof(buffer); - - // Authenticate using key A - if ((piccType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - Serial.println(F("Authenticating Classic using key A...")); - status = mfrc522.PCD_Authenticate( - MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); - } - else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the tempCard - - // Authenticate using key A - Serial.println(F("Authenticating MIFARE UL...")); - status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); - } - - if (status != MFRC522::STATUS_OK) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - - // Show the whole sector as it currently is - // Serial.println(F("Current data in sector:")); - // mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); - // Serial.println(); - - // Read data from the block - if ((piccType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - Serial.print(F("Reading data from block ")); - Serial.print(blockAddr); - Serial.println(F(" ...")); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(blockAddr, buffer, &size); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - } - else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte buffer2[18]; - byte size2 = sizeof(buffer2); - - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(8, buffer2, &size2); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_1() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - memcpy(buffer, buffer2, 4); - - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(9, buffer2, &size2); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_2() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - memcpy(buffer + 4, buffer2, 4); - - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(10, buffer2, &size2); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_3() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - memcpy(buffer + 8, buffer2, 4); - - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(11, buffer2, &size2); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_4() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - memcpy(buffer + 12, buffer2, 4); - } - - Serial.print(F("Data on Card ")); - Serial.println(F(":")); - dump_byte_array(buffer, 16); - Serial.println(); - Serial.println(); - - uint32_t tempCookie; - tempCookie = (uint32_t)buffer[0] << 24; - tempCookie += (uint32_t)buffer[1] << 16; - tempCookie += (uint32_t)buffer[2] << 8; - tempCookie += (uint32_t)buffer[3]; - - tempCard.cookie = tempCookie; - tempCard.version = buffer[4]; - tempCard.nfcFolderSettings.folder = buffer[5]; - tempCard.nfcFolderSettings.mode = buffer[6]; - tempCard.nfcFolderSettings.special = buffer[7]; - tempCard.nfcFolderSettings.special2 = buffer[8]; - - if (tempCard.cookie == cardCookie) { - - if (activeModifier != NULL && tempCard.nfcFolderSettings.folder != 0) { - if (activeModifier->handleRFID(&tempCard) == true) { - return false; - } - } - - if (tempCard.nfcFolderSettings.folder == 0) { - if (activeModifier != NULL) { - if (activeModifier->getActive() == tempCard.nfcFolderSettings.mode) { - activeModifier = NULL; - Serial.println(F("modifier removed")); - if (isPlaying()) { - mp3.playAdvertisement(261); - } - else { - mp3.start(); - delay(100); - mp3.playAdvertisement(261); - delay(100); - mp3.pause(); - } - delay(2000); - return false; - } - } - if (tempCard.nfcFolderSettings.mode != 0 && tempCard.nfcFolderSettings.mode != 255) { - if (isPlaying()) { - mp3.playAdvertisement(260); - } - else { - mp3.start(); - delay(100); - mp3.playAdvertisement(260); - delay(100); - mp3.pause(); - } - } - switch (tempCard.nfcFolderSettings.mode ) { - case 0: - case 255: - mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); adminMenu(true); break; - case 1: activeModifier = new SleepTimer(tempCard.nfcFolderSettings.special); break; - case 2: activeModifier = new FreezeDance(); break; - case 3: activeModifier = new Locked(); break; - case 4: activeModifier = new ToddlerMode(); break; - case 5: activeModifier = new KindergardenMode(); break; - case 6: activeModifier = new RepeatSingleModifier(); break; - - } - delay(2000); - return false; - } - else { - memcpy(nfcTag, &tempCard, sizeof(nfcTagObject)); - Serial.println( nfcTag->nfcFolderSettings.folder); - myFolder = &nfcTag->nfcFolderSettings; - Serial.println( myFolder->folder); - } - return true; - } - else { - memcpy(nfcTag, &tempCard, sizeof(nfcTagObject)); - return true; - } -} - - -void writeCard(nfcTagObject nfcTag) { - MFRC522::PICC_Type mifareType; - byte buffer[16] = {0x13, 0x37, 0xb3, 0x47, // 0x1337 0xb347 magic cookie to - // identify our nfc tags - 0x02, // version 1 - nfcTag.nfcFolderSettings.folder, // the folder picked by the user - nfcTag.nfcFolderSettings.mode, // the playback mode picked by the user - nfcTag.nfcFolderSettings.special, // track or function for admin cards - nfcTag.nfcFolderSettings.special2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - byte size = sizeof(buffer); - - mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); - - // Authenticate using key B - //authentificate with the card and set card specific parameters - if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - Serial.println(F("Authenticating again using key A...")); - status = mfrc522.PCD_Authenticate( - MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); - } - else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the NFCtag - - // Authenticate using key A - Serial.println(F("Authenticating UL...")); - status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); - } - - if (status != MFRC522::STATUS_OK) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - mp3.playMp3FolderTrack(401); - return; - } - - // Write data to the block - Serial.print(F("Writing data into block ")); - Serial.print(blockAddr); - Serial.println(F(" ...")); - dump_byte_array(buffer, 16); - Serial.println(); - - if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(blockAddr, buffer, 16); - } - else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte buffer2[16]; - byte size2 = sizeof(buffer2); - - memset(buffer2, 0, size2); - memcpy(buffer2, buffer, 4); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(8, buffer2, 16); - - memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 4, 4); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(9, buffer2, 16); - - memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 8, 4); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(10, buffer2, 16); - - memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 12, 4); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(11, buffer2, 16); - } - - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Write() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - mp3.playMp3FolderTrack(401); - } - else - mp3.playMp3FolderTrack(400); - Serial.println(); - delay(2000); -} - - - -/** - Helper routine to dump a byte array as hex values to Serial. -*/ -void dump_byte_array(byte * buffer, byte bufferSize) { - for (byte i = 0; i < bufferSize; i++) { - Serial.print(buffer[i] < 0x10 ? " 0" : " "); - Serial.print(buffer[i], HEX); - } -} - -///////////////////////////////////////// Check Bytes /////////////////////////////////// -bool checkTwo ( uint8_t a[], uint8_t b[] ) { - for ( uint8_t k = 0; k < 4; k++ ) { // Loop 4 times - if ( a[k] != b[k] ) { // IF a != b then false, because: one fails, all fail - return false; - } - } - return true; + tonuino.handleButtons(); + tonuino.handleChipCard(); } diff --git a/sd-card/advert/0262.mp3 b/sd-card/advert/0262.mp3 new file mode 100644 index 00000000..0a191f10 Binary files /dev/null and b/sd-card/advert/0262.mp3 differ diff --git a/sd-card/mp3/0262.mp3 b/sd-card/mp3/0262.mp3 new file mode 100644 index 00000000..0a191f10 Binary files /dev/null and b/sd-card/mp3/0262.mp3 differ diff --git a/src/array.hpp b/src/array.hpp new file mode 100644 index 00000000..cbd45b78 --- /dev/null +++ b/src/array.hpp @@ -0,0 +1,46 @@ +#ifndef SRC_ARRAY_HPP_ +#define SRC_ARRAY_HPP_ + +template +struct array { + // Storage + T data[N]; + + static size_t length() { return N; } + using type = T; + + // Item access + T &operator[](size_t index) { return data[index]; } + const T &operator[](size_t index) const { return data[index]; } + + // Iterators + T *begin() { return &data[0]; } + const T *begin() const { return &data[0]; } + T *end() { return &data[N]; } + const T *end() const { return &data[N]; } + + // Comparisons + bool operator==(const array &rhs) const { + if (this == &rhs) + return true; + for (size_t i = 0; i < N; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + } + bool operator!=(const array &rhs) const { + return !(*this == rhs); + } + array& operator=(const array& rhs) { + if (this != &rhs) + for (size_t i = 0; i < N; i++) + (*this)[i] = rhs[i]; + return *this; + } +}; + + + + + +#endif /* SRC_ARRAY_HPP_ */ diff --git a/src/buttons.cpp b/src/buttons.cpp new file mode 100644 index 00000000..d58e848e --- /dev/null +++ b/src/buttons.cpp @@ -0,0 +1,176 @@ +#include "buttons.hpp" + +namespace { + +const uint32_t LONG_PRESS = 1000; +const uint8_t buttonPause = A0; +const uint8_t buttonUp = A1; +const uint8_t buttonDown = A2; + +#ifdef FIVEBUTTONS +const uint8_t buttonFourPin = A3; +const uint8_t buttonFivePin = A4; +#endif + +} + +Buttons::Buttons(const Settings& settings) +: pauseButton(buttonPause ) +, upButton(buttonUp ) +, downButton(buttonDown ) +#ifdef FIVEBUTTONS +, buttonFour (buttonFourPin); +, buttonFive (buttonFivePin); +#endif +, settings(settings) +{ + pinMode(buttonPause , INPUT_PULLUP); + pinMode(buttonUp , INPUT_PULLUP); + pinMode(buttonDown , INPUT_PULLUP); +#ifdef FIVEBUTTONS + pinMode(buttonFourPin, INPUT_PULLUP); + pinMode(buttonFivePin, INPUT_PULLUP); +#endif +} + +button Buttons::get_button() { + button ret = button::none; + readButtons(); + if (( pauseButton.pressedFor(LONG_PRESS) + || upButton .pressedFor(LONG_PRESS) + || downButton .pressedFor(LONG_PRESS) + ) + && pauseButton.isPressed() + && upButton .isPressed() + && downButton .isPressed()) + ret = button::admin; + + else if (pauseButton.wasReleased()) { + if (not ignorePauseButton) + ret = button::pause; + ignorePauseButton = false; + } + + else if (pauseButton.pressedFor(LONG_PRESS) && not ignorePauseButton) { + ret = button::track; + ignorePauseButton = true; + } + + else if (upButton.wasReleased()) { + if (!ignoreUpButton) { + if (!settings.invertVolumeButtons) + ret = button::next; + else + ret = button::volume_up; + } + ignoreUpButton = false; + } + + else if (upButton.pressedFor(LONG_PRESS)) { +#ifndef FIVEBUTTONS + if (!settings.invertVolumeButtons) + ret = button::volume_up; + else + ret = button::next; + ignoreUpButton = true; +#endif + } + + else if (downButton.wasReleased()) { + if (!ignoreDownButton) { + if (!settings.invertVolumeButtons) + ret = button::previous; + else + ret = button::volume_down; + } + ignoreDownButton = false; + } + + else if (downButton.pressedFor(LONG_PRESS)) { +#ifndef FIVEBUTTONS + if (!settings.invertVolumeButtons) + ret = button::volume_down; + else + ret = button::previous; + ignoreDownButton = true; +#endif + } + +#ifdef FIVEBUTTONS + else if (buttonFour.wasReleased()) { + if (!settings.invertVolumeButtons) + ret = button::volume_up; + else + ret = button::next; + } + + else if (buttonFive.wasReleased()) { + if (!settings.invertVolumeButtons) + ret = button::volume_down; + else + ret = button::previous; + } +#endif + +// if (ret != button::none) { +// Serial.print(F("Button: ")); Serial.println(static_cast(ret)); +// } + return ret; +} + +void Buttons::wait_for_no_button() { + do { + readButtons(); + } while ( pauseButton.isPressed() + || upButton .isPressed() + || downButton .isPressed() +#ifdef FIVEBUTTONS + || buttonFour .isPressed() + || buttonFive .isPressed() +#endif + ); +} + +bool Buttons::is_reset() { + return (digitalRead(buttonPause) == LOW && + digitalRead(buttonUp) == LOW && + digitalRead(buttonDown) == LOW ); +} + +bool Buttons::is_break() { + readButtons(); + if (upButton.wasReleased() || downButton.wasReleased()) { + Serial.print(F("Abgebrochen!")); + return true; + } + return false; +} + +bool Buttons::askCode(Settings::pin_t &code) { + uint8_t x = 0; + while (x < 4) { + readButtons(); + if (pauseButton.pressedFor(LONG_PRESS)) + return false; + if (pauseButton.wasReleased()) + code[x++] = 1; + if (upButton.wasReleased()) + code[x++] = 2; + if (downButton.wasReleased()) + code[x++] = 3; + } + return true; +} + +void Buttons::readButtons() { + pauseButton.read(); + upButton .read(); + downButton .read(); +#ifdef FIVEBUTTONS + buttonFour .read(); + buttonFive .read(); +#endif +} + + + diff --git a/src/buttons.hpp b/src/buttons.hpp new file mode 100644 index 00000000..32943035 --- /dev/null +++ b/src/buttons.hpp @@ -0,0 +1,55 @@ +#ifndef SRC_BUTTONS_HPP_ +#define SRC_BUTTONS_HPP_ + +#include +#include + +#include "settings.hpp" + +// uncomment the below line to enable five button support +//#define FIVEBUTTONS + +enum class button { + none, + admin, + pause, + track, + volume_up, + volume_down, + next, + previous, +}; + +class Buttons { +public: + Buttons(const Settings& settings); + + button get_button(); + void wait_for_no_button(); + bool is_reset(); + bool is_break(); + bool askCode(Settings::pin_t &code); + +private: + + void readButtons(); + + Button pauseButton; + Button upButton; + Button downButton; + #ifdef FIVEBUTTONS + Button buttonFour; + Button buttonFive; + #endif + bool ignorePauseButton = false; + bool ignoreUpButton = false; + bool ignoreDownButton = false; + #ifdef FIVEBUTTONS + bool ignoreButtonFour = false; + bool ignoreButtonFive = false; + #endif + + const Settings& settings; +}; + +#endif /* SRC_BUTTONS_HPP_ */ diff --git a/src/chip_card.cpp b/src/chip_card.cpp new file mode 100644 index 00000000..c6de6d0f --- /dev/null +++ b/src/chip_card.cpp @@ -0,0 +1,244 @@ +#include +#include +#include + +#include "chip_card.hpp" + +namespace { +/** + Helper routine to dump a byte array as hex values to Serial. +*/ +void dump_byte_array(byte * buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} +const byte RST_PIN = 9; // Configurable, see typical pin layout above +const byte SS_PIN = 10; // Configurable, see typical pin layout above + +MFRC522::MIFARE_Key key{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +const byte sector = 1; +const byte trailerBlock = 7; +} // namespace + +Chip_card::Chip_card() +: mfrc522(*(new MFRC522(SS_PIN, RST_PIN))) +{} + +bool Chip_card::readCard(nfcTagObject &nfcTag) { + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + byte buffer[18]; + MFRC522::StatusCode status = MFRC522::STATUS_ERROR; + + // Authenticate using key A + if ((piccType == MFRC522::PICC_TYPE_MIFARE_MINI) || + (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || + (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) + { + Serial.println(F("Authenticating Classic using key A...")); + status = mfrc522.PCD_Authenticate( + MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + } + else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) + { + byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the tempCard + + // Authenticate using key A + Serial.println(F("Authenticating MIFARE UL...")); + status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); + } + + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + + // Show the whole sector as it currently is + // Serial.println(F("Current data in sector:")); + // mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + // Serial.println(); + + // Read data from the block + if ((piccType == MFRC522::PICC_TYPE_MIFARE_MINI) || + (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || + (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) + { + byte size = sizeof(buffer); + status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(4, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + } + else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) + { + byte buffer2[18]; + byte size2 = sizeof(buffer2); + + status = static_cast(mfrc522.MIFARE_Read(8, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read_1() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + memcpy(buffer, buffer2, 4); + + status = static_cast(mfrc522.MIFARE_Read(9, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read_2() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + memcpy(buffer + 4, buffer2, 4); + + status = static_cast(mfrc522.MIFARE_Read(10, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read_3() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + memcpy(buffer + 8, buffer2, 4); + + status = static_cast(mfrc522.MIFARE_Read(11, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read_4() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + memcpy(buffer + 12, buffer2, 4); + } + + Serial.print(F("Data on Card ")); + Serial.println(F(":")); + dump_byte_array(buffer, 16); + Serial.println(); + Serial.println(); + + uint32_t tempCookie; + tempCookie = (uint32_t)buffer[0] << 24; + tempCookie += (uint32_t)buffer[1] << 16; + tempCookie += (uint32_t)buffer[2] << 8; + tempCookie += (uint32_t)buffer[3]; + + nfcTag.cookie = tempCookie; + nfcTag.version = buffer[4]; + nfcTag.nfcFolderSettings.folder = buffer[5]; + nfcTag.nfcFolderSettings.mode = static_cast(buffer[5] ? buffer[6] : 0x80 | buffer[6]); + nfcTag.nfcFolderSettings.special = buffer[7]; + nfcTag.nfcFolderSettings.special2 = buffer[8]; + + return true; +} + +bool Chip_card::writeCard(const nfcTagObject &nfcTag) { + MFRC522::PICC_Type mifareType; + byte buffer[16] = {0x13, 0x37, 0xb3, 0x47, // 0x1337 0xb347 magic cookie to + // identify our nfc tags + 0x02, // version 1 + nfcTag.nfcFolderSettings.folder, // the folder picked by the user + static_cast(nfcTag.nfcFolderSettings.mode), // the playback mode picked by the user + nfcTag.nfcFolderSettings.special, // track or function for admin cards + nfcTag.nfcFolderSettings.special2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); + + MFRC522::StatusCode status = MFRC522::STATUS_ERROR; + + // Authenticate using key B + //authentificate with the card and set card specific parameters + if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || + (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || + (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) + { + Serial.println(F("Authenticating again using key A...")); + status = mfrc522.PCD_Authenticate( + MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + } + else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) + { + byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the NFCtag + + // Authenticate using key A + Serial.println(F("Authenticating UL...")); + status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); + } + + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + + // Write data to the block + Serial.println(F("Writing data ...")); + dump_byte_array(buffer, 16); + Serial.println(); + + if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || + (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || + (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) + { + status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(4, buffer, 16); + } + else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) + { + byte buffer2[16]; + byte size2 = sizeof(buffer2); + + memset(buffer2, 0, size2); + memcpy(buffer2, buffer, 4); + status = static_cast(mfrc522.MIFARE_Write(8, buffer2, 16)); + + memset(buffer2, 0, size2); + memcpy(buffer2, buffer + 4, 4); + status = static_cast(mfrc522.MIFARE_Write(9, buffer2, 16)); + + memset(buffer2, 0, size2); + memcpy(buffer2, buffer + 8, 4); + status = static_cast(mfrc522.MIFARE_Write(10, buffer2, 16)); + + memset(buffer2, 0, size2); + memcpy(buffer2, buffer + 12, 4); + status = static_cast(mfrc522.MIFARE_Write(11, buffer2, 16)); + } + + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + Serial.println(); + return true; +} + +void Chip_card::sleepCard() { + mfrc522.PCD_AntennaOff(); + mfrc522.PCD_SoftPowerDown(); +} + +void Chip_card::initCard() { + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 + mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader +} + +bool Chip_card::newCardPresent() { + return mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial(); +} + +void Chip_card::stopCard() { + mfrc522.PICC_HaltA(); + mfrc522.PCD_StopCrypto1(); +} diff --git a/src/chip_card.hpp b/src/chip_card.hpp new file mode 100644 index 00000000..badc6531 --- /dev/null +++ b/src/chip_card.hpp @@ -0,0 +1,67 @@ +#ifndef SRC_CHIP_CARD_HPP_ +#define SRC_CHIP_CARD_HPP_ + +#include + +const uint32_t cardCookie = 322417479; + +enum class mode_t: uint8_t { + none = 0, + + // folder modes + hoerspiel = 1, + album = 2, + party = 3, + einzel = 4, + hoerbuch = 5, + admin = 6, + hoerspiel_vb = 7, + album_vb = 8, + party_vb = 9, + + // modifier modes + sleep_timer = 0x80 | 1, + freeze_dance = 0x80 | 2, + locked = 0x80 | 3, + toddler = 0x80 | 4, + kindergarden = 0x80 | 5, + repeat_single = 0x80 | 6, + + admin_card = 0xff, +}; + +struct folderSettings { + uint8_t folder; + mode_t mode; + uint8_t special; + uint8_t special2; +}; + +// this object stores nfc tag data +struct nfcTagObject { + uint32_t cookie; + uint8_t version; + folderSettings nfcFolderSettings; +}; + +class MFRC522; // forward declaration + +class Chip_card { +public: + Chip_card(); + + bool readCard(nfcTagObject &nfcTag); + bool writeCard(const nfcTagObject &nfcTag); + void sleepCard(); + void initCard(); + void stopCard(); + bool newCardPresent(); + +private: + MFRC522 &mfrc522; +}; + + + + +#endif /* SRC_CHIP_CARD_HPP_ */ diff --git a/src/modifier.cpp b/src/modifier.cpp new file mode 100644 index 00000000..732439dc --- /dev/null +++ b/src/modifier.cpp @@ -0,0 +1,103 @@ +#include "modifier.hpp" + +#include "mp3.hpp" +#include "tonuino.hpp" + + +void SleepTimer::loop() { + if (sleepAtMillis != 0 && millis() > sleepAtMillis) { + Serial.println(F("=== SleepTimer::loop() -> SLEEP!")); + mp3.pause(); + tonuino.setStandbyTimer(); + tonuino.resetActiveModifier(); + delete this; + } +} + +void SleepTimer::start(uint8_t minutes) { + Serial.println(F("=== SleepTimer()")); + Serial.println(minutes); + sleepAtMillis = millis() + minutes * 60000; + //playAdvertisement(302); +} + +void FreezeDance::loop() { + if (nextStopAtMillis != 0 && millis() > nextStopAtMillis) { + Serial.println(F("== FreezeDance::loop() -> FREEZE!")); + if (mp3.isPlaying()) { + mp3.playAdvertisement(301); + } + setNextStopAtMillis(); + } +} + +void FreezeDance::setNextStopAtMillis() { + const uint16_t seconds = random(minSecondsBetweenStops, maxSecondsBetweenStops + 1); + Serial.println(F("=== FreezeDance::setNextStopAtMillis()")); + Serial.println(seconds); + nextStopAtMillis = millis() + seconds * 1000; +} + +bool KindergardenMode::handleNext() { + Serial.println(F("== KindergardenMode::handleNext() -> NEXT")); + //if (nextCard.cookie == cardCookie && nextCard.nfcFolderSettings.folder != 0 && nextCard.nfcFolderSettings.mode != 0) { + //myFolder = &nextCard.nfcFolderSettings; + if (cardQueued) { + cardQueued = false; + + tonuino.setCard(nextCard); + Serial.println(nextCard.nfcFolderSettings.folder); + //Serial.println(myFolder->mode); + tonuino.playFolder(); + return true; + } + return false; +} +bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { // lot of work to do! + Serial.println(F("== KindergardenMode::handleRFID() -> queued!")); + nextCard = newCard; + cardQueued = true; + if (!mp3.isPlaying()) { + handleNext(); + } + return true; +} + +bool RepeatSingleModifier::handleNext() { + Serial.println(F("== RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); + delay(50); + if (!mp3.isPlaying()) { + mp3.loop(); // this will call Mp3Notify::OnPlayFinished() but will be blocked by lastTrackFinished + Mp3Notify::ResetLastTrackFinished(); + tonuino.playCurrentTrack(); + } + + return true; +} + +//bool FeedbackModifier::handleVolumeDown() { +// if (volume > settings.minVolume) { +// playAdvertisement(volume - 1, false); +// } else { +// playAdvertisement(volume, false); +// } +// Serial.println(F("== FeedbackModifier::handleVolumeDown()!")); +// return false; +//} +//bool FeedbackModifier::handleVolumeUp() { +// if (volume < settings.maxVolume) { +// playAdvertisement(volume + 1, false); +// } else { +// playAdvertisement(volume, false); +// } +// Serial.println(F("== FeedbackModifier::handleVolumeUp()!")); +// return false; +//} +//bool FeedbackModifier::handleRFID(const nfcTagObject &/*newCard*/) { +// Serial.println(F("== FeedbackModifier::handleRFID()")); +// return false; +//} + + + + diff --git a/src/modifier.hpp b/src/modifier.hpp new file mode 100644 index 00000000..79f7e2f6 --- /dev/null +++ b/src/modifier.hpp @@ -0,0 +1,125 @@ +#ifndef SRC_MODIFIER_HPP_ +#define SRC_MODIFIER_HPP_ + +#include + +#include "chip_card.hpp" + +class Tonuino; +class Mp3; +class Settings; +struct nfcTagObject; + +class Modifier { +public: + Modifier(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): tonuino(tonuino), mp3(mp3), settings(settings) {} + virtual ~Modifier () {} + virtual void loop () {} + virtual bool handlePause () { return false; } + virtual bool handleNext () { return false; } + virtual bool handlePrevious () { return false; } + virtual bool handleNextButton () { return false; } + virtual bool handlePreviousButton() { return false; } + virtual bool handleVolumeUp () { return false; } + virtual bool handleVolumeDown () { return false; } + virtual bool handleRFID(const nfcTagObject&) + { return false; } + virtual mode_t getActive () { return mode_t::none; } + virtual void init () {} + Modifier& operator=(const Modifier&) = delete; +protected: + Tonuino &tonuino; + Mp3 &mp3; + const Settings &settings; +}; + +class SleepTimer: public Modifier { +public: + SleepTimer(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + void loop () final; + mode_t getActive() final { return mode_t::sleep_timer; } + void start (uint8_t minutes); + +private: + unsigned long sleepAtMillis = 0; +}; + +class FreezeDance: public Modifier { +public: + FreezeDance(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + void loop () final; + mode_t getActive() final { return mode_t::freeze_dance; } + void init () final { setNextStopAtMillis(); } + + void setNextStopAtMillis(); + +private: + unsigned long nextStopAtMillis = 0; + const uint8_t minSecondsBetweenStops = 5; + const uint8_t maxSecondsBetweenStops = 30; +}; + +class Locked: public Modifier { +public: + Locked(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + bool handlePause () final { Serial.println(F("== Locked::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { Serial.println(F("== Locked::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { Serial.println(F("== Locked::handlePreviousButton() -> LOCKED!")); return true; } + bool handleVolumeUp () final { Serial.println(F("== Locked::handleVolumeUp() -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { Serial.println(F("== Locked::handleVolumeDown() -> LOCKED!")) ; return true; } + bool handleRFID(const nfcTagObject&) + final { Serial.println(F("== Locked::handleRFID() -> LOCKED!")) ; return true; } + + mode_t getActive() final { return mode_t::locked; } +}; + +class ToddlerMode: public Modifier { +public: + ToddlerMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + bool handlePause () final { Serial.println(F("== ToddlerMode::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { Serial.println(F("== ToddlerMode::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { Serial.println(F("== ToddlerMode::handlePreviousButton() -> LOCKED!")); return true; } + bool handleVolumeUp () final { Serial.println(F("== ToddlerMode::handleVolumeUp() -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { Serial.println(F("== ToddlerMode::handleVolumeDown() -> LOCKED!")) ; return true; } + + mode_t getActive() final { return mode_t::toddler; } +}; + +class KindergardenMode: public Modifier { +public: + KindergardenMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + bool handleNext() final; + + //bool handlePause () final { Serial.println(F("== KindergardenMode::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { Serial.println(F("== KindergardenMode::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { Serial.println(F("== KindergardenMode::handlePreviousButton() -> LOCKED!")); return true; } + + bool handleRFID(const nfcTagObject &newCard) final; + mode_t getActive () final { return mode_t::kindergarden; } + void init () final { cardQueued = false; } + +private: + nfcTagObject nextCard{}; + bool cardQueued = false; +}; + +class RepeatSingleModifier: public Modifier { +public: + RepeatSingleModifier(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + bool handleNext() final; + mode_t getActive () final { return mode_t::repeat_single; } +}; + +// An modifier can also do somethings in addition to the modified action +// by returning false (not handled) at the end +// This simple FeedbackModifier will tell the volume before changing it and +// give some feedback once a RFID card is detected. +//class FeedbackModifier: public Modifier { +//public: +// FeedbackModifier(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} +// bool handleVolumeDown() final; +// bool handleVolumeUp () final; +// bool handleRFID (const nfcTagObject &newCard) final; +//}; + +#endif /* SRC_MODIFIER_HPP_ */ diff --git a/src/mp3.cpp b/src/mp3.cpp new file mode 100644 index 00000000..d66b261a --- /dev/null +++ b/src/mp3.cpp @@ -0,0 +1,112 @@ +#include "mp3.hpp" + +#include "tonuino.hpp" + +namespace { + +const uint8_t receivePin = 2; +const uint8_t transmitPin = 3; +const uint8_t busyPin = 4; +} + +uint16_t Mp3Notify::lastTrackFinished = 0; +void Mp3Notify::OnError(uint16_t errorCode) { + // see DfMp3_Error for code meaning + Serial.println(); + Serial.print(F("Com Error ")); + Serial.println(errorCode); +} +void Mp3Notify::OnPlaySourceOnline (DfMp3_PlaySources source) { PrintlnSourceAction(source, F("online" )); } +void Mp3Notify::OnPlaySourceInserted(DfMp3_PlaySources source) { PrintlnSourceAction(source, F("bereit" )); } +void Mp3Notify::OnPlaySourceRemoved (DfMp3_PlaySources source) { PrintlnSourceAction(source, F("entfernt")); } +void Mp3Notify::PrintlnSourceAction(DfMp3_PlaySources source, const __FlashStringHelper* action) { + if (source & DfMp3_PlaySources_Sd ) Serial.print(F("SD Karte ")); + if (source & DfMp3_PlaySources_Usb ) Serial.print(F("USB " )); + if (source & DfMp3_PlaySources_Flash) Serial.print(F("Flash " )); + Serial.println(action); +} + +void Mp3Notify::OnPlayFinished(DfMp3_PlaySources /*source*/, uint16_t track) { +// Serial.print(F("Track beendet")); +// Serial.println(track); + if (track == lastTrackFinished) + return; + else + lastTrackFinished = track; + tonuino.nextTrack(); +} + +Mp3::Mp3(const Settings& settings) +: DFMiniMp3{softwareSerial} +, softwareSerial{receivePin, transmitPin} +, settings{settings} +{ + // Busy Pin + pinMode(busyPin, INPUT); +} + +bool Mp3::isPlaying() const { + return !digitalRead(busyPin); +} + +void Mp3::waitForTrackToFinish() { + unsigned long currentTime = millis(); + const unsigned long maxStartTime = 1000; + + // wait until track is started + do { + loop(); + } while (!isPlaying() && millis() < currentTime + maxStartTime); + delay(1000); + + // wait until track is finished + do { + loop(); + } while (isPlaying()); +} + +void Mp3::playMp3FolderTrack(uint16_t track) { + DFMiniMp3::playMp3FolderTrack(track); +} + +void Mp3::playMp3FolderTrack(mp3Tracks track) { + DFMiniMp3::playMp3FolderTrack(static_cast(track)); +} + +void Mp3::playAdvertisement(uint16_t track, bool olnyIfIsPlaying) { + if (isPlaying()) { + DFMiniMp3::playAdvertisement(track); + delay(500); + } else if (not olnyIfIsPlaying) { + start(); + DFMiniMp3::playAdvertisement(track); + waitForTrackToFinish(); + pause(); + } +} + +void Mp3::playAdvertisement(advertTracks track, bool olnyIfIsPlaying) { + playAdvertisement(static_cast(track), olnyIfIsPlaying); +} + + +void Mp3::increaseVolume() { + if (volume < settings.maxVolume) { + DFMiniMp3::increaseVolume(); + ++volume; + } + Serial.println(volume); +} + +void Mp3::decreaseVolume() { + if (volume > settings.minVolume) { + DFMiniMp3::decreaseVolume(); + --volume; + } + Serial.println(volume); +} + +void Mp3::setVolume() { + volume = settings.initVolume; + DFMiniMp3::setVolume(volume); +} diff --git a/src/mp3.hpp b/src/mp3.hpp new file mode 100644 index 00000000..9233861c --- /dev/null +++ b/src/mp3.hpp @@ -0,0 +1,144 @@ +#ifndef SRC_MP3_HPP_ +#define SRC_MP3_HPP_ + +#include +#include +#include + +#include "settings.hpp" + +enum class mp3Tracks: uint16_t { + t_0 = 0, + t_262_pling = 262, + t_300_new_tag = 300, + t_301_select_folder = 301, + t_310_select_mode = 310, + t_311_mode_random_episode = 311, + t_312_mode_album = 312, + t_313_mode_party = 313, + t_314_mode_single_track = 314, + t_315_mode_audio_book = 315, + t_316_admin = 316, + t_317_special_random = 317, + t_318_special_album = 318, + t_319_special_party = 319, + t_320_select_file = 320, + t_321_select_first_file = 321, + t_322_select_last_file = 322, + t_330_select_say_number = 330, + t_331_do_not_say_number = 331, + t_332_say_number = 332, + t_400_ok = 400, + t_401_error = 401, + t_800_waiting_for_card = 800, + t_802_reset_aborted = 802, + t_900_admin = 900, + t_901_card_reset = 901, + t_902_max_volume = 902, + t_903_min_volume = 903, + t_904_init_volume = 904, + t_905_eq = 905, + t_906_modifiers = 906, + t_907_shortcut = 907, + t_908_standbytimer = 908, + t_909_batch_cards = 909, + t_910_switch_volume = 910, + t_911_reset = 911, + t_912_admin_lock = 912, + t_920_eq_intro = 920, + t_921_normal = 921, + t_922_pop = 922, + t_923_rock = 923, + t_924_jazz = 924, + t_925_classic = 925, + t_926_bass = 926, + t_930_max_volume_intro = 930, + t_931_min_volume_into = 931, + t_932_init_volume_into = 932, + t_933_switch_volume_intro = 933, + t_934_no = 934, + t_935_yes = 935, + t_936_batch_cards_intro = 936, + t_940_shortcut_into = 940, + t_941_pause = 941, + t_942_up = 942, + t_943_down = 943, + t_944_startup = 944, + t_960_timer_intro = 960, + t_961_timer_5 = 961, + t_962_timer_15 = 962, + t_963_timer_30 = 963, + t_964_timer_60 = 964, + t_965_timer_disabled = 965, + t_970_modifier_Intro = 970, + t_971_modifier_SleepTimer = 971, + t_972_modifier_FreezeDance = 972, + t_973_modifier_Locked = 973, + t_974_modifier_Toddler = 974, + t_975_modifier_KinderGarden = 975, + t_976_modifier_repeat1 = 976, + t_980_admin_lock_intro = 980, + t_981_admin_lock_disabled = 981, + t_982_admin_lock_card = 982, + t_983_admin_lock_pin = 983, + t_984_admin_lock_calc = 984, + t_991_admin_pin = 991, + t_992_admin_calc = 992, + t_993_admin_calc = 993, + t_994_admin_calc = 994, + t_999_reset_ok = 999, +}; + +inline mp3Tracks operator+(mp3Tracks lhs, uint16_t rhs) { return static_cast(static_cast(lhs)+rhs); } + +enum class advertTracks: uint16_t { + t_260_activate_mod_card = 260, + t_261_deactivate_mod_card = 261, + t_262_pling = 262, + t_300_freeze_into = 300, + t_301_freeze_freeze = 301, + t_302_sleep = 302, + t_303_locked = 303, + t_304_buttonslocked = 304, + t_305_kindergarden = 305, +}; + +// implement a notification class, +// its member methods will get called +// +class Mp3Notify { +public: + static void OnError (uint16_t errorCode); + static void OnPlayFinished (DfMp3_PlaySources source, uint16_t track); + static void OnPlaySourceOnline (DfMp3_PlaySources source); + static void OnPlaySourceInserted(DfMp3_PlaySources source); + static void OnPlaySourceRemoved (DfMp3_PlaySources source); + static void ResetLastTrackFinished() { lastTrackFinished = 0; } +private: + static void PrintlnSourceAction (DfMp3_PlaySources source, const __FlashStringHelper* action); + static uint16_t lastTrackFinished; +}; + +class Mp3: public DFMiniMp3 { +public: + Mp3(const Settings& settings); + + bool isPlaying() const; + void waitForTrackToFinish(); + void playMp3FolderTrack(uint16_t track); + void playMp3FolderTrack(mp3Tracks track); + void playAdvertisement(uint16_t track, bool olnyIfIsPlaying = true); + void playAdvertisement(advertTracks track, bool olnyIfIsPlaying = true); + + void increaseVolume(); + void decreaseVolume(); + void setVolume (); + +private: + SoftwareSerial softwareSerial; + const Settings& settings; + + uint8_t volume{}; +}; + +#endif /* SRC_MP3_HPP_ */ diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 00000000..b3634ed3 --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,110 @@ +#include "settings.hpp" + +#include + +namespace { + +const int startAddressAdminSettings = sizeof(folderSettings::folder) * 100; + +} + +void Settings::clearEEPROM() { + Serial.println(F("Reset -> EEPROM wird gelöscht")); + for (unsigned int i = 0; i < EEPROM.length(); i++) { + writeFolderSettingToFlash(i, 0); + } +} + +void Settings::writeSettingsToFlash() { + Serial.println(F("=== writeSettingsToFlash()")); + EEPROM.put(startAddressAdminSettings, *this); +} + +void Settings::resetSettings() { + Serial.println(F("=== resetSettings()")); + cookie = cardCookie; + version = 2; + maxVolume = 25; + minVolume = 5; + initVolume = 15; + eq = 1; + locked = false; + standbyTimer = 0; + invertVolumeButtons = true; + shortCuts[0].folder = 0; + shortCuts[1].folder = 0; + shortCuts[2].folder = 0; + shortCuts[3].folder = 0; + adminMenuLocked = 0; + adminMenuPin[0] = 1; + adminMenuPin[1] = 1; + adminMenuPin[2] = 1; + adminMenuPin[3] = 1; + + writeSettingsToFlash(); +} + +void Settings::migrateSettings(int oldVersion) { + if (oldVersion == 1) { + Serial.println(F("=== resetSettings()")); + Serial.println(F("1 -> 2")); + version = 2; + adminMenuLocked = 0; + adminMenuPin[0] = 1; + adminMenuPin[1] = 1; + adminMenuPin[2] = 1; + adminMenuPin[3] = 1; + writeSettingsToFlash(); + } +} + +void Settings::loadSettingsFromFlash() { + Serial.println(F("=== loadSettingsFromFlash()")); + EEPROM.get(startAddressAdminSettings, *this); + if (cookie != cardCookie) + resetSettings(); + migrateSettings(version); + + Serial.print(F("Version: ")); + Serial.println(version); + + Serial.print(F("Maximal Volume: ")); + Serial.println(maxVolume); + + Serial.print(F("Minimal Volume: ")); + Serial.println(minVolume); + + Serial.print(F("Initial Volume: ")); + Serial.println(initVolume); + + Serial.print(F("EQ: ")); + Serial.println(eq); + + Serial.print(F("Locked: ")); + Serial.println(locked); + + Serial.print(F("Sleep Timer: ")); + Serial.println(standbyTimer); + + Serial.print(F("Inverted Volume Buttons: ")); + Serial.println(invertVolumeButtons); + + Serial.print(F("Admin Menu locked: ")); + Serial.println(adminMenuLocked); + + Serial.print(F("Admin Menu Pin: ")); + Serial.print(adminMenuPin[0]); + Serial.print(adminMenuPin[1]); + Serial.print(adminMenuPin[2]); + Serial.println(adminMenuPin[3]); +} + +void Settings::writeFolderSettingToFlash(uint8_t folder, uint16_t track) { + EEPROM.update(folder, track); +} + +uint16_t Settings::readFolderSettingFromFlash(uint8_t folder) { + return EEPROM.read(folder); +} + + diff --git a/src/settings.hpp b/src/settings.hpp new file mode 100644 index 00000000..c3133e4a --- /dev/null +++ b/src/settings.hpp @@ -0,0 +1,38 @@ +#ifndef SRC_SETTINGS_HPP_ +#define SRC_SETTINGS_HPP_ + +#include + +#include "array.hpp" +#include "chip_card.hpp" + +// admin settings stored in eeprom +struct Settings { + typedef array shortCuts_t; + typedef array pin_t; + + void clearEEPROM(); + + void writeSettingsToFlash(); + void resetSettings(); + void migrateSettings(int oldVersion); + void loadSettingsFromFlash(); + + void writeFolderSettingToFlash(uint8_t folder, uint16_t track); + uint16_t readFolderSettingFromFlash(uint8_t folder); + + uint32_t cookie; + byte version; + uint8_t maxVolume; + uint8_t minVolume; + uint8_t initVolume; + uint8_t eq; + bool locked; + long standbyTimer; + bool invertVolumeButtons; + shortCuts_t shortCuts; + uint8_t adminMenuLocked; + pin_t adminMenuPin; +}; + +#endif /* SRC_SETTINGS_HPP_ */ diff --git a/src/tonuino.cpp b/src/tonuino.cpp new file mode 100644 index 00000000..d49809ba --- /dev/null +++ b/src/tonuino.cpp @@ -0,0 +1,886 @@ +#include "tonuino.hpp" + +#include +#include + +#include "array.hpp" +#include "chip_card.hpp" + +Tonuino tonuino; + +namespace { + +const uint8_t shutdownPin = 7; + +} + +void Tonuino::setup() { + pinMode(shutdownPin , OUTPUT); + digitalWrite(shutdownPin, LOW); + + // load Settings from EEPROM + settings.loadSettingsFromFlash(); + + // activate standby timer + tonuino.setStandbyTimer(); + + // DFPlayer Mini initialisieren + mp3.begin(); + // Zwei Sekunden warten bis der DFPlayer Mini initialisiert ist + delay(2000); + mp3.setVolume(); + mp3.setEq(static_cast(settings.eq - 1)); + + // NFC Leser initialisieren + chip_card.initCard(); + + // RESET --- ALLE DREI KNÖPFE BEIM STARTEN GEDRÜCKT HALTEN -> alle EINSTELLUNGEN werden gelöscht + if (buttons.is_reset()) { + settings.clearEEPROM(); + settings.loadSettingsFromFlash(); + } + + // Start Shortcut "at Startup" - e.g. Welcome Sound + tonuino.playShortCut(3); +} + +void Tonuino::handleButtons() { + checkStandbyAtMillis(); + + mp3.loop(); + + // Modifier : WIP! + activeModifier->loop(); + + switch (buttons.get_button()) { + + case button::admin: + mp3.pause(); + buttons.wait_for_no_button(); + if (not adminMenuAllowed()) { + mp3.start(); + break; + } + adminMenu(); + break; + + case button::pause: + if (activeModifier->handlePause()) + break; + if (mp3.isPlaying()) { + mp3.pause(); + setStandbyTimer(); + } else if (knownCard) { + mp3.start(); + disableStandbyTimer(); + } + break; + + case button::track: + if (activeModifier->handlePause()) + break; + if (mp3.isPlaying()) { + uint8_t advertTrack = getCurrentTrack(); + // Spezialmodus Von-Bis für Album und Party gibt die Dateinummer relativ zur Startposition wieder + if (myFolder->mode == mode_t::album_vb || myFolder->mode == mode_t::party_vb) { + advertTrack = advertTrack - myFolder->special + 1; + } + mp3.playAdvertisement(advertTrack); + } else { + playShortCut(0); + } + break; + + case button::volume_up: + if (mp3.isPlaying()) + volumeUpButton(); + else + playShortCut(1); + break; + + case button::next: + if (mp3.isPlaying()) + nextButton(); + else + playShortCut(1); + break; + + case button::volume_down: + if (mp3.isPlaying()) + volumeDownButton(); + else + playShortCut(2); + break; + + case button::previous: + if (mp3.isPlaying()) + previousButton(); + else + playShortCut(2); + break; + default: + break; + } +} + +void Tonuino::handleChipCard() { + if (!chip_card.newCardPresent()) + return; + + // RFID Karte wurde aufgelegt + nfcTagObject tempCard; + if (chip_card.readCard(tempCard) && !specialCard(tempCard)) { + setCard(tempCard); + Serial.println(myCard.nfcFolderSettings.folder); + + if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.folder != 0 + && myCard.nfcFolderSettings.mode != mode_t::none) { + knownCard = false; + mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); + mp3.waitForTrackToFinish(); + playFolder(); + } + + // Neue Karte konfigurieren + else if (myCard.cookie != cardCookie) { + knownCard = false; + mp3.playMp3FolderTrack(mp3Tracks::t_300_new_tag); + mp3.waitForTrackToFinish(); + setupCard(); + } + } + chip_card.stopCard(); +} + +void Tonuino::playFolder() { + Serial.println(F("== playFolder()")); + disableStandbyTimer(); + knownCard = true; + numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); + firstTrack = 1; + Serial.print(numTracksInFolder); + Serial.print(F(" Dateien in Ordner ")); + Serial.println(myFolder->folder); + + switch (myFolder->mode) { + + case mode_t::hoerspiel: + // Hörspielmodus: eine zufällige Datei aus dem Ordner + Serial.println(F("Hörspielmodus -> zufälligen Track wiedergeben")); + currentTrack = random(1, numTracksInFolder + 1); + Serial.println(currentTrack); + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::album: + // Album Modus: kompletten Ordner spielen + Serial.println(F("Album Modus -> kompletten Ordner wiedergeben")); + currentTrack = 1; + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::party: + // Party Modus: Ordner in zufälliger Reihenfolge + Serial.println( + F("Party Modus -> Ordner in zufälliger Reihenfolge wiedergeben")); + shuffleQueue(); + currentTrack = 1; + mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + break; + + case mode_t::einzel: + // Einzel Modus: eine Datei aus dem Ordner abspielen + Serial.println(F("Einzel Modus -> eine Datei aus dem Odrdner abspielen")); + currentTrack = myFolder->special; + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::hoerbuch: + // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken + Serial.println(F("Hörbuch Modus -> kompletten Ordner spielen und Fortschritt merken")); + currentTrack = settings.readFolderSettingFromFlash(myFolder->folder); + if (currentTrack == 0 || currentTrack > numTracksInFolder) { + currentTrack = 1; + } + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::hoerspiel_vb: + // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner + Serial.println(F("Spezialmodus Von-Bin: Hörspiel -> zufälligen Track wiedergeben")); + Serial.print(myFolder->special); + Serial.print(F(" bis ")); + Serial.println(myFolder->special2); + firstTrack = myFolder->special; + numTracksInFolder = myFolder->special2; + currentTrack = random(myFolder->special, numTracksInFolder + 1); + Serial.println(currentTrack); + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::album_vb: + // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen + Serial.println(F("Spezialmodus Von-Bis: Album: alle Dateien zwischen Start- und Enddatei spielen")); + Serial.print(myFolder->special); + Serial.print(F(" bis ")); + Serial.println(myFolder->special2); + firstTrack = myFolder->special; + numTracksInFolder = myFolder->special2; + currentTrack = myFolder->special; + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::party_vb: + // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge + Serial.println(F("Spezialmodus Von-Bis: Party -> Ordner in zufälliger Reihenfolge wiedergeben")); + firstTrack = myFolder->special; + numTracksInFolder = myFolder->special2; + shuffleQueue(); + currentTrack = 1; + mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + break; + default: + break; + } +} + +void Tonuino::playShortCut(uint8_t shortCut) { + Serial.println(F("=== playShortCut()")); + Serial.println(shortCut); + if (settings.shortCuts[shortCut].folder != 0) { + setFolder(&settings.shortCuts[shortCut]); + playFolder(); + disableStandbyTimer(); + delay(1000); + } else { + Serial.println(F("Shortcut not configured! Playing greating")); + mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); + } +} + +// Leider kann das Modul selbst keine Queue abspielen, daher müssen wir selbst die Queue verwalten +void Tonuino::nextTrack() { + if (activeModifier->handleNext()) + return; + + if (not knownCard) + // Wenn eine neue Karte angelernt wird soll das Ende eines Tracks nicht + // verarbeitet werden + return; + + Serial.println(F("=== nextTrack()")); + + switch (myFolder->mode) { + case mode_t::hoerspiel : + case mode_t::hoerspiel_vb: + if (not mp3.isPlaying()) { + Serial.println(F("Hörspielmodus ist aktiv -> keinen neuen Track spielen")); + knownCard = false; + setStandbyTimer(); + //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + } + break; + + case mode_t::album : + case mode_t::album_vb: + if (currentTrack != numTracksInFolder) { + ++currentTrack; + mp3.playFolderTrack(myFolder->folder, currentTrack); + Serial.print(F("Albummodus ist aktiv -> nächster Track: ")); + Serial.println(currentTrack); + } else { + // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + knownCard = false; + setStandbyTimer(); + } + break; + + case mode_t::party : + case mode_t::party_vb: + if (currentTrack != numTracksInFolder - firstTrack + 1) { + Serial.print(F("Party -> weiter in der Queue ")); + ++currentTrack; + } else { + Serial.println(F("Ende der Queue -> beginne von vorne")); + currentTrack = 1; + //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren + //Serial.println(F("Ende der Queue -> mische neu")); + //shuffleQueue(); + } + Serial.println(queue[currentTrack - 1]); + mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + break; + + case mode_t::einzel: + Serial.println(F("Einzel Modus aktiv -> Strom sparen")); + //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + knownCard = false; + setStandbyTimer(); + break; + + case mode_t::hoerbuch: + if (currentTrack != numTracksInFolder) { + ++currentTrack; + Serial.print(F("Hörbuch Modus ist aktiv -> nächster Track und Fortschritt speichern")); + Serial.println(currentTrack); + mp3.playFolderTrack(myFolder->folder, currentTrack); + // Fortschritt im EEPROM abspeichern + settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); + } else { + //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + // Fortschritt zurück setzen + settings.writeFolderSettingToFlash(myFolder->folder, 1); + knownCard = false; + setStandbyTimer(); + } + break; + default: + break; + } + delay(500); +} + +void Tonuino::previousTrack() { + Serial.println(F("=== previousTrack()")); + + switch (myFolder->mode) { + case mode_t::hoerspiel: + case mode_t::hoerspiel_vb: + Serial.println(F("Hörspielmodus ist aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::album: + case mode_t::album_vb: + Serial.println(F("Albummodus ist aktiv -> vorheriger Track")); + if (currentTrack != firstTrack) { + --currentTrack; + } + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::party: + case mode_t::party_vb: + if (currentTrack != 1) { + Serial.print(F("Party Modus ist aktiv -> zurück in der Qeueue ")); + --currentTrack; + } else { + Serial.print(F("Anfang der Queue -> springe ans Ende ")); + currentTrack = numTracksInFolder - firstTrack + 1; + } + Serial.println(queue[currentTrack - 1]); + mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + break; + + case mode_t::einzel: + Serial.println(F("Einzel Modus aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::hoerbuch: + Serial.println(F("Hörbuch Modus ist aktiv -> vorheriger Track und Fortschritt speichern")); + if (currentTrack != 1) { + --currentTrack; + } + mp3.playFolderTrack(myFolder->folder, currentTrack); + // Fortschritt im EEPROM abspeichern + settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); + break; + default: + break; + } + delay(500); +} + +// Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) +void Tonuino::setStandbyTimer() { + Serial.println(F("=== setStandbyTimer()")); + if (settings.standbyTimer != 0) + standbyAtMillis = millis() + (settings.standbyTimer * 60 * 1000); + else + standbyAtMillis = 0; + Serial.println(standbyAtMillis); +} + +void Tonuino::disableStandbyTimer() { + Serial.println(F("=== disablestandby()")); + standbyAtMillis = 0; +} + +void Tonuino::checkStandbyAtMillis() { + if (standbyAtMillis != 0 && millis() > standbyAtMillis) { + Serial.println(F("=== power off!")); + // enter sleep state + digitalWrite(shutdownPin, HIGH); + delay(500); + + // http://discourse.voss.earth/t/intenso-s10000-powerbank-automatische-abschaltung-software-only/805 + // powerdown to 27mA (powerbank switches off after 30-60s) + chip_card.sleepCard(); + mp3.sleep(); + + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + cli(); // Disable interrupts + sleep_mode(); + } +} + +uint8_t Tonuino::getCurrentTrack() const { + if (myFolder->mode == mode_t::party || myFolder->mode == mode_t::party_vb) + return (queue[currentTrack - 1]); + else + return currentTrack; +} + +void Tonuino::volumeUpButton() { + if (activeModifier->handleVolumeUp()) + return; + + Serial.println(F("=== volumeUp()")); + mp3.increaseVolume(); +} + +void Tonuino::volumeDownButton() { + if (activeModifier->handleVolumeDown()) + return; + + Serial.println(F("=== volumeDown()")); + mp3.decreaseVolume(); +} + +void Tonuino::nextButton() { + if (activeModifier->handleNextButton()) + return; + + nextTrack(); +} + +void Tonuino::previousButton() { + if (activeModifier->handlePreviousButton()) + return; + + previousTrack(); +} + +bool Tonuino::setupFolder(folderSettings * theFolder) { + // Ordner abfragen + theFolder->folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true, 0, 0, true); + if (theFolder->folder == 0) return false; + + // Wiedergabemodus abfragen + theFolder->mode = static_cast(voiceMenu(9, mp3Tracks::t_310_select_mode, mp3Tracks::t_310_select_mode, false, 0, 0, true)); + if (theFolder->mode == mode_t::none) return false; + + // // Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen + // writeFolderSettingToFlash(theFolder->folder, 1); + + // Einzelmodus -> Datei abfragen + if (theFolder->mode == mode_t::einzel) + theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_320_select_file, mp3Tracks::t_0, + true, theFolder->folder); + // Admin Funktionen + if (theFolder->mode == mode_t::admin) { + //theFolder->special = voiceMenu(3, 320, 320); + theFolder->folder = 0; + theFolder->mode = mode_t::admin_card; + } + // Spezialmodus Von-Bis + if (theFolder->mode == mode_t::hoerspiel_vb || theFolder->mode == mode_t::album_vb || theFolder->mode == mode_t::party_vb) { + theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_321_select_first_file, mp3Tracks::t_0, + true, theFolder->folder); + theFolder->special2 = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_322_select_last_file, mp3Tracks::t_0, + true, theFolder->folder, theFolder->special); + } + return true; +} + +void Tonuino::resetCard() { + mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); + do { + if (buttons.is_break()) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return; + } + } while (!chip_card.newCardPresent()); + + Serial.print(F("Karte wird neu konfiguriert!")); + setupCard(); +} + +void Tonuino::setupCard() { + mp3.pause(); + Serial.println(F("=== setupCard()")); + nfcTagObject newCard; + if (setupFolder(&newCard.nfcFolderSettings)) + { + // Karte ist konfiguriert -> speichern + mp3.pause(); + do { + } while (mp3.isPlaying()); + if (chip_card.writeCard(newCard)) + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + else + mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + mp3.waitForTrackToFinish(); + } +} + +bool Tonuino::specialCard(const nfcTagObject &nfcTag) { + if (nfcTag.cookie != cardCookie) + return false; + + if (nfcTag.nfcFolderSettings.folder != 0) { + if (activeModifier->handleRFID(nfcTag)) { + return true; + } + } + + else { //if (nfcTag.nfcFolderSettings.folder == 0) + //Serial.print(F("special card, mode = ")); + //Serial.println(static_cast(nfcTag.nfcFolderSettings.mode)); + if (activeModifier->getActive() == nfcTag.nfcFolderSettings.mode) { + resetActiveModifier(); + Serial.println(F("modifier removed")); + mp3.playAdvertisement(advertTracks::t_261_deactivate_mod_card, false); + return true; + } + const Modifier *oldModifier = activeModifier; + + switch (nfcTag.nfcFolderSettings.mode) { + case mode_t::none: + case mode_t::admin_card: chip_card.stopCard(); + adminMenu() ;break; + case mode_t::sleep_timer: Serial.println(F("activate sleepTimer")); + mp3.playAdvertisement(advertTracks::t_302_sleep, false); + activeModifier = &sleepTimer; + sleepTimer.start(nfcTag.nfcFolderSettings.special);break; + case mode_t::freeze_dance: Serial.println(F("activate freezeDance")); + mp3.playAdvertisement(advertTracks::t_300_freeze_into, false); + activeModifier = &freezeDance; ;break; + case mode_t::locked: Serial.println(F("activate locked")); + mp3.playAdvertisement(advertTracks::t_303_locked, false); + activeModifier = &locked ;break; + case mode_t::toddler: Serial.println(F("activate toddlerMode")); + mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false); + activeModifier = &toddlerMode ;break; + case mode_t::kindergarden: Serial.println(F("activate kindergardenMode")); + mp3.playAdvertisement(advertTracks::t_305_kindergarden, false); + activeModifier = &kindergardenMode ;break; + case mode_t::repeat_single:Serial.println(F("activate repeatSingleModifier")); + mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false); + activeModifier = &repeatSingleModifier ;break; + default: break; + } + if (oldModifier != activeModifier) + activeModifier->init(); + delay(2000); + return true; + } + return false; +} + +bool Tonuino::adminMenuAllowed() { + // Admin menu has been locked - it still can be trigged via admin card + switch (settings.adminMenuLocked) { + case 1: + return false; + + // Pin check + case 2: + Settings::pin_t pin; + mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); + return (buttons.askCode(pin) && pin == settings.adminMenuPin); + + // Match check + case 3: + { + const uint8_t a = random(10, 20); + const uint8_t b = random(1, a); + uint8_t c; + mp3.playMp3FolderTrack(mp3Tracks::t_992_admin_calc); + mp3.waitForTrackToFinish(); + mp3.playMp3FolderTrack(a); + mp3.waitForTrackToFinish(); + + if (random(1, 3) == 2) { + // a + b + c = a + b; + mp3.playMp3FolderTrack(mp3Tracks::t_993_admin_calc); + } else { + // a - b + c = a - b; + mp3.playMp3FolderTrack(mp3Tracks::t_994_admin_calc); + } + mp3.waitForTrackToFinish(); + mp3.playMp3FolderTrack(b); + Serial.println(c); + return (voiceMenu(255, mp3Tracks::t_0, mp3Tracks::t_0, false) == c); + } + } + + // not locked + return true; +} + +void Tonuino::adminMenu() { + disableStandbyTimer(); + mp3.pause(); + Serial.println(F("=== adminMenu()")); + knownCard = false; + + const int subMenu = voiceMenu(12, mp3Tracks::t_900_admin, mp3Tracks::t_900_admin, false, false, 0, true); + + switch (subMenu) { + case 0: setStandbyTimer(); + return; + case 1: resetCard(); + chip_card.stopCard(); + break; + case 2: // Maximum Volume + settings.maxVolume = voiceMenu(30 - settings.minVolume, mp3Tracks::t_930_max_volume_intro, static_cast(settings.minVolume), false, false, settings.maxVolume - settings.minVolume) + settings.minVolume; + break; + case 3: // Minimum Volume + settings.minVolume = voiceMenu(settings.maxVolume - 1, mp3Tracks::t_931_min_volume_into, mp3Tracks::t_0, false, false, settings.minVolume); + break; + case 4: // Initial Volume + settings.initVolume = voiceMenu(settings.maxVolume - settings.minVolume + 1, mp3Tracks::t_932_init_volume_into, static_cast(settings.minVolume - 1), false, false, settings.initVolume - settings.minVolume + 1) + settings.minVolume - 1; + break; + case 5: // EQ + settings.eq = voiceMenu(6, mp3Tracks::t_920_eq_intro, mp3Tracks::t_920_eq_intro, false, false, settings.eq); + mp3.setEq(static_cast(settings.eq - 1)); + break; + case 6: // create modifier card + createModifierCard(); + break; + case 7: // shortcut + setupFolder(&settings.shortCuts[voiceMenu(4, mp3Tracks::t_940_shortcut_into, mp3Tracks::t_940_shortcut_into) - 1]); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + break; + case 8: // standby timer + switch (voiceMenu(5, mp3Tracks::t_960_timer_intro, mp3Tracks::t_960_timer_intro)) { + case 1: settings.standbyTimer = 5; break; + case 2: settings.standbyTimer = 15; break; + case 3: settings.standbyTimer = 30; break; + case 4: settings.standbyTimer = 60; break; + case 5: settings.standbyTimer = 0; break; + } + break; + case 9: // Create Cards for Folder + createCardsForFolder(); + break; + case 10: // Invert Functions for Up/Down Buttons + if (voiceMenu(2, mp3Tracks::t_933_switch_volume_intro, mp3Tracks::t_933_switch_volume_intro, false) == 2) { + settings.invertVolumeButtons = true; + } + else { + settings.invertVolumeButtons = false; + } + break; + case 11: // reset EEPROM + settings.clearEEPROM(); + settings.resetSettings(); + mp3.playMp3FolderTrack(mp3Tracks::t_999_reset_ok); + break; + case 12: // lock admin menu + switch (voiceMenu(4, mp3Tracks::t_980_admin_lock_intro, mp3Tracks::t_980_admin_lock_intro, false)) { + case 1: settings.adminMenuLocked = 0; + break; + case 2: settings.adminMenuLocked = 1; + break; + case 3: { + Settings::pin_t pin; + mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); + if (buttons.askCode(pin)) + settings.adminMenuPin = pin; + else + break; + } + settings.adminMenuLocked = 2; + break; + } + break; + } + settings.writeSettingsToFlash(); + setStandbyTimer(); +} + +void Tonuino::voiceMenuNextOption( uint8_t returnValue + , mp3Tracks messageOffset + , bool preview + , int previewFromFolder) { + Serial.println(returnValue); + //mp3.pause(); + mp3.playMp3FolderTrack(messageOffset + returnValue); + if (preview) { + mp3.waitForTrackToFinish(); + if (previewFromFolder == 0) + mp3.playFolderTrack(returnValue, 1); + else + mp3.playFolderTrack(previewFromFolder, returnValue); + } +} + +uint8_t Tonuino::voiceMenu( int numberOfOptions + , mp3Tracks startMessage + , mp3Tracks messageOffset + , bool preview + , int previewFromFolder + , int defaultValue + , bool exitWithLongPress) { + uint8_t returnValue = defaultValue; + + if (startMessage != mp3Tracks::t_0) + mp3.playMp3FolderTrack(startMessage); + + Serial.print(F("=== voiceMenu() (")); + Serial.print(numberOfOptions); + Serial.println(F(" Options)")); + + do { + if (Serial.available() > 0) { + int optionSerial = Serial.parseInt(); + if (optionSerial != 0 && optionSerial <= numberOfOptions) + return optionSerial; + } + mp3.loop(); + switch(buttons.get_button()) { + case button::track: + if (exitWithLongPress) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return defaultValue; + } + break; + + case button::pause: + if (returnValue != 0) { + Serial.print(F("=== ")); + Serial.print(returnValue); + Serial.println(F(" ===")); + return returnValue; + } + //delay(1000); + break; + + case button::next: + returnValue = min(returnValue + 10, numberOfOptions); + voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + break; + + case button::volume_up: + returnValue = min(returnValue + 1, numberOfOptions); + voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + break; + + case button::previous: + returnValue = max(returnValue - 10, 1); + voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + break; + + case button::volume_down: + returnValue = max(returnValue - 1, 1); + voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + break; + default: + break; + } + } while (true); +} + +void Tonuino::createModifierCard() { + nfcTagObject tempCard; + tempCard.cookie = cardCookie; + tempCard.version = 1; + tempCard.nfcFolderSettings.folder = 0; + tempCard.nfcFolderSettings.special = 0; + tempCard.nfcFolderSettings.special2 = 0; + tempCard.nfcFolderSettings.mode = static_cast(0x80 | voiceMenu(6, mp3Tracks::t_970_modifier_Intro, mp3Tracks::t_970_modifier_Intro, false, false, 0, true)); + + if (tempCard.nfcFolderSettings.mode != mode_t::none) { + if (tempCard.nfcFolderSettings.mode == mode_t::sleep_timer) { + switch (voiceMenu(4, mp3Tracks::t_960_timer_intro, mp3Tracks::t_960_timer_intro)) { + case 1: tempCard.nfcFolderSettings.special = 5; break; + case 2: tempCard.nfcFolderSettings.special = 15; break; + case 3: tempCard.nfcFolderSettings.special = 30; break; + case 4: tempCard.nfcFolderSettings.special = 60; break; + } + } + mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); + do { + if (buttons.is_break()) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return; + } + } while (!chip_card.newCardPresent()); + + // RFID Karte wurde aufgelegt + Serial.println(F("schreibe Karte...")); + if (chip_card.writeCard(tempCard)) + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + else + mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + mp3.waitForTrackToFinish(); + chip_card.stopCard(); + } +} + +void Tonuino::createCardsForFolder() { + // Ordner abfragen + nfcTagObject tempCard; + tempCard.cookie = cardCookie; + tempCard.version = 1; + tempCard.nfcFolderSettings.mode = mode_t::einzel; + tempCard.nfcFolderSettings.folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true); + uint8_t special = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_321_select_first_file, mp3Tracks::t_0, + true, tempCard.nfcFolderSettings.folder); + uint8_t special2 = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_322_select_last_file, mp3Tracks::t_0, + true, tempCard.nfcFolderSettings.folder, special); + + mp3.playMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); + mp3.waitForTrackToFinish(); + for (uint8_t x = special; x <= special2; x++) { + mp3.playMp3FolderTrack(x); + tempCard.nfcFolderSettings.special = x; + Serial.print(x); + Serial.println(F(" Karte auflegen")); + do { + if (buttons.is_break()) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return; + } + } while (!chip_card.newCardPresent()); + + // RFID Karte wurde aufgelegt + Serial.println(F("schreibe Karte...")); + if (chip_card.writeCard(tempCard)) + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + else + mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + mp3.waitForTrackToFinish(); + chip_card.stopCard(); + mp3.waitForTrackToFinish(); + } +} + +void Tonuino::shuffleQueue() { + // Queue für die Zufallswiedergabe erstellen + for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1; x++) + queue[x] = x + firstTrack; + + // Rest mit 0 auffüllen + for (uint8_t x = numTracksInFolder - firstTrack + 1; x < maxTracksInFolder; x++) + queue[x] = 0; + + // Queue mischen + for (uint8_t i = 0; i < numTracksInFolder - firstTrack + 1; i++) { + const uint8_t j = random(0, numTracksInFolder - firstTrack + 1); + const uint8_t t = queue[i]; + queue[i] = queue[j]; + queue[j] = t; + } +// Serial.println(F("Queue :")); +// for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; x++) +// Serial.println(queue[x]); +} + + + diff --git a/src/tonuino.hpp b/src/tonuino.hpp new file mode 100644 index 00000000..216c50bb --- /dev/null +++ b/src/tonuino.hpp @@ -0,0 +1,106 @@ +#ifndef SRC_TONUINO_HPP_ +#define SRC_TONUINO_HPP_ + +#include "settings.hpp" +#include "buttons.hpp" +#include "mp3.hpp" +#include "modifier.hpp" + +class Tonuino { +public: + Tonuino() {} + + void setup (); + void handleButtons (); + void handleChipCard(); + + void playFolder (); + void playShortCut (uint8_t shortCut); + void playCurrentTrack() { mp3.playFolderTrack(myFolder->folder, getCurrentTrack()); } + + void nextTrack(); + void previousTrack(); + + void resetActiveModifier() { activeModifier = &noneModifier; } + + void setStandbyTimer (); + + void setCard (const nfcTagObject &newCard ) { myCard = newCard; setFolder(&myCard.nfcFolderSettings); } + void setFolder(const folderSettings *newFolder) { myFolder = newFolder; } + +private: + + uint8_t getCurrentTrack() const; + + void checkStandbyAtMillis(); + void disableStandbyTimer (); + + void volumeUpButton (); + void volumeDownButton(); + void nextButton (); + void previousButton (); + + bool setupFolder(folderSettings * theFolder); + + void resetCard (); + void setupCard (); + bool specialCard(const nfcTagObject &nfcTag); + + + bool adminMenuAllowed(); + void adminMenu (); + + void voiceMenuNextOption( uint8_t returnValue + , mp3Tracks messageOffset + , bool preview + , int previewFromFolder); + uint8_t voiceMenu( int numberOfOptions + , mp3Tracks startMessage + , mp3Tracks messageOffset + , bool preview = false + , int previewFromFolder = 0 + , int defaultValue = 0 + , bool exitWithLongPress = false); + void createModifierCard (); + void createCardsForFolder(); + void shuffleQueue (); + + static const size_t maxTracksInFolder = 255; + typedef array queue_t; + + Settings settings {}; + Mp3 mp3 {settings}; + Buttons buttons {settings}; + Chip_card chip_card {}; + + + Modifier noneModifier {*this, mp3, settings}; + SleepTimer sleepTimer {*this, mp3, settings}; + FreezeDance freezeDance {*this, mp3, settings}; + Locked locked {*this, mp3, settings}; + ToddlerMode toddlerMode {*this, mp3, settings}; + KindergardenMode kindergardenMode {*this, mp3, settings}; + RepeatSingleModifier repeatSingleModifier{*this, mp3, settings}; + //FeedbackModifier feedbackModifier {*this, mp3, settings}; + + Modifier *activeModifier = &noneModifier; + + uint16_t numTracksInFolder = 0; + uint16_t currentTrack = 0; + uint16_t firstTrack = 0; + queue_t queue{}; + + unsigned long standbyAtMillis = 0; + + bool knownCard = false; + + nfcTagObject myCard; + const folderSettings *myFolder = &myCard.nfcFolderSettings; + +}; + +extern Tonuino tonuino; + + + +#endif /* SRC_TONUINO_HPP_ */