From 1fe0383aca30a808b666ca08ece4afb0ae435190 Mon Sep 17 00:00:00 2001 From: Rennan Cockles Date: Sat, 31 Aug 2024 00:15:07 -0300 Subject: [PATCH 01/11] pn532 connection media --- media/pictures/pn532_i2c.jpg | Bin 0 -> 18106 bytes media/pictures/pn532_spi.jpg | Bin 0 -> 30249 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 media/pictures/pn532_i2c.jpg create mode 100644 media/pictures/pn532_spi.jpg diff --git a/media/pictures/pn532_i2c.jpg b/media/pictures/pn532_i2c.jpg new file mode 100644 index 0000000000000000000000000000000000000000..49000fcc9ca022f4a20945887557a9309426731e GIT binary patch literal 18106 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 CG48hj literal 0 HcmV?d00001 From 5fb134baf448f7caa91b1beba8879ce3ba9c3111 Mon Sep 17 00:00:00 2001 From: Rennan Cockles Date: Sat, 31 Aug 2024 00:28:00 -0300 Subject: [PATCH 02/11] PN532 clone tag --- src/modules/rfid/PN532.cpp | 25 +- src/modules/rfid/PN532.h | 1 + .../rfid/lib_pn532/Adafruit_I2CDevice.cpp | 63 +++-- src/modules/rfid/lib_pn532/Adafruit_PN532.cpp | 227 +++++++++++++----- src/modules/rfid/lib_pn532/Adafruit_PN532.h | 7 +- 5 files changed, 242 insertions(+), 81 deletions(-) diff --git a/src/modules/rfid/PN532.cpp b/src/modules/rfid/PN532.cpp index 9405e94..3659027 100644 --- a/src/modules/rfid/PN532.cpp +++ b/src/modules/rfid/PN532.cpp @@ -38,7 +38,27 @@ int PN532::read() { } int PN532::clone() { - return NOT_IMPLEMENTED; + if (!PICC_IsNewCardPresent()) return TAG_NOT_PRESENT; + if (!readDetectedPassiveTargetID()) return FAILURE; + + if (_tag_read_uid.sak != uid.sak) return TAG_NOT_MATCH; + + // uint8_t data[16]; + // byte bcc = 0; + // int i; + // for (i = 0; i < uid.size; i++) { + // data[i] = uid.uidByte[i]; + // bcc = bcc ^ uid.uidByte[i]; + // } + // data[i++] = bcc; + // data[i++] = uid.sak; + // data[i++] = uid.atqaByte[1]; + // data[i++] = uid.atqaByte[0]; + // byte tmp = 0; + // while (i<16) data[i++] = 0x62+tmp++; + + bool success = nfc.mifareclassic_WriteBlock0(block0_data); + return success ? SUCCESS : FAILURE; } int PN532::erase() { @@ -384,6 +404,7 @@ bool PN532::read_mifare_classic_data_sector(uint8_t *key, byte sector) { if (!success) { return false; } + if (blockAddr == 0) memcpy(block0_data, buffer, 16); for (byte index = 0; index < 16; index++) { strPage += buffer[index] < 0x10 ? F(" 0") : F(" "); strPage += String(buffer[index], HEX); @@ -453,6 +474,8 @@ bool PN532::read_mifare_ultralight_data_blocks() { success = nfc.ntag2xx_ReadPage(page, buffer); if (!success) return false; + if (page == 0) memcpy(block0_data, buffer, 16); + for (byte offset = 0; offset < 4; offset++) { strPage = ""; for (byte index = 0; index < 4; index++) { diff --git a/src/modules/rfid/PN532.h b/src/modules/rfid/PN532.h index edb93be..ca0551b 100644 --- a/src/modules/rfid/PN532.h +++ b/src/modules/rfid/PN532.h @@ -45,6 +45,7 @@ class PN532 : public RFIDInterface { private: bool _use_i2c; byte pn532_packetbuffer[64]; + byte block0_data[16]; Uid _tag_read_uid; ///////////////////////////////////////////////////////////////////////////////////// diff --git a/src/modules/rfid/lib_pn532/Adafruit_I2CDevice.cpp b/src/modules/rfid/lib_pn532/Adafruit_I2CDevice.cpp index 3d1d938..87ff262 100644 --- a/src/modules/rfid/lib_pn532/Adafruit_I2CDevice.cpp +++ b/src/modules/rfid/lib_pn532/Adafruit_I2CDevice.cpp @@ -57,7 +57,20 @@ bool Adafruit_I2CDevice::detected(void) { // A basic scanner, see if it ACK's _wire->beginTransmission(_addr); - return (_wire->endTransmission() == 0); +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("Address 0x")); + DEBUG_SERIAL.print(_addr); +#endif + if (_wire->endTransmission() == 0) { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F(" Detected")); +#endif + return true; + } +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F(" Not detected")); +#endif + return false; } /*! @@ -80,6 +93,9 @@ bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop, // currently not guaranteed to work if more than 32 bytes! // we will need to find out if some platforms have larger // I2C buffer sizes :/ +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("\tI2CDevice could not write such a large buffer")); +#endif return false; } @@ -88,12 +104,18 @@ bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop, // Write the prefix data (usually an address) if ((prefix_len != 0) && (prefix_buffer != nullptr)) { if (_wire->write(prefix_buffer, prefix_len) != prefix_len) { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("\tI2CDevice failed to write")); +#endif return false; } } // Write the data itself if (_wire->write(buffer, len) != len) { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("\tI2CDevice failed to write")); +#endif return false; } @@ -124,8 +146,15 @@ bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop, #endif if (_wire->endTransmission(stop) == 0) { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(); + // DEBUG_SERIAL.println("Sent!"); +#endif return true; } else { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println("\tFailed to send!"); +#endif return false; } } @@ -162,6 +191,10 @@ bool Adafruit_I2CDevice::_read(uint8_t *buffer, size_t len, bool stop) { if (recv != len) { // Not enough data available to fulfill our obligation! +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("\tI2CDevice did not receive enough data: ")); + DEBUG_SERIAL.println(recv); +#endif return false; } @@ -169,20 +202,20 @@ bool Adafruit_I2CDevice::_read(uint8_t *buffer, size_t len, bool stop) { buffer[i] = _wire->read(); } -// #ifdef DEBUG_SERIAL -// DEBUG_SERIAL.print(F("\tI2CREAD @ 0x")); -// DEBUG_SERIAL.print(_addr, HEX); -// DEBUG_SERIAL.print(F(" :: ")); -// for (uint16_t i = 0; i < len; i++) { -// DEBUG_SERIAL.print(F("0x")); -// DEBUG_SERIAL.print(buffer[i], HEX); -// DEBUG_SERIAL.print(F(", ")); -// if (len % 32 == 31) { -// DEBUG_SERIAL.println(); -// } -// } -// DEBUG_SERIAL.println(); -// #endif +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("\tI2CREAD @ 0x")); + DEBUG_SERIAL.print(_addr, HEX); + DEBUG_SERIAL.print(F(" :: ")); + for (uint16_t i = 0; i < len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + if (len % 32 == 31) { + DEBUG_SERIAL.println(); + } + } + DEBUG_SERIAL.println(); +#endif return true; } diff --git a/src/modules/rfid/lib_pn532/Adafruit_PN532.cpp b/src/modules/rfid/lib_pn532/Adafruit_PN532.cpp index 71e3b59..dfe0446 100644 --- a/src/modules/rfid/lib_pn532/Adafruit_PN532.cpp +++ b/src/modules/rfid/lib_pn532/Adafruit_PN532.cpp @@ -450,59 +450,6 @@ bool Adafruit_PN532::writeGPIO(uint8_t pinstate) { return (pn532_packetbuffer[offset] == 0x0F); } - -/**************************************************************************/ -/*! - @brief Read a PN532 register. - - @param reg the 16-bit register address. - - @returns The register value. -*/ -/**************************************************************************/ -uint8_t Adafruit_PN532::readRegister(uint16_t reg) -{ - uint8_t response; - - pn532_packetbuffer[0] = PN532_COMMAND_READREGISTER; - pn532_packetbuffer[1] = (reg >> 8) & 0xFF; - pn532_packetbuffer[2] = reg & 0xFF; - - writecommand(pn532_packetbuffer, 3); - - // read data packet - readdata(pn532_packetbuffer, sizeof(pn532_packetbuffer)); - - response = pn532_packetbuffer[0]; - - return response; -} - -/**************************************************************************/ -/*! - @brief Write to a PN532 register. - - @param reg the 16-bit register address. - @param val the 8-bit value to write. - - @returns 0 for failure, 1 for success. -*/ -/**************************************************************************/ -uint8_t Adafruit_PN532::writeRegister(uint16_t reg, uint8_t val) -{ - pn532_packetbuffer[0] = PN532_COMMAND_WRITEREGISTER; - pn532_packetbuffer[1] = (reg >> 8) & 0xFF; - pn532_packetbuffer[2] = reg & 0xFF; - pn532_packetbuffer[3] = val; - - writecommand(pn532_packetbuffer, 4); - - // read data packet - readdata(pn532_packetbuffer, sizeof(pn532_packetbuffer)); - - return 1; -} - /**************************************************************************/ /*! Reads the state of the PN532's GPIO pins @@ -642,6 +589,9 @@ bool Adafruit_PN532::readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, 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 } @@ -694,6 +644,11 @@ bool Adafruit_PN532::readDetectedPassiveTargetID(uint8_t *uid, 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; @@ -709,9 +664,19 @@ bool Adafruit_PN532::readDetectedPassiveTargetID(uint8_t *uid, /* 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; } @@ -936,7 +901,6 @@ bool Adafruit_PN532::mifareclassic_IsTrailerBlock(uint32_t uiBlock) { return ((uiBlock + 1) % 16 == 0); } - /**************************************************************************/ /*! Tries to authenticate a block of memory on a MIFARE card using the @@ -969,14 +933,14 @@ uint8_t Adafruit_PN532::mifareclassic_AuthenticateBlock(uint8_t *uid, 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 +// #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] = @@ -1258,9 +1222,17 @@ uint8_t Adafruit_PN532::mifareclassic_WriteNDEFURI(uint8_t sectorNumber, 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 */ @@ -1269,6 +1241,9 @@ uint8_t Adafruit_PN532::mifareultralight_ReadPage(uint8_t page, /* Send the command */ if (!sendCommandCheckAck(pn532_packetbuffer, 4)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Failed to receive ACK for write command")); +#endif return 0; } @@ -1382,9 +1357,17 @@ uint8_t Adafruit_PN532::ntag2xx_ReadPage(uint8_t page, uint8_t *buffer) { // 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 */ @@ -1393,6 +1376,9 @@ uint8_t Adafruit_PN532::ntag2xx_ReadPage(uint8_t page, uint8_t *buffer) { /* Send the command */ if (!sendCommandCheckAck(pn532_packetbuffer, 4)) { +#ifdef MIFAREDEBUG + PN532DEBUGPRINT.println(F("Failed to receive ACK for write command")); +#endif return 0; } @@ -1887,3 +1873,120 @@ void Adafruit_PN532::writecommand(uint8_t *cmd, uint8_t cmdlen) { } } } + + +/**************************************************************************/ +/*! + @brief Builds the command to send to write to the PN532 registers + @param reg Pointer to the command buffer, goes in the form of + ADDR1H ADDR1L VAL1...ADDRnH ADDRnL VALn + @param len Command length in bytes +*/ +/**************************************************************************/ + +bool Adafruit_PN532::WriteRegister(uint8_t *reg, uint8_t len) { + uint8_t cmd[len + 1]; + uint8_t result[8]; + cmd[0] = PN532_COMMAND_WRITEREGISTER; + for (uint8_t i = 0; i < len; i++) { + cmd[i + 1] = reg[i]; + } + if (sendCommandCheckAck(cmd, len + 1)) { + readdata(result, 8); + return true; + } else { + return false; + } +} + +/**************************************************************************/ +/*! + @brief Builds the command to send commands directly to the PICC + Works like inDataExchange but doesn't handles all the + protocol features + @param data Pointer to the command buffer + @param len Command length in bytes +*/ +/**************************************************************************/ +bool Adafruit_PN532::InCommunicateThru(uint8_t *data, uint8_t len) { + uint8_t cmd[len + 1]; + uint8_t result[8]; + cmd[0] = PN532_COMMAND_INCOMMUNICATETHRU; + for (uint8_t i = 0; i < len; i++) { + cmd[i + 1] = data[i]; + } + if (sendCommandCheckAck(cmd, len + 1)) { + readdata(result, 8); + // If byte 8 isn't 0x00 we probably have an error, + if (result[7] != 0x00) { + return false; + } + return true; + } else { + return false; + } +} + +/**************************************************************************/ +/*! + @brief Performs the command sequence in some chinese MiFare Classic tags + that unlocks a special backdoor to perform write/read + commands without authentication + >HALT + CRC (50 00 57 CD) + >UNLOCK1 (40) 7 bits + UNLOCK2 (43) + Date: Sat, 31 Aug 2024 00:29:59 -0300 Subject: [PATCH 03/11] add wigleBasicToken to bruce.conf --- src/core/globals.h | 1 + src/core/settings.cpp | 7 +++++-- src/main.cpp | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/globals.h b/src/core/globals.h index 9b68ab8..282b4c4 100644 --- a/src/core/globals.h +++ b/src/core/globals.h @@ -123,6 +123,7 @@ extern float RfFreq; extern int RfidModule; extern String cachedPassword; +extern String wigleBasicToken; // Screen sleep control variables extern unsigned long previousMillis; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index daf762d..2e9c13c 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -739,9 +739,9 @@ void getConfigs() { if(file) { // init with default settings #if ROTATION >1 - file.print("[{\"rot\":3,\"dimmerSet\":10,\"bright\":100,\"wui_usr\":\"admin\",\"wui_pwd\":\"bruce\",\"Bruce_FGCOLOR\":43023,\"IrTx\":"+String(LED)+",\"IrRx\":"+String(GROVE_SCL)+",\"RfTx\":"+String(GROVE_SDA)+",\"RfRx\":"+String(GROVE_SCL)+",\"tmz\":3,\"RfModule\":0,\"RfFreq\":433.92,\"RfidModule\":"+String(RfidModule)+",\"wifi\":[{\"ssid\":\"myNetSSID\",\"pwd\":\"myNetPassword\"}],\"devMode\":0}]"); + file.print("[{\"rot\":3,\"dimmerSet\":10,\"bright\":100,\"wui_usr\":\"admin\",\"wui_pwd\":\"bruce\",\"Bruce_FGCOLOR\":43023,\"IrTx\":"+String(LED)+",\"IrRx\":"+String(GROVE_SCL)+",\"RfTx\":"+String(GROVE_SDA)+",\"RfRx\":"+String(GROVE_SCL)+",\"tmz\":3,\"RfModule\":0,\"RfFreq\":433.92,\"RfidModule\":"+String(RfidModule)+",\"wifi\":[{\"ssid\":\"myNetSSID\",\"pwd\":\"myNetPassword\"}],\"wigleBasicToken\":\"\",\"devMode\":0}]"); #else - file.print("[{\"rot\":1,\"dimmerSet\":10,\"bright\":100,\"wui_usr\":\"admin\",\"wui_pwd\":\"bruce\",\"Bruce_FGCOLOR\":43023,\"IrTx\":"+String(LED)+",\"IrRx\":"+String(GROVE_SCL)+",\"RfTx\":"+String(GROVE_SDA)+",\"RfRx\":"+String(GROVE_SCL)+",\"tmz\":3,\"RfModule\":0,\"RfFreq\":433.92,\"RfidModule\":"+String(RfidModule)+",\"wifi\":[{\"ssid\":\"myNetSSID\",\"pwd\":\"myNetPassword\"}],\"devMode\":0}]"); + file.print("[{\"rot\":1,\"dimmerSet\":10,\"bright\":100,\"wui_usr\":\"admin\",\"wui_pwd\":\"bruce\",\"Bruce_FGCOLOR\":43023,\"IrTx\":"+String(LED)+",\"IrRx\":"+String(GROVE_SCL)+",\"RfTx\":"+String(GROVE_SDA)+",\"RfRx\":"+String(GROVE_SCL)+",\"tmz\":3,\"RfModule\":0,\"RfFreq\":433.92,\"RfidModule\":"+String(RfidModule)+",\"wifi\":[{\"ssid\":\"myNetSSID\",\"pwd\":\"myNetPassword\"}],\"wigleBasicToken\":\"\",\"devMode\":0}]"); #endif } file.close(); @@ -779,6 +779,8 @@ void getConfigs() { if(!setting.containsKey("wifi")) { count++; log_i("Fail"); } + if(setting.containsKey("wigleBasicToken")) { wigleBasicToken = setting["wigleBasicToken"].as(); } else { count++; log_i("Fail"); } + if(setting.containsKey("devMode")) { devMode = setting["devMode"].as(); } else { count++; log_i("Fail"); } log_i("Brightness: %d", bright); @@ -852,6 +854,7 @@ void saveConfigs() { WifiObj["pwd"] = "myNetPassword"; } } + setting["wigleBasicToken"] = wigleBasicToken; setting["devMode"] = devMode; // Open file for writing File file = fs->open(CONFIG_FILE, FILE_WRITE); diff --git a/src/main.cpp b/src/main.cpp index 838d7fd..0e32e03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,7 @@ int RfModule=0; // 0 - single-pinned, 1 - CC1101+SPI float RfFreq=433.92; int RfidModule=M5_RFID2_MODULE; String cachedPassword=""; +String wigleBasicToken=""; int dimmerSet; int bright=100; int tmz=3; From 3b8d0d46fe64d499cf39ea2d29df57d66077e85a Mon Sep 17 00:00:00 2001 From: Rennan Cockles Date: Sat, 31 Aug 2024 01:05:05 -0300 Subject: [PATCH 04/11] fix pn532 clone --- src/modules/rfid/PN532.cpp | 33 +++++++++++++++------------------ src/modules/rfid/PN532.h | 1 - 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/modules/rfid/PN532.cpp b/src/modules/rfid/PN532.cpp index 3659027..9486b11 100644 --- a/src/modules/rfid/PN532.cpp +++ b/src/modules/rfid/PN532.cpp @@ -43,21 +43,21 @@ int PN532::clone() { if (_tag_read_uid.sak != uid.sak) return TAG_NOT_MATCH; - // uint8_t data[16]; - // byte bcc = 0; - // int i; - // for (i = 0; i < uid.size; i++) { - // data[i] = uid.uidByte[i]; - // bcc = bcc ^ uid.uidByte[i]; - // } - // data[i++] = bcc; - // data[i++] = uid.sak; - // data[i++] = uid.atqaByte[1]; - // data[i++] = uid.atqaByte[0]; - // byte tmp = 0; - // while (i<16) data[i++] = 0x62+tmp++; - - bool success = nfc.mifareclassic_WriteBlock0(block0_data); + uint8_t data[16]; + byte bcc = 0; + int i; + for (i = 0; i < uid.size; i++) { + data[i] = uid.uidByte[i]; + bcc = bcc ^ uid.uidByte[i]; + } + data[i++] = bcc; + data[i++] = uid.sak; + data[i++] = uid.atqaByte[1]; + data[i++] = uid.atqaByte[0]; + byte tmp = 0; + while (i<16) data[i++] = 0x62+tmp++; + + bool success = nfc.mifareclassic_WriteBlock0(data); return success ? SUCCESS : FAILURE; } @@ -404,7 +404,6 @@ bool PN532::read_mifare_classic_data_sector(uint8_t *key, byte sector) { if (!success) { return false; } - if (blockAddr == 0) memcpy(block0_data, buffer, 16); for (byte index = 0; index < 16; index++) { strPage += buffer[index] < 0x10 ? F(" 0") : F(" "); strPage += String(buffer[index], HEX); @@ -474,8 +473,6 @@ bool PN532::read_mifare_ultralight_data_blocks() { success = nfc.ntag2xx_ReadPage(page, buffer); if (!success) return false; - if (page == 0) memcpy(block0_data, buffer, 16); - for (byte offset = 0; offset < 4; offset++) { strPage = ""; for (byte index = 0; index < 4; index++) { diff --git a/src/modules/rfid/PN532.h b/src/modules/rfid/PN532.h index ca0551b..edb93be 100644 --- a/src/modules/rfid/PN532.h +++ b/src/modules/rfid/PN532.h @@ -45,7 +45,6 @@ class PN532 : public RFIDInterface { private: bool _use_i2c; byte pn532_packetbuffer[64]; - byte block0_data[16]; Uid _tag_read_uid; ///////////////////////////////////////////////////////////////////////////////////// From 2f8f68e55685a86fcb5f2b806c14e154d7cddc69 Mon Sep 17 00:00:00 2001 From: Rennan Cockles Date: Sun, 1 Sep 2024 01:49:31 -0300 Subject: [PATCH 05/11] Add file info option to file menu --- src/core/sd_functions.cpp | 50 +++++++++++++++++++++++++++++++++++++++ src/core/sd_functions.h | 2 ++ 2 files changed, 52 insertions(+) diff --git a/src/core/sd_functions.cpp b/src/core/sd_functions.cpp index f3461e4..391794f 100644 --- a/src/core/sd_functions.cpp +++ b/src/core/sd_functions.cpp @@ -624,6 +624,7 @@ String loopSD(FS &fs, bool filePicker, String allowed_ext) { clearFileList(fileList); options = { {"View File", [=]() { viewFile(fs, filepath); }}, + {"File Info", [=]() { fileInfo(fs, filepath); }}, {"Rename", [=]() { renameFile(fs, filepath, filename); }}, {"Copy", [=]() { copyFile(fs, filepath); }}, {"Delete", [=]() { deleteFromSd(fs, filepath); }}, @@ -893,3 +894,52 @@ bool getFsStorage(FS *&fs) { return true; } + +/********************************************************************* +** Function: fileInfo +** Display file info +**********************************************************************/ +void fileInfo(FS fs, String filepath) { + tft.fillScreen(BGCOLOR); + tft.setCursor(0,0); + tft.setTextColor(FGCOLOR, BGCOLOR); + tft.setTextSize(FP); + + File file = fs.open(filepath, FILE_READ); + if (!file) return; + + int bytesize = file.size(); + float filesize = bytesize; + String unit = "B"; + + time_t modifiedTime = file.getLastWrite(); + + if (filesize >= 1000000) { + filesize /= 1000000.0; + unit = "MB"; + } else if (filesize >= 1000) { + filesize /= 1000.0; + unit = "kB"; + } + + padprintln(""); + tft.drawCentreString("-"+String(file.name()), WIDTH/2, tft.getCursorY(), 1); + padprintln("\n"); + padprintln("Path: " + filepath); + padprintln(""); + padprintf("Bytes: %d\n", bytesize); + padprintln(""); + padprintf("Size: %.02f %s\n", filesize, unit.c_str()); + padprintln(""); + padprintf("Modified: %s\n", ctime(&modifiedTime)); + + file.close(); + delay(100); + + while(1) { + if(checkEscPress() || checkSelPress()) break; + delay(100); + } + + return; +} \ No newline at end of file diff --git a/src/core/sd_functions.h b/src/core/sd_functions.h index 716b26f..5af3973 100644 --- a/src/core/sd_functions.h +++ b/src/core/sd_functions.h @@ -46,3 +46,5 @@ bool checkLittleFsSize(); bool checkLittleFsSizeNM(); //Don't display msg bool getFsStorage(FS *&fs); + +void fileInfo(FS fs, String filepath); From 647937c1e5c8a1eeddeafddd2119ab203c8cea8d Mon Sep 17 00:00:00 2001 From: Rennan Cockles Date: Sun, 1 Sep 2024 16:40:06 -0300 Subject: [PATCH 06/11] add upload to wigle --- src/core/display.cpp | 4 +- src/core/display.h | 2 +- src/core/sd_functions.cpp | 6 ++ src/modules/wifi/wigle.cpp | 188 +++++++++++++++++++++++++++++++++++++ src/modules/wifi/wigle.h | 40 ++++++++ 5 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 src/modules/wifi/wigle.cpp create mode 100644 src/modules/wifi/wigle.h diff --git a/src/core/display.cpp b/src/core/display.cpp index 79ff419..09f64e8 100644 --- a/src/core/display.cpp +++ b/src/core/display.cpp @@ -279,12 +279,12 @@ int loopOptions(std::vector