diff --git a/README.md b/README.md index c9c577c..f1bc184 100644 --- a/README.md +++ b/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** : `#` diff --git a/include/Com.h b/include/Com.h index 8165ebe..ac3a688 100644 --- a/include/Com.h +++ b/include/Com.h @@ -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 diff --git a/include/RFID.h b/include/RFID.h index ef5b02b..66a42f8 100644 --- a/include/RFID.h +++ b/include/RFID.h @@ -15,6 +15,7 @@ #include #include +#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 diff --git a/src/Com.cpp b/src/Com.cpp index adafc82..8c45eaa 100644 --- a/src/Com.cpp +++ b/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; + } } diff --git a/src/RFID.cpp b/src/RFID.cpp index 22c69fc..03b40d5 100644 --- a/src/RFID.cpp +++ b/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; } diff --git a/src/main.cpp b/src/main.cpp index 3b5902f..b281973 100644 --- a/src/main.cpp +++ b/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); }