2017-04-10 92 views
0

我打算在我的項目中使用Adafruit PN532 nfc https://www.adafruit.com/product/364分支卡。Python非阻塞pn532標籤讀取

有沒有辦法在python(使用中斷?)中寫一個非阻塞代碼,以避免這個代碼示例中的循環?

https://github.com/adafruit/Adafruit_Python_PN532/blob/master/examples/readmifare.py

+0

你需要查找事件循環和事件驅動的編碼風格嘗試的代碼。 Python 3.x的asyncio模塊具有內置的基本事件處理程序類。https://docs.python.org/3/library/asyncio-eventloop.html – Aaron

+1

最終,必須阻止某事等待事件,並且事件處理程序將該事件發送到正確的回調函數進行處理。只要回調立即返回,就沒有問題。如果你需要從api補丁解決方案,你不能或不想改變。將阻塞調用放入一個線程中,讓線程更新一個值並回調輪詢該值(帶鎖和任何您想要的安全內容) – Aaron

+0

有趣的是,adafruit庫中的超時關鍵字被忽略 – Aaron

回答

1

(相對於我的意見..)雖然asyncio有點重量更輕,threading也有一個事件呼叫系統,是目前好一點的記錄(線程已經有超過ASYNCIO和有點不太抽象)。據我所知,您希望能夠在處理其他任務時從阻塞資源中讀取數據。在這種情況下,我們將創建一個線程來等待卡被讀取,同時慢慢地將文本文件打印到標準輸出。這只是一個如何做這樣的事情的例子。有很多方法可以完成類似的結果。

import binascii 
import time 
import threading 
from collections import deque #use as a fifo queue 

queue = deque() #queue to pass information from thread to main process 
queue_lock = threading.Lock() #prevent possible issues from 
txt_file = '50_shades_of_grey.txt' #don't ask ;) 
running = True 

def nfc_reader(): 
    #copied from adafruit example 
    import Adafruit_PN532 as PN532 
    CS = 18 
    MOSI = 23 
    MISO = 24 
    SCLK = 25 

    pn532 = PN532.PN532(cs=CS, sclk=SCLK, mosi=MOSI, miso=MISO) 
    pn532.begin() 
    ic, ver, rev, support = pn532.get_firmware_version() 
    print(f'Found PN532 with firmware version: {ver}.{rev}') 
    pn532.SAM_configuration() 

    # Main loop to detect cards and read a block. 
    print('Waiting for MiFare card...') 
    while True: 
     if not running: #cheap way to kill a thread nicely (cheap ain't pretty) 
      return 
     #don't bother specifying a timeout, they forgot to support it in the library 
     uid = pn532.read_passive_target() 
     # Try again if no card is available. 
     if uid is None: 
      continue 
     print('Found card with UID: 0x{0}'.format(binascii.hexlify(uid))) 
     # Authenticate block 4 for reading with default key (0xFFFFFFFFFFFF). 
     if not pn532.mifare_classic_authenticate_block(uid, 4, PN532.MIFARE_CMD_AUTH_B, 
                 [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]): 
      print('Failed to authenticate block 4!') 
      continue 
     # Read block 4 data. 
     data = pn532.mifare_classic_read_block(4) 
     if data is None: 
      print('Failed to read block 4!') 
      continue 
     # make sure our queue is free 
     with queue_lock: 
      # replaced print with deque.append 
      queue.append(f'Read block 4: 0x{binascii.hexlify(data[:4])}') 
     #optionally emit some sort of signal that data is ready. In our case the main loop will chech periodically on it's own 

# Main program 
nfc_thread = threading.Thread(target=nfc_reader) 
nfc_thread.start() 

with open(txt_file, 'r') as f: 
    while True: #also cheap, but easy 
     if queue: #bool(deque) works like bool(list) 
      with queue_lock: 
       print("we found a card!") 
       print(queue.popleft()) 
       continue 
     try: 
      print(next(f)) #otherwise go back to more interesting matters 
     except StopIteration: 
      running = False 
      nfc_thread.join() 
      break 
     time.sleep(.9) #notice loop time will be less than timeout on nfc read. 
         # you could put the printing of the *book* into another thread 
         # and use events to immediately call your return from your 
         # nfc reader
+1

我沒有嘗試過您的代碼'讀書'基地:)感謝您的回答。現在我問自己如何保持程序沒有在覈心中運行一個無限循環.. –

+0

如果你的主線程退出任何子線程之前,你會有孤兒。這些可能會繼續運行,直到他們遇到錯誤(無聲)或內存不足(孤兒院沒有足夠的空間)。最終,必須保證主線程一直持續到你完成額外的線程。請注意,儘管主線程仍然處於活動狀態,但這並不意味着它必須佔用任何大量的CPU或RAM ......事件監聽器可以將CPU降至接近0%,同時靜靜地等待 – Aaron

+0

,您可以對其進行分類你的線程作爲「守護進程」,一旦主線程完成,它將被毫不客氣地刪除。像文件讀/寫這樣的事情不應該在守護進程線程中完成,因爲如果線程在錯誤的時間終止,文件句柄可能會保持打開狀態。 – Aaron

0

下面是我根據@Aaron答案

import binascii 
import time 
import threading 
from collections import deque #use as a fifo queue 
import RPi.GPIO as GPIO 
import Adafruit_PN532 as PN532 

queue = deque() #queue to pass information from thread to main process 
queue_lock = threading.Lock() #prevent possible issues from 
running = True 

def nfc_reader(): 
    CS = 18 
    MOSI = 23 
    MISO = 24 
    SCLK = 25 

    pn532 = PN532.PN532(cs=CS, sclk=SCLK, mosi=MOSI, miso=MISO) 
    pn532.begin() 
    ic, ver, rev, support = pn532.get_firmware_version() 
    print('Found PN532 with firmware version: {0}.{1}'.format(ver, rev)) 
    pn532.SAM_configuration() 

    while True: 
     if not running: #cheap way to kill a thread nicely (cheap ain't pretty) 
      return 

     uid = pn532.read_passive_target() 
     if uid is None: 
      continue 
     #print('card read') 
     message = 'Read card\n{0}'.format(binascii.hexlify(uid)) 

     # make sure our queue is free 
     with queue_lock: 
      # replaced print with deque.append 
      queue.append(message) 
      time.sleep(1) 
     #optionally emit some sort of signal that data is ready. In our case the main loop will chech periodically on it's own 

# Main program 
nfc_thread = threading.Thread(target=nfc_reader) 
nfc_thread.start() 


while True: #also cheap, but easy 
    if queue: #bool(deque) works like bool(list) 
     with queue_lock: 
      print("we found a card!") 
      print(queue.popleft()) 
      continue 
    time.sleep(.9) #notice loop time will be less than timeout on nfc read. 
        # you could put the printing of the *book* into another thread 
        # and use events to immediately call your return from your 
        # nfc reader