├── data ├── blue.jpg ├── red.jpg ├── orange.jpg └── yellow.jpg ├── images ├── alarm_off.png ├── alarm_full.png ├── alarm_part.png └── alarm_pending.png ├── README.md ├── LICENSE ├── M5Stack-HA-Alarm-panel.ino ├── MFRC522_I2C.h └── MFRC522_I2C.cpp /data/blue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhannink/M5Stack-HA-Alarm-panel/HEAD/data/blue.jpg -------------------------------------------------------------------------------- /data/red.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhannink/M5Stack-HA-Alarm-panel/HEAD/data/red.jpg -------------------------------------------------------------------------------- /data/orange.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhannink/M5Stack-HA-Alarm-panel/HEAD/data/orange.jpg -------------------------------------------------------------------------------- /data/yellow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhannink/M5Stack-HA-Alarm-panel/HEAD/data/yellow.jpg -------------------------------------------------------------------------------- /images/alarm_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhannink/M5Stack-HA-Alarm-panel/HEAD/images/alarm_off.png -------------------------------------------------------------------------------- /images/alarm_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhannink/M5Stack-HA-Alarm-panel/HEAD/images/alarm_full.png -------------------------------------------------------------------------------- /images/alarm_part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhannink/M5Stack-HA-Alarm-panel/HEAD/images/alarm_part.png -------------------------------------------------------------------------------- /images/alarm_pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhannink/M5Stack-HA-Alarm-panel/HEAD/images/alarm_pending.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Version history 2 | 0.1 - 19-01-2020 - Initial version 3 | 4 | 0.2 - 05-02-2020 - Added screen Timeout and added more Code comments 5 | 6 | 0.3 - 30-04-2020 - Added MQTT pubsubclient timeout parameter to overcome constant MQTT reconnects 7 | 8 | # M5Stack-HA-Alarm-panel 9 | M5stack alarm panel with RFID face to control Home Assistant MQTT alarm panel 10 | 11 | This M5Stack Alarm panel uses RFID to disarm the alarm and buttons to arm (away or home) the alarm system. 12 | 13 | ## Introduction 14 | Communication to Home Assistant is done via MQTT (TLS with authentication). 15 | The M5Stack alarm panel is build to be used in combination with the [Home Assistant MQTT Alarm Panel](https://www.home-assistant.io/integrations/alarm_control_panel.mqtt) 16 | 17 | It uses a [M5Stack Core](https://m5stack.com/collections/m5-core/products/basic-core-iot-development-kit) , a [M5Stack baseplate](https://m5stack.com/products/m5-faces-ii-bottom-board) and a [M5Stack RFID Face](https://m5stack.com/products/rfid-rc522-panel-for-m5-faces?_pos=2&_sid=120cb46b5&_ss=r) 18 | 19 | ## Screenshots 20 | ![Alarm Off](/images/alarm_off.png "Alarm Off") ![Alarm_pending](/images/alarm_pending.png "Alarm pending") ![Alarm_Fully On](/images/alarm_full.png "Alarm fully on") 21 | 22 | ## Configuration 23 | To compile and use the code for your own purpose/HA configuration, just change the following settings in the Arduino code: 24 | 25 | const char ssid[] = "WiFi SSID"; 26 | const char pass[] = "WiFi password"; 27 | const char* mqtt_host = "mqtt.hostname (FQDN)"; 28 | const int mqtt_port = 8883; 29 | const char* mqtt_user = "MQTT-userID"; 30 | const char* mqtt_pass = "MQTT-password"; 31 | const char* mqtt_clientId = "M5stack-alarm1"; 32 | const char* mqtt_state_topic = "home/alarm"; 33 | const char* mqtt_command_topic = "home/alarm/set"; 34 | const char* mqtt_state_full = "armed_away"; 35 | const char* mqtt_state_part = "armed_home"; 36 | const char* mqtt_state_off = "disarmed"; 37 | const char* mqtt_state_pending = "pending"; 38 | const char* mqtt_command_full = "ARM_AWAY"; 39 | const char* mqtt_command_part = "ARM_HOME"; 40 | const char* mqtt_command_off = "DISARM"; 41 | const char* display_state_full ="Fully Armed"; 42 | const char* display_state_part ="Partially Armed"; 43 | const char* display_state_off ="Switched Off"; 44 | const char* display_state_pending ="Pending"; 45 | const char* display_present_card = "Please present Card!"; 46 | const char* button_a_text = "Off"; 47 | const char* button_b_text = "Home"; 48 | const char* button_c_text = "Away"; 49 | 50 | The valid RFID UID's have to be inserted into the follwing place in the Arduino Code: 51 | 52 | //Array with valid RFID UID's 53 | byte uidArray[3][4]={ 54 | {0x11,0x22,0x33,0x44}, 55 | {0x12,0x22,0x33,0x45}, 56 | {0x13,0x22,0x33,0x46}, 57 | }; 58 | In this example, I use 3 different tokens. If you have more or less then change the `uidArray[3]` value into the correct one. 59 | 60 | You can initially read UID from your RFID tokens by connecting a serial consiole from the Arduino IDE. Whenever you present a RFID token, the UID is logged in the serial console. On of the ToDo items is add a master token to add/delete valid tokens from the system interactive. 61 | The M5Stack RFID face is actually a I2C RC522 RFID reader compatible with 13,56Mhz tokens. 62 | 63 | If you additionally want to use readable names for the cards, define these in: 64 | 65 | //Array with accompanying user names 66 | String nameArray[3]={ 67 | "Card1","Card2","Card3" 68 | }; 69 | This example is also for 3 different tokens. Please change the number `nameArray[3]` into the corecct number of tokens. 70 | 71 | The names are now displayed in the header briefly and are being dumped to the serial console. But you could sent the names via MQTT and use it for displaying/Text To Speech or presence detection. 72 | 73 | ## Uploading images to SPIFFS 74 | The pictures on the screen are PNG files loaded from the filesystem (SPIFFS) of the m5Stack. Through the Arduino IDE choose for *Tools > ESP32 Sketch Data Upload* from the menu to upload the pictures to the SPIFFS filesystem. The PNG picture files are in the [data folder](/data) of this github project. 75 | 76 | ## Used Arduino Libraries 77 | I use the MFRC522 I2C library from M5Stack and the [M5ez library from Rob Gonggrijp](https://github.com/ropg/M5ez) to create the button and header text fields. Due to a bug, the time does not (yet) show in the righthand corner, next to the wifi strentgh icon. 78 | 79 | ## Security 80 | This code uses a secured authenticated TLS connection to an MQTT server. My MQTT broker is secured using a Lets Encrypt certificate. So the Lets encrypted CA is added to the code to verify the validity of the MQTT broker certificate. 81 | 82 | **So the communication is secured by WPA2, TLS and UserID/password. That should be sufficient for us ordinary people. But bear in mind that it is relative easy to clone an existing RFID token. https://www.getkisi.com/blog/how-to-copy-access-cards-and-keyfobs** 83 | 84 | ## M5Stack alarm panel state machine 85 | The screens follow the **MQTT_Status_topic**, whatever the status topic is **(armed_away, armed_home, pending or disarmed)**, that screen is being showed om the M5Stack. 86 | If a button is pressed or a card is presented, the according Command **(ARM_AWAY, ARM_HOME or DISARM** is send to the **mqtt_command_topic**. The MQTT broker dispatches this to the alarm (via HA) and the alarm changes the status through the status topiuc, again via MQTT. 87 | 88 | ## M5Stack sound bug 89 | The I2C RFID reader is interfering in a very annoyoing way with the onboard amplifier/speaker, resulting in a hisshing/whining sound. This is a known problem with the M5stack platform. To overcome this, I connected the enable amplifier input with GPIO 5 and first enable the amplifier before beeping and disabling the amplifier directly again after the beep. Read http://community.m5stack.com/topic/367/mod-to-programmatically-disable-speaker-whine-hiss to get more information. 90 | 91 | ## ToDo 92 | * add auto screen brightness by adding an LDR or presence detection (screen on when someone in front) by using a small PIR. 93 | * Make it more secure by using more advanced features from the RFID tokens than just the UID. 94 | * Read valid UID's and names from a textfile of the SPIFFS filesystem. 95 | * Add a master card and RFID learning mode to interactivally add and remove tokens. 96 | * Clean up the Code and make it more robust . 97 | * Add Availablilty topic (LWT and birth messages) so Host can see whenever panels are not online. 98 | * Make it more configureable (e.g. Continous pending beeps are not always enjoyed ;-)). 99 | * Fix the M5ez time problem 100 | * Make a version with keypad face instead of rfid face. 101 | 102 | *I am not a programmer in any way and I have very limited time free, so please feel free to improve or extend my code with additional features.* 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /M5Stack-HA-Alarm-panel.ino: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | // M5Stack Home Assistant RFID MQTT alarm panel 5 | // Author: Remco Hannink 6 | // Version: 0.3 7 | // Date: 30-04-2020 8 | 9 | #include "FS.h" 10 | #include "SPIFFS.h" 11 | #include "M5Stack.h" 12 | #include 13 | #include 14 | #include 15 | #include "MFRC522_I2C.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | // set MQTT timeout parameters to overcome constant reconnection 23 | 24 | #define MQTT_KEEPALIVE 60 25 | 26 | // The parameters below should be adapted to your own situation 27 | 28 | const char ssid[] = "WiFi SSID"; 29 | const char pass[] = "WiFi password"; 30 | const char* mqtt_host = "mqtt.hostname (FQDN)"; 31 | const int mqtt_port = 8883; 32 | const char* mqtt_user = "MQTT-userID"; 33 | const char* mqtt_pass = "MQTT-password"; 34 | const char* mqtt_clientId = "M5stack-alarm1"; 35 | const char* mqtt_state_topic = "home/alarm"; 36 | const char* mqtt_command_topic = "home/alarm/set"; 37 | const char* mqtt_state_full = "armed_away"; 38 | const char* mqtt_state_part = "armed_home"; 39 | const char* mqtt_state_off = "disarmed"; 40 | const char* mqtt_state_pending = "pending"; 41 | const char* mqtt_command_full = "ARM_AWAY"; 42 | const char* mqtt_command_part = "ARM_HOME"; 43 | const char* mqtt_command_off = "DISARM"; 44 | const char* display_state_full ="Fully Armed"; 45 | const char* display_state_part ="Partially Armed"; 46 | const char* display_state_off ="Switched Off"; 47 | const char* display_state_pending ="Pending"; 48 | const char* display_present_card = "Please present Card!"; 49 | const char* button_a_text = "Off"; 50 | const char* button_b_text = "Home"; 51 | const char* button_c_text = "Away"; 52 | 53 | // Adding the LetsEncrypt CA to verify the MQTT server certificate 54 | 55 | const char* ca_cert = \ 56 | "-----BEGIN CERTIFICATE-----\n" \ 57 | "MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\n" \ 58 | "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \ 59 | "DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\n" \ 60 | "PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\n" \ 61 | "Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" \ 62 | "AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\n" \ 63 | "rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\n" \ 64 | "OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\n" \ 65 | "xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n" \ 66 | "7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\n" \ 67 | "aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\n" \ 68 | "HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\n" \ 69 | "SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\n" \ 70 | "ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\n" \ 71 | "AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\n" \ 72 | "R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\n" \ 73 | "JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\n" \ 74 | "Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n" \ 75 | "-----END CERTIFICATE-----\n"; 76 | 77 | WiFiClientSecure m5stackClient; 78 | PubSubClient client(m5stackClient); 79 | long lastMsg = 0; 80 | char msg[50]; 81 | char buttonString[50]; 82 | String alarmStatus =""; 83 | byte readCard[4]; 84 | 85 | //Array with valid RFID UID's 86 | byte uidArray[3][4]={ 87 | {0x11,0x22,0x33,0x44}, 88 | {0x12,0x22,0x33,0x45}, 89 | {0x13,0x22,0x33,0x46}, 90 | }; 91 | 92 | //Array with accompanying user names 93 | String nameArray[3]={ 94 | "Card1","Card2","Card3" 95 | }; 96 | 97 | int beepVolume=1; 98 | int screenTimeout = 10000; // screen timeout in mSec 99 | int screenStatus = 1; 100 | 101 | MFRC522 mfrc522(0x28); 102 | 103 | void setup(){ 104 | #include 105 | 106 | M5.begin(true, false, true, true); 107 | 108 | // turn off the M5stack amplifier to overcome I2C interference with speaker. 109 | // For modification see: http://community.m5stack.com/topic/367/mod-to-programmatically-disable-speaker-whine-hiss 110 | 111 | pinMode(5,OUTPUT); 112 | digitalWrite(5, LOW); 113 | 114 | // Setup Wifi 115 | WiFi.begin(ssid, pass); 116 | while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } 117 | Serial.print("IP address: "); 118 | Serial.println(WiFi.localIP()); 119 | 120 | // Mount SPIFFS for icons and pictures 121 | if(!SPIFFS.begin(true)){ 122 | Serial.println("SPIFFS Mount Failed"); 123 | return; 124 | } 125 | 126 | // initialize ez5stack screen and buttons 127 | ez.begin(); 128 | ez.canvas.clear(); 129 | M5.Lcd.fillScreen(TFT_WHITE); 130 | strcpy(buttonString,button_a_text); 131 | strcat(buttonString, " # "); 132 | strcat(buttonString,button_b_text); 133 | strcat(buttonString, " # "); 134 | strcat(buttonString,button_c_text); 135 | ez.buttons.show(buttonString); 136 | 137 | // Initialize RFID reader 138 | mfrc522.PCD_Init(); // Init MFRC522 139 | 140 | m5stackClient.setCACert(ca_cert); 141 | client.setServer(mqtt_host, mqtt_port); 142 | client.setCallback(callback); 143 | reconnect(); 144 | } 145 | 146 | void callback(char* topic, byte* payload, unsigned int length) { 147 | String topicText= ""; 148 | M5.Lcd.setBrightness(50); 149 | screenTimeout=10000; 150 | screenStatus = 1; 151 | Serial.print("Message arrived ["); 152 | Serial.print(topic); 153 | Serial.print("] "); 154 | for (int i = 0; i < length; i++) { 155 | Serial.print((char)payload[i]); 156 | 157 | topicText +=((char)payload[i]); 158 | } 159 | Serial.println(); 160 | 161 | // Set M5Stack screen according to incoming state message 162 | if (topicText == mqtt_state_part){ 163 | beep(400); 164 | M5.Lcd.drawJpgFile(SPIFFS, "/orange.jpg", 80, 40); 165 | ez.header.show(display_state_part); 166 | alarmStatus = mqtt_state_part; 167 | } 168 | if (topicText == mqtt_state_off){ 169 | beep(50); 170 | delay(100); 171 | beep(50); 172 | delay (100); 173 | beep(50); 174 | M5.Lcd.drawJpgFile(SPIFFS, "/blue.jpg", 80, 40); 175 | ez.header.show(display_state_off); 176 | alarmStatus = mqtt_state_off; 177 | } 178 | if (topicText == mqtt_state_full){ 179 | beep(400); 180 | M5.Lcd.drawJpgFile(SPIFFS, "/red.jpg", 80, 40); 181 | ez.header.show(display_state_full); 182 | alarmStatus = mqtt_state_full; 183 | } 184 | if (topicText == mqtt_state_pending){ 185 | beep(100); 186 | delay(100); 187 | M5.Lcd.drawJpgFile(SPIFFS, "/yellow.jpg", 80, 40); 188 | ez.header.show(display_state_pending); 189 | alarmStatus = mqtt_state_pending; 190 | } 191 | } 192 | 193 | void reconnect() { 194 | // Loop until we're reconnected 195 | while (!client.connected()) { 196 | Serial.print("Attempting MQTT connection..."); 197 | // Attempt to connect 198 | if (client.connect(mqtt_clientId,mqtt_user,mqtt_pass)) { 199 | Serial.println("connected"); 200 | // Once connected, publish an announcement... 201 | client.publish(mqtt_command_topic, "Connected"); 202 | // ... and resubscribe 203 | client.subscribe(mqtt_state_topic); 204 | } else { 205 | Serial.print("failed, rc="); 206 | Serial.print(client.state()); 207 | Serial.println(" try again in 5 seconds"); 208 | // Wait 5 seconds before retrying 209 | delay(5000); 210 | } 211 | } 212 | } 213 | void loop(){ 214 | if (!client.connected()) { 215 | reconnect(); 216 | } 217 | client.loop(); 218 | M5.update(); 219 | screenTimeout = screenTimeout-200; 220 | 221 | // If screenTimeout is zero then reset timer 222 | 223 | if (screenTimeout == 0){ 224 | screenTimeout=10000; 225 | 226 | // if screenTimeout is zero and screenStatus is on, the dim the screen 227 | 228 | if (screenStatus == 1){ 229 | 230 | // if timer is zero and screen is on, dim the screen graduately in 50 steps ans set screen Status to 0 231 | 232 | for (int i = 50; i >= 0; i--) { 233 | M5.Lcd.setBrightness(i); 234 | delay(20); 235 | } 236 | screenStatus = 0; 237 | } 238 | } 239 | 240 | // If alarm status equals pending, then make sure screen stays on 241 | 242 | if (alarmStatus == mqtt_state_pending){ 243 | M5.Lcd.setBrightness(50); 244 | screenStatus = 1; 245 | screenTimeout = 10000; 246 | } 247 | 248 | // If button A is pressed and status is not already off and screen is already on, the enable alarm off and ask for RFID token 249 | 250 | if(M5.BtnA.wasPressed() && (alarmStatus != mqtt_state_off) && (screenStatus == 1)) { 251 | beep(100); 252 | ez.header.show(display_present_card); 253 | Serial.println(display_present_card); 254 | screenTimeout = 10000; 255 | screenStatus = 1; 256 | } 257 | 258 | // If button B is pressed and status is not already home and screen is on, then enable home away mode 259 | 260 | if(M5.BtnB.wasPressed() && (alarmStatus != mqtt_state_part) && (screenStatus == 1)) { 261 | beep(100); 262 | M5.Lcd.drawJpgFile(SPIFFS, "/orange.jpg", 80, 40); 263 | ez.header.show(display_state_part); 264 | client.publish(mqtt_command_topic, mqtt_command_part); 265 | Serial.println(mqtt_command_part); 266 | screenTimeout = 10000; 267 | } 268 | 269 | // If button C is pressed and status is not already away and screen is on, then enable away mode 270 | 271 | if(M5.BtnC.wasPressed() && (alarmStatus != mqtt_state_full) && (screenStatus == 1)) { 272 | beep(100); 273 | M5.Lcd.drawJpgFile(SPIFFS, "/red.jpg", 80, 40); 274 | ez.header.show(display_state_full); 275 | client.publish(mqtt_command_topic, mqtt_command_full); 276 | Serial.println(mqtt_command_full); 277 | screenTimeout = 10000; 278 | } 279 | 280 | // If any button was pressed while screen is off, the just turn screen on and do nothing else 281 | 282 | if((M5.BtnA.wasPressed() || M5.BtnB.wasPressed() || M5.BtnC.wasPressed()) && (screenStatus == 0)){ 283 | M5.Lcd.setBrightness(50); 284 | screenTimeout=10000; 285 | screenStatus = 1; 286 | delay(200); 287 | } 288 | 289 | // Look for new cards, and select one if present 290 | 291 | if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) { 292 | delay(200); 293 | return; 294 | } 295 | 296 | // Now a card is read. The UID is in mfrc522.uid. 297 | // sound beep and turn on screen; 298 | 299 | M5.Lcd.setBrightness(50); 300 | screenTimeout = 10000; 301 | screenStatus = 1; 302 | beep(100); 303 | 304 | // Only act if alarm state != off, else, ignore card 305 | 306 | if (alarmStatus != mqtt_state_off) { 307 | 308 | Serial.print(F("Card UID:")); 309 | for (byte i = 0; i < mfrc522.uid.size; i++) { 310 | readCard[i] = mfrc522.uid.uidByte[i]; 311 | Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); 312 | Serial.print(mfrc522.uid.uidByte[i], HEX); 313 | } 314 | Serial.println(); 315 | client.publish(mqtt_state_topic, "RFID card detected"); 316 | 317 | // check if RFID UID is valid one and find username 318 | 319 | for(int i=0; i<3; i++){ 320 | Serial.print("Testing Match with checkArray "); 321 | Serial.println(i); 322 | if((memcmp(readCard,uidArray[i],4))==0){ 323 | // UID is valid, disarm the alarm and break 324 | Serial.print("Arrays Match, name is:"); 325 | Serial.println(nameArray[i]); 326 | M5.Lcd.drawJpgFile(SPIFFS, "/blue.jpg", 80, 40); 327 | ez.header.show(nameArray[i]); 328 | client.publish(mqtt_command_topic, mqtt_command_off); 329 | break; 330 | } 331 | } 332 | } 333 | 334 | } 335 | 336 | uint8_t music[1000]; 337 | void tone_volume(uint16_t frequency, uint32_t duration) { 338 | float interval=0.001257 * float(frequency); 339 | float phase=0; 340 | for (int i=0;i<1000;i++) { 341 | music[i]=127+126 * sin(phase); 342 | phase+=interval; 343 | } 344 | music[999]=0; 345 | int remains=duration; 346 | for (int i=0;i 82 | #include 83 | 84 | // Firmware data for self-test 85 | // Reference values based on firmware version 86 | // Hint: if needed, you can remove unused self-test data to save flash memory 87 | // 88 | // Version 0.0 (0x90) 89 | // Philips Semiconductors; Preliminary Specification Revision 2.0 - 01 August 2005; 16.1 Sefttest 90 | const byte MFRC522_firmware_referenceV0_0[] PROGMEM = { 91 | 0x00, 0x87, 0x98, 0x0f, 0x49, 0xFF, 0x07, 0x19, 92 | 0xBF, 0x22, 0x30, 0x49, 0x59, 0x63, 0xAD, 0xCA, 93 | 0x7F, 0xE3, 0x4E, 0x03, 0x5C, 0x4E, 0x49, 0x50, 94 | 0x47, 0x9A, 0x37, 0x61, 0xE7, 0xE2, 0xC6, 0x2E, 95 | 0x75, 0x5A, 0xED, 0x04, 0x3D, 0x02, 0x4B, 0x78, 96 | 0x32, 0xFF, 0x58, 0x3B, 0x7C, 0xE9, 0x00, 0x94, 97 | 0xB4, 0x4A, 0x59, 0x5B, 0xFD, 0xC9, 0x29, 0xDF, 98 | 0x35, 0x96, 0x98, 0x9E, 0x4F, 0x30, 0x32, 0x8D 99 | }; 100 | // Version 1.0 (0x91) 101 | // NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test 102 | const byte MFRC522_firmware_referenceV1_0[] PROGMEM = { 103 | 0x00, 0xC6, 0x37, 0xD5, 0x32, 0xB7, 0x57, 0x5C, 104 | 0xC2, 0xD8, 0x7C, 0x4D, 0xD9, 0x70, 0xC7, 0x73, 105 | 0x10, 0xE6, 0xD2, 0xAA, 0x5E, 0xA1, 0x3E, 0x5A, 106 | 0x14, 0xAF, 0x30, 0x61, 0xC9, 0x70, 0xDB, 0x2E, 107 | 0x64, 0x22, 0x72, 0xB5, 0xBD, 0x65, 0xF4, 0xEC, 108 | 0x22, 0xBC, 0xD3, 0x72, 0x35, 0xCD, 0xAA, 0x41, 109 | 0x1F, 0xA7, 0xF3, 0x53, 0x14, 0xDE, 0x7E, 0x02, 110 | 0xD9, 0x0F, 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 111 | }; 112 | // Version 2.0 (0x92) 113 | // NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test 114 | const byte MFRC522_firmware_referenceV2_0[] PROGMEM = { 115 | 0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95, 116 | 0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE, 117 | 0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82, 118 | 0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49, 119 | 0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81, 120 | 0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9, 121 | 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, 122 | 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F 123 | }; 124 | // Clone 125 | // Fudan Semiconductor FM17522 (0x88) 126 | const byte FM17522_firmware_reference[] PROGMEM = { 127 | 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, 128 | 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, 129 | 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, 130 | 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, 131 | 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, 132 | 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, 133 | 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, 134 | 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 135 | }; 136 | 137 | class MFRC522 { 138 | public: 139 | // MFRC522 registers. Described in chapter 9 of the datasheet. 140 | enum PCD_Register { 141 | // Page 0: Command and status 142 | // 0x00 // reserved for future use 143 | CommandReg = 0x01 , // starts and stops command execution 144 | ComIEnReg = 0x02 , // enable and disable interrupt request control bits 145 | DivIEnReg = 0x03 , // enable and disable interrupt request control bits 146 | ComIrqReg = 0x04 , // interrupt request bits 147 | DivIrqReg = 0x05 , // interrupt request bits 148 | ErrorReg = 0x06 , // error bits showing the error status of the last command executed 149 | Status1Reg = 0x07 , // communication status bits 150 | Status2Reg = 0x08 , // receiver and transmitter status bits 151 | FIFODataReg = 0x09 , // input and output of 64 byte FIFO buffer 152 | FIFOLevelReg = 0x0A , // number of bytes stored in the FIFO buffer 153 | WaterLevelReg = 0x0B , // level for FIFO underflow and overflow warning 154 | ControlReg = 0x0C , // miscellaneous control registers 155 | BitFramingReg = 0x0D , // adjustments for bit-oriented frames 156 | CollReg = 0x0E , // bit position of the first bit-collision detected on the RF interface 157 | // 0x0F // reserved for future use 158 | 159 | // Page 1: Command 160 | // 0x10 // reserved for future use 161 | ModeReg = 0x11 , // defines general modes for transmitting and receiving 162 | TxModeReg = 0x12 , // defines transmission data rate and framing 163 | RxModeReg = 0x13 , // defines reception data rate and framing 164 | TxControlReg = 0x14 , // controls the logical behavior of the antenna driver pins TX1 and TX2 165 | TxASKReg = 0x15 , // controls the setting of the transmission modulation 166 | TxSelReg = 0x16 , // selects the internal sources for the antenna driver 167 | RxSelReg = 0x17 , // selects internal receiver settings 168 | RxThresholdReg = 0x18 , // selects thresholds for the bit decoder 169 | DemodReg = 0x19 , // defines demodulator settings 170 | // 0x1A // reserved for future use 171 | // 0x1B // reserved for future use 172 | MfTxReg = 0x1C , // controls some MIFARE communication transmit parameters 173 | MfRxReg = 0x1D , // controls some MIFARE communication receive parameters 174 | // 0x1E // reserved for future use 175 | SerialSpeedReg = 0x1F , // selects the speed of the serial UART interface 176 | 177 | // Page 2: Configuration 178 | // 0x20 // reserved for future use 179 | CRCResultRegH = 0x21 , // shows the MSB and LSB values of the CRC calculation 180 | CRCResultRegL = 0x22 , 181 | // 0x23 // reserved for future use 182 | ModWidthReg = 0x24 , // controls the ModWidth setting? 183 | // 0x25 // reserved for future use 184 | RFCfgReg = 0x26 , // configures the receiver gain 185 | GsNReg = 0x27 , // selects the conductance of the antenna driver pins TX1 and TX2 for modulation 186 | CWGsPReg = 0x28 , // defines the conductance of the p-driver output during periods of no modulation 187 | ModGsPReg = 0x29 , // defines the conductance of the p-driver output during periods of modulation 188 | TModeReg = 0x2A , // defines settings for the internal timer 189 | TPrescalerReg = 0x2B , // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. 190 | TReloadRegH = 0x2C , // defines the 16-bit timer reload value 191 | TReloadRegL = 0x2D , 192 | TCounterValueRegH = 0x2E , // shows the 16-bit timer value 193 | TCounterValueRegL = 0x2F , 194 | 195 | // Page 3: Test Registers 196 | // 0x30 // reserved for future use 197 | TestSel1Reg = 0x31 , // general test signal configuration 198 | TestSel2Reg = 0x32 , // general test signal configuration 199 | TestPinEnReg = 0x33 , // enables pin output driver on pins D1 to D7 200 | TestPinValueReg = 0x34 , // defines the values for D1 to D7 when it is used as an I/O bus 201 | TestBusReg = 0x35 , // shows the status of the internal test bus 202 | AutoTestReg = 0x36 , // controls the digital self test 203 | VersionReg = 0x37 , // shows the software version 204 | AnalogTestReg = 0x38 , // controls the pins AUX1 and AUX2 205 | TestDAC1Reg = 0x39 , // defines the test value for TestDAC1 206 | TestDAC2Reg = 0x3A , // defines the test value for TestDAC2 207 | TestADCReg = 0x3B // shows the value of ADC I and Q channels 208 | // 0x3C // reserved for production tests 209 | // 0x3D // reserved for production tests 210 | // 0x3E // reserved for production tests 211 | // 0x3F // reserved for production tests 212 | }; 213 | 214 | // MFRC522 commands. Described in chapter 10 of the datasheet. 215 | enum PCD_Command { 216 | PCD_Idle = 0x00, // no action, cancels current command execution 217 | PCD_Mem = 0x01, // stores 25 bytes into the internal buffer 218 | PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number 219 | PCD_CalcCRC = 0x03, // activates the CRC coprocessor or performs a self test 220 | PCD_Transmit = 0x04, // transmits data from the FIFO buffer 221 | PCD_NoCmdChange = 0x07, // no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit 222 | PCD_Receive = 0x08, // activates the receiver circuits 223 | PCD_Transceive = 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission 224 | PCD_MFAuthent = 0x0E, // performs the MIFARE standard authentication as a reader 225 | PCD_SoftReset = 0x0F // resets the MFRC522 226 | }; 227 | 228 | // MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor (on the PCD). 229 | // Described in 9.3.3.6 / table 98 of the datasheet at http://www.nxp.com/documents/data_sheet/MFRC522.pdf 230 | enum PCD_RxGain { 231 | RxGain_18dB = 0x00 << 4, // 000b - 18 dB, minimum 232 | RxGain_23dB = 0x01 << 4, // 001b - 23 dB 233 | RxGain_18dB_2 = 0x02 << 4, // 010b - 18 dB, it seems 010b is a duplicate for 000b 234 | RxGain_23dB_2 = 0x03 << 4, // 011b - 23 dB, it seems 011b is a duplicate for 001b 235 | RxGain_33dB = 0x04 << 4, // 100b - 33 dB, average, and typical default 236 | RxGain_38dB = 0x05 << 4, // 101b - 38 dB 237 | RxGain_43dB = 0x06 << 4, // 110b - 43 dB 238 | RxGain_48dB = 0x07 << 4, // 111b - 48 dB, maximum 239 | RxGain_min = 0x00 << 4, // 000b - 18 dB, minimum, convenience for RxGain_18dB 240 | RxGain_avg = 0x04 << 4, // 100b - 33 dB, average, convenience for RxGain_33dB 241 | RxGain_max = 0x07 << 4 // 111b - 48 dB, maximum, convenience for RxGain_48dB 242 | }; 243 | 244 | // Commands sent to the PICC. 245 | enum PICC_Command { 246 | // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) 247 | PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. 248 | PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. 249 | PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. 250 | PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 251 | PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 252 | PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 253 | PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. 254 | // The commands used for MIFARE Classic (from http://www.nxp.com/documents/data_sheet/MF1S503x.pdf, Section 9) 255 | // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. 256 | // The read/write commands can also be used for MIFARE Ultralight. 257 | PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A 258 | PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B 259 | PICC_CMD_MF_READ = 0x30, // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. 260 | PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. 261 | PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and stores the result in the internal data register. 262 | PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and stores the result in the internal data register. 263 | PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. 264 | PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. 265 | // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) 266 | // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. 267 | PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC. 268 | }; 269 | 270 | // MIFARE constants that does not fit anywhere else 271 | enum MIFARE_Misc { 272 | MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. 273 | MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes. 274 | }; 275 | 276 | // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more. 277 | enum PICC_Type { 278 | PICC_TYPE_UNKNOWN = 0, 279 | PICC_TYPE_ISO_14443_4 = 1, // PICC compliant with ISO/IEC 14443-4 280 | PICC_TYPE_ISO_18092 = 2, // PICC compliant with ISO/IEC 18092 (NFC) 281 | PICC_TYPE_MIFARE_MINI = 3, // MIFARE Classic protocol, 320 bytes 282 | PICC_TYPE_MIFARE_1K = 4, // MIFARE Classic protocol, 1KB 283 | PICC_TYPE_MIFARE_4K = 5, // MIFARE Classic protocol, 4KB 284 | PICC_TYPE_MIFARE_UL = 6, // MIFARE Ultralight or Ultralight C 285 | PICC_TYPE_MIFARE_PLUS = 7, // MIFARE Plus 286 | PICC_TYPE_TNP3XXX = 8, // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure 287 | PICC_TYPE_NOT_COMPLETE = 255 // SAK indicates UID is not complete. 288 | }; 289 | 290 | // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. 291 | enum StatusCode { 292 | STATUS_OK = 1, // Success 293 | STATUS_ERROR = 2, // Error in communication 294 | STATUS_COLLISION = 3, // Collission detected 295 | STATUS_TIMEOUT = 4, // Timeout in communication. 296 | STATUS_NO_ROOM = 5, // A buffer is not big enough. 297 | STATUS_INTERNAL_ERROR = 6, // Internal error in the code. Should not happen ;-) 298 | STATUS_INVALID = 7, // Invalid argument. 299 | STATUS_CRC_WRONG = 8, // The CRC_A does not match 300 | STATUS_MIFARE_NACK = 9 // A MIFARE PICC responded with NAK. 301 | }; 302 | 303 | // A struct used for passing the UID of a PICC. 304 | typedef struct { 305 | byte size; // Number of bytes in the UID. 4, 7 or 10. 306 | byte uidByte[10]; 307 | byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. 308 | } Uid; 309 | 310 | // A struct used for passing a MIFARE Crypto1 key 311 | typedef struct { 312 | byte keyByte[MF_KEY_SIZE]; 313 | } MIFARE_Key; 314 | 315 | // Member variables 316 | Uid uid; // Used by PICC_ReadCardSerial(). 317 | 318 | // Size of the MFRC522 FIFO 319 | static const byte FIFO_SIZE = 64; // The FIFO is 64 bytes. 320 | 321 | ///////////////////////////////////////////////////////////////////////////////////// 322 | // Functions for setting up the Arduino 323 | ///////////////////////////////////////////////////////////////////////////////////// 324 | MFRC522(byte chipAddress); 325 | 326 | ///////////////////////////////////////////////////////////////////////////////////// 327 | // Basic interface functions for communicating with the MFRC522 328 | ///////////////////////////////////////////////////////////////////////////////////// 329 | void PCD_WriteRegister(byte reg, byte value); 330 | void PCD_WriteRegister(byte reg, byte count, byte *values); 331 | byte PCD_ReadRegister(byte reg); 332 | void PCD_ReadRegister(byte reg, byte count, byte *values, byte rxAlign = 0); 333 | void setBitMask(unsigned char reg, unsigned char mask); 334 | void PCD_SetRegisterBitMask(byte reg, byte mask); 335 | void PCD_ClearRegisterBitMask(byte reg, byte mask); 336 | byte PCD_CalculateCRC(byte *data, byte length, byte *result); 337 | 338 | ///////////////////////////////////////////////////////////////////////////////////// 339 | // Functions for manipulating the MFRC522 340 | ///////////////////////////////////////////////////////////////////////////////////// 341 | void PCD_Init(); 342 | void PCD_Reset(); 343 | void PCD_AntennaOn(); 344 | void PCD_AntennaOff(); 345 | byte PCD_GetAntennaGain(); 346 | void PCD_SetAntennaGain(byte mask); 347 | bool PCD_PerformSelfTest(); 348 | 349 | ///////////////////////////////////////////////////////////////////////////////////// 350 | // Functions for communicating with PICCs 351 | ///////////////////////////////////////////////////////////////////////////////////// 352 | byte PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); 353 | byte PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); 354 | byte PICC_RequestA(byte *bufferATQA, byte *bufferSize); 355 | byte PICC_WakeupA(byte *bufferATQA, byte *bufferSize); 356 | byte PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); 357 | byte PICC_Select(Uid *uid, byte validBits = 0); 358 | byte PICC_HaltA(); 359 | 360 | ///////////////////////////////////////////////////////////////////////////////////// 361 | // Functions for communicating with MIFARE PICCs 362 | ///////////////////////////////////////////////////////////////////////////////////// 363 | byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); 364 | void PCD_StopCrypto1(); 365 | byte MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); 366 | byte MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); 367 | byte MIFARE_Decrement(byte blockAddr, long delta); 368 | byte MIFARE_Increment(byte blockAddr, long delta); 369 | byte MIFARE_Restore(byte blockAddr); 370 | byte MIFARE_Transfer(byte blockAddr); 371 | byte MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); 372 | byte MIFARE_GetValue(byte blockAddr, long *value); 373 | byte MIFARE_SetValue(byte blockAddr, long value); 374 | 375 | ///////////////////////////////////////////////////////////////////////////////////// 376 | // Support functions 377 | ///////////////////////////////////////////////////////////////////////////////////// 378 | byte PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); 379 | // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory 380 | //const char *GetStatusCodeName(byte code); 381 | const __FlashStringHelper *GetStatusCodeName(byte code); 382 | byte PICC_GetType(byte sak); 383 | // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory 384 | //const char *PICC_GetTypeName(byte type); 385 | const __FlashStringHelper *PICC_GetTypeName(byte type); 386 | void PICC_DumpToSerial(Uid *uid); 387 | void PICC_DumpMifareClassicToSerial(Uid *uid, byte piccType, MIFARE_Key *key); 388 | void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); 389 | void PICC_DumpMifareUltralightToSerial(); 390 | void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); 391 | bool MIFARE_OpenUidBackdoor(bool logErrors); 392 | bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors); 393 | bool MIFARE_UnbrickUidSector(bool logErrors); 394 | 395 | ///////////////////////////////////////////////////////////////////////////////////// 396 | // Convenience functions - does not add extra functionality 397 | ///////////////////////////////////////////////////////////////////////////////////// 398 | bool PICC_IsNewCardPresent(); 399 | bool PICC_ReadCardSerial(); 400 | 401 | private: 402 | byte _chipAddress; 403 | byte _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) 404 | byte MIFARE_TwoStepHelper(byte command, byte blockAddr, long data); 405 | }; 406 | 407 | #endif 408 | -------------------------------------------------------------------------------- /MFRC522_I2C.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C BY AROZCAN 3 | * MFRC522.cpp - Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. 4 | * NOTE: Please also check the comments in MFRC522.h - they provide useful hints and background information. 5 | * Released into the public domain. 6 | * Author: arozcan @ https://github.com/arozcan/MFRC522-I2C-Library 7 | */ 8 | 9 | #include 10 | #include "MFRC522_I2C.h" 11 | #include 12 | 13 | ///////////////////////////////////////////////////////////////////////////////////// 14 | // Functions for setting up the Arduino 15 | ///////////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * Constructor. 19 | * Prepares the output pins. 20 | */ 21 | MFRC522::MFRC522( byte chipAddress 22 | //byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) 23 | ) { 24 | _chipAddress = chipAddress; 25 | // _resetPowerDownPin = resetPowerDownPin; 26 | } // End constructor 27 | 28 | 29 | ///////////////////////////////////////////////////////////////////////////////////// 30 | // Basic interface functions for communicating with the MFRC522 31 | ///////////////////////////////////////////////////////////////////////////////////// 32 | 33 | /** 34 | * Writes a byte to the specified register in the MFRC522 chip. 35 | * The interface is described in the datasheet section 8.1.2. 36 | */ 37 | void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. 38 | byte value ///< The value to write. 39 | ) { 40 | Wire.beginTransmission(_chipAddress); 41 | Wire.write(reg); 42 | Wire.write(value); 43 | Wire.endTransmission(); 44 | } // End PCD_WriteRegister() 45 | 46 | /** 47 | * Writes a number of bytes to the specified register in the MFRC522 chip. 48 | * The interface is described in the datasheet section 8.1.2. 49 | */ 50 | void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. 51 | byte count, ///< The number of bytes to write to the register 52 | byte *values ///< The values to write. Byte array. 53 | ) { 54 | Wire.beginTransmission(_chipAddress); 55 | Wire.write(reg); 56 | for (byte index = 0; index < count; index++) { 57 | Wire.write(values[index]); 58 | } 59 | Wire.endTransmission(); 60 | } // End PCD_WriteRegister() 61 | 62 | /** 63 | * Reads a byte from the specified register in the MFRC522 chip. 64 | * The interface is described in the datasheet section 8.1.2. 65 | */ 66 | byte MFRC522::PCD_ReadRegister( byte reg ///< The register to read from. One of the PCD_Register enums. 67 | ) { 68 | byte value; 69 | //digitalWrite(_chipSelectPin, LOW); // Select slave 70 | Wire.beginTransmission(_chipAddress); 71 | Wire.write(reg); 72 | Wire.endTransmission(); 73 | 74 | Wire.requestFrom(_chipAddress, 1); 75 | value = Wire.read(); 76 | return value; 77 | } // End PCD_ReadRegister() 78 | 79 | /** 80 | * Reads a number of bytes from the specified register in the MFRC522 chip. 81 | * The interface is described in the datasheet section 8.1.2. 82 | */ 83 | void MFRC522::PCD_ReadRegister( byte reg, ///< The register to read from. One of the PCD_Register enums. 84 | byte count, ///< The number of bytes to read 85 | byte *values, ///< Byte array to store the values in. 86 | byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated. 87 | ) { 88 | if (count == 0) { 89 | return; 90 | } 91 | byte address = reg; 92 | byte index = 0; // Index in values array. 93 | Wire.beginTransmission(_chipAddress); 94 | Wire.write(address); 95 | Wire.endTransmission(); 96 | Wire.requestFrom(_chipAddress, count); 97 | while (Wire.available()) { 98 | if (index == 0 && rxAlign) { // Only update bit positions rxAlign..7 in values[0] 99 | // Create bit mask for bit positions rxAlign..7 100 | byte mask = 0; 101 | for (byte i = rxAlign; i <= 7; i++) { 102 | mask |= (1 << i); 103 | } 104 | // Read value and tell that we want to read the same address again. 105 | byte value = Wire.read(); 106 | // Apply mask to both current value of values[0] and the new data in value. 107 | values[0] = (values[index] & ~mask) | (value & mask); 108 | } 109 | else { // Normal case 110 | values[index] = Wire.read(); 111 | } 112 | index++; 113 | } 114 | } // End PCD_ReadRegister() 115 | 116 | /** 117 | * Sets the bits given in mask in register reg. 118 | */ 119 | void MFRC522::PCD_SetRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. 120 | byte mask ///< The bits to set. 121 | ) { 122 | byte tmp; 123 | tmp = PCD_ReadRegister(reg); 124 | PCD_WriteRegister(reg, tmp | mask); // set bit mask 125 | } // End PCD_SetRegisterBitMask() 126 | 127 | /** 128 | * Clears the bits given in mask from register reg. 129 | */ 130 | void MFRC522::PCD_ClearRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. 131 | byte mask ///< The bits to clear. 132 | ) { 133 | byte tmp; 134 | tmp = PCD_ReadRegister(reg); 135 | PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask 136 | } // End PCD_ClearRegisterBitMask() 137 | 138 | 139 | /** 140 | * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. 141 | * 142 | * @return STATUS_OK on success, STATUS_??? otherwise. 143 | */ 144 | byte MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. 145 | byte length, ///< In: The number of bytes to transfer. 146 | byte *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first. 147 | ) { 148 | PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. 149 | PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit 150 | PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization 151 | PCD_WriteRegister(FIFODataReg, length, data); // Write data to the FIFO 152 | PCD_WriteRegister(CommandReg, PCD_CalcCRC); // Start the calculation 153 | 154 | // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73�s. 155 | word i = 5000; 156 | byte n; 157 | while (1) { 158 | n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved 159 | if (n & 0x04) { // CRCIRq bit set - calculation done 160 | break; 161 | } 162 | if (--i == 0) { // The emergency break. We will eventually terminate on this one after 89ms. Communication with the MFRC522 might be down. 163 | return STATUS_TIMEOUT; 164 | } 165 | } 166 | PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. 167 | 168 | // Transfer the result from the registers to the result buffer 169 | result[0] = PCD_ReadRegister(CRCResultRegL); 170 | result[1] = PCD_ReadRegister(CRCResultRegH); 171 | return STATUS_OK; 172 | } // End PCD_CalculateCRC() 173 | 174 | 175 | ///////////////////////////////////////////////////////////////////////////////////// 176 | // Functions for manipulating the MFRC522 177 | ///////////////////////////////////////////////////////////////////////////////////// 178 | 179 | /** 180 | * Initializes the MFRC522 chip. 181 | */ 182 | void MFRC522::PCD_Init() { 183 | // Set the chipSelectPin as digital output, do not select the slave yet 184 | 185 | // Set the resetPowerDownPin as digital output, do not reset or power down. 186 | // pinMode(_resetPowerDownPin, OUTPUT); 187 | 188 | 189 | // if (digitalRead(_resetPowerDownPin) == LOW) { //The MFRC522 chip is in power down mode. 190 | // digitalWrite(_resetPowerDownPin, HIGH); // Exit power down mode. This triggers a hard reset. 191 | // // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. 192 | // delay(50); 193 | // } 194 | // else { // Perform a soft reset 195 | PCD_Reset(); 196 | // } 197 | 198 | // When communicating with a PICC we need a timeout if something goes wrong. 199 | // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. 200 | // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. 201 | PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds 202 | PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25�s. 203 | PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. 204 | PCD_WriteRegister(TReloadRegL, 0xE8); 205 | 206 | PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting 207 | PCD_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) 208 | PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) 209 | } // End PCD_Init() 210 | 211 | /** 212 | * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. 213 | */ 214 | void MFRC522::PCD_Reset() { 215 | PCD_WriteRegister(CommandReg, PCD_SoftReset); // Issue the SoftReset command. 216 | // The datasheet does not mention how long the SoftRest command takes to complete. 217 | // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) 218 | // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. 219 | delay(50); 220 | // Wait for the PowerDown bit in CommandReg to be cleared 221 | while (PCD_ReadRegister(CommandReg) & (1<<4)) { 222 | // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. 223 | } 224 | } // End PCD_Reset() 225 | 226 | /** 227 | * Turns the antenna on by enabling pins TX1 and TX2. 228 | * After a reset these pins are disabled. 229 | */ 230 | void MFRC522::PCD_AntennaOn() { 231 | byte value = PCD_ReadRegister(TxControlReg); 232 | if ((value & 0x03) != 0x03) { 233 | PCD_WriteRegister(TxControlReg, value | 0x03); 234 | } 235 | } // End PCD_AntennaOn() 236 | 237 | /** 238 | * Turns the antenna off by disabling pins TX1 and TX2. 239 | */ 240 | void MFRC522::PCD_AntennaOff() { 241 | PCD_ClearRegisterBitMask(TxControlReg, 0x03); 242 | } // End PCD_AntennaOff() 243 | 244 | /** 245 | * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. 246 | * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf 247 | * NOTE: Return value scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. 248 | * 249 | * @return Value of the RxGain, scrubbed to the 3 bits used. 250 | */ 251 | byte MFRC522::PCD_GetAntennaGain() { 252 | return PCD_ReadRegister(RFCfgReg) & (0x07<<4); 253 | } // End PCD_GetAntennaGain() 254 | 255 | /** 256 | * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. 257 | * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf 258 | * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. 259 | */ 260 | void MFRC522::PCD_SetAntennaGain(byte mask) { 261 | if (PCD_GetAntennaGain() != mask) { // only bother if there is a change 262 | PCD_ClearRegisterBitMask(RFCfgReg, (0x07<<4)); // clear needed to allow 000 pattern 263 | PCD_SetRegisterBitMask(RFCfgReg, mask & (0x07<<4)); // only set RxGain[2:0] bits 264 | } 265 | } // End PCD_SetAntennaGain() 266 | 267 | /** 268 | * Performs a self-test of the MFRC522 269 | * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf 270 | * 271 | * @return Whether or not the test passed. 272 | */ 273 | bool MFRC522::PCD_PerformSelfTest() { 274 | // This follows directly the steps outlined in 16.1.1 275 | // 1. Perform a soft reset. 276 | PCD_Reset(); 277 | 278 | // 2. Clear the internal buffer by writing 25 bytes of 00h 279 | byte ZEROES[25] = {0x00}; 280 | PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // flush the FIFO buffer 281 | PCD_WriteRegister(FIFODataReg, 25, ZEROES); // write 25 bytes of 00h to FIFO 282 | PCD_WriteRegister(CommandReg, PCD_Mem); // transfer to internal buffer 283 | 284 | // 3. Enable self-test 285 | PCD_WriteRegister(AutoTestReg, 0x09); 286 | 287 | // 4. Write 00h to FIFO buffer 288 | PCD_WriteRegister(FIFODataReg, 0x00); 289 | 290 | // 5. Start self-test by issuing the CalcCRC command 291 | PCD_WriteRegister(CommandReg, PCD_CalcCRC); 292 | 293 | // 6. Wait for self-test to complete 294 | word i; 295 | byte n; 296 | for (i = 0; i < 0xFF; i++) { 297 | n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved 298 | if (n & 0x04) { // CRCIRq bit set - calculation done 299 | break; 300 | } 301 | } 302 | PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. 303 | 304 | // 7. Read out resulting 64 bytes from the FIFO buffer. 305 | byte result[64]; 306 | PCD_ReadRegister(FIFODataReg, 64, result, 0); 307 | 308 | // Auto self-test done 309 | // Reset AutoTestReg register to be 0 again. Required for normal operation. 310 | PCD_WriteRegister(AutoTestReg, 0x00); 311 | 312 | // Determine firmware version (see section 9.3.4.8 in spec) 313 | byte version = PCD_ReadRegister(VersionReg); 314 | 315 | // Pick the appropriate reference values 316 | const byte *reference; 317 | switch (version) { 318 | case 0x88: // Fudan Semiconductor FM17522 clone 319 | reference = FM17522_firmware_reference; 320 | break; 321 | case 0x90: // Version 0.0 322 | reference = MFRC522_firmware_referenceV0_0; 323 | break; 324 | case 0x91: // Version 1.0 325 | reference = MFRC522_firmware_referenceV1_0; 326 | break; 327 | case 0x92: // Version 2.0 328 | reference = MFRC522_firmware_referenceV2_0; 329 | break; 330 | default: // Unknown version 331 | return false; 332 | } 333 | 334 | // Verify that the results match up to our expectations 335 | for (i = 0; i < 64; i++) { 336 | if (result[i] != pgm_read_byte(&(reference[i]))) { 337 | return false; 338 | } 339 | } 340 | 341 | // Test passed; all is good. 342 | return true; 343 | } // End PCD_PerformSelfTest() 344 | 345 | ///////////////////////////////////////////////////////////////////////////////////// 346 | // Functions for communicating with PICCs 347 | ///////////////////////////////////////////////////////////////////////////////////// 348 | 349 | /** 350 | * Executes the Transceive command. 351 | * CRC validation can only be done if backData and backLen are specified. 352 | * 353 | * @return STATUS_OK on success, STATUS_??? otherwise. 354 | */ 355 | byte MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO. 356 | byte sendLen, ///< Number of bytes to transfer to the FIFO. 357 | byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. 358 | byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. 359 | byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default NULL. 360 | byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. 361 | bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. 362 | ) { 363 | byte waitIRq = 0x30; // RxIRq and IdleIRq 364 | return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); 365 | } // End PCD_TransceiveData() 366 | 367 | /** 368 | * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. 369 | * CRC validation can only be done if backData and backLen are specified. 370 | * 371 | * @return STATUS_OK on success, STATUS_??? otherwise. 372 | */ 373 | byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execute. One of the PCD_Command enums. 374 | byte waitIRq, ///< The bits in the ComIrqReg register that signals successful completion of the command. 375 | byte *sendData, ///< Pointer to the data to transfer to the FIFO. 376 | byte sendLen, ///< Number of bytes to transfer to the FIFO. 377 | byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. 378 | byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. 379 | byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. 380 | byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. 381 | bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. 382 | ) { 383 | byte n, _validBits; 384 | unsigned int i; 385 | 386 | // Prepare values for BitFramingReg 387 | byte txLastBits = validBits ? *validBits : 0; 388 | byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] 389 | 390 | PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. 391 | PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits 392 | PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization 393 | PCD_WriteRegister(FIFODataReg, sendLen, sendData); // Write sendData to the FIFO 394 | PCD_WriteRegister(BitFramingReg, bitFraming); // Bit adjustments 395 | PCD_WriteRegister(CommandReg, command); // Execute the command 396 | if (command == PCD_Transceive) { 397 | PCD_SetRegisterBitMask(BitFramingReg, 0x80); // StartSend=1, transmission of data starts 398 | } 399 | 400 | // Wait for the command to complete. 401 | // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting. 402 | // Each iteration of the do-while-loop takes 17.86�s. 403 | i = 2000; 404 | while (1) { 405 | n = PCD_ReadRegister(ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq 406 | if (n & waitIRq) { // One of the interrupts that signal success has been set. 407 | break; 408 | } 409 | if (n & 0x01) { // Timer interrupt - nothing received in 25ms 410 | return STATUS_TIMEOUT; 411 | } 412 | if (--i == 0) { // The emergency break. If all other condions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down. 413 | return STATUS_TIMEOUT; 414 | } 415 | } 416 | 417 | // Stop now if any errors except collisions were detected. 418 | byte errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr 419 | if (errorRegValue & 0x13) { // BufferOvfl ParityErr ProtocolErr 420 | return STATUS_ERROR; 421 | } 422 | 423 | // If the caller wants data back, get it from the MFRC522. 424 | if (backData && backLen) { 425 | n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO 426 | if (n > *backLen) { 427 | return STATUS_NO_ROOM; 428 | } 429 | *backLen = n; // Number of bytes returned 430 | PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO 431 | _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. 432 | if (validBits) { 433 | *validBits = _validBits; 434 | } 435 | } 436 | 437 | // Tell about collisions 438 | if (errorRegValue & 0x08) { // CollErr 439 | return STATUS_COLLISION; 440 | } 441 | 442 | // Perform CRC_A validation if requested. 443 | if (backData && backLen && checkCRC) { 444 | // In this case a MIFARE Classic NAK is not OK. 445 | if (*backLen == 1 && _validBits == 4) { 446 | return STATUS_MIFARE_NACK; 447 | } 448 | // We need at least the CRC_A value and all 8 bits of the last byte must be received. 449 | if (*backLen < 2 || _validBits != 0) { 450 | return STATUS_CRC_WRONG; 451 | } 452 | // Verify CRC_A - do our own calculation and store the control in controlBuffer. 453 | byte controlBuffer[2]; 454 | n = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); 455 | if (n != STATUS_OK) { 456 | return n; 457 | } 458 | if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { 459 | return STATUS_CRC_WRONG; 460 | } 461 | } 462 | 463 | return STATUS_OK; 464 | } // End PCD_CommunicateWithPICC() 465 | 466 | /** 467 | * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. 468 | * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. 469 | * 470 | * @return STATUS_OK on success, STATUS_??? otherwise. 471 | */ 472 | byte MFRC522::PICC_RequestA(byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in 473 | byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. 474 | ) { 475 | return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); 476 | } // End PICC_RequestA() 477 | 478 | /** 479 | * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. 480 | * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. 481 | * 482 | * @return STATUS_OK on success, STATUS_??? otherwise. 483 | */ 484 | byte MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in 485 | byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. 486 | ) { 487 | return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); 488 | } // End PICC_WakeupA() 489 | 490 | /** 491 | * Transmits REQA or WUPA commands. 492 | * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. 493 | * 494 | * @return STATUS_OK on success, STATUS_??? otherwise. 495 | */ 496 | byte MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA 497 | byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in 498 | byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. 499 | ) { 500 | byte validBits; 501 | byte status; 502 | 503 | if (bufferATQA == NULL || *bufferSize < 2) { // The ATQA response is 2 bytes long. 504 | return STATUS_NO_ROOM; 505 | } 506 | PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. 507 | validBits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] 508 | status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); 509 | if (status != STATUS_OK) { 510 | return status; 511 | } 512 | if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits. 513 | return STATUS_ERROR; 514 | } 515 | return STATUS_OK; 516 | } // End PICC_REQA_or_WUPA() 517 | 518 | /** 519 | * Transmits SELECT/ANTICOLLISION commands to select a single PICC. 520 | * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). 521 | * On success: 522 | * - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.) 523 | * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. 524 | * 525 | * A PICC UID consists of 4, 7 or 10 bytes. 526 | * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: 527 | * UID size Number of UID bytes Cascade levels Example of PICC 528 | * ======== =================== ============== =============== 529 | * single 4 1 MIFARE Classic 530 | * double 7 2 MIFARE Ultralight 531 | * triple 10 3 Not currently in use? 532 | * 533 | * @return STATUS_OK on success, STATUS_??? otherwise. 534 | */ 535 | byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. 536 | byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. 537 | ) { 538 | bool uidComplete; 539 | bool selectDone; 540 | bool useCascadeTag; 541 | byte cascadeLevel = 1; 542 | byte result; 543 | byte count; 544 | byte index; 545 | byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. 546 | int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. 547 | byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A 548 | byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. 549 | byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. 550 | byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. 551 | byte *responseBuffer; 552 | byte responseLength; 553 | 554 | // Description of buffer structure: 555 | // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 556 | // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. 557 | // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. 558 | // Byte 3: UID-data 559 | // Byte 4: UID-data 560 | // Byte 5: UID-data 561 | // Byte 6: BCC Block Check Character - XOR of bytes 2-5 562 | // Byte 7: CRC_A 563 | // Byte 8: CRC_A 564 | // The BCC and CRC_A is only transmitted if we know all the UID bits of the current Cascade Level. 565 | // 566 | // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) 567 | // UID size Cascade level Byte2 Byte3 Byte4 Byte5 568 | // ======== ============= ===== ===== ===== ===== 569 | // 4 bytes 1 uid0 uid1 uid2 uid3 570 | // 7 bytes 1 CT uid0 uid1 uid2 571 | // 2 uid3 uid4 uid5 uid6 572 | // 10 bytes 1 CT uid0 uid1 uid2 573 | // 2 CT uid3 uid4 uid5 574 | // 3 uid6 uid7 uid8 uid9 575 | 576 | // Sanity checks 577 | if (validBits > 80) { 578 | return STATUS_INVALID; 579 | } 580 | 581 | // Prepare MFRC522 582 | PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. 583 | 584 | // Repeat Cascade Level loop until we have a complete UID. 585 | uidComplete = false; 586 | while (!uidComplete) { 587 | // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. 588 | switch (cascadeLevel) { 589 | case 1: 590 | buffer[0] = PICC_CMD_SEL_CL1; 591 | uidIndex = 0; 592 | useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes 593 | break; 594 | 595 | case 2: 596 | buffer[0] = PICC_CMD_SEL_CL2; 597 | uidIndex = 3; 598 | useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes 599 | break; 600 | 601 | case 3: 602 | buffer[0] = PICC_CMD_SEL_CL3; 603 | uidIndex = 6; 604 | useCascadeTag = false; // Never used in CL3. 605 | break; 606 | 607 | default: 608 | return STATUS_INTERNAL_ERROR; 609 | break; 610 | } 611 | 612 | // How many UID bits are known in this Cascade Level? 613 | currentLevelKnownBits = validBits - (8 * uidIndex); 614 | if (currentLevelKnownBits < 0) { 615 | currentLevelKnownBits = 0; 616 | } 617 | // Copy the known bits from uid->uidByte[] to buffer[] 618 | index = 2; // destination index in buffer[] 619 | if (useCascadeTag) { 620 | buffer[index++] = PICC_CMD_CT; 621 | } 622 | byte bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level. 623 | if (bytesToCopy) { 624 | byte maxBytes = useCascadeTag ? 3 : 4; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag 625 | if (bytesToCopy > maxBytes) { 626 | bytesToCopy = maxBytes; 627 | } 628 | for (count = 0; count < bytesToCopy; count++) { 629 | buffer[index++] = uid->uidByte[uidIndex + count]; 630 | } 631 | } 632 | // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits 633 | if (useCascadeTag) { 634 | currentLevelKnownBits += 8; 635 | } 636 | 637 | // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. 638 | selectDone = false; 639 | while (!selectDone) { 640 | // Find out how many bits and bytes to send and receive. 641 | if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. 642 | //Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); 643 | buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes 644 | // Calculate BCC - Block Check Character 645 | buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; 646 | // Calculate CRC_A 647 | result = PCD_CalculateCRC(buffer, 7, &buffer[7]); 648 | if (result != STATUS_OK) { 649 | return result; 650 | } 651 | txLastBits = 0; // 0 => All 8 bits are valid. 652 | bufferUsed = 9; 653 | // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) 654 | responseBuffer = &buffer[6]; 655 | responseLength = 3; 656 | } 657 | else { // This is an ANTICOLLISION. 658 | //Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); 659 | txLastBits = currentLevelKnownBits % 8; 660 | count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. 661 | index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs 662 | buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits 663 | bufferUsed = index + (txLastBits ? 1 : 0); 664 | // Store response in the unused part of buffer 665 | responseBuffer = &buffer[index]; 666 | responseLength = sizeof(buffer) - index; 667 | } 668 | 669 | // Set bit adjustments 670 | rxAlign = txLastBits; // Having a seperate variable is overkill. But it makes the next line easier to read. 671 | PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] 672 | 673 | // Transmit the buffer and receive the response. 674 | result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); 675 | if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. 676 | result = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] 677 | if (result & 0x20) { // CollPosNotValid 678 | return STATUS_COLLISION; // Without a valid collision position we cannot continue 679 | } 680 | byte collisionPos = result & 0x1F; // Values 0-31, 0 means bit 32. 681 | if (collisionPos == 0) { 682 | collisionPos = 32; 683 | } 684 | if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen 685 | return STATUS_INTERNAL_ERROR; 686 | } 687 | // Choose the PICC with the bit set. 688 | currentLevelKnownBits = collisionPos; 689 | count = (currentLevelKnownBits - 1) % 8; // The bit to modify 690 | index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. 691 | buffer[index] |= (1 << count); 692 | } 693 | else if (result != STATUS_OK) { 694 | return result; 695 | } 696 | else { // STATUS_OK 697 | if (currentLevelKnownBits >= 32) { // This was a SELECT. 698 | selectDone = true; // No more anticollision 699 | // We continue below outside the while. 700 | } 701 | else { // This was an ANTICOLLISION. 702 | // We now have all 32 bits of the UID in this Cascade Level 703 | currentLevelKnownBits = 32; 704 | // Run loop again to do the SELECT. 705 | } 706 | } 707 | } // End of while (!selectDone) 708 | 709 | // We do not check the CBB - it was constructed by us above. 710 | 711 | // Copy the found UID bytes from buffer[] to uid->uidByte[] 712 | index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] 713 | bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; 714 | for (count = 0; count < bytesToCopy; count++) { 715 | uid->uidByte[uidIndex + count] = buffer[index++]; 716 | } 717 | 718 | // Check response SAK (Select Acknowledge) 719 | if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). 720 | return STATUS_ERROR; 721 | } 722 | // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. 723 | result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); 724 | if (result != STATUS_OK) { 725 | return result; 726 | } 727 | if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { 728 | return STATUS_CRC_WRONG; 729 | } 730 | if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes 731 | cascadeLevel++; 732 | } 733 | else { 734 | uidComplete = true; 735 | uid->sak = responseBuffer[0]; 736 | } 737 | } // End of while (!uidComplete) 738 | 739 | // Set correct uid->size 740 | uid->size = 3 * cascadeLevel + 1; 741 | 742 | return STATUS_OK; 743 | } // End PICC_Select() 744 | 745 | /** 746 | * Instructs a PICC in state ACTIVE(*) to go to state HALT. 747 | * 748 | * @return STATUS_OK on success, STATUS_??? otherwise. 749 | */ 750 | byte MFRC522::PICC_HaltA() { 751 | byte result; 752 | byte buffer[4]; 753 | 754 | // Build command buffer 755 | buffer[0] = PICC_CMD_HLTA; 756 | buffer[1] = 0; 757 | // Calculate CRC_A 758 | result = PCD_CalculateCRC(buffer, 2, &buffer[2]); 759 | if (result != STATUS_OK) { 760 | return result; 761 | } 762 | 763 | // Send the command. 764 | // The standard says: 765 | // If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the 766 | // HLTA command, this response shall be interpreted as 'not acknowledge'. 767 | // We interpret that this way: Only STATUS_TIMEOUT is an success. 768 | result = PCD_TransceiveData(buffer, sizeof(buffer), NULL, 0); 769 | if (result == STATUS_TIMEOUT) { 770 | return STATUS_OK; 771 | } 772 | if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-) 773 | return STATUS_ERROR; 774 | } 775 | return result; 776 | } // End PICC_HaltA() 777 | 778 | 779 | ///////////////////////////////////////////////////////////////////////////////////// 780 | // Functions for communicating with MIFARE PICCs 781 | ///////////////////////////////////////////////////////////////////////////////////// 782 | 783 | /** 784 | * Executes the MFRC522 MFAuthent command. 785 | * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. 786 | * The authentication is described in the MFRC522 datasheet section 10.3.1.9 and http://www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1. 787 | * For use with MIFARE Classic PICCs. 788 | * The PICC must be selected - ie in state ACTIVE(*) - before calling this function. 789 | * Remember to call PCD_StopCrypto1() after communicating with the authenticated PICC - otherwise no new communications can start. 790 | * 791 | * All keys are set to FFFFFFFFFFFFh at chip delivery. 792 | * 793 | * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. 794 | */ 795 | byte MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B 796 | byte blockAddr, ///< The block number. See numbering in the comments in the .h file. 797 | MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 bytes) 798 | Uid *uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used. 799 | ) { 800 | byte waitIRq = 0x10; // IdleIRq 801 | 802 | // Build command buffer 803 | byte sendData[12]; 804 | sendData[0] = command; 805 | sendData[1] = blockAddr; 806 | for (byte i = 0; i < MF_KEY_SIZE; i++) { // 6 key bytes 807 | sendData[2+i] = key->keyByte[i]; 808 | } 809 | for (byte i = 0; i < 4; i++) { // The first 4 bytes of the UID 810 | sendData[8+i] = uid->uidByte[i]; 811 | } 812 | 813 | // Start the authentication. 814 | return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); 815 | } // End PCD_Authenticate() 816 | 817 | /** 818 | * Used to exit the PCD from its authenticated state. 819 | * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. 820 | */ 821 | void MFRC522::PCD_StopCrypto1() { 822 | // Clear MFCrypto1On bit 823 | PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] 824 | } // End PCD_StopCrypto1() 825 | 826 | /** 827 | * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. 828 | * 829 | * For MIFARE Classic the sector containing the block must be authenticated before calling this function. 830 | * 831 | * For MIFARE Ultralight only addresses 00h to 0Fh are decoded. 832 | * The MF0ICU1 returns a NAK for higher addresses. 833 | * The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument. 834 | * For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned. 835 | * A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages 0Eh, 0Fh, 00h and 01h are returned. 836 | * 837 | * The buffer must be at least 18 bytes because a CRC_A is also returned. 838 | * Checks the CRC_A before returning STATUS_OK. 839 | * 840 | * @return STATUS_OK on success, STATUS_??? otherwise. 841 | */ 842 | byte MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. 843 | byte *buffer, ///< The buffer to store the data in 844 | byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. 845 | ) { 846 | byte result; 847 | 848 | // Sanity check 849 | if (buffer == NULL || *bufferSize < 18) { 850 | return STATUS_NO_ROOM; 851 | } 852 | 853 | // Build command buffer 854 | buffer[0] = PICC_CMD_MF_READ; 855 | buffer[1] = blockAddr; 856 | // Calculate CRC_A 857 | result = PCD_CalculateCRC(buffer, 2, &buffer[2]); 858 | if (result != STATUS_OK) { 859 | return result; 860 | } 861 | 862 | // Transmit the buffer and receive the response, validate CRC_A. 863 | return PCD_TransceiveData(buffer, 4, buffer, bufferSize, NULL, 0, true); 864 | } // End MIFARE_Read() 865 | 866 | /** 867 | * Writes 16 bytes to the active PICC. 868 | * 869 | * For MIFARE Classic the sector containing the block must be authenticated before calling this function. 870 | * 871 | * For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE". 872 | * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) 873 | * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. 874 | * * 875 | * @return STATUS_OK on success, STATUS_??? otherwise. 876 | */ 877 | byte MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. 878 | byte *buffer, ///< The 16 bytes to write to the PICC 879 | byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. 880 | ) { 881 | byte result; 882 | 883 | // Sanity check 884 | if (buffer == NULL || bufferSize < 16) { 885 | return STATUS_INVALID; 886 | } 887 | 888 | // Mifare Classic protocol requires two communications to perform a write. 889 | // Step 1: Tell the PICC we want to write to block blockAddr. 890 | byte cmdBuffer[2]; 891 | cmdBuffer[0] = PICC_CMD_MF_WRITE; 892 | cmdBuffer[1] = blockAddr; 893 | result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. 894 | if (result != STATUS_OK) { 895 | return result; 896 | } 897 | 898 | // Step 2: Transfer the data 899 | result = PCD_MIFARE_Transceive(buffer, bufferSize); // Adds CRC_A and checks that the response is MF_ACK. 900 | if (result != STATUS_OK) { 901 | return result; 902 | } 903 | 904 | return STATUS_OK; 905 | } // End MIFARE_Write() 906 | 907 | /** 908 | * Writes a 4 byte page to the active MIFARE Ultralight PICC. 909 | * 910 | * @return STATUS_OK on success, STATUS_??? otherwise. 911 | */ 912 | byte MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to write to. 913 | byte *buffer, ///< The 4 bytes to write to the PICC 914 | byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. 915 | ) { 916 | byte result; 917 | 918 | // Sanity check 919 | if (buffer == NULL || bufferSize < 4) { 920 | return STATUS_INVALID; 921 | } 922 | 923 | // Build commmand buffer 924 | byte cmdBuffer[6]; 925 | cmdBuffer[0] = PICC_CMD_UL_WRITE; 926 | cmdBuffer[1] = page; 927 | memcpy(&cmdBuffer[2], buffer, 4); 928 | 929 | // Perform the write 930 | result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. 931 | if (result != STATUS_OK) { 932 | return result; 933 | } 934 | return STATUS_OK; 935 | } // End MIFARE_Ultralight_Write() 936 | 937 | /** 938 | * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. 939 | * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. 940 | * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. 941 | * Use MIFARE_Transfer() to store the result in a block. 942 | * 943 | * @return STATUS_OK on success, STATUS_??? otherwise. 944 | */ 945 | byte MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. 946 | long delta ///< This number is subtracted from the value of block blockAddr. 947 | ) { 948 | return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); 949 | } // End MIFARE_Decrement() 950 | 951 | /** 952 | * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. 953 | * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. 954 | * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. 955 | * Use MIFARE_Transfer() to store the result in a block. 956 | * 957 | * @return STATUS_OK on success, STATUS_??? otherwise. 958 | */ 959 | byte MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. 960 | long delta ///< This number is added to the value of block blockAddr. 961 | ) { 962 | return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); 963 | } // End MIFARE_Increment() 964 | 965 | /** 966 | * MIFARE Restore copies the value of the addressed block into a volatile memory. 967 | * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. 968 | * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. 969 | * Use MIFARE_Transfer() to store the result in a block. 970 | * 971 | * @return STATUS_OK on success, STATUS_??? otherwise. 972 | */ 973 | byte MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. 974 | ) { 975 | // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. 976 | // Doing only a single step does not work, so I chose to transfer 0L in step two. 977 | return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); 978 | } // End MIFARE_Restore() 979 | 980 | /** 981 | * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. 982 | * 983 | * @return STATUS_OK on success, STATUS_??? otherwise. 984 | */ 985 | byte MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use 986 | byte blockAddr, ///< The block (0-0xff) number. 987 | long data ///< The data to transfer in step 2 988 | ) { 989 | byte result; 990 | byte cmdBuffer[2]; // We only need room for 2 bytes. 991 | 992 | // Step 1: Tell the PICC the command and block address 993 | cmdBuffer[0] = command; 994 | cmdBuffer[1] = blockAddr; 995 | result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. 996 | if (result != STATUS_OK) { 997 | return result; 998 | } 999 | 1000 | // Step 2: Transfer the data 1001 | result = PCD_MIFARE_Transceive( (byte *)&data, 4, true); // Adds CRC_A and accept timeout as success. 1002 | if (result != STATUS_OK) { 1003 | return result; 1004 | } 1005 | 1006 | return STATUS_OK; 1007 | } // End MIFARE_TwoStepHelper() 1008 | 1009 | /** 1010 | * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. 1011 | * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. 1012 | * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. 1013 | * 1014 | * @return STATUS_OK on success, STATUS_??? otherwise. 1015 | */ 1016 | byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. 1017 | ) { 1018 | byte result; 1019 | byte cmdBuffer[2]; // We only need room for 2 bytes. 1020 | 1021 | // Tell the PICC we want to transfer the result into block blockAddr. 1022 | cmdBuffer[0] = PICC_CMD_MF_TRANSFER; 1023 | cmdBuffer[1] = blockAddr; 1024 | result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. 1025 | if (result != STATUS_OK) { 1026 | return result; 1027 | } 1028 | return STATUS_OK; 1029 | } // End MIFARE_Transfer() 1030 | 1031 | /** 1032 | * Helper routine to read the current value from a Value Block. 1033 | * 1034 | * Only for MIFARE Classic and only for blocks in "value block" mode, that 1035 | * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing 1036 | * the block must be authenticated before calling this function. 1037 | * 1038 | * @param[in] blockAddr The block (0x00-0xff) number. 1039 | * @param[out] value Current value of the Value Block. 1040 | * @return STATUS_OK on success, STATUS_??? otherwise. 1041 | */ 1042 | byte MFRC522::MIFARE_GetValue(byte blockAddr, long *value) { 1043 | byte status; 1044 | byte buffer[18]; 1045 | byte size = sizeof(buffer); 1046 | 1047 | // Read the block 1048 | status = MIFARE_Read(blockAddr, buffer, &size); 1049 | if (status == STATUS_OK) { 1050 | // Extract the value 1051 | *value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); 1052 | } 1053 | return status; 1054 | } // End MIFARE_GetValue() 1055 | 1056 | /** 1057 | * Helper routine to write a specific value into a Value Block. 1058 | * 1059 | * Only for MIFARE Classic and only for blocks in "value block" mode, that 1060 | * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing 1061 | * the block must be authenticated before calling this function. 1062 | * 1063 | * @param[in] blockAddr The block (0x00-0xff) number. 1064 | * @param[in] value New value of the Value Block. 1065 | * @return STATUS_OK on success, STATUS_??? otherwise. 1066 | */ 1067 | byte MFRC522::MIFARE_SetValue(byte blockAddr, long value) { 1068 | byte buffer[18]; 1069 | 1070 | // Translate the long into 4 bytes; repeated 2x in value block 1071 | buffer[0] = buffer[ 8] = (value & 0xFF); 1072 | buffer[1] = buffer[ 9] = (value & 0xFF00) >> 8; 1073 | buffer[2] = buffer[10] = (value & 0xFF0000) >> 16; 1074 | buffer[3] = buffer[11] = (value & 0xFF000000) >> 24; 1075 | // Inverse 4 bytes also found in value block 1076 | buffer[4] = ~buffer[0]; 1077 | buffer[5] = ~buffer[1]; 1078 | buffer[6] = ~buffer[2]; 1079 | buffer[7] = ~buffer[3]; 1080 | // Address 2x with inverse address 2x 1081 | buffer[12] = buffer[14] = blockAddr; 1082 | buffer[13] = buffer[15] = ~blockAddr; 1083 | 1084 | // Write the whole data block 1085 | return MIFARE_Write(blockAddr, buffer, 16); 1086 | } // End MIFARE_SetValue() 1087 | 1088 | ///////////////////////////////////////////////////////////////////////////////////// 1089 | // Support functions 1090 | ///////////////////////////////////////////////////////////////////////////////////// 1091 | 1092 | /** 1093 | * Wrapper for MIFARE protocol communication. 1094 | * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. 1095 | * 1096 | * @return STATUS_OK on success, STATUS_??? otherwise. 1097 | */ 1098 | byte MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. 1099 | byte sendLen, ///< Number of bytes in sendData. 1100 | bool acceptTimeout ///< True => A timeout is also success 1101 | ) { 1102 | byte result; 1103 | byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. 1104 | 1105 | // Sanity check 1106 | if (sendData == NULL || sendLen > 16) { 1107 | return STATUS_INVALID; 1108 | } 1109 | 1110 | // Copy sendData[] to cmdBuffer[] and add CRC_A 1111 | memcpy(cmdBuffer, sendData, sendLen); 1112 | result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); 1113 | if (result != STATUS_OK) { 1114 | return result; 1115 | } 1116 | sendLen += 2; 1117 | 1118 | // Transceive the data, store the reply in cmdBuffer[] 1119 | byte waitIRq = 0x30; // RxIRq and IdleIRq 1120 | byte cmdBufferSize = sizeof(cmdBuffer); 1121 | byte validBits = 0; 1122 | result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); 1123 | if (acceptTimeout && result == STATUS_TIMEOUT) { 1124 | return STATUS_OK; 1125 | } 1126 | if (result != STATUS_OK) { 1127 | return result; 1128 | } 1129 | // The PICC must reply with a 4 bit ACK 1130 | if (cmdBufferSize != 1 || validBits != 4) { 1131 | return STATUS_ERROR; 1132 | } 1133 | if (cmdBuffer[0] != MF_ACK) { 1134 | return STATUS_MIFARE_NACK; 1135 | } 1136 | return STATUS_OK; 1137 | } // End PCD_MIFARE_Transceive() 1138 | 1139 | /** 1140 | * Returns a __FlashStringHelper pointer to a status code name. 1141 | * 1142 | * @return const __FlashStringHelper * 1143 | */ 1144 | const __FlashStringHelper *MFRC522::GetStatusCodeName(byte code ///< One of the StatusCode enums. 1145 | ) { 1146 | switch (code) { 1147 | case STATUS_OK: return F("Success."); break; 1148 | case STATUS_ERROR: return F("Error in communication."); break; 1149 | case STATUS_COLLISION: return F("Collission detected."); break; 1150 | case STATUS_TIMEOUT: return F("Timeout in communication."); break; 1151 | case STATUS_NO_ROOM: return F("A buffer is not big enough."); break; 1152 | case STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen."); break; 1153 | case STATUS_INVALID: return F("Invalid argument."); break; 1154 | case STATUS_CRC_WRONG: return F("The CRC_A does not match."); break; 1155 | case STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK."); break; 1156 | default: return F("Unknown error"); break; 1157 | } 1158 | } // End GetStatusCodeName() 1159 | 1160 | /** 1161 | * Translates the SAK (Select Acknowledge) to a PICC type. 1162 | * 1163 | * @return PICC_Type 1164 | */ 1165 | byte MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). 1166 | ) { 1167 | if (sak & 0x04) { // UID not complete 1168 | return PICC_TYPE_NOT_COMPLETE; 1169 | } 1170 | 1171 | switch (sak) { 1172 | case 0x09: return PICC_TYPE_MIFARE_MINI; break; 1173 | case 0x08: return PICC_TYPE_MIFARE_1K; break; 1174 | case 0x18: return PICC_TYPE_MIFARE_4K; break; 1175 | case 0x00: return PICC_TYPE_MIFARE_UL; break; 1176 | case 0x10: 1177 | case 0x11: return PICC_TYPE_MIFARE_PLUS; break; 1178 | case 0x01: return PICC_TYPE_TNP3XXX; break; 1179 | default: break; 1180 | } 1181 | 1182 | if (sak & 0x20) { 1183 | return PICC_TYPE_ISO_14443_4; 1184 | } 1185 | 1186 | if (sak & 0x40) { 1187 | return PICC_TYPE_ISO_18092; 1188 | } 1189 | 1190 | return PICC_TYPE_UNKNOWN; 1191 | } // End PICC_GetType() 1192 | 1193 | /** 1194 | * Returns a __FlashStringHelper pointer to the PICC type name. 1195 | * 1196 | * @return const __FlashStringHelper * 1197 | */ 1198 | const __FlashStringHelper *MFRC522::PICC_GetTypeName(byte piccType ///< One of the PICC_Type enums. 1199 | ) { 1200 | switch (piccType) { 1201 | case PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4"); break; 1202 | case PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)");break; 1203 | case PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes"); break; 1204 | case PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB"); break; 1205 | case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB"); break; 1206 | case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C"); break; 1207 | case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus"); break; 1208 | case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX"); break; 1209 | case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete."); break; 1210 | case PICC_TYPE_UNKNOWN: 1211 | default: return F("Unknown type"); break; 1212 | } 1213 | } // End PICC_GetTypeName() 1214 | 1215 | /** 1216 | * Dumps debug info about the selected PICC to Serial. 1217 | * On success the PICC is halted after dumping the data. 1218 | * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. 1219 | */ 1220 | void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). 1221 | ) { 1222 | MIFARE_Key key; 1223 | 1224 | // UID 1225 | Serial.print(F("Card UID:")); 1226 | for (byte i = 0; i < uid->size; i++) { 1227 | if(uid->uidByte[i] < 0x10) 1228 | Serial.print(F(" 0")); 1229 | else 1230 | Serial.print(F(" ")); 1231 | Serial.print(uid->uidByte[i], HEX); 1232 | } 1233 | Serial.println(); 1234 | 1235 | // PICC type 1236 | byte piccType = PICC_GetType(uid->sak); 1237 | Serial.print(F("PICC type: ")); 1238 | Serial.println(PICC_GetTypeName(piccType)); 1239 | 1240 | // Dump contents 1241 | switch (piccType) { 1242 | case PICC_TYPE_MIFARE_MINI: 1243 | case PICC_TYPE_MIFARE_1K: 1244 | case PICC_TYPE_MIFARE_4K: 1245 | // All keys are set to FFFFFFFFFFFFh at chip delivery from the factory. 1246 | for (byte i = 0; i < 6; i++) { 1247 | key.keyByte[i] = 0xFF; 1248 | } 1249 | PICC_DumpMifareClassicToSerial(uid, piccType, &key); 1250 | break; 1251 | 1252 | case PICC_TYPE_MIFARE_UL: 1253 | PICC_DumpMifareUltralightToSerial(); 1254 | break; 1255 | 1256 | case PICC_TYPE_ISO_14443_4: 1257 | case PICC_TYPE_ISO_18092: 1258 | case PICC_TYPE_MIFARE_PLUS: 1259 | case PICC_TYPE_TNP3XXX: 1260 | Serial.println(F("Dumping memory contents not implemented for that PICC type.")); 1261 | break; 1262 | 1263 | case PICC_TYPE_UNKNOWN: 1264 | case PICC_TYPE_NOT_COMPLETE: 1265 | default: 1266 | break; // No memory dump here 1267 | } 1268 | 1269 | Serial.println(); 1270 | PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. 1271 | } // End PICC_DumpToSerial() 1272 | 1273 | /** 1274 | * Dumps memory contents of a MIFARE Classic PICC. 1275 | * On success the PICC is halted after dumping the data. 1276 | */ 1277 | void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). 1278 | byte piccType, ///< One of the PICC_Type enums. 1279 | MIFARE_Key *key ///< Key A used for all sectors. 1280 | ) { 1281 | byte no_of_sectors = 0; 1282 | switch (piccType) { 1283 | case PICC_TYPE_MIFARE_MINI: 1284 | // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. 1285 | no_of_sectors = 5; 1286 | break; 1287 | 1288 | case PICC_TYPE_MIFARE_1K: 1289 | // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. 1290 | no_of_sectors = 16; 1291 | break; 1292 | 1293 | case PICC_TYPE_MIFARE_4K: 1294 | // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. 1295 | no_of_sectors = 40; 1296 | break; 1297 | 1298 | default: // Should not happen. Ignore. 1299 | break; 1300 | } 1301 | 1302 | // Dump sectors, highest address first. 1303 | if (no_of_sectors) { 1304 | Serial.println(F("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits")); 1305 | for (int8_t i = no_of_sectors - 1; i >= 0; i--) { 1306 | PICC_DumpMifareClassicSectorToSerial(uid, key, i); 1307 | } 1308 | } 1309 | PICC_HaltA(); // Halt the PICC before stopping the encrypted session. 1310 | PCD_StopCrypto1(); 1311 | } // End PICC_DumpMifareClassicToSerial() 1312 | 1313 | /** 1314 | * Dumps memory contents of a sector of a MIFARE Classic PICC. 1315 | * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. 1316 | * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. 1317 | */ 1318 | void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). 1319 | MIFARE_Key *key, ///< Key A for the sector. 1320 | byte sector ///< The sector to dump, 0..39. 1321 | ) { 1322 | byte status; 1323 | byte firstBlock; // Address of lowest address to dump actually last block dumped) 1324 | byte no_of_blocks; // Number of blocks in sector 1325 | bool isSectorTrailer; // Set to true while handling the "last" (ie highest address) in the sector. 1326 | 1327 | // The access bits are stored in a peculiar fashion. 1328 | // There are four groups: 1329 | // g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) 1330 | // g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) 1331 | // g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) 1332 | // g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) 1333 | // Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB. 1334 | // The four CX bits are stored together in a nible cx and an inverted nible cx_. 1335 | byte c1, c2, c3; // Nibbles 1336 | byte c1_, c2_, c3_; // Inverted nibbles 1337 | bool invertedError; // True if one of the inverted nibbles did not match 1338 | byte g[4]; // Access bits for each of the four groups. 1339 | byte group; // 0-3 - active group for access bits 1340 | bool firstInGroup; // True for the first block dumped in the group 1341 | 1342 | // Determine position and size of sector. 1343 | if (sector < 32) { // Sectors 0..31 has 4 blocks each 1344 | no_of_blocks = 4; 1345 | firstBlock = sector * no_of_blocks; 1346 | } 1347 | else if (sector < 40) { // Sectors 32-39 has 16 blocks each 1348 | no_of_blocks = 16; 1349 | firstBlock = 128 + (sector - 32) * no_of_blocks; 1350 | } 1351 | else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. 1352 | return; 1353 | } 1354 | 1355 | // Dump blocks, highest address first. 1356 | byte byteCount; 1357 | byte buffer[18]; 1358 | byte blockAddr; 1359 | isSectorTrailer = true; 1360 | for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { 1361 | blockAddr = firstBlock + blockOffset; 1362 | // Sector number - only on first line 1363 | if (isSectorTrailer) { 1364 | if(sector < 10) 1365 | Serial.print(F(" ")); // Pad with spaces 1366 | else 1367 | Serial.print(F(" ")); // Pad with spaces 1368 | Serial.print(sector); 1369 | Serial.print(F(" ")); 1370 | } 1371 | else { 1372 | Serial.print(F(" ")); 1373 | } 1374 | // Block number 1375 | if(blockAddr < 10) 1376 | Serial.print(F(" ")); // Pad with spaces 1377 | else { 1378 | if(blockAddr < 100) 1379 | Serial.print(F(" ")); // Pad with spaces 1380 | else 1381 | Serial.print(F(" ")); // Pad with spaces 1382 | } 1383 | Serial.print(blockAddr); 1384 | Serial.print(F(" ")); 1385 | // Establish encrypted communications before reading the first block 1386 | if (isSectorTrailer) { 1387 | status = PCD_Authenticate(PICC_CMD_MF_AUTH_KEY_A, firstBlock, key, uid); 1388 | if (status != STATUS_OK) { 1389 | Serial.print(F("PCD_Authenticate() failed: ")); 1390 | Serial.println(GetStatusCodeName(status)); 1391 | return; 1392 | } 1393 | } 1394 | // Read block 1395 | byteCount = sizeof(buffer); 1396 | status = MIFARE_Read(blockAddr, buffer, &byteCount); 1397 | if (status != STATUS_OK) { 1398 | Serial.print(F("MIFARE_Read() failed: ")); 1399 | Serial.println(GetStatusCodeName(status)); 1400 | continue; 1401 | } 1402 | // Dump data 1403 | for (byte index = 0; index < 16; index++) { 1404 | if(buffer[index] < 0x10) 1405 | Serial.print(F(" 0")); 1406 | else 1407 | Serial.print(F(" ")); 1408 | Serial.print(buffer[index], HEX); 1409 | if ((index % 4) == 3) { 1410 | Serial.print(F(" ")); 1411 | } 1412 | } 1413 | // Parse sector trailer data 1414 | if (isSectorTrailer) { 1415 | c1 = buffer[7] >> 4; 1416 | c2 = buffer[8] & 0xF; 1417 | c3 = buffer[8] >> 4; 1418 | c1_ = buffer[6] & 0xF; 1419 | c2_ = buffer[6] >> 4; 1420 | c3_ = buffer[7] & 0xF; 1421 | invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) || (c3 != (~c3_ & 0xF)); 1422 | g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0); 1423 | g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1); 1424 | g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2); 1425 | g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3); 1426 | isSectorTrailer = false; 1427 | } 1428 | 1429 | // Which access group is this block in? 1430 | if (no_of_blocks == 4) { 1431 | group = blockOffset; 1432 | firstInGroup = true; 1433 | } 1434 | else { 1435 | group = blockOffset / 5; 1436 | firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); 1437 | } 1438 | 1439 | if (firstInGroup) { 1440 | // Print access bits 1441 | Serial.print(F(" [ ")); 1442 | Serial.print((g[group] >> 2) & 1, DEC); Serial.print(F(" ")); 1443 | Serial.print((g[group] >> 1) & 1, DEC); Serial.print(F(" ")); 1444 | Serial.print((g[group] >> 0) & 1, DEC); 1445 | Serial.print(F(" ] ")); 1446 | if (invertedError) { 1447 | Serial.print(F(" Inverted access bits did not match! ")); 1448 | } 1449 | } 1450 | 1451 | if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block 1452 | long value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); 1453 | Serial.print(F(" Value=0x")); Serial.print(value, HEX); 1454 | Serial.print(F(" Adr=0x")); Serial.print(buffer[12], HEX); 1455 | } 1456 | Serial.println(); 1457 | } 1458 | 1459 | return; 1460 | } // End PICC_DumpMifareClassicSectorToSerial() 1461 | 1462 | /** 1463 | * Dumps memory contents of a MIFARE Ultralight PICC. 1464 | */ 1465 | void MFRC522::PICC_DumpMifareUltralightToSerial() { 1466 | byte status; 1467 | byte byteCount; 1468 | byte buffer[18]; 1469 | byte i; 1470 | 1471 | Serial.println(F("Page 0 1 2 3")); 1472 | // Try the mpages of the original Ultralight. Ultralight C has more pages. 1473 | for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. 1474 | // Read pages 1475 | byteCount = sizeof(buffer); 1476 | status = MIFARE_Read(page, buffer, &byteCount); 1477 | if (status != STATUS_OK) { 1478 | Serial.print(F("MIFARE_Read() failed: ")); 1479 | Serial.println(GetStatusCodeName(status)); 1480 | break; 1481 | } 1482 | // Dump data 1483 | for (byte offset = 0; offset < 4; offset++) { 1484 | i = page + offset; 1485 | if(i < 10) 1486 | Serial.print(F(" ")); // Pad with spaces 1487 | else 1488 | Serial.print(F(" ")); // Pad with spaces 1489 | Serial.print(i); 1490 | Serial.print(F(" ")); 1491 | for (byte index = 0; index < 4; index++) { 1492 | i = 4 * offset + index; 1493 | if(buffer[i] < 0x10) 1494 | Serial.print(F(" 0")); 1495 | else 1496 | Serial.print(F(" ")); 1497 | Serial.print(buffer[i], HEX); 1498 | } 1499 | Serial.println(); 1500 | } 1501 | } 1502 | } // End PICC_DumpMifareUltralightToSerial() 1503 | 1504 | /** 1505 | * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tupples C1 is MSB (=4) and C3 is LSB (=1). 1506 | */ 1507 | void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. 1508 | byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) 1509 | byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) 1510 | byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) 1511 | byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) 1512 | ) { 1513 | byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); 1514 | byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); 1515 | byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); 1516 | 1517 | accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF); 1518 | accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF); 1519 | accessBitBuffer[2] = c3 << 4 | c2; 1520 | } // End MIFARE_SetAccessBits() 1521 | 1522 | 1523 | /** 1524 | * Performs the "magic sequence" needed to get Chinese UID changeable 1525 | * Mifare cards to allow writing to sector 0, where the card UID is stored. 1526 | * 1527 | * Note that you do not need to have selected the card through REQA or WUPA, 1528 | * this sequence works immediately when the card is in the reader vicinity. 1529 | * This means you can use this method even on "bricked" cards that your reader does 1530 | * not recognise anymore (see MFRC522::MIFARE_UnbrickUidSector). 1531 | * 1532 | * Of course with non-bricked devices, you're free to select them before calling this function. 1533 | */ 1534 | bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) { 1535 | // Magic sequence: 1536 | // > 50 00 57 CD (HALT + CRC) 1537 | // > 40 (7 bits only) 1538 | // < A (4 bits only) 1539 | // > 43 1540 | // < A (4 bits only) 1541 | // Then you can write to sector 0 without authenticating 1542 | 1543 | PICC_HaltA(); // 50 00 57 CD 1544 | 1545 | byte cmd = 0x40; 1546 | byte validBits = 7; /* Our command is only 7 bits. After receiving card response, 1547 | this will contain amount of valid response bits. */ 1548 | byte response[32]; // Card's response is written here 1549 | byte received; 1550 | byte status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 1551 | if(status != STATUS_OK) { 1552 | if(logErrors) { 1553 | Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?")); 1554 | Serial.print(F("Error name: ")); 1555 | Serial.println(GetStatusCodeName(status)); 1556 | } 1557 | return false; 1558 | } 1559 | if (received != 1 || response[0] != 0x0A) { 1560 | if (logErrors) { 1561 | Serial.print(F("Got bad response on backdoor 0x40 command: ")); 1562 | Serial.print(response[0], HEX); 1563 | Serial.print(F(" (")); 1564 | Serial.print(validBits); 1565 | Serial.print(F(" valid bits)\r\n")); 1566 | } 1567 | return false; 1568 | } 1569 | 1570 | cmd = 0x43; 1571 | validBits = 8; 1572 | status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43 1573 | if(status != STATUS_OK) { 1574 | if(logErrors) { 1575 | Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40")); 1576 | Serial.print(F("Error name: ")); 1577 | Serial.println(GetStatusCodeName(status)); 1578 | } 1579 | return false; 1580 | } 1581 | if (received != 1 || response[0] != 0x0A) { 1582 | if (logErrors) { 1583 | Serial.print(F("Got bad response on backdoor 0x43 command: ")); 1584 | Serial.print(response[0], HEX); 1585 | Serial.print(F(" (")); 1586 | Serial.print(validBits); 1587 | Serial.print(F(" valid bits)\r\n")); 1588 | } 1589 | return false; 1590 | } 1591 | 1592 | // You can now write to sector 0 without authenticating! 1593 | return true; 1594 | } // End MIFARE_OpenUidBackdoor() 1595 | 1596 | /** 1597 | * Reads entire block 0, including all manufacturer data, and overwrites 1598 | * that block with the new UID, a freshly calculated BCC, and the original 1599 | * manufacturer data. 1600 | * 1601 | * It assumes a default KEY A of 0xFFFFFFFFFFFF. 1602 | * Make sure to have selected the card before this function is called. 1603 | */ 1604 | bool MFRC522::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) { 1605 | 1606 | // UID + BCC byte can not be larger than 16 together 1607 | if (!newUid || !uidSize || uidSize > 15) { 1608 | if (logErrors) { 1609 | Serial.println(F("New UID buffer empty, size 0, or size > 15 given")); 1610 | } 1611 | return false; 1612 | } 1613 | 1614 | // Authenticate for reading 1615 | MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 1616 | byte status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); 1617 | if (status != STATUS_OK) { 1618 | 1619 | if (status == STATUS_TIMEOUT) { 1620 | // We get a read timeout if no card is selected yet, so let's select one 1621 | 1622 | // Wake the card up again if sleeping 1623 | // byte atqa_answer[2]; 1624 | // byte atqa_size = 2; 1625 | // PICC_WakeupA(atqa_answer, &atqa_size); 1626 | 1627 | if (!PICC_IsNewCardPresent() || !PICC_ReadCardSerial()) { 1628 | Serial.println(F("No card was previously selected, and none are available. Failed to set UID.")); 1629 | return false; 1630 | } 1631 | 1632 | status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); 1633 | if (status != STATUS_OK) { 1634 | // We tried, time to give up 1635 | if (logErrors) { 1636 | Serial.println(F("Failed to authenticate to card for reading, could not set UID: ")); 1637 | Serial.println(GetStatusCodeName(status)); 1638 | } 1639 | return false; 1640 | } 1641 | } 1642 | else { 1643 | if (logErrors) { 1644 | Serial.print(F("PCD_Authenticate() failed: ")); 1645 | Serial.println(GetStatusCodeName(status)); 1646 | } 1647 | return false; 1648 | } 1649 | } 1650 | 1651 | // Read block 0 1652 | byte block0_buffer[18]; 1653 | byte byteCount = sizeof(block0_buffer); 1654 | status = MIFARE_Read((byte)0, block0_buffer, &byteCount); 1655 | if (status != STATUS_OK) { 1656 | if (logErrors) { 1657 | Serial.print(F("MIFARE_Read() failed: ")); 1658 | Serial.println(GetStatusCodeName(status)); 1659 | Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?")); 1660 | } 1661 | return false; 1662 | } 1663 | 1664 | // Write new UID to the data we just read, and calculate BCC byte 1665 | byte bcc = 0; 1666 | for (int i = 0; i < uidSize; i++) { 1667 | block0_buffer[i] = newUid[i]; 1668 | bcc ^= newUid[i]; 1669 | } 1670 | 1671 | // Write BCC byte to buffer 1672 | block0_buffer[uidSize] = bcc; 1673 | 1674 | // Stop encrypted traffic so we can send raw bytes 1675 | PCD_StopCrypto1(); 1676 | 1677 | // Activate UID backdoor 1678 | if (!MIFARE_OpenUidBackdoor(logErrors)) { 1679 | if (logErrors) { 1680 | Serial.println(F("Activating the UID backdoor failed.")); 1681 | } 1682 | return false; 1683 | } 1684 | 1685 | // Write modified block 0 back to card 1686 | status = MIFARE_Write((byte)0, block0_buffer, (byte)16); 1687 | if (status != STATUS_OK) { 1688 | if (logErrors) { 1689 | Serial.print(F("MIFARE_Write() failed: ")); 1690 | Serial.println(GetStatusCodeName(status)); 1691 | } 1692 | return false; 1693 | } 1694 | 1695 | // Wake the card up again 1696 | byte atqa_answer[2]; 1697 | byte atqa_size = 2; 1698 | PICC_WakeupA(atqa_answer, &atqa_size); 1699 | 1700 | return true; 1701 | } 1702 | 1703 | /** 1704 | * Resets entire sector 0 to zeroes, so the card can be read again by readers. 1705 | */ 1706 | bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) { 1707 | MIFARE_OpenUidBackdoor(logErrors); 1708 | 1709 | byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 1710 | 1711 | // Write modified block 0 back to card 1712 | byte status = MIFARE_Write((byte)0, block0_buffer, (byte)16); 1713 | if (status != STATUS_OK) { 1714 | if (logErrors) { 1715 | Serial.print(F("MIFARE_Write() failed: ")); 1716 | Serial.println(GetStatusCodeName(status)); 1717 | } 1718 | return false; 1719 | } 1720 | return true; 1721 | } 1722 | 1723 | ///////////////////////////////////////////////////////////////////////////////////// 1724 | // Convenience functions - does not add extra functionality 1725 | ///////////////////////////////////////////////////////////////////////////////////// 1726 | 1727 | /** 1728 | * Returns true if a PICC responds to PICC_CMD_REQA. 1729 | * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. 1730 | * 1731 | * @return bool 1732 | */ 1733 | bool MFRC522::PICC_IsNewCardPresent() { 1734 | byte bufferATQA[2]; 1735 | byte bufferSize = sizeof(bufferATQA); 1736 | byte result = PICC_RequestA(bufferATQA, &bufferSize); 1737 | return (result == STATUS_OK || result == STATUS_COLLISION); 1738 | } // End PICC_IsNewCardPresent() 1739 | 1740 | /** 1741 | * Simple wrapper around PICC_Select. 1742 | * Returns true if a UID could be read. 1743 | * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. 1744 | * The read UID is available in the class variable uid. 1745 | * 1746 | * @return bool 1747 | */ 1748 | bool MFRC522::PICC_ReadCardSerial() { 1749 | byte result = PICC_Select(&uid); 1750 | return (result == STATUS_OK); 1751 | } // End PICC_ReadCardSerial() 1752 | --------------------------------------------------------------------------------