From cad91967b1051c8ba1813bba0f36c68f6c8fcec7 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 23 May 2024 16:16:00 +0200 Subject: [PATCH 01/14] migration to platformio --- .gitattributes | 49 ++ .gitignore | 2 + include/README | 39 + lib/README | 46 + platformio.ini | 18 + src/Adafruit_PN532.cpp | 1820 ++++++++++++++++++++++++++++++++++++++++ src/Adafruit_PN532.h | 221 +++++ src/audioState.h | 73 ++ src/context.cpp | 17 + src/context.h | 15 + src/main.cpp | 185 ++++ src/sounds.h | 10 + src/state.h | 17 + src/statemachine.cpp | 37 + src/statemachine.h | 21 + src/states.cpp | 213 +++++ src/states.h | 98 +++ src/station.cpp | 17 + src/station.h | 12 + src/timer.cpp | 47 ++ src/timer.h | 17 + test/README | 11 + 22 files changed, 2985 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 include/README create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/Adafruit_PN532.cpp create mode 100644 src/Adafruit_PN532.h create mode 100644 src/audioState.h create mode 100644 src/context.cpp create mode 100644 src/context.h create mode 100644 src/main.cpp create mode 100644 src/sounds.h create mode 100644 src/state.h create mode 100644 src/statemachine.cpp create mode 100644 src/statemachine.h create mode 100644 src/states.cpp create mode 100644 src/states.h create mode 100644 src/station.cpp create mode 100644 src/station.h create mode 100644 src/timer.cpp create mode 100644 src/timer.h create mode 100644 test/README diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..145857d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,49 @@ +# Auto detect text files and perform LF normalization +* text=auto +# Custom for Visual Studio +*.cs diff=csharp +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain + +# ------------------------ +# LFS +#------------------------- +# libraries and packages +*.a filter=lfs -crlf +*.apk filter=lfs diff=lfs merge=lfs -text +*.dll filter=lfs -crlf +*.dylib filter=lfs diff=lfs merge=lfs -text +*.exe filter=lfs -crlf +*.rar filter=lfs diff=lfs merge=lfs -text +*.unitypackage filter=lfs -crlf +*.zip filter=lfs -crlf + +# models and playables +*.blend filter=lfs -crlf +*.wav filter=lfs diff=lfs merge=lfs -text + +# images +*.cubemap filter=lfs -crlf +*.exr filter=lfs -crlf +*.fbx filter=lfs -crlf +*.hdr filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs -crlf +*.gif filter=lfs -crlf +*.mp3 filter=lfs -crlf +*.wav filter=lfs -crlf +*.mp4 filter=lfs -crlf +*.png filter=lfs -crlf +*.psd filter=lfs -crlf +*.tga filter=lfs -crlf +*.tif filter=lfs -crlf + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b43bfca --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.pio +.idea diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..2593a33 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..89e16ed --- /dev/null +++ b/platformio.ini @@ -0,0 +1,18 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:uno] +platform = atmelavr +board = uno +framework = arduino +lib_deps = + adafruit/Adafruit BusIO@^1.16.1 + dfrobot/DFRobotDFPlayerMini@^1.0.6 + fmalpartida/LiquidCrystal@^1.5.0 diff --git a/src/Adafruit_PN532.cpp b/src/Adafruit_PN532.cpp new file mode 100644 index 0000000..802db33 --- /dev/null +++ b/src/Adafruit_PN532.cpp @@ -0,0 +1,1820 @@ +/**************************************************************************/ +/*! + @file Adafruit_PN532.cpp + + @section intro_sec Introduction + + Driver for NXP's PN532 NFC/13.56MHz RFID Transceiver + + This is a library for the Adafruit PN532 NFC/RFID breakout boards + This library works with the Adafruit NFC breakout + ----> https://www.adafruit.com/products/364 + + Check out the links above for our tutorials and wiring diagrams + These chips use SPI or I2C to communicate. + + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + @section author Author + + Adafruit Industries + + @section license License + + BSD (see license.txt) + + @section HISTORY + + v2.2 - Added startPassiveTargetIDDetection() to start card detection and + readDetectedPassiveTargetID() to read it, useful when using the + IRQ pin. + + v2.1 - Added NTAG2xx helper functions + + v2.0 - Refactored to add I2C support from Adafruit_NFCShield_I2C library. + + v1.4 - Added setPassiveActivationRetries() + + v1.2 - Added writeGPIO() + - Added readGPIO() + + v1.1 - Changed readPassiveTargetID() to handle multiple UID sizes + - Added the following helper functions for text display + static void PrintHex(const byte * data, const uint32_t numBytes) + static void PrintHexChar(const byte * pbtData, const uint32_t + numBytes) + - Added the following Mifare Classic functions: + bool mifareclassic_IsFirstBlock (uint32_t uiBlock) + bool mifareclassic_IsTrailerBlock (uint32_t uiBlock) + uint8_t mifareclassic_AuthenticateBlock (uint8_t * uid, uint8_t + uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t * keyData) uint8_t + mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t * data) uint8_t + mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t * data) + - Added the following Mifare Ultalight functions: + uint8_t mifareultralight_ReadPage (uint8_t page, uint8_t * buffer) +*/ +/**************************************************************************/ + +#include "Adafruit_PN532.h" + +byte pn532ack[] = {0x00, 0x00, 0xFF, + 0x00, 0xFF, 0x00}; ///< ACK message from PN532 +byte pn532response_firmwarevers[] = { + 0x00, 0x00, 0xFF, + 0x06, 0xFA, 0xD5}; ///< Expected firmware version message from PN532 + +// Uncomment these lines to enable debug output for PN532(SPI) and/or MIFARE +// related code + +// #define PN532DEBUG +// #define MIFAREDEBUG + +// If using Native Port on Arduino Zero or Due define as SerialUSB +#define PN532DEBUGPRINT Serial ///< Fixed name for debug Serial instance +//#define PN532DEBUGPRINT SerialUSB ///< Fixed name for debug Serial instance + +#define PN532_PACKBUFFSIZ 64 ///< Packet buffer size in bytes +byte pn532_packetbuffer[PN532_PACKBUFFSIZ]; ///< Packet buffer used in various + ///< transactions + +/**************************************************************************/ +/*! + @brief Instantiates a new PN532 class using software SPI. + + @param clk SPI clock pin (SCK) + @param miso SPI MISO pin + @param mosi SPI MOSI pin + @param ss SPI chip select pin (CS/SSEL) +*/ +/**************************************************************************/ +Adafruit_PN532::Adafruit_PN532(uint8_t clk, uint8_t miso, uint8_t mosi, + uint8_t ss) { + _cs = ss; + spi_dev = new Adafruit_SPIDevice(ss, clk, miso, mosi, 1000000, + SPI_BITORDER_LSBFIRST, SPI_MODE0); +} + +/**************************************************************************/ +/*! + @brief Instantiates a new PN532 class using I2C. + + @param irq Location of the IRQ pin + @param reset Location of the RSTPD_N pin + @param theWire pointer to I2C bus to use +*/ +/**************************************************************************/ +Adafruit_PN532::Adafruit_PN532(uint8_t irq, uint8_t reset, TwoWire *theWire) + : _irq(irq), _reset(reset) { + pinMode(_irq, INPUT); + pinMode(_reset, OUTPUT); + i2c_dev = new Adafruit_I2CDevice(PN532_I2C_ADDRESS, theWire); +} + +/**************************************************************************/ +/*! + @brief Instantiates a new PN532 class using hardware SPI. + + @param ss SPI chip select pin (CS/SSEL) + @param theSPI pointer to the SPI bus to use +*/ +/**************************************************************************/ +Adafruit_PN532::Adafruit_PN532(uint8_t ss, SPIClass *theSPI) { + _cs = ss; + spi_dev = new Adafruit_SPIDevice(ss, 1000000, SPI_BITORDER_LSBFIRST, + SPI_MODE0, theSPI); +} + +/**************************************************************************/ +/*! + @brief Instantiates a new PN532 class using hardware UART (HSU). + + @param reset Location of the RSTPD_N pin + @param theSer pointer to HardWare Serial bus to use +*/ +/**************************************************************************/ +Adafruit_PN532::Adafruit_PN532(uint8_t reset, HardwareSerial *theSer) + : _reset(reset) { + pinMode(_reset, OUTPUT); + ser_dev = theSer; +} + +/**************************************************************************/ +/*! + @brief Setups the HW + + @returns true if successful, otherwise false +*/ +/**************************************************************************/ +bool Adafruit_PN532::begin() { + if (spi_dev) { + // SPI initialization + if (!spi_dev->begin()) { + return false; + } + } else if (i2c_dev) { + // I2C initialization + // PN532 will fail address check since its asleep, so suppress + if (!i2c_dev->begin(false)) { + return false; + } + } else if (ser_dev) { + ser_dev->begin(115200); + // clear out anything in read buffer + while (ser_dev->available()) + ser_dev->read(); + } else { + // no interface specified + return false; + } + reset(); // HW reset - put in known state + delay(10); + wakeup(); // hey! wakeup! + return true; +} + +/**************************************************************************/ +/*! + @brief Perform a hardware reset. Requires reset pin to have been provided. +*/ +/**************************************************************************/ +void Adafruit_PN532::reset(void) { + // see Datasheet p.209, Fig.48 for timings + if (_reset != -1) { + digitalWrite(_reset, LOW); + delay(1); // min 20ns + digitalWrite(_reset, HIGH); + delay(2); // max 2ms + } +} + +/**************************************************************************/ +/*! + @brief Wakeup from LowVbat mode into Normal Mode. +*/ +/**************************************************************************/ +void Adafruit_PN532::wakeup(void) { + // interface specific wakeups - each one is unique! + if (spi_dev) { + // hold CS low for 2ms + digitalWrite(_cs, LOW); + delay(2); + } else if (ser_dev) { + uint8_t w[3] = {0x55, 0x00, 0x00}; + ser_dev->write(w, 3); + delay(2); + } + + // PN532 will clock stretch I2C during SAMConfig as a "wakeup" + + // need to config SAM to stay in Normal Mode + SAMConfig(); +} + +/**************************************************************************/ +/*! + @brief Prints a hexadecimal value in plain characters + + @param data Pointer to the byte data + @param numBytes Data length in bytes +*/ +/**************************************************************************/ +void Adafruit_PN532::PrintHex(const byte *data, const uint32_t numBytes) { + uint32_t szPos; + for (szPos = 0; szPos < numBytes; szPos++) { + PN532DEBUGPRINT.print(F("0x")); + // Append leading 0 for small values + if (data[szPos] <= 0xF) + PN532DEBUGPRINT.print(F("0")); + PN532DEBUGPRINT.print(data[szPos] & 0xff, HEX); + if ((numBytes > 1) && (szPos != numBytes - 1)) { + PN532DEBUGPRINT.print(F(" ")); + } + } + PN532DEBUGPRINT.println(); +} + +/**************************************************************************/ +/*! + @brief Prints a hexadecimal value in plain characters, along with + the char equivalents in the following format + + 00 00 00 00 00 00 ...... + + @param data Pointer to the byte data + @param numBytes Data length in bytes +*/ +/**************************************************************************/ +void Adafruit_PN532::PrintHexChar(const byte *data, const uint32_t numBytes) { + uint32_t szPos; + for (szPos = 0; szPos < numBytes; szPos++) { + // Append leading 0 for small values + if (data[szPos] <= 0xF) + PN532DEBUGPRINT.print(F("0")); + PN532DEBUGPRINT.print(data[szPos], HEX); + if ((numBytes > 1) && (szPos != numBytes - 1)) { + PN532DEBUGPRINT.print(F(" ")); + } + } + PN532DEBUGPRINT.print(F(" ")); + for (szPos = 0; szPos < numBytes; szPos++) { + if (data[szPos] <= 0x1F) + PN532DEBUGPRINT.print(F(".")); + else + PN532DEBUGPRINT.print((char)data[szPos]); + } + PN532DEBUGPRINT.println(); +} + +/**************************************************************************/ +/*! + @brief Checks the firmware version of the PN5xx chip + + @returns The chip's firmware version and ID +*/ +/**************************************************************************/ +uint32_t Adafruit_PN532::getFirmwareVersion(void) { + uint32_t response; + + pn532_packetbuffer[0] = PN532_COMMAND_GETFIRMWAREVERSION; + + if (!sendCommandCheckAck(pn532_packetbuffer, 1)) { + return 0; + } + + // read data packet + readdata(pn532_packetbuffer, 13); + + // check some basic stuff + if (0 != memcmp((char *)pn532_packetbuffer, + (char *)pn532response_firmwarevers, 6)) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Firmware doesn't match!")); +#endif + return 0; + } + + int offset = 7; + response = pn532_packetbuffer[offset++]; + response <<= 8; + response |= pn532_packetbuffer[offset++]; + response <<= 8; + response |= pn532_packetbuffer[offset++]; + response <<= 8; + response |= pn532_packetbuffer[offset++]; + + return response; +} + +/**************************************************************************/ +/*! + @brief Sends a command and waits a specified period for the ACK + + @param cmd Pointer to the command buffer + @param cmdlen The size of the command in bytes + @param timeout timeout before giving up + + @returns 1 if everything is OK, 0 if timeout occured before an + ACK was recieved +*/ +/**************************************************************************/ +// default timeout of one second +bool Adafruit_PN532::sendCommandCheckAck(uint8_t *cmd, uint8_t cmdlen, + uint16_t timeout) { + + // I2C works without using IRQ pin by polling for RDY byte + // seems to work best with some delays between transactions + uint8_t SLOWDOWN = 0; + if (i2c_dev) + SLOWDOWN = 1; + + // write the command + writecommand(cmd, cmdlen); + + // I2C TUNING + delay(1); + + // Wait for chip to say its ready! + if (!waitready(timeout)) { + return false; + } + +#ifdef PN532DEBUG + if (spi_dev == NULL) { + PN532DEBUGPRINT.println(F("IRQ received")); + } +#endif + + // read acknowledgement + if (!readack()) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("No ACK frame received!")); +#endif + return false; + } + + // I2C TUNING + delay(SLOWDOWN); + + // Wait for chip to say its ready! + if (!waitready(timeout)) { + return false; + } + + return true; // ack'd command +} + +/**************************************************************************/ +/*! + @brief Writes an 8-bit value that sets the state of the PN532's GPIO + pins. + @param pinstate P3 pins state. + + @warning This function is provided exclusively for board testing and + is dangerous since it will throw an error if any pin other + than the ones marked "Can be used as GPIO" are modified! All + pins that can not be used as GPIO should ALWAYS be left high + (value = 1) or the system will become unstable and a HW reset + will be required to recover the PN532. + + pinState[0] = P30 Can be used as GPIO + pinState[1] = P31 Can be used as GPIO + pinState[2] = P32 *** RESERVED (Must be 1!) *** + pinState[3] = P33 Can be used as GPIO + pinState[4] = P34 *** RESERVED (Must be 1!) *** + pinState[5] = P35 Can be used as GPIO + + @return 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool Adafruit_PN532::writeGPIO(uint8_t pinstate) { + // uint8_t errorbit; + + // Make sure pinstate does not try to toggle P32 or P34 + pinstate |= (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34); + + // Fill command buffer + pn532_packetbuffer[0] = PN532_COMMAND_WRITEGPIO; + pn532_packetbuffer[1] = PN532_GPIO_VALIDATIONBIT | pinstate; // P3 Pins + pn532_packetbuffer[2] = 0x00; // P7 GPIO Pins (not used ... taken by SPI) + +#ifdef PN532DEBUG + PN532DEBUGPRINT.print(F("Writing P3 GPIO: ")); + PN532DEBUGPRINT.println(pn532_packetbuffer[1], HEX); +#endif + + // Send the WRITEGPIO command (0x0E) + if (!sendCommandCheckAck(pn532_packetbuffer, 3)) + return 0x0; + + // Read response packet (00 FF PLEN PLENCHECKSUM D5 CMD+1(0x0F) DATACHECKSUM + // 00) + readdata(pn532_packetbuffer, 8); + +#ifdef PN532DEBUG + PN532DEBUGPRINT.print(F("Received: ")); + PrintHex(pn532_packetbuffer, 8); + PN532DEBUGPRINT.println(); +#endif + + int offset = 6; + return (pn532_packetbuffer[offset] == 0x0F); +} + +/**************************************************************************/ +/*! + Reads the state of the PN532's GPIO pins + + @returns An 8-bit value containing the pin state where: + + pinState[0] = P30 + pinState[1] = P31 + pinState[2] = P32 + pinState[3] = P33 + pinState[4] = P34 + pinState[5] = P35 +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::readGPIO(void) { + pn532_packetbuffer[0] = PN532_COMMAND_READGPIO; + + // Send the READGPIO command (0x0C) + if (!sendCommandCheckAck(pn532_packetbuffer, 1)) + return 0x0; + + // Read response packet (00 FF PLEN PLENCHECKSUM D5 CMD+1(0x0D) P3 P7 IO1 + // DATACHECKSUM 00) + readdata(pn532_packetbuffer, 11); + + /* READGPIO response should be in the following format: + + byte Description + ------------- ------------------------------------------ + b0..5 Frame header and preamble (with I2C there is an extra 0x00) + b6 P3 GPIO Pins + b7 P7 GPIO Pins (not used ... taken by SPI) + b8 Interface Mode Pins (not used ... bus select pins) + b9..10 checksum */ + + int p3offset = 7; + +#ifdef PN532DEBUG + PN532DEBUGPRINT.print(F("Received: ")); + PrintHex(pn532_packetbuffer, 11); + PN532DEBUGPRINT.println(); + PN532DEBUGPRINT.print(F("P3 GPIO: 0x")); + PN532DEBUGPRINT.println(pn532_packetbuffer[p3offset], HEX); + PN532DEBUGPRINT.print(F("P7 GPIO: 0x")); + PN532DEBUGPRINT.println(pn532_packetbuffer[p3offset + 1], HEX); + PN532DEBUGPRINT.print(F("IO GPIO: 0x")); + PN532DEBUGPRINT.println(pn532_packetbuffer[p3offset + 2], HEX); + // Note: You can use the IO GPIO value to detect the serial bus being used + switch (pn532_packetbuffer[p3offset + 2]) { + case 0x00: // Using UART + PN532DEBUGPRINT.println(F("Using UART (IO = 0x00)")); + break; + case 0x01: // Using I2C + PN532DEBUGPRINT.println(F("Using I2C (IO = 0x01)")); + break; + case 0x02: // Using SPI + PN532DEBUGPRINT.println(F("Using SPI (IO = 0x02)")); + break; + } +#endif + + return pn532_packetbuffer[p3offset]; +} + +/**************************************************************************/ +/*! + @brief Configures the SAM (Secure Access Module) + @return true on success, false otherwise. +*/ +/**************************************************************************/ +bool Adafruit_PN532::SAMConfig(void) { + pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; + pn532_packetbuffer[1] = 0x01; // normal mode; + pn532_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second + pn532_packetbuffer[3] = 0x01; // use IRQ pin! + + if (!sendCommandCheckAck(pn532_packetbuffer, 4)) + return false; + + // read data packet + readdata(pn532_packetbuffer, 9); + + int offset = 6; + return (pn532_packetbuffer[offset] == 0x15); +} + +/**************************************************************************/ +/*! + Sets the MxRtyPassiveActivation byte of the RFConfiguration register + + @param maxRetries 0xFF to wait forever, 0x00..0xFE to timeout + after mxRetries + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool Adafruit_PN532::setPassiveActivationRetries(uint8_t maxRetries) { + pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; + pn532_packetbuffer[1] = 5; // Config item 5 (MaxRetries) + pn532_packetbuffer[2] = 0xFF; // MxRtyATR (default = 0xFF) + pn532_packetbuffer[3] = 0x01; // MxRtyPSL (default = 0x01) + pn532_packetbuffer[4] = maxRetries; + +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Setting MxRtyPassiveActivation to ")); + PN532DEBUGPRINT.print(maxRetries, DEC); + PN532DEBUGPRINT.println(F(" ")); +#endif + + if (!sendCommandCheckAck(pn532_packetbuffer, 5)) + return 0x0; // no ACK + + return 1; +} + +/***** ISO14443A Commands ******/ + +/**************************************************************************/ +/*! + @brief Waits for an ISO14443A target to enter the field and reads + its ID. + + @param cardbaudrate Baud rate of the card + @param uid Pointer to the array that will be populated + with the card's UID (up to 7 bytes) + @param uidLength Pointer to the variable that will hold the + length of the card's UID. + @param timeout Timeout in milliseconds. + + @return 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool Adafruit_PN532::readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, + uint8_t *uidLength, uint16_t timeout) { + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) + pn532_packetbuffer[2] = cardbaudrate; + + if (!sendCommandCheckAck(pn532_packetbuffer, 3, timeout)) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("No card(s) read")); +#endif + return 0x0; // no cards read + } + + return readDetectedPassiveTargetID(uid, uidLength); +} + +/**************************************************************************/ +/*! + @brief Put the reader in detection mode, non blocking so interrupts + must be enabled. + @param cardbaudrate Baud rate of the card + @return 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool Adafruit_PN532::startPassiveTargetIDDetection(uint8_t cardbaudrate) { + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) + pn532_packetbuffer[2] = cardbaudrate; + + return sendCommandCheckAck(pn532_packetbuffer, 3); +} + +/**************************************************************************/ +/*! + Reads the ID of the passive target the reader has deteceted. + + @param uid Pointer to the array that will be populated + with the card's UID (up to 7 bytes) + @param uidLength Pointer to the variable that will hold the + length of the card's UID. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool Adafruit_PN532::readDetectedPassiveTargetID(uint8_t *uid, + uint8_t *uidLength) { + // read data packet + readdata(pn532_packetbuffer, 20); + // check some basic stuff + + /* ISO14443A card response should be in the following format: + + byte Description + ------------- ------------------------------------------ + b0..6 Frame header and preamble + b7 Tags Found + b8 Tag Number (only one used in this example) + b9..10 SENS_RES + b11 SEL_RES + b12 NFCID Length + b13..NFCIDLen NFCID */ + +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Found ")); + PN532DEBUGPRINT.print(pn532_packetbuffer[7], DEC); + PN532DEBUGPRINT.println(F(" tags")); +#endif + if (pn532_packetbuffer[7] != 1) + return 0; + + uint16_t sens_res = pn532_packetbuffer[9]; + sens_res <<= 8; + sens_res |= pn532_packetbuffer[10]; +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("ATQA: 0x")); + PN532DEBUGPRINT.println(sens_res, HEX); + PN532DEBUGPRINT.print(F("SAK: 0x")); + PN532DEBUGPRINT.println(pn532_packetbuffer[11], HEX); +#endif + + /* Card appears to be Mifare Classic */ + *uidLength = pn532_packetbuffer[12]; +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("UID:")); +#endif + for (uint8_t i = 0; i < pn532_packetbuffer[12]; i++) { + uid[i] = pn532_packetbuffer[13 + i]; +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F(" 0x")); + PN532DEBUGPRINT.print(uid[i], HEX); +#endif + } +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(); +#endif + + return 1; +} + +/**************************************************************************/ +/*! + @brief Exchanges an APDU with the currently inlisted peer + + @param send Pointer to data to send + @param sendLength Length of the data to send + @param response Pointer to response data + @param responseLength Pointer to the response data length + @return true on success, false otherwise. +*/ +/**************************************************************************/ +bool Adafruit_PN532::inDataExchange(uint8_t *send, uint8_t sendLength, + uint8_t *response, + uint8_t *responseLength) { + if (sendLength > PN532_PACKBUFFSIZ - 2) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("APDU length too long for packet buffer")); +#endif + return false; + } + uint8_t i; + + pn532_packetbuffer[0] = 0x40; // PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = _inListedTag; + for (i = 0; i < sendLength; ++i) { + pn532_packetbuffer[i + 2] = send[i]; + } + + if (!sendCommandCheckAck(pn532_packetbuffer, sendLength + 2, 1000)) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Could not send APDU")); +#endif + return false; + } + + if (!waitready(1000)) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Response never received for APDU...")); +#endif + return false; + } + + readdata(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + if (pn532_packetbuffer[0] == 0 && pn532_packetbuffer[1] == 0 && + pn532_packetbuffer[2] == 0xff) { + uint8_t length = pn532_packetbuffer[3]; + if (pn532_packetbuffer[4] != (uint8_t)(~length + 1)) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Length check invalid")); + PN532DEBUGPRINT.println(length, HEX); + PN532DEBUGPRINT.println((~length) + 1, HEX); +#endif + return false; + } + if (pn532_packetbuffer[5] == PN532_PN532TOHOST && + pn532_packetbuffer[6] == PN532_RESPONSE_INDATAEXCHANGE) { + if ((pn532_packetbuffer[7] & 0x3f) != 0) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Status code indicates an error")); +#endif + return false; + } + + length -= 3; + + if (length > *responseLength) { + length = *responseLength; // silent truncation... + } + + for (i = 0; i < length; ++i) { + response[i] = pn532_packetbuffer[8 + i]; + } + *responseLength = length; + + return true; + } else { + PN532DEBUGPRINT.print(F("Don't know how to handle this command: ")); + PN532DEBUGPRINT.println(pn532_packetbuffer[6], HEX); + return false; + } + } else { + PN532DEBUGPRINT.println(F("Preamble missing")); + return false; + } +} + +/**************************************************************************/ +/*! + @brief 'InLists' a passive target. PN532 acting as reader/initiator, + peer acting as card/responder. + @return true on success, false otherwise. +*/ +/**************************************************************************/ +bool Adafruit_PN532::inListPassiveTarget() { + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = 0; + +#ifdef PN532DEBUG + PN532DEBUGPRINT.print(F("About to inList passive target")); +#endif + + if (!sendCommandCheckAck(pn532_packetbuffer, 3, 1000)) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Could not send inlist message")); +#endif + return false; + } + + if (!waitready(30000)) { + return false; + } + + readdata(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + if (pn532_packetbuffer[0] == 0 && pn532_packetbuffer[1] == 0 && + pn532_packetbuffer[2] == 0xff) { + uint8_t length = pn532_packetbuffer[3]; + if (pn532_packetbuffer[4] != (uint8_t)(~length + 1)) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Length check invalid")); + PN532DEBUGPRINT.println(length, HEX); + PN532DEBUGPRINT.println((~length) + 1, HEX); +#endif + return false; + } + if (pn532_packetbuffer[5] == PN532_PN532TOHOST && + pn532_packetbuffer[6] == PN532_RESPONSE_INLISTPASSIVETARGET) { + if (pn532_packetbuffer[7] != 1) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Unhandled number of targets inlisted")); +#endif + PN532DEBUGPRINT.println(F("Number of tags inlisted:")); + PN532DEBUGPRINT.println(pn532_packetbuffer[7]); + return false; + } + + _inListedTag = pn532_packetbuffer[8]; + PN532DEBUGPRINT.print(F("Tag number: ")); + PN532DEBUGPRINT.println(_inListedTag); + + return true; + } else { +#ifdef PN532DEBUG + PN532DEBUGPRINT.print(F("Unexpected response to inlist passive host")); +#endif + return false; + } + } else { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println(F("Preamble missing")); +#endif + return false; + } + + return true; +} + +/***** Mifare Classic Functions ******/ + +/**************************************************************************/ +/*! + @brief Indicates whether the specified block number is the first block + in the sector (block 0 relative to the current sector) + @param uiBlock Block number to test. + @return true if first block, false otherwise. +*/ +/**************************************************************************/ +bool Adafruit_PN532::mifareclassic_IsFirstBlock(uint32_t uiBlock) { + // Test if we are in the small or big sectors + if (uiBlock < 128) + return ((uiBlock) % 4 == 0); + else + return ((uiBlock) % 16 == 0); +} + +/**************************************************************************/ +/*! + @brief Indicates whether the specified block number is the sector + trailer. + @param uiBlock Block number to test. + @return true if sector trailer, false otherwise. +*/ +/**************************************************************************/ +bool Adafruit_PN532::mifareclassic_IsTrailerBlock(uint32_t uiBlock) { + // Test if we are in the small or big sectors + if (uiBlock < 128) + return ((uiBlock + 1) % 4 == 0); + else + return ((uiBlock + 1) % 16 == 0); +} + +/**************************************************************************/ +/*! + Tries to authenticate a block of memory on a MIFARE card using the + INDATAEXCHANGE command. See section 7.3.8 of the PN532 User Manual + for more information on sending MIFARE and other commands. + + @param uid Pointer to a byte array containing the card UID + @param uidLen The length (in bytes) of the card's UID (Should + be 4 for MIFARE Classic) + @param blockNumber The block number to authenticate. (0..63 for + 1KB cards, and 0..255 for 4KB cards). + @param keyNumber Which key type to use during authentication + (0 = MIFARE_CMD_AUTH_A, 1 = MIFARE_CMD_AUTH_B) + @param keyData Pointer to a byte array containing the 6 byte + key value + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::mifareclassic_AuthenticateBlock(uint8_t *uid, + uint8_t uidLen, + uint32_t blockNumber, + uint8_t keyNumber, + uint8_t *keyData) { + // uint8_t len; + uint8_t i; + + // Hang on to the key and uid data + memcpy(_key, keyData, 6); + memcpy(_uid, uid, uidLen); + _uidLen = uidLen; + +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Trying to authenticate card ")); + Adafruit_PN532::PrintHex(_uid, _uidLen); + PN532DEBUGPRINT.print(F("Using authentication KEY ")); + PN532DEBUGPRINT.print(keyNumber ? 'B' : 'A'); + PN532DEBUGPRINT.print(F(": ")); + Adafruit_PN532::PrintHex(_key, 6); +#endif + + // Prepare the authentication command // + pn532_packetbuffer[0] = + PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */ + pn532_packetbuffer[1] = 1; /* Max card numbers */ + pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; + pn532_packetbuffer[3] = + blockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */ + memcpy(pn532_packetbuffer + 4, _key, 6); + for (i = 0; i < _uidLen; i++) { + pn532_packetbuffer[10 + i] = _uid[i]; /* 4 byte card ID */ + } + + if (!sendCommandCheckAck(pn532_packetbuffer, 10 + _uidLen)) + return 0; + + // Read the response packet + readdata(pn532_packetbuffer, 12); + + // check if the response is valid and we are authenticated??? + // for an auth success it should be bytes 5-7: 0xD5 0x41 0x00 + // Mifare auth error is technically byte 7: 0x14 but anything other and 0x00 + // is not good + if (pn532_packetbuffer[7] != 0x00) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.print(F("Authentification failed: ")); + Adafruit_PN532::PrintHexChar(pn532_packetbuffer, 12); +#endif + return 0; + } + + return 1; +} + +/**************************************************************************/ +/*! + Tries to read an entire 16-byte data block at the specified block + address. + + @param blockNumber The block number to authenticate. (0..63 for + 1KB cards, and 0..255 for 4KB cards). + @param data Pointer to the byte array that will hold the + retrieved data (if any) + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::mifareclassic_ReadDataBlock(uint8_t blockNumber, + uint8_t *data) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Trying to read 16 bytes from block ")); + PN532DEBUGPRINT.println(blockNumber); +#endif + + /* Prepare the command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + pn532_packetbuffer[3] = + blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + + /* Send the command */ + if (!sendCommandCheckAck(pn532_packetbuffer, 4)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Failed to receive ACK for read command")); +#endif + return 0; + } + + /* Read the response packet */ + readdata(pn532_packetbuffer, 26); + + /* If byte 8 isn't 0x00 we probably have an error */ + if (pn532_packetbuffer[7] != 0x00) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Unexpected response")); + Adafruit_PN532::PrintHexChar(pn532_packetbuffer, 26); +#endif + return 0; + } + + /* Copy the 16 data bytes to the output buffer */ + /* Block content starts at byte 9 of a valid response */ + memcpy(data, pn532_packetbuffer + 8, 16); + +/* Display data for debug if requested */ +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Block ")); + PN532DEBUGPRINT.println(blockNumber); + Adafruit_PN532::PrintHexChar(data, 16); +#endif + + return 1; +} + +/**************************************************************************/ +/*! + Tries to write an entire 16-byte data block at the specified block + address. + + @param blockNumber The block number to authenticate. (0..63 for + 1KB cards, and 0..255 for 4KB cards). + @param data The byte array that contains the data to write. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::mifareclassic_WriteDataBlock(uint8_t blockNumber, + uint8_t *data) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Trying to write 16 bytes to block ")); + PN532DEBUGPRINT.println(blockNumber); +#endif + + /* Prepare the first command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_WRITE; /* Mifare Write command = 0xA0 */ + pn532_packetbuffer[3] = + blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + memcpy(pn532_packetbuffer + 4, data, 16); /* Data Payload */ + + /* Send the command */ + if (!sendCommandCheckAck(pn532_packetbuffer, 20)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Failed to receive ACK for write command")); +#endif + return 0; + } + delay(10); + + /* Read the response packet */ + readdata(pn532_packetbuffer, 26); + + return 1; +} + +/**************************************************************************/ +/*! + Formats a Mifare Classic card to store NDEF Records + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::mifareclassic_FormatNDEF(void) { + uint8_t sectorbuffer1[16] = {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, + 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}; + uint8_t sectorbuffer2[16] = {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, + 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}; + uint8_t sectorbuffer3[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, + 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + // Note 0xA0 0xA1 0xA2 0xA3 0xA4 0xA5 must be used for key A + // for the MAD sector in NDEF records (sector 0) + + // Write block 1 and 2 to the card + if (!(mifareclassic_WriteDataBlock(1, sectorbuffer1))) + return 0; + if (!(mifareclassic_WriteDataBlock(2, sectorbuffer2))) + return 0; + // Write key A and access rights card + if (!(mifareclassic_WriteDataBlock(3, sectorbuffer3))) + return 0; + + // Seems that everything was OK (?!) + return 1; +} + +/**************************************************************************/ +/*! + Writes an NDEF URI Record to the specified sector (1..15) + + Note that this function assumes that the Mifare Classic card is + already formatted to work as an "NFC Forum Tag" and uses a MAD1 + file system. You can use the NXP TagWriter app on Android to + properly format cards for this. + + @param sectorNumber The sector that the URI record should be written + to (can be 1..15 for a 1K card) + @param uriIdentifier The uri identifier code (0 = none, 0x01 = + "http://www.", etc.) + @param url The uri text to write (max 38 characters). + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::mifareclassic_WriteNDEFURI(uint8_t sectorNumber, + uint8_t uriIdentifier, + const char *url) { + // Figure out how long the string is + uint8_t len = strlen(url); + + // Make sure we're within a 1K limit for the sector number + if ((sectorNumber < 1) || (sectorNumber > 15)) + return 0; + + // Make sure the URI payload is between 1 and 38 chars + if ((len < 1) || (len > 38)) + return 0; + + // Note 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 must be used for key A + // in NDEF records + + // Setup the sector buffer (w/pre-formatted TLV wrapper and NDEF message) + uint8_t sectorbuffer1[16] = {0x00, + 0x00, + 0x03, + (uint8_t)(len + 5), + 0xD1, + 0x01, + (uint8_t)(len + 1), + 0x55, + uriIdentifier, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00}; + uint8_t sectorbuffer2[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t sectorbuffer3[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t sectorbuffer4[16] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, + 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + if (len <= 6) { + // Unlikely we'll get a url this short, but why not ... + memcpy(sectorbuffer1 + 9, url, len); + sectorbuffer1[len + 9] = 0xFE; + } else if (len == 7) { + // 0xFE needs to be wrapped around to next block + memcpy(sectorbuffer1 + 9, url, len); + sectorbuffer2[0] = 0xFE; + } else if ((len > 7) && (len <= 22)) { + // Url fits in two blocks + memcpy(sectorbuffer1 + 9, url, 7); + memcpy(sectorbuffer2, url + 7, len - 7); + sectorbuffer2[len - 7] = 0xFE; + } else if (len == 23) { + // 0xFE needs to be wrapped around to final block + memcpy(sectorbuffer1 + 9, url, 7); + memcpy(sectorbuffer2, url + 7, len - 7); + sectorbuffer3[0] = 0xFE; + } else { + // Url fits in three blocks + memcpy(sectorbuffer1 + 9, url, 7); + memcpy(sectorbuffer2, url + 7, 16); + memcpy(sectorbuffer3, url + 23, len - 24); + sectorbuffer3[len - 22] = 0xFE; + } + + // Now write all three blocks back to the card + if (!(mifareclassic_WriteDataBlock(sectorNumber * 4, sectorbuffer1))) + return 0; + if (!(mifareclassic_WriteDataBlock((sectorNumber * 4) + 1, sectorbuffer2))) + return 0; + if (!(mifareclassic_WriteDataBlock((sectorNumber * 4) + 2, sectorbuffer3))) + return 0; + if (!(mifareclassic_WriteDataBlock((sectorNumber * 4) + 3, sectorbuffer4))) + return 0; + + // Seems that everything was OK (?!) + return 1; +} + +/***** Mifare Ultralight Functions ******/ + +/**************************************************************************/ +/*! + @brief Tries to read an entire 4-byte page at the specified address. + + @param page The page number (0..63 in most cases) + @param buffer Pointer to the byte array that will hold the + retrieved data (if any) + @return 1 on success, 0 on error. +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::mifareultralight_ReadPage(uint8_t page, + uint8_t *buffer) { + if (page >= 64) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Page value out of range")); +#endif + return 0; + } + +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Reading page ")); + PN532DEBUGPRINT.println(page); +#endif + + /* Prepare the command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + pn532_packetbuffer[3] = page; /* Page Number (0..63 in most cases) */ + + /* Send the command */ + if (!sendCommandCheckAck(pn532_packetbuffer, 4)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Failed to receive ACK for write command")); +#endif + return 0; + } + + /* Read the response packet */ + readdata(pn532_packetbuffer, 26); +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Received: ")); + Adafruit_PN532::PrintHexChar(pn532_packetbuffer, 26); +#endif + + /* If byte 8 isn't 0x00 we probably have an error */ + if (pn532_packetbuffer[7] == 0x00) { + /* Copy the 4 data bytes to the output buffer */ + /* Block content starts at byte 9 of a valid response */ + /* Note that the command actually reads 16 byte or 4 */ + /* pages at a time ... we simply discard the last 12 */ + /* bytes */ + memcpy(buffer, pn532_packetbuffer + 8, 4); + } else { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Unexpected response reading block: ")); + Adafruit_PN532::PrintHexChar(pn532_packetbuffer, 26); +#endif + return 0; + } + +/* Display data for debug if requested */ +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Page ")); + PN532DEBUGPRINT.print(page); + PN532DEBUGPRINT.println(F(":")); + Adafruit_PN532::PrintHexChar(buffer, 4); +#endif + + // Return OK signal + return 1; +} + +/**************************************************************************/ +/*! + Tries to write an entire 4-byte page at the specified block + address. + + @param page The page number to write. (0..63 for most cases) + @param data The byte array that contains the data to write. + Should be exactly 4 bytes long. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::mifareultralight_WritePage(uint8_t page, + uint8_t *data) { + + if (page >= 64) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Page value out of range")); +#endif + // Return Failed Signal + return 0; + } + +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Trying to write 4 byte page")); + PN532DEBUGPRINT.println(page); +#endif + + /* Prepare the first command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = + MIFARE_ULTRALIGHT_CMD_WRITE; /* Mifare Ultralight Write command = 0xA2 */ + pn532_packetbuffer[3] = page; /* Page Number (0..63 for most cases) */ + memcpy(pn532_packetbuffer + 4, data, 4); /* Data Payload */ + + /* Send the command */ + if (!sendCommandCheckAck(pn532_packetbuffer, 8)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Failed to receive ACK for write command")); +#endif + + // Return Failed Signal + return 0; + } + delay(10); + + /* Read the response packet */ + readdata(pn532_packetbuffer, 26); + + // Return OK Signal + return 1; +} + +/***** NTAG2xx Functions ******/ + +/**************************************************************************/ +/*! + @brief Tries to read an entire 4-byte page at the specified address. + + @param page The page number (0..63 in most cases) + @param buffer Pointer to the byte array that will hold the + retrieved data (if any) + @return 1 on success, 0 on error. +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::ntag2xx_ReadPage(uint8_t page, uint8_t *buffer) { + // TAG Type PAGES USER START USER STOP + // -------- ----- ---------- --------- + // NTAG 203 42 4 39 + // NTAG 213 45 4 39 + // NTAG 215 135 4 129 + // NTAG 216 231 4 225 + + if (page >= 231) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Page value out of range")); +#endif + return 0; + } + +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Reading page ")); + PN532DEBUGPRINT.println(page); +#endif + + /* Prepare the command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + pn532_packetbuffer[3] = page; /* Page Number (0..63 in most cases) */ + + /* Send the command */ + if (!sendCommandCheckAck(pn532_packetbuffer, 4)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Failed to receive ACK for write command")); +#endif + return 0; + } + + /* Read the response packet */ + readdata(pn532_packetbuffer, 26); +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Received: ")); + Adafruit_PN532::PrintHexChar(pn532_packetbuffer, 26); +#endif + + /* If byte 8 isn't 0x00 we probably have an error */ + if (pn532_packetbuffer[7] == 0x00) { + /* Copy the 4 data bytes to the output buffer */ + /* Block content starts at byte 9 of a valid response */ + /* Note that the command actually reads 16 byte or 4 */ + /* pages at a time ... we simply discard the last 12 */ + /* bytes */ + memcpy(buffer, pn532_packetbuffer + 8, 4); + } else { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Unexpected response reading block: ")); + Adafruit_PN532::PrintHexChar(pn532_packetbuffer, 26); +#endif + return 0; + } + +/* Display data for debug if requested */ +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Page ")); + PN532DEBUGPRINT.print(page); + PN532DEBUGPRINT.println(F(":")); + Adafruit_PN532::PrintHexChar(buffer, 4); +#endif + + // Return OK signal + return 1; +} + +/**************************************************************************/ +/*! + Tries to write an entire 4-byte page at the specified block + address. + + @param page The page number to write. (0..63 for most cases) + @param data The byte array that contains the data to write. + Should be exactly 4 bytes long. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::ntag2xx_WritePage(uint8_t page, uint8_t *data) { + // TAG Type PAGES USER START USER STOP + // -------- ----- ---------- --------- + // NTAG 203 42 4 39 + // NTAG 213 45 4 39 + // NTAG 215 135 4 129 + // NTAG 216 231 4 225 + + if ((page < 4) || (page > 225)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Page value out of range")); +#endif + // Return Failed Signal + return 0; + } + +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.print(F("Trying to write 4 byte page")); + PN532DEBUGPRINT.println(page); +#endif + + /* Prepare the first command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = + MIFARE_ULTRALIGHT_CMD_WRITE; /* Mifare Ultralight Write command = 0xA2 */ + pn532_packetbuffer[3] = page; /* Page Number (0..63 for most cases) */ + memcpy(pn532_packetbuffer + 4, data, 4); /* Data Payload */ + + /* Send the command */ + if (!sendCommandCheckAck(pn532_packetbuffer, 8)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Failed to receive ACK for write command")); +#endif + + // Return Failed Signal + return 0; + } + delay(10); + + /* Read the response packet */ + readdata(pn532_packetbuffer, 26); + + // Return OK Signal + return 1; +} + +/**************************************************************************/ +/*! + Writes an NDEF URI Record starting at the specified page (4..nn) + + Note that this function assumes that the NTAG2xx card is + already formatted to work as an "NFC Forum Tag". + + @param uriIdentifier The uri identifier code (0 = none, 0x01 = + "http://www.", etc.) + @param url The uri text to write (null-terminated string). + @param dataLen The size of the data area for overflow checks. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::ntag2xx_WriteNDEFURI(uint8_t uriIdentifier, char *url, + uint8_t dataLen) { + uint8_t pageBuffer[4] = {0, 0, 0, 0}; + + // Remove NDEF record overhead from the URI data (pageHeader below) + uint8_t wrapperSize = 12; + + // Figure out how long the string is + uint8_t len = strlen(url); + + // Make sure the URI payload will fit in dataLen (include 0xFE trailer) + if ((len < 1) || (len + 1 > (dataLen - wrapperSize))) + return 0; + + // Setup the record header + // See NFCForum-TS-Type-2-Tag_1.1.pdf for details + uint8_t pageHeader[12] = { + /* NDEF Lock Control TLV (must be first and always present) */ + 0x01, /* Tag Field (0x01 = Lock Control TLV) */ + 0x03, /* Payload Length (always 3) */ + 0xA0, /* The position inside the tag of the lock bytes (upper 4 = page + address, lower 4 = byte offset) */ + 0x10, /* Size in bits of the lock area */ + 0x44, /* Size in bytes of a page and the number of bytes each lock bit can + lock (4 bit + 4 bits) */ + /* NDEF Message TLV - URI Record */ + 0x03, /* Tag Field (0x03 = NDEF Message) */ + (uint8_t)(len + 5), /* Payload Length (not including 0xFE trailer) */ + 0xD1, /* NDEF Record Header (TNF=0x1:Well known record + SR + ME + MB) */ + 0x01, /* Type Length for the record type indicator */ + (uint8_t)(len + 1), /* Payload len */ + 0x55, /* Record Type Indicator (0x55 or 'U' = URI Record) */ + uriIdentifier /* URI Prefix (ex. 0x01 = "http://www.") */ + }; + + // Write 12 byte header (three pages of data starting at page 4) + memcpy(pageBuffer, pageHeader, 4); + if (!(ntag2xx_WritePage(4, pageBuffer))) + return 0; + memcpy(pageBuffer, pageHeader + 4, 4); + if (!(ntag2xx_WritePage(5, pageBuffer))) + return 0; + memcpy(pageBuffer, pageHeader + 8, 4); + if (!(ntag2xx_WritePage(6, pageBuffer))) + return 0; + + // Write URI (starting at page 7) + uint8_t currentPage = 7; + char *urlcopy = url; + while (len) { + if (len < 4) { + memset(pageBuffer, 0, 4); + memcpy(pageBuffer, urlcopy, len); + pageBuffer[len] = 0xFE; // NDEF record footer + if (!(ntag2xx_WritePage(currentPage, pageBuffer))) + return 0; + // DONE! + return 1; + } else if (len == 4) { + memcpy(pageBuffer, urlcopy, len); + if (!(ntag2xx_WritePage(currentPage, pageBuffer))) + return 0; + memset(pageBuffer, 0, 4); + pageBuffer[0] = 0xFE; // NDEF record footer + currentPage++; + if (!(ntag2xx_WritePage(currentPage, pageBuffer))) + return 0; + // DONE! + return 1; + } else { + // More than one page of data left + memcpy(pageBuffer, urlcopy, 4); + if (!(ntag2xx_WritePage(currentPage, pageBuffer))) + return 0; + currentPage++; + urlcopy += 4; + len -= 4; + } + } + + // Seems that everything was OK (?!) + return 1; +} + +/************** high level communication functions (handles both I2C and SPI) */ + +/**************************************************************************/ +/*! + @brief Tries to read the SPI or I2C ACK signal +*/ +/**************************************************************************/ +bool Adafruit_PN532::readack() { + uint8_t ackbuff[6]; + + if (spi_dev) { + uint8_t cmd = PN532_SPI_DATAREAD; + spi_dev->write_then_read(&cmd, 1, ackbuff, 6); + } else if (i2c_dev || ser_dev) { + readdata(ackbuff, 6); + } + + return (0 == memcmp((char *)ackbuff, (char *)pn532ack, 6)); +} + +/**************************************************************************/ +/*! + @brief Return true if the PN532 is ready with a response. +*/ +/**************************************************************************/ +bool Adafruit_PN532::isready() { + if (spi_dev) { + // SPI ready check via Status Request + uint8_t cmd = PN532_SPI_STATREAD; + uint8_t reply; + spi_dev->write_then_read(&cmd, 1, &reply, 1); + return reply == PN532_SPI_READY; + } else if (i2c_dev) { + // I2C ready check via reading RDY byte + uint8_t rdy[1]; + i2c_dev->read(rdy, 1); + return rdy[0] == PN532_I2C_READY; + } else if (ser_dev) { + // Serial ready check based on non-zero read buffer + return (ser_dev->available() != 0); + } else if (_irq != -1) { + uint8_t x = digitalRead(_irq); + return x == 0; + } + return false; +} + +/**************************************************************************/ +/*! + @brief Waits until the PN532 is ready. + + @param timeout Timeout before giving up +*/ +/**************************************************************************/ +bool Adafruit_PN532::waitready(uint16_t timeout) { + uint16_t timer = 0; + while (!isready()) { + if (timeout != 0) { + timer += 10; + if (timer > timeout) { +#ifdef PN532DEBUG + PN532DEBUGPRINT.println("TIMEOUT!"); +#endif + return false; + } + } + delay(10); + } + return true; +} + +/**************************************************************************/ +/*! + @brief Reads n bytes of data from the PN532 via SPI or I2C. + + @param buff Pointer to the buffer where data will be written + @param n Number of bytes to be read +*/ +/**************************************************************************/ +void Adafruit_PN532::readdata(uint8_t *buff, uint8_t n) { + if (spi_dev) { + // SPI read + uint8_t cmd = PN532_SPI_DATAREAD; + spi_dev->write_then_read(&cmd, 1, buff, n); + } else if (i2c_dev) { + // I2C read + uint8_t rbuff[n + 1]; // +1 for leading RDY byte + i2c_dev->read(rbuff, n + 1); + for (uint8_t i = 0; i < n; i++) { + buff[i] = rbuff[i + 1]; + } + } else if (ser_dev) { + // Serial read + ser_dev->readBytes(buff, n); + } +#ifdef PN532DEBUG + PN532DEBUGPRINT.print(F("Reading: ")); + for (uint8_t i = 0; i < n; i++) { + PN532DEBUGPRINT.print(F(" 0x")); + PN532DEBUGPRINT.print(buff[i], HEX); + } + PN532DEBUGPRINT.println(); +#endif +} + +/**************************************************************************/ +/*! + @brief set the PN532 as iso14443a Target behaving as a SmartCard + @return true on success, false otherwise. + @note Author: Salvador Mendoza (salmg.net) new functions: + -AsTarget + -getDataTarget + -setDataTarget +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::AsTarget() { + pn532_packetbuffer[0] = 0x8C; + uint8_t target[] = { + 0x8C, // INIT AS TARGET + 0x00, // MODE -> BITFIELD + 0x08, 0x00, // SENS_RES - MIFARE PARAMS + 0xdc, 0x44, 0x20, // NFCID1T + 0x60, // SEL_RES + 0x01, 0xfe, // NFCID2T MUST START WITH 01fe - FELICA PARAMS - POL_RES + 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xc0, + 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, // PAD + 0xff, 0xff, // SYSTEM CODE + 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, 0x11, 0x01, 0x00, // NFCID3t MAX 47 BYTES ATR_RES + 0x0d, 0x52, 0x46, 0x49, 0x44, 0x49, 0x4f, + 0x74, 0x20, 0x50, 0x4e, 0x35, 0x33, 0x32 // HISTORICAL BYTES + }; + if (!sendCommandCheckAck(target, sizeof(target))) + return false; + + // read data packet + readdata(pn532_packetbuffer, 8); + + int offset = 6; + return (pn532_packetbuffer[offset] == 0x15); +} +/**************************************************************************/ +/*! + @brief Retrieve response from the emulation mode + + @param cmd = data + @param cmdlen = data length + @return true on success, false otherwise. +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::getDataTarget(uint8_t *cmd, uint8_t *cmdlen) { + uint8_t length; + pn532_packetbuffer[0] = 0x86; + if (!sendCommandCheckAck(pn532_packetbuffer, 1, 1000)) { + PN532DEBUGPRINT.println(F("Error en ack")); + return false; + } + + // read data packet + readdata(pn532_packetbuffer, 64); + length = pn532_packetbuffer[3] - 3; + + // if (length > *responseLength) {// Bug, should avoid it in the reading + // target data + // length = *responseLength; // silent truncation... + //} + + for (int i = 0; i < length; ++i) { + cmd[i] = pn532_packetbuffer[8 + i]; + } + *cmdlen = length; + return true; +} + +/**************************************************************************/ +/*! + @brief Set data in PN532 in the emulation mode + + @param cmd = data + @param cmdlen = data length + @return true on success, false otherwise. +*/ +/**************************************************************************/ +uint8_t Adafruit_PN532::setDataTarget(uint8_t *cmd, uint8_t cmdlen) { + uint8_t length; + // cmd1[0] = 0x8E; Must! + + if (!sendCommandCheckAck(cmd, cmdlen)) + return false; + + // read data packet + readdata(pn532_packetbuffer, 8); + length = pn532_packetbuffer[3] - 3; + for (int i = 0; i < length; ++i) { + cmd[i] = pn532_packetbuffer[8 + i]; + } + // cmdl = 0 + cmdlen = length; + + int offset = 6; + return (pn532_packetbuffer[offset] == 0x15); +} + +/**************************************************************************/ +/*! + @brief Writes a command to the PN532, automatically inserting the + preamble and required frame details (checksum, len, etc.) + + @param cmd Pointer to the command buffer + @param cmdlen Command length in bytes +*/ +/**************************************************************************/ +void Adafruit_PN532::writecommand(uint8_t *cmd, uint8_t cmdlen) { + if (spi_dev) { + // SPI command write. + uint8_t checksum; + uint8_t packet[9 + cmdlen]; + uint8_t *p = packet; + cmdlen++; + + p[0] = PN532_SPI_DATAWRITE; + p++; + + p[0] = PN532_PREAMBLE; + p++; + p[0] = PN532_STARTCODE1; + p++; + p[0] = PN532_STARTCODE2; + p++; + checksum = PN532_PREAMBLE + PN532_STARTCODE1 + PN532_STARTCODE2; + + p[0] = cmdlen; + p++; + p[0] = ~cmdlen + 1; + p++; + + p[0] = PN532_HOSTTOPN532; + p++; + checksum += PN532_HOSTTOPN532; + + for (uint8_t i = 0; i < cmdlen - 1; i++) { + p[0] = cmd[i]; + p++; + checksum += cmd[i]; + } + + p[0] = ~checksum; + p++; + p[0] = PN532_POSTAMBLE; + p++; + +#ifdef PN532DEBUG + Serial.print("Sending : "); + for (int i = 1; i < 8 + cmdlen; i++) { + Serial.print("0x"); + Serial.print(packet[i], HEX); + Serial.print(", "); + } + Serial.println(); +#endif + + spi_dev->write(packet, 8 + cmdlen); + } else if (i2c_dev || ser_dev) { + // I2C or Serial command write. + uint8_t packet[8 + cmdlen]; + uint8_t LEN = cmdlen + 1; + + packet[0] = PN532_PREAMBLE; + packet[1] = PN532_STARTCODE1; + packet[2] = PN532_STARTCODE2; + packet[3] = LEN; + packet[4] = ~LEN + 1; + packet[5] = PN532_HOSTTOPN532; + uint8_t sum = 0; + for (uint8_t i = 0; i < cmdlen; i++) { + packet[6 + i] = cmd[i]; + sum += cmd[i]; + } + packet[6 + cmdlen] = ~(PN532_HOSTTOPN532 + sum) + 1; + packet[7 + cmdlen] = PN532_POSTAMBLE; + +#ifdef PN532DEBUG + Serial.print("Sending : "); + for (int i = 1; i < 8 + cmdlen; i++) { + Serial.print("0x"); + Serial.print(packet[i], HEX); + Serial.print(", "); + } + Serial.println(); +#endif + + if (i2c_dev) { + i2c_dev->write(packet, 8 + cmdlen); + } else { + ser_dev->write(packet, 8 + cmdlen); + } + } +} diff --git a/src/Adafruit_PN532.h b/src/Adafruit_PN532.h new file mode 100644 index 0000000..744e7cf --- /dev/null +++ b/src/Adafruit_PN532.h @@ -0,0 +1,221 @@ +/**************************************************************************/ +/*! + @file Adafruit_PN532.h + + v2.0 - Refactored to add I2C support from Adafruit_NFCShield_I2C library. + + v1.1 - Added full command list + - Added 'verbose' mode flag to constructor to toggle debug output + - Changed readPassiveTargetID() to return variable length values +*/ +/**************************************************************************/ + +#ifndef ADAFRUIT_PN532_H +#define ADAFRUIT_PN532_H + +#include "Arduino.h" + +#include +#include + +#define PN532_PREAMBLE (0x00) ///< Command sequence start, byte 1/3 +#define PN532_STARTCODE1 (0x00) ///< Command sequence start, byte 2/3 +#define PN532_STARTCODE2 (0xFF) ///< Command sequence start, byte 3/3 +#define PN532_POSTAMBLE (0x00) ///< EOD + +#define PN532_HOSTTOPN532 (0xD4) ///< Host-to-PN532 +#define PN532_PN532TOHOST (0xD5) ///< PN532-to-host + +// PN532 Commands +#define PN532_COMMAND_DIAGNOSE (0x00) ///< Diagnose +#define PN532_COMMAND_GETFIRMWAREVERSION (0x02) ///< Get firmware version +#define PN532_COMMAND_GETGENERALSTATUS (0x04) ///< Get general status +#define PN532_COMMAND_READREGISTER (0x06) ///< Read register +#define PN532_COMMAND_WRITEREGISTER (0x08) ///< Write register +#define PN532_COMMAND_READGPIO (0x0C) ///< Read GPIO +#define PN532_COMMAND_WRITEGPIO (0x0E) ///< Write GPIO +#define PN532_COMMAND_SETSERIALBAUDRATE (0x10) ///< Set serial baud rate +#define PN532_COMMAND_SETPARAMETERS (0x12) ///< Set parameters +#define PN532_COMMAND_SAMCONFIGURATION (0x14) ///< SAM configuration +#define PN532_COMMAND_POWERDOWN (0x16) ///< Power down +#define PN532_COMMAND_RFCONFIGURATION (0x32) ///< RF config +#define PN532_COMMAND_RFREGULATIONTEST (0x58) ///< RF regulation test +#define PN532_COMMAND_INJUMPFORDEP (0x56) ///< Jump for DEP +#define PN532_COMMAND_INJUMPFORPSL (0x46) ///< Jump for PSL +#define PN532_COMMAND_INLISTPASSIVETARGET (0x4A) ///< List passive target +#define PN532_COMMAND_INATR (0x50) ///< ATR +#define PN532_COMMAND_INPSL (0x4E) ///< PSL +#define PN532_COMMAND_INDATAEXCHANGE (0x40) ///< Data exchange +#define PN532_COMMAND_INCOMMUNICATETHRU (0x42) ///< Communicate through +#define PN532_COMMAND_INDESELECT (0x44) ///< Deselect +#define PN532_COMMAND_INRELEASE (0x52) ///< Release +#define PN532_COMMAND_INSELECT (0x54) ///< Select +#define PN532_COMMAND_INAUTOPOLL (0x60) ///< Auto poll +#define PN532_COMMAND_TGINITASTARGET (0x8C) ///< Init as target +#define PN532_COMMAND_TGSETGENERALBYTES (0x92) ///< Set general bytes +#define PN532_COMMAND_TGGETDATA (0x86) ///< Get data +#define PN532_COMMAND_TGSETDATA (0x8E) ///< Set data +#define PN532_COMMAND_TGSETMETADATA (0x94) ///< Set metadata +#define PN532_COMMAND_TGGETINITIATORCOMMAND (0x88) ///< Get initiator command +#define PN532_COMMAND_TGRESPONSETOINITIATOR (0x90) ///< Response to initiator +#define PN532_COMMAND_TGGETTARGETSTATUS (0x8A) ///< Get target status + +#define PN532_RESPONSE_INDATAEXCHANGE (0x41) ///< Data exchange +#define PN532_RESPONSE_INLISTPASSIVETARGET (0x4B) ///< List passive target + +#define PN532_WAKEUP (0x55) ///< Wake + +#define PN532_SPI_STATREAD (0x02) ///< Stat read +#define PN532_SPI_DATAWRITE (0x01) ///< Data write +#define PN532_SPI_DATAREAD (0x03) ///< Data read +#define PN532_SPI_READY (0x01) ///< Ready + +#define PN532_I2C_ADDRESS (0x48 >> 1) ///< Default I2C address +#define PN532_I2C_READBIT (0x01) ///< Read bit +#define PN532_I2C_BUSY (0x00) ///< Busy +#define PN532_I2C_READY (0x01) ///< Ready +#define PN532_I2C_READYTIMEOUT (20) ///< Ready timeout + +#define PN532_MIFARE_ISO14443A (0x00) ///< MiFare + +// Mifare Commands +#define MIFARE_CMD_AUTH_A (0x60) ///< Auth A +#define MIFARE_CMD_AUTH_B (0x61) ///< Auth B +#define MIFARE_CMD_READ (0x30) ///< Read +#define MIFARE_CMD_WRITE (0xA0) ///< Write +#define MIFARE_CMD_TRANSFER (0xB0) ///< Transfer +#define MIFARE_CMD_DECREMENT (0xC0) ///< Decrement +#define MIFARE_CMD_INCREMENT (0xC1) ///< Increment +#define MIFARE_CMD_STORE (0xC2) ///< Store +#define MIFARE_ULTRALIGHT_CMD_WRITE (0xA2) ///< Write (MiFare Ultralight) + +// Prefixes for NDEF Records (to identify record type) +#define NDEF_URIPREFIX_NONE (0x00) ///< No prefix +#define NDEF_URIPREFIX_HTTP_WWWDOT (0x01) ///< HTTP www. prefix +#define NDEF_URIPREFIX_HTTPS_WWWDOT (0x02) ///< HTTPS www. prefix +#define NDEF_URIPREFIX_HTTP (0x03) ///< HTTP prefix +#define NDEF_URIPREFIX_HTTPS (0x04) ///< HTTPS prefix +#define NDEF_URIPREFIX_TEL (0x05) ///< Tel prefix +#define NDEF_URIPREFIX_MAILTO (0x06) ///< Mailto prefix +#define NDEF_URIPREFIX_FTP_ANONAT (0x07) ///< FTP +#define NDEF_URIPREFIX_FTP_FTPDOT (0x08) ///< FTP dot +#define NDEF_URIPREFIX_FTPS (0x09) ///< FTPS +#define NDEF_URIPREFIX_SFTP (0x0A) ///< SFTP +#define NDEF_URIPREFIX_SMB (0x0B) ///< SMB +#define NDEF_URIPREFIX_NFS (0x0C) ///< NFS +#define NDEF_URIPREFIX_FTP (0x0D) ///< FTP +#define NDEF_URIPREFIX_DAV (0x0E) ///< DAV +#define NDEF_URIPREFIX_NEWS (0x0F) ///< NEWS +#define NDEF_URIPREFIX_TELNET (0x10) ///< Telnet prefix +#define NDEF_URIPREFIX_IMAP (0x11) ///< IMAP prefix +#define NDEF_URIPREFIX_RTSP (0x12) ///< RTSP +#define NDEF_URIPREFIX_URN (0x13) ///< URN +#define NDEF_URIPREFIX_POP (0x14) ///< POP +#define NDEF_URIPREFIX_SIP (0x15) ///< SIP +#define NDEF_URIPREFIX_SIPS (0x16) ///< SIPS +#define NDEF_URIPREFIX_TFTP (0x17) ///< TFPT +#define NDEF_URIPREFIX_BTSPP (0x18) ///< BTSPP +#define NDEF_URIPREFIX_BTL2CAP (0x19) ///< BTL2CAP +#define NDEF_URIPREFIX_BTGOEP (0x1A) ///< BTGOEP +#define NDEF_URIPREFIX_TCPOBEX (0x1B) ///< TCPOBEX +#define NDEF_URIPREFIX_IRDAOBEX (0x1C) ///< IRDAOBEX +#define NDEF_URIPREFIX_FILE (0x1D) ///< File +#define NDEF_URIPREFIX_URN_EPC_ID (0x1E) ///< URN EPC ID +#define NDEF_URIPREFIX_URN_EPC_TAG (0x1F) ///< URN EPC tag +#define NDEF_URIPREFIX_URN_EPC_PAT (0x20) ///< URN EPC pat +#define NDEF_URIPREFIX_URN_EPC_RAW (0x21) ///< URN EPC raw +#define NDEF_URIPREFIX_URN_EPC (0x22) ///< URN EPC +#define NDEF_URIPREFIX_URN_NFC (0x23) ///< URN NFC + +#define PN532_GPIO_VALIDATIONBIT (0x80) ///< GPIO validation bit +#define PN532_GPIO_P30 (0) ///< GPIO 30 +#define PN532_GPIO_P31 (1) ///< GPIO 31 +#define PN532_GPIO_P32 (2) ///< GPIO 32 +#define PN532_GPIO_P33 (3) ///< GPIO 33 +#define PN532_GPIO_P34 (4) ///< GPIO 34 +#define PN532_GPIO_P35 (5) ///< GPIO 35 + +/** + * @brief Class for working with Adafruit PN532 NFC/RFID breakout boards. + */ +class Adafruit_PN532 { +public: + Adafruit_PN532(uint8_t clk, uint8_t miso, uint8_t mosi, + uint8_t ss); // Software SPI + Adafruit_PN532(uint8_t ss, SPIClass *theSPI = &SPI); // Hardware SPI + Adafruit_PN532(uint8_t irq, uint8_t reset, + TwoWire *theWire = &Wire); // Hardware I2C + Adafruit_PN532(uint8_t reset, HardwareSerial *theSer); // Hardware UART + bool begin(void); + + void reset(void); + void wakeup(void); + + // Generic PN532 functions + bool SAMConfig(void); + uint32_t getFirmwareVersion(void); + bool sendCommandCheckAck(uint8_t *cmd, uint8_t cmdlen, + uint16_t timeout = 100); + bool writeGPIO(uint8_t pinstate); + uint8_t readGPIO(void); + bool setPassiveActivationRetries(uint8_t maxRetries); + + // ISO14443A functions + bool readPassiveTargetID( + uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, + uint16_t timeout = 0); // timeout 0 means no timeout - will block forever. + bool startPassiveTargetIDDetection(uint8_t cardbaudrate); + bool readDetectedPassiveTargetID(uint8_t *uid, uint8_t *uidLength); + bool inDataExchange(uint8_t *send, uint8_t sendLength, uint8_t *response, + uint8_t *responseLength); + bool inListPassiveTarget(); + uint8_t AsTarget(); + uint8_t getDataTarget(uint8_t *cmd, uint8_t *cmdlen); + uint8_t setDataTarget(uint8_t *cmd, uint8_t cmdlen); + + // Mifare Classic functions + bool mifareclassic_IsFirstBlock(uint32_t uiBlock); + bool mifareclassic_IsTrailerBlock(uint32_t uiBlock); + uint8_t mifareclassic_AuthenticateBlock(uint8_t *uid, uint8_t uidLen, + uint32_t blockNumber, + uint8_t keyNumber, uint8_t *keyData); + uint8_t mifareclassic_ReadDataBlock(uint8_t blockNumber, uint8_t *data); + uint8_t mifareclassic_WriteDataBlock(uint8_t blockNumber, uint8_t *data); + uint8_t mifareclassic_FormatNDEF(void); + uint8_t mifareclassic_WriteNDEFURI(uint8_t sectorNumber, + uint8_t uriIdentifier, const char *url); + + // Mifare Ultralight functions + uint8_t mifareultralight_ReadPage(uint8_t page, uint8_t *buffer); + uint8_t mifareultralight_WritePage(uint8_t page, uint8_t *data); + + // NTAG2xx functions + uint8_t ntag2xx_ReadPage(uint8_t page, uint8_t *buffer); + uint8_t ntag2xx_WritePage(uint8_t page, uint8_t *data); + uint8_t ntag2xx_WriteNDEFURI(uint8_t uriIdentifier, char *url, + uint8_t dataLen); + + // Help functions to display formatted text + static void PrintHex(const byte *data, const uint32_t numBytes); + static void PrintHexChar(const byte *pbtData, const uint32_t numBytes); + +private: + int8_t _irq = -1, _reset = -1, _cs = -1; + int8_t _uid[7]; // ISO14443A uid + int8_t _uidLen; // uid len + int8_t _key[6]; // Mifare Classic key + int8_t _inListedTag; // Tg number of inlisted tag. + + // Low level communication functions that handle both SPI and I2C. + void readdata(uint8_t *buff, uint8_t n); + void writecommand(uint8_t *cmd, uint8_t cmdlen); + bool isready(); + bool waitready(uint16_t timeout); + bool readack(); + + Adafruit_SPIDevice *spi_dev = NULL; + Adafruit_I2CDevice *i2c_dev = NULL; + HardwareSerial *ser_dev = NULL; +}; + +#endif diff --git a/src/audioState.h b/src/audioState.h new file mode 100644 index 0000000..bc75ac7 --- /dev/null +++ b/src/audioState.h @@ -0,0 +1,73 @@ +#pragma once +#include +#include +#include + +void printMp3Detail(LiquidCrystal* lcd, uint8_t type, int value) +{ + lcd->clear(); + switch (type) + { + case TimeOut: + lcd->print("Time Out!"); + break; + case WrongStack: + lcd->print("Stack Wrong!"); + break; + case DFPlayerCardInserted: + lcd->print("Card Inserted!"); + break; + case DFPlayerCardRemoved: + lcd->print("Card Removed!"); + break; + case DFPlayerCardOnline: + lcd->print("Card Online!"); + break; + case DFPlayerUSBInserted: + lcd->print("USB Inserted!"); + break; + case DFPlayerUSBRemoved: + lcd->print("USB Removed!"); + break; + case DFPlayerPlayFinished: + lcd->print("Num:"); + lcd->print(value); + lcd->print(" Finished!"); + break; + case DFPlayerError: + lcd->print("DFPlayerError:"); + switch (value) + { + case Busy: + lcd->print("Card not found"); + break; + case Sleeping: + lcd->print("Sleeping"); + break; + case SerialWrongStack: + lcd->print("Wrong Stack"); + break; + case CheckSumNotMatch: + lcd->print("Checksum Error"); + break; + case FileIndexOut: + lcd->print("File Index OOB"); + break; + case FileMismatch: + lcd->print("File Mismatch"); + break; + case Advertise: + lcd->print("In Advertise"); + break; + default: + lcd->print("Unknown Error"); + break; + } + break; + default: + lcd->print("Unknown Type"); + break; + } + delay(2000); // Display the message for 2 seconds +} + diff --git a/src/context.cpp b/src/context.cpp new file mode 100644 index 0000000..b9a6081 --- /dev/null +++ b/src/context.cpp @@ -0,0 +1,17 @@ +#include "Context.h" + +Context::Context(Station* stations, const int stationCount, const unsigned long delta, DFRobotDFPlayerMini* dfPlayer) { + this->stations = stations; + this->stationCount = stationCount; + this->delta = delta; + this->dfPlayer = dfPlayer; +} +int Context::getStationIndex(const Station* station) const +{ + for (int i = 0; i < stationCount; ++i) { + if (&stations[i] == station) { + return i; + } + } + return -1; +} diff --git a/src/context.h b/src/context.h new file mode 100644 index 0000000..f1ee594 --- /dev/null +++ b/src/context.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include "station.h" + +class Context { +public: + Context(Station* stations, int stationCount, unsigned long delta, DFRobotDFPlayerMini* dfPlayer); + + Station* stations; + int stationCount; + unsigned long delta; + DFRobotDFPlayerMini* dfPlayer; + + int getStationIndex(const Station* station) const; +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..04ce725 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include + +#include "station.h" +#include "statemachine.h" +#include "state.h" +#include "states.h" +#include "timer.h" +#include "audioState.h" + +#define PN532_CS 10 + +// Using this library to set a timeout on readPassiveTagID +// The library in use is slightly modified to fix this issue: https://github.com/adafruit/Adafruit-PN532/issues/117 +Adafruit_PN532 nfc(PN532_CS); + +constexpr int rs = 9, en = 8, d4 = 5, d5 = 4, d6 = 3, d7 = 2; +LiquidCrystal lcd(rs, en, d4, d5, d6, d7); + +SoftwareSerial softSerial(6, 7); // RX, TX +DFRobotDFPlayerMini dfPlayer; + +Station stations[5] = { + Station(1680767519, "Yellow"), Station(3346823711, "Green"), Station(3569318175, "Pink"), + Station(2174777887, "Blue"), Station(102951984, "Weird") +}; +Station* currentStation; +int currentStationIndex = -1; + +Timer stationDetectionTimer(500, true); + +String previousDisplayContent; + +StateMachine stateMachine(new Startup()); + +unsigned long lastLoopTime = 0; +unsigned long currentLoopTime = 0; +unsigned long deltaTime = 0; + + +void setupNFC() +{ + nfc.begin(); + + uint32_t versiondata = nfc.getFirmwareVersion(); + if (!versiondata) + { + while (1); // halt + } + // configure board to read RFID tags and cards + nfc.SAMConfig(); +} + + +Station* detectStation() +{ + uint8_t uid[7]; + uint8_t uidLength; + constexpr uint16_t timeout = 100; + const bool success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, timeout); + if (!success) + { + return nullptr; + } + + uint32_t id = 0; + for (uint8_t i = 0; i < uidLength; i++) + { + id <<= 8; + id |= uid[i]; + } + + Station* found = 0; + for (unsigned int i = 0; i < sizeof(stations) / sizeof(stations[0]); i++) + { + Station* currentStation = &stations[i]; + if (currentStation->check(id)) + { + found = currentStation; + } + } + return found; +} + +void checkStations() +{ + Station* station = detectStation(); + + if (station != nullptr) + { + if (currentStation == nullptr) + { + currentStation = station; + stateMachine.putDown(Context(stations, sizeof(stations) / sizeof(stations[0]), deltaTime, &dfPlayer), currentStation); + } + } + else + { + if (currentStation != nullptr) + { + currentStation = nullptr; + stateMachine.pickedUp(Context(stations, sizeof(stations) / sizeof(stations[0]), deltaTime, &dfPlayer)); + } + } +} + +void printString(const String& str) +{ + int currentColumn = 0; + int currentRow = 0; + lcd.clear(); + + for (unsigned int i = 0; i < str.length(); i++) + { + lcd.setCursor(currentColumn, currentRow); + lcd.print(str[i]); + currentColumn++; + if (currentColumn >= 16) + { + currentColumn = 0; + currentRow++; + if (currentRow >= 2) + { + break; + } + } + } +} + +void loop(void) +{ + currentLoopTime = millis(); + deltaTime = currentLoopTime - lastLoopTime; + + bool doCheck = stationDetectionTimer.update(deltaTime); + if (doCheck) + { + checkStations(); + } + + stateMachine.update(Context(stations, sizeof(stations) / sizeof(stations[0]), deltaTime, &dfPlayer)); + String newContent = stateMachine.updateDisplay(); + if (previousDisplayContent != newContent) + { + lcd.clear(); + printString(newContent); + previousDisplayContent = newContent; + } + + lastLoopTime = currentLoopTime; + delay(10); +} + +void setup(void) +{ + setupNFC(); + + Serial.begin(9600); + + lcd.begin(16, 2); + stationDetectionTimer.start(); + + softSerial.begin(9600); + + lcd.print("Initializing MP3"); + lcd.clear(); + if (!dfPlayer.begin(softSerial, true, true)) + { + lcd.print("Failed to init"); + lcd.setCursor(0, 1); + lcd.print("DFPlayer!"); + while (true) + { + delay(100); + printMp3Detail(&lcd, dfPlayer.readType(), dfPlayer.read()); + } + } + + // dfPlayer.volume(15); + // dfPlayer.play(1); +} diff --git a/src/sounds.h b/src/sounds.h new file mode 100644 index 0000000..f48acd2 --- /dev/null +++ b/src/sounds.h @@ -0,0 +1,10 @@ +#include + +#define IDLE 1 +#define WRONG_SIDE_STATION 2 +#define TIMER_DOWN 3 +#define GAME_WON 4 +#define REMOVED_FROM_SIDE_STATION 5 +#define REMOVED_FROM_MAIN_STATION 6 +#define OBJECT_PLACED_ON_MAIN_STATION 7 +#define OBJECT_PLACED_ON_SIDE_STATION 8 diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..b3d8160 --- /dev/null +++ b/src/state.h @@ -0,0 +1,17 @@ +#pragma once +#include "station.h" +#include "context.h" + +class Station; + +class State +{ +public: + virtual ~State() = default; + + virtual State* pickedUp(Context context) { return this; } + virtual State* putDown(Context context, Station* newStation) { return this; } + virtual State* update(Context context) { return this; } + + virtual String updateDisplay() { return ""; } +}; diff --git a/src/statemachine.cpp b/src/statemachine.cpp new file mode 100644 index 0000000..b558512 --- /dev/null +++ b/src/statemachine.cpp @@ -0,0 +1,37 @@ +#include "statemachine.h" +#include "station.h" +#include "context.h" + +StateMachine::StateMachine(State* initialState) + : currentState(initialState) {} + +StateMachine::~StateMachine() { + delete currentState; +} + +void StateMachine::updateState(State* newState) { + if (newState != currentState) { + delete currentState; + currentState = newState; + } +} + +void StateMachine::pickedUp(const Context& context) { + State* newState = currentState->pickedUp(context); + updateState(newState); +} + +void StateMachine::putDown(const Context& context, Station* newStation) { + State* newState = currentState->putDown(context, newStation); + updateState(newState); +} + +void StateMachine::update(const Context& context) { + State* newState = currentState->update(context); + updateState(newState); +} + +String StateMachine::updateDisplay() const +{ + return currentState->updateDisplay(); +} diff --git a/src/statemachine.h b/src/statemachine.h new file mode 100644 index 0000000..f16b413 --- /dev/null +++ b/src/statemachine.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include "state.h" +#include "station.h" +#include "context.h" + +class StateMachine { +private: + State* currentState; + void updateState(State* newState); + +public: + StateMachine(State* initialState); + ~StateMachine(); + + void pickedUp(const Context& context); + void putDown(const Context& context, Station* newStation); + void update(const Context& context); + + String updateDisplay() const; +}; diff --git a/src/states.cpp b/src/states.cpp new file mode 100644 index 0000000..5ac1241 --- /dev/null +++ b/src/states.cpp @@ -0,0 +1,213 @@ +#include "states.h" +#include "state.h" +#include "station.h" + +Startup::Startup() = default; + +State* Startup::putDown(const Context context, Station* newStation) +{ + const int newStationIndex = context.getStationIndex(newStation); + if (newStationIndex == 0) + { + return new WaitingForGameStart(); + } + + return this; +} + +State* Startup::update(Context context) +{ + return this; +} + +String Startup::updateDisplay() +{ + return "Initializing:Put" + "on first station"; +} + + +//------------------------------------ + + +WaitingForGameStart::WaitingForGameStart() = default; + +State* WaitingForGameStart::pickedUp(const Context context) +{ + return new OnTheMove(context.stations + 1); // first element of the array +} + +State* WaitingForGameStart::update(Context context) +{ + return this; +} + +String WaitingForGameStart::updateDisplay() +{ + return "Waiting for Game" + "Start..."; +} + + +//------------------------------------ + + +OnTheMove::OnTheMove(Station* targetStation) +{ + this->targetStation = targetStation; +} + +State* OnTheMove::putDown(const Context context, Station* newStation) +{ + if (newStation == this->targetStation) + { + if (targetStation == context.stations) // first station + { + return new End(); + } + return new Hacking(this->targetStation); + } + + return new IncorrectStation(targetStation); +} + +State* OnTheMove::update(const Context context) +{ + return this; +} + +String OnTheMove::updateDisplay() +{ + return "On the Move... "; +} + + +//------------------------------------ + + +IncorrectStation::IncorrectStation(Station* targetStation) +{ + this->targetStation = targetStation; +} + +State* IncorrectStation::pickedUp(const Context context) +{ + return new OnTheMove(this->targetStation); +} + +State* IncorrectStation::update(const Context context) +{ + return this; +} + +String IncorrectStation::updateDisplay() +{ + return "Wrong Station! "; +} + + +//------------------------------------ + + +Hacking::Hacking(Station* currentStation): timer(5000, false) +{ + this->currentStation = currentStation; + this->timer.start(); +} + +State* Hacking::pickedUp(const Context context) +{ + return new Complain(currentStation); +} + +State* Hacking::update(const Context context) +{ + Serial.println(String(context.delta)); + const bool done = timer.update(context.delta); + if (done) + { + const int currentStationIndex = context.getStationIndex(currentStation); + const int nextStationIndex = (currentStationIndex + 1) % context.stationCount; + Station* nextStation = context.stations + nextStationIndex; + return new WaitingForPickup(currentStation, nextStation); + } + return this; +} + +String Hacking::updateDisplay() +{ + return "Hacking B)"; +} + + +//------------------------------------ + + +Complain::Complain(Station* currentStation) +{ + this->currentStation = currentStation; +} + +State* Complain::putDown(const Context context, Station* newStation) +{ + if (this->currentStation == newStation) // was put back on correct station + { + return new Hacking(this->currentStation); + } + return this; // was not put back on correct station (just keeps complaining) +} + +State* Complain::update(Context context) +{ + return this; +} + +String Complain::updateDisplay() +{ + return "Put me back!!"; +} + + +//------------------------------------ + + +WaitingForPickup::WaitingForPickup(Station* currentStation, Station* targetStation) +{ + this->currentStation = currentStation; + this->targetStation = targetStation; +} + +State* WaitingForPickup::pickedUp(Context context) +{ + return new OnTheMove(targetStation); +} + +State* WaitingForPickup::update(Context context) +{ + return this; +} + +String WaitingForPickup::updateDisplay() +{ + return "Pick me up "; +} + +End::End() = default; + + +State* End::pickedUp(const Context context) +{ + // todo what happens when we pick up here?... + return this; +} + +State* End::update(const Context context) +{ + // todo after some time return waiting for game start... + return this; +} + +String End::updateDisplay() +{ + return "END..."; +} diff --git a/src/states.h b/src/states.h new file mode 100644 index 0000000..40b6015 --- /dev/null +++ b/src/states.h @@ -0,0 +1,98 @@ +#pragma once +#include + +#include "context.h" +#include "state.h" +#include "states.h" +#include "station.h" + +class Startup final : public State +{ +public: + Startup(); + State* putDown(Context context, Station* newStation) override; + State* update(Context context) override; + String updateDisplay() override; +}; + + +class WaitingForGameStart final : public State +{ +public: + WaitingForGameStart(); + State* pickedUp(Context context) override; + State* update(Context context) override; + String updateDisplay() override; +}; + +class OnTheMove final : public State +{ +private: + Station* targetStation; + +public: + explicit OnTheMove(Station* targetStation); + State* putDown(Context context, Station* newStation) override; + State* update(Context context) override; + String updateDisplay() override; +}; + +class IncorrectStation final : public State +{ +private: + Station* targetStation; + +public: + explicit IncorrectStation(Station* targetStation); + State* pickedUp(Context context) override; + State* update(Context context) override; + String updateDisplay() override; +}; + + +class Hacking final : public State +{ +private: + Timer timer; + Station* currentStation; + +public: + explicit Hacking(Station* currentStation); + State* pickedUp(Context context) override; + State* update(Context context) override; + String updateDisplay() override; +}; + +class Complain final : public State +{ +private: + Station* currentStation; + +public: + explicit Complain(Station* currentStation); + State* putDown(Context context, Station* newStation) override; + State* update(Context context) override; + String updateDisplay() override; +}; + +class WaitingForPickup final : public State +{ +private: + Station* currentStation; + Station* targetStation; + +public: + WaitingForPickup(Station* currentStation, Station* targetStation); + State* pickedUp(Context context) override; + State* update(Context context) override; + String updateDisplay() override; +}; + +class End final : public State +{ +public: + End(); + State* pickedUp(Context context) override; + State* update(Context context) override; + String updateDisplay() override; +}; diff --git a/src/station.cpp b/src/station.cpp new file mode 100644 index 0000000..68927c7 --- /dev/null +++ b/src/station.cpp @@ -0,0 +1,17 @@ +#include +#include "station.h" + +Station::Station(uint32_t nfcTagId, String name) { + { + this->nfcTagId = nfcTagId; + this->name = name; + } +} + +bool Station::check(uint32_t nfcTagId) { + return nfcTagId == this->nfcTagId; +} + +String Station::getName() { + return this->name; +} diff --git a/src/station.h b/src/station.h new file mode 100644 index 0000000..c8468bc --- /dev/null +++ b/src/station.h @@ -0,0 +1,12 @@ +#pragma once +#include + +class Station { +private: + uint32_t nfcTagId; + String name; +public: + Station(uint32_t nfcTagId, String name); + bool check(uint32_t nfcTagId); + String getName(); +}; diff --git a/src/timer.cpp b/src/timer.cpp new file mode 100644 index 0000000..5d5c9a4 --- /dev/null +++ b/src/timer.cpp @@ -0,0 +1,47 @@ +#include "timer.h" + +#include + +Timer::Timer(const unsigned long duration, const bool autoRestart): currentTime(0), running(false) +{ + this->autoRestart = autoRestart; + this->duration = duration; + this->endTime = duration; +} + +void Timer::start() +{ + this->running = true; +} + +bool Timer::update(const unsigned long delta) +{ + Serial.print("Timer "); + if (!running) + { + Serial.print(" not running..."); + return false; + } + bool timedOut = false; + this->currentTime += delta; + Serial.print(" target : " + String(this->currentTime) + "/" + String(this->endTime)); + if (currentTime > this->endTime) + { + timedOut = true; + } + if (timedOut && this->autoRestart) + { + this->endTime += this->duration; + } + if (timedOut && !this->autoRestart) + { + running = false; + } + Serial.println(); + return timedOut; +} + +bool Timer::isRunning() const +{ + return this->running; +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..9667847 --- /dev/null +++ b/src/timer.h @@ -0,0 +1,17 @@ +#pragma once + +class Timer { +private: + unsigned long duration; + bool autoRestart; + + unsigned long currentTime; + unsigned long endTime; + bool running; + +public: + Timer(unsigned long duration, bool autoRestart); + void start(); + bool update(unsigned long delta); + bool isRunning() const; +}; diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html From bc5d7e1941a18e7f5782a9668b18d2103a8406aa Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 23 May 2024 16:28:30 +0200 Subject: [PATCH 02/14] adds sound files --- assets/0001Idle.mp3 | 3 +++ assets/0002Falsche Nebenstation.mp3 | 3 +++ assets/0003Timer abgelaufen.mp3 | 3 +++ assets/0004Spiel gewonnen.mp3 | 3 +++ assets/0005Objekt von der Nebenstation entfernt.mp3 | 3 +++ assets/0006Objekt von der Haupstation entfernt.mp3 | 3 +++ assets/0007Objekt auf Hauptstation.mp3 | 3 +++ assets/0008Objekt auf einer Nebenstation.mp3 | 3 +++ 8 files changed, 24 insertions(+) create mode 100644 assets/0001Idle.mp3 create mode 100644 assets/0002Falsche Nebenstation.mp3 create mode 100644 assets/0003Timer abgelaufen.mp3 create mode 100644 assets/0004Spiel gewonnen.mp3 create mode 100644 assets/0005Objekt von der Nebenstation entfernt.mp3 create mode 100644 assets/0006Objekt von der Haupstation entfernt.mp3 create mode 100644 assets/0007Objekt auf Hauptstation.mp3 create mode 100644 assets/0008Objekt auf einer Nebenstation.mp3 diff --git a/assets/0001Idle.mp3 b/assets/0001Idle.mp3 new file mode 100644 index 0000000..97690f6 --- /dev/null +++ b/assets/0001Idle.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:007daa6dd0fcda10a4c31ce0fb6d2f75ec623bb094557e37d977df861fe4b2ed +size 11886 diff --git a/assets/0002Falsche Nebenstation.mp3 b/assets/0002Falsche Nebenstation.mp3 new file mode 100644 index 0000000..57fdfb8 --- /dev/null +++ b/assets/0002Falsche Nebenstation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7782fc7bc2ff00baaf2e59109efe8f6cb7c4485c3e90bc5d215186b97c289ec +size 30238 diff --git a/assets/0003Timer abgelaufen.mp3 b/assets/0003Timer abgelaufen.mp3 new file mode 100644 index 0000000..bc0a18c --- /dev/null +++ b/assets/0003Timer abgelaufen.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98ea95f497c3d77e312c953ee5d0d317026b1317976491b9ae2c488524cd418c +size 27690 diff --git a/assets/0004Spiel gewonnen.mp3 b/assets/0004Spiel gewonnen.mp3 new file mode 100644 index 0000000..b4e6662 --- /dev/null +++ b/assets/0004Spiel gewonnen.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aacc30cf4fee2d815ecb8d5374d527ab23534c54656a88fb4acf567a2f043e28 +size 25864 diff --git a/assets/0005Objekt von der Nebenstation entfernt.mp3 b/assets/0005Objekt von der Nebenstation entfernt.mp3 new file mode 100644 index 0000000..429bfd9 --- /dev/null +++ b/assets/0005Objekt von der Nebenstation entfernt.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51c18a31dd9f7b338ae2c43a41eaeb53f0a649b13fe7f750ecb01adaad991971 +size 37334 diff --git a/assets/0006Objekt von der Haupstation entfernt.mp3 b/assets/0006Objekt von der Haupstation entfernt.mp3 new file mode 100644 index 0000000..14364b6 --- /dev/null +++ b/assets/0006Objekt von der Haupstation entfernt.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4be3d3e0130a9825cb388959e15d2d103776cf495c32fdadf6b016d265e27a0 +size 41317 diff --git a/assets/0007Objekt auf Hauptstation.mp3 b/assets/0007Objekt auf Hauptstation.mp3 new file mode 100644 index 0000000..2e22898 --- /dev/null +++ b/assets/0007Objekt auf Hauptstation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e1c509f2c3ea6329e37c57c120c44dae7c4814e045a048af5b2dd5e2917f71a +size 31153 diff --git a/assets/0008Objekt auf einer Nebenstation.mp3 b/assets/0008Objekt auf einer Nebenstation.mp3 new file mode 100644 index 0000000..6f424a4 --- /dev/null +++ b/assets/0008Objekt auf einer Nebenstation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df11e75f9b213b6bf05230ca4a7854f50a54effd4c02cd2c7182f7681c96b4e8 +size 31711 From 43d31651cdb5e0fbf0c8d533d74f630644a81468 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 27 May 2024 10:19:28 +0200 Subject: [PATCH 03/14] finishes state flow and adds temp audio --- src/main.cpp | 3 +-- src/state.h | 2 ++ src/statemachine.cpp | 9 +++++---- src/statemachine.h | 2 +- src/states.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/states.h | 8 ++++++++ 6 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 04ce725..8c2245a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -180,6 +180,5 @@ void setup(void) } } - // dfPlayer.volume(15); - // dfPlayer.play(1); + dfPlayer.volume(7); } diff --git a/src/state.h b/src/state.h index b3d8160..5d0b1d7 100644 --- a/src/state.h +++ b/src/state.h @@ -13,5 +13,7 @@ public: virtual State* putDown(Context context, Station* newStation) { return this; } virtual State* update(Context context) { return this; } + virtual void activated(Context context) { } + virtual String updateDisplay() { return ""; } }; diff --git a/src/statemachine.cpp b/src/statemachine.cpp index b558512..15142e6 100644 --- a/src/statemachine.cpp +++ b/src/statemachine.cpp @@ -9,26 +9,27 @@ StateMachine::~StateMachine() { delete currentState; } -void StateMachine::updateState(State* newState) { +void StateMachine::updateState(State* newState, const Context& context) { if (newState != currentState) { delete currentState; currentState = newState; + currentState->activated(context); } } void StateMachine::pickedUp(const Context& context) { State* newState = currentState->pickedUp(context); - updateState(newState); + updateState(newState, context); } void StateMachine::putDown(const Context& context, Station* newStation) { State* newState = currentState->putDown(context, newStation); - updateState(newState); + updateState(newState, context); } void StateMachine::update(const Context& context) { State* newState = currentState->update(context); - updateState(newState); + updateState(newState, context); } String StateMachine::updateDisplay() const diff --git a/src/statemachine.h b/src/statemachine.h index f16b413..0f4c72c 100644 --- a/src/statemachine.h +++ b/src/statemachine.h @@ -7,7 +7,7 @@ class StateMachine { private: State* currentState; - void updateState(State* newState); + void updateState(State* newState, const Context& context); public: StateMachine(State* initialState); diff --git a/src/states.cpp b/src/states.cpp index 5ac1241..2aa65be 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -1,6 +1,7 @@ #include "states.h" #include "state.h" #include "station.h" +#include "sounds.h" Startup::Startup() = default; @@ -26,6 +27,10 @@ String Startup::updateDisplay() "on first station"; } +void Startup::activated(Context context) +{ +} + //------------------------------------ @@ -48,6 +53,11 @@ String WaitingForGameStart::updateDisplay() "Start..."; } +void WaitingForGameStart::activated(const Context context) +{ + context.dfPlayer->play(IDLE); +} + //------------------------------------ @@ -81,6 +91,18 @@ String OnTheMove::updateDisplay() return "On the Move... "; } +void OnTheMove::activated(const Context context) +{ + const int index = context.getStationIndex(this->targetStation); + if (index == 1) + { + context.dfPlayer->play(REMOVED_FROM_MAIN_STATION); + } else + { + context.dfPlayer->play(REMOVED_FROM_SIDE_STATION); + } +} + //------------------------------------ @@ -105,6 +127,10 @@ String IncorrectStation::updateDisplay() return "Wrong Station! "; } +void IncorrectStation::activated(Context context) +{ +} + //------------------------------------ @@ -139,6 +165,11 @@ String Hacking::updateDisplay() return "Hacking B)"; } +void Hacking::activated(const Context context) +{ + context.dfPlayer->play(OBJECT_PLACED_ON_MAIN_STATION); +} + //------------------------------------ @@ -167,6 +198,10 @@ String Complain::updateDisplay() return "Put me back!!"; } +void Complain::activated(Context context) +{ +} + //------------------------------------ @@ -192,6 +227,14 @@ String WaitingForPickup::updateDisplay() return "Pick me up "; } +void WaitingForPickup::activated(Context context) +{ +} + + +//------------------------------------ + + End::End() = default; @@ -211,3 +254,8 @@ String End::updateDisplay() { return "END..."; } + +void End::activated(Context context) +{ + context.dfPlayer->play(GAME_WON); +} diff --git a/src/states.h b/src/states.h index 40b6015..f89b856 100644 --- a/src/states.h +++ b/src/states.h @@ -13,6 +13,7 @@ public: State* putDown(Context context, Station* newStation) override; State* update(Context context) override; String updateDisplay() override; + void activated(Context context) override; }; @@ -23,6 +24,7 @@ public: State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; + void activated(Context context) override; }; class OnTheMove final : public State @@ -35,6 +37,7 @@ public: State* putDown(Context context, Station* newStation) override; State* update(Context context) override; String updateDisplay() override; + void activated(Context context) override; }; class IncorrectStation final : public State @@ -47,6 +50,7 @@ public: State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; + void activated(Context context) override; }; @@ -61,6 +65,7 @@ public: State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; + void activated(Context context) override; }; class Complain final : public State @@ -73,6 +78,7 @@ public: State* putDown(Context context, Station* newStation) override; State* update(Context context) override; String updateDisplay() override; + void activated(Context context) override; }; class WaitingForPickup final : public State @@ -86,6 +92,7 @@ public: State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; + void activated(Context context) override; }; class End final : public State @@ -95,4 +102,5 @@ public: State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; + void activated(Context context) override; }; From 6997f3c98fe3abcc034dee4ab9bfb078e9891aa1 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 27 May 2024 11:17:53 +0200 Subject: [PATCH 04/14] changes used library for dfplayer --- platformio.ini | 1 + src/audioState.h | 70 ------------------------------------------------ src/context.cpp | 25 ++++++++++------- src/context.h | 7 ++--- src/main.cpp | 7 ++--- 5 files changed, 24 insertions(+), 86 deletions(-) diff --git a/platformio.ini b/platformio.ini index 89e16ed..7b4935c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,3 +16,4 @@ lib_deps = adafruit/Adafruit BusIO@^1.16.1 dfrobot/DFRobotDFPlayerMini@^1.0.6 fmalpartida/LiquidCrystal@^1.5.0 + powerbroker2/DFPlayerMini_Fast@^1.2.4 diff --git a/src/audioState.h b/src/audioState.h index bc75ac7..e853445 100644 --- a/src/audioState.h +++ b/src/audioState.h @@ -1,73 +1,3 @@ #pragma once #include -#include #include - -void printMp3Detail(LiquidCrystal* lcd, uint8_t type, int value) -{ - lcd->clear(); - switch (type) - { - case TimeOut: - lcd->print("Time Out!"); - break; - case WrongStack: - lcd->print("Stack Wrong!"); - break; - case DFPlayerCardInserted: - lcd->print("Card Inserted!"); - break; - case DFPlayerCardRemoved: - lcd->print("Card Removed!"); - break; - case DFPlayerCardOnline: - lcd->print("Card Online!"); - break; - case DFPlayerUSBInserted: - lcd->print("USB Inserted!"); - break; - case DFPlayerUSBRemoved: - lcd->print("USB Removed!"); - break; - case DFPlayerPlayFinished: - lcd->print("Num:"); - lcd->print(value); - lcd->print(" Finished!"); - break; - case DFPlayerError: - lcd->print("DFPlayerError:"); - switch (value) - { - case Busy: - lcd->print("Card not found"); - break; - case Sleeping: - lcd->print("Sleeping"); - break; - case SerialWrongStack: - lcd->print("Wrong Stack"); - break; - case CheckSumNotMatch: - lcd->print("Checksum Error"); - break; - case FileIndexOut: - lcd->print("File Index OOB"); - break; - case FileMismatch: - lcd->print("File Mismatch"); - break; - case Advertise: - lcd->print("In Advertise"); - break; - default: - lcd->print("Unknown Error"); - break; - } - break; - default: - lcd->print("Unknown Type"); - break; - } - delay(2000); // Display the message for 2 seconds -} - diff --git a/src/context.cpp b/src/context.cpp index b9a6081..d4da98f 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -1,17 +1,22 @@ #include "Context.h" -Context::Context(Station* stations, const int stationCount, const unsigned long delta, DFRobotDFPlayerMini* dfPlayer) { - this->stations = stations; - this->stationCount = stationCount; - this->delta = delta; - this->dfPlayer = dfPlayer; +Context::Context(Station* stations, const int stationCount, const unsigned long delta, DFPlayerMini_Fast* dfPlayer) +{ + this->stations = stations; + this->stationCount = stationCount; + this->delta = delta; + this->dfPlayer = dfPlayer; } + int Context::getStationIndex(const Station* station) const { - for (int i = 0; i < stationCount; ++i) { - if (&stations[i] == station) { - return i; + for (int i = 0; i < stationCount; ++i) + { + if (&stations[i] == station) + { + return i; + } } - } - return -1; + return -1; } + diff --git a/src/context.h b/src/context.h index f1ee594..f521a3c 100644 --- a/src/context.h +++ b/src/context.h @@ -1,15 +1,16 @@ #pragma once -#include +#include #include "station.h" class Context { public: - Context(Station* stations, int stationCount, unsigned long delta, DFRobotDFPlayerMini* dfPlayer); + Context(Station* stations, int stationCount, unsigned long delta, DFPlayerMini_Fast* dfPlayer); Station* stations; int stationCount; unsigned long delta; - DFRobotDFPlayerMini* dfPlayer; + DFPlayerMini_Fast* dfPlayer; int getStationIndex(const Station* station) const; + bool isPlaying() const; }; diff --git a/src/main.cpp b/src/main.cpp index 8c2245a..130f17c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "station.h" #include "statemachine.h" @@ -22,7 +22,7 @@ constexpr int rs = 9, en = 8, d4 = 5, d5 = 4, d6 = 3, d7 = 2; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); SoftwareSerial softSerial(6, 7); // RX, TX -DFRobotDFPlayerMini dfPlayer; +DFPlayerMini_Fast dfPlayer; Station stations[5] = { Station(1680767519, "Yellow"), Station(3346823711, "Green"), Station(3569318175, "Pink"), @@ -176,7 +176,8 @@ void setup(void) while (true) { delay(100); - printMp3Detail(&lcd, dfPlayer.readType(), dfPlayer.read()); + dfPlayer.printError(); + // printMp3Detail(&lcd, dfPlayer.readType(), dfPlayer.read()); } } From 662464d34da0d42f3c89d59757856095baca7aba Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 27 May 2024 11:45:36 +0200 Subject: [PATCH 05/14] changes of previous commit reverted --- platformio.ini | 1 - src/context.cpp | 2 +- src/context.h | 6 +++--- src/main.cpp | 5 ++--- src/states.cpp | 1 + 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/platformio.ini b/platformio.ini index 7b4935c..89e16ed 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,4 +16,3 @@ lib_deps = adafruit/Adafruit BusIO@^1.16.1 dfrobot/DFRobotDFPlayerMini@^1.0.6 fmalpartida/LiquidCrystal@^1.5.0 - powerbroker2/DFPlayerMini_Fast@^1.2.4 diff --git a/src/context.cpp b/src/context.cpp index d4da98f..3e05729 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -1,6 +1,6 @@ #include "Context.h" -Context::Context(Station* stations, const int stationCount, const unsigned long delta, DFPlayerMini_Fast* dfPlayer) +Context::Context(Station* stations, const int stationCount, const unsigned long delta, DFRobotDFPlayerMini* dfPlayer) { this->stations = stations; this->stationCount = stationCount; diff --git a/src/context.h b/src/context.h index f521a3c..993522c 100644 --- a/src/context.h +++ b/src/context.h @@ -1,15 +1,15 @@ #pragma once -#include +#include #include "station.h" class Context { public: - Context(Station* stations, int stationCount, unsigned long delta, DFPlayerMini_Fast* dfPlayer); + Context(Station* stations, int stationCount, unsigned long delta, DFRobotDFPlayerMini* dfPlayer); Station* stations; int stationCount; unsigned long delta; - DFPlayerMini_Fast* dfPlayer; + DFRobotDFPlayerMini* dfPlayer; int getStationIndex(const Station* station) const; bool isPlaying() const; diff --git a/src/main.cpp b/src/main.cpp index 130f17c..3c10689 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "station.h" #include "statemachine.h" @@ -22,7 +22,7 @@ constexpr int rs = 9, en = 8, d4 = 5, d5 = 4, d6 = 3, d7 = 2; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); SoftwareSerial softSerial(6, 7); // RX, TX -DFPlayerMini_Fast dfPlayer; +DFRobotDFPlayerMini dfPlayer; Station stations[5] = { Station(1680767519, "Yellow"), Station(3346823711, "Green"), Station(3569318175, "Pink"), @@ -176,7 +176,6 @@ void setup(void) while (true) { delay(100); - dfPlayer.printError(); // printMp3Detail(&lcd, dfPlayer.readType(), dfPlayer.read()); } } diff --git a/src/states.cpp b/src/states.cpp index 2aa65be..51bcf15 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -150,6 +150,7 @@ State* Hacking::update(const Context context) { Serial.println(String(context.delta)); const bool done = timer.update(context.delta); + if (done) { const int currentStationIndex = context.getStationIndex(currentStation); From 27fc0535b38bb73731b6ab673f7da7215bdf5c6d Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 28 May 2024 14:09:39 +0200 Subject: [PATCH 06/14] non working sequence code --- src/audioState.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 9 ++++-- src/sequence.cpp | 44 ++++++++++++++++++++++++++++ src/sequence.h | 25 ++++++++++++++++ src/sequences.cpp | 21 ++++++++++++++ src/sequences.h | 16 +++++++++++ src/sounds.h | 1 + src/state.h | 56 +++++++++++++++++++++++++++++++++++- src/states.cpp | 60 ++++++++++++++++++++++++-------------- src/states.h | 6 ++-- src/timer.cpp | 7 ++--- 11 files changed, 286 insertions(+), 32 deletions(-) create mode 100644 src/sequence.cpp create mode 100644 src/sequence.h create mode 100644 src/sequences.cpp create mode 100644 src/sequences.h diff --git a/src/audioState.h b/src/audioState.h index e853445..c3f26f1 100644 --- a/src/audioState.h +++ b/src/audioState.h @@ -1,3 +1,76 @@ #pragma once #include #include + +#pragma once +#include +#include +#include + +inline void printMp3Detail(const uint8_t type, const int value) +{ + Serial.println(); // Add a newline before printing the details + switch (type) + { + case TimeOut: + Serial.println("Time Out!"); + break; + case WrongStack: + Serial.println("Stack Wrong!"); + break; + case DFPlayerCardInserted: + Serial.println("Card Inserted!"); + break; + case DFPlayerCardRemoved: + Serial.println("Card Removed!"); + break; + case DFPlayerCardOnline: + Serial.println("Card Online!"); + break; + case DFPlayerUSBInserted: + Serial.println("USB Inserted!"); + break; + case DFPlayerUSBRemoved: + Serial.println("USB Removed!"); + break; + case DFPlayerPlayFinished: + Serial.print("Num:"); + Serial.print(value); + Serial.println(" Finished!"); + break; + case DFPlayerError: + Serial.print("DFPlayerError: "); + switch (value) + { + case Busy: + Serial.println("Card not found"); + break; + case Sleeping: + Serial.println("Sleeping"); + break; + case SerialWrongStack: + Serial.println("Wrong Stack"); + break; + case CheckSumNotMatch: + Serial.println("Checksum Error"); + break; + case FileIndexOut: + Serial.println("File Index OOB"); + break; + case FileMismatch: + Serial.println("File Mismatch"); + break; + case Advertise: + Serial.println("In Advertise"); + break; + default: + Serial.println("Unknown Error"); + break; + } + break; + default: + Serial.println("Unknown Type"); + break; + } + delay(100); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 3c10689..6b6f09f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "states.h" #include "timer.h" #include "audioState.h" +#include "sequences.h" #define PN532_CS 10 @@ -135,13 +136,13 @@ void loop(void) { currentLoopTime = millis(); deltaTime = currentLoopTime - lastLoopTime; - + bool doCheck = stationDetectionTimer.update(deltaTime); if (doCheck) { checkStations(); } - + stateMachine.update(Context(stations, sizeof(stations) / sizeof(stations[0]), deltaTime, &dfPlayer)); String newContent = stateMachine.updateDisplay(); if (previousDisplayContent != newContent) @@ -150,16 +151,18 @@ void loop(void) printString(newContent); previousDisplayContent = newContent; } - + lastLoopTime = currentLoopTime; delay(10); } void setup(void) { + initSequences(); setupNFC(); Serial.begin(9600); + // Serial.println(ON_MAIN_STATION.getEntryAtIndex(0)->isAudio); lcd.begin(16, 2); stationDetectionTimer.start(); diff --git a/src/sequence.cpp b/src/sequence.cpp new file mode 100644 index 0000000..419d4b0 --- /dev/null +++ b/src/sequence.cpp @@ -0,0 +1,44 @@ +#include +#include "sequence.h" + +SequenceEntry::SequenceEntry(const bool is_audio, const int duration, const int audio_id): + isAudio(is_audio), + duration(duration), + audioId(audio_id) +{ +} + +SequenceEntry* Sequence::getEntryAtIndex(const int index) const +{ + if (index >= this->length) + { + // illegal argument + return nullptr; + } + return this->entries[index]; +} + +int Sequence::getLength() const +{ + return this->length; +} + +int Sequence::getRepeatIndex() const +{ + return repeatIndex; +} + +Sequence::Sequence(const int repeatIndex): entries{}, length(0), repeatIndex(repeatIndex) +{ +} + +void Sequence::addEntry(SequenceEntry entry) +{ + if (this->length == 20) + { + // max amount of entries + return; + } + entries[length] = &entry; + length += 1; +} diff --git a/src/sequence.h b/src/sequence.h new file mode 100644 index 0000000..3757ece --- /dev/null +++ b/src/sequence.h @@ -0,0 +1,25 @@ +#pragma once +#include + +class SequenceEntry { +public: + SequenceEntry(bool is_audio, int duration, int audio_id); + + bool isAudio; + int duration; + int audioId; +}; + +class Sequence { +private: + SequenceEntry* entries[20]; + int length; + int repeatIndex; +public: + explicit Sequence(int repeatIndex); + + SequenceEntry* getEntryAtIndex(int index) const; + int getLength() const; + int getRepeatIndex() const; + void addEntry(SequenceEntry entry); +}; diff --git a/src/sequences.cpp b/src/sequences.cpp new file mode 100644 index 0000000..38a9968 --- /dev/null +++ b/src/sequences.cpp @@ -0,0 +1,21 @@ +#include "sequences.h" +#include "sequence.h" +#include "sounds.h" + +// Define global sequences +Sequence ON_MAIN_STATION(0); +Sequence AFTER_MAIN_STATION(0); +Sequence HACKING_STATION_MUHVELLA(0); +Sequence AFTER_MUHVELLA(0); +Sequence HACKING_STATION_MAGIE(0); +Sequence AFTER_MAGIE(0); +Sequence HACKING_STATION_BICOLA(0); +Sequence AFTER_BICOLA(0); +Sequence HACKING_STATION_TOBIONE(0); +Sequence AFTER_TOBIONE(0); + +void initSequences() { + ON_MAIN_STATION.addEntry(SequenceEntry(true, 1000, IDLE)); + ON_MAIN_STATION.addEntry(SequenceEntry(false, 3000, 0)); + ON_MAIN_STATION.addEntry(SequenceEntry(true, 1000, IDLE)); +} \ No newline at end of file diff --git a/src/sequences.h b/src/sequences.h new file mode 100644 index 0000000..d2c9690 --- /dev/null +++ b/src/sequences.h @@ -0,0 +1,16 @@ +#pragma once +#include "sequences.h" +#include "sequence.h" + +extern Sequence ON_MAIN_STATION; +extern Sequence AFTER_MAIN_STATION; +extern Sequence HACKING_STATION_MUHVELLA; +extern Sequence AFTER_MUHVELLA; +extern Sequence HACKING_STATION_MAGIE; +extern Sequence AFTER_MAGIE; +extern Sequence HACKING_STATION_BICOLA; +extern Sequence AFTER_BICOLA; +extern Sequence HACKING_STATION_TOBIONE; +extern Sequence AFTER_TOBIONE; + +void initSequences(); diff --git a/src/sounds.h b/src/sounds.h index f48acd2..8ae16ad 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1,5 +1,6 @@ #include +#define NONE 0 #define IDLE 1 #define WRONG_SIDE_STATION 2 #define TIMER_DOWN 3 diff --git a/src/state.h b/src/state.h index 5d0b1d7..7615be1 100644 --- a/src/state.h +++ b/src/state.h @@ -1,19 +1,73 @@ #pragma once +#include +#include + #include "station.h" #include "context.h" +#include "sequence.h" class Station; class State { +private: + Timer sequenceTimer; + bool currentEntryHandled; + +protected: + Sequence* sequence; + int sequencePointer; + bool sequenceDone; + public: virtual ~State() = default; + explicit State(Sequence* sequence): sequenceTimer(Timer(0, false)), currentEntryHandled(false), sequence(sequence), + sequencePointer(0), sequenceDone(false) + { + } + virtual State* pickedUp(Context context) { return this; } virtual State* putDown(Context context, Station* newStation) { return this; } virtual State* update(Context context) { return this; } - virtual void activated(Context context) { } + virtual void activated(Context context) + { + } virtual String updateDisplay() { return ""; } + + void updateSeqence(const Context& context) + { + if (sequence == nullptr || sequence->getLength() == 0) + { + return; + } + if (!currentEntryHandled) + { + const auto currentEntry = sequence->getEntryAtIndex(sequencePointer); + const auto addressInt = reinterpret_cast(currentEntry); + this->sequenceTimer = Timer(currentEntry->duration, false); + this->sequenceTimer.start(); + if (currentEntry->isAudio) + { + // printMp3Detail(context.dfPlayer->readType(), context.dfPlayer->read()); + context.dfPlayer->play(currentEntry->audioId); + } + currentEntryHandled = true; + } + const bool timeOut = this->sequenceTimer.update(context.delta); + if (timeOut) + { + if (this->sequencePointer + 2 >= this->sequence->getLength()) + { + sequencePointer = sequence->getRepeatIndex(); + sequenceDone = true; + currentEntryHandled = false; + return; + } + this->sequencePointer += 1; + currentEntryHandled = false; + } + } }; diff --git a/src/states.cpp b/src/states.cpp index 51bcf15..e717945 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -1,9 +1,14 @@ #include "states.h" + +#include + #include "state.h" #include "station.h" #include "sounds.h" -Startup::Startup() = default; +Startup::Startup(): State(nullptr) +{ +} State* Startup::putDown(const Context context, Station* newStation) { @@ -16,8 +21,9 @@ State* Startup::putDown(const Context context, Station* newStation) return this; } -State* Startup::update(Context context) +State* Startup::update(const Context context) { + updateSeqence(context); return this; } @@ -35,15 +41,20 @@ void Startup::activated(Context context) //------------------------------------ -WaitingForGameStart::WaitingForGameStart() = default; +WaitingForGameStart::WaitingForGameStart() : State(&ON_MAIN_STATION) +{ +} State* WaitingForGameStart::pickedUp(const Context context) { - return new OnTheMove(context.stations + 1); // first element of the array + return new OnTheMove(context.stations + 1, &AFTER_MAIN_STATION); // first element of the array } -State* WaitingForGameStart::update(Context context) +State* WaitingForGameStart::update(const Context context) { + // Serial.println("update wait"); + updateSeqence(context); + // Serial.println("after"); return this; } @@ -62,7 +73,7 @@ void WaitingForGameStart::activated(const Context context) //------------------------------------ -OnTheMove::OnTheMove(Station* targetStation) +OnTheMove::OnTheMove(Station* targetStation, Sequence* sequence): State(sequence) { this->targetStation = targetStation; } @@ -75,7 +86,7 @@ State* OnTheMove::putDown(const Context context, Station* newStation) { return new End(); } - return new Hacking(this->targetStation); + return new Hacking(this->targetStation, nullptr); } return new IncorrectStation(targetStation); @@ -83,6 +94,7 @@ State* OnTheMove::putDown(const Context context, Station* newStation) State* OnTheMove::update(const Context context) { + updateSeqence(context); return this; } @@ -96,10 +108,11 @@ void OnTheMove::activated(const Context context) const int index = context.getStationIndex(this->targetStation); if (index == 1) { - context.dfPlayer->play(REMOVED_FROM_MAIN_STATION); - } else + context.dfPlayer->play(REMOVED_FROM_MAIN_STATION); + } + else { - context.dfPlayer->play(REMOVED_FROM_SIDE_STATION); + context.dfPlayer->play(REMOVED_FROM_SIDE_STATION); } } @@ -107,18 +120,19 @@ void OnTheMove::activated(const Context context) //------------------------------------ -IncorrectStation::IncorrectStation(Station* targetStation) +IncorrectStation::IncorrectStation(Station* targetStation): State(nullptr) // todo remove nullptr { this->targetStation = targetStation; } State* IncorrectStation::pickedUp(const Context context) { - return new OnTheMove(this->targetStation); + return new OnTheMove(this->targetStation, nullptr); // todo fix nullptr } State* IncorrectStation::update(const Context context) { + updateSeqence(context); return this; } @@ -135,7 +149,7 @@ void IncorrectStation::activated(Context context) //------------------------------------ -Hacking::Hacking(Station* currentStation): timer(5000, false) +Hacking::Hacking(Station* currentStation, Sequence* sequence): State(sequence), timer(5000, false) { this->currentStation = currentStation; this->timer.start(); @@ -148,15 +162,16 @@ State* Hacking::pickedUp(const Context context) State* Hacking::update(const Context context) { + updateSeqence(context); Serial.println(String(context.delta)); const bool done = timer.update(context.delta); - + if (done) { const int currentStationIndex = context.getStationIndex(currentStation); const int nextStationIndex = (currentStationIndex + 1) % context.stationCount; Station* nextStation = context.stations + nextStationIndex; - return new WaitingForPickup(currentStation, nextStation); + return new WaitingForPickup(currentStation, nextStation, nullptr); } return this; } @@ -175,7 +190,7 @@ void Hacking::activated(const Context context) //------------------------------------ -Complain::Complain(Station* currentStation) +Complain::Complain(Station* currentStation): State(nullptr) { this->currentStation = currentStation; } @@ -184,7 +199,7 @@ State* Complain::putDown(const Context context, Station* newStation) { if (this->currentStation == newStation) // was put back on correct station { - return new Hacking(this->currentStation); + return new Hacking(this->currentStation, nullptr); } return this; // was not put back on correct station (just keeps complaining) } @@ -207,7 +222,7 @@ void Complain::activated(Context context) //------------------------------------ -WaitingForPickup::WaitingForPickup(Station* currentStation, Station* targetStation) +WaitingForPickup::WaitingForPickup(Station* currentStation, Station* targetStation, Sequence* sequence): State(sequence) { this->currentStation = currentStation; this->targetStation = targetStation; @@ -215,7 +230,7 @@ WaitingForPickup::WaitingForPickup(Station* currentStation, Station* targetStati State* WaitingForPickup::pickedUp(Context context) { - return new OnTheMove(targetStation); + return new OnTheMove(targetStation, nullptr); } State* WaitingForPickup::update(Context context) @@ -236,7 +251,10 @@ void WaitingForPickup::activated(Context context) //------------------------------------ -End::End() = default; +End::End():State(nullptr) +{ + +} State* End::pickedUp(const Context context) @@ -256,7 +274,7 @@ String End::updateDisplay() return "END..."; } -void End::activated(Context context) +void End::activated(const Context context) { context.dfPlayer->play(GAME_WON); } diff --git a/src/states.h b/src/states.h index f89b856..12a05d6 100644 --- a/src/states.h +++ b/src/states.h @@ -33,7 +33,7 @@ private: Station* targetStation; public: - explicit OnTheMove(Station* targetStation); + explicit OnTheMove(Station* targetStation, Sequence* sequence); State* putDown(Context context, Station* newStation) override; State* update(Context context) override; String updateDisplay() override; @@ -61,7 +61,7 @@ private: Station* currentStation; public: - explicit Hacking(Station* currentStation); + explicit Hacking(Station* currentStation, Sequence* sequence); State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; @@ -88,7 +88,7 @@ private: Station* targetStation; public: - WaitingForPickup(Station* currentStation, Station* targetStation); + WaitingForPickup(Station* currentStation, Station* targetStation, Sequence* sequence); State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; diff --git a/src/timer.cpp b/src/timer.cpp index 5d5c9a4..c3b12eb 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -16,15 +16,15 @@ void Timer::start() bool Timer::update(const unsigned long delta) { - Serial.print("Timer "); + // Serial.print("Timer "); if (!running) { - Serial.print(" not running..."); + // Serial.print(" not running..."); return false; } bool timedOut = false; this->currentTime += delta; - Serial.print(" target : " + String(this->currentTime) + "/" + String(this->endTime)); + // Serial.print(" target : " + String(this->currentTime) + "/" + String(this->endTime)); if (currentTime > this->endTime) { timedOut = true; @@ -37,7 +37,6 @@ bool Timer::update(const unsigned long delta) { running = false; } - Serial.println(); return timedOut; } From 0ba199a777520795c5f204d2d8bc9f6c204fe886 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 31 May 2024 15:55:14 +0200 Subject: [PATCH 07/14] updates audio files --- assets/0001Idle.mp3 | 3 - assets/0001WaitingForGameStart.mp3 | 3 + assets/0002Falsche Nebenstation.mp3 | 3 - assets/0002OnTheMoveMainStation.mp3 | 3 + assets/0003StationOneCorrect.mp3 | 3 + assets/0003Timer abgelaufen.mp3 | 3 - assets/0004Spiel gewonnen.mp3 | 3 - assets/0004StationTwoCorrekt.mp3 | 3 + ...05Objekt von der Nebenstation entfernt.mp3 | 3 - assets/0005StationTreeCorrekt.mp3 | 3 + ...006Objekt von der Haupstation entfernt.mp3 | 3 - assets/0006StationFourCorrekt.mp3 | 3 + assets/0007Objekt auf Hauptstation.mp3 | 3 - assets/0007WrongStation.mp3 | 3 + assets/0008Ende.mp3 | 3 + assets/0008Objekt auf einer Nebenstation.mp3 | 3 - assets/0009Stress.mp3 | 3 + assets/0010StationTwoPickUp-240528_1353.mp3 | 3 + assets/0011StationTreePickup-240528_1354.mp3 | 3 + assets/0012StationFourPickUp-240528_1357.mp3 | 3 + src/main.cpp | 17 ++- src/sequences.cpp | 17 --- src/sequences.h | 13 --- src/sounds.h | 31 ++++-- src/state.h | 47 -------- src/states.cpp | 104 ++++++++++++------ src/states.h | 6 +- 27 files changed, 138 insertions(+), 157 deletions(-) delete mode 100644 assets/0001Idle.mp3 create mode 100644 assets/0001WaitingForGameStart.mp3 delete mode 100644 assets/0002Falsche Nebenstation.mp3 create mode 100644 assets/0002OnTheMoveMainStation.mp3 create mode 100644 assets/0003StationOneCorrect.mp3 delete mode 100644 assets/0003Timer abgelaufen.mp3 delete mode 100644 assets/0004Spiel gewonnen.mp3 create mode 100644 assets/0004StationTwoCorrekt.mp3 delete mode 100644 assets/0005Objekt von der Nebenstation entfernt.mp3 create mode 100644 assets/0005StationTreeCorrekt.mp3 delete mode 100644 assets/0006Objekt von der Haupstation entfernt.mp3 create mode 100644 assets/0006StationFourCorrekt.mp3 delete mode 100644 assets/0007Objekt auf Hauptstation.mp3 create mode 100644 assets/0007WrongStation.mp3 create mode 100644 assets/0008Ende.mp3 delete mode 100644 assets/0008Objekt auf einer Nebenstation.mp3 create mode 100644 assets/0009Stress.mp3 create mode 100644 assets/0010StationTwoPickUp-240528_1353.mp3 create mode 100644 assets/0011StationTreePickup-240528_1354.mp3 create mode 100644 assets/0012StationFourPickUp-240528_1357.mp3 diff --git a/assets/0001Idle.mp3 b/assets/0001Idle.mp3 deleted file mode 100644 index 97690f6..0000000 --- a/assets/0001Idle.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:007daa6dd0fcda10a4c31ce0fb6d2f75ec623bb094557e37d977df861fe4b2ed -size 11886 diff --git a/assets/0001WaitingForGameStart.mp3 b/assets/0001WaitingForGameStart.mp3 new file mode 100644 index 0000000..770243b --- /dev/null +++ b/assets/0001WaitingForGameStart.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec88a517b806134f631356e8d1fdee31f1bb7680d9a444d799376e00268228ef +size 160913 diff --git a/assets/0002Falsche Nebenstation.mp3 b/assets/0002Falsche Nebenstation.mp3 deleted file mode 100644 index 57fdfb8..0000000 --- a/assets/0002Falsche Nebenstation.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7782fc7bc2ff00baaf2e59109efe8f6cb7c4485c3e90bc5d215186b97c289ec -size 30238 diff --git a/assets/0002OnTheMoveMainStation.mp3 b/assets/0002OnTheMoveMainStation.mp3 new file mode 100644 index 0000000..a031088 --- /dev/null +++ b/assets/0002OnTheMoveMainStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77f0d5058b2cec382951dd88e4d1cb34b62f083bcb0ac466895d93a34b47152a +size 788688 diff --git a/assets/0003StationOneCorrect.mp3 b/assets/0003StationOneCorrect.mp3 new file mode 100644 index 0000000..74cf115 --- /dev/null +++ b/assets/0003StationOneCorrect.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:412db447abde936505799b6b1db4b382712c890ee333a8d52b8568fd844d818e +size 496952 diff --git a/assets/0003Timer abgelaufen.mp3 b/assets/0003Timer abgelaufen.mp3 deleted file mode 100644 index bc0a18c..0000000 --- a/assets/0003Timer abgelaufen.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98ea95f497c3d77e312c953ee5d0d317026b1317976491b9ae2c488524cd418c -size 27690 diff --git a/assets/0004Spiel gewonnen.mp3 b/assets/0004Spiel gewonnen.mp3 deleted file mode 100644 index b4e6662..0000000 --- a/assets/0004Spiel gewonnen.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aacc30cf4fee2d815ecb8d5374d527ab23534c54656a88fb4acf567a2f043e28 -size 25864 diff --git a/assets/0004StationTwoCorrekt.mp3 b/assets/0004StationTwoCorrekt.mp3 new file mode 100644 index 0000000..4924619 --- /dev/null +++ b/assets/0004StationTwoCorrekt.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0876d584bd52ff56d25fe38ff8fc33e90c2a10bd56f3879bc503954554e8802f +size 529135 diff --git a/assets/0005Objekt von der Nebenstation entfernt.mp3 b/assets/0005Objekt von der Nebenstation entfernt.mp3 deleted file mode 100644 index 429bfd9..0000000 --- a/assets/0005Objekt von der Nebenstation entfernt.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:51c18a31dd9f7b338ae2c43a41eaeb53f0a649b13fe7f750ecb01adaad991971 -size 37334 diff --git a/assets/0005StationTreeCorrekt.mp3 b/assets/0005StationTreeCorrekt.mp3 new file mode 100644 index 0000000..c765810 --- /dev/null +++ b/assets/0005StationTreeCorrekt.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02f59fbe2074954153b434e0d5ee7ed9ccf1a04e38aa138f7d7e036b28b87b4a +size 577200 diff --git a/assets/0006Objekt von der Haupstation entfernt.mp3 b/assets/0006Objekt von der Haupstation entfernt.mp3 deleted file mode 100644 index 14364b6..0000000 --- a/assets/0006Objekt von der Haupstation entfernt.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f4be3d3e0130a9825cb388959e15d2d103776cf495c32fdadf6b016d265e27a0 -size 41317 diff --git a/assets/0006StationFourCorrekt.mp3 b/assets/0006StationFourCorrekt.mp3 new file mode 100644 index 0000000..3352851 --- /dev/null +++ b/assets/0006StationFourCorrekt.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f976b55be7544fd935cbd6641814b6ad68d159f0196d1beb5c325f8398000eb9 +size 560900 diff --git a/assets/0007Objekt auf Hauptstation.mp3 b/assets/0007Objekt auf Hauptstation.mp3 deleted file mode 100644 index 2e22898..0000000 --- a/assets/0007Objekt auf Hauptstation.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6e1c509f2c3ea6329e37c57c120c44dae7c4814e045a048af5b2dd5e2917f71a -size 31153 diff --git a/assets/0007WrongStation.mp3 b/assets/0007WrongStation.mp3 new file mode 100644 index 0000000..8a9302a --- /dev/null +++ b/assets/0007WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db9a11bf61e119fa54e2c1076c927a9f5e7ae8456bccbc218f93b6f9e977f40f +size 112848 diff --git a/assets/0008Ende.mp3 b/assets/0008Ende.mp3 new file mode 100644 index 0000000..7189145 --- /dev/null +++ b/assets/0008Ende.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72ae1487606e80e17c1feaf3d658bda123fd5899ecc093cd7b1f76c2a30395a9 +size 433004 diff --git a/assets/0008Objekt auf einer Nebenstation.mp3 b/assets/0008Objekt auf einer Nebenstation.mp3 deleted file mode 100644 index 6f424a4..0000000 --- a/assets/0008Objekt auf einer Nebenstation.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:df11e75f9b213b6bf05230ca4a7854f50a54effd4c02cd2c7182f7681c96b4e8 -size 31711 diff --git a/assets/0009Stress.mp3 b/assets/0009Stress.mp3 new file mode 100644 index 0000000..be66a66 --- /dev/null +++ b/assets/0009Stress.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b397a70389e33a600499e5099f877aae097efee606fb5c4d20a26d99deac2e3c +size 177213 diff --git a/assets/0010StationTwoPickUp-240528_1353.mp3 b/assets/0010StationTwoPickUp-240528_1353.mp3 new file mode 100644 index 0000000..193d8db --- /dev/null +++ b/assets/0010StationTwoPickUp-240528_1353.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ce4b76e0861a9b09d0c0eb91887c57d725f00f7679077eab78307b4b00dc8d7 +size 114101 diff --git a/assets/0011StationTreePickup-240528_1354.mp3 b/assets/0011StationTreePickup-240528_1354.mp3 new file mode 100644 index 0000000..9673539 --- /dev/null +++ b/assets/0011StationTreePickup-240528_1354.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5715202057ba2064c689c4aece267dc05dbb779c4646ac59a4bbfd7814ebaa4c +size 168854 diff --git a/assets/0012StationFourPickUp-240528_1357.mp3 b/assets/0012StationFourPickUp-240528_1357.mp3 new file mode 100644 index 0000000..6db8374 --- /dev/null +++ b/assets/0012StationFourPickUp-240528_1357.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:531ff46ad49986287d2a460b283d225cf0514b2318202af6ffe175e939867559 +size 152972 diff --git a/src/main.cpp b/src/main.cpp index 6b6f09f..ccb1681 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,15 +19,14 @@ // The library in use is slightly modified to fix this issue: https://github.com/adafruit/Adafruit-PN532/issues/117 Adafruit_PN532 nfc(PN532_CS); -constexpr int rs = 9, en = 8, d4 = 5, d5 = 4, d6 = 3, d7 = 2; -LiquidCrystal lcd(rs, en, d4, d5, d6, d7); +LiquidCrystal lcd(9, 8, 5, 4, 3, 2); SoftwareSerial softSerial(6, 7); // RX, TX DFRobotDFPlayerMini dfPlayer; Station stations[5] = { - Station(1680767519, "Yellow"), Station(3346823711, "Green"), Station(3569318175, "Pink"), - Station(2174777887, "Blue"), Station(102951984, "Weird") + Station(1680767519, "Main"), Station(102951984, "Muhvella"), Station(3346823711, "Magie"), + Station(2174777887, "Bicola"), Station(3569318175, "Tobione") }; Station* currentStation; int currentStationIndex = -1; @@ -158,17 +157,17 @@ void loop(void) void setup(void) { - initSequences(); - setupNFC(); Serial.begin(9600); // Serial.println(ON_MAIN_STATION.getEntryAtIndex(0)->isAudio); + setupNFC(); lcd.begin(16, 2); + stationDetectionTimer.start(); softSerial.begin(9600); - + lcd.print("Initializing MP3"); lcd.clear(); if (!dfPlayer.begin(softSerial, true, true)) @@ -182,6 +181,6 @@ void setup(void) // printMp3Detail(&lcd, dfPlayer.readType(), dfPlayer.read()); } } - - dfPlayer.volume(7); + + dfPlayer.volume(18); } diff --git a/src/sequences.cpp b/src/sequences.cpp index 38a9968..896a4da 100644 --- a/src/sequences.cpp +++ b/src/sequences.cpp @@ -2,20 +2,3 @@ #include "sequence.h" #include "sounds.h" -// Define global sequences -Sequence ON_MAIN_STATION(0); -Sequence AFTER_MAIN_STATION(0); -Sequence HACKING_STATION_MUHVELLA(0); -Sequence AFTER_MUHVELLA(0); -Sequence HACKING_STATION_MAGIE(0); -Sequence AFTER_MAGIE(0); -Sequence HACKING_STATION_BICOLA(0); -Sequence AFTER_BICOLA(0); -Sequence HACKING_STATION_TOBIONE(0); -Sequence AFTER_TOBIONE(0); - -void initSequences() { - ON_MAIN_STATION.addEntry(SequenceEntry(true, 1000, IDLE)); - ON_MAIN_STATION.addEntry(SequenceEntry(false, 3000, 0)); - ON_MAIN_STATION.addEntry(SequenceEntry(true, 1000, IDLE)); -} \ No newline at end of file diff --git a/src/sequences.h b/src/sequences.h index d2c9690..1ce68e7 100644 --- a/src/sequences.h +++ b/src/sequences.h @@ -1,16 +1,3 @@ #pragma once #include "sequences.h" #include "sequence.h" - -extern Sequence ON_MAIN_STATION; -extern Sequence AFTER_MAIN_STATION; -extern Sequence HACKING_STATION_MUHVELLA; -extern Sequence AFTER_MUHVELLA; -extern Sequence HACKING_STATION_MAGIE; -extern Sequence AFTER_MAGIE; -extern Sequence HACKING_STATION_BICOLA; -extern Sequence AFTER_BICOLA; -extern Sequence HACKING_STATION_TOBIONE; -extern Sequence AFTER_TOBIONE; - -void initSequences(); diff --git a/src/sounds.h b/src/sounds.h index 8ae16ad..8c1c6aa 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1,11 +1,26 @@ #include #define NONE 0 -#define IDLE 1 -#define WRONG_SIDE_STATION 2 -#define TIMER_DOWN 3 -#define GAME_WON 4 -#define REMOVED_FROM_SIDE_STATION 5 -#define REMOVED_FROM_MAIN_STATION 6 -#define OBJECT_PLACED_ON_MAIN_STATION 7 -#define OBJECT_PLACED_ON_SIDE_STATION 8 +#define WAITING_FOR_GAME_START 1 +#define AFTER_MAIN_STATION 2 + +#define HACKING_STATION_MUHVELLA 3 +#define PICKUP_STATION_MUHVELLA 10 +#define AFTER_MUHVELLA 10 // incorrect + +#define HACKING_STATION_MAGIE 4 +#define PICKUP_STATION_MAGIE 11 +#define AFTER_MAGIE 11 // incorrect + +#define HACKING_STATION_BICOLA 5 +#define PICKUP_STATION_BICOLA 12 +#define AFTER_BICOLA 12 // incorrect + +#define HACKING_STATION_TOBIONE 6 +#define PICKUP_STATION_TOBIONE 13 +#define AFTER_TOBIONE 13 // incorrect + +#define END 8 +#define WRONG_STATION 7 + +#define STRESS 9 diff --git a/src/state.h b/src/state.h index 7615be1..a9774c5 100644 --- a/src/state.h +++ b/src/state.h @@ -10,23 +10,9 @@ class Station; class State { -private: - Timer sequenceTimer; - bool currentEntryHandled; - -protected: - Sequence* sequence; - int sequencePointer; - bool sequenceDone; - public: virtual ~State() = default; - explicit State(Sequence* sequence): sequenceTimer(Timer(0, false)), currentEntryHandled(false), sequence(sequence), - sequencePointer(0), sequenceDone(false) - { - } - virtual State* pickedUp(Context context) { return this; } virtual State* putDown(Context context, Station* newStation) { return this; } virtual State* update(Context context) { return this; } @@ -37,37 +23,4 @@ public: virtual String updateDisplay() { return ""; } - void updateSeqence(const Context& context) - { - if (sequence == nullptr || sequence->getLength() == 0) - { - return; - } - if (!currentEntryHandled) - { - const auto currentEntry = sequence->getEntryAtIndex(sequencePointer); - const auto addressInt = reinterpret_cast(currentEntry); - this->sequenceTimer = Timer(currentEntry->duration, false); - this->sequenceTimer.start(); - if (currentEntry->isAudio) - { - // printMp3Detail(context.dfPlayer->readType(), context.dfPlayer->read()); - context.dfPlayer->play(currentEntry->audioId); - } - currentEntryHandled = true; - } - const bool timeOut = this->sequenceTimer.update(context.delta); - if (timeOut) - { - if (this->sequencePointer + 2 >= this->sequence->getLength()) - { - sequencePointer = sequence->getRepeatIndex(); - sequenceDone = true; - currentEntryHandled = false; - return; - } - this->sequencePointer += 1; - currentEntryHandled = false; - } - } }; diff --git a/src/states.cpp b/src/states.cpp index e717945..ef9a478 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -6,9 +6,7 @@ #include "station.h" #include "sounds.h" -Startup::Startup(): State(nullptr) -{ -} +Startup::Startup() = default; State* Startup::putDown(const Context context, Station* newStation) { @@ -23,7 +21,6 @@ State* Startup::putDown(const Context context, Station* newStation) State* Startup::update(const Context context) { - updateSeqence(context); return this; } @@ -41,20 +38,15 @@ void Startup::activated(Context context) //------------------------------------ -WaitingForGameStart::WaitingForGameStart() : State(&ON_MAIN_STATION) -{ -} +WaitingForGameStart::WaitingForGameStart() = default; State* WaitingForGameStart::pickedUp(const Context context) { - return new OnTheMove(context.stations + 1, &AFTER_MAIN_STATION); // first element of the array + return new OnTheMove(context.stations + 1); // first element of the array } State* WaitingForGameStart::update(const Context context) { - // Serial.println("update wait"); - updateSeqence(context); - // Serial.println("after"); return this; } @@ -66,14 +58,14 @@ String WaitingForGameStart::updateDisplay() void WaitingForGameStart::activated(const Context context) { - context.dfPlayer->play(IDLE); + context.dfPlayer->loop(WAITING_FOR_GAME_START); } //------------------------------------ -OnTheMove::OnTheMove(Station* targetStation, Sequence* sequence): State(sequence) +OnTheMove::OnTheMove(Station* targetStation) { this->targetStation = targetStation; } @@ -86,7 +78,7 @@ State* OnTheMove::putDown(const Context context, Station* newStation) { return new End(); } - return new Hacking(this->targetStation, nullptr); + return new Hacking(this->targetStation); } return new IncorrectStation(targetStation); @@ -94,7 +86,6 @@ State* OnTheMove::putDown(const Context context, Station* newStation) State* OnTheMove::update(const Context context) { - updateSeqence(context); return this; } @@ -108,11 +99,19 @@ void OnTheMove::activated(const Context context) const int index = context.getStationIndex(this->targetStation); if (index == 1) { - context.dfPlayer->play(REMOVED_FROM_MAIN_STATION); + context.dfPlayer->play(AFTER_MAIN_STATION); } - else + else if (index == 2) { - context.dfPlayer->play(REMOVED_FROM_SIDE_STATION); + context.dfPlayer->play(AFTER_MUHVELLA); + } + else if (index == 3) + { + context.dfPlayer->play(AFTER_MAGIE); + } + else if (index == 4) + { + context.dfPlayer->play(AFTER_BICOLA); } } @@ -120,19 +119,18 @@ void OnTheMove::activated(const Context context) //------------------------------------ -IncorrectStation::IncorrectStation(Station* targetStation): State(nullptr) // todo remove nullptr +IncorrectStation::IncorrectStation(Station* targetStation) { this->targetStation = targetStation; } State* IncorrectStation::pickedUp(const Context context) { - return new OnTheMove(this->targetStation, nullptr); // todo fix nullptr + return new OnTheMove(this->targetStation); // todo fix nullptr } State* IncorrectStation::update(const Context context) { - updateSeqence(context); return this; } @@ -141,15 +139,16 @@ String IncorrectStation::updateDisplay() return "Wrong Station! "; } -void IncorrectStation::activated(Context context) +void IncorrectStation::activated(const Context context) { + context.dfPlayer->play(WRONG_STATION); } //------------------------------------ -Hacking::Hacking(Station* currentStation, Sequence* sequence): State(sequence), timer(5000, false) +Hacking::Hacking(Station* currentStation): timer(5000, false) { this->currentStation = currentStation; this->timer.start(); @@ -162,7 +161,6 @@ State* Hacking::pickedUp(const Context context) State* Hacking::update(const Context context) { - updateSeqence(context); Serial.println(String(context.delta)); const bool done = timer.update(context.delta); @@ -171,7 +169,7 @@ State* Hacking::update(const Context context) const int currentStationIndex = context.getStationIndex(currentStation); const int nextStationIndex = (currentStationIndex + 1) % context.stationCount; Station* nextStation = context.stations + nextStationIndex; - return new WaitingForPickup(currentStation, nextStation, nullptr); + return new WaitingForPickup(currentStation, nextStation); } return this; } @@ -183,14 +181,35 @@ String Hacking::updateDisplay() void Hacking::activated(const Context context) { - context.dfPlayer->play(OBJECT_PLACED_ON_MAIN_STATION); + auto currentStationIndex = context.getStationIndex(this->currentStation); + if (currentStationIndex == 1) + { + timer = Timer(31000, false); + context.dfPlayer->play(HACKING_STATION_MUHVELLA); + } + if (currentStationIndex == 2) + { + timer = Timer(33000, false); + context.dfPlayer->play(HACKING_STATION_MAGIE); + } + if (currentStationIndex == 3) + { + timer = Timer(36000, false); + context.dfPlayer->play(HACKING_STATION_BICOLA); + } + if (currentStationIndex == 4) + { + timer = Timer(35000, false); + context.dfPlayer->play(HACKING_STATION_TOBIONE); + } + timer.start(); } //------------------------------------ -Complain::Complain(Station* currentStation): State(nullptr) +Complain::Complain(Station* currentStation) { this->currentStation = currentStation; } @@ -199,7 +218,7 @@ State* Complain::putDown(const Context context, Station* newStation) { if (this->currentStation == newStation) // was put back on correct station { - return new Hacking(this->currentStation, nullptr); + return new Hacking(this->currentStation); } return this; // was not put back on correct station (just keeps complaining) } @@ -222,7 +241,7 @@ void Complain::activated(Context context) //------------------------------------ -WaitingForPickup::WaitingForPickup(Station* currentStation, Station* targetStation, Sequence* sequence): State(sequence) +WaitingForPickup::WaitingForPickup(Station* currentStation, Station* targetStation) { this->currentStation = currentStation; this->targetStation = targetStation; @@ -230,7 +249,7 @@ WaitingForPickup::WaitingForPickup(Station* currentStation, Station* targetStati State* WaitingForPickup::pickedUp(Context context) { - return new OnTheMove(targetStation, nullptr); + return new OnTheMove(targetStation); } State* WaitingForPickup::update(Context context) @@ -245,17 +264,30 @@ String WaitingForPickup::updateDisplay() void WaitingForPickup::activated(Context context) { + auto currentStationIndex = context.getStationIndex(this->currentStation); + if (currentStationIndex == 1) + { + context.dfPlayer->loop(PICKUP_STATION_MUHVELLA); + } + if (currentStationIndex == 2) + { + context.dfPlayer->loop(PICKUP_STATION_MAGIE); + } + if (currentStationIndex == 3) + { + context.dfPlayer->loop(PICKUP_STATION_BICOLA); + } + if (currentStationIndex == 4) + { + context.dfPlayer->loop(PICKUP_STATION_TOBIONE); + } } //------------------------------------ -End::End():State(nullptr) -{ - -} - +End::End() = default; State* End::pickedUp(const Context context) { @@ -276,5 +308,5 @@ String End::updateDisplay() void End::activated(const Context context) { - context.dfPlayer->play(GAME_WON); + context.dfPlayer->play(END); } diff --git a/src/states.h b/src/states.h index 12a05d6..f89b856 100644 --- a/src/states.h +++ b/src/states.h @@ -33,7 +33,7 @@ private: Station* targetStation; public: - explicit OnTheMove(Station* targetStation, Sequence* sequence); + explicit OnTheMove(Station* targetStation); State* putDown(Context context, Station* newStation) override; State* update(Context context) override; String updateDisplay() override; @@ -61,7 +61,7 @@ private: Station* currentStation; public: - explicit Hacking(Station* currentStation, Sequence* sequence); + explicit Hacking(Station* currentStation); State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; @@ -88,7 +88,7 @@ private: Station* targetStation; public: - WaitingForPickup(Station* currentStation, Station* targetStation, Sequence* sequence); + WaitingForPickup(Station* currentStation, Station* targetStation); State* pickedUp(Context context) override; State* update(Context context) override; String updateDisplay() override; From 47195d9445ecb457157ca62d0a00dec97f8e1579 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 3 Jun 2024 02:34:26 +0200 Subject: [PATCH 08/14] adds new audio files and updates durations --- assets/0001WaitingForGameStart.mp3 | 4 +-- assets/0002AfterMainstation.mp3 | 3 +++ assets/0002OnTheMoveMainStation.mp3 | 3 --- assets/0003HackingStationMuhvella.mp3 | 3 +++ assets/0003StationOneCorrect.mp3 | 3 --- assets/0004PickupStationMuhvella.mp3 | 3 +++ assets/0004StationTwoCorrekt.mp3 | 3 --- assets/0005AfterStationMuhvella.mp3 | 3 +++ assets/0005StationTreeCorrekt.mp3 | 3 --- assets/0006HackingStationMagie.mp3 | 3 +++ assets/0006StationFourCorrekt.mp3 | 3 --- assets/0007PickupStationMagie.mp3 | 3 +++ assets/0007WrongStation.mp3 | 3 --- assets/0008AfterSationMagie.mp3 | 3 +++ assets/0008Ende.mp3 | 3 --- assets/0009HackingStationBicola.mp3 | 3 +++ assets/0009Stress.mp3 | 3 --- assets/0010PickupStationBicola.mp3 | 3 +++ assets/0010StationTwoPickUp-240528_1353.mp3 | 3 --- assets/0011AfterStationBicola.mp3 | 3 +++ assets/0011StationTreePickup-240528_1354.mp3 | 3 --- assets/0012HackingStationTobione.mp3 | 3 +++ assets/0012StationFourPickUp-240528_1357.mp3 | 3 --- assets/0013PickupStationTobione.mp3 | 3 +++ assets/0014AfterTobione.mp3 | 3 +++ assets/0015End.mp3 | 3 +++ assets/0016WrongStation.mp3 | 3 +++ src/sounds.h | 26 +++++++++----------- src/states.cpp | 23 +++++++++++------ src/states.h | 2 ++ 30 files changed, 76 insertions(+), 57 deletions(-) create mode 100644 assets/0002AfterMainstation.mp3 delete mode 100644 assets/0002OnTheMoveMainStation.mp3 create mode 100644 assets/0003HackingStationMuhvella.mp3 delete mode 100644 assets/0003StationOneCorrect.mp3 create mode 100644 assets/0004PickupStationMuhvella.mp3 delete mode 100644 assets/0004StationTwoCorrekt.mp3 create mode 100644 assets/0005AfterStationMuhvella.mp3 delete mode 100644 assets/0005StationTreeCorrekt.mp3 create mode 100644 assets/0006HackingStationMagie.mp3 delete mode 100644 assets/0006StationFourCorrekt.mp3 create mode 100644 assets/0007PickupStationMagie.mp3 delete mode 100644 assets/0007WrongStation.mp3 create mode 100644 assets/0008AfterSationMagie.mp3 delete mode 100644 assets/0008Ende.mp3 create mode 100644 assets/0009HackingStationBicola.mp3 delete mode 100644 assets/0009Stress.mp3 create mode 100644 assets/0010PickupStationBicola.mp3 delete mode 100644 assets/0010StationTwoPickUp-240528_1353.mp3 create mode 100644 assets/0011AfterStationBicola.mp3 delete mode 100644 assets/0011StationTreePickup-240528_1354.mp3 create mode 100644 assets/0012HackingStationTobione.mp3 delete mode 100644 assets/0012StationFourPickUp-240528_1357.mp3 create mode 100644 assets/0013PickupStationTobione.mp3 create mode 100644 assets/0014AfterTobione.mp3 create mode 100644 assets/0015End.mp3 create mode 100644 assets/0016WrongStation.mp3 diff --git a/assets/0001WaitingForGameStart.mp3 b/assets/0001WaitingForGameStart.mp3 index 770243b..7496557 100644 --- a/assets/0001WaitingForGameStart.mp3 +++ b/assets/0001WaitingForGameStart.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec88a517b806134f631356e8d1fdee31f1bb7680d9a444d799376e00268228ef -size 160913 +oid sha256:21bf168a9f7630f5c48687a15fbd3d6bdd303b5dabecaea76ac2e994f8839fef +size 889344 diff --git a/assets/0002AfterMainstation.mp3 b/assets/0002AfterMainstation.mp3 new file mode 100644 index 0000000..e81d240 --- /dev/null +++ b/assets/0002AfterMainstation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63371bb5c9d3b2a91588a31de26049c365c737e1561d5a6ea93b20b173bf18ba +size 797184 diff --git a/assets/0002OnTheMoveMainStation.mp3 b/assets/0002OnTheMoveMainStation.mp3 deleted file mode 100644 index a031088..0000000 --- a/assets/0002OnTheMoveMainStation.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:77f0d5058b2cec382951dd88e4d1cb34b62f083bcb0ac466895d93a34b47152a -size 788688 diff --git a/assets/0003HackingStationMuhvella.mp3 b/assets/0003HackingStationMuhvella.mp3 new file mode 100644 index 0000000..e9f1861 --- /dev/null +++ b/assets/0003HackingStationMuhvella.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5fc464f5809f594a3b9ab8994017de1dbc8b99c5ec78f59a253641f3454e5f0 +size 401280 diff --git a/assets/0003StationOneCorrect.mp3 b/assets/0003StationOneCorrect.mp3 deleted file mode 100644 index 74cf115..0000000 --- a/assets/0003StationOneCorrect.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:412db447abde936505799b6b1db4b382712c890ee333a8d52b8568fd844d818e -size 496952 diff --git a/assets/0004PickupStationMuhvella.mp3 b/assets/0004PickupStationMuhvella.mp3 new file mode 100644 index 0000000..5c37542 --- /dev/null +++ b/assets/0004PickupStationMuhvella.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d85ce2880b0d24df851080426b124527896803acf5143e9ba4c1a5e16243ec3 +size 277248 diff --git a/assets/0004StationTwoCorrekt.mp3 b/assets/0004StationTwoCorrekt.mp3 deleted file mode 100644 index 4924619..0000000 --- a/assets/0004StationTwoCorrekt.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0876d584bd52ff56d25fe38ff8fc33e90c2a10bd56f3879bc503954554e8802f -size 529135 diff --git a/assets/0005AfterStationMuhvella.mp3 b/assets/0005AfterStationMuhvella.mp3 new file mode 100644 index 0000000..4ccf604 --- /dev/null +++ b/assets/0005AfterStationMuhvella.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:624d43f1a8a05041684cdd9fa3dbf8a23e8775ee57eed7f69ed995ec05cb3beb +size 445440 diff --git a/assets/0005StationTreeCorrekt.mp3 b/assets/0005StationTreeCorrekt.mp3 deleted file mode 100644 index c765810..0000000 --- a/assets/0005StationTreeCorrekt.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:02f59fbe2074954153b434e0d5ee7ed9ccf1a04e38aa138f7d7e036b28b87b4a -size 577200 diff --git a/assets/0006HackingStationMagie.mp3 b/assets/0006HackingStationMagie.mp3 new file mode 100644 index 0000000..53138cf --- /dev/null +++ b/assets/0006HackingStationMagie.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4521f8ca1193c391a097baecc27038241d351ab671ae53f62db697bcd4dcec48 +size 469248 diff --git a/assets/0006StationFourCorrekt.mp3 b/assets/0006StationFourCorrekt.mp3 deleted file mode 100644 index 3352851..0000000 --- a/assets/0006StationFourCorrekt.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f976b55be7544fd935cbd6641814b6ad68d159f0196d1beb5c325f8398000eb9 -size 560900 diff --git a/assets/0007PickupStationMagie.mp3 b/assets/0007PickupStationMagie.mp3 new file mode 100644 index 0000000..0ac1a7e --- /dev/null +++ b/assets/0007PickupStationMagie.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:afa71a30311abaf5b82aa57a1ed6af889fb5c044e16f2848e9a134c94f29811b +size 477312 diff --git a/assets/0007WrongStation.mp3 b/assets/0007WrongStation.mp3 deleted file mode 100644 index 8a9302a..0000000 --- a/assets/0007WrongStation.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:db9a11bf61e119fa54e2c1076c927a9f5e7ae8456bccbc218f93b6f9e977f40f -size 112848 diff --git a/assets/0008AfterSationMagie.mp3 b/assets/0008AfterSationMagie.mp3 new file mode 100644 index 0000000..10b4b1a --- /dev/null +++ b/assets/0008AfterSationMagie.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b5c44ebde9933b5be6ab494ac80708a681902cd4dcf3f9de7eaf8645e64c44a +size 425472 diff --git a/assets/0008Ende.mp3 b/assets/0008Ende.mp3 deleted file mode 100644 index 7189145..0000000 --- a/assets/0008Ende.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:72ae1487606e80e17c1feaf3d658bda123fd5899ecc093cd7b1f76c2a30395a9 -size 433004 diff --git a/assets/0009HackingStationBicola.mp3 b/assets/0009HackingStationBicola.mp3 new file mode 100644 index 0000000..601b40e --- /dev/null +++ b/assets/0009HackingStationBicola.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af7c1d630d6949c577cfeffccb91169080da5be83328e116b4d7a61700094c76 +size 588480 diff --git a/assets/0009Stress.mp3 b/assets/0009Stress.mp3 deleted file mode 100644 index be66a66..0000000 --- a/assets/0009Stress.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b397a70389e33a600499e5099f877aae097efee606fb5c4d20a26d99deac2e3c -size 177213 diff --git a/assets/0010PickupStationBicola.mp3 b/assets/0010PickupStationBicola.mp3 new file mode 100644 index 0000000..8428882 --- /dev/null +++ b/assets/0010PickupStationBicola.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3544ff936304b0e53af972d146c7cb9e9f8b77c992f0fe5ccb5d2c463e78bb88 +size 1271520 diff --git a/assets/0010StationTwoPickUp-240528_1353.mp3 b/assets/0010StationTwoPickUp-240528_1353.mp3 deleted file mode 100644 index 193d8db..0000000 --- a/assets/0010StationTwoPickUp-240528_1353.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7ce4b76e0861a9b09d0c0eb91887c57d725f00f7679077eab78307b4b00dc8d7 -size 114101 diff --git a/assets/0011AfterStationBicola.mp3 b/assets/0011AfterStationBicola.mp3 new file mode 100644 index 0000000..323a23a --- /dev/null +++ b/assets/0011AfterStationBicola.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f71d507d83a59ac45bee44b5e8849507c80136edec61724770d7936f308cd8e2 +size 511680 diff --git a/assets/0011StationTreePickup-240528_1354.mp3 b/assets/0011StationTreePickup-240528_1354.mp3 deleted file mode 100644 index 9673539..0000000 --- a/assets/0011StationTreePickup-240528_1354.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5715202057ba2064c689c4aece267dc05dbb779c4646ac59a4bbfd7814ebaa4c -size 168854 diff --git a/assets/0012HackingStationTobione.mp3 b/assets/0012HackingStationTobione.mp3 new file mode 100644 index 0000000..422720e --- /dev/null +++ b/assets/0012HackingStationTobione.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0974c1d83bf05487c95360542ab9573713397c0d7bc83adfbf1efd055df3819a +size 791520 diff --git a/assets/0012StationFourPickUp-240528_1357.mp3 b/assets/0012StationFourPickUp-240528_1357.mp3 deleted file mode 100644 index 6db8374..0000000 --- a/assets/0012StationFourPickUp-240528_1357.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:531ff46ad49986287d2a460b283d225cf0514b2318202af6ffe175e939867559 -size 152972 diff --git a/assets/0013PickupStationTobione.mp3 b/assets/0013PickupStationTobione.mp3 new file mode 100644 index 0000000..f299b45 --- /dev/null +++ b/assets/0013PickupStationTobione.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25b07721ba9e5f27ba728a1f7f05075af1ff95ed315345a4a3d0b61f60f102fb +size 1421760 diff --git a/assets/0014AfterTobione.mp3 b/assets/0014AfterTobione.mp3 new file mode 100644 index 0000000..1358d34 --- /dev/null +++ b/assets/0014AfterTobione.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:278d81deba822df2df47b773c7a01529beae7ebb492df8dbc1eb9a24ecfb92d3 +size 441600 diff --git a/assets/0015End.mp3 b/assets/0015End.mp3 new file mode 100644 index 0000000..11dedcd --- /dev/null +++ b/assets/0015End.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1db9be7221cb13fe7451b854b9853c18fc001c93ac463db08a1585dcb3da8f4 +size 741600 diff --git a/assets/0016WrongStation.mp3 b/assets/0016WrongStation.mp3 new file mode 100644 index 0000000..fbcc67a --- /dev/null +++ b/assets/0016WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8b3f3890eec7ce0a9e2984e9fc205e4d5daa62953a00d7fe719227e8cb81ae4 +size 772805 diff --git a/src/sounds.h b/src/sounds.h index 8c1c6aa..8bdda9b 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -5,22 +5,20 @@ #define AFTER_MAIN_STATION 2 #define HACKING_STATION_MUHVELLA 3 -#define PICKUP_STATION_MUHVELLA 10 -#define AFTER_MUHVELLA 10 // incorrect +#define PICKUP_STATION_MUHVELLA 4 +#define AFTER_MUHVELLA 5 -#define HACKING_STATION_MAGIE 4 -#define PICKUP_STATION_MAGIE 11 -#define AFTER_MAGIE 11 // incorrect +#define HACKING_STATION_MAGIE 6 +#define PICKUP_STATION_MAGIE 7 +#define AFTER_MAGIE 8 -#define HACKING_STATION_BICOLA 5 -#define PICKUP_STATION_BICOLA 12 -#define AFTER_BICOLA 12 // incorrect +#define HACKING_STATION_BICOLA 9 +#define PICKUP_STATION_BICOLA 10 +#define AFTER_BICOLA 11 -#define HACKING_STATION_TOBIONE 6 +#define HACKING_STATION_TOBIONE 12 #define PICKUP_STATION_TOBIONE 13 -#define AFTER_TOBIONE 13 // incorrect +#define AFTER_TOBIONE 14 -#define END 8 -#define WRONG_STATION 7 - -#define STRESS 9 +#define END 15 +#define WRONG_STATION 16 diff --git a/src/states.cpp b/src/states.cpp index ef9a478..db5f72a 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -184,22 +184,22 @@ void Hacking::activated(const Context context) auto currentStationIndex = context.getStationIndex(this->currentStation); if (currentStationIndex == 1) { - timer = Timer(31000, false); + timer = Timer(25000, false); context.dfPlayer->play(HACKING_STATION_MUHVELLA); } if (currentStationIndex == 2) { - timer = Timer(33000, false); + timer = Timer(29000, false); context.dfPlayer->play(HACKING_STATION_MAGIE); } if (currentStationIndex == 3) { - timer = Timer(36000, false); + timer = Timer(29000, false); context.dfPlayer->play(HACKING_STATION_BICOLA); } if (currentStationIndex == 4) { - timer = Timer(35000, false); + timer = Timer(39000, false); context.dfPlayer->play(HACKING_STATION_TOBIONE); } timer.start(); @@ -287,17 +287,23 @@ void WaitingForPickup::activated(Context context) //------------------------------------ -End::End() = default; +End::End(): timer(Timer(0, false)) +{ +} State* End::pickedUp(const Context context) { - // todo what happens when we pick up here?... - return this; + // todo to be discussed if this is the expected outcome... + return new OnTheMove(context.stations + 1); // starting game early } State* End::update(const Context context) { - // todo after some time return waiting for game start... + bool done = timer.update(context.delta); + if (done) + { + return new WaitingForGameStart(); + } return this; } @@ -309,4 +315,5 @@ String End::updateDisplay() void End::activated(const Context context) { context.dfPlayer->play(END); + timer = Timer(27000, false); } diff --git a/src/states.h b/src/states.h index f89b856..ac28ee1 100644 --- a/src/states.h +++ b/src/states.h @@ -97,6 +97,8 @@ public: class End final : public State { +protected: + Timer timer; public: End(); State* pickedUp(Context context) override; From c8346ded11b774a6f5269ff04b4d462966818ebf Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 3 Jun 2024 04:50:54 +0200 Subject: [PATCH 09/14] fixes audiofiles and adds screentext --- assets/0007PickupStationMagie.mp3 | 2 +- assets/0008AfterSationMagie.mp3 | 3 - .../0008ObjektvonderNebenstationentfernt.mp3 | 3 + assets/0017AfterStationMagie.mp3 | 3 + src/main.cpp | 4 +- src/sounds.h | 2 +- src/state.h | 2 +- src/statemachine.cpp | 70 +++++----- src/statemachine.h | 2 +- src/states.cpp | 123 +++++++++++++----- src/states.h | 24 ++-- src/text.h | 29 +++++ 12 files changed, 184 insertions(+), 83 deletions(-) delete mode 100644 assets/0008AfterSationMagie.mp3 create mode 100644 assets/0008ObjektvonderNebenstationentfernt.mp3 create mode 100644 assets/0017AfterStationMagie.mp3 create mode 100644 src/text.h diff --git a/assets/0007PickupStationMagie.mp3 b/assets/0007PickupStationMagie.mp3 index 0ac1a7e..3fc7ada 100644 --- a/assets/0007PickupStationMagie.mp3 +++ b/assets/0007PickupStationMagie.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afa71a30311abaf5b82aa57a1ed6af889fb5c044e16f2848e9a134c94f29811b +oid sha256:d7dd0e1a62798011a83f7f0650d7b540d6d1a3fe54dcc9bca57eedee67dffe64 size 477312 diff --git a/assets/0008AfterSationMagie.mp3 b/assets/0008AfterSationMagie.mp3 deleted file mode 100644 index 10b4b1a..0000000 --- a/assets/0008AfterSationMagie.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b5c44ebde9933b5be6ab494ac80708a681902cd4dcf3f9de7eaf8645e64c44a -size 425472 diff --git a/assets/0008ObjektvonderNebenstationentfernt.mp3 b/assets/0008ObjektvonderNebenstationentfernt.mp3 new file mode 100644 index 0000000..429bfd9 --- /dev/null +++ b/assets/0008ObjektvonderNebenstationentfernt.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51c18a31dd9f7b338ae2c43a41eaeb53f0a649b13fe7f750ecb01adaad991971 +size 37334 diff --git a/assets/0017AfterStationMagie.mp3 b/assets/0017AfterStationMagie.mp3 new file mode 100644 index 0000000..fb09a94 --- /dev/null +++ b/assets/0017AfterStationMagie.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b618ea2a7b01e411f4f8236c7554cbaf846576a8d1dca9a5d5ad233f6382371 +size 429312 diff --git a/src/main.cpp b/src/main.cpp index ccb1681..678b1ed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -143,7 +143,7 @@ void loop(void) } stateMachine.update(Context(stations, sizeof(stations) / sizeof(stations[0]), deltaTime, &dfPlayer)); - String newContent = stateMachine.updateDisplay(); + String newContent = stateMachine.updateDisplay(Context(stations, sizeof(stations) / sizeof(stations[0]), deltaTime, &dfPlayer)); if (previousDisplayContent != newContent) { lcd.clear(); @@ -182,5 +182,5 @@ void setup(void) } } - dfPlayer.volume(18); + dfPlayer.volume(15); } diff --git a/src/sounds.h b/src/sounds.h index 8bdda9b..638dcb3 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -10,7 +10,7 @@ #define HACKING_STATION_MAGIE 6 #define PICKUP_STATION_MAGIE 7 -#define AFTER_MAGIE 8 +#define AFTER_MAGIE 17 // weird hack because file 8 does not seem to work... #define HACKING_STATION_BICOLA 9 #define PICKUP_STATION_BICOLA 10 diff --git a/src/state.h b/src/state.h index a9774c5..dae5be1 100644 --- a/src/state.h +++ b/src/state.h @@ -21,6 +21,6 @@ public: { } - virtual String updateDisplay() { return ""; } + virtual String updateDisplay(const Context& context) { return ""; } }; diff --git a/src/statemachine.cpp b/src/statemachine.cpp index 15142e6..7c98391 100644 --- a/src/statemachine.cpp +++ b/src/statemachine.cpp @@ -3,36 +3,44 @@ #include "context.h" StateMachine::StateMachine(State* initialState) - : currentState(initialState) {} - -StateMachine::~StateMachine() { - delete currentState; -} - -void StateMachine::updateState(State* newState, const Context& context) { - if (newState != currentState) { - delete currentState; - currentState = newState; - currentState->activated(context); - } -} - -void StateMachine::pickedUp(const Context& context) { - State* newState = currentState->pickedUp(context); - updateState(newState, context); -} - -void StateMachine::putDown(const Context& context, Station* newStation) { - State* newState = currentState->putDown(context, newStation); - updateState(newState, context); -} - -void StateMachine::update(const Context& context) { - State* newState = currentState->update(context); - updateState(newState, context); -} - -String StateMachine::updateDisplay() const + : currentState(initialState) { - return currentState->updateDisplay(); +} + +StateMachine::~StateMachine() +{ + delete currentState; +} + +void StateMachine::updateState(State* newState, const Context& context) +{ + if (newState != currentState) + { + delete currentState; + currentState = newState; + currentState->activated(context); + } +} + +void StateMachine::pickedUp(const Context& context) +{ + State* newState = currentState->pickedUp(context); + updateState(newState, context); +} + +void StateMachine::putDown(const Context& context, Station* newStation) +{ + State* newState = currentState->putDown(context, newStation); + updateState(newState, context); +} + +void StateMachine::update(const Context& context) +{ + State* newState = currentState->update(context); + updateState(newState, context); +} + +String StateMachine::updateDisplay(const Context& context) const +{ + return currentState->updateDisplay(context); } diff --git a/src/statemachine.h b/src/statemachine.h index 0f4c72c..e9209cd 100644 --- a/src/statemachine.h +++ b/src/statemachine.h @@ -17,5 +17,5 @@ public: void putDown(const Context& context, Station* newStation); void update(const Context& context); - String updateDisplay() const; + String updateDisplay(const Context& context) const; }; diff --git a/src/states.cpp b/src/states.cpp index db5f72a..6106eb0 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -1,10 +1,9 @@ #include "states.h" -#include - #include "state.h" #include "station.h" #include "sounds.h" +#include "text.h" Startup::Startup() = default; @@ -24,10 +23,9 @@ State* Startup::update(const Context context) return this; } -String Startup::updateDisplay() +String Startup::updateDisplay(const Context& context) { - return "Initializing:Put" - "on first station"; + return "Legg mi uf die ersti Station!"; } void Startup::activated(Context context) @@ -42,7 +40,7 @@ WaitingForGameStart::WaitingForGameStart() = default; State* WaitingForGameStart::pickedUp(const Context context) { - return new OnTheMove(context.stations + 1); // first element of the array + return new OnTheMove(context.stations + 1, context.stations); // first element of the array } State* WaitingForGameStart::update(const Context context) @@ -50,10 +48,9 @@ State* WaitingForGameStart::update(const Context context) return this; } -String WaitingForGameStart::updateDisplay() +String WaitingForGameStart::updateDisplay(const Context& context) { - return "Waiting for Game" - "Start..."; + return WAITING_FOR_START_TEXT; } void WaitingForGameStart::activated(const Context context) @@ -65,9 +62,10 @@ void WaitingForGameStart::activated(const Context context) //------------------------------------ -OnTheMove::OnTheMove(Station* targetStation) +OnTheMove::OnTheMove(Station* targetStation, Station* previousStation) { this->targetStation = targetStation; + this->previousStation = previousStation; } State* OnTheMove::putDown(const Context context, Station* newStation) @@ -81,7 +79,7 @@ State* OnTheMove::putDown(const Context context, Station* newStation) return new Hacking(this->targetStation); } - return new IncorrectStation(targetStation); + return new IncorrectStation(targetStation, previousStation); } State* OnTheMove::update(const Context context) @@ -89,15 +87,41 @@ State* OnTheMove::update(const Context context) return this; } -String OnTheMove::updateDisplay() +String OnTheMove::updateDisplay(const Context& context) { - return "On the Move... "; + const int index = context.getStationIndex(this->targetStation); + if (index == 0) + { + return TOBIONE_AFTER_TEXT; + } + else if (index == 1) + { + return MAIN_AFTER_PICKUP_TEXT; + } + else if (index == 2) + { + return MUHVELLA_AFTER_TEXT; + } + else if (index == 3) + { + return MAGIE_AFTER_TEXT; + } + else if (index == 4) + { + return BICOLA_AFTER_TEXT; + } + + return "UNDEFINED"; } void OnTheMove::activated(const Context context) { const int index = context.getStationIndex(this->targetStation); - if (index == 1) + if (index == 0) + { + context.dfPlayer->play(AFTER_TOBIONE); + } + else if (index == 1) { context.dfPlayer->play(AFTER_MAIN_STATION); } @@ -119,14 +143,15 @@ void OnTheMove::activated(const Context context) //------------------------------------ -IncorrectStation::IncorrectStation(Station* targetStation) +IncorrectStation::IncorrectStation(Station* targetStation, Station* previousStation) { this->targetStation = targetStation; + this->previousStation = previousStation; } State* IncorrectStation::pickedUp(const Context context) { - return new OnTheMove(this->targetStation); // todo fix nullptr + return new OnTheMove(this->targetStation, previousStation); } State* IncorrectStation::update(const Context context) @@ -134,9 +159,9 @@ State* IncorrectStation::update(const Context context) return this; } -String IncorrectStation::updateDisplay() +String IncorrectStation::updateDisplay(const Context& context) { - return "Wrong Station! "; + return WRONG_STATION_TEXT; } void IncorrectStation::activated(const Context context) @@ -174,9 +199,26 @@ State* Hacking::update(const Context context) return this; } -String Hacking::updateDisplay() +String Hacking::updateDisplay(const Context& context) { - return "Hacking B)"; + auto currentStationIndex = context.getStationIndex(this->currentStation); + if (currentStationIndex == 1) + { + return MUHVELLA_HACKING_TEXT; + } + if (currentStationIndex == 2) + { + return MAGIE_HACKING_TEXT; + } + if (currentStationIndex == 3) + { + return BICOLA_HACKING_TEXT; + } + if (currentStationIndex == 4) + { + return TOBIONE_HACKING_TEXT; + } + return "UNDEFINED"; } void Hacking::activated(const Context context) @@ -199,7 +241,7 @@ void Hacking::activated(const Context context) } if (currentStationIndex == 4) { - timer = Timer(39000, false); + timer = Timer(40000, false); context.dfPlayer->play(HACKING_STATION_TOBIONE); } timer.start(); @@ -228,9 +270,9 @@ State* Complain::update(Context context) return this; } -String Complain::updateDisplay() +String Complain::updateDisplay(const Context& context) { - return "Put me back!!"; + return COMPLAIN_TEXT; } void Complain::activated(Context context) @@ -249,7 +291,7 @@ WaitingForPickup::WaitingForPickup(Station* currentStation, Station* targetStati State* WaitingForPickup::pickedUp(Context context) { - return new OnTheMove(targetStation); + return new OnTheMove(targetStation, currentStation); } State* WaitingForPickup::update(Context context) @@ -257,9 +299,25 @@ State* WaitingForPickup::update(Context context) return this; } -String WaitingForPickup::updateDisplay() +String WaitingForPickup::updateDisplay(const Context& context) { - return "Pick me up "; + auto currentStationIndex = context.getStationIndex(this->currentStation); + if (currentStationIndex == 1) + { + return MUHVELLA_PICKUP_TEXT; + } + if (currentStationIndex == 2) + { + return MAGIE_PICKUP_TEXT; + } + if (currentStationIndex == 3) + { + return BICOLA_PICKUP_TEXT; + } + if (currentStationIndex == 4) + { + return TOBIONE_PICKUP_TEXT; + } } void WaitingForPickup::activated(Context context) @@ -293,13 +351,13 @@ End::End(): timer(Timer(0, false)) State* End::pickedUp(const Context context) { - // todo to be discussed if this is the expected outcome... - return new OnTheMove(context.stations + 1); // starting game early + // todo complain + return new Complain(context.stations); } State* End::update(const Context context) { - bool done = timer.update(context.delta); + const bool done = timer.update(context.delta); if (done) { return new WaitingForGameStart(); @@ -307,13 +365,14 @@ State* End::update(const Context context) return this; } -String End::updateDisplay() +String End::updateDisplay(const Context& context) { - return "END..."; + return END_TEXT; } void End::activated(const Context context) { context.dfPlayer->play(END); - timer = Timer(27000, false); + timer = Timer(38000, false); + timer.start(); } diff --git a/src/states.h b/src/states.h index ac28ee1..33b09a4 100644 --- a/src/states.h +++ b/src/states.h @@ -12,7 +12,7 @@ public: Startup(); State* putDown(Context context, Station* newStation) override; State* update(Context context) override; - String updateDisplay() override; + String updateDisplay(const Context& context) override; void activated(Context context) override; }; @@ -23,7 +23,7 @@ public: WaitingForGameStart(); State* pickedUp(Context context) override; State* update(Context context) override; - String updateDisplay() override; + String updateDisplay(const Context& context) override; void activated(Context context) override; }; @@ -31,12 +31,13 @@ class OnTheMove final : public State { private: Station* targetStation; - + Station* previousStation; + public: - explicit OnTheMove(Station* targetStation); + explicit OnTheMove(Station* targetStation, Station* previousStation); State* putDown(Context context, Station* newStation) override; State* update(Context context) override; - String updateDisplay() override; + String updateDisplay(const Context& context) override; void activated(Context context) override; }; @@ -44,12 +45,13 @@ class IncorrectStation final : public State { private: Station* targetStation; + Station* previousStation; public: - explicit IncorrectStation(Station* targetStation); + explicit IncorrectStation(Station* targetStation, Station* previousStation); State* pickedUp(Context context) override; State* update(Context context) override; - String updateDisplay() override; + String updateDisplay(const Context& context) override; void activated(Context context) override; }; @@ -64,7 +66,7 @@ public: explicit Hacking(Station* currentStation); State* pickedUp(Context context) override; State* update(Context context) override; - String updateDisplay() override; + String updateDisplay(const Context& context) override; void activated(Context context) override; }; @@ -77,7 +79,7 @@ public: explicit Complain(Station* currentStation); State* putDown(Context context, Station* newStation) override; State* update(Context context) override; - String updateDisplay() override; + String updateDisplay(const Context& context) override; void activated(Context context) override; }; @@ -91,7 +93,7 @@ public: WaitingForPickup(Station* currentStation, Station* targetStation); State* pickedUp(Context context) override; State* update(Context context) override; - String updateDisplay() override; + String updateDisplay(const Context& context) override; void activated(Context context) override; }; @@ -103,6 +105,6 @@ public: End(); State* pickedUp(Context context) override; State* update(Context context) override; - String updateDisplay() override; + String updateDisplay(const Context& context) override; void activated(Context context) override; }; diff --git a/src/text.h b/src/text.h new file mode 100644 index 0000000..47a8ea4 --- /dev/null +++ b/src/text.h @@ -0,0 +1,29 @@ +#pragma once + +#define WAITING_FOR_START_TEXT "Lupf mich uf" +#define MAIN_AFTER_PICKUP_TEXT "Danke :D <3 Lueg chli ume" + +#define MUHVELLA_HACKING_TEXT "Eifach ligge lahBitte warte" +#define MUHVELLA_PICKUP_TEXT "Los! Witer =O" +#define MUHVELLA_AFTER_TEXT "Ufd Suechi! Gids Suppe? :P" + +#define MAGIE_HACKING_TEXT "Wart rasch ^^ " +#define MAGIE_PICKUP_TEXT "Und witer gahts " +#define MAGIE_AFTER_TEXT "Yoga und Chrueter? :/" + +#define BICOLA_HACKING_TEXT "Mues chli sueche" +#define BICOLA_PICKUP_TEXT "Und witer!" +#define BICOLA_AFTER_TEXT "Fast gschafft :,)" + +#define TOBIONE_HACKING_TEXT "So nah und doch so fern -.-" +#define TOBIONE_PICKUP_TEXT "Endspurt!" +#define TOBIONE_AFTER_TEXT "goi mer wieder hei" + +#define WRONG_STATION_TEXT "falschi Station" +#define COMPLAIN_TEXT "Wieder zrug stelle" + +#define END_TEXT "DU HESCH ES GSCHAFFT!!!" + + + + From e4c09b4b517e1b33835e8280329774b6ddaa30b5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 4 Jun 2024 03:47:40 +0200 Subject: [PATCH 10/14] WORKING STATE - PLAYTESTING --- assets/0003HackingStationMuhvella.mp3 | 2 +- ...ntfernt.mp3 => 0008DONTUSEDOESNOTWORK.mp3} | 0 assets/0009HackingBicola.mp3 | 3 ++ assets/0009HackingStationBicola.mp3 | 3 -- assets/0010PickupBicola.mp3 | 3 ++ assets/0010PickupStationBicola.mp3 | 3 -- assets/0011AfterBicola.mp3 | 3 ++ assets/0011AfterStationBicola.mp3 | 3 -- assets/0012HackingStationTobione.mp3 | 3 -- assets/0012HackingTobione.mp3 | 3 ++ assets/0013PickupStationTobione.mp3 | 3 -- assets/0013PickupTobione.mp3 | 3 ++ assets/0014AfterTobione.mp3 | 2 +- assets/0015End.mp3 | 4 +-- ...ionMagie.mp3 => 0016AfterStationMagie.mp3} | 0 assets/0016WrongStation.mp3 | 3 -- assets/0017WrongStation.mp3 | 3 ++ assets/0018WrongStation.mp3 | 3 ++ assets/0019WrongStation.mp3 | 3 ++ assets/0020WrongStation.mp3 | 3 ++ assets/0021WrongStation.mp3 | 3 ++ assets/0022WrongStation.mp3 | 3 ++ assets/0023WrongStation.mp3 | 3 ++ assets/0024PutMeBack.mp3 | 3 ++ assets/0025PutMeBack.mp3 | 3 ++ assets/0026PutMeBack.mp3 | 3 ++ assets/0027PutMeBack.mp3 | 3 ++ assets/0028PutMeBack.mp3 | 3 ++ copyFiles.py | 30 +++++++++++++++++++ src/main.cpp | 4 +-- src/sounds.h | 7 +++-- src/states.cpp | 5 ++-- 32 files changed, 95 insertions(+), 28 deletions(-) rename assets/{0008ObjektvonderNebenstationentfernt.mp3 => 0008DONTUSEDOESNOTWORK.mp3} (100%) create mode 100644 assets/0009HackingBicola.mp3 delete mode 100644 assets/0009HackingStationBicola.mp3 create mode 100644 assets/0010PickupBicola.mp3 delete mode 100644 assets/0010PickupStationBicola.mp3 create mode 100644 assets/0011AfterBicola.mp3 delete mode 100644 assets/0011AfterStationBicola.mp3 delete mode 100644 assets/0012HackingStationTobione.mp3 create mode 100644 assets/0012HackingTobione.mp3 delete mode 100644 assets/0013PickupStationTobione.mp3 create mode 100644 assets/0013PickupTobione.mp3 rename assets/{0017AfterStationMagie.mp3 => 0016AfterStationMagie.mp3} (100%) delete mode 100644 assets/0016WrongStation.mp3 create mode 100644 assets/0017WrongStation.mp3 create mode 100644 assets/0018WrongStation.mp3 create mode 100644 assets/0019WrongStation.mp3 create mode 100644 assets/0020WrongStation.mp3 create mode 100644 assets/0021WrongStation.mp3 create mode 100644 assets/0022WrongStation.mp3 create mode 100644 assets/0023WrongStation.mp3 create mode 100644 assets/0024PutMeBack.mp3 create mode 100644 assets/0025PutMeBack.mp3 create mode 100644 assets/0026PutMeBack.mp3 create mode 100644 assets/0027PutMeBack.mp3 create mode 100644 assets/0028PutMeBack.mp3 create mode 100644 copyFiles.py diff --git a/assets/0003HackingStationMuhvella.mp3 b/assets/0003HackingStationMuhvella.mp3 index e9f1861..abe5e3a 100644 --- a/assets/0003HackingStationMuhvella.mp3 +++ b/assets/0003HackingStationMuhvella.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5fc464f5809f594a3b9ab8994017de1dbc8b99c5ec78f59a253641f3454e5f0 +oid sha256:69febc15f9893be142276849afa471331d9823e76fcf320cb45b6a22f597bb1a size 401280 diff --git a/assets/0008ObjektvonderNebenstationentfernt.mp3 b/assets/0008DONTUSEDOESNOTWORK.mp3 similarity index 100% rename from assets/0008ObjektvonderNebenstationentfernt.mp3 rename to assets/0008DONTUSEDOESNOTWORK.mp3 diff --git a/assets/0009HackingBicola.mp3 b/assets/0009HackingBicola.mp3 new file mode 100644 index 0000000..f856f57 --- /dev/null +++ b/assets/0009HackingBicola.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6972d6fe940dcf817972cbc26e157251f48dd6d93c832147c5ec5a38f1642b3d +size 591840 diff --git a/assets/0009HackingStationBicola.mp3 b/assets/0009HackingStationBicola.mp3 deleted file mode 100644 index 601b40e..0000000 --- a/assets/0009HackingStationBicola.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:af7c1d630d6949c577cfeffccb91169080da5be83328e116b4d7a61700094c76 -size 588480 diff --git a/assets/0010PickupBicola.mp3 b/assets/0010PickupBicola.mp3 new file mode 100644 index 0000000..0cf7b7a --- /dev/null +++ b/assets/0010PickupBicola.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cec0c449f1cf58e6b5b69491783071d5085246d103a5b384886fa6ee014ac923 +size 1161600 diff --git a/assets/0010PickupStationBicola.mp3 b/assets/0010PickupStationBicola.mp3 deleted file mode 100644 index 8428882..0000000 --- a/assets/0010PickupStationBicola.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3544ff936304b0e53af972d146c7cb9e9f8b77c992f0fe5ccb5d2c463e78bb88 -size 1271520 diff --git a/assets/0011AfterBicola.mp3 b/assets/0011AfterBicola.mp3 new file mode 100644 index 0000000..6015ab1 --- /dev/null +++ b/assets/0011AfterBicola.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49a43802e9040975492b13422a74810a5dbda88925f48909f60c2389e2f71da1 +size 521760 diff --git a/assets/0011AfterStationBicola.mp3 b/assets/0011AfterStationBicola.mp3 deleted file mode 100644 index 323a23a..0000000 --- a/assets/0011AfterStationBicola.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f71d507d83a59ac45bee44b5e8849507c80136edec61724770d7936f308cd8e2 -size 511680 diff --git a/assets/0012HackingStationTobione.mp3 b/assets/0012HackingStationTobione.mp3 deleted file mode 100644 index 422720e..0000000 --- a/assets/0012HackingStationTobione.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0974c1d83bf05487c95360542ab9573713397c0d7bc83adfbf1efd055df3819a -size 791520 diff --git a/assets/0012HackingTobione.mp3 b/assets/0012HackingTobione.mp3 new file mode 100644 index 0000000..bd9154a --- /dev/null +++ b/assets/0012HackingTobione.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64ab0f541e5fe67b9f3663b0fc87ad77ddd217df05dc31bf546138fc0a2e5e9e +size 791520 diff --git a/assets/0013PickupStationTobione.mp3 b/assets/0013PickupStationTobione.mp3 deleted file mode 100644 index f299b45..0000000 --- a/assets/0013PickupStationTobione.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:25b07721ba9e5f27ba728a1f7f05075af1ff95ed315345a4a3d0b61f60f102fb -size 1421760 diff --git a/assets/0013PickupTobione.mp3 b/assets/0013PickupTobione.mp3 new file mode 100644 index 0000000..88d0b7d --- /dev/null +++ b/assets/0013PickupTobione.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:173dbe32eeccba13fa27cd8880bca34f22db63029401f529fc01f66cddd5ab3d +size 1161600 diff --git a/assets/0014AfterTobione.mp3 b/assets/0014AfterTobione.mp3 index 1358d34..7203f8a 100644 --- a/assets/0014AfterTobione.mp3 +++ b/assets/0014AfterTobione.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:278d81deba822df2df47b773c7a01529beae7ebb492df8dbc1eb9a24ecfb92d3 +oid sha256:3d89019ca9477457555209104e2def1d2a6cf5b87625ecbac5f429f8c912d236 size 441600 diff --git a/assets/0015End.mp3 b/assets/0015End.mp3 index 11dedcd..b317e7d 100644 --- a/assets/0015End.mp3 +++ b/assets/0015End.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1db9be7221cb13fe7451b854b9853c18fc001c93ac463db08a1585dcb3da8f4 -size 741600 +oid sha256:e04c9d52e55708c65ceb13d5aefda2a23c53186e33b9740c4f38abe8c23acdc8 +size 921600 diff --git a/assets/0017AfterStationMagie.mp3 b/assets/0016AfterStationMagie.mp3 similarity index 100% rename from assets/0017AfterStationMagie.mp3 rename to assets/0016AfterStationMagie.mp3 diff --git a/assets/0016WrongStation.mp3 b/assets/0016WrongStation.mp3 deleted file mode 100644 index fbcc67a..0000000 --- a/assets/0016WrongStation.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c8b3f3890eec7ce0a9e2984e9fc205e4d5daa62953a00d7fe719227e8cb81ae4 -size 772805 diff --git a/assets/0017WrongStation.mp3 b/assets/0017WrongStation.mp3 new file mode 100644 index 0000000..f8086ed --- /dev/null +++ b/assets/0017WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb59118f9918a030f911fa60e144cf2982366a8632ac2540a9e4b69bd181b7ad +size 45312 diff --git a/assets/0018WrongStation.mp3 b/assets/0018WrongStation.mp3 new file mode 100644 index 0000000..0e2d04d --- /dev/null +++ b/assets/0018WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed1b9d93e941b6c17aec2a4316ea4a7b8642ae43794eac5a76786f09b6d07f7c +size 53376 diff --git a/assets/0019WrongStation.mp3 b/assets/0019WrongStation.mp3 new file mode 100644 index 0000000..a5adfcd --- /dev/null +++ b/assets/0019WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9170b58d7bf75ba8927a6eee31e83890987b8b3940fe404a5f27450e7020a4d +size 24192 diff --git a/assets/0020WrongStation.mp3 b/assets/0020WrongStation.mp3 new file mode 100644 index 0000000..7c2863a --- /dev/null +++ b/assets/0020WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3bff3e23c0e914546442a3b7d9ea75feb10c44718ec1a06c8733f59b97765fc +size 31872 diff --git a/assets/0021WrongStation.mp3 b/assets/0021WrongStation.mp3 new file mode 100644 index 0000000..9e47f5b --- /dev/null +++ b/assets/0021WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d0ee396011b9cf8380f9df60cd606a66797aa9410e5f033697b78f855fd46b3 +size 30720 diff --git a/assets/0022WrongStation.mp3 b/assets/0022WrongStation.mp3 new file mode 100644 index 0000000..3245619 --- /dev/null +++ b/assets/0022WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34394dc039e62d781bbe1cee10b948748a3d1aaa0a90015737e9fdf0ade12837 +size 67968 diff --git a/assets/0023WrongStation.mp3 b/assets/0023WrongStation.mp3 new file mode 100644 index 0000000..caf134f --- /dev/null +++ b/assets/0023WrongStation.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6af150d5a38f17e8e18b614752974170b499e428ea1042b8fcee47d2390f6c93 +size 50688 diff --git a/assets/0024PutMeBack.mp3 b/assets/0024PutMeBack.mp3 new file mode 100644 index 0000000..7d747a6 --- /dev/null +++ b/assets/0024PutMeBack.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3b1e420cdc424fb8c57537f59b5d385d4e3ba917fd18d9ed99479c82470f4fa +size 70656 diff --git a/assets/0025PutMeBack.mp3 b/assets/0025PutMeBack.mp3 new file mode 100644 index 0000000..9117927 --- /dev/null +++ b/assets/0025PutMeBack.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb6b21eba6b65dbaee9b1661f99da1a61ab65bbe9e7492556a731492ac6cd4c3 +size 45312 diff --git a/assets/0026PutMeBack.mp3 b/assets/0026PutMeBack.mp3 new file mode 100644 index 0000000..3caf488 --- /dev/null +++ b/assets/0026PutMeBack.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6b81f5b59afac770b88924bb011977d5b58c44cdd2264ebdb7dc041ebcd9345 +size 50688 diff --git a/assets/0027PutMeBack.mp3 b/assets/0027PutMeBack.mp3 new file mode 100644 index 0000000..ea9ba54 --- /dev/null +++ b/assets/0027PutMeBack.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f12a1fb1728525df4d87f98c9019d0486188452aadb3667e01e477139528123c +size 77184 diff --git a/assets/0028PutMeBack.mp3 b/assets/0028PutMeBack.mp3 new file mode 100644 index 0000000..f7fa1ac --- /dev/null +++ b/assets/0028PutMeBack.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1fa605420a37ab0d18fd571f9be13bcac42401b9de7add097ada89f37515dae +size 51840 diff --git a/copyFiles.py b/copyFiles.py new file mode 100644 index 0000000..80d028e --- /dev/null +++ b/copyFiles.py @@ -0,0 +1,30 @@ +import os +import shutil + +def copy_files_to_root(src_folder, dest_root): + # Check if source folder exists + if not os.path.exists(src_folder): + print(f"Source folder '{src_folder}' does not exist.") + return + + # Create destination root folder if it does not exist + if not os.path.exists(dest_root): + os.makedirs(dest_root) + + # Copy files + for item in os.listdir(src_folder): + src_path = os.path.join(src_folder, item) + dest_path = os.path.join(dest_root, item) + + if os.path.isfile(src_path): + shutil.copy2(src_path, dest_path) + print(f"Copied '{src_path}' to '{dest_path}'") + else: + print(f"Skipped '{src_path}' as it is not a file") + +# Define source folder and destination root +src_folder = 'C:/Users/Marcel/CLionProjects/BuenzliIO/assets' +dest_root = 'D:/' + +# Execute the copy function +copy_files_to_root(src_folder, dest_root) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 678b1ed..4bb6e47 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -157,7 +157,7 @@ void loop(void) void setup(void) { - + randomSeed(analogRead(0)); Serial.begin(9600); // Serial.println(ON_MAIN_STATION.getEntryAtIndex(0)->isAudio); @@ -182,5 +182,5 @@ void setup(void) } } - dfPlayer.volume(15); + dfPlayer.volume(25); } diff --git a/src/sounds.h b/src/sounds.h index 638dcb3..cb7c3c9 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -10,7 +10,7 @@ #define HACKING_STATION_MAGIE 6 #define PICKUP_STATION_MAGIE 7 -#define AFTER_MAGIE 17 // weird hack because file 8 does not seem to work... +#define AFTER_MAGIE 16 // weird hack because file 8 does not seem to work... #define HACKING_STATION_BICOLA 9 #define PICKUP_STATION_BICOLA 10 @@ -21,4 +21,7 @@ #define AFTER_TOBIONE 14 #define END 15 -#define WRONG_STATION 16 +#define WRONG_STATION_START 17 +#define WRONG_STATION_END 23 +#define PUT_ME_BACK_START 24 +#define PUT_ME_BACK_END 28 diff --git a/src/states.cpp b/src/states.cpp index 6106eb0..d79f733 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -166,7 +166,7 @@ String IncorrectStation::updateDisplay(const Context& context) void IncorrectStation::activated(const Context context) { - context.dfPlayer->play(WRONG_STATION); + context.dfPlayer->play(random(WRONG_STATION_START, WRONG_STATION_END + 1)); } @@ -275,8 +275,9 @@ String Complain::updateDisplay(const Context& context) return COMPLAIN_TEXT; } -void Complain::activated(Context context) +void Complain::activated(const Context context) { + context.dfPlayer->play(random(PUT_ME_BACK_START, PUT_ME_BACK_END + 1)); } From 31858bebd0bc7ba4ffd4ce56190a06ce73bd0a48 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 4 Jun 2024 05:35:52 +0200 Subject: [PATCH 11/14] adds start instruction state and fixes some issues --- assets/0001WaitingForGameStart.mp3 | 4 +- assets/0002AfterMainstation.mp3 | 4 +- assets/0003HackingStationMuhvella.mp3 | 2 +- assets/0004PickupStationMuhvella.mp3 | 4 +- assets/0005AfterStationMuhvella.mp3 | 4 +- assets/0006HackingStationMagie.mp3 | 4 +- assets/0007PickupStationMagie.mp3 | 4 +- assets/0009HackingBicola.mp3 | 4 +- assets/0010PickupBicola.mp3 | 4 +- assets/0012HackingTobione.mp3 | 4 +- assets/0013PickupTobione.mp3 | 4 +- assets/0014AfterTobione.mp3 | 4 +- assets/0015End.mp3 | 4 +- assets/0016AfterStationMagie.mp3 | 4 +- assets/0029Intro.mp3 | 3 ++ src/main.cpp | 2 +- src/sounds.h | 3 ++ src/states.cpp | 72 ++++++++++++++++++++++++--- src/states.h | 14 ++++++ src/text.h | 3 +- 20 files changed, 115 insertions(+), 36 deletions(-) create mode 100644 assets/0029Intro.mp3 diff --git a/assets/0001WaitingForGameStart.mp3 b/assets/0001WaitingForGameStart.mp3 index 7496557..e8f2b79 100644 --- a/assets/0001WaitingForGameStart.mp3 +++ b/assets/0001WaitingForGameStart.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21bf168a9f7630f5c48687a15fbd3d6bdd303b5dabecaea76ac2e994f8839fef -size 889344 +oid sha256:071d551d6e05672be0e2d0164e547251e105547f63ab5a220c4ba81b4ef42a12 +size 953472 diff --git a/assets/0002AfterMainstation.mp3 b/assets/0002AfterMainstation.mp3 index e81d240..40bde85 100644 --- a/assets/0002AfterMainstation.mp3 +++ b/assets/0002AfterMainstation.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63371bb5c9d3b2a91588a31de26049c365c737e1561d5a6ea93b20b173bf18ba -size 797184 +oid sha256:121cf70913522060d0dc273c4d7b98d73bc147895e13f3d987108b16515e0a3b +size 185472 diff --git a/assets/0003HackingStationMuhvella.mp3 b/assets/0003HackingStationMuhvella.mp3 index abe5e3a..874a7ce 100644 --- a/assets/0003HackingStationMuhvella.mp3 +++ b/assets/0003HackingStationMuhvella.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69febc15f9893be142276849afa471331d9823e76fcf320cb45b6a22f597bb1a +oid sha256:1dbe24aef80632e46f4ded6241d4192c13fa1357a7c38ea3da9d1c2dcab9ac55 size 401280 diff --git a/assets/0004PickupStationMuhvella.mp3 b/assets/0004PickupStationMuhvella.mp3 index 5c37542..2c65546 100644 --- a/assets/0004PickupStationMuhvella.mp3 +++ b/assets/0004PickupStationMuhvella.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d85ce2880b0d24df851080426b124527896803acf5143e9ba4c1a5e16243ec3 -size 277248 +oid sha256:3f65d84d2e024526be1b320ca8e816025d089ee8c7ad57f695d8e73b20267345 +size 401280 diff --git a/assets/0005AfterStationMuhvella.mp3 b/assets/0005AfterStationMuhvella.mp3 index 4ccf604..4d81b64 100644 --- a/assets/0005AfterStationMuhvella.mp3 +++ b/assets/0005AfterStationMuhvella.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:624d43f1a8a05041684cdd9fa3dbf8a23e8775ee57eed7f69ed995ec05cb3beb -size 445440 +oid sha256:dee7d91d478548b08c9ce518d749fa2a9e33f4577e4a496e611fb17fba0a94a2 +size 316032 diff --git a/assets/0006HackingStationMagie.mp3 b/assets/0006HackingStationMagie.mp3 index 53138cf..3b14bc4 100644 --- a/assets/0006HackingStationMagie.mp3 +++ b/assets/0006HackingStationMagie.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4521f8ca1193c391a097baecc27038241d351ab671ae53f62db697bcd4dcec48 -size 469248 +oid sha256:a3f970cfb76b4b912aa38ff9abdb28921aa9aab7242965d144d8e66316ad5719 +size 465408 diff --git a/assets/0007PickupStationMagie.mp3 b/assets/0007PickupStationMagie.mp3 index 3fc7ada..4abc441 100644 --- a/assets/0007PickupStationMagie.mp3 +++ b/assets/0007PickupStationMagie.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7dd0e1a62798011a83f7f0650d7b540d6d1a3fe54dcc9bca57eedee67dffe64 -size 477312 +oid sha256:218e6de26f8ea212edea03f06d249ef135ee11cbea5913ce27b416a3e203dc4a +size 529152 diff --git a/assets/0009HackingBicola.mp3 b/assets/0009HackingBicola.mp3 index f856f57..b5defe9 100644 --- a/assets/0009HackingBicola.mp3 +++ b/assets/0009HackingBicola.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6972d6fe940dcf817972cbc26e157251f48dd6d93c832147c5ec5a38f1642b3d -size 591840 +oid sha256:49fe3a01005997436144572df11792aeb13f50b83bc5f4ed00fc05f221be34f5 +size 611520 diff --git a/assets/0010PickupBicola.mp3 b/assets/0010PickupBicola.mp3 index 0cf7b7a..cfa97d7 100644 --- a/assets/0010PickupBicola.mp3 +++ b/assets/0010PickupBicola.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cec0c449f1cf58e6b5b69491783071d5085246d103a5b384886fa6ee014ac923 -size 1161600 +oid sha256:e30d3faa9b214a340828c2d57dbe7681fa98c2e984479f271bcd3663048f7638 +size 1121760 diff --git a/assets/0012HackingTobione.mp3 b/assets/0012HackingTobione.mp3 index bd9154a..b73a808 100644 --- a/assets/0012HackingTobione.mp3 +++ b/assets/0012HackingTobione.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64ab0f541e5fe67b9f3663b0fc87ad77ddd217df05dc31bf546138fc0a2e5e9e -size 791520 +oid sha256:42d9b18e9fd14c9a0bc2cd87ffe02fb88c2171eda0133fcb77023f31d8d17f59 +size 801600 diff --git a/assets/0013PickupTobione.mp3 b/assets/0013PickupTobione.mp3 index 88d0b7d..34c4870 100644 --- a/assets/0013PickupTobione.mp3 +++ b/assets/0013PickupTobione.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:173dbe32eeccba13fa27cd8880bca34f22db63029401f529fc01f66cddd5ab3d -size 1161600 +oid sha256:01e7855ce6c7b8098761f9a66058be20d709f33faf9263ac268aef048b513cb2 +size 1321440 diff --git a/assets/0014AfterTobione.mp3 b/assets/0014AfterTobione.mp3 index 7203f8a..aa96aa4 100644 --- a/assets/0014AfterTobione.mp3 +++ b/assets/0014AfterTobione.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d89019ca9477457555209104e2def1d2a6cf5b87625ecbac5f429f8c912d236 -size 441600 +oid sha256:9f848a6ca577d3c5d170cf656bce66d1be66ed4059c28365174a4199c23bd48c +size 461760 diff --git a/assets/0015End.mp3 b/assets/0015End.mp3 index b317e7d..bd9154a 100644 --- a/assets/0015End.mp3 +++ b/assets/0015End.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e04c9d52e55708c65ceb13d5aefda2a23c53186e33b9740c4f38abe8c23acdc8 -size 921600 +oid sha256:64ab0f541e5fe67b9f3663b0fc87ad77ddd217df05dc31bf546138fc0a2e5e9e +size 791520 diff --git a/assets/0016AfterStationMagie.mp3 b/assets/0016AfterStationMagie.mp3 index fb09a94..02812fe 100644 --- a/assets/0016AfterStationMagie.mp3 +++ b/assets/0016AfterStationMagie.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b618ea2a7b01e411f4f8236c7554cbaf846576a8d1dca9a5d5ad233f6382371 -size 429312 +oid sha256:bc9c4ca1e2e0c738e0b6c0323db49da9db2a204796b33f67b2078f5f6cc20769 +size 401280 diff --git a/assets/0029Intro.mp3 b/assets/0029Intro.mp3 new file mode 100644 index 0000000..f1e53c6 --- /dev/null +++ b/assets/0029Intro.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95f104be43cae044964c6f5a6c159b47e596c5685bcbf1ccf23a80e0a8bdeb59 +size 617472 diff --git a/src/main.cpp b/src/main.cpp index 4bb6e47..02de43d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -182,5 +182,5 @@ void setup(void) } } - dfPlayer.volume(25); + dfPlayer.volume(15); } diff --git a/src/sounds.h b/src/sounds.h index cb7c3c9..12199a2 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -25,3 +25,6 @@ #define WRONG_STATION_END 23 #define PUT_ME_BACK_START 24 #define PUT_ME_BACK_END 28 + +#define START_INSTRUCTIONS 29 + diff --git a/src/states.cpp b/src/states.cpp index d79f733..5446753 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -40,7 +40,7 @@ WaitingForGameStart::WaitingForGameStart() = default; State* WaitingForGameStart::pickedUp(const Context context) { - return new OnTheMove(context.stations + 1, context.stations); // first element of the array + return new StartInstructions(context.stations + 1); } State* WaitingForGameStart::update(const Context context) @@ -62,6 +62,51 @@ void WaitingForGameStart::activated(const Context context) //------------------------------------ +StartInstructions::StartInstructions(Station* targetStation): timer(Timer(0, false)) +{ + this->targetStation = targetStation; +} + +State* StartInstructions::putDown(const Context context, Station* newStation) +{ + if (newStation == this->targetStation) + { + return new Hacking(targetStation); + } + + if (newStation == context.stations) + { + return new WaitingForGameStart(); // todo replace with Restarting + } + return new IncorrectStation(targetStation, context.stations); +} + +State* StartInstructions::update(const Context context) +{ + bool done = timer.update(context.delta); + if (done) + { + return new OnTheMove(targetStation, context.stations); + } + return this; +} + +String StartInstructions::updateDisplay(const Context& context) +{ + return INTRO_TEXT; +} + +void StartInstructions::activated(const Context context) +{ + context.dfPlayer->play(START_INSTRUCTIONS); + timer = Timer(10000, false); // todo set correct time + timer.start(); +} + + +//------------------------------------ + + OnTheMove::OnTheMove(Station* targetStation, Station* previousStation) { this->targetStation = targetStation; @@ -72,12 +117,16 @@ State* OnTheMove::putDown(const Context context, Station* newStation) { if (newStation == this->targetStation) { - if (targetStation == context.stations) // first station + if (targetStation == context.stations) // is home base? { return new End(); } return new Hacking(this->targetStation); } + if (newStation == context.stations) + { + return new WaitingForGameStart(); // todo replace with Restarting + } return new IncorrectStation(targetStation, previousStation); } @@ -110,7 +159,7 @@ String OnTheMove::updateDisplay(const Context& context) { return BICOLA_AFTER_TEXT; } - + return "UNDEFINED"; } @@ -223,25 +272,26 @@ String Hacking::updateDisplay(const Context& context) void Hacking::activated(const Context context) { + // todo change times auto currentStationIndex = context.getStationIndex(this->currentStation); if (currentStationIndex == 1) { - timer = Timer(25000, false); + timer = Timer(5000, false); context.dfPlayer->play(HACKING_STATION_MUHVELLA); } if (currentStationIndex == 2) { - timer = Timer(29000, false); + timer = Timer(5000, false); context.dfPlayer->play(HACKING_STATION_MAGIE); } if (currentStationIndex == 3) { - timer = Timer(29000, false); + timer = Timer(5000, false); context.dfPlayer->play(HACKING_STATION_BICOLA); } if (currentStationIndex == 4) { - timer = Timer(40000, false); + timer = Timer(5000, false); context.dfPlayer->play(HACKING_STATION_TOBIONE); } timer.start(); @@ -260,8 +310,16 @@ State* Complain::putDown(const Context context, Station* newStation) { if (this->currentStation == newStation) // was put back on correct station { + if (currentStation == context.stations) // if wants to be put back on main station + { + return new End(); + } return new Hacking(this->currentStation); } + if (newStation == context.stations) // if was incorrectly put back on main station + { + return new WaitingForGameStart(); // todo return Restarting + } return this; // was not put back on correct station (just keeps complaining) } diff --git a/src/states.h b/src/states.h index 33b09a4..2dc6913 100644 --- a/src/states.h +++ b/src/states.h @@ -27,6 +27,20 @@ public: void activated(Context context) override; }; +class StartInstructions final : public State +{ +private: + Station* targetStation; + Timer timer; + +public: + explicit StartInstructions(Station* targetStation); + State* putDown(Context context, Station* newStation) override; + State* update(Context context) override; + String updateDisplay(const Context& context) override; + void activated(Context context) override; +}; + class OnTheMove final : public State { private: diff --git a/src/text.h b/src/text.h index 47a8ea4..a6e059c 100644 --- a/src/text.h +++ b/src/text.h @@ -1,7 +1,8 @@ #pragma once #define WAITING_FOR_START_TEXT "Lupf mich uf" -#define MAIN_AFTER_PICKUP_TEXT "Danke :D <3 Lueg chli ume" +#define INTRO_TEXT "Danke :D <3" +#define MAIN_AFTER_PICKUP_TEXT "Lueg chli ume" #define MUHVELLA_HACKING_TEXT "Eifach ligge lahBitte warte" #define MUHVELLA_PICKUP_TEXT "Los! Witer =O" From 6cdcca198e5c34a34b3a9f4f438b174652fc6623 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 4 Jun 2024 06:23:51 +0200 Subject: [PATCH 12/14] adds restarting state --- assets/0002AfterMainstation.mp3 | 4 +-- assets/0012HackingTobione.mp3 | 4 +-- assets/0015End.mp3 | 4 +-- src/audioState.h | 5 +-- src/states.cpp | 59 +++++++++++++++++++++++++-------- src/states.h | 12 +++++++ 6 files changed, 65 insertions(+), 23 deletions(-) diff --git a/assets/0002AfterMainstation.mp3 b/assets/0002AfterMainstation.mp3 index 40bde85..d712de1 100644 --- a/assets/0002AfterMainstation.mp3 +++ b/assets/0002AfterMainstation.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:121cf70913522060d0dc273c4d7b98d73bc147895e13f3d987108b16515e0a3b -size 185472 +oid sha256:d62be81090dc60237585f64d22cbbd659146569807394f6b8c99cee5068beaa6 +size 337152 diff --git a/assets/0012HackingTobione.mp3 b/assets/0012HackingTobione.mp3 index b73a808..96db91f 100644 --- a/assets/0012HackingTobione.mp3 +++ b/assets/0012HackingTobione.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42d9b18e9fd14c9a0bc2cd87ffe02fb88c2171eda0133fcb77023f31d8d17f59 -size 801600 +oid sha256:5e57161e59f565b14c7819844768e9871d0f983597bed8033852e2895d55c881 +size 736800 diff --git a/assets/0015End.mp3 b/assets/0015End.mp3 index bd9154a..9754bce 100644 --- a/assets/0015End.mp3 +++ b/assets/0015End.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64ab0f541e5fe67b9f3663b0fc87ad77ddd217df05dc31bf546138fc0a2e5e9e -size 791520 +oid sha256:ee259ea16340622bd563297bc4dd3672cdcc9d3818612d5549d9f6d52c42de99 +size 911520 diff --git a/src/audioState.h b/src/audioState.h index c3f26f1..4bdfc1d 100644 --- a/src/audioState.h +++ b/src/audioState.h @@ -1,12 +1,9 @@ -#pragma once -#include -#include - #pragma once #include #include #include + inline void printMp3Detail(const uint8_t type, const int value) { Serial.println(); // Add a newline before printing the details diff --git a/src/states.cpp b/src/states.cpp index 5446753..6e30087 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -76,7 +76,7 @@ State* StartInstructions::putDown(const Context context, Station* newStation) if (newStation == context.stations) { - return new WaitingForGameStart(); // todo replace with Restarting + return new Restarting(); } return new IncorrectStation(targetStation, context.stations); } @@ -125,7 +125,7 @@ State* OnTheMove::putDown(const Context context, Station* newStation) } if (newStation == context.stations) { - return new WaitingForGameStart(); // todo replace with Restarting + return new Restarting(); } return new IncorrectStation(targetStation, previousStation); @@ -168,23 +168,23 @@ void OnTheMove::activated(const Context context) const int index = context.getStationIndex(this->targetStation); if (index == 0) { - context.dfPlayer->play(AFTER_TOBIONE); + context.dfPlayer->loop(AFTER_TOBIONE); } else if (index == 1) { - context.dfPlayer->play(AFTER_MAIN_STATION); + context.dfPlayer->loop(AFTER_MAIN_STATION); } else if (index == 2) { - context.dfPlayer->play(AFTER_MUHVELLA); + context.dfPlayer->loop(AFTER_MUHVELLA); } else if (index == 3) { - context.dfPlayer->play(AFTER_MAGIE); + context.dfPlayer->loop(AFTER_MAGIE); } else if (index == 4) { - context.dfPlayer->play(AFTER_BICOLA); + context.dfPlayer->loop(AFTER_BICOLA); } } @@ -316,10 +316,10 @@ State* Complain::putDown(const Context context, Station* newStation) } return new Hacking(this->currentStation); } - if (newStation == context.stations) // if was incorrectly put back on main station - { - return new WaitingForGameStart(); // todo return Restarting - } + if (newStation == context.stations) // if was incorrectly put back on main station + { + return new Restarting(); + } return this; // was not put back on correct station (just keeps complaining) } @@ -410,7 +410,6 @@ End::End(): timer(Timer(0, false)) State* End::pickedUp(const Context context) { - // todo complain return new Complain(context.stations); } @@ -419,7 +418,7 @@ State* End::update(const Context context) const bool done = timer.update(context.delta); if (done) { - return new WaitingForGameStart(); + return new Restarting(); } return this; } @@ -435,3 +434,37 @@ void End::activated(const Context context) timer = Timer(38000, false); timer.start(); } + + +//------------------------------------ + + +Restarting::Restarting(): timer(Timer(10000, false)) +{ +} + +State* Restarting::pickedUp(const Context context) +{ + return new StartInstructions(context.stations + 1); // starting despite not beeing in waiting for game start +} + +State* Restarting::update(const Context context) +{ + const bool done = timer.update(context.delta); + if (done) + { + return new WaitingForGameStart(); + } + return this; +} + +String Restarting::updateDisplay(const Context& context) +{ + return "RESTARTING......"; // todo +} + +void Restarting::activated(const Context context) +{ + timer.start(); + context.dfPlayer->stop(); +} diff --git a/src/states.h b/src/states.h index 2dc6913..869334e 100644 --- a/src/states.h +++ b/src/states.h @@ -122,3 +122,15 @@ public: String updateDisplay(const Context& context) override; void activated(Context context) override; }; + +class Restarting final : public State +{ +protected: + Timer timer; +public: + Restarting(); + State* pickedUp(Context context) override; + State* update(Context context) override; + String updateDisplay(const Context& context) override; + void activated(Context context) override; +}; From 47df29c8d8b128e5de7bd5e9579a73c6d27f94d2 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 4 Jun 2024 07:06:43 +0200 Subject: [PATCH 13/14] adds funny restart animation --- src/states.cpp | 11 ++++++++++- src/timer.cpp | 10 ++++++++++ src/timer.h | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/states.cpp b/src/states.cpp index 6e30087..7ba3e29 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -460,7 +460,16 @@ State* Restarting::update(const Context context) String Restarting::updateDisplay(const Context& context) { - return "RESTARTING......"; // todo + String animLine = ""; + const String full = ".-`-._.-`-._. :)"; + const float percent = static_cast(timer.getCurrentTime()) / static_cast(timer.getDuration()); + Serial.println(String("updating display ") + percent); + for (int i = 0; i < full.length() * percent; i++) + { + Serial.println(String("i: ") + i + "len " + (full.length() * percent)); + animLine = animLine + full[i]; + } + return "RESTARTING " + animLine; } void Restarting::activated(const Context context) diff --git a/src/timer.cpp b/src/timer.cpp index c3b12eb..2003585 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -44,3 +44,13 @@ bool Timer::isRunning() const { return this->running; } + +unsigned long Timer::getDuration() const +{ + return this->duration; +} + +unsigned long Timer::getCurrentTime() const +{ + return this->currentTime; +} diff --git a/src/timer.h b/src/timer.h index 9667847..33b3709 100644 --- a/src/timer.h +++ b/src/timer.h @@ -14,4 +14,6 @@ public: void start(); bool update(unsigned long delta); bool isRunning() const; + unsigned long getDuration() const; + unsigned long getCurrentTime() const; }; From b858f8a773095e58dd5c0b005d51fb70c1137e9c Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 5 Jun 2024 10:37:38 +0200 Subject: [PATCH 14/14] updates volume --- assets/0015End.mp3 | 4 ++-- src/main.cpp | 2 +- src/states.cpp | 15 ++++++--------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/assets/0015End.mp3 b/assets/0015End.mp3 index 9754bce..9b424f2 100644 --- a/assets/0015End.mp3 +++ b/assets/0015End.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee259ea16340622bd563297bc4dd3672cdcc9d3818612d5549d9f6d52c42de99 -size 911520 +oid sha256:bc911b69fcf5e7a8d865263f3b7a764988af92d0047dddcb352dbf535a333ec1 +size 761760 diff --git a/src/main.cpp b/src/main.cpp index 02de43d..7af61cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -182,5 +182,5 @@ void setup(void) } } - dfPlayer.volume(15); + dfPlayer.volume(20); } diff --git a/src/states.cpp b/src/states.cpp index 7ba3e29..18284ce 100644 --- a/src/states.cpp +++ b/src/states.cpp @@ -99,7 +99,7 @@ String StartInstructions::updateDisplay(const Context& context) void StartInstructions::activated(const Context context) { context.dfPlayer->play(START_INSTRUCTIONS); - timer = Timer(10000, false); // todo set correct time + timer = Timer(39000, false); // todo set correct time timer.start(); } @@ -235,7 +235,6 @@ State* Hacking::pickedUp(const Context context) State* Hacking::update(const Context context) { - Serial.println(String(context.delta)); const bool done = timer.update(context.delta); if (done) @@ -276,22 +275,22 @@ void Hacking::activated(const Context context) auto currentStationIndex = context.getStationIndex(this->currentStation); if (currentStationIndex == 1) { - timer = Timer(5000, false); + timer = Timer(25000, false); context.dfPlayer->play(HACKING_STATION_MUHVELLA); } if (currentStationIndex == 2) { - timer = Timer(5000, false); + timer = Timer(29000, false); context.dfPlayer->play(HACKING_STATION_MAGIE); } if (currentStationIndex == 3) { - timer = Timer(5000, false); + timer = Timer(30000, false); context.dfPlayer->play(HACKING_STATION_BICOLA); } if (currentStationIndex == 4) { - timer = Timer(5000, false); + timer = Timer(37000, false); context.dfPlayer->play(HACKING_STATION_TOBIONE); } timer.start(); @@ -431,7 +430,7 @@ String End::updateDisplay(const Context& context) void End::activated(const Context context) { context.dfPlayer->play(END); - timer = Timer(38000, false); + timer = Timer(45000, false); timer.start(); } @@ -463,10 +462,8 @@ String Restarting::updateDisplay(const Context& context) String animLine = ""; const String full = ".-`-._.-`-._. :)"; const float percent = static_cast(timer.getCurrentTime()) / static_cast(timer.getDuration()); - Serial.println(String("updating display ") + percent); for (int i = 0; i < full.length() * percent; i++) { - Serial.println(String("i: ") + i + "len " + (full.length() * percent)); animLine = animLine + full[i]; } return "RESTARTING " + animLine;