From 549080b8509e8e17b6eb7e718ab1089f0eee66a3 Mon Sep 17 00:00:00 2001 From: andrethomas Date: Tue, 8 Jan 2019 20:53:09 +0200 Subject: [PATCH] PN532: Add Read/Write Data support --- sonoff/my_user_config.h | 3 +- sonoff/xsns_40_pn532_i2c.ino | 209 ++++++++++++++++++++++++++++++++++- 2 files changed, 206 insertions(+), 6 deletions(-) diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 4f7c97dc8..73ccb9669 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -322,7 +322,8 @@ // #define USE_DS3231 // Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code) // #define USE_RTC_ADDR 0x68 // Default I2C address 0x68 // #define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) -// #define USE_PN532_I2C // Enable PN532 - Near Field Communication (NFC) controller (+1k6 code) +// #define USE_PN532_I2C // Enable PN532 - Near Field Communication (NFC) controller (+3k3 code, 508 bytes of mem) +// #define USE_PN532_CAUSE_EVENTS // Enable PN532 driver to cause event's on card read in addition to immediate telemetry // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/sonoff/xsns_40_pn532_i2c.ino b/sonoff/xsns_40_pn532_i2c.ino index 787f48faa..853faefef 100644 --- a/sonoff/xsns_40_pn532_i2c.ino +++ b/sonoff/xsns_40_pn532_i2c.ino @@ -34,8 +34,14 @@ #define PN532_COMMAND_GETFIRMWAREVERSION 0x02 #define PN532_COMMAND_SAMCONFIGURATION 0x14 +#define PN532_COMMAND_INDATAEXCHANGE 0x40 #define PN532_COMMAND_INLISTPASSIVETARGET 0x4A +#define MIFARE_CMD_READ 0x30 +#define MIFARE_CMD_AUTH_A 0x60 +#define MIFARE_CMD_AUTH_B 0x61 +#define MIFARE_CMD_WRITE 0xA0 + #define PN532_PREAMBLE 0x00 #define PN532_STARTCODE1 0x00 #define PN532_STARTCODE2 0xFF @@ -56,6 +62,8 @@ uint8_t pn532_i2c_packetbuffer[64]; uint8_t pn532_i2c_scan_defer_report = 0; // If a valid card was found we will not scan for one again in the same two seconds so we set this to 19 if a card was found uint8_t pn532_i2c_command = 0; uint8_t pn532_i2c_disable = 0; +uint8_t pn532_i2c_function = 0; +uint8_t pn532_i2c_newdata[16]; const uint8_t PROGMEM pn532_global_timeout = 10; @@ -227,8 +235,8 @@ bool PN532_SAMConfig(void) { pn532_i2c_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; pn532_i2c_packetbuffer[1] = 0x01; // normal mode; - pn532_i2c_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second - pn532_i2c_packetbuffer[3] = 0x01; // use IRQ pin! + pn532_i2c_packetbuffer[2] = 0x01; // timeout 50ms * 1 = 50ms + pn532_i2c_packetbuffer[3] = 0x01; // Disable IRQ pin if (PN532_writeCommand(pn532_i2c_packetbuffer, 4)) return false; @@ -277,26 +285,170 @@ boolean PN532_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *u return true; } +uint8_t 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]; + uint8_t _uidLen; + + // Hang on to the key and uid data + memcpy (_key, keyData, 6); + memcpy (_uid, uid, uidLen); + _uidLen = uidLen; + + // Prepare the authentication command // + pn532_i2c_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */ + pn532_i2c_packetbuffer[1] = 1; /* Max card numbers */ + pn532_i2c_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; + pn532_i2c_packetbuffer[3] = blockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */ + memcpy (&pn532_i2c_packetbuffer[4], _key, 6); + for (i = 0; i < _uidLen; i++) { + pn532_i2c_packetbuffer[10 + i] = _uid[i]; /* 4 bytes card ID */ + } + + if (PN532_writeCommand(pn532_i2c_packetbuffer, 10 + _uidLen)) + return 0; + + // Read the response packet + PN532_readResponse(pn532_i2c_packetbuffer, sizeof(pn532_i2c_packetbuffer)); + + // 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_i2c_packetbuffer[0] != 0x00) { + // Authentification failed + return 0; + } + + return 1; +} + +uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) +{ + /* Prepare the command */ + pn532_i2c_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_i2c_packetbuffer[1] = 1; /* Card number */ + pn532_i2c_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + pn532_i2c_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + + /* Send the command */ + if (PN532_writeCommand(pn532_i2c_packetbuffer, 4)) { + return 0; + } + + /* Read the response packet */ + PN532_readResponse(pn532_i2c_packetbuffer, sizeof(pn532_i2c_packetbuffer)); + + /* If byte 8 isn't 0x00 we probably have an error */ + if (pn532_i2c_packetbuffer[0] != 0x00) { + return 0; + } + + /* Copy the 16 data bytes to the output buffer */ + /* Block content starts at byte 9 of a valid response */ + memcpy (data, &pn532_i2c_packetbuffer[1], 16); + + return 1; +} + +uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) +{ + /* Prepare the first command */ + pn532_i2c_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_i2c_packetbuffer[1] = 1; /* Card number */ + pn532_i2c_packetbuffer[2] = MIFARE_CMD_WRITE; /* Mifare Write command = 0xA0 */ + pn532_i2c_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + memcpy(&pn532_i2c_packetbuffer[4], data, 16); /* Data Payload */ + + /* Send the command */ + if (PN532_writeCommand(pn532_i2c_packetbuffer, 20)) { + return 0; + } + + /* Read the response packet */ + return (0 < PN532_readResponse(pn532_i2c_packetbuffer, sizeof(pn532_i2c_packetbuffer))); +} + + void PN532_ScanForTag(void) { if (pn532_i2c_disable) { return; } uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; uint8_t uid_len = 0; + uint8_t card_data[16]; + boolean erase_success = false; + boolean set_success = false; if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uid_len)) { if (pn532_i2c_scan_defer_report > 0) { pn532_i2c_scan_defer_report--; } else { char uids[15]; + char card_datas[34]; sprintf(uids,""); for (uint8_t i = 0;i < uid_len;i++) { sprintf(uids,"%s%02X",uids,uid[i]); } + if (uid_len == 4) { // Lets try to read block 0 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)) { + if (mifareclassic_ReadDataBlock(1, card_data)) { + memcpy(&card_datas,card_data,sizeof(card_data)); // Cast block 0 to a string + } + if (pn532_i2c_function == 1) { // erase block 1 of card + for (uint8_t i = 0;i<16;i++) { + card_data[i] = 0x00; + } + if (mifareclassic_WriteDataBlock(1, card_data)) { + erase_success = true; + snprintf_P(log_data, sizeof(log_data),"I2C: PN532 NFC - Erase success"); + AddLog(LOG_LEVEL_INFO); + memcpy(&card_datas,card_data,sizeof(card_data)); // Cast block 0 to a string + } + } + if (pn532_i2c_function == 2) { + memcpy(&card_data,&pn532_i2c_newdata,sizeof(card_data)); + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + snprintf_P(log_data, sizeof(log_data),"I2C: PN532 NFC - Data write successful"); + AddLog(LOG_LEVEL_INFO); + memcpy(&card_datas,card_data,sizeof(card_data)); // Cast block 0 to a string + } + } + } else { + sprintf(card_datas,"AUTHFAIL"); + } + } + switch (pn532_i2c_function) { + case 0x01: + if (!erase_success) { + snprintf_P(log_data, sizeof(log_data),"I2C: PN532 NFC - Erase fail - exiting erase mode"); + AddLog(LOG_LEVEL_INFO); + } + break; + case 0x02: + if (!set_success) { + snprintf_P(log_data, sizeof(log_data),"I2C: PN532 NFC - Write failed - exiting set mode"); + AddLog(LOG_LEVEL_INFO); + } + default: + break; + } + pn532_i2c_function = 0; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PN532\":{\"UID\":\"%s\"}}"), mqtt_data, uids); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), mqtt_data, uids, card_datas); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - char command[27]; - sprintf(command,"event PN532=%s",uids); + +#ifdef USE_PN532_CAUSE_EVENTS + + char command[64]; + sprintf(command,"event PN532_UID=%s",uids); ExecuteCommand(command, SRC_RULE); + sprintf(command,"event PN532_DATA=%s",card_datas); + ExecuteCommand(command, SRC_RULE); + +#endif + pn532_i2c_scan_defer_report = 7; // Ignore tags found for two seconds } } else { @@ -304,6 +456,48 @@ void PN532_ScanForTag(void) } } +boolean PN532_Command(void) +{ + boolean serviced = true; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + char sub_string_tmp[XdrvMailbox.data_len]; + for (uint8_t ca=0;ca 1) { + sprintf(sub_string_tmp,subStr(sub_string, XdrvMailbox.data, ",", 2)); + uint8_t dlen = strlen(sub_string_tmp); + if (dlen > 15) { dlen = 15; } + memcpy(&pn532_i2c_newdata,&sub_string_tmp,dlen); + pn532_i2c_newdata[dlen] = 0x00; // Null terminate the string + pn532_i2c_function = 2; + snprintf_P(log_data, sizeof(log_data),"I2C: PN532 NFC - Next scanned tag data block 1 will be set to '%s'",pn532_i2c_newdata); + AddLog(LOG_LEVEL_INFO); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PN532\":{\"COMMAND\":\"S\"\"}}"), mqtt_data); + return serviced; + } + } +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -322,6 +516,11 @@ boolean Xsns40(byte function) case FUNC_EVERY_SECOND: PN532_Detect(); break; + case FUNC_COMMAND: + if (XSNS_40 == XdrvMailbox.index) { + result = PN532_Command(); + } + break; case FUNC_SAVE_BEFORE_RESTART: if (!pn532_i2c_disable) { pn532_i2c_disable = 1;