From 3932b576600951196559459d120cea86de51095f Mon Sep 17 00:00:00 2001 From: TheJoKlLa Date: Fri, 7 Oct 2022 01:58:26 +0200 Subject: [PATCH] WTF --- .gitmodules | 3 + src/FabReader_v2/.vscode/settings.json | 10 +- src/FabReader_v2/lib/rfid-desfire | 1 + src/FabReader_v2/src/NFC.cpp | 100 +- src/FabReader_v2/src/NFC.h | 11 +- src/FabReader_v2/src/OTAProxy.cpp | 31 +- src/FabReader_v2/src/OTAProxy.h | 1 - src/FabReader_v2/src/helpers.cpp | 61 + src/FabReader_v2/src/helpers.h | 11 + src/FabReader_v2/src/main.cpp | 231 ++-- src/FabReader_v2/src/main.cpp.deprecated | 1418 ++++++++++++++++++++++ src/FabReader_v2/src/main.cpp.old | 168 +++ 12 files changed, 1891 insertions(+), 155 deletions(-) create mode 100644 .gitmodules create mode 160000 src/FabReader_v2/lib/rfid-desfire create mode 100644 src/FabReader_v2/src/helpers.cpp create mode 100644 src/FabReader_v2/src/helpers.h create mode 100644 src/FabReader_v2/src/main.cpp.deprecated create mode 100644 src/FabReader_v2/src/main.cpp.old diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ae61f10 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/FabReader_v2/lib/rfid-desfire"] + path = src/FabReader_v2/lib/rfid-desfire + url = https://github.com/JPG-Consulting/rfid-desfire.git diff --git a/src/FabReader_v2/.vscode/settings.json b/src/FabReader_v2/.vscode/settings.json index 1428c38..355a413 100644 --- a/src/FabReader_v2/.vscode/settings.json +++ b/src/FabReader_v2/.vscode/settings.json @@ -1,5 +1,13 @@ { "files.associations": { - "random": "cpp" + "random": "cpp", + "cstddef": "cpp", + "array": "cpp", + "string_view": "cpp", + "initializer_list": "cpp", + "ranges": "cpp", + "utility": "cpp", + "string": "cpp", + "functional": "cpp" } } \ No newline at end of file diff --git a/src/FabReader_v2/lib/rfid-desfire b/src/FabReader_v2/lib/rfid-desfire new file mode 160000 index 0000000..ee897be --- /dev/null +++ b/src/FabReader_v2/lib/rfid-desfire @@ -0,0 +1 @@ +Subproject commit ee897bedb4c55ff488058b8f85df1650e64c8568 diff --git a/src/FabReader_v2/src/NFC.cpp b/src/FabReader_v2/src/NFC.cpp index 95fbca2..f17ec56 100644 --- a/src/FabReader_v2/src/NFC.cpp +++ b/src/FabReader_v2/src/NFC.cpp @@ -1,40 +1,51 @@ #include "NFC.h" #include #include -#include +#include +#include "helpers.h" NFC::NFC(int pin_ss, int pin_rst) { SPI.begin(); - rfid = new MFRC522(pin_ss, pin_rst); + rfid = new DESFire(pin_ss, pin_rst); rfid->PCD_Init(); } bool NFC::hasNewCard() { - if(rfid->PICC_IsNewCardPresent() && rfid->PICC_ReadCardSerial()) + if(rfid->PICC_IsNewCardPresent() && rfid->PICC_ReadCardSerial() && rfid->uid.sak == 0x20) { + Serial.println("New Card detected"); return true; - // byte atqaLen = sizeof(atqa); - // MFRC522::StatusCode status = rfid->PICC_RequestA(atqa, &atqaLen); - // Serial.printf("PICC_RequestA: 0x%02x\n", status); - // if(status == MFRC522::STATUS_OK && atqa[0] == 0x44) - // { - // return true; - // } } return false; } +MFRC522::Uid NFC::getUID() +{ + return uid; +} + bool NFC::selectCard() { - // MFRC522::StatusCode status = rfid->PICC_Select(&(rfid->uid)); - // Serial.printf("PICC_Select: 0x%02x\n", status); - // if(status != MFRC522::STATUS_OK) - // { - // return false; - // } + MFRC522::StatusCode status; + byte ats_buffer[16]; + byte ats_buffer_len = 16; + + status = rfid->PICC_RequestATS(ats_buffer, &ats_buffer_len); + if (status != MFRC522::STATUS_OK) + { + Serial.printf("PICC_RequestATS: 0x%02x\n", status); + return false; + } + + status = rfid->PICC_ProtocolAndParameterSelection(0x00, 0x11, 0x00); + if (status != MFRC522::STATUS_OK) + { + Serial.printf("PICC_ProtocolAndParameterSelection: 0x%02x\n", status); + return false; + } cardSelected = true; uid = rfid->uid; @@ -44,19 +55,17 @@ bool NFC::selectCard() bool NFC::deselectCard() { - // MFRC522::StatusCode status = rfid->PICC_HaltA(); - // Serial.printf("PICC_HaltA: 0x%02x\n", status); - // if(status != MFRC522::STATUS_OK) - // { - // return false; - // } - cardSelected = false; Serial.println("Card Deselected"); return true; } +bool NFC::hasCardSelected() +{ + return cardSelected; +} + bool NFC::isCardLost() { uint8_t control = 0x00; @@ -76,15 +85,46 @@ bool NFC::isCardLost() } control += 0x4; } - return !(control == 13 || control == 14); + + if(!(control == 13 || control == 14)) + { + Serial.println("Card Lost"); + return true; + } + else + { + return false; + } } -MFRC522::StatusCode NFC::Transceive(byte* command, byte command_len, byte* response, byte response_len) +MFRC522::StatusCode NFC::Transceive(byte* command, byte command_len, byte* response, byte* response_len) { - return rfid->PCD_TransceiveData(command, command_len, response, &response_len, NULL, 0, true); -} + byte buffer[APDU_BUFFER_SIZE + 4]; + byte buffer_len = command_len + 4; + MFRC522::StatusCode status; -MFRC522::Uid NFC::getUID() -{ - return uid; + buffer[0] = 0x0A; + buffer[1] = 0x00; + memcpy(&buffer[2], command, command_len); + + status = rfid->PCD_CalculateCRC(buffer, buffer_len - 2, &buffer[buffer_len - 2]); + if (status != MFRC522::STATUS_OK) + { + Serial.printf("PCD_CalculateCRC: 0x%02x\n", status); + return status; + } + + // Serial.println(buffer_len); + // char command_h[APDU_BUFFER_SIZE*2] = { 0 }; + // bytes2chars(buffer, buffer_len, command_h, true); + // Serial.println(command_h); + + status = rfid->PCD_TransceiveData(buffer, buffer_len, response, response_len); + if (status != MFRC522::STATUS_OK) + { + Serial.printf("PCD_TransceiveData: 0x%02x\n", status); + return status; + } + + return status; } \ No newline at end of file diff --git a/src/FabReader_v2/src/NFC.h b/src/FabReader_v2/src/NFC.h index df0fc02..04b0237 100644 --- a/src/FabReader_v2/src/NFC.h +++ b/src/FabReader_v2/src/NFC.h @@ -2,26 +2,27 @@ #define NFC_H #include -#include +#include +#define APDU_BUFFER_SIZE 256 class NFC { private: MFRC522::Uid uid; - bool cardSelected; - byte atqa[2]; + bool cardSelected = false; public: - MFRC522* rfid; + DESFire* rfid; NFC(int pin_ss, int pin_rst); bool hasNewCard(); bool selectCard(); bool deselectCard(); bool isCardLost(); + bool hasCardSelected(); MFRC522::Uid getUID(); - MFRC522::StatusCode Transceive(byte* command, byte command_len, byte* response, byte response_len); + MFRC522::StatusCode Transceive(byte* command, byte command_len, byte* response, byte* response_len); }; #endif diff --git a/src/FabReader_v2/src/OTAProxy.cpp b/src/FabReader_v2/src/OTAProxy.cpp index 1db28d5..c1bbb18 100644 --- a/src/FabReader_v2/src/OTAProxy.cpp +++ b/src/FabReader_v2/src/OTAProxy.cpp @@ -48,17 +48,26 @@ void OTAProxy::continueOTA(char* topic, byte* payload, unsigned int length) { Serial.println("Request OTA"); byte response[APDU_BUFFER_SIZE] = {0}; - - MFRC522::StatusCode status; - do - { - status = nfc->Transceive(payload, length, response, APDU_BUFFER_SIZE); - Serial.printf("PICC_Tranceive: 0x%02x\n", status); - delay(100); - } - while (status != MFRC522::STATUS_OK); + byte response_len; - for(int i = 0; i < APDU_BUFFER_SIZE; i++) + MFRC522::StatusCode status; + while (true) + { + if(nfc->isCardLost()) + { + Serial.println("Card Lost"); + return; + } + Serial.println("Run Transceive"); + status = nfc->Transceive(payload, length, response, &response_len); + Serial.printf("PICC_Tranceive: 0x%02x\n", status); + if(status == MFRC522::STATUS_OK) + { + break; + } + } + + for(int i = 0; i < response_len; i++) { char hexCar[2]; @@ -67,7 +76,7 @@ void OTAProxy::continueOTA(char* topic, byte* payload, unsigned int length) } Serial.println(); - mqtt->publish(topic_responseOTA, response, APDU_BUFFER_SIZE); + mqtt->publish(topic_responseOTA, response, response_len); Serial.println("Response OTA"); } else if(!strcmp(topic, topic_stopOTA)) diff --git a/src/FabReader_v2/src/OTAProxy.h b/src/FabReader_v2/src/OTAProxy.h index 596bffa..3fb1c24 100644 --- a/src/FabReader_v2/src/OTAProxy.h +++ b/src/FabReader_v2/src/OTAProxy.h @@ -4,7 +4,6 @@ #include #include "NFC.h" #define MSG_BUFFER_SIZE 50 -#define APDU_BUFFER_SIZE 256 class OTAProxy { diff --git a/src/FabReader_v2/src/helpers.cpp b/src/FabReader_v2/src/helpers.cpp new file mode 100644 index 0000000..0067c9d --- /dev/null +++ b/src/FabReader_v2/src/helpers.cpp @@ -0,0 +1,61 @@ +#include "helpers.h" + +void char2byte(char* str, byte* array) +{ + for(int i=0; i <2 ; i++) + { + char c = *str; + if (c >= '0' && c <= '9') + { + *array *= 16; + *array += c - '0'; + } + else if (c >= 'A' && c <= 'F') + { + *array *= 16; + *array += (c - 'A') + 10; + } + else if (c >= 'a' && c <= 'f') + { + *array *= 16; + *array += (c - 'a') + 10; + } + str++; + } +} + +void chars2bytes(char* str, byte* array, bool msb) +{ + int len = strlen(str); + for(int i = 0; i < len; i += 2) + { + if(msb) + { + char2byte(&str[i], &array[i/2]); + } + else + { + char2byte(&str[i], &array[len-2 - i/2]); + } + } +} + +void byte2char(byte* array, char* str) +{ + sprintf(str, "%02x", *array); +} + +void bytes2chars(byte* array, byte array_len, char* str, bool msb) +{ + for(int i = 0; i < array_len; i++) + { + if(msb) + { + byte2char(&array[i], &str[i*2]); + } + else + { + byte2char(&array[array_len-2 - i], &str[i*2]); + } + } +} \ No newline at end of file diff --git a/src/FabReader_v2/src/helpers.h b/src/FabReader_v2/src/helpers.h new file mode 100644 index 0000000..3e2d5b9 --- /dev/null +++ b/src/FabReader_v2/src/helpers.h @@ -0,0 +1,11 @@ +#ifndef HELPERS_H +#define HELPERS_H + +#include +void char2byte(char* str, byte* array); +void chars2bytes(char* str, byte* array, bool msb); + +void byte2char(byte* array, char* str); +void bytes2chars(byte* array, byte array_len, char* str, bool msb); + +#endif \ No newline at end of file diff --git a/src/FabReader_v2/src/main.cpp b/src/FabReader_v2/src/main.cpp index 49ccd83..a157253 100644 --- a/src/FabReader_v2/src/main.cpp +++ b/src/FabReader_v2/src/main.cpp @@ -1,128 +1,145 @@ -#include -#include +/* + * -------------------------------------------------------------------------------------------------------------------- + * Example sketch/program showing how to read data from a PICC to serial. + * -------------------------------------------------------------------------------------------------------------------- + * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid + * + * Example sketch/program showing how to read data from a PICC (that is: a RFID Tag or Card) using a MFRC522 based RFID + * Reader on the Arduino SPI interface. + * + * When the Arduino and the MFRC522 module are connected (see the pin layout below), load this sketch into Arduino IDE + * then verify/compile and upload it. To see the output: use Tools, Serial Monitor of the IDE (hit Ctrl+Shft+M). When + * you present a PICC (that is: a RFID Tag or Card) at reading distance of the MFRC522 Reader/PCD, the serial output + * will show the ID/UID, type and any data blocks it can read. Note: you may see "Timeout in communication" messages + * when removing the PICC from reading distance too early. + * + * If your reader supports it, this sketch/program will read all the PICCs presented (that is: multiple tag reading). + * So if you stack two or more PICCs on top of each other and present them to the reader, it will first output all + * details of the first and then the next PICC. Note that this may take some time as all data blocks are dumped, so + * keep the PICCs at reading distance until complete. + * + * @license Released into the public domain. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + */ -#include "config.h" -#include "pins.h" -#include "nfc.h" -#include "otaproxy.h" - -#include -#include - -#include -#include #include #include -#include +#include -WiFiClient espClient; -//Config_Data config; +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above -PubSubClient* mqtt; -NFC* nfc; -OTAProxy* ota; +#include "Pins.h" -void setup_wifi() -{ - delay(10); - Serial.println("Connecting Wifi ..."); +DESFire mfrc522(PIN_RFID_SPI_SS, PIN_RFID_RST); // Create MFRC522 instance - WiFi.mode(WIFI_STA); - WiFi.begin(WLAN_SSID, WLAN_PASS); - - while (WiFi.status() != WL_CONNECTED) - { - delay(500); - } - - randomSeed(micros()); - Serial.println("WiFi connected"); +void setup() { + Serial.begin(115200); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 + mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader details + Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks...")); } -void reconnect() -{ - while (!mqtt->connected()) - { - String clientId = "FabReader_"; - clientId += String(FABREADERID); +void loop() { + // Look for new cards + if ( ! mfrc522.PICC_IsNewCardPresent()) { + return; + } - Serial.println("Connecting MQTT ..."); - if (mqtt->connect(clientId.c_str())) - { - Serial.println("MQTT connected"); - char id[6] = "00000"; - sprintf(id, "%05d", FABREADERID); - mqtt->publish("fabreader", id); + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) { + return; + } - char topic_requestOTA[] = "fabreader/00000/requestOTA"; - sprintf(topic_requestOTA, "fabreader/%05d/requestOTA", FABREADERID); - mqtt->subscribe(topic_requestOTA); + if (mfrc522.uid.sak != 0x20) { + // Dump debug info about the card; PICC_HaltA() is automatically called + mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); + return; + } - char topic_stopOTA[] = "fabreader/00000/stopOTA"; - sprintf(topic_stopOTA, "fabreader/%05d/stopOTA", FABREADERID); - mqtt->subscribe(topic_stopOTA); - } else { - Serial.print("failed, rc="); - Serial.print(mqtt->state()); - Serial.println(" try again in 5 seconds"); - delay(5000); - } - } -} + // Show an extra line + Serial.println(); -void callback(char* topic, byte* payload, unsigned int length) -{ - Serial.println("Receive Message"); - Serial.println(topic); - if(ota->hasActiveOTA()) - { - ota->continueOTA(topic, payload, length); - } -} + DESFire::mifare_desfire_tag tag; + DESFire::StatusCode response; -void setup() -{ - Serial.begin(115200); - Serial.print("\n\n\n"); - Serial.println("Booting ..."); + tag.pcb = 0x0A; + tag.cid = 0x00; + memset(tag.selected_application, 0, 3); + + // Make sure none DESFire status codes have DESFireStatus code to OK + response.desfire = DESFire::MF_OPERATION_OK; + + byte ats[16]; + byte atsLength = 16; + response.mfrc522 = mfrc522.PICC_RequestATS(ats, &atsLength); + if ( ! mfrc522.IsStatusCodeOK(response)) { + Serial.println(F("Failed to get ATS!")); + Serial.println(mfrc522.GetStatusCodeName(response)); + Serial.println(response.mfrc522); - pinMode(PIN_BUZZER, OUTPUT); - pinMode(PIN_BUTTON, INPUT); - pinMode(PIN_ETH_SPI_SS, OUTPUT); - digitalWrite(PIN_ETH_SPI_SS, HIGH); + mfrc522.PICC_HaltA(); + return; + } - Serial.println("Connecting NFC ..."); - nfc = new NFC(PIN_RFID_SPI_SS, PIN_RFID_RST); - if(!(nfc->rfid->PCD_PerformSelfTest())) - { - Serial.println("NFC Test failed"); - } - Serial.println("NFC connected"); + // TODO: Should do checks but since I know my DESFire allows and requires PPS... + // PPS1 is ommitted and, therefore, 0x00 is used (106kBd) + response.mfrc522 = mfrc522.PICC_ProtocolAndParameterSelection(0x00, 0x11); + if ( ! mfrc522.IsStatusCodeOK(response)) { + Serial.println(F("Failed to perform protocol and parameter selection (PPS)!")); + Serial.println(mfrc522.GetStatusCodeName(response)); + mfrc522.PICC_HaltA(); + return; + } - setup_wifi(); + // MIFARE DESFire should respond to a GetVersion command + DESFire::MIFARE_DESFIRE_Version_t desfireVersion; + response = mfrc522.MIFARE_DESFIRE_GetVersion(&tag, &desfireVersion); + if ( ! mfrc522.IsStatusCodeOK(response)) { + Serial.println(F("Failed to get a response for GetVersion!")); + Serial.println(mfrc522.GetStatusCodeName(response)); + mfrc522.PICC_HaltA(); + return; + } + + // Dump MIFARE DESFire version information. + // NOTE: KEEP YOUR CARD CLOSE TO THE READER! + // This method takes some time and the card will be read + // once output ends! If you remove the card too fast + // a timeout will occur! + mfrc522.PICC_DumpMifareDesfireVersion(&tag, &desfireVersion); - mqtt = new PubSubClient(espClient); - mqtt->setServer(MQTT_BROKER, 1883); - mqtt->setCallback(callback); + mfrc522.PICC_DumpMifareDesfireMasterKey(&tag); - ota = new OTAProxy(mqtt, nfc, FABREADERID); -} + DESFire::mifare_desfire_aid_t aids[MIFARE_MAX_APPLICATION_COUNT]; + byte applicationCount = 0; + response = mfrc522.MIFARE_DESFIRE_GetApplicationIds(&tag, aids, &applicationCount); + if ( ! mfrc522.IsStatusCodeOK(response)) { + Serial.println(F("Failed to get application IDs!")); + Serial.println(mfrc522.GetStatusCodeName(response)); + mfrc522.PICC_HaltA(); + return; + } -void loop() -{ - if (!mqtt->connected()) - { - reconnect(); - } - mqtt->loop(); - - if(ota->hasActiveOTA() && nfc->isCardLost()) - { - ota->cancelOTA(); - } - - if(!ota->hasActiveOTA()) - { - ota->startOTA(); - } + // Dump all applications + for (byte aidIndex = 0; aidIndex < applicationCount; aidIndex++) { + mfrc522.PICC_DumpMifareDesfireApplication(&tag, &(aids[aidIndex])); + } + + // Call PICC_HaltA() + mfrc522.PICC_HaltA(); + Serial.println(); } \ No newline at end of file diff --git a/src/FabReader_v2/src/main.cpp.deprecated b/src/FabReader_v2/src/main.cpp.deprecated new file mode 100644 index 0000000..0d526ea --- /dev/null +++ b/src/FabReader_v2/src/main.cpp.deprecated @@ -0,0 +1,1418 @@ + +/* + ESP8266 RFID Reader - MQTT with OLED display for WiFi and Enthernet + + The aim of the project is, to have a RFID-cardreader which has Wifi and Ethernet capabilities at low costs. + The hardware therfor consists of low cost modules like: + - ESP8266 or ESP32 + - 0.96" OLED module (ESP8266) or e-ink module (ESP32) + - RC522 RFID module + - ENC28j60 Ethernet module + + With only a minimum of additional components they provide a full functioning hardware + + General function: + The RC522 reads a RFID token and sends the UID via MQTT to a broker. The data is sent in JASON format. For security reasons, the tokens can be encrypted + and a timestamp can be stored on the card wich changes each time the card is read. The old and new timestamp will be transmitted + to the broker as well (if activated). + The hardware forsees an input (ADC in case of the EPS8266), where a button can (must, for the first configuration) be connected. During boot, this button can be pressed to enter + into configuration mode. This creates an access point (AP). By connecting to this access point via a browser (http://192.168.4.1/ ), all connection + parameters of the unit can be changed. Here the connection mode (WiFI or ethernet) can be set aswell. + + Seceurity issues: + PLEASE BE AWARE + 1. the used Mifare Classic technology has been hacked and RFID-tokens can be copied including the UID. Do not use this + technology for sensitive applications. Use DESfire or Mifare Ultralight C instead. + 2. all connection data are stored in the EEPROM. To prevent someone from reading these data, they need to be encrypted. + Only the ESP32 is capable of encrypting the EEPROM (allthough this encryption can be hacked in a day by skillfull people). + When using a ESP8266 use a separate network for MQTT or secure the devices against theft. + + + Version description: + Version 01 + initial Version + Version 01 + include ESP32 as option + Version 6 + show text messages. + +*/ + +#ifdef ESP32 + #include // ESP32 + #include // ESP32 + #include // ESP32 + #include + #include // E-Paper + //#include // 2.13" b/w + //#include // 2.13" b/w new panel + #include // E-Paper + #include + #include + #include + #include + #include + #include +#else + #include // EPS8266 + #include // ESP8266 + #include // ESP8266 + // #include /// +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// #include // for retreiving the MAC adress + + +// Definition der Pins +#ifdef ESP32 + #define SS_PIN 32 // GPIO17 + #define RST_PIN 33 // GPIO16 + #define BEEP_PIN 27 // GPIO16 + #define BUTTON_PIN 39 // GPIO39 + #define CS 19 // ESP32 + + #define SPI_MOSI 23 + #define SPI_MISO -1 + #define SPI_CLK 18 + + #define ELINK_SS 5 + #define ELINK_BUSY 4 + #define ELINK_RESET 16 + #define ELINK_DC 17 +#else +// #define SS_PIN 15 // GPIO15 -> uncomment this for the old PCB + #define SS_PIN 2 // GPIO2 + #define RST_PIN 0 // GPIO00 + #define BEEP_PIN 16 // GPIO16 + #define BUTTON_PIN 13 // GPIO2 -> uncomment this for the old PCB + #define CS 15 // ESP8266 +#endif + +#define HSPI_MOSI 15 +#define HSPI_MISO 2 +#define HSPI_SCLK 14 + + +// to make some messages easier to read +#define CONNECT_TO_WIFI 0 +#define CONNECT_TO_MQTT 1 +#define PUSH_BUTTON 2 +#define IN_AP_MODE 3 +#define CONNECT_TO_LAN 4 +#define ERROR_RFID 5 +#define MQTT_RFID 6 +#define WRONG_RFID_TYPE 7 +#define ERROR_DHCP 8 + + +int Old_PCB=0; + + +/* configuration variables for your wifi -> will be loaded from the EEPROM !!! */ +char ssid[32] = "WLAN"; // WLAN ID +char password[64] = "Passwort"; // WLAN password for WPA PSK + +/* configuration variables MQTT-client mode -> will be loaded from the EEPROM !!!*/ +char host[128] = "rfid_reader"; // Name at the MQTT Broker +char broker[16] = "192.168.2.13"; // IP adresse of the MQTT broker in your local (W)LAN +char mqttUser[32] = "mqttuser"; // to access the the MQTT broker, if broker is configured that way +char mqttPass[32] = "mqttpassword"; // to access the the MQTT broker, if broker is configured that way +char ReaderID[5] ; //= "000"; // identifyer for the reader. +char subTopic[18] = "/cmnd/reader/"; // MQTT Topic where to find commands for the reader + ReaderID + 0x00 +char ConType [3] = "0"; + +/* Configuration for access point mode -> can be anything you like*/ +const char *ssidAP = "RFID-Reader"; // modify to your needs +const char *passwordAP = "Passwort"; // modify to your needs + +const char* vers = "V02"; // Software Version +boolean StartUpMode = 0; // to start either as access point (0) and MQTT-client (1) +boolean OTA=0; // is there currently an OTA process running? +byte Wifi_Ether = 0; // to connect either via Wifi (0) or Ethernet (1) -> using a Byte to store it in EEPROM easily +byte RFID_Mod; // what card type to be read -> using a Byte to store it in EEPROM easily +byte Format_Mod; // what format to send data -> using a Byte to store it in EEPROM easily + +uint64_t IV=0; +byte C_key[24]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +//= { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46, 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42}; + +unsigned long oldMillis; // used to identify time outs + +int maxMessage = 18; + +String lastLine1, lastLine2; // to save previous displayed messages +boolean linesSaved = 0; // no previous message has been saved +int fsize=0; // current font size (for E-paper) + +String Message_Disp[] = {"verfuegbar", // 0 - equipment available // 0 + "reserviert", // 0 - equipment reserved // 1 + "reserviert ab", // 13 - reserved starting from // 2 + "in Benutzung", // 0 - equipment in use // 3 + "freigegeben", // 0 - access granted // 4 + "Anmeld. fehlgeschlagen", // 8 - login error // 5 + "Nutzungsgebuehr", // 15 - usage fee // 6 + "Nutzung nicht erlaubt", // 13 - operation not allowed // 7 + "Nutzung auf eigenem Risiko", // 11 - operation at own risk // 8 + "gesperrt durch", // 14 - equipment locked by // 9 + "freizugeben durch", // 11 - to be released by // 10 + "ausserhalb der Nutzungzeit", // 15 - outside ooperating hours// 11 + "Nutzungsdauer", // 13 - duration of use // 12 + "abgemeldet", // 0 - logged out // 13 + "abschalten in", // 13 - equipment will lock down// 14 + "Karte gesperrt", // 0 - card locked // 15 + "Karte unbekannt", // 0 - card unknown // 16 + "Berechtigung endet", // 12 - training to expire // 17 + "Ruhemodus", // 0 - sleep mode // 18 + "Aufsicht bestaetigen"}; // 8 - confirm presence // 19 + +int SplitAt[]={0,0,13,0,0,8,15,13,11,15,11,14,14,0,13,0,0,12,0,8}; + +/* +String Message_Com[] = {"1", // equipment available + "2", // equipment reserved + "3", // reserved starting from + "4", // equipment in use + "5", // access granted + "6", // login error + "7", // usage fee + "8", // operation not allowed + "9", // operation at own risk + "10", // equipment locked by + "11", // to be released by + "12", // outside ooperating hours + "13", // duration of use + "14", // logged out + "15" // equipment will lock down + "16", // card locked + "17", // card unknown + "18", // training to expire + "19", + "20"}; // sleep mode +*/ + + +//----------- E-Paper-Stuff +#ifdef ESP32 + SPIClass eSPI(HSPI); + GxIO_Class io(eSPI, /*CS=5*/ ELINK_SS, /*DC=*/ ELINK_DC, /*RST=*/ ELINK_RESET); + GxEPD_Class EPaper(io, /*RST=*/ ELINK_RESET, /*BUSY=*/ ELINK_BUSY); +#endif + + +EthernetClient ethClient; // in case of Ethernet connection +WiFiClient wifiClient; // in case of Wifi connection +PubSubClient MQTTclient; // The MQTT Client + +#ifdef ESP32 + WebServer httpServer(80); // ESP32 +#else + ESP8266WebServer httpServer(80); // ESP8266 +#endif + + +// instance for the RFID-Reader +MFRC522 rfid(SS_PIN, RST_PIN); +MFRC522::MIFARE_Key key; +MFRC522::StatusCode status; +DES des; + +//Starting page for parameter configuration +const char INDEX_HTML[] = +"" +"" +"" +"" +"" +"Verbindungsdaten RFID-Reader" +"" +"" +"" +"

Verbindungsdaten RFID-Reader

" +"
" +"

" +"
" +"
" +"
" +"
" +"Connection
" +"Ehternet
" +"WiFi
" +"RFID-Type
" +"Mifare Classic
" +"Mifare Classic + Timestamp
" +"Mifare DESFire
" +"Mifar Ultralight C
" +"Data Package Format
" +"JSON
" +"Text
" +" " +"

" +"
" +"" +""; + + +// Initialize the OLED display using Wire library +#ifdef ESP32 + SSD1306Wire display(0x3c, 21, 22); // ESP32 SSD1306Wire display(0x3c, SDA, SCL); +#else + SSD1306Wire display(0x3c, 4, 5); // ESP8266 SSD1306Wire display(0x3c, SDA, SCL); +#endif + + + + +void setup(void){ + + pinMode(BEEP_PIN, OUTPUT); // buzzer pin + pinMode(BUTTON_PIN, INPUT); // button pin + pinMode(CS, OUTPUT); // CS-Pin of the ENC28J60 Module + + digitalWrite(CS, HIGH); // -> needs to be set, otherwise MISO will be pulled LOW + + #ifdef ESP32 + SPI.begin(HSPI_SCLK, HSPI_MISO, HSPI_MOSI, CS); // for the LAN Module and the RC522 module + eSPI.begin(SPI_CLK, SPI_MISO, SPI_MOSI, ELINK_SS); // For the e-Paper Module + #else + SPI.begin(); + #endif + + rfid.PCD_Init(); // RC522 initialize + // rfid.PCD_PerformSelfTest(); + + Serial.begin(115200); + Serial.println(); + Serial.println("Booting..."); + + des.init(C_key,IV); + + #ifdef ESP32 + EPaper.init(); // enable diagnostic output on Serial + EPaper.setRotation(3); + EPaper.fillScreen(GxEPD_WHITE); + EPaper.setTextColor(GxEPD_BLACK); + EPaper.setFont(&FreeMonoBold12pt7b); + EPaper.setCursor(0, 0); + EPaper.update(); + #endif + + display.init(); // initialize display + display.clear(); + display.flipScreenVertically(); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.display(); + + EEPROM.begin(512); // initialize EEPROM + + StatusToDisplay(PUSH_BUTTON); + + // displayPushButton(); // message shown during the boot up + delay(5000); // dealy, to give time to press the button + + int value=800; + if(Old_PCB){ + if(digitalRead(BUTTON_PIN)==LOW) + value=0; + } + else{ + value = analogRead(A0); + } + + Serial.print("value: "); + Serial.println(value); + + if (value > 512) // button pressed? -> attention: button signal is inverted + setupAP(); // -> configuration + else + setupMQTT(); // -> start MQTT-Client + + oldMillis=millis(); + +} + +void setupAP(void){ + StartUpMode = 0; + + Serial.println("Configuring access point..."); + WiFi.mode(WIFI_AP); //Access Point mode IP: 192.168.4.1 + + // Serial.println("Setting soft-AP configuration ... "); + // WiFi.softAPConfig(ap_local_IP, ap_gateway, ap_subnet); + Serial.println("Setting soft-AP ... "); + WiFi.softAP(ssidAP, passwordAP); + Serial.print("Soft-AP IP address = "); + Serial.println(WiFi.softAPIP()); + + //Configuring the web server + httpServer.on("/", handleRootAP); + httpServer.onNotFound(handleNotFoundAP); + httpServer.begin(); + Serial.println("HTTP server started"); + StatusToDisplay(IN_AP_MODE); +} + +void setupMQTT(void){ + uint8_t mac[6]; + + StartUpMode = 1; + readConfig(); // loading parameter set from EEPROM + MQTTclient.setBufferSize(356); // 256 APDU size + topic + JSON chars + if(Wifi_Ether){ // either start via ethernet or Wifi + // esp_read_mac(mac, ESP_MAC_WIFI_STA); // reading teh MAC address of the unit + StatusToDisplay(CONNECT_TO_LAN); + + WiFi.macAddress(mac); + Ethernet.init(CS); // set the correct pin + if(Ethernet.begin(mac) == 0) { + Serial.println(F("Ethernet configuration using DHCP failed")); + StatusToDisplay(ERROR_DHCP); + while(1); + } + else{ + Serial.println(F("Ethernet configuration using DHCP OK")); + } + Serial.print("set Client to Ethernet: "); + MQTTclient.setClient(ethClient); + //WiFi.forceSleepBegin(); // TODO + //WiFi.mode(WIFI_OFF); // TODO + Serial.println("OK"); + } + else{ + StatusToDisplay(CONNECT_TO_WIFI); + WiFi.mode(WIFI_STA); // only station, no access point + WiFi.begin(ssid, password); + Serial.print("set Client to Wifi: "); + MQTTclient.setClient(wifiClient); + Serial.println("OK"); + Serial.print("MAC address: "); + Serial.println(WiFi.macAddress()); + } + StatusToDisplay(CONNECT_TO_MQTT); + Serial.print("set MQTT-Server..."); + MQTTclient.setServer(broker,1883); //for using local broker + Serial.println(" OK"); + Serial.print("setting callback..."); + MQTTclient.setCallback(callback); + Serial.println(" OK"); + //Serial.print("starting MDNS..."); + //MDNS.begin(host); + //Serial.println(" OK"); + Serial.println("Up and running!"); + +/* Generate the Mifare Keys (key A and B) + * Standard key is FFFFFFFFFFFFh for unformatted RFID tokens + * Special key can be configured and will be loaded from EEPROM (To-Do) + */ + + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + StatusToDisplay(MQTT_RFID); +} + + + +void loop(void){ + if(StartUpMode){ // if in MQTT-Mode + if(!MQTTclient.connected()) { // if not connected + connect(); + } + MQTTclient.loop(); // do all the MQTT-stuff + delay(10); + if(!OTA) // search only for new cards if there is no OTA process ongoing + handleRFID(); // if a card was found and data was sent + delay(50); + } + + httpServer.handleClient(); +} + + +/* + * ------------------------------------------------------------------------------------------------------------------ + * Code for Access Point mode + */ + +/* + * Show configuration page + */ +void handleRootAP() { + + if (httpServer.hasArg("ssid")&& httpServer.hasArg("Passwort")&& httpServer.hasArg("BrokerIP")&&httpServer.hasArg("ReaderID") ) {//If all form fields contain data call handelSubmit() + handleSubmit(); + } + else { + String s = INDEX_HTML; + httpServer.send(200, "text/html", s); + } + +} + +/* + * Shows the data, to be saved in the EEPROM + */ + +void handleSubmit(){ + + // TO DO - check if entries are too long and promt an error! + + String response="

SSID: "; + response += httpServer.arg("ssid"); // max 32 characters + response +="
"; + response +="Password: "; + response +=httpServer.arg("Passwort"); // max 64 characters + response +="
"; + response +="The broker IP-Address is: "; + response +=httpServer.arg("BrokerIP"); // max 16 characters + response +="
"; + response +="Hostname is: "; // max 128 characters + response +="rfid_reader_"; // TO DO -> complete argument for the Hostname + response +=httpServer.arg("ReaderID"); // max 3 characters + response +="
"; + response +="Topic name is: "; + response +="/rfid_reader/"; // TO DO -> complete argument for the topic + response +=httpServer.arg("ReaderID"); + response +="
"; + response +="connection via: "; + if(httpServer.arg("ConType")=="1"){ + response +="Ethernet"; + Wifi_Ether=1; + } + else{ + response +="Wifi"; + Wifi_Ether=0; + } + response +="
"; + response +="RFID-ID: "; + if (httpServer.arg("RFIDType")=="1"){ + response +="Mifare Classic + Timestamp"; + RFID_Mod=1; + } + else if (httpServer.arg("RFIDType")=="2"){ + response +="Mifare DESFire"; + RFID_Mod=2; + } + else if (httpServer.arg("RFIDType")=="3"){ + response +="Mifar Ultralight C"; + RFID_Mod=3; + } + else{ // default + response +="Mifare Classic"; + RFID_Mod=0; + } + response +="
"; + response +="MQTT-Data Format: "; + if (httpServer.arg("DatFormat")=="1"){ + response +="Text"; + Format_Mod=1; + } + else{ + response +="JSON"; + Format_Mod=0; + } + + + response +="


"; + response +="

go home


"; + + // show data + httpServer.send(200, "text/html", response); + + // write data to EEPROM + write_to_Memory(String(httpServer.arg("ssid")),String(httpServer.arg("Passwort")),String(httpServer.arg("BrokerIP")),String(httpServer.arg("ReaderID")), Wifi_Ether, RFID_Mod, Format_Mod ); +} + + +/* + * Write data to the EEPROM + * The data is merged to a single string, separated by "\r" + * TO DO -> the complete string is then encrypted. + * resulting String is wrtitten to the EEPROM + * + */ +void write_to_Memory(String s,String p,String i, String g, byte w, byte r, byte f){ + EEPROM.write(0,w); // how to connect + EEPROM.write(1,r); // what type of RFID tokens are used + EEPROM.write(2,f); // how to format MQTT-data + s+="\r"; // SSID max 32 characters + s+=p; // password max 64 characters + s+="\r"; + s+=i; // Broker-IP 16 charaacters + s+="\r"; + s+=g; // ID max 3 characters + s+="\r"; + write_EEPROM(s,3); + EEPROM.commit(); +} + +//Daten in den EEPROM schreiben +void write_EEPROM(String x,int pos){ + for(int n=pos;ngo home
"; + httpServer.send(404, "text/plain", message); +} + + + +/*--------------------------------------------------------------------------------------------------------------------------------------- + * Code for MQQT Client mode + */ + +/* + * connect to Wifi / MQTT-broker + */ +void connect() { + if(!Wifi_Ether){ // only if connected by Wifi !!! + StatusToDisplay(CONNECT_TO_WIFI); + while(WiFi.waitForConnectResult() != WL_CONNECTED){ + WiFi.begin(ssid, password); + Serial.println("WiFi failed, retrying."); + } + + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } + while (!MQTTclient.connected()) { + StatusToDisplay(CONNECT_TO_MQTT); + Serial.println("Reconnecting MQTT..."); + if (!MQTTclient.connect(ReaderID, mqttUser, mqttPass)) { + Serial.print("failed, rc="); + Serial.println(MQTTclient.state()); + } + } + byte topicLenght=sizeof(subTopic); + topicLenght=topicLenght-1; + for (int i=0; i<3 ;i++){ + subTopic[(topicLenght-4)+i]=ReaderID[i]; + } + subTopic[topicLenght]=0x00; + MQTTclient.subscribe(subTopic); + Serial.println(F("connected!")); + Serial.print(F("subscribed to topic: ")); + Serial.println(subTopic); + StatusToDisplay(MQTT_RFID); +} + + +/* + * answer to HTTP-requests + */ + +void handleRootMQTT() { + httpServer.send(200, "text/plain", "It works!!!"); +} + +/* + * Handle received MQTT Messages + * examples: + * show message on Display - JSON object = {"Cmd": "message", "MssgID": 4 , "ClrTxt":"Hello World" , "AddnTxt":"3:00"} // mID as int + * communicate with reader - JSON object = {"Cmd": "sendPICC", "data": "0123456789ABCDEF"} // data in ASCII + * communicate with reader - JSON object = {"Cmd": "haltPICC"} + * recieve auth Key - JSON object = {"Cmd": "Key", "data": 0123456789ABCDEF} // data in ASCII + * Confirm User attendence - JSON object = {"Cmd": "ConfirmUser"} + */ + +void callback(char* topic, byte* payload, unsigned int length) { +int mID; +int len; + + Serial.print("Received message ["); + Serial.print(topic); + Serial.print("] "); + for (int i = 0; i < length; i++) { + Serial.print((char)payload[i]); + } + Serial.println(); + + DynamicJsonDocument doc(256); + deserializeJson(doc, (char*)payload); + JsonObject root = doc.as(); + + + + if(!root["Cmd"].isNull()) { + if(root["Cmd"] == "message") { // Message to display + if(!root["MssgID"].isNull()) { + mID=root["MssgID"]; + MessageToDisplay(mID, root["ClrTxt"], root["AddnTxt"]); + } + } + if(root["Cmd"] == "sendPICC") { // command for OTA communication with the PICC + if(!root["data"].isNull()) { + APDUtrancieve( root["data"]); + } + } + if(root["Cmd"] == "haltPICC") { // command for ending OTA communication with the PICC + rfid.PICC_HaltA(); + OTA=0; + } + if(root["Cmd"] == "Key"){ // set the auth key for Mifare Ultralight C or NTAG216 + setAuthKey( root["data"]); + } + if(root["Cmd"] == "ConfirmUser") { // start beeping and flashing -> user needs to confirm its presence at the equipment + + } + } +} + + +/* + * Reading the RFID Token + * + * When a token is recocnized.... + * - read the UID and SAK-value + * + * Mifare Classic without time stamp (Mode=0) + * Mifare Classic Mode + timestamp (Mode=1) + * DESFire - Mode (Mode=2) + * Ultralight C - Mode (Mode=3) + + * + * CAUTION: The total MQTT message (topic + payload) must not be too long, otherwise the data and the connection will be lost!!!! + * + */ + +void handleRFID(void) { + + // Try to access a RFID-token + if (!rfid.PICC_IsNewCardPresent()) return; // is there a RFID token at all? + if (!rfid.PICC_ReadCardSerial()) return; // can I read it? + + Serial.print(F("Mod = ")); + Serial.println(RFID_Mod); + + if((RFID_Mod==0)||(RFID_Mod==1)){ + if((rfid.uid.sak != 0x08)&&(rfid.uid.sak != 0x09)&&(rfid.uid.sak != 0x18)){ + Serial.println(F("should be Mifare Classic, but isn't")); + popRFIDerror(); + } + else{ + if(!RequestMifare()){ // test for Ultralight "C" + popRFIDerror(); + } + } + } + if(RFID_Mod==2){ + if(rfid.uid.sak != 0x20){ // should be Mifare DESFire, but isn't + Serial.println(F("should be Mifare DESFire, but isn't")); + popRFIDerror(); + } + else{ + if(!HandleDESFire()){ + popRFIDerror(); + } + } + } + if(RFID_Mod==3){ + if(rfid.uid.sak != 0x00){ // should be Mifare Ultralight C, but isn't + Serial.println(F("should be Mifare Ultralight C, but isn't")); + popRFIDerror(); + } + else{ + if(!RequestAuthUltralightC()){ // test for Ultralight "C" + popRFIDerror(); + } + } + } + if(!OTA){ // close only when OTA there is no OTA process ongoing + rfid.PICC_HaltA(); + rfid.PCD_StopCrypto1(); + } +} + + +boolean HandleDESFire(void){ +byte buffer[67]; // buffer for reading data max 63 bytes per frame + 2 bytes status + 2 bytes CRC +byte bufferSize=sizeof(buffer); +char UID[21]; + + byte2charArray(rfid.uid.uidByte, UID, rfid.uid.size); + Serial.print(F("UID: ")); + Serial.println(UID); + + // start with ATS- Build command buffer + buffer[0] = 0xE0; //PICC_CMD_RATS; + buffer[1] = 0x50; // FSD=64, CID=0 + rfid.PCD_CalculateCRC(buffer, 2, &buffer[2]); + status = rfid.PCD_TransceiveData(buffer, 4, buffer, &bufferSize, NULL, 0, true); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("Failed to get ATS: ")); + Serial.println(rfid.GetStatusCodeName(status)); + return 0; + } + + bufferSize=sizeof(buffer); + + byte cid=0x00; + + buffer[0] = 0xD0 | (cid & 0x0F); + buffer[1] = 0x11; // PPS0: PPS1 will follow + buffer[2] = 0x00; // PPS1: set to 106kBaud in both directions DSI and DRI + rfid.PCD_CalculateCRC(buffer, 3, &buffer[3]); + status = rfid.PCD_TransceiveData(buffer, 5, buffer, &bufferSize, NULL, 0, true); + if (status == MFRC522::STATUS_OK) { + // This is how my MFRC522 is by default. + // Reading https://www.nxp.com/documents/data_sheet/MFRC522.pdf it seems CRC generation can only be disabled in this mode. + // if (pps1 == 0x00) { // =buffer[2] TODO why? + rfid.PCD_WriteRegister(MFRC522::TxModeReg, 0x00); + rfid.PCD_WriteRegister(MFRC522::RxModeReg, 0x00); + //} + } + else + return 0; + + OTA=1; // Over The Air (OTA) Communication enabled + sendUID(UID, NULL, NULL); + return 1; +} + +/* + * APDUtrancieve(char* in, char* out, byte len) + * Sends the string "in" to the PICC and provides "out" as the response. + * "in" and "out" are formatted as readable chars, and converted internally to bytes. + * "len" provides the length of the provided string (twice the len of the real bytes transfered) + * communicate with reader - JSON object = {"Cmd": "readPICC", "data": "0123456789ABCDEF"} + */ + +boolean APDUtrancieve(String in){ +byte buffer[67]; // buffer for reading data max 63 bytes per frame + 2 bytes status + 2 bytes CRC +byte len; +byte len_send; +char JSONmessageBuffer[200]; // contains the full JSON message +char topic[32]; + + for(len=0; len<126; len++){ + if(in[len]==0x00) + break; + } + + buffer[0]=0x0A; // PCB - Protocol Control Byte + buffer[1]=0x00; // CID - Card ID, always assumed as 0x00 as multicard reading is not implemented + // NAD - Node Address - not implemented and not required according to the setting of Protocol Control Byte (PCB) + char2byteArray(in, &buffer[2], len); + len=len/2; + len=len+2; // two first bytes + status = rfid.PCD_CalculateCRC(buffer, len, &buffer[len]); + if (status != rfid.STATUS_OK) { + Serial.print(F("CRC-gen failed: ")); + Serial.println(rfid.GetStatusCodeName(status)); + return 0; + } + + Serial.print(F("APDU-Command: ")); + dump_byte_array(&buffer[2], len-2); + + len_send=len+2; + len=sizeof(buffer); + status = rfid.PCD_TransceiveData(buffer, len_send, buffer, &len, NULL, 0, true); + if (status != rfid.STATUS_OK) { + Serial.print(F("APDU-command failed: ")); + Serial.println(rfid.GetStatusCodeName(status)); + return 0; + } + + len=len-4; // two bytes at the beginning (status-bytes) and the two CRC bytes + + Serial.print(F("APDU-Response: ")); + dump_byte_array(&buffer[2], len); + + // communicate with reader - JSON object = {"Cmd": "readPICC", "data": "0123456789ABCDEF"} // data in ASCII + + snprintf(JSONmessageBuffer, sizeof(JSONmessageBuffer), R"({"Cmd":"readPICC","data":")"); // 27 characters + byte2charArray(&buffer[2], &JSONmessageBuffer[26], len); + // org: snprintf(JSONmessageBuffer, sizeof(JSONmessageBuffer), R"({"UID":"%s","KeyOld":"%s","KeyNew":"%s"})", UID, KeyOld, KeyNew); + + len=len*2; // as we have now a char array 00-AA instead of bytes 0x00-0xFF + + JSONmessageBuffer[26+len]='"'; + JSONmessageBuffer[26+len+1]='}'; + JSONmessageBuffer[26+len+2]=0x00; + + Serial.print(F("payload :")); + Serial.println(JSONmessageBuffer); + + snprintf(topic, sizeof(topic), "/rfid_reader/%s", ReaderID); // generat a topic with the ReaderID + Serial.print(F("topic :")); + Serial.println(topic); + Serial.println(""); + MQTTclient.publish(topic, JSONmessageBuffer); // send it to the broker + + return 1; + +} + +/* + * Handle Mifare Classic cards + */ + +boolean RequestMifare(void){ +int i; + +char UID [21]; +char KeyOld [33]; // 16 bytes in HEX = 32 + 1 for 0x00 +char KeyNew[33]; // 16 bytes in HEX = 32 + 1 for 0x00 +byte sector = 1; +byte blockAddr = 4; // where to find the data +byte trailerBlock = 7; // block for autentification + +byte buffer[18]; // buffer for reading data +byte size = sizeof(buffer); +unsigned long KeyGen; + + Serial.println(printHex(rfid.uid.uidByte, rfid.uid.size)); // found somthing + + byte2charArray(rfid.uid.uidByte, UID, rfid.uid.size); + Serial.print(F("UID: ")); + Serial.println(UID); + + if(RFID_Mod==1){ // Timestamp enabled + + Serial.println(F("Authenticating using key A...")); + status = (MFRC522::StatusCode) rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(rfid.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(rfid.GetStatusCodeName(status)); + popRFIDerror(); + return 0; + } + + // reading the first block + Serial.print(F("Reading data from block ")); Serial.print(blockAddr); + Serial.println(F(" ...")); + status = (MFRC522::StatusCode) rfid.MIFARE_Read(blockAddr, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(rfid.GetStatusCodeName(status)); + popRFIDerror(); + rfid.PCD_PerformSelfTest(); // reset the reader + return 0; + } + else{ // OK, Card can be accesse + Serial.print(F("Data in block ")); + Serial.print(blockAddr); + Serial.println(F(":")); + dump_byte_array(buffer, 16); + + byte2charArray(buffer, KeyOld, 8); // Only the first 8 Bytes are used, otherwise the MQTT message will be too long + + KeyGen=millis(); // generate a a new time stamp + + for(i=0; i<16; i++){ // convert it to a byte array + buffer[i]=KeyGen&0xFF; + KeyGen=KeyGen/10; + } + + byte2charArray(buffer, KeyNew, 8); // Only the first 8 Bytes are used, otherwise the MQTT message will be too long + + Serial.print(F("Writing data into block ")); Serial.print(blockAddr); + Serial.println(F(" ...")); + dump_byte_array(buffer, 16); + status = (MFRC522::StatusCode) rfid.MIFARE_Write(blockAddr, buffer, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(rfid.GetStatusCodeName(status)); + popRFIDerror(); + return 0; + } + } + } + + sendUID(UID, KeyOld, KeyNew); + + return 1; +} + +/* + * This small routine starts a request for encryption. + * for details see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf + */ +boolean RequestAuthUltralightC(void){ +int i; + byte AuthBuffer[24] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // + byte AuthLength=24; + byte RndARndB[16]; + byte RndA[8]={0,0,0,0,0,0,0,0}; + byte dRndA[8]={0,0,0,0,0,0,0,0}; // decrypted RndA + byte iv_ar[8]; // this is the starting IV for decryption + // byte buffer[24]; + // byte byteCount=sizeof(buffer); + char UID [21]; + + byte2charArray(rfid.uid.uidByte, UID, rfid.uid.size); + Serial.print(F("UID: ")); + Serial.println(UID); + + + // set IV to 0x00 + for(i=0 ; i<8 ; i++){ + iv_ar[i] = 0x00; + } + memcpy(&IV,iv_ar,8); // set IV + des.set_IV(IV); + + // Build command buffer + AuthBuffer[0] = 0x1A; // CMD_3DES_AUTH -> Ultralight C 3DES Authentication. + AuthBuffer[1] = 0x00; // + + // Calculate CRC_A + status = rfid.PCD_CalculateCRC(AuthBuffer, 2, &AuthBuffer[2]); + if (status != rfid.STATUS_OK) { + return 0; + } + + AuthLength=sizeof(AuthBuffer); + + // Transmit the buffer and receive the response, validate CRC_A. + status = rfid.PCD_TransceiveData(AuthBuffer, 4, AuthBuffer, &AuthLength, NULL, 0, true); + if (status != rfid.STATUS_OK) { + Serial.println("Ultralight C Auth failed"); + Serial.println(rfid.GetStatusCodeName(status)); + return 0; + } + + memcpy(iv_ar,AuthBuffer+1,8); // use enc(RndB) as new IV for the next encryption. + + des.set_size(8); + des.tdesCbcDecipher(AuthBuffer+1,RndARndB+8); // decrypt enc(RndB) -> now we have RndB in the last 8 bytes of RndARndB + + randomSeed(AuthBuffer[1]); + int number; + for(int i=0; i<8; i++){ + number=random(255); + RndARndB[i]=number&0xFF; // write random Bytes to the first part of RndARndB + } + + rol(RndARndB+8,8); // roll the "RndB"-part of RndARndB + memcpy(RndARndB,RndA,8); // backup of RndA for later + + AuthBuffer[0] = 0xAF; // set the PCD-command + + memcpy(&IV,iv_ar,8); // set IV = to enk(RndB) for later encryption + des.set_IV(IV); + des.set_size(16); + des.tdesCbcEncipher(RndARndB, &AuthBuffer[1]); // careful! for some kind of reason RndARndB ist modified in this process + + status = rfid.PCD_CalculateCRC(AuthBuffer, 17, &AuthBuffer[17]); + if (status != rfid.STATUS_OK) { + return 0; + } + + memcpy(&IV,&AuthBuffer[9],8); // set IV to decrypt reply from PICC enc(RndA') -> RndA + des.set_IV(IV); + + status = rfid.PCD_TransceiveData(AuthBuffer, 19, AuthBuffer, &AuthLength, NULL, 0, true); + if (status != rfid.STATUS_OK) { + Serial.print(F("Auth failed failed: ")); + Serial.println(rfid.GetStatusCodeName(status)); + return 0; + } + else{ + if(AuthBuffer[0]==0x00){ // reply from PICC should start with 0x00 + des.set_size(8); + des.tdesCbcDecipher(AuthBuffer+1,dRndA); // decrypt now we have decrypted RndA' + + rol(RndA,8); // rotate orgiginal RndA to RndA' + for(i=0; i<8; i++){ // compare RndA' and dRndA + if(RndA[i] != dRndA[i]){ + i=9; + } + } + if(i==8){ + Serial.println(F("Keys match :-)")); + sendUID(UID, NULL, NULL); + } + else{ + Serial.println(F("Keys do not match")); + return 0; + } + } + else{ + Serial.println(F("Wrong answer!!!")); + return 0; + } + } + return 1; +} + +// Needed to create RndB' out of RndB +void rol(byte *data, int len){ + byte first = data[0]; + for (int i = 0; i < len-1; i++) { + data[i] = data[i+1]; + } + data[len-1] = first; +} + + + +void sendUID(char *UID, char *KeyOld, char *KeyNew){ + +char JSONmessageBuffer[200]; // contains the full JSON message +char topic[32]; + + if (Format_Mod==0){ // JSON object + if(RFID_Mod==1) // including timestamp + snprintf(JSONmessageBuffer, sizeof(JSONmessageBuffer), R"({"UID":"%s","KeyOld":"%s","KeyNew":"%s"})", UID, KeyOld, KeyNew); + else // only UID + snprintf(JSONmessageBuffer, sizeof(JSONmessageBuffer), R"({"UID":"%s"})", UID); + } + else{ // simple Text, sparated by ; + if(RFID_Mod==1) + snprintf(JSONmessageBuffer, sizeof(JSONmessageBuffer), "UID %s; KeyOld %s; KeyNew %s", UID, KeyOld, KeyNew); + else + snprintf(JSONmessageBuffer, sizeof(JSONmessageBuffer), "UID %s" , UID); + } + Serial.print(F("payload: ")); + Serial.println(JSONmessageBuffer); + + snprintf(topic, sizeof(topic), "/rfid_reader/%s", ReaderID); // generat a topic with the ReaderID + Serial.print(F("topic: ")); + Serial.println(topic); + Serial.println(""); + MQTTclient.publish(topic, JSONmessageBuffer); // send it to the broker +} + + +boolean setAuthKey(String Sdata){ +int i; +byte C_key[24]; + + for(i=0;i<32;i++){ + if(Sdata[i]==0x00){ + Serial.println(F("Key too short! (needs to be 32 digits)")); + return 0; + } + else{ + if((i%2==0)){ + C_key[i/2]=char2byte(&Sdata[i]); + } + } + } + for(i=0;i<8;i++){ + C_key[i+16]=C_key[i]; + } + des.change_key(C_key); + Serial.println(F("key set successfully!")); + return 1; +} + + + + + +/* ------------------------------------------------------------------------------------------------- + * Display stuff + */ + +/* + * Show a predefined message on the display. If the the message id is unknow, show the + * clear text message. The addText is used to provide variable information to a predefined + * text like "duration" + */ +void MessageToDisplay(int ID, String clearText, String addnText){ +String dump = ""; +String s1, s2; +int len, split=16; + + if(ID < maxMessage){ // if a predefined message is used + split=SplitAt[ID]; // look up where it should be splitted + dump += Message_Disp[ID]; // copy the predefined message + } + else{ + dump += clearText; // display the message provided as clear text + } + + if(addnText!=""){ + dump += " "; // add a space between + dump += addnText; // add a potential flexible Message like "countdown" or "Username" to the message + } + + len = dump.length(); // how long is the message? + + if(len>32) // if too long + len=32; // make it shorter + + if((len>split)&&(split>0)){ // if pre defined message is too long for a single line + s1 = dump.substring(0, split); + s2 = dump.substring(split,(len)); + } + else if(len>15){ // if a user defined message is too long for a single line + s1 = dump.substring(0, 15); + s2 = dump.substring(15,len); + } + else{ + s1 = dump.substring(0, len); // Just create a single line + } + s1 += ""; // terminate the strings properly + s2 += ""; // same for the second line. + display2lines(s1,s2); + + // save the lines in case an error needs to be prompted + lastLine1 = ""; + lastLine1 += s1; + lastLine2 = ""; + lastLine2 += s2; + linesSaved=1; +} + + +/* + * used to show reader generated status messages + */ + +void StatusToDisplay(int item){ + #ifdef ESP32 + EPaper.setFont(&FreeMonoBold18pt7b); + fsize=18; + #endif + display.setFont(ArialMT_Plain_24); + + if(item==CONNECT_TO_WIFI){ + display2lines("connecting...", "Wifi"); + } + else if (item==CONNECT_TO_MQTT){ + display2lines("connecting...", "MQTT"); + } + else if (item==PUSH_BUTTON){ + display2lines("For AP-Mode", "press button"); + } + else if(item==CONNECT_TO_LAN){ + display2lines("LAN", "connecting..."); + } + else if(item==MQTT_RFID){ + display2linesRAW("MQTT", "RFID"); + } + else if(item==IN_AP_MODE){ + display2linesRAW("AP-", "MODE"); + } + else if(item==ERROR_RFID){ + display2linesRAW("RFID-", "ERROR"); + } + else if(item==WRONG_RFID_TYPE){ + display2linesRAW("Read-", "ERROR"); + } + else if(item==ERROR_DHCP){ + display2lines("DHCP-ERROR", "pls. reboot"); + } +} + +void LastMessageDisplay(void){ + if(linesSaved){ + display.setFont(ArialMT_Plain_16); + #ifdef ESP32 + EPaper.setFont(&FreeMonoBold12pt7b); + fsize=12; + #endif + display2linesRAW(lastLine1, lastLine2); + } + else + StatusToDisplay(MQTT_RFID); +} + +void popRFIDerror(void){ + StatusToDisplay(ERROR_RFID); // show error + delay(2000); + LastMessageDisplay(); // show last message displayed +} + +/* + * Displays 2 lines of text in size 16 + */ + +void display2lines(String str1, String str2) { + display.setFont(ArialMT_Plain_16); + #ifdef ESP32 + EPaper.setFont(&FreeMonoBold12pt7b); + fsize=12; + #endif + display2linesRAW(str1, str2); +} + +/* + * Displays 2 lines of text in any size defined by the calling function + */ + +void display2linesRAW(String str1, String str2) { + display.clear(); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 13, str1); + display.drawString(64, 35, str2); + display.setFont(ArialMT_Plain_10); + display.drawString(64, 0, ReaderID); // Show the reader ID on top of the screen + display.display(); + #ifdef ESP32 + EPaper.fillScreen(GxEPD_WHITE); + int x1=(EPaper.width()/2-(str1.length()*fsize)/2); + EPaper.setCursor(x1,20+2*fsize); + EPaper.println(str1); + int x2=(EPaper.width()/2-(str2.length()*fsize)/2); + EPaper.setCursor(x2,50+2*fsize); + EPaper.println(str2); + EPaper.setFont(&FreeMonoBold9pt7b); + EPaper.setCursor(EPaper.width()-8*9,12); + EPaper.print(F("ID:")); + EPaper.println(ReaderID); + EPaper.updateWindow(0, 0, EPaper.width()-1, EPaper.height()-1, true); + // EPaper.update(); + #endif +} + + +/* + * Reading data from EEPROM + */ + +void readConfig (void){ +int i=0, j=0; +int item=0; // how many items to be read +char val; + + Wifi_Ether = EEPROM.read(0); + RFID_Mod = EEPROM.read(1); + Format_Mod = EEPROM.read(2); + + + i=3; + while(i<512){ + val=EEPROM.read(i); + if(val!='\r'){ + if(item==0){ // host + ssid[j]=val; + ssid[j+1]=0x00; + } + else if(item==1){ + password[j]=val; + password[j+1]=0x00; + } + else if(item==2){ + broker[j]=val; + broker[j+1]=0x00; + } + else if(item==3){ + ReaderID[j]=val; + ReaderID[j+1]=0x00; + } + else if(item==4){ + if(val=='1') + Wifi_Ether=1; + else if(val == '0') + Wifi_Ether=0; + } + else if(item==5){ + i=512; // stop. + } + j++; + } + else if((val==0x00)||(val==0xFF)){ // momory not initialized + i=512; + } + else{ + item++; + j=0; + } + i++; + } +} + + +void char2byteArray(String in, byte *out, int len_in){ + for(int i=0; i= '0' && c <= '9') { + x *= 16; + x += c - '0'; + } + else if (c >= 'A' && c <= 'F') { + x *= 16; + x += (c - 'A') + 10; + } + else if (c >= 'a' && c <= 'f') { + x *= 16; + x += (c - 'a') + 10; + } + s++; + } + return x; +} + + + + + +// convert byte array to HEX-character array +void byte2charArray(byte *in, char *out, int len_in){ +char translate[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + for(int i=0;i +#include + +#include "config.h" +#include "pins.h" +#include "nfc.h" +#include "otaproxy.h" +#include "helpers.h" +#include "Desfire.h" + +#include +#include + +#include +#include +#include +#include + +WiFiClient espClient; +//Config_Data config; + +PubSubClient* mqtt; +NFC* nfc; +OTAProxy* ota; + +void setup_wifi() +{ + delay(10); + Serial.println("Connecting Wifi ..."); + + WiFi.mode(WIFI_STA); + WiFi.begin(WLAN_SSID, WLAN_PASS); + + while (WiFi.status() != WL_CONNECTED) + { + delay(500); + } + + randomSeed(micros()); + Serial.println("WiFi connected"); +} + +void reconnect() +{ + while (!mqtt->connected()) + { + String clientId = "FabReader_"; + clientId += String(FABREADERID); + + Serial.println("Connecting MQTT ..."); + if (mqtt->connect(clientId.c_str())) + { + Serial.println("MQTT connected"); + char id[6] = "00000"; + sprintf(id, "%05d", FABREADERID); + mqtt->publish("fabreader", id); + + char topic_requestOTA[] = "fabreader/00000/requestOTA"; + sprintf(topic_requestOTA, "fabreader/%05d/requestOTA", FABREADERID); + mqtt->subscribe(topic_requestOTA); + + char topic_stopOTA[] = "fabreader/00000/stopOTA"; + sprintf(topic_stopOTA, "fabreader/%05d/stopOTA", FABREADERID); + mqtt->subscribe(topic_stopOTA); + } else { + Serial.print("failed, rc="); + Serial.print(mqtt->state()); + Serial.println(" try again in 5 seconds"); + delay(5000); + } + } +} + +void callback(char* topic, byte* payload, unsigned int length) +{ + Serial.println("Receive Message"); + Serial.println(topic); + if(ota->hasActiveOTA()) + { + ota->continueOTA(topic, payload, length); + } +} + +void setup() +{ + Serial.begin(115200); + Serial.print("\n\n\n"); + Serial.println("Booting ..."); + + pinMode(PIN_BUZZER, OUTPUT); + pinMode(PIN_BUTTON, INPUT); + pinMode(PIN_ETH_SPI_SS, OUTPUT); + digitalWrite(PIN_ETH_SPI_SS, HIGH); + + Serial.println("Connecting NFC ..."); + nfc = new NFC(PIN_RFID_SPI_SS, PIN_RFID_RST); + if(!(nfc->rfid->PCD_PerformSelfTest())) + { + Serial.println("NFC Test failed"); + } + Serial.println("NFC connected"); + + // setup_wifi(); + + // mqtt = new PubSubClient(espClient); + // mqtt->setServer(MQTT_BROKER, 1883); + // mqtt->setCallback(callback); + + // ota = new OTAProxy(mqtt, nfc, FABREADERID); +} + +void loop() +{ + if(!(nfc->hasCardSelected()) && nfc->hasNewCard() && nfc->selectCard()) + { + // DESFire::mifare_desfire_tag tag; + // tag.pcb = 0x0A; + // tag.cid = 0x00; + // DESFire::mifare_desfire_aid_t aid; + + // aid.data[0] = 0x42; + // aid.data[1] = 0x41; + // aid.data[2] = 0x46; + + // DESFire::StatusCode status = nfc->rfid->MIFARE_DESFIRE_SelectApplication(&tag, &aid); + // Serial.printf("%02x\n", status.mfrc522); + + char command_h[APDU_BUFFER_SIZE*2] = "905a00000342414600"; + byte command[APDU_BUFFER_SIZE] = { 0 }; + byte command_len = strlen(command_h)/2; + + + byte response[APDU_BUFFER_SIZE] = { 0 }; + byte response_len = 0; + char response_h[APDU_BUFFER_SIZE*2] = { 0 }; + + Serial.print("Command: "); + Serial.println(command_h); + + chars2bytes(command_h, command, true); + MFRC522::StatusCode status = nfc->Transceive(command, command_len, response, &response_len); + Serial.printf("PICC_Tranceive: 0x%02x\n", status); + // bytes2chars(response, response_len, response_h, true); + + // Serial.print("Response: "); + // Serial.println(response_h); + } + + if(nfc->hasCardSelected() && nfc->isCardLost()) + { + nfc->deselectCard(); + } + // if (!mqtt->connected()) + // { + // reconnect(); + // } + // mqtt->loop(); + + // if(ota->hasActiveOTA() && nfc->isCardLost()) + // { + // ota->cancelOTA(); + // } + + // if(!ota->hasActiveOTA() && nfc->hasNewCard()) + // { + // ota->startOTA(); + // } +} \ No newline at end of file