From 8b7aa60b2388050da8f5524d543e279dcf1d182d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Vo=C3=9F?= Date: Tue, 28 Aug 2018 08:55:16 +0200 Subject: [PATCH] =?UTF-8?q?Fehlermeldung=20beim=20Anlernen=20der=20Karte?= =?UTF-8?q?=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SD-Karte/mp3/0401_error.mp3 | Bin 0 -> 40123 bytes TonUINO/Tonuino.ino | 559 ++++++++++++++++++++++++++++++++++ Tonuino.ino | 5 +- community/TonUINO/Tonuino.ino | 559 ++++++++++++++++++++++++++++++++++ create-soundfiles.sh | 8 +- 5 files changed, 1126 insertions(+), 5 deletions(-) create mode 100644 SD-Karte/mp3/0401_error.mp3 create mode 100644 TonUINO/Tonuino.ino create mode 100644 community/TonUINO/Tonuino.ino diff --git a/SD-Karte/mp3/0401_error.mp3 b/SD-Karte/mp3/0401_error.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b6e4ccacbf1b6cce039e2ae0933cc9b2d3837aec GIT binary patch literal 40123 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 +#include +#include +#include +#include +#include + +// DFPlayer Mini +SoftwareSerial mySoftwareSerial(2, 3); // RX, TX +uint16_t numTracksInFolder; +uint16_t track; + +// this object stores nfc tag data +struct nfcTagObject { + uint32_t cookie; + uint8_t version; + uint8_t folder; + uint8_t mode; + uint8_t special; +}; + +nfcTagObject myCard; + +static void nextTrack(); +int voiceMenu(int numberOfOptions, int startMessage, int messageOffset, + bool preview = false, int previewFromFolder = 0); + +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 OnPlayFinished(uint16_t track) { + Serial.print("Track beendet"); + Serial.println(track); + delay(100); + nextTrack(); + } + static void OnCardOnline(uint16_t code) { + Serial.println(F("SD Karte online ")); + } + static void OnCardInserted(uint16_t code) { + Serial.println(F("SD Karte bereit ")); + } + static void OnCardRemoved(uint16_t code) { + Serial.println(F("SD Karte entfernt ")); + } +}; + +static DFMiniMp3 mp3(mySoftwareSerial); + +// Leider kann das Modul keine Queue abspielen. +static void nextTrack() { + if (knownCard == false) + // Wenn eine neue Karte angelernt wird soll das Ende eines Tracks nicht + // verarbeitet werden + return; + + if (myCard.mode == 1) { + Serial.println(F("Hörspielmodus ist aktiv -> Strom sparen")); + mp3.sleep(); + } + if (myCard.mode == 2) { + if (track != numTracksInFolder) { + track = track + 1; + mp3.playFolderTrack(myCard.folder, track); + Serial.print(F("Albummodus ist aktiv -> nächster Track: ")); + Serial.print(track); + } else + mp3.sleep(); + } + if (myCard.mode == 3) { + track = random(1, numTracksInFolder + 1); + Serial.print(F("Party Modus ist aktiv -> zufälligen Track spielen: ")); + Serial.println(track); + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 4) { + Serial.println(F("Einzel Modus aktiv -> Strom sparen")); + mp3.sleep(); + } + if (myCard.mode == 5) { + if (track != numTracksInFolder) { + track = track + 1; + Serial.print(F("Hörbuch Modus ist aktiv -> nächster Track und " + "Fortschritt speichern")); + Serial.println(track); + mp3.playFolderTrack(myCard.folder, track); + // Fortschritt im EEPROM abspeichern + EEPROM.write(myCard.folder, track); + } else + mp3.sleep(); + } +} + +static void previousTrack() { + if (myCard.mode == 1) { + Serial.println(F("Hörspielmodus ist aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 2) { + Serial.println(F("Albummodus ist aktiv -> vorheriger Track")); + if (track != 1) { + track = track - 1; + } + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 3) { + Serial.println(F("Party Modus ist aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 4) { + Serial.println(F("Einzel Modus aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 5) { + Serial.println(F("Hörbuch Modus ist aktiv -> vorheriger Track und " + "Fortschritt speichern")); + if (track != 1) { + track = track - 1; + } + mp3.playFolderTrack(myCard.folder, track); + // Fortschritt im EEPROM abspeichern + EEPROM.write(myCard.folder, track); + } +} + +// 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 LONG_PRESS 1000 + +Button pauseButton(buttonPause); +Button upButton(buttonUp); +Button downButton(buttonDown); +bool ignorePauseButton = false; +bool ignoreUpButton = false; +bool ignoreDownButton = false; + +uint8_t numberOfCards = 0; + +bool isPlaying() { return !digitalRead(busyPin); } + +void setup() { + + Serial.begin(115200); // Es gibt ein paar Debug Ausgaben über die serielle + // Schnittstelle + randomSeed(analogRead(A0)); // Zufallsgenerator initialisieren + + Serial.println(F("TonUINO Version 2.0")); + Serial.println(F("(c) Thorsten Voß")); + + // Knöpfe mit PullUp + pinMode(buttonPause, INPUT_PULLUP); + pinMode(buttonUp, INPUT_PULLUP); + pinMode(buttonDown, INPUT_PULLUP); + + // Busy Pin + pinMode(busyPin, INPUT); + + // DFPlayer Mini initialisieren + mp3.begin(); + mp3.setVolume(15); + + // 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; + } + + // RESET --- ALLE DREI KNÖPFE BEIM STARTEN GEDRÜCKT HALTEN -> alle bekannten + // Karten 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.write(i, 0); + } + } + +} + +void loop() { + do { + mp3.loop(); + // Buttons werden nun über JS_Button gehandelt, dadurch kann jede Taste + // doppelt belegt werden + pauseButton.read(); + upButton.read(); + downButton.read(); + + if (pauseButton.wasReleased()) { + if (ignorePauseButton == false) + if (isPlaying()) + mp3.pause(); + else + mp3.start(); + ignorePauseButton = false; + } else if (pauseButton.pressedFor(LONG_PRESS) && + ignorePauseButton == false) { + if (isPlaying()) + mp3.playAdvertisement(track); + else { + knownCard = false; + mp3.playMp3FolderTrack(800); + Serial.println(F("Karte resetten...")); + resetCard(); + mfrc522.PICC_HaltA(); + mfrc522.PCD_StopCrypto1(); + } + ignorePauseButton = true; + } + + if (upButton.pressedFor(LONG_PRESS)) { + Serial.println(F("Volume Up")); + mp3.increaseVolume(); + ignoreUpButton = true; + } else if (upButton.wasReleased()) { + if (!ignoreUpButton) + nextTrack(); + else + ignoreUpButton = false; + } + + if (downButton.pressedFor(LONG_PRESS)) { + Serial.println(F("Volume Down")); + mp3.decreaseVolume(); + ignoreDownButton = true; + } else if (downButton.wasReleased()) { + if (!ignoreDownButton) + previousTrack(); + else + ignoreDownButton = false; + } + // Ende der Buttons + } while (!mfrc522.PICC_IsNewCardPresent()); + + // RFID Karte wurde aufgelegt + + if (!mfrc522.PICC_ReadCardSerial()) + return; + + if (readCard(&myCard) == true) { + if (myCard.cookie == 322417479 && myCard.folder != 0 && myCard.mode != 0) { + + knownCard = true; + numTracksInFolder = mp3.getFolderTrackCount(myCard.folder); + + // Hörspielmodus: eine zufällige Datei aus dem Ordner + if (myCard.mode == 1) { + Serial.println(F("Hörspielmodus -> zufälligen Track wiedergeben")); + track = random(1, numTracksInFolder + 1); + Serial.println(track); + mp3.playFolderTrack(myCard.folder, track); + } + // Album Modus: kompletten Ordner spielen + if (myCard.mode == 2) { + Serial.println(F("Album Modus -> kompletten Ordner wiedergeben")); + track = 1; + mp3.playFolderTrack(myCard.folder, track); + } + // Party Modus: Ordner in zufälliger Reihenfolge + if (myCard.mode == 3) { + Serial.println( + F("Party Modus -> Ordner in zufälliger Reihenfolge wiedergeben")); + track = random(1, numTracksInFolder + 1); + mp3.playFolderTrack(myCard.folder, track); + } + // Einzel Modus: eine Datei aus dem Ordner abspielen + if (myCard.mode == 4) { + Serial.println( + F("Einzel Modus -> eine Datei aus dem Odrdner abspielen")); + track = myCard.special; + mp3.playFolderTrack(myCard.folder, track); + } + // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken + if (myCard.mode == 5) { + Serial.println(F("Hörbuch Modus -> kompletten Ordner spielen und " + "Fortschritt merken")); + track = EEPROM.read(myCard.folder); + mp3.playFolderTrack(myCard.folder, track); + } + } + + // Neue Karte konfigurieren + else { + knownCard = false; + setupCard(); + } + } + mfrc522.PICC_HaltA(); + mfrc522.PCD_StopCrypto1(); +} + +int voiceMenu(int numberOfOptions, int startMessage, int messageOffset, + bool preview = false, int previewFromFolder = 0) { + int returnValue = 0; + if (startMessage != 0) + mp3.playMp3FolderTrack(startMessage); + do { + pauseButton.read(); + upButton.read(); + downButton.read(); + mp3.loop(); + if (pauseButton.wasPressed()) { + if (returnValue != 0) + return returnValue; + delay(1000); + } + + if (upButton.pressedFor(LONG_PRESS)) { + returnValue = min(returnValue + 10, numberOfOptions); + mp3.playMp3FolderTrack(messageOffset + returnValue); + delay(1000); + if (preview) { + do { + delay(10); + } while (isPlaying()); + 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); + mp3.playMp3FolderTrack(messageOffset + returnValue); + delay(1000); + if (preview) { + do { + delay(10); + } while (isPlaying()); + if (previewFromFolder == 0) + mp3.playFolderTrack(returnValue, 1); + else + mp3.playFolderTrack(previewFromFolder, returnValue); + } + } else + ignoreUpButton = false; + } + + if (downButton.pressedFor(LONG_PRESS)) { + returnValue = max(returnValue - 10, 1); + mp3.playMp3FolderTrack(messageOffset + returnValue); + delay(1000); + if (preview) { + do { + delay(10); + } while (isPlaying()); + 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); + mp3.playMp3FolderTrack(messageOffset + returnValue); + delay(1000); + if (preview) { + do { + delay(10); + } while (isPlaying()); + if (previewFromFolder == 0) + mp3.playFolderTrack(returnValue, 1); + else + mp3.playFolderTrack(previewFromFolder, returnValue); + } + } else + ignoreDownButton = false; + } + } while (true); +} + +void resetCard() { + 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(); +} + +void setupCard() { + mp3.pause(); + Serial.print(F("Neue Karte konfigurieren")); + + // Ordner abfragen + myCard.folder = voiceMenu(99, 300, 0, true); + + // Wiedergabemodus abfragen + myCard.mode = voiceMenu(6, 310, 310); + + // Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen + EEPROM.write(myCard.folder,1); + + // Einzelmodus -> Datei abfragen + if (myCard.mode == 4) + myCard.special = voiceMenu(mp3.getFolderTrackCount(myCard.folder), 320, 0, + true, myCard.folder); + + // Admin Funktionen + if (myCard.mode == 6) + myCard.special = voiceMenu(3, 320, 320); + + // Karte ist konfiguriert -> speichern + writeCard(myCard); +} + +bool readCard(nfcTagObject *nfcTag) { + bool returnValue = true; + // 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 + Serial.println(F("Authenticating using key A...")); + status = (MFRC522::StatusCode)mfrc522.PCD_Authenticate( + MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + returnValue = false; + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // 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 + 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) { + returnValue = false; + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + Serial.print(F("Data in block ")); + Serial.print(blockAddr); + 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->folder = buffer[5]; + nfcTag->mode = buffer[6]; + nfcTag->special = buffer[7]; + + return returnValue; +} + +void writeCard(nfcTagObject nfcTag) { + MFRC522::PICC_Type mifareType; + byte buffer[16] = {0x13, 0x37, 0xb3, 0x47, // 0x1337 0xb347 magic cookie to + // identify our nfc tags + 0x01, // version 1 + nfcTag.folder, // the folder picked by the user + nfcTag.mode, // the playback mode picked by the user + nfcTag.special, // track or function for admin cards + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + byte size = sizeof(buffer); + + mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); + + // Authenticate using key B + Serial.println(F("Authenticating again using key B...")); + status = (MFRC522::StatusCode)mfrc522.PCD_Authenticate( + MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + 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(); + status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(blockAddr, buffer, 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(100); +} + +/** + * 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); + } +} + diff --git a/Tonuino.ino b/Tonuino.ino index e48a2a5..1ce775d 100644 --- a/Tonuino.ino +++ b/Tonuino.ino @@ -441,7 +441,6 @@ void setupCard() { myCard.special = voiceMenu(3, 320, 320); // Karte ist konfiguriert -> speichern - mp3.playMp3FolderTrack(400); writeCard(myCard); } @@ -527,6 +526,7 @@ void writeCard(nfcTagObject nfcTag) { if (status != MFRC522::STATUS_OK) { Serial.print(F("PCD_Authenticate() failed: ")); Serial.println(mfrc522.GetStatusCodeName(status)); + mp3.playMp3FolderTrack(401); return; } @@ -540,7 +540,10 @@ void writeCard(nfcTagObject nfcTag) { 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(100); } diff --git a/community/TonUINO/Tonuino.ino b/community/TonUINO/Tonuino.ino new file mode 100644 index 0000000..1a17221 --- /dev/null +++ b/community/TonUINO/Tonuino.ino @@ -0,0 +1,559 @@ +#include +#include +#include +#include +#include +#include + +// DFPlayer Mini +SoftwareSerial mySoftwareSerial(2, 3); // RX, TX +uint16_t numTracksInFolder; +uint16_t track; + +// this object stores nfc tag data +struct nfcTagObject { + uint32_t cookie; + uint8_t version; + uint8_t folder; + uint8_t mode; + uint8_t special; +}; + +nfcTagObject myCard; + +static void nextTrack(); +int voiceMenu(int numberOfOptions, int startMessage, int messageOffset, + bool preview = false, int previewFromFolder = 0); + +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 OnPlayFinished(uint16_t track) { + Serial.print("Track beendet"); + Serial.println(track); + delay(100); + nextTrack(); + } + static void OnCardOnline(uint16_t code) { + Serial.println(F("SD Karte online ")); + } + static void OnCardInserted(uint16_t code) { + Serial.println(F("SD Karte bereit ")); + } + static void OnCardRemoved(uint16_t code) { + Serial.println(F("SD Karte entfernt ")); + } +}; + +static DFMiniMp3 mp3(mySoftwareSerial); + +// Leider kann das Modul keine Queue abspielen. +static void nextTrack() { + if (knownCard == false) + // Wenn eine neue Karte angelernt wird soll das Ende eines Tracks nicht + // verarbeitet werden + return; + + if (myCard.mode == 1) { + Serial.println(F("Hörspielmodus ist aktiv -> Strom sparen")); + mp3.sleep(); + } + if (myCard.mode == 2) { + if (track != numTracksInFolder) { + track = track + 1; + mp3.playFolderTrack(myCard.folder, track); + Serial.print(F("Albummodus ist aktiv -> nächster Track: ")); + Serial.print(track); + } else + mp3.sleep(); + } + if (myCard.mode == 3) { + track = random(1, numTracksInFolder + 1); + Serial.print(F("Party Modus ist aktiv -> zufälligen Track spielen: ")); + Serial.println(track); + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 4) { + Serial.println(F("Einzel Modus aktiv -> Strom sparen")); + mp3.sleep(); + } + if (myCard.mode == 5) { + if (track != numTracksInFolder) { + track = track + 1; + Serial.print(F("Hörbuch Modus ist aktiv -> nächster Track und " + "Fortschritt speichern")); + Serial.println(track); + mp3.playFolderTrack(myCard.folder, track); + // Fortschritt im EEPROM abspeichern + EEPROM.write(myCard.folder, track); + } else + mp3.sleep(); + } +} + +static void previousTrack() { + if (myCard.mode == 1) { + Serial.println(F("Hörspielmodus ist aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 2) { + Serial.println(F("Albummodus ist aktiv -> vorheriger Track")); + if (track != 1) { + track = track - 1; + } + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 3) { + Serial.println(F("Party Modus ist aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 4) { + Serial.println(F("Einzel Modus aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myCard.folder, track); + } + if (myCard.mode == 5) { + Serial.println(F("Hörbuch Modus ist aktiv -> vorheriger Track und " + "Fortschritt speichern")); + if (track != 1) { + track = track - 1; + } + mp3.playFolderTrack(myCard.folder, track); + // Fortschritt im EEPROM abspeichern + EEPROM.write(myCard.folder, track); + } +} + +// 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 LONG_PRESS 1000 + +Button pauseButton(buttonPause); +Button upButton(buttonUp); +Button downButton(buttonDown); +bool ignorePauseButton = false; +bool ignoreUpButton = false; +bool ignoreDownButton = false; + +uint8_t numberOfCards = 0; + +bool isPlaying() { return !digitalRead(busyPin); } + +void setup() { + + Serial.begin(115200); // Es gibt ein paar Debug Ausgaben über die serielle + // Schnittstelle + randomSeed(analogRead(A0)); // Zufallsgenerator initialisieren + + Serial.println(F("TonUINO Version 2.0")); + Serial.println(F("(c) Thorsten Voß")); + + // Knöpfe mit PullUp + pinMode(buttonPause, INPUT_PULLUP); + pinMode(buttonUp, INPUT_PULLUP); + pinMode(buttonDown, INPUT_PULLUP); + + // Busy Pin + pinMode(busyPin, INPUT); + + // DFPlayer Mini initialisieren + mp3.begin(); + mp3.setVolume(15); + + // 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; + } + + // RESET --- ALLE DREI KNÖPFE BEIM STARTEN GEDRÜCKT HALTEN -> alle bekannten + // Karten 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.write(i, 0); + } + } + +} + +void loop() { + do { + mp3.loop(); + // Buttons werden nun über JS_Button gehandelt, dadurch kann jede Taste + // doppelt belegt werden + pauseButton.read(); + upButton.read(); + downButton.read(); + + if (pauseButton.wasReleased()) { + if (ignorePauseButton == false) + if (isPlaying()) + mp3.pause(); + else + mp3.start(); + ignorePauseButton = false; + } else if (pauseButton.pressedFor(LONG_PRESS) && + ignorePauseButton == false) { + if (isPlaying()) + mp3.playAdvertisement(track); + else { + knownCard = false; + mp3.playMp3FolderTrack(800); + Serial.println(F("Karte resetten...")); + resetCard(); + mfrc522.PICC_HaltA(); + mfrc522.PCD_StopCrypto1(); + } + ignorePauseButton = true; + } + + if (upButton.pressedFor(LONG_PRESS)) { + Serial.println(F("Volume Up")); + mp3.increaseVolume(); + ignoreUpButton = true; + } else if (upButton.wasReleased()) { + if (!ignoreUpButton) + nextTrack(); + else + ignoreUpButton = false; + } + + if (downButton.pressedFor(LONG_PRESS)) { + Serial.println(F("Volume Down")); + mp3.decreaseVolume(); + ignoreDownButton = true; + } else if (downButton.wasReleased()) { + if (!ignoreDownButton) + previousTrack(); + else + ignoreDownButton = false; + } + // Ende der Buttons + } while (!mfrc522.PICC_IsNewCardPresent()); + + // RFID Karte wurde aufgelegt + + if (!mfrc522.PICC_ReadCardSerial()) + return; + + if (readCard(&myCard) == true) { + if (myCard.cookie == 322417479 && myCard.folder != 0 && myCard.mode != 0) { + + knownCard = true; + numTracksInFolder = mp3.getFolderTrackCount(myCard.folder); + + // Hörspielmodus: eine zufällige Datei aus dem Ordner + if (myCard.mode == 1) { + Serial.println(F("Hörspielmodus -> zufälligen Track wiedergeben")); + track = random(1, numTracksInFolder + 1); + Serial.println(track); + mp3.playFolderTrack(myCard.folder, track); + } + // Album Modus: kompletten Ordner spielen + if (myCard.mode == 2) { + Serial.println(F("Album Modus -> kompletten Ordner wiedergeben")); + track = 1; + mp3.playFolderTrack(myCard.folder, track); + } + // Party Modus: Ordner in zufälliger Reihenfolge + if (myCard.mode == 3) { + Serial.println( + F("Party Modus -> Ordner in zufälliger Reihenfolge wiedergeben")); + track = random(1, numTracksInFolder + 1); + mp3.playFolderTrack(myCard.folder, track); + } + // Einzel Modus: eine Datei aus dem Ordner abspielen + if (myCard.mode == 4) { + Serial.println( + F("Einzel Modus -> eine Datei aus dem Odrdner abspielen")); + track = myCard.special; + mp3.playFolderTrack(myCard.folder, track); + } + // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken + if (myCard.mode == 5) { + Serial.println(F("Hörbuch Modus -> kompletten Ordner spielen und " + "Fortschritt merken")); + track = EEPROM.read(myCard.folder); + mp3.playFolderTrack(myCard.folder, track); + } + } + + // Neue Karte konfigurieren + else { + knownCard = false; + setupCard(); + } + } + mfrc522.PICC_HaltA(); + mfrc522.PCD_StopCrypto1(); +} + +int voiceMenu(int numberOfOptions, int startMessage, int messageOffset, + bool preview = false, int previewFromFolder = 0) { + int returnValue = 0; + if (startMessage != 0) + mp3.playMp3FolderTrack(startMessage); + do { + pauseButton.read(); + upButton.read(); + downButton.read(); + mp3.loop(); + if (pauseButton.wasPressed()) { + if (returnValue != 0) + return returnValue; + delay(1000); + } + + if (upButton.pressedFor(LONG_PRESS)) { + returnValue = min(returnValue + 10, numberOfOptions); + mp3.playMp3FolderTrack(messageOffset + returnValue); + delay(1000); + if (preview) { + do { + delay(10); + } while (isPlaying()); + 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); + mp3.playMp3FolderTrack(messageOffset + returnValue); + delay(1000); + if (preview) { + do { + delay(10); + } while (isPlaying()); + if (previewFromFolder == 0) + mp3.playFolderTrack(returnValue, 1); + else + mp3.playFolderTrack(previewFromFolder, returnValue); + } + } else + ignoreUpButton = false; + } + + if (downButton.pressedFor(LONG_PRESS)) { + returnValue = max(returnValue - 10, 1); + mp3.playMp3FolderTrack(messageOffset + returnValue); + delay(1000); + if (preview) { + do { + delay(10); + } while (isPlaying()); + 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); + mp3.playMp3FolderTrack(messageOffset + returnValue); + delay(1000); + if (preview) { + do { + delay(10); + } while (isPlaying()); + if (previewFromFolder == 0) + mp3.playFolderTrack(returnValue, 1); + else + mp3.playFolderTrack(previewFromFolder, returnValue); + } + } else + ignoreDownButton = false; + } + } while (true); +} + +void resetCard() { + 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(); +} + +void setupCard() { + mp3.pause(); + Serial.print(F("Neue Karte konfigurieren")); + + // Ordner abfragen + myCard.folder = voiceMenu(99, 300, 0, true); + + // Wiedergabemodus abfragen + myCard.mode = voiceMenu(6, 310, 310); + + // Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen + EEPROM.write(myCard.folder,1); + + // Einzelmodus -> Datei abfragen + if (myCard.mode == 4) + myCard.special = voiceMenu(mp3.getFolderTrackCount(myCard.folder), 320, 0, + true, myCard.folder); + + // Admin Funktionen + if (myCard.mode == 6) + myCard.special = voiceMenu(3, 320, 320); + + // Karte ist konfiguriert -> speichern + writeCard(myCard); +} + +bool readCard(nfcTagObject *nfcTag) { + bool returnValue = true; + // 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 + Serial.println(F("Authenticating using key A...")); + status = (MFRC522::StatusCode)mfrc522.PCD_Authenticate( + MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + returnValue = false; + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // 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 + 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) { + returnValue = false; + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + Serial.print(F("Data in block ")); + Serial.print(blockAddr); + 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->folder = buffer[5]; + nfcTag->mode = buffer[6]; + nfcTag->special = buffer[7]; + + return returnValue; +} + +void writeCard(nfcTagObject nfcTag) { + MFRC522::PICC_Type mifareType; + byte buffer[16] = {0x13, 0x37, 0xb3, 0x47, // 0x1337 0xb347 magic cookie to + // identify our nfc tags + 0x01, // version 1 + nfcTag.folder, // the folder picked by the user + nfcTag.mode, // the playback mode picked by the user + nfcTag.special, // track or function for admin cards + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + byte size = sizeof(buffer); + + mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); + + // Authenticate using key B + Serial.println(F("Authenticating again using key B...")); + status = (MFRC522::StatusCode)mfrc522.PCD_Authenticate( + MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + 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(); + status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(blockAddr, buffer, 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(100); +} + +/** + * 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); + } +} + diff --git a/create-soundfiles.sh b/create-soundfiles.sh index 3871aa3..1d8831c 100755 --- a/create-soundfiles.sh +++ b/create-soundfiles.sh @@ -72,14 +72,14 @@ say -v Anna "Ja, Nummer ansagen." -o 0332.aiff sox 0332.aiff 0332.wav pitch 800 lame -b 128 0332.wav 0332.mp3 -#say -v Anna "Bitte lege die Karte erneut auf und warte auf die Bestätigung." -o 0400.aiff -#sox 0400.aiff 0400.wav pitch 800 -#lame -b 128 0400.wav 0400_wait_for_card.mp3 - say -v Anna "OK. Ich habe die Karte konfiguriert." -o 0400.aiff sox 0400.aiff 0400.wav pitch 800 lame -b 128 0400.wav 0400_ok.mp3 +say -v Anna "Oh weh! Das hat leider nicht geklappt!." -o 0401.aiff +sox 0401.aiff 0401.wav pitch 800 +lame -b 128 0401.wav 0401_error.mp3 + rm *.aiff rm *.wav