M5Unit-RFID 0.2.0 git rev:79c1939
Loading...
Searching...
No Matches
uhf.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
3 *
4 * SPDX-License-Identifier: MIT
5 */
10#ifndef M5_UNIT_RFID_UHF_UHF_HPP
11#define M5_UNIT_RFID_UHF_UHF_HPP
12
13#include <array>
14#include <cstddef>
15#include <cstdint>
16#include <string>
17#include <vector>
18
19#include <m5_utility/crc.hpp>
20
21namespace m5 {
26namespace uhf {
27
32enum class Bank : uint8_t {
33 Reserved,
34 Epc,
35 Tid,
36 User,
37};
38
44enum class Region : uint8_t {
45 Unspecified = 0x00,
46 China900MHz = 0x01,
47 America = 0x02,
48 Europe = 0x03,
49 China800MHz = 0x04,
50 SouthKorea = 0x06,
51};
52
57enum class Session : uint8_t { S0, S1, S2, S3 };
58
63enum class Target : uint8_t { A, B };
64
73enum class SelectFilter : uint8_t {
74 All,
75 AllAlias,
77 Selected,
78};
79
90enum class Reason : uint8_t {
91 // Nothing went out over the air
94 Busy,
96
97 // The tag answered and declined, in the terms EPC Gen2 Annex I sets out
98 Locked,
101 Refused,
102
103 // What became of it is not known
104 NoAnswer,
107 Malformed
108};
109
121inline bool tagUnchanged(const Reason reason)
122{
123 return reason < Reason::NoAnswer;
124}
125
127inline const char* reasonAsString(const Reason reason)
128{
129 switch (reason) {
130 case Reason::NoSelection:
131 return "no tag selected";
132 case Reason::BadArgument:
133 return "the request cannot succeed as asked";
134 case Reason::Busy:
135 return "the reader could not make room to send it";
136 case Reason::Unsupported:
137 return "this chip does not have the command";
138 case Reason::Locked:
139 return "the memory is locked";
140 case Reason::InsufficientPower:
141 return "the tag has not the power";
142 case Reason::MemoryOverrun:
143 return "past the end of the memory";
144 case Reason::Refused:
145 return "the tag declined";
146 case Reason::NoAnswer:
147 return "nobody answered";
148 case Reason::AccessFailed:
149 return "the tag would not be accessed; the password may be wrong";
150 case Reason::ReaderFault:
151 return "the reader did not carry it out";
152 default:
153 return "the answer could not be read";
154 }
155}
156
158constexpr size_t EPC_MAX_BYTES{62};
165constexpr size_t TID_MAX_BYTES{40};
166
173struct Epc {
174 std::array<uint8_t, EPC_MAX_BYTES> data{};
175 uint8_t size{};
176
178 inline uint8_t operator[](const size_t i) const
179 {
180 return data[i];
181 }
183 inline uint8_t& operator[](const size_t i)
184 {
185 return data[i];
186 }
187 inline const uint8_t* begin() const
188 {
189 return data.data();
190 }
191 inline const uint8_t* end() const
192 {
193 return data.data() + size;
194 }
196 inline bool empty() const
197 {
198 return size == 0;
199 }
201 bool assign(const uint8_t* src, const size_t len);
202 bool operator==(const Epc& o) const;
204 std::string toString() const;
205};
206
213struct Tid {
214 std::array<uint8_t, TID_MAX_BYTES> data{};
215 uint8_t size{};
216
218 inline uint8_t operator[](const size_t i) const
219 {
220 return data[i];
221 }
223 inline uint8_t& operator[](const size_t i)
224 {
225 return data[i];
226 }
227 inline const uint8_t* begin() const
228 {
229 return data.data();
230 }
231 inline const uint8_t* end() const
232 {
233 return data.data() + size;
234 }
236 inline bool empty() const
237 {
238 return size == 0;
239 }
241 bool assign(const uint8_t* src, const size_t len);
242 bool operator==(const Tid& o) const;
244 std::string toString() const;
245};
246
258enum class Vendor : uint16_t {
259 Unknown = 0x000,
260 Impinj = 0x001,
261 TexasInstruments = 0x002,
262 Alien = 0x003,
263 Atmel = 0x005,
264 NXP = 0x006,
265 STMicroelectronics = 0x007,
266 EPMicroelectronics = 0x008,
267 EMMicroelectronic = 0x00B,
268 Quanray = 0x00F,
269 Fujitsu = 0x010,
270 Nationz = 0x01B,
271 Invengo = 0x01C,
272 RFMicron = 0x024,
273 FudanMicroelectronics = 0x027,
274 AMS = 0x02F,
275 HuadaSemiconductor = 0x032,
276 Mikron = 0x034,
277};
278
279namespace detail {
281inline uint16_t word_at(const uint8_t* data, const size_t word_index)
282{
283 return static_cast<uint16_t>((data[word_index * 2] << 8) | data[word_index * 2 + 1]);
284}
285
292 const char* name;
293};
294
302inline const VendorEntry* vendorEntries(size_t& count)
303{
304 static const VendorEntry entries[] = {
305 {Vendor::Impinj, "Impinj"},
306 {Vendor::TexasInstruments, "Texas Instruments"},
307 {Vendor::Alien, "Alien Technology"},
308 {Vendor::Atmel, "Atmel"},
309 {Vendor::NXP, "NXP Semiconductors"},
310 {Vendor::STMicroelectronics, "ST Microelectronics"},
311 {Vendor::EPMicroelectronics, "EP Microelectronics"},
312 {Vendor::EMMicroelectronic, "EM Microelectronic"},
313 {Vendor::Quanray, "Quanray Electronics"},
314 {Vendor::Fujitsu, "Fujitsu"},
315 {Vendor::Nationz, "Nationz"},
316 {Vendor::Invengo, "Invengo"},
317 {Vendor::RFMicron, "RFMicron"},
318 {Vendor::FudanMicroelectronics, "Shanghai Fudan Microelectronics Group"},
319 {Vendor::AMS, "ams AG"},
320 {Vendor::HuadaSemiconductor, "Huada Semiconductor"},
321 {Vendor::Mikron, "PJSC Mikron"},
322 };
323 count = sizeof(entries) / sizeof(entries[0]);
324 return entries;
325}
326} // namespace detail
327
332enum class Chip : uint8_t {
333 Unknown,
334 AlienHiggs3,
335 AlienHiggs4,
336 AlienHiggs9,
337 Em4423SmallEpc,
338 Em4423LargeEpc,
339 ImpinjMonza4D,
340 ImpinjMonza4E,
341 ImpinjMonza4i,
342 ImpinjMonza4QT,
343 ImpinjM730,
344 ImpinjM750,
345 ImpinjM770,
346 ImpinjM780,
347 ImpinjM781,
348 NxpUcodeG2iM,
349 NxpUcodeG2iMPlus,
350 NxpUcode7,
351 NxpUcode7xm1k,
352 NxpUcode7xm2k,
353 NxpUcode7xmPlus,
354 NxpUcode8,
355 NxpUcode8m,
356 NxpUcode9,
357 NxpUcode9xe,
358};
359
366struct Tag {
367 // Filled in by detection
368 uint16_t pc{};
369 uint16_t crc{};
370 int8_t rssi{};
372
373 // Filled in by identification
375 Chip chip{Chip::Unknown};
376 Vendor vendor{Vendor::Unknown};
377 uint16_t mdid{};
378 uint16_t model_number{};
379 uint16_t serial_bits{};
383 uint32_t epc_max_bits{};
385 bool has_xtid{};
393
395 inline bool valid() const
396 {
397 return !epc.empty();
398 }
400 std::string vendorAsString() const;
402 std::string chipAsString() const;
403};
404
412inline uint32_t chipUserMemoryBits(const Chip chip)
413{
414 switch (chip) {
415 case Chip::AlienHiggs9:
416 // ALC-390 offers "688 bit user memory", which a tag holds only while its EPC is
417 // the 96 bit it leaves the factory with. chipSharedUserMemoryBits is what sizes
418 // this chip; this is the figure to fall back on when the PC is unknown
419 return 688;
420 case Chip::AlienHiggs4:
421 return 128; // Higgs 4 V1.3.6 3: "128 bits of NVM for User data"
422 // EM4423 4423-DS-02 v6.0 7: "The small EPC memory configuration provides 128 bits for
423 // encoding and 160 bits of USER memory", and 224 with 64 for the large one. Which of
424 // the two a tag is, is in its model number, so neither size has to be shared
425 case Chip::Em4423SmallEpc:
426 return 160;
427 case Chip::Em4423LargeEpc:
428 return 64;
429 // Monza 4 V10.0 Table 2-8
430 case Chip::ImpinjMonza4D:
431 return 32;
432 case Chip::ImpinjMonza4E:
433 return 128;
434 case Chip::ImpinjMonza4i:
435 return 480;
436 case Chip::ImpinjMonza4QT:
437 return 512; // Monza 4 Rev8.0 2.3.1: "512 bits of user memory". 32 words, confirmed
438 // M700 Series v6.4 1, and M780 / M781 v1.1 1
439 case Chip::ImpinjM750:
440 return 32;
441 case Chip::ImpinjM770:
442 return 32;
443 case Chip::ImpinjM780:
444 return 128;
445 case Chip::ImpinjM781:
446 return 512;
447 case Chip::NxpUcode8m:
448 return 32; // SL3S1205_15 Rev3.6 Table 7: "User Memory 32 bit"
449 // SL3S10X4 memory tables
450 case Chip::NxpUcode7xm1k:
451 return 1024;
452 case Chip::NxpUcode7xm2k:
453 case Chip::NxpUcode7xmPlus:
454 return 2048;
455 case Chip::NxpUcodeG2iM:
456 // Rev3.7 Table 7 and Table 15. The "640 bit configurable User Memory" of the
457 // general description is the whole pool, and this part keeps its EPC fixed at
458 // 256 bit, which takes 128 of those bits away
459 return 512;
460 case Chip::NxpUcodeG2iMPlus:
461 // Rev3.7 Table 16, which is what it ships with. The EPC can be taken up to 448 bit
462 // and the User bank shrinks by as much, so chipSharedUserMemoryBits is what sizes
463 // this chip; this is the figure to fall back on when the PC is unknown
464 return 640;
465 default:
466 return 0;
467 }
468}
469
476inline uint32_t chipEpcMaxBits(const Chip chip)
477{
478 switch (chip) {
479 case Chip::AlienHiggs9:
480 return 496; // ALC-390: "Supports EPC size up to 496b"
481 case Chip::AlienHiggs4:
482 return 128; // Higgs 4 V1.3.6 3: "128 bits of NVM for Electronic Product Code"
483 // EM4423 4423-DS-02 v6.0 7
484 case Chip::Em4423SmallEpc:
485 return 128;
486 case Chip::Em4423LargeEpc:
487 return 224;
488 // Monza 4 V10.0 Table 2-8
489 case Chip::ImpinjMonza4D:
490 return 128;
491 case Chip::ImpinjMonza4E:
492 return 496;
493 case Chip::ImpinjMonza4i:
494 return 256;
495 case Chip::ImpinjMonza4QT:
496 return 128; // Monza 4 Rev8.0 2.3.1: "128 bits of EPC memory"
497 // M700 Series v6.4 1, and M780 / M781 v1.1 1
498 case Chip::ImpinjM730:
499 return 128;
500 case Chip::ImpinjM750:
501 return 96;
502 case Chip::ImpinjM770:
503 return 128;
504 case Chip::ImpinjM780:
505 return 496;
506 case Chip::ImpinjM781:
507 return 128;
508 case Chip::NxpUcode7:
509 return 128; // SL3S1204 Rev4.0 Table 8
510 case Chip::NxpUcode8m:
511 return 96; // SL3S1205_15 Rev3.6 Table 7
512 case Chip::NxpUcode7xm1k:
513 case Chip::NxpUcode7xm2k:
514 case Chip::NxpUcode7xmPlus:
515 return 448; // SL3S10X4 memory tables: "EPC (excluding CRC, PC) 448 bit"
516 case Chip::NxpUcode8:
517 // SL3S1205_15 Rev3.6 Table 6 gives 128 bit, and a real tag takes a PC of eight
518 // words and refuses nine
519 return 128;
520 case Chip::NxpUcode9:
521 return 96; // SL3S1206 Rev3.5 Table 5: "EPC (excluding 16 bit CRC-16 and 16-bit PC) 96 bit"
522 case Chip::NxpUcode9xe:
523 return 128; // SL3S1216 Rev3.3 2.1.1: "128-bit of EPC memory"
524 case Chip::NxpUcodeG2iM:
525 return 256; // G2iM Rev3.7: "256 bit of EPC memory"
526 case Chip::NxpUcodeG2iMPlus:
527 return 448; // G2iM Rev3.7: "up to 448 bit EPC for UCODE G2iM+"
528 default:
529 return 0;
530 }
531}
532
541inline uint16_t chipTidWords(const Chip chip)
542{
543 switch (chip) {
544 case Chip::AlienHiggs3:
545 // Higgs 3 IC Datasheet Supplement, TID Bank rows 0 to 5: the first two words name
546 // the chip and the four after them are Unique ID Word 0 to 3. Row 6 onwards is
547 // reserved, and the XTID indicator of this chip reads zero
548 return 6;
549 case Chip::NxpUcodeG2iM:
550 case Chip::NxpUcodeG2iMPlus:
551 // SL3S1003_1013 Rev3.7 Table 8: 30h to 5Fh is a permalocked serial number, under an
552 // XTID header that reads 00h. The TID ends there; 60h onwards is User TID memory
553 return 6;
554 default:
555 return 0;
556 }
557}
558
567inline uint16_t chipPermalockBlockCount(const Chip chip)
568{
569 switch (chip) {
570 case Chip::ImpinjMonza4QT:
571 return 4; // Monza 4 V10.0 2.3.1: "four fixed, 128-bit sections of user memory"
572 case Chip::ImpinjMonza4i:
573 return 4; // Monza 4 V10.0 Table 2-16
574 case Chip::AlienHiggs3:
575 // Higgs 3 Supplement: "BlockPermaLock: Block lengths are 32 bits and a total of
576 // 4 blocks"
577 return 4;
578 // EM4423 4423-DS-02 v6.0, the Gen2V2config word: the small EPC layout names Block 0
579 // Locked through Block 4 Locked, and the large one stops at Block 1 Locked
580 case Chip::Em4423SmallEpc:
581 return 5;
582 case Chip::Em4423LargeEpc:
583 return 2;
584 default:
585 return 0;
586 }
587}
588
596enum class Support : uint8_t {
597 Unknown,
598 No,
599 Yes
600};
601
603inline const char* supportAsString(const Support support)
604{
605 switch (support) {
606 case Support::Yes:
607 return "yes";
608 case Support::No:
609 return "no";
610 default:
611 return "unknown";
612 }
613}
614
624{
625 switch (chip) {
626 case Chip::NxpUcodeG2iM:
627 case Chip::NxpUcodeG2iMPlus:
628 // SL3S1003_1013 Rev3.7 Table 13: "BlockPermalock ... yes ... yes"
629 return Support::Yes;
630 case Chip::ImpinjMonza4QT:
631 // Monza 4 V10.0 2.3.1, the four 128-bit sections it can lock
632 return Support::Yes;
633 case Chip::ImpinjMonza4i:
634 // Monza 4 V10.0 Table 2-14: "User Memory in Monza 4QT (in Private mode) and
635 // Monza 4i only"
636 return Support::Yes;
637 case Chip::NxpUcode7xm1k:
638 case Chip::NxpUcode7xm2k:
639 case Chip::NxpUcode7xmPlus:
640 return Support::Yes; // SL3S10X4 p.3 and p.12
641 case Chip::AlienHiggs3:
642 return Support::Yes; // Higgs 3 Supplement, optional commands
643 case Chip::Em4423SmallEpc:
644 case Chip::Em4423LargeEpc:
645 // EM4423 4423-DS-02 v6.0 8: "BlockPermalock '11001001' Optional"
646 return Support::Yes;
647 case Chip::NxpUcode8:
648 case Chip::NxpUcode8m:
649 case Chip::NxpUcode9:
650 case Chip::NxpUcode9xe:
651 // The optional commands these list are Access, BlockWrite and Untraceable
652 return Support::No;
653 default:
654 return Support::Unknown;
655 }
656}
657
663inline Support chipQTSupport(const Chip chip)
664{
665 switch (chip) {
666 case Chip::ImpinjMonza4QT:
667 // Monza 4 V10.0 Table 2-8, the only part of the family with the column ticked
668 return Support::Yes;
669 case Chip::Em4423SmallEpc:
670 case Chip::Em4423LargeEpc:
671 // EM4423 4423-DS-02 v6.0 8 sets out "all implemented commands", and QT is not one
672 return Support::No;
673 default:
674 return Support::Unknown;
675 }
676}
677
686{
687 switch (chip) {
688 case Chip::NxpUcodeG2iM:
689 case Chip::NxpUcodeG2iMPlus:
690 // SL3S1003_1013 Rev3.7 8: the custom commands, "backward compatible to UCODE G2X".
691 // The G2X and G2iL that line came from are not in this table
692 return Support::Yes;
693 case Chip::NxpUcode8:
694 case Chip::NxpUcode8m:
695 // SL3S1205_15 Rev3.6 9.4: ACCESS, Block Write and Untraceable, and nothing else
696 return Support::No;
697 case Chip::Em4423SmallEpc:
698 case Chip::Em4423LargeEpc:
699 // EM4423 4423-DS-02 v6.0 8 sets out "all implemented commands", and none of these
700 // is one. They are NXP's, and this chip is EM Microelectronic's
701 return Support::No;
702 default:
703 return Support::Unknown;
704 }
705}
706
718inline uint32_t chipPermalockBlockBits(const Chip chip)
719{
720 switch (chip) {
721 case Chip::ImpinjMonza4QT:
722 // Monza 4 V10.0 2.3.1: "four fixed, 128-bit sections of user memory"
723 return 128;
724 case Chip::NxpUcode7xm1k:
725 case Chip::NxpUcode7xm2k:
726 case Chip::NxpUcode7xmPlus:
727 // SL3S10X4: "BlockPermalock (block size of 256-bit)". The UCODE G2iM has the command
728 // as well, but its datasheet does not give a block size
729 return 256;
730 case Chip::ImpinjMonza4i:
731 // Monza 4 V10.0 Table 2-16: three blocks of 128 bits and a fourth of 96, which is
732 // 128 all along with the user memory running out 32 bits into the last one
733 return 128;
734 case Chip::AlienHiggs3:
735 return 32; // Higgs 3 Supplement: "Block lengths are 32 bits"
736 case Chip::Em4423SmallEpc:
737 case Chip::Em4423LargeEpc:
738 // EM4423 4423-DS-02 v6.0 8: "USER memory block size is two words."
739 return 32;
740 default:
741 return 0;
742 }
743}
744
754inline uint32_t chipPermalockLastBlockBits(const Chip chip)
755{
756 const uint32_t bits = chipPermalockBlockBits(chip);
757 const uint16_t count = chipPermalockBlockCount(chip);
758 if (bits == 0 || count == 0) {
759 return 0;
760 }
761 const uint32_t user = chipUserMemoryBits(chip);
762 const uint32_t upto = bits * count;
763 // Blocks that stop short of the bank cover the whole of themselves; Alien's Higgs-3 has
764 // four 32-bit blocks over 512 bits of user memory and none of them is cut off
765 if (user == 0 || user >= upto) {
766 return bits;
767 }
768 return user - bits * (count - 1);
769}
770
778inline bool chipHasNoUserMemory(const Chip chip)
779{
780 switch (chip) {
781 case Chip::NxpUcode8:
782 // SL3S1205_15 Rev3.6 Table 8: the memory map has no Bank 11 and the UMI bit of the
783 // PC is "hardwired to 0"
784 return true;
785 case Chip::NxpUcode9:
786 return true; // SL3S1206 Rev3.5 Table 6, the same way
787 case Chip::NxpUcode9xe:
788 return true; // SL3S1216 Rev3.3 Table 5, the same way
789 case Chip::NxpUcode7:
790 return true; // SL3S1204 Rev4.0 features: "No User Memory"
791 case Chip::ImpinjM730:
792 return true; // M700 Series v6.4 1: "128 bits of EPC memory, 0 bits of user memory"
793 default:
794 return false;
795 }
796}
797
809inline uint32_t chipSharedUserMemoryBits(const Chip chip, const uint16_t pc)
810{
811 // ALC-390: 6 words of EPC and 43 of User as it ships, and every word the EPC grows by is a
812 // word the User bank loses
813 constexpr uint16_t HIGGS9_POOL_WORDS{49};
814 // Rev3.7 Tables 16 and 17: 128 bit of EPC with 640 of User, or 448 with 320. Both come to
815 // 768 bit, which the datasheet says moves "in steps of 64 bit"
816 constexpr uint16_t G2IM_PLUS_POOL_WORDS{48};
817
818 uint16_t pool{};
819 switch (chip) {
820 case Chip::AlienHiggs9:
821 pool = HIGGS9_POOL_WORDS;
822 break;
823 case Chip::NxpUcodeG2iMPlus:
824 pool = G2IM_PLUS_POOL_WORDS;
825 break;
826 default:
827 return 0;
828 }
829 // A tag selected by its TID reaches identify() with no PC, and a bank sized from a length
830 // of zero would be the whole pool. Returning 0 rather than a wrong size
831 if (pc == 0) {
832 return 0;
833 }
834 const uint16_t epc_words = static_cast<uint16_t>((pc >> 11) & 0x1F);
835 return epc_words < pool ? static_cast<uint32_t>(pool - epc_words) * 16U : 0U;
836}
837
839constexpr uint8_t TID_CLASS_EPCGLOBAL{0xE2};
840
850inline bool tidHasXtid(const uint8_t* tid, const size_t len)
851{
852 return tid != nullptr && len >= 2 && (tid[1] & 0x80) != 0;
853}
854
862inline uint16_t xtidSerialBits(const uint16_t xtid_header)
863{
864 const uint8_t v = static_cast<uint8_t>((xtid_header >> 13) & 0x07);
865 return v == 0 ? 0U : static_cast<uint16_t>(48 + (v - 1) * 16);
866}
867
874constexpr uint16_t XTID_OPTIONAL_COMMAND_SUPPORT{0x1000};
875constexpr uint16_t XTID_BLOCKWRITE_BLOCKERASE{0x0800};
876constexpr uint16_t XTID_USER_MEMORY_BLOCKPERMALOCK{0x0400};
877constexpr uint16_t XTID_LOCK_BIT{0x0200};
878constexpr uint16_t XTID_EXTENDED_HEADER{0x0001};
880
882constexpr size_t TID_FIXED_WORDS{2};
884constexpr size_t XTID_FIXED_WORDS{3};
885
893inline size_t xtidTotalWords(const uint16_t xtid_header)
894{
895 size_t words = XTID_FIXED_WORDS + xtidSerialBits(xtid_header) / 16;
896 if (xtid_header & XTID_OPTIONAL_COMMAND_SUPPORT) {
897 words += 1;
898 }
899 if (xtid_header & XTID_BLOCKWRITE_BLOCKERASE) {
900 words += 4;
901 }
902 if (xtid_header & XTID_USER_MEMORY_BLOCKPERMALOCK) {
903 words += 2;
904 }
905 if (xtid_header & XTID_LOCK_BIT) {
906 words += 1;
907 }
908 return words;
909}
910
914constexpr size_t TID_CHIP_WORDS{3};
915
932inline bool tidTellsTagsApart(const uint8_t* tid, const size_t len)
933{
934 if (tid == nullptr || len <= TID_CHIP_WORDS * 2) {
935 return false;
936 }
937 for (size_t i = TID_CHIP_WORDS * 2; i < len; ++i) {
938 if (tid[i] != 0x00) {
939 return true;
940 }
941 }
942 return false;
943}
944
950inline Vendor resolveVendor(const uint16_t mdid)
951{
952 size_t count{};
953 const detail::VendorEntry* entries = detail::vendorEntries(count);
954 for (size_t i = 0; i < count; ++i) {
955 if (static_cast<uint16_t>(entries[i].vendor) == mdid) {
956 return entries[i].vendor;
957 }
958 }
959 return Vendor::Unknown;
960}
961
970inline Chip resolveChip(const Vendor vendor, const uint16_t model_number)
971{
972 switch (vendor) {
973 case Vendor::Impinj:
974 switch (model_number) {
975 // Monza 4 datasheet V10.0 Table 4-8
976 case 0x100:
977 return Chip::ImpinjMonza4D;
978 case 0x105:
979 return Chip::ImpinjMonza4QT;
980 case 0x10C:
981 return Chip::ImpinjMonza4E;
982 case 0x114:
983 return Chip::ImpinjMonza4i;
984 // M700 Series datasheet v6.4 Table 23
985 case 0x190:
986 return Chip::ImpinjM750;
987 case 0x191:
988 return Chip::ImpinjM730;
989 case 0x1A0:
990 return Chip::ImpinjM770;
991 // M780 / M781 datasheet v1.1 Table 21
992 case 0x1C0:
993 return Chip::ImpinjM780;
994 case 0x1C1:
995 return Chip::ImpinjM781;
996 default:
997 return Chip::Unknown;
998 }
999 case Vendor::Alien:
1000 switch (model_number) {
1001 case 0x412:
1002 return Chip::AlienHiggs3; // Higgs 3 IC datasheet, Table 1
1003 case 0x414:
1004 // Higgs 4 IC Datasheet V1.3.6 TID table: "1 / 0x3414 / Alien Manufacturer
1005 // Code lower bits, Higgs 4 IC"
1006 return Chip::AlienHiggs4;
1007 case 0x821:
1008 // Neither Alien nor the GS1 registry publishes this one; it comes from
1009 // third-party tables, and was read off a real Higgs 9 whose TID is
1010 // E2803821 2000 6820042E3E3B
1011 return Chip::AlienHiggs9;
1012 default:
1013 return Chip::Unknown;
1014 }
1015 case Vendor::EMMicroelectronic:
1016 switch (model_number) {
1017 // EM4423 4423-DS-02 v6.0, the TID memory bank: word 1 holds "Tag MDID LSB's
1018 // (Bh)" and then the model number, whose last bit is note 1, "EPC size, where
1019 // 0 indicates small EPC memory and 1 indicates large EPC memory"
1020 case 0x0A0:
1021 return Chip::Em4423SmallEpc;
1022 case 0x0A1:
1023 return Chip::Em4423LargeEpc;
1024 default:
1025 return Chip::Unknown;
1026 }
1027 case Vendor::NXP:
1028 switch (model_number) {
1029 case 0x80A:
1030 return Chip::NxpUcodeG2iM; // UCODE G2iM datasheet Rev 3.7
1031 case 0x80B:
1032 return Chip::NxpUcodeG2iMPlus;
1033 case 0x890:
1034 return Chip::NxpUcode7; // SL3S1204 Rev4.0 Table 12: "E28068902000"
1035 // SL3S10X4 TID table: E2806D12, E2806F12 and E2806D92
1036 case 0xD12:
1037 return Chip::NxpUcode7xm1k;
1038 case 0xF12:
1039 return Chip::NxpUcode7xm2k;
1040 case 0xD92:
1041 return Chip::NxpUcode7xmPlus;
1042 case 0x894:
1043 return Chip::NxpUcode8; // SL3S1205/1215 datasheet
1044 case 0x994:
1045 return Chip::NxpUcode8m; // SL3S1205_15 Rev3.6, the 8m of the same datasheet
1046 case 0xA16:
1047 case 0xA96:
1048 // The same change of model number, made to this part at the same time. Its
1049 // datasheet gives both TIDs outright: "HEX E280 6A16 ... or HEX E280 6A96"
1050 // (SL3S1216 Rev3.3 Table 5 note 1)
1051 return Chip::NxpUcode9xe;
1052 case 0x995:
1053 case 0x915:
1054 // The model number changed without the part being renamed, and the
1055 // datasheet gives both TIDs: "HEX E280 6995 ... or HEX E280 6915 ..."
1056 // (SL3S1206 Rev3.5 Table 6 note 1)
1057 return Chip::NxpUcode9;
1058 default:
1059 return Chip::Unknown;
1060 }
1061 default:
1062 return Chip::Unknown;
1063 }
1064}
1065
1073inline void fillSizesFromChip(Tag& tag)
1074{
1075 if (tag.user_memory_bits == 0) {
1076 // A chip that shares a pool has no one size to look up, so the PC is asked first
1078 }
1079 if (tag.user_memory_bits == 0) {
1081 }
1082 if (tag.epc_max_bits == 0) {
1083 tag.epc_max_bits = chipEpcMaxBits(tag.chip);
1084 }
1085 if (tag.permalock_block_bits == 0) {
1087 }
1088}
1089
1098inline Chip chipFromTid(const uint8_t* tid, const size_t len)
1099{
1100 if (tid == nullptr || len < TID_FIXED_WORDS * 2 || tid[0] != TID_CLASS_EPCGLOBAL) {
1101 return Chip::Unknown;
1102 }
1103 const uint16_t mdid = static_cast<uint16_t>(((tid[1] & 0x1F) << 4) | (tid[2] >> 4));
1104 const uint16_t model = static_cast<uint16_t>(((tid[2] & 0x0F) << 8) | tid[3]);
1105 return resolveChip(resolveVendor(mdid), model);
1106}
1107
1113 uint16_t word_address{};
1114 uint16_t words{};
1115 bool fits{true};
1116};
1117
1129inline TidReadPlan tidReadPlan(const uint8_t* tid, const size_t len)
1130{
1131 TidReadPlan plan{};
1132 if (len < TID_FIXED_WORDS * 2) {
1133 plan.words = static_cast<uint16_t>(TID_FIXED_WORDS);
1134 return plan;
1135 }
1136 if (!tidHasXtid(tid, len)) {
1137 // No header to say how long the TID is. The chip may still be one that keeps a serial
1138 // past the words that name it, and that serial is the whole point of reading a TID
1139 const size_t total = chipTidWords(chipFromTid(tid, len));
1140 const size_t held = len / 2;
1141 if (total > held) {
1142 plan.word_address = static_cast<uint16_t>(held);
1143 plan.words = static_cast<uint16_t>(total - held);
1144 }
1145 return plan;
1146 }
1147 if (len < XTID_FIXED_WORDS * 2) {
1148 plan.word_address = static_cast<uint16_t>(TID_FIXED_WORDS);
1149 plan.words = static_cast<uint16_t>(XTID_FIXED_WORDS - TID_FIXED_WORDS);
1150 return plan;
1151 }
1152 const uint16_t header = static_cast<uint16_t>((tid[TID_FIXED_WORDS * 2] << 8) | tid[TID_FIXED_WORDS * 2 + 1]);
1153 const size_t total = xtidTotalWords(header);
1154 if (total * 2 > TID_MAX_BYTES) {
1155 plan.fits = false;
1156 return plan;
1157 }
1158 const size_t have = len / 2;
1159 if (have < total) {
1160 plan.word_address = static_cast<uint16_t>(have);
1161 plan.words = static_cast<uint16_t>(total - have);
1162 }
1163 return plan;
1164}
1165
1176inline bool decodeTid(Tag& tag, const uint8_t* tid, const size_t len)
1177{
1178 if (tid == nullptr || len < 4 || tid[0] != TID_CLASS_EPCGLOBAL) {
1179 return false;
1180 }
1181 tag.has_xtid = (tid[1] & 0x80) != 0;
1182 tag.supports_security = (tid[1] & 0x40) != 0;
1183 tag.supports_file = (tid[1] & 0x20) != 0;
1184 tag.mdid = static_cast<uint16_t>(((tid[1] & 0x1F) << 4) | (tid[2] >> 4));
1185 tag.vendor = resolveVendor(tag.mdid);
1186 tag.model_number = static_cast<uint16_t>(((tid[2] & 0x0F) << 8) | tid[3]);
1187 tag.chip = resolveChip(tag.vendor, tag.model_number);
1188
1189 tag.serial_bits = 0;
1190 tag.epc_max_bits = 0;
1191 tag.user_memory_bits = 0;
1192 tag.permalock_block_bits = 0;
1193 tag.supports_block_permalock = false;
1194 if (!tag.has_xtid || len < XTID_FIXED_WORDS * 2) {
1195 fillSizesFromChip(tag);
1196 return true;
1197 }
1198
1199 const uint16_t header = detail::word_at(tid, 2);
1200 tag.serial_bits = xtidSerialBits(header);
1201
1202 // The segments sit past the serial, in the order the header lists them. Each one is stepped
1203 // over whether or not the caller read far enough to hold it, so the offsets of the segments
1204 // below it stay right even when only part of the TID is in hand
1205 size_t word = XTID_FIXED_WORDS + tag.serial_bits / 16;
1206 if (header & XTID_OPTIONAL_COMMAND_SUPPORT) {
1207 if ((word + 1) * 2 <= len) {
1208 const uint16_t ocs = detail::word_at(tid, word);
1209 // Max EPC Size is what fits in the length field of the PC, so it counts words
1210 tag.epc_max_bits = static_cast<uint32_t>(ocs & 0x1F) * 16U;
1211 tag.supports_block_permalock = (ocs & 0x1000) != 0;
1212 }
1213 word += 1;
1214 }
1215 if (header & XTID_BLOCKWRITE_BLOCKERASE) {
1216 word += 4;
1217 }
1218 if (header & XTID_USER_MEMORY_BLOCKPERMALOCK) {
1219 if ((word + 2) * 2 <= len) {
1220 // The lower address holds bits 31 to 16 and the higher one bits 15 to 0, so the
1221 // block size comes first and the user memory size second (TDS Table 16-2)
1222 tag.permalock_block_bits = static_cast<uint32_t>(detail::word_at(tid, word)) * 16U;
1223 tag.user_memory_bits = static_cast<uint32_t>(detail::word_at(tid, word + 1)) * 16U;
1224 }
1225 word += 2;
1226 }
1227 fillSizesFromChip(tag);
1228 return true;
1229}
1230
1235enum class LockTarget : uint8_t { KillPassword, AccessPassword, Epc, Tid, User };
1236
1244enum class LockAction : uint8_t {
1245 Open = 0x00,
1246 PermanentOpen = 0x01,
1247 Lock = 0x02,
1248 PermanentLock = 0x03,
1249};
1250
1256 LockTarget target{};
1257 LockAction action{};
1258
1259 LockSetting() = default;
1260 // Written out because the members carry defaults, which stops C++11 from treating the
1261 // struct as an aggregate and so rules out LockSetting{target, action} without it
1262 LockSetting(const LockTarget t, const LockAction a) : target{t}, action{a}
1263 {
1264 }
1265};
1266
1268inline bool isPermanent(const LockAction action)
1269{
1270 return action == LockAction::PermanentOpen || action == LockAction::PermanentLock;
1271}
1272
1283inline uint32_t buildLockPayload(const LockSetting* settings, const size_t count)
1284{
1285 uint32_t payload{};
1286 for (size_t i = 0; i < count; ++i) {
1287 const uint8_t slot = static_cast<uint8_t>(settings[i].target); // 0:Kill ... 4:User
1288 const uint8_t action = static_cast<uint8_t>(settings[i].action);
1289 const uint8_t mask = static_cast<uint8_t>(0x02 | (action & 0x01));
1290 const uint8_t mask_shift = static_cast<uint8_t>(18 - slot * 2);
1291 const uint8_t action_shift = static_cast<uint8_t>(8 - slot * 2);
1292 payload |= static_cast<uint32_t>(mask) << mask_shift;
1293 payload |= static_cast<uint32_t>(action) << action_shift;
1294 }
1295 return payload;
1296}
1297
1303 uint8_t q{};
1304 Session session{Session::S0};
1305 Target target{Target::A};
1311 SelectFilter filter{SelectFilter::All};
1312};
1313
1315constexpr size_t SELECT_MASK_MAX_BYTES{32};
1316
1326constexpr uint16_t NXP_CONFIG_TAMPER_ALARM{0x8000};
1327constexpr uint16_t NXP_CONFIG_EXTERNAL_SUPPLY{0x4000};
1328constexpr uint16_t NXP_CONFIG_INVERT_OUTPUT{0x0800};
1329constexpr uint16_t NXP_CONFIG_TRANSPARENT_MODE{0x0400};
1330constexpr uint16_t NXP_CONFIG_RAW_DATA_MODE{0x0200};
1331constexpr uint16_t NXP_CONFIG_CONDITIONAL_RANGE{0x0100};
1332constexpr uint16_t NXP_CONFIG_CONDITIONAL_SHORT{0x0080};
1333constexpr uint16_t NXP_CONFIG_MAX_BACKSCATTER{0x0040};
1334constexpr uint16_t NXP_CONFIG_DIGITAL_OUTPUT{0x0020};
1335constexpr uint16_t NXP_CONFIG_RANGE_REDUCTION{0x0010};
1336constexpr uint16_t NXP_CONFIG_PROTECT_USER{0x0008};
1337constexpr uint16_t NXP_CONFIG_PROTECT_EPC{0x0004};
1338constexpr uint16_t NXP_CONFIG_PROTECT_TID{0x0002};
1339constexpr uint16_t NXP_CONFIG_PSF_ALARM{0x0001};
1341
1359 bool invert_output{};
1360 bool transparent_mode{};
1361 bool raw_data_mode{};
1373 bool psf_alarm{};
1375};
1376
1383inline NxpConfigWord decodeNxpConfigWord(const uint16_t word)
1384{
1385 NxpConfigWord out{};
1386 out.tamper_alarm = (word & NXP_CONFIG_TAMPER_ALARM) != 0;
1387 out.external_supply = (word & NXP_CONFIG_EXTERNAL_SUPPLY) != 0;
1388 out.invert_output = (word & NXP_CONFIG_INVERT_OUTPUT) != 0;
1389 out.transparent_mode = (word & NXP_CONFIG_TRANSPARENT_MODE) != 0;
1390 out.raw_data_mode = (word & NXP_CONFIG_RAW_DATA_MODE) != 0;
1391 out.conditional_range = (word & NXP_CONFIG_CONDITIONAL_RANGE) != 0;
1392 out.conditional_short = (word & NXP_CONFIG_CONDITIONAL_SHORT) != 0;
1393 out.max_backscatter = (word & NXP_CONFIG_MAX_BACKSCATTER) != 0;
1394 out.digital_output = (word & NXP_CONFIG_DIGITAL_OUTPUT) != 0;
1395 out.range_reduction = (word & NXP_CONFIG_RANGE_REDUCTION) != 0;
1396 out.protect_user = (word & NXP_CONFIG_PROTECT_USER) != 0;
1397 out.protect_epc = (word & NXP_CONFIG_PROTECT_EPC) != 0;
1398 out.protect_tid = (word & NXP_CONFIG_PROTECT_TID) != 0;
1399 out.psf_alarm = (word & NXP_CONFIG_PSF_ALARM) != 0;
1400 return out;
1401}
1402
1415};
1416
1425 uint8_t target{};
1426 uint8_t action{};
1427 Bank bank{Bank::Epc};
1428 uint32_t pointer_bits{};
1430 bool truncate{};
1431 std::array<uint8_t, SELECT_MASK_MAX_BYTES> mask{};
1432 uint8_t mask_size{};
1433
1435 std::string maskAsString() const;
1436};
1437
1443 std::string hardware_version{};
1444 std::string software_version{};
1445 std::string manufacturer{};
1446};
1447
1455 uint8_t first_channel{};
1456 std::vector<int8_t> dbm{};
1457};
1458
1459namespace detail {
1461inline std::string to_hex(const uint8_t* data, const size_t len)
1462{
1463 static const char digits[] = "0123456789ABCDEF";
1464 std::string s{};
1465 s.reserve(len * 2);
1466 for (size_t i = 0; i < len; ++i) {
1467 s += digits[(data[i] >> 4) & 0x0F];
1468 s += digits[data[i] & 0x0F];
1469 }
1470 return s;
1471}
1472} // namespace detail
1473
1474inline bool Epc::assign(const uint8_t* src, const size_t len)
1475{
1476 if (!src || len > EPC_MAX_BYTES) {
1477 return false;
1478 }
1479 data.fill(0);
1480 for (size_t i = 0; i < len; ++i) {
1481 data[i] = src[i];
1482 }
1483 size = static_cast<uint8_t>(len);
1484 return true;
1485}
1486
1487inline bool Epc::operator==(const Epc& o) const
1488{
1489 if (size != o.size) {
1490 return false;
1491 }
1492 for (size_t i = 0; i < size; ++i) {
1493 if (data[i] != o.data[i]) {
1494 return false;
1495 }
1496 }
1497 return true;
1498}
1499
1500inline std::string Epc::toString() const
1501{
1502 return detail::to_hex(data.data(), size);
1503}
1504
1505inline bool Tid::assign(const uint8_t* src, const size_t len)
1506{
1507 if (!src || len > TID_MAX_BYTES) {
1508 return false;
1509 }
1510 data.fill(0);
1511 for (size_t i = 0; i < len; ++i) {
1512 data[i] = src[i];
1513 }
1514 size = static_cast<uint8_t>(len);
1515 return true;
1516}
1517
1518inline bool Tid::operator==(const Tid& o) const
1519{
1520 if (size != o.size) {
1521 return false;
1522 }
1523 for (size_t i = 0; i < size; ++i) {
1524 if (data[i] != o.data[i]) {
1525 return false;
1526 }
1527 }
1528 return true;
1529}
1530
1531inline std::string SelectParameter::maskAsString() const
1532{
1533 return detail::to_hex(mask.data(), mask_size);
1534}
1535
1536inline std::string Tid::toString() const
1537{
1538 return detail::to_hex(data.data(), size);
1539}
1540
1541inline std::string Tag::vendorAsString() const
1542{
1543 size_t count{};
1544 const detail::VendorEntry* entries = detail::vendorEntries(count);
1545 for (size_t i = 0; i < count; ++i) {
1546 if (entries[i].vendor == vendor) {
1547 return entries[i].name;
1548 }
1549 }
1550 return "Unknown";
1551}
1552
1562{
1563 if (tag.supports_block_permalock) {
1564 return Support::Yes;
1565 }
1566 return chipBlockPermalockSupport(tag.chip);
1567}
1568
1576inline Support tagQTSupport(const Tag& tag)
1577{
1578 if (tag.vendor != Vendor::Unknown && tag.vendor != Vendor::Impinj) {
1579 return Support::No;
1580 }
1581 return chipQTSupport(tag.chip);
1582}
1583
1593{
1594 if (tag.vendor != Vendor::Unknown && tag.vendor != Vendor::NXP) {
1595 return Support::No;
1596 }
1598}
1599
1600inline std::string Tag::chipAsString() const
1601{
1602 switch (chip) {
1603 case Chip::AlienHiggs3:
1604 return "Alien Higgs-3";
1605 case Chip::AlienHiggs9:
1606 return "Alien Higgs-9";
1607 case Chip::AlienHiggs4:
1608 return "Alien Higgs-4";
1609 case Chip::Em4423SmallEpc:
1610 return "EM Microelectronic EM4423 (small EPC)";
1611 case Chip::Em4423LargeEpc:
1612 return "EM Microelectronic EM4423 (large EPC)";
1613 case Chip::ImpinjMonza4D:
1614 return "Impinj Monza 4D";
1615 case Chip::ImpinjMonza4E:
1616 return "Impinj Monza 4E";
1617 case Chip::ImpinjMonza4i:
1618 return "Impinj Monza 4i";
1619 case Chip::ImpinjMonza4QT:
1620 return "Impinj Monza 4QT";
1621 case Chip::ImpinjM730:
1622 return "Impinj M730";
1623 case Chip::ImpinjM750:
1624 return "Impinj M750";
1625 case Chip::ImpinjM770:
1626 return "Impinj M770";
1627 case Chip::ImpinjM780:
1628 return "Impinj M780";
1629 case Chip::ImpinjM781:
1630 return "Impinj M781";
1631 case Chip::NxpUcodeG2iM:
1632 return "NXP UCODE G2iM";
1633 case Chip::NxpUcodeG2iMPlus:
1634 return "NXP UCODE G2iM+";
1635 case Chip::NxpUcode7:
1636 return "NXP UCODE 7";
1637 case Chip::NxpUcode8:
1638 return "NXP UCODE 8";
1639 case Chip::NxpUcode7xm1k:
1640 return "NXP UCODE 7xm-1k";
1641 case Chip::NxpUcode7xm2k:
1642 return "NXP UCODE 7xm-2k";
1643 case Chip::NxpUcode7xmPlus:
1644 return "NXP UCODE 7xm+";
1645 case Chip::NxpUcode8m:
1646 return "NXP UCODE 8m";
1647 case Chip::NxpUcode9:
1648 return "NXP UCODE 9";
1649 case Chip::NxpUcode9xe:
1650 return "NXP UCODE 9xe";
1651 default:
1652 return "Unknown";
1653 }
1654}
1655
1657constexpr uint16_t GEN2_CRC16_INIT{0xFFFF};
1659constexpr uint16_t GEN2_CRC16_POLYNOMIAL{0x1021};
1661constexpr uint16_t GEN2_CRC16_XOROUT{0xFFFF};
1662
1669inline uint16_t gen2_crc16(const uint8_t* data, const size_t len)
1670{
1671 m5::utility::CRC16 crc{GEN2_CRC16_INIT, GEN2_CRC16_POLYNOMIAL, false, false, GEN2_CRC16_XOROUT};
1672 return crc.range(data, len);
1673}
1674
1681inline bool verify_tag_crc(const Tag& tag)
1682{
1683 if (tag.epc.empty()) {
1684 return false;
1685 }
1686 // The Gen2 CRC-16 covers the PC followed by the EPC, so they are laid out contiguously
1687 uint8_t buf[2 + EPC_MAX_BYTES]{};
1688 buf[0] = static_cast<uint8_t>(tag.pc >> 8);
1689 buf[1] = static_cast<uint8_t>(tag.pc & 0xFF);
1690 for (size_t i = 0; i < tag.epc.size; ++i) {
1691 buf[2 + i] = tag.epc[i];
1692 }
1693 return gen2_crc16(buf, tag.epc.size + 2U) == tag.crc;
1694}
1695
1705inline uint8_t pcEPCLengthWords(const uint16_t pc)
1706{
1707 return static_cast<uint8_t>((pc >> 11) & 0x1F);
1708}
1709
1720inline bool pcUserMemoryIndicator(const uint16_t pc)
1721{
1722 return (pc & 0x0400) != 0;
1723}
1724
1730inline bool pcXPCIndicator(const uint16_t pc)
1731{
1732 return (pc & 0x0200) != 0;
1733}
1734
1744inline bool pcToggle(const uint16_t pc)
1745{
1746 return (pc & 0x0100) != 0;
1747}
1748
1758inline uint16_t pcNumberingSystemIdentifier(const uint16_t pc)
1759{
1760 return static_cast<uint16_t>(pc & 0x01FF);
1761}
1762
1770inline bool append_unique(std::vector<Tag>& dst, const Tag& tag)
1771{
1772 if (tag.epc.empty()) {
1773 return false;
1774 }
1775 for (size_t i = 0; i < dst.size(); ++i) {
1776 if (dst[i].epc == tag.epc) {
1777 return false;
1778 }
1779 }
1780 dst.push_back(tag);
1781 return true;
1782}
1783
1784} // namespace uhf
1785} // namespace m5
1786#endif
Top level namespace of M5Stack.
UHF-RFID (EPC Gen2) related namespace.
Signal level measured on each channel of the operating region.
Definition uhf.hpp:1454
std::vector< int8_t > dbm
Measured level of each channel in dBm (signed)
Definition uhf.hpp:1456
uint8_t first_channel
Index of the first measured channel.
Definition uhf.hpp:1455
EPC of a tag.
Definition uhf.hpp:173
bool assign(const uint8_t *src, const size_t len)
Copy in from a raw buffer, refusing anything longer than EPC_MAX_BYTES.
Definition uhf.hpp:1474
uint8_t operator[](const size_t i) const
Byte at the given index.
Definition uhf.hpp:178
std::string toString() const
Uppercase hex.
Definition uhf.hpp:1500
std::array< uint8_t, EPC_MAX_BYTES > data
Bytes, only the first size are valid.
Definition uhf.hpp:174
uint8_t size
Number of valid bytes.
Definition uhf.hpp:175
uint8_t & operator[](const size_t i)
Byte at the given index.
Definition uhf.hpp:183
bool empty() const
Holds no EPC?
Definition uhf.hpp:196
One entry of a lock operation. Targets left out keep their current lock state.
Definition uhf.hpp:1255
Reader module information.
Definition uhf.hpp:1442
Config-Word of an NXP UCODE G2iM or G2iM+, spelled out.
Definition uhf.hpp:1351
bool external_supply
A supply is present on the chip's input.
Definition uhf.hpp:1355
bool tamper_alarm
The tamper loop has been broken.
Definition uhf.hpp:1354
bool max_backscatter
Answer as loudly as the chip can. Set from the factory.
Definition uhf.hpp:1367
bool digital_output
Reduce the range when the condition holds.
Definition uhf.hpp:1368
bool protect_epc
EPC reads back as zeroes.
Definition uhf.hpp:1371
bool conditional_range
Reduce the range when the condition holds.
Definition uhf.hpp:1365
bool protect_tid
TID reads back as zeroes.
Definition uhf.hpp:1372
bool conditional_short
Which state of the condition is the one that reduces it.
Definition uhf.hpp:1366
bool protect_user
User memory reads back as zeroes.
Definition uhf.hpp:1370
bool range_reduction
G2iM+ only.
Definition uhf.hpp:1369
bool psf_alarm
Product Status Flag. A tag holding this answers EAS_Alarm.
Definition uhf.hpp:1373
QT control word of an Impinj Monza 4QT.
Definition uhf.hpp:1410
bool public_memory
The tag shows its public memory map rather than its private one.
Definition uhf.hpp:1414
bool short_range
The tag answers at a reduced range once it is open or about to be.
Definition uhf.hpp:1412
EPC Gen2 query parameters.
Definition uhf.hpp:1302
Session session
Session whose inventoried flag qualifies the round.
Definition uhf.hpp:1304
Target target
Inventoried flag value that is invited to answer.
Definition uhf.hpp:1305
uint8_t q
Q value: a tag answers in one of 2^Q slots.
Definition uhf.hpp:1303
SelectFilter filter
Which tags the round invites, in terms of SL.
Definition uhf.hpp:1311
The mask the reader holds, and how it is matched.
Definition uhf.hpp:1424
Bank bank
Bank the mask is matched against.
Definition uhf.hpp:1427
bool truncate
Whether a matching tag replies with only the bits past the mask.
Definition uhf.hpp:1430
uint8_t mask_length_bits
Length of the mask in bits.
Definition uhf.hpp:1429
uint8_t target
Target: which flag the action acts on, 100 being SL.
Definition uhf.hpp:1425
uint8_t mask_size
Bytes of mask in use.
Definition uhf.hpp:1432
std::string maskAsString() const
Mask as uppercase hex.
Definition uhf.hpp:1531
uint32_t pointer_bits
Where the mask starts, as a bit address inside the bank.
Definition uhf.hpp:1428
std::array< uint8_t, SELECT_MASK_MAX_BYTES > mask
Mask bytes.
Definition uhf.hpp:1431
uint8_t action
Action: what matching and non-matching tags are set to.
Definition uhf.hpp:1426
A tag, as the EPC Gen2 standard calls it.
Definition uhf.hpp:366
bool supports_security
Security indicator (TID bit 09h)
Definition uhf.hpp:386
Epc epc
EPC.
Definition uhf.hpp:371
Chip chip
Identified chip, Unknown when the pair below is not listed.
Definition uhf.hpp:375
uint16_t crc
CRC-16 reported by the tag.
Definition uhf.hpp:369
std::string vendorAsString() const
Mask designer as a printable name, "Unknown" when the identifier is not one we list.
Definition uhf.hpp:1541
uint32_t permalock_block_bits
BlockPermalock granularity, 0 when unknown.
Definition uhf.hpp:384
int8_t rssi
RSSI in dBm (signed)
Definition uhf.hpp:370
bool has_xtid
XTID indicator (TID bit 08h)
Definition uhf.hpp:385
bool supports_file
Definition uhf.hpp:387
uint32_t user_memory_bits
Size of the User bank, 0 when unknown. On a chip that shares a pool with the EPC this is what the ban...
Definition uhf.hpp:382
bool supports_block_permalock
BlockPermaLock support, as the XTID reports it.
Definition uhf.hpp:392
uint16_t mdid
Raw mask-designer identifier (9 bits)
Definition uhf.hpp:377
uint16_t model_number
Tag model number (12 bits)
Definition uhf.hpp:378
Tid tid
TID starting at word 0.
Definition uhf.hpp:374
std::string chipAsString() const
Chip name, "Unknown" until the tag has been identified.
Definition uhf.hpp:1600
bool valid() const
Holds a usable EPC?
Definition uhf.hpp:395
uint16_t serial_bits
Definition uhf.hpp:379
uint16_t pc
Protocol Control.
Definition uhf.hpp:368
Vendor vendor
Named mask designer, Unknown when mdid is not listed.
Definition uhf.hpp:376
uint32_t epc_max_bits
Largest EPC the chip accepts, 0 when unknown.
Definition uhf.hpp:383
TID of a tag.
Definition uhf.hpp:213
bool empty() const
Holds no TID?
Definition uhf.hpp:236
uint8_t & operator[](const size_t i)
Byte at the given index.
Definition uhf.hpp:223
uint8_t operator[](const size_t i) const
Byte at the given index.
Definition uhf.hpp:218
uint8_t size
Number of valid bytes.
Definition uhf.hpp:215
bool assign(const uint8_t *src, const size_t len)
Copy in from a raw buffer, refusing anything longer than TID_MAX_BYTES.
Definition uhf.hpp:1505
std::array< uint8_t, TID_MAX_BYTES > data
Bytes, only the first size are valid.
Definition uhf.hpp:214
std::string toString() const
Uppercase hex.
Definition uhf.hpp:1536
What to read next of a tag's TID, given what has been read of it so far.
Definition uhf.hpp:1112
uint16_t words
Words to read, zero once nothing is left to read.
Definition uhf.hpp:1114
uint16_t word_address
Word address the next read starts at.
Definition uhf.hpp:1113
bool fits
False when the header describes more than a Tid can hold.
Definition uhf.hpp:1115
A named mask designer paired with the name the registry spells it out as.
Definition uhf.hpp:290
const char * name
Name as the GS1 registry gives it.
Definition uhf.hpp:292
Vendor vendor
Enumerator.
Definition uhf.hpp:291
constexpr uint16_t NXP_CONFIG_CONDITIONAL_SHORT
Which way the condition is read.
Definition uhf.hpp:1332
size_t xtidTotalWords(const uint16_t xtid_header)
Length of the whole TID an XTID header describes, in 16-bit words.
Definition uhf.hpp:893
uint32_t chipSharedUserMemoryBits(const Chip chip, const uint16_t pc)
User memory of a chip that carves it out of one pool with the EPC, in bits.
Definition uhf.hpp:809
uint32_t chipEpcMaxBits(const Chip chip)
Largest EPC a chip is known to accept, in bits.
Definition uhf.hpp:476
bool append_unique(std::vector< Tag > &dst, const Tag &tag)
Append a tag unless an identical EPC is already present.
Definition uhf.hpp:1770
constexpr uint16_t NXP_CONFIG_RAW_DATA_MODE
Cleared when the tag loses power.
Definition uhf.hpp:1330
bool tidHasXtid(const uint8_t *tid, const size_t len)
Does a TID say it carries an extended TID, and with it a serial number?
Definition uhf.hpp:850
constexpr uint16_t GEN2_CRC16_POLYNOMIAL
Polynomial of the EPC Gen2 CRC-16 (x^16 + x^12 + x^5 + 1)
Definition uhf.hpp:1659
constexpr uint16_t NXP_CONFIG_DIGITAL_OUTPUT
Drives the chip's output pad.
Definition uhf.hpp:1334
uint32_t buildLockPayload(const LockSetting *settings, const size_t count)
Build the 20-bit payload of the Gen2 Lock command.
Definition uhf.hpp:1283
bool isPermanent(const LockAction action)
Is this action one of the two that cannot be undone?
Definition uhf.hpp:1268
TidReadPlan tidReadPlan(const uint8_t *tid, const size_t len)
Say what to read next of a TID.
Definition uhf.hpp:1129
constexpr uint16_t NXP_CONFIG_EXTERNAL_SUPPLY
Read only. External supply present.
Definition uhf.hpp:1327
NxpConfigWord decodeNxpConfigWord(const uint16_t word)
Spell out the Config-Word of an NXP UCODE G2iM or G2iM+.
Definition uhf.hpp:1383
const char * reasonAsString(const Reason reason)
Spell out a Reason.
Definition uhf.hpp:127
Support tagQTSupport(const Tag &tag)
What can be said about this tag having the Impinj QT command.
Definition uhf.hpp:1576
Vendor resolveVendor(const uint16_t mdid)
Resolve a named mask designer from its identifier.
Definition uhf.hpp:950
constexpr size_t XTID_FIXED_WORDS
Words the two fixed TID words and the XTID header take up.
Definition uhf.hpp:884
constexpr uint16_t NXP_CONFIG_PROTECT_TID
TID reads back as zeroes.
Definition uhf.hpp:1338
void fillSizesFromChip(Tag &tag)
Fill in what the tag left unsaid from what its chip is known to hold.
Definition uhf.hpp:1073
constexpr size_t SELECT_MASK_MAX_BYTES
Longest select mask the module accepts, the length field being 8 bits wide.
Definition uhf.hpp:1315
Chip chipFromTid(const uint8_t *tid, const size_t len)
Name the chip from the two fixed words of a TID.
Definition uhf.hpp:1098
constexpr uint16_t XTID_USER_MEMORY_BLOCKPERMALOCK
bit 10, two words
Definition uhf.hpp:876
constexpr uint16_t XTID_EXTENDED_HEADER
bit 0, header continues
Definition uhf.hpp:878
uint32_t chipPermalockBlockBits(const Chip chip)
Size of the block BlockPermalock works in, in bits.
Definition uhf.hpp:718
uint16_t chipTidWords(const Chip chip)
Words of TID a chip carries without an XTID header to say so.
Definition uhf.hpp:541
uint8_t pcEPCLengthWords(const uint16_t pc)
EPC length in 16-bit words encoded in the PC.
Definition uhf.hpp:1705
uint16_t xtidSerialBits(const uint16_t xtid_header)
Serial length an XTID header announces.
Definition uhf.hpp:862
Session
EPC Gen2 session.
Definition uhf.hpp:57
SelectFilter
Which tags an inventory round invites, in terms of the SL flag.
Definition uhf.hpp:73
@ NotSelected
Only tags whose SL flag is deasserted.
@ Selected
Only tags whose SL flag is asserted.
@ All
Every tag answers, whatever its SL flag says.
@ AllAlias
Same as All; the standard leaves this encoding equivalent.
uint16_t pcNumberingSystemIdentifier(const uint16_t pc)
Numbering System Identifier of the PC.
Definition uhf.hpp:1758
std::string to_hex(const uint8_t *data, const size_t len)
Render a byte buffer as uppercase hex.
Definition uhf.hpp:1461
Support
What can be said about a chip having a command.
Definition uhf.hpp:596
@ Yes
Said to have it.
@ No
Said not to have it.
constexpr uint16_t XTID_LOCK_BIT
bit 9, one word
Definition uhf.hpp:877
uint16_t gen2_crc16(const uint8_t *data, const size_t len)
Calculate the EPC Gen2 CRC-16.
Definition uhf.hpp:1669
constexpr size_t TID_CHIP_WORDS
Words at the front of a TID that name the chip rather than the individual tag.
Definition uhf.hpp:914
Target
EPC Gen2 inventoried flag target.
Definition uhf.hpp:63
constexpr size_t TID_MAX_BYTES
How much of the TID identify() keeps.
Definition uhf.hpp:165
constexpr uint16_t NXP_CONFIG_INVERT_OUTPUT
Cleared when the tag loses power.
Definition uhf.hpp:1328
constexpr uint8_t TID_CLASS_EPCGLOBAL
ISO/IEC 15963 allocation class identifier used by EPCglobal tags.
Definition uhf.hpp:839
bool chipHasNoUserMemory(const Chip chip)
Is this a chip whose datasheet says it has no User bank at all?
Definition uhf.hpp:778
const VendorEntry * vendorEntries(size_t &count)
The mask designers this library names.
Definition uhf.hpp:302
LockAction
What a lock setting does.
Definition uhf.hpp:1244
@ PermanentLock
Not writeable from any state. Cannot be undone.
@ Lock
Writeable from the secured state only.
@ Open
Writeable from either the open or the secured state.
@ PermanentOpen
Permanently writeable and can never be locked. Cannot be undone.
constexpr uint16_t NXP_CONFIG_TRANSPARENT_MODE
Cleared when the tag loses power.
Definition uhf.hpp:1329
Support tagBlockPermalockSupport(const Tag &tag)
What can be said about this tag having BlockPermalock.
Definition uhf.hpp:1561
Support chipNxpCustomCommandSupport(const Chip chip)
What the chip's datasheet says about it having the commands NXP added of its own.
Definition uhf.hpp:685
constexpr uint16_t GEN2_CRC16_INIT
Initial value of the EPC Gen2 CRC-16.
Definition uhf.hpp:1657
bool pcXPCIndicator(const uint16_t pc)
XPC indicator of the PC.
Definition uhf.hpp:1730
Support tagNxpCustomCommandSupport(const Tag &tag)
What can be said about this tag having the commands NXP added of its own.
Definition uhf.hpp:1592
bool decodeTid(Tag &tag, const uint8_t *tid, const size_t len)
Decode the fixed part of a TID into a tag.
Definition uhf.hpp:1176
Support chipBlockPermalockSupport(const Chip chip)
What the chip's datasheet says about it having BlockPermalock.
Definition uhf.hpp:623
bool pcToggle(const uint16_t pc)
Toggle bit of the PC.
Definition uhf.hpp:1744
constexpr uint16_t XTID_OPTIONAL_COMMAND_SUPPORT
bit 12, one word
Definition uhf.hpp:874
Chip
Chip a tag is built around, resolved from the mask-designer identifier and model number.
Definition uhf.hpp:332
constexpr size_t TID_FIXED_WORDS
Words every TID starts with, which say what the chip is and whether an XTID follows.
Definition uhf.hpp:882
Chip resolveChip(const Vendor vendor, const uint16_t model_number)
Resolve a chip from its mask designer and model number.
Definition uhf.hpp:970
bool pcUserMemoryIndicator(const uint16_t pc)
User Memory Indicator of the PC.
Definition uhf.hpp:1720
bool tagUnchanged(const Reason reason)
Is the tag known to be as it was before the operation?
Definition uhf.hpp:121
constexpr size_t EPC_MAX_BYTES
Longest EPC the standard allows: the PC length field is 5 bits, so 31 words.
Definition uhf.hpp:158
Support chipQTSupport(const Chip chip)
What the chip's datasheet says about it having the Impinj QT command.
Definition uhf.hpp:663
Reason
Why an operation meant to change a tag did not change it.
Definition uhf.hpp:90
@ ReaderFault
The reader answered, and said it had not done it.
@ Malformed
The answer could not be read.
@ MemoryOverrun
The address or length reaches past what the tag holds.
@ NoSelection
No tag had been selected.
@ AccessFailed
The reader could not get the tag into the state the command needs.
@ InsufficientPower
The tag has not the power to carry it out.
@ Unsupported
This chip is said not to have the command at all.
@ Locked
The memory is locked against this.
@ Busy
The reader could not make room to send it.
@ NoAnswer
Nobody answered. Whether the tag acted on it cannot be told from here.
@ Refused
Declined without saying which of these it was.
@ BadArgument
The request cannot succeed as it was asked.
constexpr uint16_t NXP_CONFIG_TAMPER_ALARM
Read only. Tag Tamper Alarm.
Definition uhf.hpp:1326
bool tidTellsTagsApart(const uint8_t *tid, const size_t len)
Can a mask built from this TID tell one tag of the chip from another?
Definition uhf.hpp:932
bool verify_tag_crc(const Tag &tag)
Verify the CRC-16 reported with a detected tag.
Definition uhf.hpp:1681
uint32_t chipPermalockLastBlockBits(const Chip chip)
How many bits the last block of a chip actually covers.
Definition uhf.hpp:754
constexpr uint16_t NXP_CONFIG_PSF_ALARM
Product Status Flag, read by EAS.
Definition uhf.hpp:1339
constexpr uint16_t NXP_CONFIG_MAX_BACKSCATTER
Set as the chip leaves the factory.
Definition uhf.hpp:1333
constexpr uint16_t NXP_CONFIG_PROTECT_EPC
EPC reads back as zeroes.
Definition uhf.hpp:1337
uint32_t chipUserMemoryBits(const Chip chip)
User memory a chip is known to hold, in bits.
Definition uhf.hpp:412
Vendor
Mask designers this library knows by name.
Definition uhf.hpp:258
constexpr uint16_t NXP_CONFIG_PROTECT_USER
User memory reads back as zeroes.
Definition uhf.hpp:1336
constexpr uint16_t XTID_BLOCKWRITE_BLOCKERASE
bit 11, four words
Definition uhf.hpp:875
Bank
EPC Gen2 memory bank.
Definition uhf.hpp:32
@ User
User memory.
@ Reserved
Kill password and access password.
constexpr uint16_t NXP_CONFIG_RANGE_REDUCTION
Range reduction, G2iM+ only.
Definition uhf.hpp:1335
constexpr uint16_t GEN2_CRC16_XOROUT
Final xor value of the EPC Gen2 CRC-16.
Definition uhf.hpp:1661
Region
Operating region.
Definition uhf.hpp:44
@ Unspecified
Keep the module's factory setting.
const char * supportAsString(const Support support)
Spell out a Support.
Definition uhf.hpp:603
constexpr uint16_t NXP_CONFIG_CONDITIONAL_RANGE
Conditional read range reduction.
Definition uhf.hpp:1331
uint16_t chipPermalockBlockCount(const Chip chip)
How many blocks a chip divides its user memory into for BlockPermalock.
Definition uhf.hpp:567
LockTarget
What a lock setting applies to, in the order EPC Gen2 lays them out.
Definition uhf.hpp:1235
uint16_t word_at(const uint8_t *data, const size_t word_index)
Read one big-endian 16-bit word out of a byte buffer.
Definition uhf.hpp:281