diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 24077f12f..e107c385d 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -776,9 +776,8 @@ #define MP3_VOLUME 30 // Set the startup volume on init, the range can be 0..100(max) // #define USE_DY_SV17F // Use of DY-SV17F MP3 Player commands: play, stop, track and volume //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger (+1k6 code) -//#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) -// #define USE_PN532_DATA_FUNCTION // Add sensor40 command support for erase, setting data block content (+1k7 code, 388 bytes mem) -// #define USE_PN532_DATA_RAW // Allow DATA block to be used by non-alpha-numberic data (+ 80 bytes code, 48 bytes ram) +//#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k7 code, 156 bytes mem) +// #define USE_PN532_DATA_FUNCTION // Add sensor40 command support for erase, setting data block content (+3k code, 32 bytes mem) //#define USE_RDM6300 // Add support for RDM6300 125kHz RFID Reader (+0k8) //#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) //#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+3k1 code, +132 bytes RAM) diff --git a/tasmota/tasmota_xsns_sensor/xsns_40_pn532.ino b/tasmota/tasmota_xsns_sensor/xsns_40_pn532.ino index 03eba20c7..aaad8602a 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_40_pn532.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_40_pn532.ino @@ -1,7 +1,7 @@ /* xsns_40_pn532.ino - Support for PN532 (HSU) NFC Tag Reader on Tasmota - Copyright (C) 2021 Andre Thomas and Theo Arends + Copyright (C) 2021 Andre Thomas, Theo Arends and md5sum-as (https://github.com/md5sum-as) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,24 +47,48 @@ TasmotaSerial *PN532_Serial; #define PN532_COMMAND_SAMCONFIGURATION 0x14 #define PN532_COMMAND_RFCONFIGURATION 0x32 #define PN532_COMMAND_INDATAEXCHANGE 0x40 +#define PN532_COMMAND_INCOMMUNICATETHRU 0x42 #define PN532_COMMAND_INLISTPASSIVETARGET 0x4A - +#define PN532_COMMAND_INRELEASE 0x52 +#define PN532_COMMAND_INSELECT 0x54 #define PN532_MIFARE_ISO14443A 0x00 #define MIFARE_CMD_READ 0x30 #define MIFARE_CMD_AUTH_A 0x60 #define MIFARE_CMD_AUTH_B 0x61 #define MIFARE_CMD_WRITE 0xA0 +#define NTAG21X_CMD_GET_VERSION 0x60 +#define NTAG2XX_CMD_READ 0x30 +#define NTAG21X_CMD_FAST_READ 0x3A +#define NTAG21X_CMD_PWD_AUTH 0x1B +#define NTAG2XX_CMD_WRITE 0xA2 + +const struct { + uint8_t version[6]; + uint8_t confPage; +} NTAG[] PROGMEM ={ + {.version={0x04, 0x02, 0x01, 0x00, 0x0f, 0x03},.confPage=0x29}, /* NTAG213 */ + {.version={0x04, 0x02, 0x01, 0x00, 0x11, 0x03},.confPage=0x83}, /* NTAG215 */ + {.version={0x04, 0x02, 0x01, 0x00, 0x13, 0x03},.confPage=0xe3}, /* NTAG216 */ + {.version={0x04, 0x05, 0x02, 0x02, 0x13, 0x03},.confPage=0xe3}, /* NT3H2111 */ + {.version={0x04, 0x05, 0x02, 0x02, 0x15, 0x03},.confPage=0xe3}, /* NT3H2211 */ +}; +#define NTAG_CNT (sizeof(NTAG)/7) // num records in NTAG array + struct PN532 { char uids[21]; // Number of bytes in the UID. 4, 7 or 10 uint8_t packetbuffer[64]; // Global buffer used to store packet uint8_t command = 0; // Carry command code between functions uint8_t scantimer = 0; // Prevent multiple successful reads within 2 second window bool present = false; // Maintain detection flag + uint16_t atqa; #ifdef USE_PN532_DATA_FUNCTION uint8_t newdata[16]; uint8_t function = 0; - uint8_t newdata_len = 0; + uint32_t pwd_auth=0x64636261; + uint16_t pwd_pack=0x6665; + uint32_t pwd_auth_new; + uint16_t pwd_pack_new; #endif // USE_PN532_DATA_FUNCTION } Pn532; @@ -273,9 +297,9 @@ bool PN532_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidL return 0; } - uint16_t sens_res = Pn532.packetbuffer[2]; - sens_res <<= 8; - sens_res |= Pn532.packetbuffer[3]; + Pn532.atqa = Pn532.packetbuffer[2]; + Pn532.atqa <<= 8; + Pn532.atqa |= Pn532.packetbuffer[3]; /* Card appears to be Mifare Classic */ *uidLength = Pn532.packetbuffer[5]; @@ -310,9 +334,27 @@ bool PN532_SAMConfig(void) { return (0 < PN532_readResponse(Pn532.packetbuffer, sizeof(Pn532.packetbuffer))); } +/* void PN532_inSelect(void) { + Pn532.packetbuffer[0] = PN532_COMMAND_INSELECT; + Pn532.packetbuffer[1] = 1; + if (PN532_writeCommand(Pn532.packetbuffer, 2)) { + return ; + } + int16_t res = PN532_readResponse(Pn532.packetbuffer, sizeof(Pn532.packetbuffer)); +} */ + #ifdef USE_PN532_DATA_FUNCTION -uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) { +void PN532_inRelease(void) { + Pn532.packetbuffer[0] = PN532_COMMAND_INRELEASE; + Pn532.packetbuffer[1] = 1; + if (PN532_writeCommand(Pn532.packetbuffer, 2)) { + return; + } + PN532_readResponse(Pn532.packetbuffer, sizeof(Pn532.packetbuffer)); +} + +uint8_t PN532_mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) { uint8_t i; uint8_t _key[6]; uint8_t _uid[7]; @@ -349,7 +391,7 @@ uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t return 1; } -uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) { +uint8_t PN532_mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) { /* Prepare the command */ Pn532.packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; Pn532.packetbuffer[1] = 1; /* Card number */ @@ -376,7 +418,7 @@ uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) { return 1; } -uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) { +uint8_t PN532_mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) { /* Prepare the first command */ Pn532.packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; Pn532.packetbuffer[1] = 1; /* Card number */ @@ -393,73 +435,181 @@ uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) { return (0 < PN532_readResponse(Pn532.packetbuffer, sizeof(Pn532.packetbuffer))); } +uint8_t PN532_ntag21x_probe (void) { + uint8_t result=0; + + Pn532.packetbuffer[0] = PN532_COMMAND_INCOMMUNICATETHRU; + Pn532.packetbuffer[1] = NTAG21X_CMD_GET_VERSION; + + if (PN532_writeCommand(Pn532.packetbuffer, 2)) { + return result; + } + + if (PN532_readResponse(Pn532.packetbuffer, sizeof(Pn532.packetbuffer))<9){ + return result; + } + + if (Pn532.packetbuffer[3] != 4) { // not NTAG type + return result; + } + + for (uint8_t i=0; i0) { + /* NTAG EV1 found*/ + str_pwd=PWD_NONE; + if (!PN532_ntag2xx_read16(4, card_datas)) { + if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, nuid, &nuid_len)) { + if (memcmp(uid, nuid, sizeof(uid))==0) { + if (PN532_ntag21x_auth()) { + str_pwd=PWD_OK; + if (Pn532.function == 3) { /* new password */ + success = PN532_ntag21x_set_password(confPage, false); + } + if (Pn532.function == 4) { /* clear password */ + success = PN532_ntag21x_set_password(confPage, true); + } + } else { + str_pwd=PWD_NOK; + } + if (!PN532_ntag2xx_read16(4, card_datas)) card_datas[0]=0; + } + } + } else { + if (Pn532.function == 3) { /* new password */ + success = PN532_ntag21x_set_password(confPage, false); + } + } + } else { + if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, nuid, &nuid_len)) { + if (memcmp(uid, nuid, sizeof(uid))==0) { + if (!PN532_ntag2xx_read16(4, card_datas)) card_datas[0]=0; + } + } + } + if ((Pn532.function == 1) || (Pn532.function == 2)) { + success = PN532_ntag2xx_write16(4, (char *)Pn532.newdata); + if (!PN532_ntag2xx_read16(4, card_datas)) card_datas[0]=0; + } + } + else if (uid_len == 4) { // Lets try to read blocks 1 & 2 of the mifare classic card for more information uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - if (mifareclassic_AuthenticateBlock (uid, uid_len, 1, 1, keyuniversal)) { - uint8_t card_data[16]; - if (mifareclassic_ReadDataBlock(1, card_data)) { -#ifdef USE_PN532_DATA_RAW - memcpy(&card_datas,&card_data,sizeof(card_data)); -#else - for (uint32_t i = 0;i < sizeof(card_data);i++) { - if ((isalpha(card_data[i])) || ((isdigit(card_data[i])))) { - card_datas[i] = char(card_data[i]); - } else { - card_datas[i] = '\0'; - } - } -#endif // USE_PN532_DATA_RAW + if (PN532_mifareclassic_AuthenticateBlock (uid, uid_len, 1, 1, keyuniversal)) { + if ((Pn532.function == 1) || (Pn532.function == 2)) { + success=PN532_mifareclassic_WriteDataBlock(1, Pn532.newdata); } - if (Pn532.function == 1) { // erase block 1 of card - for (uint32_t i = 0;i<16;i++) { - card_data[i] = 0x00; - } - if (mifareclassic_WriteDataBlock(1, card_data)) { - erase_success = true; - AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); - memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string - } - } - if (Pn532.function == 2) { -#ifdef USE_PN532_DATA_RAW - memcpy(&card_data,&Pn532.newdata,sizeof(card_data)); - if (mifareclassic_WriteDataBlock(1, card_data)) { - set_success = true; - AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); - memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string - } -#else - bool IsAlphaNumeric = true; - for (uint32_t i = 0;i < Pn532.newdata_len;i++) { - if ((!isalpha(Pn532.newdata[i])) && (!isdigit(Pn532.newdata[i]))) { - IsAlphaNumeric = false; + if (PN532_mifareclassic_ReadDataBlock(1, (uint8_t *)card_datas)) { + for (uint32_t i = 0; i < 16; i++) { + if (!isprint(card_datas[i])) { + // do not output non-printable characters to the console + card_datas[i] = 0; } } - if (IsAlphaNumeric) { - memcpy(&card_data,&Pn532.newdata,Pn532.newdata_len); - card_data[Pn532.newdata_len] = '\0'; // Enforce null termination - if (mifareclassic_WriteDataBlock(1, card_data)) { - set_success = true; - AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); - memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string - } - } else { - AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); - } -#endif // USE_PN532_DATA_RAW + } else { + card_datas[0] = 0; } } else { sprintf_P(card_datas, PSTR("AUTHFAIL")); @@ -467,19 +617,50 @@ void PN532_ScanForTag(void) { } switch (Pn532.function) { case 0x01: - if (!erase_success) { - AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); + if (success) { + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Erase success")); + } else { + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Erase fail - exiting erase mode")); } break; case 0x02: - if (!set_success) { - AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); + if (success) { + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Data write successful")); + } else{ + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Write failed - exiting set mode")); } + break; + case 0x03: + if (success) { + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Set password successful")); + } else{ + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Set password failed - exiting set mode")); + } + break; + case 0x04: + if (success) { + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Unset password successful")); + } else{ + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Unset password failed - exiting set mode")); + } + break; default: break; } Pn532.function = 0; - ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\",\"" D_JSON_DATA "\":\"%s\"}}"), Pn532.uids, card_datas); + card_datas[16] = 0; + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\",\"" D_JSON_DATA "\":\"%s\""), Pn532.uids, card_datas); + if (str_pwd == PWD_NONE) { + ResponseAppend_P(PSTR(",\"Auth\":\"None\"")); + } else + if (str_pwd == PWD_OK) { + ResponseAppend_P(PSTR(",\"Auth\":\"Ok\"")); + } else + if (str_pwd == PWD_NOK) { + ResponseAppend_P(PSTR(",\"Auth\":\"NOk\"")); + } + ResponseAppend_P(PSTR("}}")); + PN532_inRelease(); #else ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), Pn532.uids); #endif // USE_PN532_DATA_FUNCTION @@ -492,44 +673,70 @@ void PN532_ScanForTag(void) { #ifdef USE_PN532_DATA_FUNCTION bool PN532_Command(void) { - bool serviced = true; - uint8_t paramcount = 0; - if (XdrvMailbox.data_len > 0) { - paramcount=1; - } else { - serviced = false; + bool serviced = false; + char command[10]; + char log[70]; + if (ArgC() < 1) { return serviced; } + char argument[XdrvMailbox.data_len]; - for (uint32_t ca=0;ca 1) { - if (XdrvMailbox.data[XdrvMailbox.data_len-1] == ',') { - serviced = false; - return serviced; - } + if (!strcmp_P(argument,PSTR("WRITE"))) { + if (ArgC() > 1) { ArgV(argument, 2); - Pn532.newdata_len = strlen(argument); - if (Pn532.newdata_len > 15) { Pn532.newdata_len = 15; } - memcpy(&Pn532.newdata,&argument,Pn532.newdata_len); - Pn532.newdata[Pn532.newdata_len] = 0x00; // Null terminate the string + memset(Pn532.newdata,0,sizeof(Pn532.newdata)); + strncpy((char *)Pn532.newdata,argument,sizeof(Pn532.newdata)); + if (strlen(argument)>16) argument[16]=0; Pn532.function = 2; - AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), Pn532.newdata); - ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); - return serviced; + snprintf_P(log, sizeof(log), PSTR("data block 1 (4-7 for NTAG) will be set to '%s'"), argument); + serviced = true; } } - return false; + if (!strcmp_P(argument,PSTR("AUTH"))) { + if (ArgC() > 1) { + Pn532.pwd_auth=strtoul(ArgV(argument,2),nullptr,0); + } + if (ArgC() > 2) { + Pn532.pwd_pack=strtoul(ArgV(argument,3),nullptr,0); + } + serviced = true; + } + if (!strcmp_P(argument,PSTR("SET_PWD"))) { + snprintf_P(log, sizeof(log), PSTR("will be protected")); + Pn532.pwd_auth_new=Pn532.pwd_auth; + Pn532.pwd_pack_new=Pn532.pwd_pack; + if (ArgC() > 1) { + Pn532.pwd_auth_new=strtoul(ArgV(argument,2),nullptr,0); + } + if (ArgC() > 2) { + Pn532.pwd_pack_new=strtoul(ArgV(argument,3),nullptr,0); + } + Pn532.function = 3; + serviced = true; + } + if (!strcmp_P(argument,PSTR("UNSET_PWD"))) { + snprintf_P(log, sizeof(log), PSTR("will be unprotected")); + Pn532.function = 4; + serviced = true; + } + if (!strcmp_P(argument,PSTR("CANCEL"))) { + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Job canceled")); + Pn532.function = 0; + serviced = true; + } else { + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 - Next scanned tag %s"), log); + } + if (serviced) ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"%s\"}}"),command); + return serviced; } #endif // USE_PN532_DATA_FUNCTION