به نام خدا : تو این مطلب پروژه و آموزش آردوینو کار با ماژول rc522 رو میخوام قرار بدم، فقط چون مطلب یکم طولانی هستش زیاد حرف نمیزنم، بریم سراغ مطلبمون :
پروژه و آموزش آردوینو کار با ماژول rc522
شماتیک پروژه : از اینجا تا آخر مطلب هر پروژه ای که دیدید شماتیکش به صورت زیر هستش :
کد زیر رو هم ببینید که ترتیب پایه ها برا میکروهای مختلف توش ذکر شده ( روی دکمه زیر کیک کنید تا اتصالات نمایش داده شود ) :
اولا طبق دیتاشیت ماژول RC522 به کارتهای زیر اصطلاح MIFARE اطلاق میشه.
MIFARE Mini
MIFARE 1K
MIFARE 4K
MIFARE Ultralight
MIFARE DESFire EV1
MIFARE Plus RF
کارتهای مایفر هم کارت هایی حافظه دار هستن.
که مثلا مورد 2 و 3 بالا رو ببینید، میبینید! که کارت اولی حافظش 1کیلو هستش و بعدی 4کیلو!، که من تو پروژه های این مطلب از کارت 1کیلو استفاده کردم(MIFARE Classic 1k)، در زیر جدول تقسیم بندی حافظه این کارت(مایفر 1 کیلو) رو مشاهده میکنید(برای مشاهده شکل مربوط به کارت 4 کیلو که شبیه عکس زیر هستش به دیتاشیتی که ته همین عنوان قرار دادم مراجعه کنید) :
چند تا نکته کوچولو :
- همون طور که میبینید(عکس بالا رو) 16 تا Sector داریم و تو هر Sector هم 4 تا Block داریم و هر Block هم 16 بایت هستش. (16تا سکتور * 4 تا بلوک * 16بایت = 1024 بایت = اندازه حافظه کارت های مایفر 1k)
- در مکان Sector0 و Block0 از حافظه(عکس بالا!) کد کارخانه کارتمون ( UID = کد اختصاصی هر کارت ) قرار داره و این مکان از حافظه رزرو شده هتسش.
- هر Sector هم به وسیله دو کد با نام های keyA و keyB(که هر کدومشون 6 رقمی هستن!) محافظت میشه و این کد در 4 امین Block از هر Sector قرار داره ( و به این بلوک میگن sector trailer )
- هر کدوم از این کلید های امنیتی داخل هر سکتور میتونن کنترل کنن عملیات خوندن و نوشتن داخل اون سکتور رو.
- قبل انجام هر کاری بر روی کارت ابتدا باید کارت انتخاب بشه و بعد اعتبار سازی بشه!
- همون طور که میبیند(عکس بالا رو میگم ^_^) هر بلوک کد دسترسی داره!(اول کد رو وارد میکنی و بعد اطلاعات رو بخونی/مینویسی)
- پسورد پیشفرض تمام Sectorهای کارتها به صورت پیشفرض برابر 0xff هستش.
- تو مطلب نیازتون میشه که آدرس یه بلوک رو به یه تابعی بدید!، حالا داستان آدرس بلوک چیه؟ از سکتور 0 بگیرید تا سکتور 15، حدود 16*4 تا بلوک دارم یعنی 64 تا بلوک، شمارش بوک ها از 0 شروع میشه تا 63، به بلوک 0 سکتور0 آدرس 0 میدن و همین طوری میریم بالا و در آخر به بلوک 3 سکتور 15 آدرسش میشه 63 (تو شکل بالا میبینید که تو هر سکتور شماره بلوک ها از 0 هستش تا 3 !!! )
خب یه سری توضیحات هم هستش برا بحث Access Bits که دیگه دیدم اگه بخوام ادامه بدم توضیحاتش رو اوضاع قاراشمیش میشه لذا گفتم فیلم ضبط کنم، در ضمن در یادگیری موضوع فیلم زیر لینک زیر کمک کرد بهم که طبق حرفم تو فیلم لینکش رو میزارم براتون : تغییر رمز کارت های مایفر
خب اگه دیتاشیت کارتهای MIFARE رو بخونید میبینید که یه کلمه هستش با نام UID که در مکان BLOCK0 و Sector0 حافظه کارت قرار داره و یه شماره اختصاصی هستش.(حالا این که کارتی وجود داره که بشه این شمارش رو تغییر داد من اطلاع ندارم که هستش یا نه و شاید هم همه این کارت های MIFARE رو میشه این شماره اختصاصیش رو تغییر داد و من اطلاع ندارم)
یه ساختاری(struct) داخل کتابخونه تعریف شده که اسم این نوع متغییر Uid هستش و داخل خود کتابخونه این ماژول هم یه متغییر از این نوع با نام uid ساخته که ما تو پروژه هامون از این متغییر استفاده میکنیم.
تعریف این ساختار و متغییر ساخته شده ازش به صورت زیر هستش و بعدش متغییر هایی که داخل ساختار Uid قرار داره و ما به کمک متغییر uid بهشون دسترسی پیدا میکنیم رو توضیح میدم :
1 2 3 4 5 6 7 | typedef struct { byte size; // Number of bytes in the UID. 4, 7 or 10. byte uidByte[10]; byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. } Uid; // Member variables Uid uid; |
uidByte : کد اختصاصی کارتمون ( UID ) در این آرایه ذخیره میشه.
sak : چیزی که من فهمیدم و تست کردم و جواب گرفتم اینه که به کمک این متغییر میتونیم مدل کارت رو بفهمیم.(که انواع مدل کارت رو در عنوانی قبلی قرار دادم)، در عناوین بعدی میبینید که از این متغییر برای پیدا کردن مدل کارت استفاده میکنیم.
تو توابع بعدی زیاد با این ساختار و متغییر uid کار داریم.
1 2 3 4 5 | #include <SPI.h> #include <MFRC522.h> MFRC522 mfrc522(10/*SS_PIN*/, 9/*RST_PIN*/); MFRC522::MIFARE_Key key; MFRC522::StatusCode status; |
خب فراخونی کتابخونه و ساخت اشیای جدید و... که باید در اول پروژه تون قرار بدید.
خط 1و2 : فراخونی کتابخونه های مورد نیاز!
خط 3 : ساخت یه شی با نام mfrc522 از کلاس MFRC522 (این کلاس در داخل فایلی که در خط 2 فراخونی کردیم قرار داره)، پارامتر اول شی برا تعیین پایه SS (یا SCK) ماژول هستش که برای انتخاب ماژول برای ارسال و دریافت به کار میره، مربوط میشه به بحث SPI، که برای این که میکرو بتونه از طریق SPI با چندین قطعه ارتباط برقرار کنه یه پایه گزاشت با این نام برای این که میکرو اون قطعه رو به کمک این پایه انتخاب کنه و باهاش تبادل دیتا داشته باشه، خواستین میتونید مطلب زیر رو بخونید :
آموزش جامع آردوینو جلسه ۱۴ SPI AND Virtual SPI
پارامتر دوم این شی هم برا تعیین پایه Reset (یا RST) ماژول هستش.
خط 4و5 : بعد از خوندن مطلب زیر متوجه میشید این دو خط چی هستن، در مورد کارشون هم اینو فقط میگم که خط 4 یه نوع متغییر برای ذخیره کردن کد امنیتی 6 رقمی و خط 5 یه نوع متغییر برای ذخیره کردن خروجی توابع(که به کمکش میفهمیم تابع کارش رو درست انجام داده یا غلط و اگه انجام نداده علتش چی بوده): آموزش کامل typedef و struct
1 2 | SPI.begin(); mfrc522.PCD_Init(); |
خب برای هر کتابخونه ای یه همچین توابعی هستش که با فراخونیشون اون کتابخونه راه اندازی میشه، کلا کار این تابع ها مقدار دهی اولیه یه سری مقادیر ظرروی اون کتابخونه هستش.(خط 1 برای کتابخونهه SPI و خط 2 برای کتابخونه MFRC522 هستش.)
1 | bool PICC_IsNewCardPresent(); |
اگه کارتی( tag ) به ماژول ( RFID -RC522 ) نزدیک بشه این تابع مقدار 1 رو بر میگردونه در غیر این صورت مقدار 0 رو برمیگردونه.
1 | bool PICC_ReadCardSerial(); |
این تابع کارش خوندن دیتای داخل کارت هستش - اگه نتونه بخونه مقدار 0 و در صورت موفق بودن کار عدد 1 رو برمیگردونه.
توجه 0 (مهم) : همون طور که تو نکته 5 از عنوان "توضیحات کوچولویی درباره کارت های MIFARE" خدمتتون عرض کردم، اول باید کارت انتخاب بشه، حالا اگه کدهای داخل این تابع رو نگاه کرده باشید میبینید که درش از تابع PICC_Select استفاده شده، یعنی این تابع کارش هم خوندن اطلاعات کارت هستش و هم انتخاب کارت، ما با تابع PICC_Select کاری نداریم اصلا، و از همین تابعی که این جا میبینید استفاده میکنیم، اینم گفتم تا یه وقت نگید داش شما گفتی کارت باس انتخاب بشه و از این جور حرفا، پس کجاس تابعش و ما باید چطور کارت رو انتخاب کنیم و ...، پس این تابع هم اطلاعات کارت رو میخونه و هم کارت رو انتخاب میکنه(البته این تابع هم چیز خاصی نداره و همش 2-3 خط هستش.)
توجه 1 : بعد از این که به کمک تابع PICC_IsNewCardPresent فهمیدیم کارتی به ماژول نزدیک شده، از این تابع برای خودندن اطلاعات کارت استفاده میکنیم.
توجه 2 : این تابعی اطلاعی نشون نمیده و چیزی به ما نمیده، در ادامه میبینید که چطور به اطلاعات دریافت شده توسط این تابع دسترسی پیدا میکنیم.(در قسمت مربوط به عنوان uid توضیح میدم)
1 | void PCD_DumpVersionToSerial(); |
نمایش نسخه Firmware ماژول (همون نمایش نسخه ماژول ^_^) در پنجره سریال
توجه : اینو همینجا میگم دیگه برای توابع مثل این دیگه نمیگم، این توابعی که اطلاعاتشون رو تو پنجره سریال(پورت سریال یا هر چی اسمش رو میزارید) باید برای استفاده از این توابع، قبلش تابع Serial.begin رو در تابع Setup قرار داده باشین تا پورت سریال فعال بشه، اگه در این باره چیزی نمیدونید مطالب زیر رو بخونید :
آموزش جامع آردوینو جلسه 4
آموزش جامع آردوینو جلسه 5
آموزش جامع آردوینو جلسه 6
این تابع جوابی به فرم زیر تو پنجره سریال نمایش میده ( که بسته به نسخه ماژولتون داره ) :
Firmware Version: 0x92 = v2.0
1 2 | PICC_Type PICC_GetType(byte sak); const __FlashStringHelper *PICC_GetTypeName(PICC_Type type); |
1 2 | MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); Serial.println(mfrc522.PICC_GetTypeName(piccType)); |
PICC type: MIFARE 1KB
1 | void PICC_DumpToSerial(Uid *uid); |
جواب1 : سوال خوبی بود ولی چرا میزنی حالا
خب ببیند تو این تابع فرض بر این هستش که رمز تمام سکتور ها برابر مقدار پیشفرض یعنی 0xff هستش ولی اگه رمز رو تغییر بدید دیگه این تابع به کارتون نمیخوره(البته اون موقع بازم میشه ازش استفاده کرد اونم تو نقش جرز لای در
سوال2 : خب حالا ما اومدیم و رمز رو تغییر دادم، حالا تابعی وجود داره که بشه رمز جدید رو بهش داد و به کمکش کل اطلاع کارت رو تو پنجره سریال نمایش داد؟
جواب2 : آراه داش هستش، تو عنوان بعدی قرارش میدم.
1 | mfrc522.PICC_DumpToSerial(&mfrc522.uid); |
جواب1 : خب اگه شما عنوان "فراخونی کتابخونه و ساخت اشیای جدید و..." که در اوایل این مطلب من گزاشتم رو بری ببینی و خط 3 کدهای اون قسمت رو ببینی، میبینی! که من یه شی با نام mfrc522 از کلاس MFRC522 ساختم و برای دسترسی به توابع و متغییر های public یه کلاس، و برا این کار نام شی یی که ساختم رو مینویسم و بعدی نقطه میزارم و بعدش تابع یا متغییر مد نظرم رو فرا می خونم.
سوال2 : حالا mfrc522.uid& از کجا اومد؟؟؟
جواب2 : خب گفتم دیگه، mfrc522 یه شی از کلاس MFRC522 هستش و uid یه متغییر هستش که داخل کلاس فوق قرار داره و ما به کمک شی ساخته شده بهش دسترسی پیدا میکنیم(توابع جلوی اسمشون پرانتز دارن ولی متغییر ها نه ^_^) و کاراکتر & هم که میدونید برا بحث آدرس به کار میره، الان تو کد بالا من اومدم و آدرس متغییر mfrc522.uid رو به تابع PICC_DumpToSerial دادم.
سوال3 : حالا متغییر uid چیه اصلا؟
جواب3 : برو عناوین بالا رو بخون، اول مطلب کامل توضیح دادم این متغییر رو *_*
1 | void PICC_DumpMifareClassicToSerial(Uid *uid, PICC_Type piccType, MIFARE_Key *key); |
جواب : تو هم گیر دادی هاااا
1 | void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); |
uid : اطلاعات کارت سنس شده.
key : کد دسترسی به این سکتور.
sector : سکتور مد نظرتون.
این تابع رو میتونید به صورت زیر استفاده کنید(کد زیر قسمتی از پروژه ای هستش که تو عنوان بعدی یا بعدیش یا بعدیهاش ^_^ قرار میدم)
1 2 3 4 5 6 | // نمایش سکستور به سکتور اطلاعات کارت سنس شده for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; Serial.println(F("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits")); for (int8_t i = 16 - 1; i >= 0; i--) mfrc522.PICC_DumpMifareClassicSectorToSerial(&mfrc522.uid, &key, i); mfrc522.PICC_HaltA(); // Halt the PICC before stopping the encrypted session. mfrc522.PCD_StopCrypto1(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <SPI.h> #include <MFRC522.h> MFRC522 mfrc522(10/*SS_PIN*/, 9/*RST_PIN*/); MFRC522::MIFARE_Key key; MFRC522::StatusCode status; void setup() { Serial.begin(115200); SPI.begin(); mfrc522.PCD_Init(); } void loop() { up: if ( mfrc522.PICC_IsNewCardPresent()) { if ( ! mfrc522.PICC_ReadCardSerial()) { Serial.print("Data Read Fail"); goto up; } // نمایش ورژن ماژول mfrc522.PCD_DumpVersionToSerial(); // نمایش مدل کارت Serial.print("PICC type: "); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); Serial.println(mfrc522.PICC_GetTypeName(piccType)); // نمایش کد اختصاصی کارت Serial.print("Card UID:"); dump_byte_array(mfrc522.uid.uidByte ,mfrc522.uid.size); Serial.print("\n\n\n"); // نمایش تمام اطلاعات کارت به کمک پسورد پیشفرض Serial.println("PICC_DumpToSerial"); mfrc522.PICC_DumpToSerial(&mfrc522.uid); Serial.print("\n\n\n"); // نمایش سکستور به سکتور اطلاعات کارت سنس شده for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; Serial.println(F("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits")); for (int8_t i = 16 - 1; i >= 0; i--) mfrc522.PICC_DumpMifareClassicSectorToSerial(&mfrc522.uid, &key, i); mfrc522.PICC_HaltA(); // Halt the PICC before stopping the encrypted session. mfrc522.PCD_StopCrypto1(); } } void dump_byte_array(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } |
پروژه ماژول RC522 با آردوینو
تو این پروژه شماره اختصاصی هر کارت(UID) رو میخونم و اگه در میکرو وجود داشت تو پنجره سریال مینویسم کارت وجود داره در غیر این صورت مینویسم وجود نداره، یه پروژه ساده.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <SPI.h> #include <MFRC522.h> MFRC522 rfid(10/*SS_PIN*/, 9/*RST_PIN*/); MFRC522::MIFARE_Key key; byte ListMyTag[10][4] = { {}, { 107, 104, 15, 43 }, { 70, 27, 235, 72 }, { 109, 131, 58, 213 }, { 87, 84, 58, 213 }, { 23, 222, 58, 213 }, { 183, 161, 58, 213 } }; unsigned char NumberCard; void setup() { Serial.begin(9600); SPI.begin(); rfid.PCD_Init(); } void loop() { if ( ! rfid.PICC_IsNewCardPresent()) return; if ( ! rfid.PICC_ReadCardSerial()) return; Serial.print("The NUID tag = "); dump_byte_array(rfid.uid.uidByte, rfid.uid.size); Serial.print(" ---> "); NumberCard = BarresiVejodeKart(rfid.uid.uidByte) ; if(NumberCard) Serial.println((String)"Card " + NumberCard); else Serial.println(F("Card Vejod Nadarad Dar Hafeze.")); rfid.PICC_HaltA(); // Halt PICC rfid.PCD_StopCrypto1(); // Stop encryption on PCD } void dump_byte_array(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } unsigned char BarresiVejodeKart(byte* YourCard) { for(int i=1; ListMyTag[i][0]!='\0'; i++) { if( YourCard[0] == ListMyTag[i][0] && YourCard[1] == ListMyTag[i][1] && YourCard[2] == ListMyTag[i][2] && YourCard[3] == ListMyTag[i][3] ) return i; } return 0; } |
اینم از جواب پروژه که من با 10 تا کارت تست کردم، 6 تا از کارت ها رو تو میکرو کدشون رو ذخیره کردم و 4 تا رو نه و نتیجه رو در زیر میبینید که پروژه درست کار کرده.
The NUID tag = 6B 68 0F 2B ---> Card 1
The NUID tag = 46 1B EB 48 ---> Card 2
The NUID tag = B7 A1 3A D5 ---> Card 6
The NUID tag = 17 DE 3A D5 ---> Card 5
The NUID tag = 57 54 3A D5 ---> Card 4
The NUID tag = 6D 83 3A D5 ---> Card 3
The NUID tag = D6 88 DA 5D ---> Card Vejod Nadarad Dar Hafeze.
The NUID tag = 46 50 4F 5E ---> Card Vejod Nadarad Dar Hafeze.
The NUID tag = 16 66 2F 5E ---> Card Vejod Nadarad Dar Hafeze.
The NUID tag = 76 89 48 5E ---> Card Vejod Nadarad Dar Hafeze.
فعلا همین دو تا پروژه کافیه تا اینجای مطلب، بقیه پروژه ها بمونه برا بعد، فعلا چند تا دیگه تابع توضیح بدم و بعد برا اون توابع هم مثال قرار میدم.
1 2 | void PCD_AntennaOn(); void PCD_AntennaOff(); |
1 | void PCD_Reset(); |
از اسمش معلومه کارش چیه ولی فک نکنم بکارتون بیاد
1 | bool PCD_PerformSelfTest(); |
بررسی این که ماژول سالمه یا نه - اگه سالم بود عدد 1 وگرنه 0 رو برمیگردونه.
اگه دیتاشیت ماژول قسمت 16.1.1 رو بخونید، میبینید که ماژول یه ویژگی داره به نام Self test که یه سری مراحل داره ولی چون ما از کتابخونه آماده استفاده میکینم دیگه لازم نیست اون مراجل رو انجام بدیم ولی اگه دوست داشتین میتونید به کتابخونه این ماژول مراجعه کنید و ببنید کدهاش رو و ساختار این تابع رو ببینید.
1 | const __FlashStringHelper *GetStatusCodeName(StatusCode code); |
خب تو این مطلب بعضی توابع خروجیشون به صورت StatusCode هستش، که شما اون متغییر (از نوع ساختار) status که تو قسمت "فراخونی کتابخونه و ساخت اشیای جدید و..." دیدید که من بعد از فراخونی کتابخونه ها این کد رو تو پروژه اضافه کردم، خب میگفتم اون توابعی که خروجیشون به صورت StatusCode هستش رو مقدار اون تابع رو میدیم به متغییر status و حالا برای این که status رو ببینم پیغامش چیه، از این تابعی که میبینید استفاده میکنیم، و این تابع معنای مقدار ذخیره شده داخل status رو به صورت یه رشته به ما میده.(چقدر پیچوندم، لپ کلوم : status به این تابع میدی و معنای اون مقدار ذخیره شده تو status رو به صورت رشته دریافت میکنی، وسلام!)
حالا تعداد حالاتی که بایت تست کنیم تا پسورد یه سکتور رو به دست بیاریم میشه :
6 ^ 256 = 281474976710656
حالا فرض کنیم تست هر 10 تا پسورد 1 ثانیه طول میکشه، برای به دست ابردن پسورد یه سکتور باید زمان زیر رو صرف کرد :
281474976710656 / 10 = 28147497671065.6 Sec
28147497671065.6 / 60 = 469124961184.4 Min
469124961184.4 / 60 = 7818749353.0 Hour
7818749353.0 / 24 = 325781223.0 Day
325781223.0 / 365 = 21421231.1 Year
تازه این برای یک سکتور از 16 سکتور هستش، یعنی عملا هک کردن ماژول فایده ای نداره.
هر چی فک میکنم محاسبات بالا درست هستش، اگه اشتباهی رخ داده بگید.
من الان از تابع هک کردن استفاده کردم برای پیدا کردن پسورد سکتور 2 یکی از کارت هام، حدود 37120 قدر پسورد رو تو مدت 45 دقیقه میکرو تست کرده و هنوز پسورد رو پیدا نکرده
نکته اخلاقی : خب حتما فک کردین میخوام الان بگم که نرین سراغ هک کردن این کارت ها و از این جور حرفا، نه اینو نمیخوام بگم چون میدونم نمیتونید(البته کار نشد نداره ) میخوام بگم که بالام جان حواست باشه پسورد رو عوض میکنی، فراموشش نکنی اون پسورد رو که عین من بدبخت میشی، الان منم زدم پسورد یکی از سکتورها رو تغییر دادم و هر چی تلاش میکنم پیدا نمیکنم پسوردش رو، یعنی عملا دیگه اون کارته بدرد نمیخوره.
1 | StatusCode PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); |
خب همون طور که تو نکته 5 عنوان " توضیحات کوچولویی درباره کارت های MIFARE " گفتم(و عمرا یادتون باشه ) : قبل از خوندن باید عملیات اعتار سازی رو انجام بدید و بعد هر بلایی که دوس داشتین سر کارت بیارین(خواستین اطلاعاتش رو بخونید یا روش بنویسید) و در آخر هم باید عملیات رو پایان بدید که این پایان دادن به وسیله دو تا تابع انجام میشه که در عنوان بعدی توضیحشون میدم و بعدش میرم سراغ خوندن و نوشتن و بعدش یه چند تا پروژه مشتی
خب حالا چون که من میدونم شما خیلی ذهتون کنجکاو هستش و اهل سوال پرسیدن هستید(که عمرا نیستید ^_^) میخوام دو تا پارامتر اول تابع بالا رو توضیح بدم، بقیه پارامترهاش رو هم تا الان صد بار دیدم این جور پارامتر ها رو :
command : خب تو این جا میگیم که عملیات اعتبار سازی به وسیله KeyA انجام بشه یا KeyB، برای این کار دو تا متغییر(از نوع enum) داریم به صورت زیر که از اسمشون معلومه که کدوم برا KeyA و کدوم برا KeyB هستش.
PICC_CMD_MF_AUTH_KEY_A
PICC_CMD_MF_AUTH_KEY_B
blockAddr : خب تو هر سکتوری یه 4 تا بوک هستش و یکی از این بلوک ها محل ذخیره سازی پسوردها هستش(آخرین بلوک هر سکتور) و ما آدرس این بلوک رو به تابع میدیم.(نام این بلوک هم trailer Block یا sector trailer هستش!!! کلا مهم اینه که تو اسمش trailer داره )
1 2 | mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); |
بعد از این که اعتار سنجی کردیم و کارتمون رو خوندیم/نوشتیم حالا باید به ارتباط پایان بدیم(بهترین جمله ای که پیدا کردم همین بود!) و برای این کار باید بعد از اتمام کارمون با کارت، این دو تابع رو فراخونی کنیم.
1 | StatusCode MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); |
به کمک این تابع میتونیم اطلاعات یه بلوک از حافظه کارتمون رو بخونیم.
blockAddr : خب همون طور که تو نکته8 عنوان "توضیحات کوچولویی درباره کارت های MIFARE" گفتم، آدرس بلوک ها از 0 شروع میشه تا 63 (برای کارتهای 1کیلو کلاسیک)، آدرس بلوکی که میخوای بخونیش رو اینجا قرار میدی.
buffer : یه آرایه با حداقل اندازه 18 به این تابع میدی تا اطلاعات خونده شده توسط تابع داخل این تابع ذخیره بشه و بعد ما این تابع رو میخونیم.
bufferSize : خب از اسمش معلومه، اندازه آرایه buffer هستش.
توضیحات بیشتر و مثال بمونه برا پروژه ای که در ادامه میزارم.
1 | StatusCode MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); |
خب اینم که از اسمش معلومه برا نوشتن اطلاعات یه بلوک به کار میره، که به کمک blockAddr میایم و آدرس بلوکی که میخوایم توش دیتا بنویسیم رو به تابع میدیم، به کمک buffer اطلاعاتی که میخوایم تو اون بلوک نوشته بشه رو تعیین میکنیم، bufferSize هم که بیانگر اندازه buffer هستش.
توضیحات بیشتر و مثال در عنوان بعدِ بعدی!
1 2 3 4 5 6 7 8 9 10 11 | enum StatusCode { STATUS_OK , // Success STATUS_ERROR , // Error in communication STATUS_COLLISION , // Collission detected STATUS_TIMEOUT , // Timeout in communication. STATUS_NO_ROOM , // A buffer is not big enough. STATUS_INTERNAL_ERROR , // Internal error in the code. Should not happen ;-) STATUS_INVALID , // Invalid argument. STATUS_CRC_WRONG , // The CRC_A does not match STATUS_MIFARE_NACK = 255 // A MIFARE PICC responded with NAK. }; |
خب همون طور که تا حالا دیدید خروجی بعضی توابع به صورت StatusCode هستش که اون تابع یه مقداری از مقادیر بالا رو برای ما ارسال میکنه، در ادامه مطلب و پروژه ها ما بیشتر کارمون با STATUS_OK هستش و میخوایم بررسی کنیم که تابع مد نظر کارش رو درست انجام داده یا نه، برا این کار میایم میبینیم که مقداری که اون تابع به ما داده اگه برابر با STATUS_OK بود که یعنی تابع کارش رو درست انجام داده، در غیر این صورت میفهمیم که تابع کارش رو درست انجام نداده و لذا دیگه بقیه عملیات رو متوقف میکنیم، در زیر یه مثال میزنم :
در پروژه ای که در عنوان بعدی میخوام بزارم، اول میام اعتبار سازی میکنم و بعد میام اطلاعات رو میخونم، خب بعد از اعتبار سازی میام بررسی میکنم که آیا این عملیات اعتبار سازی موفقیت آمیز بوده یا نه، اگه بود که میرم سراغ تابع خوندن و اگه نبود که از تابع خوندن هم دیگه خبری نیست و کدهاش اجرا نمیشه.
خب الان شما فقط باس بررسی کنید که که تابع مقدار STATUS_OK رو بر میگردونه یا نه، فقط همین، با بقیه مقادیر ساختار بالا هم کاری نداشته باشید که چی هستن و کارشون چیه و ...
پروژه آردوینو RC522 خواندن و نوشتن اطلاعات کارت
توضیح کلی پروژه : خب تو این پروژه ما اطلاع بلوک به آدرس 2 رو میخونیم و بعد تو اون بلوک اطلاعاتمون رو مینویسیم و بعد دوباره مقدار اون بلوک رو میخونیم تا نشون بدیم به شما که اطلاعات اون بلوک (به درستی) ویرایش شدن؛ این پروژه هم جزو پروژه های نمونه خود این کتابخونه RC522 هستش فقط من یکمکی 😀 تغییرش دادم.
توضیح بیشتر پروژه :
خط 1تا5 : خب این خط ها رو اگه تا اینجای مطلب متوجه نشدید کارشون چیه، دیگه ادامه مطلبو اصلا نخونید.
خط 9تا13 : داخل کدها توضیح خط به خط دادم!
خط 21 : خب تو این جا فرض اینه که شما یه کارت جدید دارین و پسورد و سطح دسترسیش رو تغییر ندادید.(فعلا این پروژه ساده رو ببینید، تو پروژه های بعدی میریم سراغ کارت هایی که پسورد و سطح دسترسیشون رو تغییر دادیم)، الان تو این سطح دسترسی و پسوردی که این کارت های تازه دارن، میتونیم پسورد 0XFFFFFFFFFFFF رو به هر کلید امنیتی(A یا B) بدیم و اطلاعات بلوک ها رو بخونیم یا تغییر بدیم.(الان این پسوردی که تو این خط تعیین کردم برا هر دو کلید A و B هستش ولی همیشه این طور نیست، ممکنه بعضا کلید A با B فرق داشته باشه و کلید هر سکتور هم با سکتور های دیگه برابر نباشه و سطح دسترسی بوک ها هم با هم فرق داشته باشه، لذا همیشه به این سادگی نیست که اول پروژه یه کلید تعریف کنید و دیگه تموم! فعلا چون اول کار هستش پروژه ساده گزاشتیم، تو پروژه های بعدی انشاالله تمام حالت ها رو در حد توان با هم میبینیم و بررسی میکنیم ---> گامس گاماس )
خطوط 32تا57 : تو این خطوط با استفاده از کلید A اول میام عملیات "اعتبار سنجی" رو انجام میدم و در صورت موفق بودن بعدش میرم سراغ خوندن بلوک مد نظرم.
خطوط 65تا101 : تو این خطوط هم با کلید B بعد اعتبار سنجی و موفق بودنش میام تو بلوک مد نظرم اطلاعات مد نظرم رو مینویسم و بعد میام همون بلوک رو میخونم.
دیدید پروژه چقدر آسون بود و چیزی نداشت
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #include <SPI.h> #include <MFRC522.h> MFRC522 mfrc522(10/*SS_PIN*/, 9/*RST_PIN*/); MFRC522::MIFARE_Key key; MFRC522::StatusCode status; byte blockAddr = 2; // آدرس بلوکی که میخوایم اطلاعاتش رو بخونیم و بنویسیم اطلاعاتمون رو داخلش byte trailerBlock = 3; // اینم بلوک تریلر و هم سکتوری! بلوک به آدرس بالا byte dataBlock[] = { 0x13, 0x73, 0x5, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; // اطلاعاتی که میخوایم بنویسیم در بلوک مد نظرمون byte buffer[18]; // اطلاعات خونده شده از بلوک مد نظرمون داخل این آرایه ذخیره میشه byte size = sizeof(buffer); // اندازه آرایه بالا void setup() { Serial.begin(9600); SPI.begin(); mfrc522.PCD_Init(); for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; } void loop() { if ( ! mfrc522.PICC_IsNewCardPresent()) return; if ( ! mfrc522.PICC_ReadCardSerial()) return; // Authenticate using key A Serial.println("Authenticating using key A..."); status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); if (status == MFRC522::STATUS_OK) { Serial.println("Authenticating using key A is Successful"); // Read data from the block Serial.println((String)"Reading data from block " + blockAddr + (String)" ..."); status = mfrc522.MIFARE_Read(blockAddr, buffer, &size); if (status == MFRC522::STATUS_OK) { Serial.println("Reading Successful"); Serial.print((String)"Data in block " + blockAddr + (String)":"); dump_byte_array(buffer, 16); Serial.print("\n\n\n"); } else { Serial.print("Reading Failed : "); Serial.println(mfrc522.GetStatusCodeName(status)); } } else { Serial.print("Authenticating using key A is Failed : "); Serial.println(mfrc522.GetStatusCodeName(status)); } Serial.print("\n\n\n\n\n\n"); // Authenticate using key B Serial.println("Authenticating again using key B..."); status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); if (status == MFRC522::STATUS_OK) { Serial.println("Authenticating using key B is Successful"); // Write data to the block Serial.println((String)"Writing data to block " + blockAddr + (String)" ..."); status = mfrc522.MIFARE_Write(blockAddr, dataBlock, 16); if (status == MFRC522::STATUS_OK) { Serial.println("Writing Successful"); } else { Serial.print("Writing Failed : "); Serial.println(mfrc522.GetStatusCodeName(status)); } // Read data from the block (again, should now be what we have written) Serial.println((String)"Reading data from block " + blockAddr + (String)" ..."); status = mfrc522.MIFARE_Read(blockAddr, buffer, &size); if (status == MFRC522::STATUS_OK) { Serial.println("Reading Successful"); Serial.print((String)"Data in block " + blockAddr + (String)":"); dump_byte_array(buffer, 16); Serial.println(); } else { Serial.print(F("MIFARE_Read() failed: ")); Serial.println(mfrc522.GetStatusCodeName(status)); } } else { Serial.print("Authenticating using key B is Failed : "); Serial.println(mfrc522.GetStatusCodeName(status)); } mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } void dump_byte_array(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } |
1 | void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); |
همون طور که تو فیلم اول مطلب توضیح دادم، با تجوه به 2-3 تا جدول توی دیتاشیت، میتونیم سطح دسترسی رو تعیین کنیم، به این تابع یه سری دیتا میدیم و این تابع 3بایت موردنیاز برای دادن به بیت های Access Bits رو به ما میده!(عمرا کسی فهمیده باشه!)
accessBitBuffer : آرایه ای که به تابع میدیم و تابع 3 بیت مورد نیاز ما رو داخل این تابع ذخیره میکنه و ما هم این 3 بیت رو در بلوک مد نظرمون در قسمت اکسس بیت مینویسیم!
g0,g1,g2,g3 : خب این 4 تا متغییر از نوع byte هر کدوم برا یه بلوک هستن، مثلا g0 برا بلوک 0 و به همین ترتیب g3 برای بلوک3، حالا این متغییر ها چی هستن و ما باید چی چی به جای این متغییر ها قرار بدیم و از این جور حرفا، که در زیر توضیح میدم :
همون طور که در عکس زیر میبینید، برای بلوک های دیتا و Trailer جدول هایی داریم که میتونیم سطح دسترسی مد نظرمون رو به کمکشون انتخاب کنیم، حالا مثلا ما مقادیر C1تاC3 رو برای 4 بلوکمون به صورت زیر برا بر نیازمون انتخاب کردیم :
Block0 : C1=0, C2=0, C3=0 ----> g0=?
Block1 : C1=1, C2=1, C3=0 ----> g1=?
Block2 : C1=1, C2=0, C3=1 ----> g2=?
Block3 : C1=0, C2=0, C3=1 ----> g3=?
حالا شما C3 رو بیت0، C2 رو بیت1 و C1 رو بیت2 در نظر بگیرید لذا مقادیر gها به صورت زیر میشه.
g0=0, g1=6, g2=5, g3=1
حالا این gها رو ما به تابع بالا میدیم و یه آرایه به اندازه 3 خونه به تابع میدیم و کدهای مورد نیاز رو تابع بالا داخل آرایه میریزه و ما ازش استفاده میکنیم.
جواب : خب همون طور که شکل زیر رو میبینید مربوطه به بلوک Trailer که 6تا بایت اول پسA و 4تا وسطی مربوط به Access Bit و 6تای آخر هم مربوط به پسB هستش، بیت 4ام اکسس بیت که سرکاری هستش و مقدارش مهم نیست، الان ما 3بیت اصلی Access Bit که بیت های 6و7و8 هستش رو داریم، میمونه KeyA و KeyB که این دو مورد رو هم باس داشته باشیم!
و بعد یه آرایه 16 تایی ایجاد میکنیم و پسAوB و اکسس بیت ها رو میریزیم داخلش و بعد این آرایه رو داخل این بلوک مینویسیم، این طوری سطح دسرسی بلوک های 0تا3 رو تعیین کردیم و پسورد هم امکان داره تغییر کنه(بستگی به این داره که در چه مدی هستید و آیا میتونید پس رو تغییر بدید یا نه و این که پسورد جدید وارد کردید یا نه، اگه همون پسورد قبلی رو وارد کرده باشید که پسورد هم تغییر نمیکنه و فقط سطح دسترسی ها رو تغییر دادید.)

1 2 | StatusCode MIFARE_GetValue(byte blockAddr, long *value); StatusCode MIFARE_SetValue(byte blockAddr, long value); |

قبل از این که این تابع رو فراخونی بکنید باید بلوک اعتبار سازی بشه(که تابعش رو دربالا براتون گزاشتم قبلا!)
حالا این دو تابع کارشون چیه؟ البته از اسمشون معلومه ولی خب در زیر بازم توضیح میدم :
MIFARE_GetValue : خوندن مقدار بلوک blockAddr و ذخیره مقدارش در متغییر value
MIFARE_SetValue : نوشتن متغییر value در بلوک blockAddr
جواب : خب سوال خوبی هستش، ببینید تو دو تابعی که گفتی ما مقدار Hex تک تک خونه ها رو مینویسیم و میخونیم، ولی تو این دو تا تابعی که الان معرفی کردم مقادیر رو به صورت یه متغییر عددی میخونید و مینویسید، مثلا فک کنید کارت های مترو، یه قسمت این کارت ها مقدار هزینه کارت رو ذخیره میکنه، خب حالا این آقا رفته تو مترو و کارتش رو زده به دستگاه، حالا باید یه پولی از اون کارت کم بشه دیگه، برای این کم کردن میتونیم مقدار پول اون کارت رو به کمک MIFARE_GetValue بخونیم و بعد یه چیزی ازش کنم کنیم و مقدار جدید رو به کمک MIFARE_SetValue بنوسیم تو اون بلوک از حافظه کارت، حالا شاید بازم براتون سوال بشه که خب این کارا رو هم با اون دو تا تابعی که قبلا باهاش آشنا شدیم هم میشه کرد، جواب اینه که آره میشه ولی خب این دو مورد یکم کار رو ساده کردن، البته برا بحث این کارت های مترو، دو تا تابع بعدی رو هم بخونید و یه توضیح کامل تری دربارشون میدم.
1 2 3 4 | StatusCode MIFARE_Decrement(byte blockAddr, long delta); StatusCode MIFARE_Increment(byte blockAddr, long delta); StatusCode MIFARE_Restore(byte blockAddr); StatusCode MIFARE_Transfer(byte blockAddr); |
این دو تابع هم شبیه دو تابع بالا هستا تقریبا، باید قبل از استفاده ازشون عملیات اعتبار سازی رو انجام بدید، باید مد دسترسی بلوک های دیتا(که میخواید برا اون بلوک ها از این دو تابع استفاده کنید) رو رو حالت Value Block بزارید.
MIFARE_Decrement : بلوک به آدرس blockAddr ، مقدارش رو به اندازه delta قدر کاهش میده.
MIFARE_Increment : بلوک به آدرس blockAddr ، مقدارش رو به اندازه delta قدر افزایش میده.
مثال : مثلا همون کارت های مترو، طرف کارتش رو میزنه به دستگاه و الان باید یه مقدار پولی از کارتش کم بشه، به کمک تابع MIFARE_Decrement میشه این کارو کرد، یا مثلا طرف میره کارتش رو شارژ کنه، به کمک تابع MIFARE_Increment میشه این کارو کرد و برا خوندن مقدار پول کارت میتونیم از تابع MIFARE_GetValue استفاده کنیم.
MIFARE_Transfer : خب حالا بعد از این که از توابع بالا استفاده کردید باید برای اعمال تغییرات این تابع رو فراخونی بکنید.
MIFARE_Restore : این تابع رو هم من هر چی تست کردم ازش جواب نگرفتم، کسی اگه جواب گرفته بگه.
تو خطوط 20تا30 هم اومدم سطح دسترسی رو تعیین کردم، شما فقط یک بار از این تابع برای هر کارت استفاده کنید و در دفعات بعدی این کد رو از پروژه حذف کنید(به حالت توضیحات در بیارید.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #include <SPI.h> #include <MFRC522.h> MFRC522 mfrc522(10/*SS_PIN*/, 9/*RST_PIN*/); MFRC522::MIFARE_Key key; MFRC522::StatusCode status; byte sector = 1; byte valueBlockA = 5; byte trailerBlock = 7; long value; long Number = 0; void setup() { Serial.begin(9600); SPI.begin(); mfrc522.PCD_Init(); for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; byte trailerBuffer[] = { 255, 255, 255, 255, 255, 255, // Keep default key A 0, 0, 0, 0, 255, 255, 255, 255, 255, 255 // Keep default key B }; mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], 0, 6, 6, 3); status = mfrc522.MIFARE_Write(trailerBlock, trailerBuffer, 16); if (status != MFRC522::STATUS_OK) { Serial.println(mfrc522.GetStatusCodeName(status)); } } void loop() { if ( ! mfrc522.PICC_IsNewCardPresent()) return; if ( ! mfrc522.PICC_ReadCardSerial()) return; status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.println((String)"AuthenticateB Failed: " + mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_SetValue(valueBlockA, Number); if (status != MFRC522::STATUS_OK) { Serial.println((String)"SetValue Failed: " + mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_GetValue(valueBlockA, &value); if (status != MFRC522::STATUS_OK) { Serial.println((String)"GetValue Failed: " + mfrc522.GetStatusCodeName(status)); return; } Serial.println((String)"New value of value block " + valueBlockA + (String)" = " + value); Number += 1000; // Dump the sector data mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); Serial.println(); mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } void dump_byte_array(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #include <SPI.h> #include <MFRC522.h> MFRC522 mfrc522(10/*SS_PIN*/, 9/*RST_PIN*/); MFRC522::MIFARE_Key key; MFRC522::StatusCode status; byte sector = 1; byte valueBlockA = 5; byte trailerBlock = 7; long value; void setup() { Serial.begin(9600); SPI.begin(); mfrc522.PCD_Init(); for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; byte trailerBuffer[] = { 255, 255, 255, 255, 255, 255, // Keep default key A 0, 0, 0, 0, 255, 255, 255, 255, 255, 255 // Keep default key B }; mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], 0, 6, 6, 3); status = mfrc522.MIFARE_Write(trailerBlock, trailerBuffer, 16); if (status != MFRC522::STATUS_OK) { Serial.println(mfrc522.GetStatusCodeName(status)); } } void loop() { if ( ! mfrc522.PICC_IsNewCardPresent()) return; if ( ! mfrc522.PICC_ReadCardSerial()) return; status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.println((String)"AuthenticateB Failed: " + mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_Increment(valueBlockA, 1000); if (status != MFRC522::STATUS_OK) { Serial.println((String)"Increment Failed: " + mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_Transfer(valueBlockA); if (status != MFRC522::STATUS_OK) { Serial.println((String)"Transfer Failed: " + mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_GetValue(valueBlockA, &value); if (status != MFRC522::STATUS_OK) { Serial.println((String)"GetValue Failed: " + mfrc522.GetStatusCodeName(status)); return; } Serial.println((String)"New value of value block " + valueBlockA + (String)" = " + value); if (value > 10000) { // Check some boundary... Serial.println("Up 10000, so resetting it to 10000 = 0x2710"); status = mfrc522.MIFARE_SetValue(valueBlockA, 10000); if (status != MFRC522::STATUS_OK) { Serial.print((String)"SetValue Failed: " + mfrc522.GetStatusCodeName(status)); Serial.println(); return; } } // Dump the sector data mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); Serial.println(); mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } void dump_byte_array(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #include <SPI.h> #include <MFRC522.h> MFRC522 mfrc522(10/*SS_PIN*/, 9/*RST_PIN*/); MFRC522::MIFARE_Key key; MFRC522::StatusCode status; void setup() { Serial.begin(9600); SPI.begin(); mfrc522.PCD_Init(); for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; /* byte trailerBuffer[] = { 255, 255, 255, 255, 255, 255, // Keep default key A 0, 0, 0, 0, 255, 255, 255, 255, 255, 255 }; // Keep default key B mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], 0, 6, 6, 3); status = mfrc522.MIFARE_Write(trailerBlock, trailerBuffer, 16); if (status != MFRC522::STATUS_OK) { Serial.println(mfrc522.GetStatusCodeName(status)); while(1); } */ } void loop() { if ( ! mfrc522.PICC_IsNewCardPresent()) return; if ( ! mfrc522.PICC_ReadCardSerial()) return; byte sector = 1; byte valueBlockA = 5; byte trailerBlock = 7; byte buffer[18]; byte size = sizeof(buffer); long value; status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.println((String)"AuthenticateB Failed: " + mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_Decrement(valueBlockA, 1500); if (status != MFRC522::STATUS_OK) { Serial.print((String)"Decrement Failed: " + mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_Transfer(valueBlockA); if (status != MFRC522::STATUS_OK) { Serial.print((String)"Transfer Failed: " + mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_GetValue(valueBlockA, &value); if (status != MFRC522::STATUS_OK) { Serial.print((String)"GetValue Failed: " + mfrc522.GetStatusCodeName(status)); return; } Serial.println((String)"New value of value block " + valueBlockA + (String)" = " + value); if (value <= 0) { // Check some boundary... Serial.println("Below 0, so resetting it to 0 = 0x00"); status = mfrc522.MIFARE_SetValue(valueBlockA, 0); if (status != MFRC522::STATUS_OK) { Serial.print((String)"SetValue Failed: " + mfrc522.GetStatusCodeName(status)); Serial.println(); return; } } // Dump the sector data mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); Serial.println(); mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } void dump_byte_array(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } |
- برای این که پروژتون فقط از کارت های 1کیلو پشتیبانی کنه و در قبال کارت های مایفر دیگه کاری نکنه میتونید از کد زیر استفاده کنید، در زیر من یه پیغام گزاشتم داخل if ها ولی شما میتونید، در صورت درست بودن مدل کارت کدهای پروژتون رو اجرا کنید و کدهای پروژتون رو قرار بدید، و اگه کارت پشتیبانی نشد، مثلا یه پیغام بدید که ببم جان، کارتت پشتیبانی نمیشه یا اصلا پیغامی ندید یا هر طور دوس داشتید!
1 2 3 4 5 6 7 | MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); if ( piccType != MFRC522::PICC_TYPE_MIFARE_1K ) { Serial.println("This Project only work with MIFARE Classic 1K"); } else { Serial.println("Your Card is MIFARE Classic 1K And Support in This Project"); } |
تا همینجا کافیه، خسته شدم، مطلب هم شدش 7هزار واژه، البته بازم هست بعضی چیزا که میشه گفتم ولی دیگه خداییش خیلی خسته شدمف الان چند روزه پای این مطلبم
خب این مطلب هم تموم شد، دوستان ازم میپرسیدن این مطلبو کی میزاری، چندین نفر منتظر من بودم، همین جا عذرخواهی میکنم بابت دیر شدن، چون نمیخواستم یه چیزی نوشته باشم که نوشته باشم(بر خلاف بعضی ها که همین طوری یه چیزی مینویسن تا سایتشون بیاد بالا)، تا جایی که در توانم و وقتم بود سعی کردم مطلب رو خوب و کامل بنویسم، حالا اگه جایی رو نگفتم و بد گفتم به بزرگی خودتون ببخشید و گوشزد کونید اون موارد رو تا تصحیح کنم.
داستان! : خب برای هر مطلب سایت من چندین روز وقت میزارم و برا بعضی مطالب بعضا شده که 1ماه دنبالش بودم!، از این سایت هم که کسی به ما پولی نمیده و ما هم که از کسی پولی نمیگیریم(چون میدونیم کسی کمک نمیکنه، چرا؟ بله چون وقتی خودم به سایت های مفید کمک نمیکنم چطور توقع باید داشته باشم که دیگران به سایت من کمک کنن)، پروژه دانشجویی هم که مشکل قانونی داره و ما انجام نمیدیم و پروژه های دیگه رو هم به ما نمیدن برا انجام، سالی هم 500 تومن بابت سایت من هزینه میکنم تقریبا، هزینه قطعاتی هم که تا الان خریدم به کنار(که 4-5 کارتون بزرگ الان من دم دستگاه دارم!)، اینا رو نمیگم که منت بزارم در کل اینو میخوام بگم، زورم میاد و ناراحت میشم وقتی کسی رو میبینم که یه چیزی بلد هستش ولی نمیاد یاد بده و به اشتراک بزاره، بارها شده که افرادی رو میشناختم گفتم بیاید مطلب بزار گفتن باشه ولی نیومدن، بعضیها اومدن و چند مطلب گزاشتن و بعد ولش کردن، من خودم پروژه انجام میدم و بعد با ذوق و شوق با پولش میرم قطعه میخرم(جالا بگذریم از وقتایی که این فروشگاه ها به من ظرر زدن، مثلا همین eca به من 130 تومن همین آفتاب رایانه با ارسال یه نمایشگر شکسته_به دست من که رسید شکسته بود_ حدود 45 تومن ظرر زدن و موارد دیگه) و بعد خرید قطعه کار باهاشون رو میرم یاد میگیرم و مطلبش رو میزارم تو سایت تا دیگران مجبور نباشن برا یادگیری کار با اون قطعه دیگه وقت بزارن، و من برنامم اینه که تو این زمینه به دوستان کمک کوچیکی کرده باشم، ولی ناراحت میشم وقتی کسی رو میبینم که فک میکنه اگه چیزی رو که بلده تو اینترنت به اشتراک بزاره …. بگذریم؛ به هر حال یکی از اهداف من تو رایگان گزاشتن مطالب اینه که شاید باشن کسایی که مثل ما جیبشون خالی باشه و پول نداشته باشن بدن آموزش های پولی و این یکی از دلایل و اهداف کار من هستش، و من از این کار بعضیا جدا خیلی ناراحت میشم.(منظورم اونایی نیست که مطلب و آموزش پولی میزارن، یه وقت برداشت اشتباه نکنید از حرفام!)
هزینه مطلب : به بچه های محلتون این چیزایی که بلد هستید رو برید یاد بدید، مثلا همین آردوینو رو برید یاد بدید، یه 10 جلسه 1 ساعته برای رضای خدا برید برای اینا کلاس بزارید، حالا تو مساجد، حسینیه ها میتونید کلاساتون رو برگزار کنید، به فرهنگی یا بسیج مساجد مراجعه کنید و بگید من آمادگیشو رو دارم تا فلان کلاس رو برگزار کنم و آموزش بدم، اونا هم از خداشونه، این طوری اگه تو هر کلاس 10 نفر باشه، هم بچه های محلتون رو یه چیزی یاد دادید و علاقه مندشون کردید به این کارها(من خودم بچگیام یه مدار فلشر دیدم که داشتم ساخته بود و سر همون اومدم رشته الکترونیک و بعد این کارها – اگه همون زمان یکی این چیزا رو یاد میداد بهم به جای این که اون موقع تو جوبا ماهی میگرفتم با آهنگ ربا!، بجاش میرفتم سراغ الکترونیک از همون بچگیم و به این سن که میرسیم یه مخی میشدم برا خودم، خداییش غیر از اینه؟) و میتونن همین بچه ها از طرف مدرسشون برن مسابقات رباتیک و …، هم بچه ها رو از کوچه خیابون ها جمع کردید(خداییش دیگه خودتون باید بدونید که بچه ای که تو کوچه خیابون باشه همش چیزای درستی یاد نمیگیره)….. والا من هرچی فک میکنم ایده خوبی هستش، یه برد آردوینو، یه کامپیوتر یا لبتاب و چند تا قطعه ساده مثل lcd و led و سون سگمنت و موتور dc و از این جور چیزا لازم داره.(خداییش انجام بدید این کار رو، و نتیجه اش رو بهم همینجا بگید)
تا مطلب بعد اگه زنده بودیم یا علی.
مهمان
سلام آرزوی بهترین ها رو برای شما دارم
ولی یه نکته شما درست میگید آدم تو یاد دادن یاد میگیره
بنده در زمینه برق و الکترونیک خودرو فعالیت دارم و در حد توان یه چیزایی رو به اشتراک گزاشتم ولی بعد چند وقت تمامی اطلاعاتی که لو دادم بر ضرر خودم تموم شد باید ببخشید جمله ادبی مناسبی نیست ولی دست یه تعداد افراد نا لایق و سود جو افتاد که رو همون اطلاعات الان دارن کار انجام میدن و 20 تومان اجرت میگیرن فارغ از این که بدونن چه زحماتی برای این اطلاعات کشیده شده
وقتی آدم این رو میبینه خشک و ترو با هم میسوزونه این نظر منه شاید هم اشتباه فکر کنم ؟؟؟؟ …….
مهمان
باسلام
ممنونم از آموزش کاملتان
یک سوالی داشتم :
در رابطه با تابع:
mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], g0, g1, g2, g3);
من نحوه مقدار دهی g ها رو متوجه نشدم خوب.
مثلا برای بلوک های زیر g ها چه طور مقدار دهی میشوند
block 0 = C10=0 ,C20=1 ,C30=1
block 1 = C11=0 ,C21=1 ,C31=1
block 2 = C12=0 ,C22=1 ,C32=1
block 3 = C13=1 ,C23=1 ,C33=1
ممنون میشوم راهنمایی ام کنید
مهمان
به نظرم یه ” خدا خیرت بده ” خستگی رو از تنت در میکنه . کلا من خیلی چیزا ازت یاد گرفتم و متاسفانه نه شمارو دیدم نه میشناسم فقط میدونم خیلی گلی حتی نمیدونم زنی یا مردی در این حد . خوشحال میشم واتساپ بدی گرفتاریهای الکترونیکیمون رو باهات مطرح کنیم . فدای مهربونیات . اگه مردی بوس بوس ..
مهمان
سلام، من این ماژول رو با atmega8 a و یک کتابخونه آماده راه اندازی کردم و وقتی کارت رو نزدیک می کنم یه سریال 16 رقمی توی مبنای 16 نمایش میده الان نمی دونم این واقعا سریال هست یا چیز دیگه به نظر شما چکار کنم؟
نویسنده این مطلب
سلام/فرمت اطلاعات رو تو دیتاشیت ماژول ببین/کتابخونه رو هم یه بررسی بکن محاسباتشو.
مهمان
آقا مهدی دمت گرم ما که یه تیکه هاشا نفهمیدیم
(البته منظورم بعضی قسمت های تو اون فیلم هست
) اما درکل میخوام اینو بگم درباره “هزینه مطلب” والا ما میخوایم یاد بدیم کسی هم باشه سئوال تو تلگرام جواب میدیم اما خدا شاهده این مسجد و حسینیه و اینا و مدرسه و محله رو نمیشه نمیدونم خودت اینکارو کرد یا نه؟
بعد کلی وقت یه نفر 2 سال بزرگ تر خودم که درگیر کنکوره گفته بیا یادم بده
تو مدرسه هم همینطور شما راضی باش ما منتشر نمیکنیم اما سئوال بود جواب میدیم 
اما هر شب که میرم مسجد یه هم سنما نمیبینم اگه هم میبینم کارشون شده مسخره ما که جن زده شدیم و تو فضا داریم سیر میکنیم و کلا این کارا آت و آشغالن
نویسنده این مطلب
مهمان
سلام مهندس عزیز. خدا قوت.مطالبی که در سایت هست مشخصا زحمتی زیادی میبره. از طرف قشر الکترونیک ازت تشکر میکنم.