1
0
Fork 0

Compare commits

...

5 commits

Author SHA1 Message Date
5618f52a0e main: Add message handling
We want to check for new messages at the end of the loop, when all
other events should have happenned.
Handle the two supported messages there : tag info request and tag
programming command.

The timing might need some testing, hopefully it is short enough.
2024-01-06 00:40:27 +00:00
38e4982aac RFID: Implement tag programming
The software needs tag data to be able to send programming requests, so
go through the active tags and extract the data in the new Com structs.

programTag() handles tag wakeup and selection, so might fail if the
requested tag is on another reader. This allows chaining them together
and not keeping track of which tag which reader sees.

Add an assert in case the communication buffer would get too small to
send all active tags as the protocol does not define a multi-message
response.
2024-01-06 00:36:19 +00:00
b5b5268b79 Com: Implement tag programming
Define the protocol allowing the software to track tags
and program them, in the README.

The data structure is shared between a programming command and an
information message, so create a new struct for it and an array
to keep track of it and expose to the rest of the program.
The goal is to keep the protocol core and message construction
entirely inside Com, so this intermediary buffer allows exposing
it outside while hiding the protocol details.

This means that we are starting to receive data from the software,
so implement generic functions to check and receive data as well
as receiveMessage() which parses the message and handles them.
2024-01-06 00:32:19 +00:00
324cf50653 RFID: Implement writing to blocks
In order to set the ID to the tag, we need to be able to write blocks.
Implement writeBlock() similarly to readBlock() to allow that.

Make sure to clean the communication buffer and handle failure states,
this will become more important for dual RFID readers as will allow
'chaining' calls and cover both readers without keeping track of
what reader sees what tag.

Only allow to write a byte for now, we don't need more.
2024-01-06 00:25:46 +00:00
73ceeb493c RFID: Clean up and generalize
Add a constant for the ID block used and replace it.

Clean up some comments and clarify expectations.

readBlock() did not properly halt tags after errors and the else
clause was superfluous. Properly handle error status.

Move currentActiveTags to be static and initialize it.
This allows for keeping track of max/current tags accross multiple instances.
2024-01-06 00:15:48 +00:00
6 changed files with 257 additions and 11 deletions

View file

@ -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** : `#`

View file

@ -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

View file

@ -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

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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);
}