commit
Dependencies: MPU6050_SIM5320_TEST
Fork of MPU_SDCARD by
Revision 1:75966605a6a3, committed 2017-11-10
- Comitter:
- suads
- Date:
- Fri Nov 10 15:35:26 2017 +0000
- Parent:
- 0:203cf529f52a
- Commit message:
- mbed cli
Changed in this revision
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/CaseFsm.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/CaseFsm.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,63 @@ +#include <DeviceEmpty.h> +#include <DeviceFull.h> +#include <PhoneUndetected.h> +#include <PhoneDetected.h> +#include <CaseFsm.h> + +CaseFsmStates::CaseFsmStates() : deviceEmptyState_(new DeviceEmpty(*this,timer)), + deviceFullState_(new DeviceFull(*this,timer)), + phoneUndetectedState_(new PhoneUndetected(*this,timer)), + phoneDetectedState_(new PhoneDetected(*this,timer)) +{ + currentState(deviceEmptyState()); +} + + +CaseState* CaseFsmStates::deviceEmptyState() +{ + return deviceEmptyState_; +} + +void CaseFsm::timerCallback(){ + printf("Odoh u DEVICE EMPTY STATE\r\n"); + states_.currentState(states_.deviceEmptyState()); + } + +CaseState* CaseFsmStates::deviceFullState() +{ + return deviceFullState_; +} +CaseState* CaseFsmStates::phoneUndetectedState() +{ + return phoneUndetectedState_; +} +CaseState* CaseFsmStates::phoneDetectedState() +{ + return phoneDetectedState_; +} + +CaseState* CaseFsmStates::currentState() +{ + return currentState_; +} +void CaseFsmStates::currentState(CaseState* state) +{ + currentState_ = state; +} + +CaseFsmStates::~CaseFsmStates() +{ + delete deviceEmptyState_; + delete deviceFullState_; + delete phoneUndetectedState_; + delete phoneDetectedState_; +} + +CaseFsm::CaseFsm() : states_() +{ + + +} + + +
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/CaseState.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/CaseState.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,10 @@ +#include <CaseFsm.h> +#include <CaseState.h> + +CaseState::CaseState(CaseFsmStates& states,Timer& timer): states_(states),stateTimer_(timer){} + +CaseState* CaseState::handle(SwitchPosition& event) { return states_.currentState(); } +CaseState* CaseState::handle(RFIDEvent& event) { return states_.currentState(); } +CaseState* CaseState::handle(TimerEnd& event) { return states_.currentState(); } + +
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/DeviceEmpty.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/DeviceEmpty.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,18 @@ +#include <DeviceEmpty.h> +#include <CaseEvents.h> +#include <CaseFsm.h> + +DeviceEmpty::DeviceEmpty(CaseFsmStates& states, Timer& timer): CaseState(states, timer) +{ + + +} + +CaseState* DeviceEmpty::handle(SwitchPosition& event) +{ + printf("Odoh u DEVICE FULL STATE\r\n"); + + return states_.deviceFullState(); +} + +
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/DeviceFull.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/DeviceFull.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,50 @@ +#include <DeviceEmpty.h> +#include <DeviceFull.h> +#include <CaseFsm.h> + + + +DeviceFull::DeviceFull(CaseFsmStates& states, Timer& timer) : CaseState(states,timer) +{ +} + +CaseState* DeviceFull::handle(SwitchPosition& event) +{ + printf("Odoh u DEVICE EMPTY STATE\r\n"); + return states_.deviceEmptyState(); +} + + + +CaseState* DeviceFull::handle(RFIDEvent& event) +{ + int count=0; + uint8_t tag[]= {0xDB,0x6C,0x10,0x2B}; + //printf("%d\r\n",event.cardType); + for(int i=0; i<event.size; i++) { + if(tag[i]==event.uid[i]) { + // printf(" %x count=%d",event.uid[i], count); + count++; + } + } + + //printf(" count %d\r\n",count); + + if(count==4) { //Je li to validan TAG + printf("Odoh u PHONE DETECTED STATE\r\n"); + + return states_.phoneDetectedState(); //TAG je validan idem u detected + + } else { + printf("Odoh u PHONE UNDETECTED STATE\r\n"); + return states_.phoneUndetectedState(); //Ako nije idem u undetected + + } +} +CaseState* DeviceFull::handle(TimerEnd& event) +{ + printf("Odoh u DEVICE UNDETECTED STATE\r\n"); + return states_.phoneUndetectedState(); +} + +
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/PhoneDetected.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/PhoneDetected.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,20 @@ +#include <PhoneDetected.h> +#include <DeviceEmpty.h> +#include <CaseEvents.h> +#include <CaseFsm.h> + +PhoneDetected::PhoneDetected(CaseFsmStates& states, Timer& timer): CaseState(states,timer) +{ + + +} + +CaseState* PhoneDetected::handle(SwitchPosition& event) +{ + + printf("Odoh u DEVICE EMPTY STATE\r\n"); + return states_.deviceEmptyState(); +} + + +
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/PhoneUndetected.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/PhoneUndetected.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,16 @@ +#include <PhoneUndetected.h> +#include <DeviceEmpty.h> +#include <CaseEvents.h> +#include <CaseFsm.h> + +PhoneUndetected::PhoneUndetected(CaseFsmStates& states, Timer& timer): CaseState(states,timer) {} + +CaseState* PhoneUndetected::handle(SwitchPosition& event) +{ + + printf("Odoh u DEVICE EMPTY STATE\r\n"); + return states_.deviceEmptyState(); +} + + +
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/include/CaseEvents.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/include/CaseEvents.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,12 @@ +#ifndef CASEFSM_EVENTS_H_ +#define CASEFSM_EVENTS_H_ + +struct SwitchPosition {}; +struct RFIDEvent { + uint8_t cardType; + uint8_t uid[10]; + uint8_t size; + }; +struct TimerEnd {}; + +#endif /* ifndef CASEFSM_EVENTS_H_ */
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/include/CaseFsm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/include/CaseFsm.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,72 @@ +#ifndef CASEFSM_FSM_H_ +#define CASEFSM_FSM_H_ +#include <CaseState.h> +#include "mbed.h" + +#include <CaseEvents.h> + +struct SwitchPosition; +struct RFIDEvent; + +class CaseFsmStates +{ +public: + CaseFsmStates(); + + CaseState* deviceEmptyState(); + CaseState* deviceFullState(); + CaseState* phoneUndetectedState(); + CaseState* phoneDetectedState(); + CaseState* currentState(); + + void currentState(CaseState* state); + ~CaseFsmStates(); + +private: + + CaseState* deviceEmptyState_; + CaseState* deviceFullState_; + CaseState* phoneUndetectedState_; + CaseState* phoneDetectedState_; + CaseState* currentState_; + + Timer timer; + +}; + +class CaseFsm +{ +public: + CaseFsm(); + Timeout rfidTimeout; + void timerCallback(); + template <typename Event> + void handle(Event& event) { + CaseState* state = states_.currentState()->handle(event); + if(state==states_.deviceFullState()) { + rfidTimeout.attach(this, &CaseFsm::timerCallback, 5.0); + } + if(state==states_.deviceEmptyState()) { + rfidTimeout.detach(); + } + if(state==states_.phoneDetectedState()) { + rfidTimeout.detach(); + } + states_.currentState(state); + } + bool DeviceInFullState() { + return states_.currentState() == states_.deviceFullState(); + } + bool DeviceInPhoneDetectedState() { + return states_.currentState() == states_.phoneDetectedState(); + } + bool DeviceInDeviceEmptyState() { + return states_.currentState() == states_.deviceEmptyState(); + } + virtual ~CaseFsm() {} + +private: + CaseFsmStates states_; + +}; +#endif /* ifndef CASEFSM_FSM_H_ */
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/include/CaseState.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/include/CaseState.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,21 @@ +#ifndef CASEFSM_STATE_H_ +#define CASEFSM_STATE_H_ +#include "mbed.h" +struct SwitchPosition; +struct RFIDEvent; +struct TimerEnd; +class CaseFsmStates; + +class CaseState { + public: + CaseState(CaseFsmStates& state, Timer& timer); + virtual CaseState* handle(SwitchPosition& event); + virtual CaseState* handle(RFIDEvent& event); + virtual CaseState* handle(TimerEnd& event); + virtual ~CaseState() {} + + protected: + CaseFsmStates& states_; + Timer& stateTimer_; +}; +#endif /* ifndef CASEFSM_STATE_H_ */
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/include/DeviceEmpty.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/include/DeviceEmpty.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,14 @@ +#ifndef CASEFSM_DEVICEEMPTY_H_ +#define CASEFSM_DEVICEEMPTY_H_ + +#include <CaseState.h> +#include "mbed.h" + +class CaseFsmStates; + +class DeviceEmpty : public CaseState { + public: + DeviceEmpty(CaseFsmStates& states, Timer& timer); + CaseState* handle(SwitchPosition& event); +}; +#endif /* ifndef CASEFSM_DEVICEEMPTY_H_ */
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/include/DeviceFull.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/include/DeviceFull.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,18 @@ +#ifndef CASEFSM_DEVICEFULL_H_ +#define CASEFSM_DEVICEFULL_H_ + +#include <CaseState.h> + +class CaseFsmStates; + +class DeviceFull : public CaseState +{ +public: + DeviceFull(CaseFsmStates& states, Timer& timer); + CaseState* handle(SwitchPosition& event); + CaseState* handle(RFIDEvent& event); + CaseState* handle(TimerEnd& event); + + +}; +#endif /* ifndef CASEFSM_DEVICEFULL_H_ */
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/include/PhoneDetected.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/include/PhoneDetected.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,17 @@ +#ifndef CASEFSM_PHONEDETECTED_H_ +#define CASEFSM_PHONEDETECTED_H_ + +#include <CaseState.h> + +class CaseFsmStates; + +class PhoneDetected : public CaseState { + public: + PhoneDetected(CaseFsmStates& states, Timer& timer); + CaseState* handle(SwitchPosition& event); + + private: + unsigned timeForLongPress_; +}; + +#endif /* ifndef CASEFSM_PHONEDETECTED_H_ */
diff -r 203cf529f52a -r 75966605a6a3 CaseFSM/include/PhoneUndetected.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CaseFSM/include/PhoneUndetected.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,18 @@ +#ifndef CASEFSM_PHONEUNDETECTED_H_ +#define CASEFSM_PHONEUNDETECTED_H_ + +#include <CaseState.h> + +class CaseFsmStates; + +class PhoneUndetected : public CaseState { + public: + PhoneUndetected(CaseFsmStates& states, Timer& timer); + CaseState* handle(SwitchPosition& event); + + private: + unsigned timeForLongPress_; + +}; + +#endif /* ifndef CASEFSM_PHONEUNDETECTED_H_ */
diff -r 203cf529f52a -r 75966605a6a3 DataLogger/DataLogger.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DataLogger/DataLogger.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,155 @@ + +#include <stdio.h> + + +#include "log.h" +#include "DataLogger.h" + +using namespace std; + + + +template<class T> +void DataLogger<T>::savelog(T templog) +{ + Log nule(0,0,0,0); + uint16_t del=position(); + + if (_index < (DATASIZE - 1)) { + _index++; + _datalist[_index] = templog; + + } + else { + _index = DATASIZE-del; + + for (int k = 0; k<(DATASIZE-del) && _index!=-1; k++) + _datalist[k]=_datalist[del+k]; + + for (int k = 0; k<DATASIZE; k++){ + if(k>=(DATASIZE-del)) + _datalist[k]=nule; + } + + _datalist[DATASIZE-del]=templog; + +} + + } + +template<class T> +void DataLogger<T>::print() +{ + //Log *p; + //p=getfirst(); + //cout<<"dirst"<<(*p).getTime()<<endl; + + printf("\nPrint\n"); + for (int i = 0; i < DATASIZE; i++) { + printf("Date %d ", _datalist[i].getDate()); + printf("Time %d ", _datalist[i].getTime()); + printf("Fsm %d ", _datalist[i].getFsmtype()); + printf("Event %d\n", _datalist[i].getEvent()); + + } +} + +template<class T> +uint8_t DataLogger<T>::position() +{ + int k=0; + int datum=_datalist[0].getDate(); + for (int i = 0; i < DATASIZE; i++) { + if(_datalist[i].getDate()==datum) + k++; + } + return k; +} + + + + +template<class T> +uint16_t DataLogger<T>::getsize() +{ + + return sizeof(_datalist) / 4; +} + +template<class T> +T* DataLogger<T>::getfirst(){ + + return _datalist; + +} + +template<class T> +T* DataLogger<T>::getlast(){ +int i=0; + for(i;i<DATASIZE;i++){ + if(_datalist[i].getDate()==0) + break; + + } + return (_datalist+i-1); +} + +template<class T> +uint16_t DataLogger<T>::getnumberOfDays(){ + uint16_t countDays=1; + Log *p1=getfirst(); + Log *p2; + p2=getLastStop(); + uint16_t currentDate=(*p1).getDate(); + + if((*p2).getDate()==currentDate && (*p2).getDate()!=0) + countDays; + + // cout<<"last stop "<<(*p1).getTime()<<endl; + + + while(p1!=p2){ + // cout<<"d "<<(*p1).getDate()<<endl; + if((*p1).getDate() != currentDate){ + countDays++; + currentDate=(*p1).getDate(); + } + p1++; + } + + // for(int i=0;i<DATASIZE;i++){ + // if(_datalist[i].getDate() != _datalist[i+1].getDate()) + // countDays++; + + // } + // cout<<"dani "<<countDays<<endl; + + return countDays; +} + + +template<class T> +T* DataLogger<T>::getLastStop(){ + + //cout<<"getlaststop"<<endl; + + int i=DATASIZE-1; + + for(i; i>=0; i--) + if (_datalist[i].getDate()!=0 && _datalist[i].getEvent()==0) + { + //cout<<"i "<<i<<" "<<_datalist[i].getTime()<<"Event "<<_datalist[i].getEvent()<<endl; + break; + + } + + return (_datalist+i); +} + + + + + +template class DataLogger<Log>; + +
diff -r 203cf529f52a -r 75966605a6a3 DataLogger/DataLogger.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DataLogger/DataLogger.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,44 @@ +#ifndef _DATALOGGER_H +#define _DATALOGGER_H + +//#include "DataLogger.h" +#include <stdint.h> + +#define DATASIZE 20 + +using namespace std; + + +//*************************************************************LOGGER**************************************************************************// + +template<class T> +class DataLogger{ +private: + + T _datalist[DATASIZE]; + int _index; + + +public: + + + DataLogger(){//cout<<"Default Constructor"<<endl; + for(int i=0;i<DATASIZE;i++) + _datalist[i]._date=0; + _index=-1;} + DataLogger(T b){//cout<<"Constructor"<<endl; + _index=-1;} + void savelog(T templog); + void print(); + uint16_t getsize(); + uint8_t position(); + T* getfirst(); + T* getlast(); + uint16_t getnumberOfDays(); + T* getLastStop(); + + + +}; +#endif +
diff -r 203cf529f52a -r 75966605a6a3 Log/log.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Log/log.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,81 @@ + #include <stdio.h> + #include <stdlib.h> + #include <stdint.h> + +// #include <string> +// #include <typeinfo> + +#include "log.h" + +Log::Log(uint16_t ttime, uint8_t fsmtype, uint8_t event,uint16_t date) +{ + uint16_t h=ttime/100; + uint16_t m=ttime%100; + uint16_t data=fsmtype<<12|event<<11; + h=h<<6; + _date=date; + _data=data|h|m; +} + +Log::Log(){_data=0;} + +uint16_t Log::getsize() +{ + return sizeof(_data); +} +uint16_t Log::getDate() const +{ + return _date; +} + +bool Log::getFsmtype() const +{ + return (_data & (1 << 12))!=0; +} + +bool Log::getEvent() const +{ + return (_data & (1 << 11))!=0; +} + +uint16_t Log::getTime() const +{ +char temp[6]; +//string z; +uint16_t x = (_data & 0x7c0)>>6; +uint16_t y = (_data & 0x3F); +//cout<<x<<" "<<y<<endl; +if(y<=9) + sprintf(temp, "%d0%d", x,y); + //z=to_string(x)+"0"+to_string(y); +else + sprintf(temp, "%d%d", x,y); + //z =to_string(x)+to_string(y); + temp[6]='\0'; + //std::string z(temp); + //cout<<"temp "<<temp<<endl; +return atoi(temp); +} + +Log& Log::operator =(const Log &a){ // b.operator=(a); + + uint16_t date=a.getDate(); + _date=date; + uint16_t h=a.getTime()/100; + uint16_t m=a.getTime()%100; + uint16_t data=((a.getFsmtype())<<12)|((a.getEvent())<<11); + h=h<<6; + _data=data|h|m; + return *this; +} + +void Log::setLog(uint16_t ttime, uint8_t fsmtype, uint8_t event,uint16_t date) +{ + uint16_t h=ttime/100; + uint16_t m=ttime%100; + uint16_t data=fsmtype<<12|event<<11; + h=h<<6; + _data=data|h|m; + _date=date; +} +
diff -r 203cf529f52a -r 75966605a6a3 Log/log.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Log/log.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,30 @@ +#ifndef _LOG_H +#define _LOG_H + +#include <stdint.h> +//#include <stdio.h> +//#include <stdlib.h> + + +//************************************************************LOG*******************************************************************// +class Log{ +private: + +public: +uint16_t _data; +uint16_t _date; +Log(uint16_t ttime, uint8_t fsmtype, uint8_t event, uint16_t date); +Log(); +uint16_t getsize(); +bool getFsmtype() const; +bool getEvent() const; +uint16_t getTime() const; +uint16_t getDate() const; +void setLog(uint16_t ttime, uint8_t fsmtype, uint8_t event,uint16_t date); + +Log& operator =(const Log &a); + +}; + +#endif +
diff -r 203cf529f52a -r 75966605a6a3 MFRC522.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MFRC522.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,1156 @@ +/* +* MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. +* _Please_ see the comments in MFRC522.h - they give useful hints and background. +* Released into the public domain. +*/ + +#include "MFRC522.h" + +static const char* const _TypeNamePICC[] = +{ + "Unknown type", + "PICC compliant with ISO/IEC 14443-4", + "PICC compliant with ISO/IEC 18092 (NFC)", + "MIFARE Mini, 320 bytes", + "MIFARE 1KB", + "MIFARE 4KB", + "MIFARE Ultralight or Ultralight C", + "MIFARE Plus", + "MIFARE TNP3XXX", + + /* not complete UID */ + "SAK indicates UID is not complete" +}; + +static const char* const _ErrorMessage[] = +{ + "Unknown error", + "Success", + "Error in communication", + "Collision detected", + "Timeout in communication", + "A buffer is not big enough", + "Internal error in the code, should not happen", + "Invalid argument", + "The CRC_A does not match", + "A MIFARE PICC responded with NAK" +}; + +#define MFRC522_MaxPICCs (sizeof(_TypeNamePICC)/sizeof(_TypeNamePICC[0])) +#define MFRC522_MaxError (sizeof(_ErrorMessage)/sizeof(_ErrorMessage[0])) + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for setting up the driver +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor. + * Prepares the output pins. + */ +MFRC522::MFRC522(PinName mosi, + PinName miso, + PinName sclk, + PinName cs, + PinName reset) : m_SPI(mosi, miso, sclk), m_CS(cs), m_RESET(reset) +{ + /* Configure SPI bus */ + m_SPI.format(8, 0); + m_SPI.frequency(8000000); + + /* Release SPI-CS pin */ + m_CS = 1; + + /* Release RESET pin */ + m_RESET = 1; +} // End constructor + + +/** + * Destructor. + */ +MFRC522::~MFRC522() +{ + +} + + +///////////////////////////////////////////////////////////////////////////////////// +// Basic interface functions for communicating with the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Writes a byte to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_WriteRegister(uint8_t reg, uint8_t value) +{ + m_CS = 0; /* Select SPI Chip MFRC522 */ + + // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + (void) m_SPI.write(reg & 0x7E); + (void) m_SPI.write(value); + + m_CS = 1; /* Release SPI Chip MFRC522 */ +} // End PCD_WriteRegister() + +/** + * Writes a number of bytes to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_WriteRegister(uint8_t reg, uint8_t count, uint8_t *values) +{ + m_CS = 0; /* Select SPI Chip MFRC522 */ + + // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + (void) m_SPI.write(reg & 0x7E); + for (uint8_t index = 0; index < count; index++) + { + (void) m_SPI.write(values[index]); + } + + m_CS = 1; /* Release SPI Chip MFRC522 */ +} // End PCD_WriteRegister() + +/** + * Reads a byte from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +uint8_t MFRC522::PCD_ReadRegister(uint8_t reg) +{ + uint8_t value; + m_CS = 0; /* Select SPI Chip MFRC522 */ + + // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + (void) m_SPI.write(0x80 | reg); + + // Read the value back. Send 0 to stop reading. + value = m_SPI.write(0); + + m_CS = 1; /* Release SPI Chip MFRC522 */ + + return value; +} // End PCD_ReadRegister() + +/** + * Reads a number of bytes from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_ReadRegister(uint8_t reg, uint8_t count, uint8_t *values, uint8_t rxAlign) +{ + if (count == 0) { return; } + + uint8_t address = 0x80 | reg; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + uint8_t index = 0; // Index in values array. + + m_CS = 0; /* Select SPI Chip MFRC522 */ + count--; // One read is performed outside of the loop + (void) m_SPI.write(address); // Tell MFRC522 which address we want to read + + while (index < count) + { + if ((index == 0) && rxAlign) // Only update bit positions rxAlign..7 in values[0] + { + // Create bit mask for bit positions rxAlign..7 + uint8_t mask = 0; + for (uint8_t i = rxAlign; i <= 7; i++) + { + mask |= (1 << i); + } + + // Read value and tell that we want to read the same address again. + uint8_t value = m_SPI.write(address); + + // Apply mask to both current value of values[0] and the new data in value. + values[0] = (values[index] & ~mask) | (value & mask); + } + else + { + // Read value and tell that we want to read the same address again. + values[index] = m_SPI.write(address); + } + + index++; + } + + values[index] = m_SPI.write(0); // Read the final byte. Send 0 to stop reading. + + m_CS = 1; /* Release SPI Chip MFRC522 */ +} // End PCD_ReadRegister() + +/** + * Sets the bits given in mask in register reg. + */ +void MFRC522::PCD_SetRegisterBits(uint8_t reg, uint8_t mask) +{ + uint8_t tmp = PCD_ReadRegister(reg); + PCD_WriteRegister(reg, tmp | mask); // set bit mask +} // End PCD_SetRegisterBitMask() + +/** + * Clears the bits given in mask from register reg. + */ +void MFRC522::PCD_ClrRegisterBits(uint8_t reg, uint8_t mask) +{ + uint8_t tmp = PCD_ReadRegister(reg); + PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask +} // End PCD_ClearRegisterBitMask() + + +/** + * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. + */ +uint8_t MFRC522::PCD_CalculateCRC(uint8_t *data, uint8_t length, uint8_t *result) +{ + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. + PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit + PCD_SetRegisterBits(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization + PCD_WriteRegister(FIFODataReg, length, data); // Write data to the FIFO + PCD_WriteRegister(CommandReg, PCD_CalcCRC); // Start the calculation + + // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us. + uint16_t i = 5000; + uint8_t n; + while (1) + { + n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved + if (n & 0x04) + { + // CRCIRq bit set - calculation done + break; + } + + if (--i == 0) + { + // The emergency break. We will eventually terminate on this one after 89ms. + // Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; + } + } + + // Stop calculating CRC for new content in the FIFO. + PCD_WriteRegister(CommandReg, PCD_Idle); + + // Transfer the result from the registers to the result buffer + result[0] = PCD_ReadRegister(CRCResultRegL); + result[1] = PCD_ReadRegister(CRCResultRegH); + return STATUS_OK; +} // End PCD_CalculateCRC() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for manipulating the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Initializes the MFRC522 chip. + */ +void MFRC522::PCD_Init() +{ + /* Reset MFRC522 */ + m_RESET = 0; + wait_ms(10); + m_RESET = 1; + + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74us. Let us be generous: 50ms. + wait_ms(50); + + // When communicating with a PICC we need a timeout if something goes wrong. + // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. + // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. + PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds + PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25us. + PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. + PCD_WriteRegister(TReloadRegL, 0xE8); + + PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting + 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) + + PCD_WriteRegister(RFCfgReg, (0x07<<4)); // Set Rx Gain to max + + PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) +} // End PCD_Init() + +/** + * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. + */ +void MFRC522::PCD_Reset() +{ + PCD_WriteRegister(CommandReg, PCD_SoftReset); // Issue the SoftReset command. + // The datasheet does not mention how long the SoftRest command takes to complete. + // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74us. Let us be generous: 50ms. + wait_ms(50); + + // Wait for the PowerDown bit in CommandReg to be cleared + while (PCD_ReadRegister(CommandReg) & (1<<4)) + { + // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. + } +} // End PCD_Reset() + +/** + * Turns the antenna on by enabling pins TX1 and TX2. + * After a reset these pins disabled. + */ +void MFRC522::PCD_AntennaOn() +{ + uint8_t value = PCD_ReadRegister(TxControlReg); + if ((value & 0x03) != 0x03) + { + PCD_WriteRegister(TxControlReg, value | 0x03); + } +} // End PCD_AntennaOn() + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Executes the Transceive command. + * CRC validation can only be done if backData and backLen are specified. + */ +uint8_t MFRC522::PCD_TransceiveData(uint8_t *sendData, + uint8_t sendLen, + uint8_t *backData, + uint8_t *backLen, + uint8_t *validBits, + uint8_t rxAlign, + bool checkCRC) +{ + uint8_t waitIRq = 0x30; // RxIRq and IdleIRq + return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); +} // End PCD_TransceiveData() + +/** + * Transfers data to the MFRC522 FIFO, executes a commend, waits for completion and transfers data back from the FIFO. + * CRC validation can only be done if backData and backLen are specified. + */ +uint8_t MFRC522::PCD_CommunicateWithPICC(uint8_t command, + uint8_t waitIRq, + uint8_t *sendData, + uint8_t sendLen, + uint8_t *backData, + uint8_t *backLen, + uint8_t *validBits, + uint8_t rxAlign, + bool checkCRC) +{ + uint8_t n, _validBits = 0; + uint32_t i; + + // Prepare values for BitFramingReg + uint8_t txLastBits = validBits ? *validBits : 0; + uint8_t bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. + PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits + PCD_SetRegisterBits(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization + PCD_WriteRegister(FIFODataReg, sendLen, sendData); // Write sendData to the FIFO + PCD_WriteRegister(BitFramingReg, bitFraming); // Bit adjustments + PCD_WriteRegister(CommandReg, command); // Execute the command + if (command == PCD_Transceive) + { + PCD_SetRegisterBits(BitFramingReg, 0x80); // StartSend=1, transmission of data starts + } + + // Wait for the command to complete. + // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting. + // Each iteration of the do-while-loop takes 17.86us. + i = 2000; + while (1) + { + n = PCD_ReadRegister(ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq + if (n & waitIRq) + { // One of the interrupts that signal success has been set. + break; + } + + if (n & 0x01) + { // Timer interrupt - nothing received in 25ms + return STATUS_TIMEOUT; + } + + 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. + return STATUS_TIMEOUT; + } + } + + // Stop now if any errors except collisions were detected. + uint8_t errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr + if (errorRegValue & 0x13) + { // BufferOvfl ParityErr ProtocolErr + return STATUS_ERROR; + } + + // If the caller wants data back, get it from the MFRC522. + if (backData && backLen) + { + n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO + if (n > *backLen) + { + return STATUS_NO_ROOM; + } + + *backLen = n; // Number of bytes returned + PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO + _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. + if (validBits) + { + *validBits = _validBits; + } + } + + // Tell about collisions + if (errorRegValue & 0x08) + { // CollErr + return STATUS_COLLISION; + } + + // Perform CRC_A validation if requested. + if (backData && backLen && checkCRC) + { + // In this case a MIFARE Classic NAK is not OK. + if ((*backLen == 1) && (_validBits == 4)) + { + return STATUS_MIFARE_NACK; + } + + // We need at least the CRC_A value and all 8 bits of the last byte must be received. + if ((*backLen < 2) || (_validBits != 0)) + { + return STATUS_CRC_WRONG; + } + + // Verify CRC_A - do our own calculation and store the control in controlBuffer. + uint8_t controlBuffer[2]; + n = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); + if (n != STATUS_OK) + { + return n; + } + + if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) + { + return STATUS_CRC_WRONG; + } + } + + return STATUS_OK; +} // End PCD_CommunicateWithPICC() + +/* + * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + */ +uint8_t MFRC522::PICC_RequestA(uint8_t *bufferATQA, uint8_t *bufferSize) +{ + return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); +} // End PICC_RequestA() + +/** + * 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. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + */ +uint8_t MFRC522::PICC_WakeupA(uint8_t *bufferATQA, uint8_t *bufferSize) +{ + return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); +} // End PICC_WakeupA() + +/* + * Transmits REQA or WUPA commands. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + */ +uint8_t MFRC522::PICC_REQA_or_WUPA(uint8_t command, uint8_t *bufferATQA, uint8_t *bufferSize) +{ + uint8_t validBits; + uint8_t status; + + if (bufferATQA == NULL || *bufferSize < 2) + { // The ATQA response is 2 bytes long. + return STATUS_NO_ROOM; + } + + // ValuesAfterColl=1 => Bits received after collision are cleared. + PCD_ClrRegisterBits(CollReg, 0x80); + + // For REQA and WUPA we need the short frame format + // - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] + validBits = 7; + + status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); + if (status != STATUS_OK) + { + return status; + } + + if ((*bufferSize != 2) || (validBits != 0)) + { // ATQA must be exactly 16 bits. + return STATUS_ERROR; + } + + return STATUS_OK; +} // End PICC_REQA_or_WUPA() + +/* + * Transmits SELECT/ANTICOLLISION commands to select a single PICC. + */ +uint8_t MFRC522::PICC_Select(Uid *uid, uint8_t validBits) +{ + bool uidComplete; + bool selectDone; + bool useCascadeTag; + uint8_t cascadeLevel = 1; + uint8_t result; + uint8_t count; + uint8_t index; + uint8_t uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. + uint8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. + uint8_t buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A + uint8_t bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. + uint8_t rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. + uint8_t txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. + uint8_t *responseBuffer; + uint8_t responseLength; + + // Description of buffer structure: + // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 + // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. + // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. + // Byte 3: UID-data + // Byte 4: UID-data + // Byte 5: UID-data + // Byte 6: BCC Block Check Character - XOR of bytes 2-5 + // Byte 7: CRC_A + // Byte 8: CRC_A + // The BCC and CRC_A is only transmitted if we know all the UID bits of the current Cascade Level. + // + // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) + // UID size Cascade level Byte2 Byte3 Byte4 Byte5 + // ======== ============= ===== ===== ===== ===== + // 4 bytes 1 uid0 uid1 uid2 uid3 + // 7 bytes 1 CT uid0 uid1 uid2 + // 2 uid3 uid4 uid5 uid6 + // 10 bytes 1 CT uid0 uid1 uid2 + // 2 CT uid3 uid4 uid5 + // 3 uid6 uid7 uid8 uid9 + + // Sanity checks + if (validBits > 80) + { + return STATUS_INVALID; + } + + // Prepare MFRC522 + // ValuesAfterColl=1 => Bits received after collision are cleared. + PCD_ClrRegisterBits(CollReg, 0x80); + + // Repeat Cascade Level loop until we have a complete UID. + uidComplete = false; + while ( ! uidComplete) + { + // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. + switch (cascadeLevel) + { + case 1: + buffer[0] = PICC_CMD_SEL_CL1; + uidIndex = 0; + useCascadeTag = validBits && (uid->size > 4); // When we know that the UID has more than 4 bytes + break; + + case 2: + buffer[0] = PICC_CMD_SEL_CL2; + uidIndex = 3; + useCascadeTag = validBits && (uid->size > 7); // When we know that the UID has more than 7 bytes + break; + + case 3: + buffer[0] = PICC_CMD_SEL_CL3; + uidIndex = 6; + useCascadeTag = false; // Never used in CL3. + break; + + default: + return STATUS_INTERNAL_ERROR; + //break; + } + + // How many UID bits are known in this Cascade Level? + if(validBits > (8 * uidIndex)) + { + currentLevelKnownBits = validBits - (8 * uidIndex); + } + else + { + currentLevelKnownBits = 0; + } + + // Copy the known bits from uid->uidByte[] to buffer[] + index = 2; // destination index in buffer[] + if (useCascadeTag) + { + buffer[index++] = PICC_CMD_CT; + } + + uint8_t bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level. + if (bytesToCopy) + { + // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag + uint8_t maxBytes = useCascadeTag ? 3 : 4; + if (bytesToCopy > maxBytes) + { + bytesToCopy = maxBytes; + } + + for (count = 0; count < bytesToCopy; count++) + { + buffer[index++] = uid->uidByte[uidIndex + count]; + } + } + + // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits + if (useCascadeTag) + { + currentLevelKnownBits += 8; + } + + // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. + selectDone = false; + while ( ! selectDone) + { + // Find out how many bits and bytes to send and receive. + if (currentLevelKnownBits >= 32) + { // All UID bits in this Cascade Level are known. This is a SELECT. + //Serial.print("SELECT: currentLevelKnownBits="); Serial.println(currentLevelKnownBits, DEC); + buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes + + // Calulate BCC - Block Check Character + buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; + + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 7, &buffer[7]); + if (result != STATUS_OK) + { + return result; + } + + txLastBits = 0; // 0 => All 8 bits are valid. + bufferUsed = 9; + + // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) + responseBuffer = &buffer[6]; + responseLength = 3; + } + else + { // This is an ANTICOLLISION. + //Serial.print("ANTICOLLISION: currentLevelKnownBits="); Serial.println(currentLevelKnownBits, DEC); + txLastBits = currentLevelKnownBits % 8; + count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. + index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs + buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits + bufferUsed = index + (txLastBits ? 1 : 0); + + // Store response in the unused part of buffer + responseBuffer = &buffer[index]; + responseLength = sizeof(buffer) - index; + } + + // Set bit adjustments + rxAlign = txLastBits; // Having a seperate variable is overkill. But it makes the next line easier to read. + PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + // Transmit the buffer and receive the response. + result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); + if (result == STATUS_COLLISION) + { // More than one PICC in the field => collision. + result = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (result & 0x20) + { // CollPosNotValid + return STATUS_COLLISION; // Without a valid collision position we cannot continue + } + + uint8_t collisionPos = result & 0x1F; // Values 0-31, 0 means bit 32. + if (collisionPos == 0) + { + collisionPos = 32; + } + + if (collisionPos <= currentLevelKnownBits) + { // No progress - should not happen + return STATUS_INTERNAL_ERROR; + } + + // Choose the PICC with the bit set. + currentLevelKnownBits = collisionPos; + count = (currentLevelKnownBits - 1) % 8; // The bit to modify + index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. + buffer[index] |= (1 << count); + } + else if (result != STATUS_OK) + { + return result; + } + else + { // STATUS_OK + if (currentLevelKnownBits >= 32) + { // This was a SELECT. + selectDone = true; // No more anticollision + // We continue below outside the while. + } + else + { // This was an ANTICOLLISION. + // We now have all 32 bits of the UID in this Cascade Level + currentLevelKnownBits = 32; + // Run loop again to do the SELECT. + } + } + } // End of while ( ! selectDone) + + // We do not check the CBB - it was constructed by us above. + + // Copy the found UID bytes from buffer[] to uid->uidByte[] + index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] + bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; + for (count = 0; count < bytesToCopy; count++) + { + uid->uidByte[uidIndex + count] = buffer[index++]; + } + + // Check response SAK (Select Acknowledge) + if (responseLength != 3 || txLastBits != 0) + { // SAK must be exactly 24 bits (1 byte + CRC_A). + return STATUS_ERROR; + } + + // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. + result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); + if (result != STATUS_OK) + { + return result; + } + + if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) + { + return STATUS_CRC_WRONG; + } + + if (responseBuffer[0] & 0x04) + { // Cascade bit set - UID not complete yes + cascadeLevel++; + } + else + { + uidComplete = true; + uid->sak = responseBuffer[0]; + } + } // End of while ( ! uidComplete) + + // Set correct uid->size + uid->size = 3 * cascadeLevel + 1; + + return STATUS_OK; +} // End PICC_Select() + +/* + * Instructs a PICC in state ACTIVE(*) to go to state HALT. + */ +uint8_t MFRC522::PICC_HaltA() +{ + uint8_t result; + uint8_t buffer[4]; + + // Build command buffer + buffer[0] = PICC_CMD_HLTA; + buffer[1] = 0; + + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 2, &buffer[2]); + if (result == STATUS_OK) + { + // Send the command. + // The standard says: + // If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the + // HLTA command, this response shall be interpreted as 'not acknowledge'. + // We interpret that this way: Only STATUS_TIMEOUT is an success. + result = PCD_TransceiveData(buffer, sizeof(buffer), NULL, 0); + if (result == STATUS_TIMEOUT) + { + result = STATUS_OK; + } + else if (result == STATUS_OK) + { // That is ironically NOT ok in this case ;-) + result = STATUS_ERROR; + } + } + + return result; +} // End PICC_HaltA() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with MIFARE PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/* + * Executes the MFRC522 MFAuthent command. + */ +uint8_t MFRC522::PCD_Authenticate(uint8_t command, uint8_t blockAddr, MIFARE_Key *key, Uid *uid) +{ + uint8_t i, waitIRq = 0x10; // IdleIRq + + // Build command buffer + uint8_t sendData[12]; + sendData[0] = command; + sendData[1] = blockAddr; + + for (i = 0; i < MF_KEY_SIZE; i++) + { // 6 key bytes + sendData[2+i] = key->keyByte[i]; + } + + for (i = 0; i < 4; i++) + { // The first 4 bytes of the UID + sendData[8+i] = uid->uidByte[i]; + } + + // Start the authentication. + return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); +} // End PCD_Authenticate() + +/* + * Used to exit the PCD from its authenticated state. + * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. + */ +void MFRC522::PCD_StopCrypto1() +{ + // Clear MFCrypto1On bit + PCD_ClrRegisterBits(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] +} // End PCD_StopCrypto1() + +/* + * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. + */ +uint8_t MFRC522::MIFARE_Read(uint8_t blockAddr, uint8_t *buffer, uint8_t *bufferSize) +{ + uint8_t result = STATUS_NO_ROOM; + + // Sanity check + if ((buffer == NULL) || (*bufferSize < 18)) + { + return result; + } + + // Build command buffer + buffer[0] = PICC_CMD_MF_READ; + buffer[1] = blockAddr; + + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 2, &buffer[2]); + if (result != STATUS_OK) + { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + return PCD_TransceiveData(buffer, 4, buffer, bufferSize, NULL, 0, true); +} // End MIFARE_Read() + +/* + * Writes 16 bytes to the active PICC. + */ +uint8_t MFRC522::MIFARE_Write(uint8_t blockAddr, uint8_t *buffer, uint8_t bufferSize) +{ + uint8_t result; + + // Sanity check + if (buffer == NULL || bufferSize < 16) + { + return STATUS_INVALID; + } + + // Mifare Classic protocol requires two communications to perform a write. + // Step 1: Tell the PICC we want to write to block blockAddr. + uint8_t cmdBuffer[2]; + cmdBuffer[0] = PICC_CMD_MF_WRITE; + cmdBuffer[1] = blockAddr; + // Adds CRC_A and checks that the response is MF_ACK. + result = PCD_MIFARE_Transceive(cmdBuffer, 2); + if (result != STATUS_OK) + { + return result; + } + + // Step 2: Transfer the data + // Adds CRC_A and checks that the response is MF_ACK. + result = PCD_MIFARE_Transceive(buffer, bufferSize); + if (result != STATUS_OK) + { + return result; + } + + return STATUS_OK; +} // End MIFARE_Write() + +/* + * Writes a 4 byte page to the active MIFARE Ultralight PICC. + */ +uint8_t MFRC522::MIFARE_UltralightWrite(uint8_t page, uint8_t *buffer, uint8_t bufferSize) +{ + uint8_t result; + + // Sanity check + if (buffer == NULL || bufferSize < 4) + { + return STATUS_INVALID; + } + + // Build commmand buffer + uint8_t cmdBuffer[6]; + cmdBuffer[0] = PICC_CMD_UL_WRITE; + cmdBuffer[1] = page; + memcpy(&cmdBuffer[2], buffer, 4); + + // Perform the write + result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) + { + return result; + } + + return STATUS_OK; +} // End MIFARE_Ultralight_Write() + +/* + * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. + */ +uint8_t MFRC522::MIFARE_Decrement(uint8_t blockAddr, uint32_t delta) +{ + return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); +} // End MIFARE_Decrement() + +/* + * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. + */ +uint8_t MFRC522::MIFARE_Increment(uint8_t blockAddr, uint32_t delta) +{ + return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); +} // End MIFARE_Increment() + +/** + * MIFARE Restore copies the value of the addressed block into a volatile memory. + */ +uint8_t MFRC522::MIFARE_Restore(uint8_t blockAddr) +{ + // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. + // Doing only a single step does not work, so I chose to transfer 0L in step two. + return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); +} // End MIFARE_Restore() + +/* + * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. + */ +uint8_t MFRC522::MIFARE_TwoStepHelper(uint8_t command, uint8_t blockAddr, uint32_t data) +{ + uint8_t result; + uint8_t cmdBuffer[2]; // We only need room for 2 bytes. + + // Step 1: Tell the PICC the command and block address + cmdBuffer[0] = command; + cmdBuffer[1] = blockAddr; + + // Adds CRC_A and checks that the response is MF_ACK. + result = PCD_MIFARE_Transceive(cmdBuffer, 2); + if (result != STATUS_OK) + { + return result; + } + + // Step 2: Transfer the data + // Adds CRC_A and accept timeout as success. + result = PCD_MIFARE_Transceive((uint8_t *) &data, 4, true); + if (result != STATUS_OK) + { + return result; + } + + return STATUS_OK; +} // End MIFARE_TwoStepHelper() + +/* + * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. + */ +uint8_t MFRC522::MIFARE_Transfer(uint8_t blockAddr) +{ + uint8_t cmdBuffer[2]; // We only need room for 2 bytes. + + // Tell the PICC we want to transfer the result into block blockAddr. + cmdBuffer[0] = PICC_CMD_MF_TRANSFER; + cmdBuffer[1] = blockAddr; + + // Adds CRC_A and checks that the response is MF_ACK. + return PCD_MIFARE_Transceive(cmdBuffer, 2); +} // End MIFARE_Transfer() + + +///////////////////////////////////////////////////////////////////////////////////// +// Support functions +///////////////////////////////////////////////////////////////////////////////////// + +/* + * Wrapper for MIFARE protocol communication. + * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. + */ +uint8_t MFRC522::PCD_MIFARE_Transceive(uint8_t *sendData, uint8_t sendLen, bool acceptTimeout) +{ + uint8_t result; + uint8_t cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. + + // Sanity check + if (sendData == NULL || sendLen > 16) + { + return STATUS_INVALID; + } + + // Copy sendData[] to cmdBuffer[] and add CRC_A + memcpy(cmdBuffer, sendData, sendLen); + result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); + if (result != STATUS_OK) + { + return result; + } + + sendLen += 2; + + // Transceive the data, store the reply in cmdBuffer[] + uint8_t waitIRq = 0x30; // RxIRq and IdleIRq + uint8_t cmdBufferSize = sizeof(cmdBuffer); + uint8_t validBits = 0; + result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); + if (acceptTimeout && result == STATUS_TIMEOUT) + { + return STATUS_OK; + } + + if (result != STATUS_OK) + { + return result; + } + + // The PICC must reply with a 4 bit ACK + if (cmdBufferSize != 1 || validBits != 4) + { + return STATUS_ERROR; + } + + if (cmdBuffer[0] != MF_ACK) + { + return STATUS_MIFARE_NACK; + } + + return STATUS_OK; +} // End PCD_MIFARE_Transceive() + + +/* + * Translates the SAK (Select Acknowledge) to a PICC type. + */ +uint8_t MFRC522::PICC_GetType(uint8_t sak) +{ + uint8_t retType = PICC_TYPE_UNKNOWN; + + if (sak & 0x04) + { // UID not complete + retType = PICC_TYPE_NOT_COMPLETE; + } + else + { + switch (sak) + { + case 0x09: retType = PICC_TYPE_MIFARE_MINI; break; + case 0x08: retType = PICC_TYPE_MIFARE_1K; break; + case 0x18: retType = PICC_TYPE_MIFARE_4K; break; + case 0x00: retType = PICC_TYPE_MIFARE_UL; break; + case 0x10: + case 0x11: retType = PICC_TYPE_MIFARE_PLUS; break; + case 0x01: retType = PICC_TYPE_TNP3XXX; break; + default: + if (sak & 0x20) + { + retType = PICC_TYPE_ISO_14443_4; + } + else if (sak & 0x40) + { + retType = PICC_TYPE_ISO_18092; + } + break; + } + } + + return (retType); +} // End PICC_GetType() + +/* + * Returns a string pointer to the PICC type name. + */ +char* MFRC522::PICC_GetTypeName(uint8_t piccType) +{ + if(piccType == PICC_TYPE_NOT_COMPLETE) + { + piccType = MFRC522_MaxPICCs - 1; + } + + return((char *) _TypeNamePICC[piccType]); +} // End PICC_GetTypeName() + +/* + * Returns a string pointer to a status code name. + */ +char* MFRC522::GetStatusCodeName(uint8_t code) +{ + return((char *) _ErrorMessage[code]); +} // End GetStatusCodeName() + +/* + * 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). + */ +void MFRC522::MIFARE_SetAccessBits(uint8_t *accessBitBuffer, + uint8_t g0, + uint8_t g1, + uint8_t g2, + uint8_t g3) +{ + uint8_t c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); + uint8_t c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); + uint8_t c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); + + accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF); + accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF); + accessBitBuffer[2] = c3 << 4 | c2; +} // End MIFARE_SetAccessBits() + +///////////////////////////////////////////////////////////////////////////////////// +// Convenience functions - does not add extra functionality +///////////////////////////////////////////////////////////////////////////////////// + +/* + * Returns true if a PICC responds to PICC_CMD_REQA. + * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. + */ +bool MFRC522::PICC_IsNewCardPresent(void) +{ + uint8_t bufferATQA[2]; + uint8_t bufferSize = sizeof(bufferATQA); + uint8_t result = PICC_RequestA(bufferATQA, &bufferSize); + return ((result == STATUS_OK) || (result == STATUS_COLLISION)); +} // End PICC_IsNewCardPresent() + +/* + * Simple wrapper around PICC_Select. + */ +bool MFRC522::PICC_ReadCardSerial(void) +{ + uint8_t result = PICC_Select(&uid); + return (result == STATUS_OK); +} // End PICC_ReadCardSerial() + +
diff -r 203cf529f52a -r 75966605a6a3 MFRC522.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MFRC522.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,787 @@ +/** + * MFRC522.h - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. + * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) + * Created by Miguel Balboa (circuitito.com), Jan, 2012. + * Rewritten by Soren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) + * Ported to mbed by Martin Olejar, Dec, 2013 + * + * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions. + * Search for "mf-rc522" on ebay.com to purchase the MF-RC522 board. + * + * There are three hardware components involved: + * 1) The micro controller: An Arduino + * 2) The PCD (short for Proximity Coupling Device): NXP MFRC522 Contactless Reader IC + * 3) The PICC (short for Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203. + * + * The microcontroller and card reader uses SPI for communication. + * The protocol is described in the MFRC522 datasheet: http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * + * The card reader and the tags communicate using a 13.56MHz electromagnetic field. + * The protocol is defined in ISO/IEC 14443-3 Identification cards -- Contactless integrated circuit cards -- Proximity cards -- Part 3: Initialization and anticollision". + * A free version of the final draft can be found at http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf + * Details are found in chapter 6, Type A: Initialization and anticollision. + * + * If only the PICC UID is wanted, the above documents has all the needed information. + * To read and write from MIFARE PICCs, the MIFARE protocol is used after the PICC has been selected. + * The MIFARE Classic chips and protocol is described in the datasheets: + * 1K: http://www.nxp.com/documents/data_sheet/MF1S503x.pdf + * 4K: http://www.nxp.com/documents/data_sheet/MF1S703x.pdf + * Mini: http://www.idcardmarket.com/download/mifare_S20_datasheet.pdf + * The MIFARE Ultralight chip and protocol is described in the datasheets: + * Ultralight: http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf + * Ultralight C: http://www.nxp.com/documents/short_data_sheet/MF0ICU2_SDS.pdf + * + * MIFARE Classic 1K (MF1S503x): + * Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. + * The blocks are numbered 0-63. + * Block 3 in each sector is the Sector Trailer. See http://www.nxp.com/documents/data_sheet/MF1S503x.pdf sections 8.6 and 8.7: + * Bytes 0-5: Key A + * Bytes 6-8: Access Bits + * Bytes 9: User data + * Bytes 10-15: Key B (or user data) + * Block 0 is read only manufacturer data. + * To access a block, an authentication using a key from the block's sector must be performed first. + * Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11). + * All keys are set to FFFFFFFFFFFFh at chip delivery. + * Warning: Please read section 8.7 "Memory Access". It includes this text: if the PICC detects a format violation the whole sector is irreversibly blocked. + * To use a block in "value block" mode (for Increment/Decrement operations) you need to change the sector trailer. Use PICC_SetAccessBits() to calculate the bit patterns. + * MIFARE Classic 4K (MF1S703x): + * Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. + * The blocks are numbered 0-255. + * The last block in each sector is the Sector Trailer like above. + * MIFARE Classic Mini (MF1 IC S20): + * Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. + * The blocks are numbered 0-19. + * The last block in each sector is the Sector Trailer like above. + * + * MIFARE Ultralight (MF0ICU1): + * Has 16 pages of 4 bytes = 64 bytes. + * Pages 0 + 1 is used for the 7-byte UID. + * Page 2 contains the last chech digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + * Pages 4-15 are read/write unless blocked by the lock bytes in page 2. + * MIFARE Ultralight C (MF0ICU2): + * Has 48 pages of 4 bytes = 64 bytes. + * Pages 0 + 1 is used for the 7-byte UID. + * Page 2 contains the last chech digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + * Pages 4-39 are read/write unless blocked by the lock bytes in page 2. + * Page 40 Lock bytes + * Page 41 16 bit one way counter + * Pages 42-43 Authentication configuration + * Pages 44-47 Authentication key + */ +#ifndef MFRC522_h +#define MFRC522_h + +#include "mbed.h" + +/** +* MFRC522 example +* +* @code +* #include "mbed.h" +* #include "MFRC522.h" +* +* //KL25Z Pins for MFRC522 SPI interface +* #define SPI_MOSI PTC6 +* #define SPI_MISO PTC7 +* #define SPI_SCLK PTC5 +* #define SPI_CS PTC4 +* // KL25Z Pin for MFRC522 reset +* #define MF_RESET PTC3 +* // KL25Z Pins for Debug UART port +* #define UART_RX PTA1 +* #define UART_TX PTA2 +* +* DigitalOut LedRed (LED_RED); +* DigitalOut LedGreen (LED_GREEN); +* +* Serial DebugUART(UART_TX, UART_RX); +* MFRC522 RfChip (SPI_MOSI, SPI_MISO, SPI_SCLK, SPI_CS, MF_RESET); +* +* int main(void) { +* // Set debug UART speed +* DebugUART.baud(115200); +* +* // Init. RC522 Chip +* RfChip.PCD_Init(); +* +* while (true) { +* LedRed = 1; +* LedGreen = 1; +* +* // Look for new cards +* if ( ! RfChip.PICC_IsNewCardPresent()) +* { +* wait_ms(500); +* continue; +* } +* +* LedRed = 0; +* +* // Select one of the cards +* if ( ! RfChip.PICC_ReadCardSerial()) +* { +* wait_ms(500); +* continue; +* } +* +* LedRed = 1; +* LedGreen = 0; +* +* // Print Card UID +* printf("Card UID: "); +* for (uint8_t i = 0; i < RfChip.uid.size; i++) +* { +* printf(" %X02", RfChip.uid.uidByte[i]); +* } +* printf("\n\r"); +* +* // Print Card type +* uint8_t piccType = RfChip.PICC_GetType(RfChip.uid.sak); +* printf("PICC Type: %s \n\r", RfChip.PICC_GetTypeName(piccType)); +* wait_ms(1000); +* } +* } +* @endcode +*/ + +class MFRC522 { +public: + + /** + * MFRC522 registers (described in chapter 9 of the datasheet). + * When using SPI all addresses are shifted one bit left in the "SPI address byte" (section 8.1.2.3) + */ + enum PCD_Register { + // Page 0: Command and status + // 0x00 // reserved for future use + CommandReg = 0x01 << 1, // starts and stops command execution + ComIEnReg = 0x02 << 1, // enable and disable interrupt request control bits + DivIEnReg = 0x03 << 1, // enable and disable interrupt request control bits + ComIrqReg = 0x04 << 1, // interrupt request bits + DivIrqReg = 0x05 << 1, // interrupt request bits + ErrorReg = 0x06 << 1, // error bits showing the error status of the last command executed + Status1Reg = 0x07 << 1, // communication status bits + Status2Reg = 0x08 << 1, // receiver and transmitter status bits + FIFODataReg = 0x09 << 1, // input and output of 64 byte FIFO buffer + FIFOLevelReg = 0x0A << 1, // number of bytes stored in the FIFO buffer + WaterLevelReg = 0x0B << 1, // level for FIFO underflow and overflow warning + ControlReg = 0x0C << 1, // miscellaneous control registers + BitFramingReg = 0x0D << 1, // adjustments for bit-oriented frames + CollReg = 0x0E << 1, // bit position of the first bit-collision detected on the RF interface + // 0x0F // reserved for future use + + // Page 1:Command + // 0x10 // reserved for future use + ModeReg = 0x11 << 1, // defines general modes for transmitting and receiving + TxModeReg = 0x12 << 1, // defines transmission data rate and framing + RxModeReg = 0x13 << 1, // defines reception data rate and framing + TxControlReg = 0x14 << 1, // controls the logical behavior of the antenna driver pins TX1 and TX2 + TxASKReg = 0x15 << 1, // controls the setting of the transmission modulation + TxSelReg = 0x16 << 1, // selects the internal sources for the antenna driver + RxSelReg = 0x17 << 1, // selects internal receiver settings + RxThresholdReg = 0x18 << 1, // selects thresholds for the bit decoder + DemodReg = 0x19 << 1, // defines demodulator settings + // 0x1A // reserved for future use + // 0x1B // reserved for future use + MfTxReg = 0x1C << 1, // controls some MIFARE communication transmit parameters + MfRxReg = 0x1D << 1, // controls some MIFARE communication receive parameters + // 0x1E // reserved for future use + SerialSpeedReg = 0x1F << 1, // selects the speed of the serial UART interface + + // Page 2: Configuration + // 0x20 // reserved for future use + CRCResultRegH = 0x21 << 1, // shows the MSB and LSB values of the CRC calculation + CRCResultRegL = 0x22 << 1, + // 0x23 // reserved for future use + ModWidthReg = 0x24 << 1, // controls the ModWidth setting? + // 0x25 // reserved for future use + RFCfgReg = 0x26 << 1, // configures the receiver gain + GsNReg = 0x27 << 1, // selects the conductance of the antenna driver pins TX1 and TX2 for modulation + CWGsPReg = 0x28 << 1, // defines the conductance of the p-driver output during periods of no modulation + ModGsPReg = 0x29 << 1, // defines the conductance of the p-driver output during periods of modulation + TModeReg = 0x2A << 1, // defines settings for the internal timer + TPrescalerReg = 0x2B << 1, // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. + TReloadRegH = 0x2C << 1, // defines the 16-bit timer reload value + TReloadRegL = 0x2D << 1, + TCntValueRegH = 0x2E << 1, // shows the 16-bit timer value + TCntValueRegL = 0x2F << 1, + + // Page 3:Test Registers + // 0x30 // reserved for future use + TestSel1Reg = 0x31 << 1, // general test signal configuration + TestSel2Reg = 0x32 << 1, // general test signal configuration + TestPinEnReg = 0x33 << 1, // enables pin output driver on pins D1 to D7 + TestPinValueReg = 0x34 << 1, // defines the values for D1 to D7 when it is used as an I/O bus + TestBusReg = 0x35 << 1, // shows the status of the internal test bus + AutoTestReg = 0x36 << 1, // controls the digital self test + VersionReg = 0x37 << 1, // shows the software version + AnalogTestReg = 0x38 << 1, // controls the pins AUX1 and AUX2 + TestDAC1Reg = 0x39 << 1, // defines the test value for TestDAC1 + TestDAC2Reg = 0x3A << 1, // defines the test value for TestDAC2 + TestADCReg = 0x3B << 1 // shows the value of ADC I and Q channels + // 0x3C // reserved for production tests + // 0x3D // reserved for production tests + // 0x3E // reserved for production tests + // 0x3F // reserved for production tests + }; + + // MFRC522 commands Described in chapter 10 of the datasheet. + enum PCD_Command { + PCD_Idle = 0x00, // no action, cancels current command execution + PCD_Mem = 0x01, // stores 25 bytes into the internal buffer + PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number + PCD_CalcCRC = 0x03, // activates the CRC coprocessor or performs a self test + PCD_Transmit = 0x04, // transmits data from the FIFO buffer + PCD_NoCmdChange = 0x07, // no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit + PCD_Receive = 0x08, // activates the receiver circuits + PCD_Transceive = 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission + PCD_MFAuthent = 0x0E, // performs the MIFARE standard authentication as a reader + PCD_SoftReset = 0x0F // resets the MFRC522 + }; + + // Commands sent to the PICC. + enum PICC_Command { + // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) + 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. + 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. + PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. + PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 + PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 1 + PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 1 + PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. + + // The commands used for MIFARE Classic (from http://www.nxp.com/documents/data_sheet/MF1S503x.pdf, Section 9) + // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. + // The read/write commands can also be used for MIFARE Ultralight. + PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A + PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B + PICC_CMD_MF_READ = 0x30, // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. + PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. + PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. + PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. + + // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) + // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. + PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC. + }; + + // MIFARE constants that does not fit anywhere else + enum MIFARE_Misc { + MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. + MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes. + }; + + // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more. + enum PICC_Type { + PICC_TYPE_UNKNOWN = 0, + PICC_TYPE_ISO_14443_4 = 1, // PICC compliant with ISO/IEC 14443-4 + PICC_TYPE_ISO_18092 = 2, // PICC compliant with ISO/IEC 18092 (NFC) + PICC_TYPE_MIFARE_MINI = 3, // MIFARE Classic protocol, 320 bytes + PICC_TYPE_MIFARE_1K = 4, // MIFARE Classic protocol, 1KB + PICC_TYPE_MIFARE_4K = 5, // MIFARE Classic protocol, 4KB + PICC_TYPE_MIFARE_UL = 6, // MIFARE Ultralight or Ultralight C + PICC_TYPE_MIFARE_PLUS = 7, // MIFARE Plus + PICC_TYPE_TNP3XXX = 8, // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure + PICC_TYPE_NOT_COMPLETE = 255 // SAK indicates UID is not complete. + }; + + // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. + enum StatusCode { + STATUS_OK = 1, // Success + STATUS_ERROR = 2, // Error in communication + STATUS_COLLISION = 3, // Collision detected + STATUS_TIMEOUT = 4, // Timeout in communication. + STATUS_NO_ROOM = 5, // A buffer is not big enough. + STATUS_INTERNAL_ERROR = 6, // Internal error in the code. Should not happen ;-) + STATUS_INVALID = 7, // Invalid argument. + STATUS_CRC_WRONG = 8, // The CRC_A does not match + STATUS_MIFARE_NACK = 9 // A MIFARE PICC responded with NAK. + }; + + // A struct used for passing the UID of a PICC. + typedef struct { + uint8_t size; // Number of bytes in the UID. 4, 7 or 10. + uint8_t uidByte[10]; + uint8_t sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. + } Uid; + + // A struct used for passing a MIFARE Crypto1 key + typedef struct { + uint8_t keyByte[MF_KEY_SIZE]; + } MIFARE_Key; + + // Member variables + Uid uid; // Used by PICC_ReadCardSerial(). + + // Size of the MFRC522 FIFO + static const uint8_t FIFO_SIZE = 64; // The FIFO is 64 bytes. + + /** + * MFRC522 constructor + * + * @param mosi SPI MOSI pin + * @param miso SPI MISO pin + * @param sclk SPI SCLK pin + * @param cs SPI CS pin + * @param reset Reset pin + */ + MFRC522(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName reset); + + /** + * MFRC522 destructor + */ + ~MFRC522(); + + + // ************************************************************************************ + //! @name Functions for manipulating the MFRC522 + // ************************************************************************************ + //@{ + + /** + * Initializes the MFRC522 chip. + */ + void PCD_Init (void); + + /** + * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. + */ + void PCD_Reset (void); + + /** + * Turns the antenna on by enabling pins TX1 and TX2. + * After a reset these pins disabled. + */ + void PCD_AntennaOn (void); + + /** + * Writes a byte to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + * + * @param reg The register to write to. One of the PCD_Register enums. + * @param value The value to write. + */ + void PCD_WriteRegister (uint8_t reg, uint8_t value); + + /** + * Writes a number of bytes to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + * + * @param reg The register to write to. One of the PCD_Register enums. + * @param count The number of bytes to write to the register + * @param values The values to write. Byte array. + */ + void PCD_WriteRegister (uint8_t reg, uint8_t count, uint8_t *values); + + /** + * Reads a byte from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + * + * @param reg The register to read from. One of the PCD_Register enums. + * @returns Register value + */ + uint8_t PCD_ReadRegister (uint8_t reg); + + /** + * Reads a number of bytes from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + * + * @param reg The register to read from. One of the PCD_Register enums. + * @param count The number of bytes to read. + * @param values Byte array to store the values in. + * @param rxAlign Only bit positions rxAlign..7 in values[0] are updated. + */ + void PCD_ReadRegister (uint8_t reg, uint8_t count, uint8_t *values, uint8_t rxAlign = 0); + + /** + * Sets the bits given in mask in register reg. + * + * @param reg The register to update. One of the PCD_Register enums. + * @param mask The bits to set. + */ + void PCD_SetRegisterBits(uint8_t reg, uint8_t mask); + + /** + * Clears the bits given in mask from register reg. + * + * @param reg The register to update. One of the PCD_Register enums. + * @param mask The bits to clear. + */ + void PCD_ClrRegisterBits(uint8_t reg, uint8_t mask); + + /** + * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. + * + * @param data Pointer to the data to transfer to the FIFO for CRC calculation. + * @param length The number of bytes to transfer. + * @param result Pointer to result buffer. Result is written to result[0..1], low byte first. + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PCD_CalculateCRC (uint8_t *data, uint8_t length, uint8_t *result); + + /** + * Executes the Transceive command. + * CRC validation can only be done if backData and backLen are specified. + * + * @param sendData Pointer to the data to transfer to the FIFO. + * @param sendLen Number of bytes to transfer to the FIFO. + * @param backData NULL or pointer to buffer if data should be read back after executing the command. + * @param backLen Max number of bytes to write to *backData. Out: The number of bytes returned. + * @param validBits The number of valid bits in the last byte. 0 for 8 valid bits. Default NULL. + * @param rxAlign Defines the bit position in backData[0] for the first bit received. Default 0. + * @param checkCRC True => The last two bytes of the response is assumed to be a CRC_A that must be validated. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PCD_TransceiveData (uint8_t *sendData, + uint8_t sendLen, + uint8_t *backData, + uint8_t *backLen, + uint8_t *validBits = NULL, + uint8_t rxAlign = 0, + bool checkCRC = false); + + + /** + * Transfers data to the MFRC522 FIFO, executes a commend, waits for completion and transfers data back from the FIFO. + * CRC validation can only be done if backData and backLen are specified. + * + * @param command The command to execute. One of the PCD_Command enums. + * @param waitIRq The bits in the ComIrqReg register that signals successful completion of the command. + * @param sendData Pointer to the data to transfer to the FIFO. + * @param sendLen Number of bytes to transfer to the FIFO. + * @param backData NULL or pointer to buffer if data should be read back after executing the command. + * @param backLen In: Max number of bytes to write to *backData. Out: The number of bytes returned. + * @param validBits In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. + * @param rxAlign In: Defines the bit position in backData[0] for the first bit received. Default 0. + * @param checkCRC In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PCD_CommunicateWithPICC(uint8_t command, + uint8_t waitIRq, + uint8_t *sendData, + uint8_t sendLen, + uint8_t *backData = NULL, + uint8_t *backLen = NULL, + uint8_t *validBits = NULL, + uint8_t rxAlign = 0, + bool checkCRC = false); + + /** + * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PICC_RequestA (uint8_t *bufferATQA, uint8_t *bufferSize); + + /** + * 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. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PICC_WakeupA (uint8_t *bufferATQA, uint8_t *bufferSize); + + /** + * Transmits REQA or WUPA commands. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @param command The command to send - PICC_CMD_REQA or PICC_CMD_WUPA + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PICC_REQA_or_WUPA (uint8_t command, uint8_t *bufferATQA, uint8_t *bufferSize); + + /** + * Transmits SELECT/ANTICOLLISION commands to select a single PICC. + * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). + * On success: + * - 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.) + * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. + * + * A PICC UID consists of 4, 7 or 10 bytes. + * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: + * + * UID size Number of UID bytes Cascade levels Example of PICC + * ======== =================== ============== =============== + * single 4 1 MIFARE Classic + * double 7 2 MIFARE Ultralight + * triple 10 3 Not currently in use? + * + * + * @param uid Pointer to Uid struct. Normally output, but can also be used to supply a known UID. + * @param validBits The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PICC_Select (Uid *uid, uint8_t validBits = 0); + + /** + * Instructs a PICC in state ACTIVE(*) to go to state HALT. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PICC_HaltA (void); + + // ************************************************************************************ + //@} + + + // ************************************************************************************ + //! @name Functions for communicating with MIFARE PICCs + // ************************************************************************************ + //@{ + + /** + * Executes the MFRC522 MFAuthent command. + * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. + * 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. + * For use with MIFARE Classic PICCs. + * The PICC must be selected - ie in state ACTIVE(*) - before calling this function. + * Remember to call PCD_StopCrypto1() after communicating with the authenticated PICC - otherwise no new communications can start. + * + * All keys are set to FFFFFFFFFFFFh at chip delivery. + * + * @param command PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B + * @param blockAddr The block number. See numbering in the comments in the .h file. + * @param key Pointer to the Crypteo1 key to use (6 bytes) + * @param uid Pointer to Uid struct. The first 4 bytes of the UID is used. + * + * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. + */ + uint8_t PCD_Authenticate (uint8_t command, uint8_t blockAddr, MIFARE_Key *key, Uid *uid); + + /** + * Used to exit the PCD from its authenticated state. + * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. + */ + void PCD_StopCrypto1 (void); + + /** + * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. + * + * For MIFARE Classic the sector containing the block must be authenticated before calling this function. + * + * For MIFARE Ultralight only addresses 00h to 0Fh are decoded. + * The MF0ICU1 returns a NAK for higher addresses. + * The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument. + * For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned. + * A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages 0Eh, 0Fh, 00h and 01h are returned. + * + * The buffer must be at least 18 bytes because a CRC_A is also returned. + * Checks the CRC_A before returning STATUS_OK. + * + * @param blockAddr MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. + * @param buffer The buffer to store the data in + * @param bufferSize Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t MIFARE_Read (uint8_t blockAddr, uint8_t *buffer, uint8_t *bufferSize); + + /** + * Writes 16 bytes to the active PICC. + * + * For MIFARE Classic the sector containing the block must be authenticated before calling this function. + * + * For MIFARE Ultralight the opretaion is called "COMPATIBILITY WRITE". + * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) + * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. + * + * @param blockAddr MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. + * @param buffer The 16 bytes to write to the PICC + * @param bufferSize Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t MIFARE_Write (uint8_t blockAddr, uint8_t *buffer, uint8_t bufferSize); + + /** + * Writes a 4 byte page to the active MIFARE Ultralight PICC. + * + * @param page The page (2-15) to write to. + * @param buffer The 4 bytes to write to the PICC + * @param bufferSize Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t MIFARE_UltralightWrite(uint8_t page, uint8_t *buffer, uint8_t bufferSize); + + /** + * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @param blockAddr The block (0-0xff) number. + * @param delta This number is subtracted from the value of block blockAddr. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t MIFARE_Decrement (uint8_t blockAddr, uint32_t delta); + + /** + * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @param blockAddr The block (0-0xff) number. + * @param delta This number is added to the value of block blockAddr. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t MIFARE_Increment (uint8_t blockAddr, uint32_t delta); + + /** + * MIFARE Restore copies the value of the addressed block into a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @param blockAddr The block (0-0xff) number. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t MIFARE_Restore (uint8_t blockAddr); + + /** + * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * + * @param blockAddr The block (0-0xff) number. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t MIFARE_Transfer (uint8_t blockAddr); + + // ************************************************************************************ + //@} + + + // ************************************************************************************ + //! @name Support functions + // ************************************************************************************ + //@{ + + /** + * Wrapper for MIFARE protocol communication. + * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. + * + * @param sendData Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. + * @param sendLen Number of bytes in sendData. + * @param acceptTimeout True => A timeout is also success + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t PCD_MIFARE_Transceive(uint8_t *sendData, uint8_t sendLen, bool acceptTimeout = false); + + /** + * Translates the SAK (Select Acknowledge) to a PICC type. + * + * @param sak The SAK byte returned from PICC_Select(). + * + * @return PICC_Type + */ + uint8_t PICC_GetType (uint8_t sak); + + /** + * Returns a string pointer to the PICC type name. + * + * @param type One of the PICC_Type enums. + * + * @return A string pointer to the PICC type name. + */ + char* PICC_GetTypeName (uint8_t type); + + /** + * Returns a string pointer to a status code name. + * + * @param code One of the StatusCode enums. + * + * @return A string pointer to a status code name. + */ + char* GetStatusCodeName (uint8_t code); + + /** + * 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). + * + * @param accessBitBuffer Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. + * @param g0 Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + * @param g1 Access bits [C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + * @param g2 Access bits [C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + * @param g3 Access bits [C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) + */ + void MIFARE_SetAccessBits (uint8_t *accessBitBuffer, + uint8_t g0, + uint8_t g1, + uint8_t g2, + uint8_t g3); + + // ************************************************************************************ + //@} + + + // ************************************************************************************ + //! @name Convenience functions - does not add extra functionality + // ************************************************************************************ + //@{ + + /** + * Returns true if a PICC responds to PICC_CMD_REQA. + * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. + * + * @return bool + */ + bool PICC_IsNewCardPresent(void); + + /** + * Simple wrapper around PICC_Select. + * Returns true if a UID could be read. + * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. + * The read UID is available in the class variable uid. + * + * @return bool + */ + bool PICC_ReadCardSerial (void); + + // ************************************************************************************ + //@} + + +private: + SPI m_SPI; + DigitalOut m_CS; + DigitalOut m_RESET; + + /** + * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. + * + * @param command The command to use + * @param blockAddr The block (0-0xff) number. + * @param data The data to transfer in step 2 + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + uint8_t MIFARE_TwoStepHelper(uint8_t command, uint8_t blockAddr, uint32_t data); +}; + +#endif + +
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/Fsm.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/Fsm.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,45 @@ +#include <Moving.h> +#include <Stacionary.h> +#include <Fsm.h> + +FsmStates::FsmStates() : movingState_(new Moving(*this,timer)), + stacionaryState_(new Stacionary(*this,timer)) +{ + currentState(stacionaryState()); +} + + + +State* FsmStates::stacionaryState() +{ + return stacionaryState_; +} + +State* FsmStates::movingState() +{ + return movingState_; +} + +State* FsmStates::currentState() +{ + return currentState_; +} +void FsmStates::currentState(State* state) +{ + currentState_ = state; +} + +FsmStates::~FsmStates() +{ + delete stacionaryState_; + delete movingState_; +} + +Fsm::Fsm() : states_() +{ + + +} + + +
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/Moving.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/Moving.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,14 @@ +#include <Moving.h> +#include <Events.h> +#include <Fsm.h> + +Moving::Moving(FsmStates& states, Timer& timer): State(states,timer) {} + +State* Moving::handle(Stop& event) +{ + printf("Odoh u ********************************** STACIONARY STATE\r\n"); + return states_.stacionaryState(); +} + + +
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/Stacionary.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/Stacionary.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,13 @@ +#include <Stacionary.h> +#include <Events.h> +#include <Fsm.h> + +Stacionary::Stacionary(FsmStates& states, Timer& timer): State(states, timer) {} + +State* Stacionary::handle(Move& event) +{ + printf("Odoh u ********************************** MOVING STATE\r\n"); + return states_.movingState(); +} + +
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/State.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/State.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,9 @@ +#include <Fsm.h> +#include <State.h> + +State::State(FsmStates& states,Timer& timer): states_(states),stateTimer_(timer){} + +State* State::handle(Stop& event) { return states_.currentState(); } +State* State::handle(Move& event) { return states_.currentState(); } + +
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/include/Events.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/include/Events.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,7 @@ +#ifndef MOTIONFSM_EVENTS_H_ +#define MOTIONFSM_EVENTS_H_ + +struct Stop {}; +struct Move {}; + +#endif /* ifndef MOTIONFSM_EVENTS_H_ */
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/include/Fsm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/include/Fsm.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,48 @@ +#ifndef MOTIONFSM_FSM_H_ +#define MOTIONFSM_FSM_H_ +#include <State.h> +#include "mbed.h" + +#include <Events.h> + +struct Stop; +struct Move; + +class FsmStates { + public: + FsmStates(); + + State* movingState(); + State* stacionaryState(); + State* currentState(); + + void currentState(State* state); + + ~FsmStates(); + + private: + + State* movingState_; + State* stacionaryState_; + State* currentState_; + + Timer timer; + +}; + +class Fsm { + public: + Fsm(); + template <typename Event> + void handle(Event& event){ + State* state = states_.currentState()->handle(event); + states_.currentState(state); + } + + virtual ~Fsm() {} + FsmStates states_; + private: + + +}; +#endif /* ifndef MOTIONFSM_FSM_H_ */
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/include/Moving.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/include/Moving.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,17 @@ +#ifndef FSM_MOVING_H_ +#define FSM_MOVING_H_ + +#include <State.h> +struct Stop; +class FsmStates; + +class Moving : public State { + public: + Moving(FsmStates& states, Timer& timer); + State* handle(Stop& event); + + private: + unsigned timeForLongPress_; +}; + +#endif /* ifndef FSM_CONNECTED_TRANSIOTION_H_ */
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/include/Stacionary.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/include/Stacionary.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,15 @@ +#ifndef FSM_STACIONARY_H_ +#define FSM_STACIONARY_H_ + +#include <State.h> +#include "mbed.h" + +struct Move; +class FsmStates; + +class Stacionary : public State { + public: + Stacionary(FsmStates& states, Timer& timer); + State* handle(Move& event); +}; +#endif /* ifndef FSM_DISCONNECTED_H_ */
diff -r 203cf529f52a -r 75966605a6a3 MotionFSM/include/State.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotionFSM/include/State.h Fri Nov 10 15:35:26 2017 +0000 @@ -0,0 +1,20 @@ +#ifndef FSM_STATE_H_ +#define FSM_STATE_H_ +#include "mbed.h" +struct Stop; +struct Move; + +class FsmStates; + +class State { + public: + State(FsmStates& state, Timer& timer); + virtual State* handle(Stop& event); + virtual State* handle(Move& event); + virtual ~State() {} + + protected: + FsmStates& states_; + Timer& stateTimer_; +}; +#endif /* ifndef FSM_STATE_H_ */
diff -r 203cf529f52a -r 75966605a6a3 img/uvision.png
diff -r 203cf529f52a -r 75966605a6a3 main.cpp --- a/main.cpp Thu Sep 14 16:50:16 2017 +0000 +++ b/main.cpp Fri Nov 10 15:35:26 2017 +0000 @@ -1,191 +1,425 @@ #include "mbed.h" -#include "SDFileSystem.h" -#include "SIM5320.h" #include "MPU6050.h" -#include "SensorBoards.h" -#include "WakeUp.h" +#include "I2Cdev.h" +#include <CaseFsm.h> +#include <CaseEvents.h> +#include <Fsm.h> +#include <Events.h> +#include "MFRC522.h" + +#include "log.h" +#include "DataLogger.h" + +#define CURRENT_TIME 1510164886 + +#define SPI_MOSI PA_7 +#define SPI_MISO PA_6 +#define SPI_SCK PA_5 +#define SPI_CS PA_8 + + + + +int cbx[20]; +int cby[20]; +int cbz[20]; + + + Log mojlog; + DataLogger <Log> mojlogger; + // mojlogger.print(); + // mojlogger.savelog(mojlog); + // mojlogger.print(); -uint8_t write_sd_buffer[12]; -void create_sensor_data(uint8_t *write_sd_buffer, uint8_t *sensor_board_readings, uint8_t b_id, uint8_t s_id); -//SIM5320 sim5320(PA_9,PA_10); -DigitalOut SIM5320_PWR(PA_1); -DigitalOut SIM_PWR_KEY(PA_15); -InterruptIn mpuInterrupt(PB_6); -uint8_t sensor_board_readings[4]; -bool test = false; -// MOSI, MISO, SCLK, CS, name -SDFileSystem sd(PA_7, PA_6, PA_5, PA_4, "sd"); +DigitalOut rfidpower(PA_12); + +int x=0; + +DigitalOut led1(LED1); +//Serial pc(USBTX,USBRX); + +bool XnegMD, XposMD, YnegMD, YposMD, ZnegMD, ZposMD; +int8_t threshold; +bool zero_detect; +float temp; + +//void flip(){test=1;} +Fsm* motionFsm; +CaseFsm* caseFsm; +SwitchPosition switchPosition; +RFIDEvent rfidEvent; +TimerEnd timerEnd; +Move move; +Stop stop; +uint8_t print=0; +//#include "PinDetect.h" + +InterruptIn button1(PD_2); +InterruptIn button2(PC_11); +InterruptIn button3(PC_10); +InterruptIn button4(PC_12);//PRINT + + +InterruptIn mpuInterrupt(PB_5); -int go_to_sleep = 0; +volatile uint8_t test = 0; +volatile uint8_t mot=0; + +bool flag = false; +bool DeviceIsFull = false; + +void button1PressedCallback() +{ + // printf("button1\n"); + caseFsm->handle(switchPosition); +} + +void button2PressedCallback() +{ + //printf("button2\n"); + caseFsm->handle(rfidEvent); + +} +void button3PressedCallback() +{ + //printf("button3\n"); + caseFsm->handle(timerEnd); + +} + +void button4PressedCallback() +{ + print=1; + +} + void mpuInterruptCallback() { - test=!test; - -} -void rtc_wakeup() -{ - go_to_sleep = 1; + test=1; } + MPU6050 mpu; +uint8_t nomotion=0; +uint8_t motionlogged=0; + +void getXYZMotions() +{ + wait(0.1); + uint8_t motionByte = mpu.readThisByte(MPU6050_RA_MOT_DETECT_STATUS); + // motionLog.motionByte = motionByte; + //motionLog.timeStamp = time(NULL); + //mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); + uint8_t b; + bool zero = false; + char x,y,z; + int mx=0; + int my=0; + int mz=0; + x=y=z='0'; + for(int i = 0; i<=7; i++) { + b = motionByte & (1<<i); + if(i==0 && b==1) { + //time_t sec = time(NULL); + //printf("Current time is: %s\r\n", ctime(&sec)); + zero = true; + printf("ZERO MOTION detected.\r\n"); + //pc.printf(" %c %c %c | ",x,y,z); + //pc.printf("%d;%d;%d;%d;%d;%d ZERO DETECTED\r\n",ax,ay,az,gx,gy,gz); + } + if(i==1 && b==2) { + printf("This should not happen :).\r\n"); + } + if(i==6 && b==64) { + x='+'; + mx=1; + printf("X POSITIVE motion detected.\r\n"); + } + if(i==7 && b==128) { + x='-'; + mx=1; + printf("X NEGATIVE motion detected.\r\n"); + } + if(i==4 && b==16) { + y='+'; + my=1; + printf("Y POSITIVE motion detected.\r\n"); + } + if(i==5 && b==32) { + y='-'; + my=1; + printf("Y NEGATIVE motion detected.\r\n"); + } + if(i==2 && b==4) { + z='+'; + mz=1; + printf("Z POSITIVE motion detected.\r\n"); + } + if(i==3 && b==8) { + z='-'; + mz=1; + printf("Z NEGATIVE motion detected.\r\n"); + } + } + printf("%d %d %d\r\n",mx,my,mz); + + if(mx==0 && my==0 && mz==0) { + nomotion++; + if(nomotion==3 && motionlogged==0) { + printf("SAVE NO motion\n"); + //motionFsm->handle(stop); + mojlog.setLog(900,0,0,900); + mojlogger.savelog(mojlog); + motionlogged=1; + nomotion=0; + } + } + + else if(motionlogged==1) { + printf("SAVE motion\n"); + motionlogged=0; + //motionFsm->handle(move); + mojlog.setLog(2222,0,1,2222); + mojlogger.savelog(mojlog); + nomotion=0; + } + + + + + + + + +} + + + + + + + +MFRC522 RfChip(SPI_MOSI, SPI_MISO, SPI_SCK, SPI_CS, PB_4); + int main() { -/////////////////////////////////////////////////MPU_DEEP_SLEEP_WAKEUP//////////////////////////// -/* + + rfidpower=1; + + + + CaseFsm caseF; + caseFsm = &caseF; + + //mpuInterrupt.mode(PullUp); + button1.mode(PullUp); + button2.mode(PullUp); + button3.mode(PullUp); + button4.mode(PullUp); //print + button4.fall(button4PressedCallback); + button3.fall(button3PressedCallback); + button2.fall(button2PressedCallback); + button1.fall(&button1PressedCallback); + + //mpuInterrupt.fall(mpuInterruptCallback); + + RfChip.PCD_Init(); + wait(0.2); + + + + + + + ////////////////////MPU INTERUPT RADI////////////////////////////////////// + mpuInterrupt.fall(mpuInterruptCallback); - wait(2); + wait(0.2); mpu.calibrate(accelBias, gyroBias); mpu.initialize(); mpu.setSleepEnabled(0); - wait(2); + wait(0.2); mpu.setIntFreefallEnabled(1); - mpu.setIntZeroMotionEnabled(0); + mpu.setIntZeroMotionEnabled(1); mpu.setIntMotionEnabled(1); printf("Setting mpu parameters...\r\n"); mpu.setMotionDetectionThreshold(1); mpu.setMotionDetectionDuration(15); + mpu.setZeroMotionDetectionThreshold(1); + mpu.setZeroMotionDetectionDuration(5); - while(1){ - printf("sleep\r\n"); - deepsleep(); - if(test){ - wait(1); - printf("Desio se motion\r\n"); - test = 0; - } - } -*/ - -/////////////////////////////////////////////////////////////////////////////////////////// - - // wait(3); - // SIM5320_PWR = 1; - // SIM_PWR_KEY = 1; - // wait(1); - // SIM_PWR_KEY = 0; - /* - wait(25); + + ///////////////////////////////////////////////////////////////////////////// + /* + mpu.calibrate(accelBias, gyroBias); + mpu.initialize(); + mpu.setSleepEnabled(0); + mpu.setIntDMPEnabled(1); + + uint8_t pinValue = mpu.getIntEnabled(); + printf("Is int enabled? : %d\n", pinValue); + printf("MPU6050 testing connection \n"); + + wait(2); - printf("Starting...\r\n"); - printf("Starting...\r\n"); - printf("Starting...\r\n"); - printf("Starting...\r\n"); - sim5320.sendCommand("AT+CNUM",2); - printf("Checking credit balance...\r\n"); - sim5320.sendCommand("AT+CUSD=1, \"*100#\",15",5); - //Testing GPS - - sim5320.enableGPS(true); - wait(30); - - //Gettting GPS location - sim5320.sendCommand("AT+CGPSINFO",2); - sim5320.sendCommand("AT+CGPSINFO",2); - sim5320.sendCommand("AT+CGPSINFO",2); + bool mpu6050TestResult = mpu.testConnection(); + if(mpu6050TestResult) { + printf("MPU6050 test passed \n"); + } else { + printf("MPU6050 test failed \n"); + } + mpu.setIntEnabled(1); + //mpu.setIntFreefallEnabled(1); + //mpu.setIntZeroMotionEnabled(0); + //mpu.setIntMotionEnabled(1); + mpu.setDHPFMode(1); - //Testing network connection and disconnection - sim5320.connect("active.bhmobile.ba","",""); - wait(2); - if(sim5320.disconnect()) { - printf("Disconnected\r\n"); - } + //******************************************************************************************************************************* + + printf("\n\n\n"); + uint8_t motionDetectionThreshold = mpu.getMotionDetectionThreshold(); + //printf("MPU6050 motion detection threshold is: %d \r\n", motionDetectionThreshold); + //printf("MPU6050 set motion detection threshold to 2\r\n"); + mpu.setMotionDetectionThreshold(1); + motionDetectionThreshold = mpu.getMotionDetectionThreshold(); + //printf("MPU6050 motion detection threshold now is: %d \r\n", motionDetectionThreshold); + printf("Motion detection threshold is: %d \r\n", motionDetectionThreshold); + uint8_t motionDetectionDuration = mpu.getMotionDetectionDuration(); + //printf("MPU6050 motion detection duration is: %d \r\n", motionDetectionDuration); + //printf("MPU6050 set motion detection duration to 40\r\n"); + mpu.setMotionDetectionDuration(20); + motionDetectionDuration = mpu.getMotionDetectionDuration(); + //printf("MPU6050 motion detection duration now is: %d \r\n", motionDetectionDuration); + printf("Motion detection duration is: %d \r\n", motionDetectionDuration); + + //******************************************************************************************************************************* - else { - printf("Still connected or error occured!\r\n"); - } + //printf("\n\n\n"); + uint8_t zeroMotionDetectionThreshold = mpu.getZeroMotionDetectionThreshold(); + //printf("MPU6050 zero motion detection threshold is: %d \r\n", zeroMotionDetectionThreshold); + //printf("MPU6050 set zero motion detection threshold to 2\r\n"); + mpu.setZeroMotionDetectionThreshold(2); + zeroMotionDetectionThreshold = mpu.getZeroMotionDetectionThreshold(); + //printf("MPU6050 zero motion detection threshold now is: %d \r\n", zeroMotionDetectionThreshold); + printf("Zero motion detection threshold is: %d \r\n", zeroMotionDetectionThreshold); + //printf("\n\n\n"); + int8_t zeroMotionDetectionDuration = mpu.getZeroMotionDetectionDuration(); + //printf("MPU6050 zero motion detection duration is: %d \r\n", zeroMotionDetectionDuration); + //printf("MPU6050 set zero motion detection duration to 1\r\n"); + mpu.setZeroMotionDetectionDuration(1); + zeroMotionDetectionDuration = mpu.getZeroMotionDetectionDuration(); + //printf("MPU6050 zero motion detection duration now is: %d \r\n", zeroMotionDetectionDuration); + printf("Zero motion detection duration is: %d \r\n", zeroMotionDetectionDuration); + + //******************************************************************************************************************************* + + //printf("\n\n\n"); + int8_t freefallDetectionThreshold = mpu.getFreefallDetectionThreshold(); + //printf("MPU6050 freefall motion detection threshold is: %d \r\n", freefallDetectionThreshold); + //printf("MPU6050 set freefall motion detection threshold to 40\r\n"); + //mpu.setFreefallDetectionThreshold(2); + freefallDetectionThreshold = mpu.getFreefallDetectionThreshold(); + //printf("MPU6050 freefall motion detection threshold now is: %d \r\n", freefallDetectionThreshold); + printf("Freefall motion detection threshold is: %d \r\n", freefallDetectionThreshold); + //printf("\n\n\n"); + int8_t freefallDetectionDuration = mpu.getFreefallDetectionDuration(); + //printf("MPU6050 freefall motion detection duration is: %d \r\n", freefallDetectionDuration); + //printf("MPU6050 set freefall motion detection duration to 40\r\n"); + //mpu.setFreefallDetectionDuration(40); + freefallDetectionDuration = mpu.getFreefallDetectionDuration(); + //printf("MPU6050 freefall motion detection duration now is: %d \r\n", freefallDetectionDuration); + printf("Freefall motion detection duration is: %d \r\n", freefallDetectionDuration); + printf("\n\n\n"); + printf("MPU6050 test\n\n"); + + + //******************************************************************************************************************************* + */ while(1) { - wait(1); - sim5320.sendCommand("AT",1); - } - */ - - - -/////////////////////////////////////////////////RTC_DEEP_SLEEP_WAKEUP//////////////////////////// - /* - set_time(1495040081); // Set RTC time to Wed, 28 Oct 2009 11:35:37 - WakeUp::attach(&rtc_wakeup); - WakeUp::calibrate(); - while (1) { - WakeUp::set_ms(5000); - time_t seconds = time(NULL); - printf("Time = %s\n", ctime(&seconds)); - wait(1); - printf("sleep\n"); - deepsleep(); - if (go_to_sleep == 1) { - wait(1); - printf("Wake_up\n"); - go_to_sleep=0; + + if(test==1) { + wait(0.2); + test = 0; + getXYZMotions(); + //mot=2; } - } -*/ -//////////////////////////////////////////////////////////////////////////////////////////////// + + + + if(print==1) { + mojlogger.print(); + print=0; + } -//////////////////////////////////////////////CODE FOR SENDOR_BOARDS//////////////////////////////////////////////////////////////// - /* - uint8_t IDBuffer[8]; - uint8_t IDMeasure[4]; - float measure=0.0; - float measure2=0.0; - +//printf("caseFsm->DeviceInFullState()=%d\n",caseFsm->DeviceInFullState()); + //This goes into caseFsm, but for now we are testing in main.cpp + if(caseFsm->DeviceInFullState()) { + printf("Waiting 5 seconds for RFID event...\r\n"); + while (caseFsm->DeviceInFullState()) { + // Look for new cards + + + if ( ! RfChip.PICC_IsNewCardPresent()) { + wait_ms(500); + printf("!RfChip.PICC_IsNewCardPresent()\n"); + continue; + } + + // Select one of the cards + if ( ! RfChip.PICC_ReadCardSerial()) { + printf("!RfChip.PICC_ReadCardSerial()\n"); + wait_ms(500); + continue; + } - SensorBoards B1; - B1.getSensorReadings(0x4a,0x10,IDMeasure); - printf("number_of__boards= %d\n",B1.numberOfBoards); - B1.sensorBoardScanner(); - printf("SnesorBoardAdress= %d\n",B1.boards[0].I2CAddress); - B1.getSensorNumbers(); - printf("SensorBoardsensors= %d\n",B1.boards[0].numberOfSensors); - B1.getSensorIDs(); - printf("SensorBoardIDs=%d %d \n",B1.boards[0].sensorIDs[0],B1.boards[0].sensorIDs[1]); - - B1.getSensorReadings(0x4a,0x10,IDMeasure); - - for(int k=0; k<1; k++) { - printf("broj senzora %d \n",B1.boards[k].numberOfSensors); - for(int i=0; i<B1.boards[k].numberOfSensors; i++){ - B1.getSensorReadings(B1.boards[k].I2CAddress,B1.boards[k].sensorIDs[i],IDMeasure); - printf("measure=%d %d %d %d keaj\n",IDMeasure[0],IDMeasure[1],IDMeasure[2],IDMeasure[3]); - //create_sensor_data(write_sd_buffer,IDMeasure,B1.boards[k].I2CAddress,B1.boards[k].sensorIDs[i]); + // Print Card UID + printf("Card UID: "); + for (uint8_t i = 0; i < RfChip.uid.size; i++) { + rfidEvent.uid[i] = RfChip.uid.uidByte[i]; + printf("%X ", RfChip.uid.uidByte[i]); } + rfidEvent.size = RfChip.uid.size; + printf("\n\r"); + + // Print Card type + uint8_t piccType = RfChip.PICC_GetType(RfChip.uid.sak); + printf("PICC Type: %s \n\r", RfChip.PICC_GetTypeName(piccType)); + wait_ms(1000); + rfidEvent.cardType = piccType; + //phone inserted + caseFsm->handle(rfidEvent); + printf("_%d_\n",caseFsm->DeviceInPhoneDetectedState()); + if(caseFsm->DeviceInPhoneDetectedState()) { + wait(0.3); + x++; + printf("snimi\n"); + mojlog.setLog(600,1,0,600); + mojlogger.savelog(mojlog); + ;//LOGOVATI INSERTED + + } + + } + if(caseFsm->DeviceInDeviceEmptyState()) { + wait(0.3); + printf("ne snimi\n"); + x++; //LOGOVATI REMOVED + mojlog.setLog(400,0,0,400); + mojlogger.savelog(mojlog); + } } - for(int i=0;i<12;i++) - printf("_%d\n",write_sd_buffer[i]); - */ -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - - -return 0; -} -void create_sensor_data(uint8_t *write_sd_buffer, uint8_t *sensor_board_readings, uint8_t b_id, uint8_t s_id) -{ - //pcf8563_read(&rtc); - write_sd_buffer[0]=0; - write_sd_buffer[1]=1; - write_sd_buffer[2]=2; - write_sd_buffer[3]=3; - write_sd_buffer[4]=4; - write_sd_buffer[5] =b_id; //sensor boaard id - write_sd_buffer[6] =s_id; //sensor id - write_sd_buffer[7] =sensor_board_readings[3]; //sensor reading - write_sd_buffer[8] =sensor_board_readings[2]; //sensor reading - write_sd_buffer[9] =sensor_board_readings[1]; //sensor reading - write_sd_buffer[10]=sensor_board_readings[0]; //sensor reading - write_sd_buffer[11]=0xFF; - write_sd_buffer[12]=0xFF; + } } \ No newline at end of file