Compare commits
5 commits
a438cb13d4
...
5618f52a0e
Author | SHA1 | Date | |
---|---|---|---|
5618f52a0e | |||
38e4982aac | |||
b5b5268b79 | |||
324cf50653 | |||
73ceeb493c |
6 changed files with 257 additions and 11 deletions
55
README.md
55
README.md
|
@ -216,6 +216,61 @@ Octet 1
|
|||
`D`: bit 7, 1 si enlevé, 0 sinon
|
||||
`C`: bit 6, 1 si personnage, 0 si année
|
||||
|
||||
### Requête d'information pour la programmation de tag
|
||||
|
||||
**En-tête** : `?`
|
||||
**Longueur** : 0 octets
|
||||
**Description** : Message **vide** envoyé du logiciel vers l'objet pour
|
||||
demander les informations complètes qui permettront de programmer les tags.
|
||||
Il attend une réponse de l'objet, décrit dans la catégorie suivante.
|
||||
|
||||
### Réponse d'information pour la programmation de tag
|
||||
|
||||
**En-tête** : `!`
|
||||
**Longueur** : 1 + 5*n octets, n le nombre de figurines présentes (>=0)
|
||||
**Description** : Message de réponse à la requête d'information pour la programmation
|
||||
des tags.
|
||||
Le premier octet est fixé et indique le nombre de figurines présentes, et donc
|
||||
le nombre d'entrées dans le message.
|
||||
Ensuite, chaque entrée est constituée de l'ID actuel, sur un octet, puis de l'UID
|
||||
du tag, sur quatre octets.
|
||||
C'est l'UID qui est utilisé dans la demande de programmation pour sélectionner le tag.
|
||||
|
||||
#### Schéma
|
||||
|
||||
```
|
||||
│Optional, N records
|
||||
┌─┼────┬──┬────┬──┐
|
||||
│N│UID1│I1│UID2│I2│...
|
||||
└─┴────┴──┴────┴──┘
|
||||
0 1 5 6 10 11 - Octets
|
||||
```
|
||||
`N` : Nombre de tags présents, 1 octet
|
||||
`UIDn` : UID du tag n, 4 octets
|
||||
`In` : ID du tag n, 1 octet
|
||||
|
||||
### Message de programmation de tag
|
||||
|
||||
**En-tête** : `=`
|
||||
**Longueur** : 5 octets
|
||||
**Description** : Message envoyé par le logiciel à l'objet, permet de changer
|
||||
(programmer) l'ID stocké dans un tag.
|
||||
Le contenu est formaté de façon similaire à la réponse d'information :
|
||||
l'UID du tag à programmer, sur quatre octets, puis le nouvel identifiant du tag,
|
||||
sur un octet.
|
||||
On ne programme qu'un tag à la fois.
|
||||
|
||||
#### Schéma
|
||||
|
||||
```
|
||||
┌─────┬────┐
|
||||
│ UID │ ID │
|
||||
└─────┴────┘
|
||||
0 4 5 - Octets
|
||||
```
|
||||
`UID` : UID du tag à programmer, 4 octets
|
||||
`ID` : Nouvel ID à programmer, 1 octet
|
||||
|
||||
### Message informatif
|
||||
|
||||
**En-tête** : `#`
|
||||
|
|
|
@ -13,8 +13,43 @@
|
|||
* functions for the fixed protocols.
|
||||
*/
|
||||
namespace Com {
|
||||
enum ReceivedMessage {
|
||||
NO_MESSAGE,
|
||||
TAG_INFO_REQUEST,
|
||||
TAG_PROGRAMMING,
|
||||
INVALID_MESSAGE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct used for tag info responses and tag programming.
|
||||
* Would be 8 bytes if not packed due to padding.
|
||||
*/
|
||||
struct __attribute__((packed)) TagInfoRecord {
|
||||
uint32_t uid;
|
||||
uint8_t figId;
|
||||
};
|
||||
|
||||
void sendFigUpdate(int8_t event);
|
||||
void sendComment(const char message[62], ...);
|
||||
/**
|
||||
* Send the response to the tag info request.
|
||||
* The TagInfoRecord buffer needs to be properly filled through getTagRecords().
|
||||
* @param tagCount The amount of tags that are present in the response.
|
||||
*/
|
||||
void sendTagInfo(uint8_t tagCount);
|
||||
|
||||
/**
|
||||
* @return Pointer to a buffer of TagInfoRecords
|
||||
*/
|
||||
TagInfoRecord* getTagRecords();
|
||||
|
||||
/**
|
||||
* Check for any incoming message and handle it if needed, returning the
|
||||
* kind of message received if known, no message if there was nothing to read
|
||||
* and invalid message otherwise.
|
||||
* @return Kind of message received.
|
||||
*/
|
||||
ReceivedMessage receiveMessage();
|
||||
}
|
||||
|
||||
#endif //JIN_BARBAPAPA_COM_H
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <MFRC522DriverPinSimple.h>
|
||||
#include <MFRC522DriverSPI.h>
|
||||
|
||||
#include "Com.h"
|
||||
#include "IDs.h"
|
||||
#include "Pinout.h"
|
||||
|
||||
|
@ -30,6 +31,8 @@ struct uidNode {
|
|||
|
||||
class RFID {
|
||||
public:
|
||||
static constexpr int maxTags = 2;
|
||||
|
||||
/***
|
||||
* Use the main SPI port and NFC chip select by default, but allow choosing
|
||||
* an alternate chip select and/or SPI bus if needed to allow multiple
|
||||
|
@ -48,11 +51,26 @@ public:
|
|||
* @return 0 if no change, ID if new tag detected, ID & sign bit if the tag left.
|
||||
*/
|
||||
int8_t checkTags();
|
||||
|
||||
/***
|
||||
* Populate an already allocated array of sufficient size to fit
|
||||
* maxTags number of TagInfoRecords.
|
||||
* @param tagData Pointer to an array of size maxTags*sizeof(Com::TagInfoRecord)
|
||||
* @return Number of tags actually put in the array.
|
||||
*/
|
||||
uint8_t gatherTagInfo(Com::TagInfoRecord* tagData);
|
||||
|
||||
/***
|
||||
* Change the ID stored in a tag so that it matches tagToProgram.
|
||||
* @param tagToProgram Pointer to an existing struct with the data to program.
|
||||
* @return True if the programming succeeded.
|
||||
*/
|
||||
bool programTag(Com::TagInfoRecord* tagToProgram);
|
||||
private:
|
||||
static const byte tagIdBlock = 0x11;
|
||||
/* Used for "encrypted" communication with the tags. */
|
||||
static MFRC522Constants::MIFARE_Key defaultKey;
|
||||
static constexpr int maxTags = 2;
|
||||
int currentActiveTags = 0;
|
||||
static int currentActiveTags;
|
||||
|
||||
/*
|
||||
* The linked list tracking active tags.
|
||||
|
@ -90,11 +108,22 @@ private:
|
|||
/**
|
||||
* Read block at blockADdr from tagUid and return if the read succeeded or not.
|
||||
* The read block will be in comData.
|
||||
* The tag needs to be already woken up.
|
||||
* @param tagUid Uid of the tag to be read.
|
||||
* @param blockAddr Address of the block to be read.
|
||||
* @return True if read succeeded, false otherwise.
|
||||
*/
|
||||
bool readBlock(MFRC522Constants::Uid &tagUid, byte blockAddr);
|
||||
|
||||
/**
|
||||
* Write data to tagUid at blockAddr and return if the write succeeded or not.
|
||||
* The tag needs to be already woken up.
|
||||
* @param tagUid Uid of the tag to write to.
|
||||
* @param blockAddr Address of the block to write to.
|
||||
* @param data Byte to write.
|
||||
* @return True if write succeeded, false otherwise.
|
||||
*/
|
||||
bool writeBlock(MFRC522Constants::Uid &tagUid, byte blockAddr, uint8_t data);
|
||||
};
|
||||
|
||||
#endif //JIN_BARBAPAPA_RFID_H
|
||||
|
|
59
src/Com.cpp
59
src/Com.cpp
|
@ -3,9 +3,12 @@
|
|||
//
|
||||
|
||||
#include "Com.h"
|
||||
#include "RFID.h"
|
||||
|
||||
namespace Com {
|
||||
byte buffer[64];
|
||||
TagInfoRecord records[RFID::maxTags] = {};
|
||||
|
||||
/**
|
||||
* Will flush buffer through either HID or Serial depending on build
|
||||
* options.
|
||||
|
@ -19,7 +22,7 @@ namespace Com {
|
|||
Serial.write((const char*)buffer);
|
||||
#endif
|
||||
/* Clear the buffer. */
|
||||
memset(buffer, 0, 64);
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
}
|
||||
|
||||
void sendFigUpdate(int8_t event) {
|
||||
|
@ -36,4 +39,58 @@ namespace Com {
|
|||
va_end(args);
|
||||
flushBuffer();
|
||||
}
|
||||
|
||||
void sendTagInfo(uint8_t tagCount) {
|
||||
buffer[0] = '!';
|
||||
buffer[1] = tagCount;
|
||||
memcpy(buffer + 2, records, sizeof(TagInfoRecord)*tagCount);
|
||||
flushBuffer();
|
||||
}
|
||||
|
||||
TagInfoRecord* getTagRecords() {
|
||||
return records;
|
||||
}
|
||||
|
||||
inline bool dataAvailable() {
|
||||
#ifdef USB_RAWHID
|
||||
return usb_rawhid_available();
|
||||
#else
|
||||
/* Serial is used for debug, do not care about message length. */
|
||||
return Serial.available();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void receiveData() {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
#ifdef USB_RAWHID
|
||||
/* We know we have data, don't need to set a timeout. */
|
||||
usb_rawhid_recv(buffer, 0);
|
||||
#else
|
||||
/* Could lead to incomplete messages, but should be OK on Teensy. */
|
||||
Serial.readBytes(buffer, Serial.available());
|
||||
#endif
|
||||
}
|
||||
|
||||
ReceivedMessage receiveMessage() {
|
||||
if (!dataAvailable())
|
||||
return NO_MESSAGE;
|
||||
receiveData();
|
||||
|
||||
ReceivedMessage messageType;
|
||||
switch (buffer[0]) {
|
||||
case '?':
|
||||
messageType = TAG_INFO_REQUEST;
|
||||
break;
|
||||
case '=':
|
||||
messageType = TAG_PROGRAMMING;
|
||||
/* Make the record available to the rest of the code. */
|
||||
memset(records, 0, sizeof(records));
|
||||
memcpy(records, buffer + 1, sizeof(TagInfoRecord));
|
||||
break;
|
||||
default:
|
||||
messageType = INVALID_MESSAGE;
|
||||
Serial.printf("Invalid header received : %c\n", buffer[0]);
|
||||
}
|
||||
return messageType;
|
||||
}
|
||||
}
|
||||
|
|
76
src/RFID.cpp
76
src/RFID.cpp
|
@ -4,7 +4,11 @@
|
|||
|
||||
#include "RFID.h"
|
||||
|
||||
static_assert(RFID::maxTags * sizeof(Com::TagInfoRecord) <= 62,
|
||||
"RFID::maxTags too high, protocol cannot support it.\n");
|
||||
|
||||
MFRC522Constants::MIFARE_Key RFID::defaultKey = {0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
int RFID::currentActiveTags = 0;
|
||||
|
||||
void RFID::init() {
|
||||
// Create and set up a static linked list to manage active tags.
|
||||
|
@ -58,15 +62,47 @@ int8_t RFID::checkTags() {
|
|||
return 0;
|
||||
}
|
||||
// If the read fails, we cannot use this tag anyway so remove it from the list.
|
||||
if (!readBlock(newTag->uid, 0x11) ) {
|
||||
if (!readBlock(newTag->uid, tagIdBlock) ) {
|
||||
removeActiveTag(newTag, nullptr);
|
||||
return 0;
|
||||
}
|
||||
// TODO: ID check ?
|
||||
// Don' t check the ID, otherwise we cannot program the tag.
|
||||
newTag->tag_ID = comData[0];
|
||||
return newTag->tag_ID;
|
||||
}
|
||||
|
||||
uint8_t RFID::gatherTagInfo(Com::TagInfoRecord *tagData) {
|
||||
int tagCount = 0;
|
||||
for (uidNode* node = activeTags; node; node = node->next) {
|
||||
// The protocol does not support UIDs other than 4 bytes, so assume it
|
||||
// is the case here directly.
|
||||
tagData[tagCount].uid = *(uint32_t*)(node->uid.uidByte);
|
||||
tagData[tagCount].figId = node->tag_ID;
|
||||
tagCount++;
|
||||
}
|
||||
return tagCount;
|
||||
}
|
||||
|
||||
bool RFID::programTag(Com::TagInfoRecord *tagToProgram) {
|
||||
|
||||
byte comSize = sizeof(comData);
|
||||
|
||||
mfrc.uid.size = 4;
|
||||
*(uint32_t*)(mfrc.uid.uidByte) = tagToProgram->uid;
|
||||
mfrc.PICC_WakeupA(comData, &comSize);
|
||||
if (mfrc.PICC_Select(&mfrc.uid, mfrc.uid.size) != MFRC522Constants::STATUS_OK) {
|
||||
Serial.println("Failed to wakeup for programming. Is tag still there ?\n");
|
||||
mfrc.PICC_HaltA();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!writeBlock(mfrc.uid, tagIdBlock, tagToProgram->figId)) {
|
||||
Serial.println("Failed to program tag.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uidNode* RFID::addActiveTag(const MFRC522Constants::Uid &newTag) {
|
||||
if (!nextFreeTagSlot) {
|
||||
return nullptr;
|
||||
|
@ -101,16 +137,40 @@ bool RFID::readBlock(MFRC522Constants::Uid &uidToRead, byte blockAddr) {
|
|||
if (mfrc.PCD_Authenticate(
|
||||
MFRC522Constants::PICC_Command::PICC_CMD_MF_AUTH_KEY_A,
|
||||
blockAddr, (&defaultKey),&uidToRead)!= MFRC522Constants::STATUS_OK) {
|
||||
Serial.println("Failed to authenticate");
|
||||
Serial.println("readBlock: Failed to authenticate");
|
||||
mfrc.PICC_HaltA();
|
||||
return false;
|
||||
} else {
|
||||
byte size = sizeof(comData);
|
||||
mfrc.MIFARE_Read(blockAddr, comData, &size);
|
||||
// Serial.printf("Read block : 0x%lX\n", *(uint32_t *)comData);
|
||||
}
|
||||
|
||||
byte size = sizeof(comData);
|
||||
MFRC522::StatusCode status = mfrc.MIFARE_Read(blockAddr, comData, &size);
|
||||
// Serial.printf("Read block : 0x%lX\n", *(uint32_t *)comData);
|
||||
// Needed otherwise no new communications can happen.
|
||||
mfrc.PCD_StopCrypto1();
|
||||
// No need to keep the tag active.
|
||||
mfrc.PICC_HaltA();
|
||||
return true;
|
||||
return status == MFRC522Constants::STATUS_OK;
|
||||
}
|
||||
|
||||
bool RFID::writeBlock(MFRC522Constants::Uid &uidToRead, byte blockAddr, uint8_t data) {
|
||||
if (mfrc.PCD_Authenticate(
|
||||
MFRC522Constants::PICC_Command::PICC_CMD_MF_AUTH_KEY_A,
|
||||
blockAddr, (&defaultKey),&uidToRead)!= MFRC522Constants::STATUS_OK) {
|
||||
Serial.println("writeBlock: Failed to authenticate");
|
||||
mfrc.PICC_HaltA();
|
||||
return false;
|
||||
}
|
||||
|
||||
byte size = sizeof(comData);
|
||||
for (byte& bufferByte: comData)
|
||||
bufferByte = 0;
|
||||
|
||||
*comData = data;
|
||||
|
||||
MFRC522::StatusCode status = mfrc.MIFARE_Write(blockAddr, comData, size);
|
||||
// Needed otherwise no new communications can happen.
|
||||
mfrc.PCD_StopCrypto1();
|
||||
// No need to keep the tag active.
|
||||
mfrc.PICC_HaltA();
|
||||
return status == MFRC522Constants::STATUS_OK;
|
||||
}
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -77,6 +77,16 @@ __attribute__((noreturn)) int main() {
|
|||
}
|
||||
}
|
||||
|
||||
Com::ReceivedMessage message = Com::receiveMessage();
|
||||
if (message == Com::TAG_INFO_REQUEST) {
|
||||
uint8_t tagCount = rfid.gatherTagInfo(Com::getTagRecords());
|
||||
Com::sendTagInfo(tagCount);
|
||||
} else if (message == Com::TAG_PROGRAMMING) {
|
||||
if (!rfid.programTag(Com::getTagRecords())) {
|
||||
Com::sendComment("Tag programming failed.");
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Drop delay, WFE+timer interrupt(s) ? */
|
||||
delay(25);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue