Skip to content

Commit 5f23435

Browse files
committed
Support NFC Type 2 NDEF record parsing & PN5180
1 parent ca73269 commit 5f23435

File tree

2 files changed

+84
-20
lines changed

2 files changed

+84
-20
lines changed

lib/nfc_handler.py

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def _handle_iso14443a_cards(self, reader) -> bool:
177177
)
178178
if len(uids) >= 1:
179179
card = session.connect_iso14443a(uids[0])
180-
self._read_from_card(card)
180+
self._read_from_card(card, True)
181181
return True
182182
return False
183183

@@ -190,7 +190,7 @@ def _handle_iso15693_cards(self, reader):
190190
)
191191
if len(uids) >= 1:
192192
card = session.connect_iso15693(uids[0])
193-
self._read_from_card(card)
193+
self._read_from_card(card, False)
194194
return True
195195
return False
196196

@@ -228,28 +228,91 @@ def stop(self) -> None:
228228
"""Call to stop the handler"""
229229
self._should_stop_event.set()
230230

231-
def _read_from_card(self, card) -> None:
231+
class _Tag: # pylint: disable=too-few-public-methods
232+
def __init__(self, card):
233+
self._card = card
234+
self._records = None
235+
236+
def _read_field(self, mem: bytes, offset: int) -> tuple[int, int | None]:
237+
if offset >= len(mem):
238+
return (-1, None)
239+
val = mem[offset]
240+
offset += 1
241+
if val < 255:
242+
return (val, offset)
243+
if offset + 2 > len(mem):
244+
return (-1, None)
245+
val = (mem[offset] << 8) | mem[offset + 1]
246+
offset += 2
247+
return (val, offset)
248+
249+
def _find_ndef_offset(self, mem: bytes) -> int | None:
250+
if len(mem) < 32:
251+
return None
252+
if mem[12] != 0xE1:
253+
return None
254+
offset: int | None = 16
255+
while offset is not None and offset < len(mem):
256+
(typ, new_offset) = self._read_field(mem, offset)
257+
if new_offset is None:
258+
return None
259+
offset = new_offset
260+
if typ == 0x00:
261+
continue
262+
(length, new_offset) = self._read_field(mem, offset)
263+
if new_offset is None:
264+
return None
265+
offset = new_offset
266+
if typ == 0x03:
267+
break
268+
offset += length
269+
if offset is None or offset >= len(mem):
270+
return None
271+
272+
return offset
273+
274+
@property
275+
def records(self) -> list[ndef.Record]:
276+
"""Return parsed NDEF Records"""
277+
if self._records is None:
278+
mem = b""
279+
try:
280+
offset = 0
281+
while True:
282+
# print(f"Reading from offset {offset}")
283+
chunk = self._card.read_memory(offset // 4, 64)
284+
offset += len(chunk)
285+
mem += chunk
286+
except TimeoutError:
287+
pass
288+
289+
ndef_offset = self._find_ndef_offset(mem)
290+
if ndef_offset is not None:
291+
self._records = list(ndef.message_decoder(mem[ndef_offset:]))
292+
else:
293+
self._records = []
294+
return self._records
295+
296+
def _read_from_card(self, card, parse_ndef: bool) -> None:
232297
"""Read data from tag and call callback"""
233298
if self._on_nfc_tag_present:
234299
identifier: str = card.uid.hex(":")
235300

236-
# pylint: disable=fixme
237-
# TODO: Read memory and parse NDEF
238-
mem = b""
239-
try:
240-
offset = 0
241-
while True:
242-
chunk = card.read_memory(offset // 4, 64)
243-
# print(f"Read {len(chunk)}")
244-
offset += len(chunk)
245-
mem += chunk
246-
except TimeoutError:
247-
pass
248-
249-
# print(f"Decoding:\nLEN: {len(mem)}\n{mem.hex(' ')}")
250-
# print(mem)
251-
252-
self._on_nfc_tag_present(mem, identifier)
301+
if parse_ndef:
302+
tag = self._Tag(card)
303+
self._on_nfc_tag_present(tag, identifier)
304+
else:
305+
mem = b""
306+
try:
307+
offset = 0
308+
while True:
309+
# print(f"Reading from offset {offset}")
310+
chunk = card.read_memory(offset // 4, 64)
311+
offset += len(chunk)
312+
mem += chunk
313+
except TimeoutError:
314+
pass
315+
self._on_nfc_tag_present(mem, identifier)
253316

254317

255318
def create_nfc_handler(nfc_device: str, implementation: str = "nfcpy") -> NfcInterface:

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cbor2==5.8.0
22
flask==3.0.3
33
Gunicorn==23.0.0
4+
ndeflib==0.3.3
45
nfcpy==1.0.4
56
npyscreen==4.10.5
67
pn5180-tagomatic==0.0.3

0 commit comments

Comments
 (0)