Occasional write fail

Asked by ian@iwjohnston.com on 2017-01-08

Stephen,

The following code snippet works well generally. However once in a while it fails with the error thrown as shown in the second section below. A timing issue?

Code (part)
# This the tag reader scan loop.

        while True:

        # Scan the tag reader

            with nfc.ContactlessFrontend('usb') as clf:
                def connected(tag): return False
                tag = clf.connect(rdwr={'on-connect':connected})
                if tag.ndef:

                    record1 = tag.ndef.message[0]
                    record2 = tag.ndef.message[1]
                    record3 = tag.ndef.message[2] #Test for tag status.
                    tagcheck = record3.data

                else:
                    print 'NOT A SYSTEM TAG'
                    # Sound the buzzer
                    GPIO.output(17, True)
                    time.sleep(0.2)
                    GPIO.output(17, False)
                    time.sleep(2)
                    continue

                if tagcheck == 'XXX':
                    break

                if tagcheck == 'OUT':
                    tagstatus = ' IN'

                elif tagcheck == ' IN':
                    tagstatus = 'OUT'

                record3 = nfc.ndef.Record("urn:nfc:wkt:T", "Access Status", tagstatus)
                tagtime = str(datetime.now())
                tagtime1 = (tagtime[0:19])
                record4 = nfc.ndef.Record("urn:nfc:wkt:T", "Time Stamp", tagtime1)

                 # Write the tag

                tag = clf.connect(rdwr={'on-connect':connected})
                tag.ndef.message = nfc.ndef.Message(record1, record2, record3, record4)

                 # Now save the new tag data to file

                file_time = tagtime1
                file_name = record1.data
                file_mob = record2.data
                file_status = tagstatus

                working_month = open ('/home/pi/data/' + current_month +'.csv', 'a')
                working_month.write (file_time + ',' + file_name + ',' + file_mob + ','+ file_status+'\n')
                working_month.close()

                 # print (file_time + ' ' + file_name + ' ' + file_mob + ' ' + file_status+'\n')

                 # Light the correct LED

                if tagstatus == ' IN':

                    GPIO.output(5, True)
                    time.sleep (1)
                    GPIO.output (5, False)

                elif tagstatus == 'OUT':

                    GPIO.output(6, True)/Users/ianwj/Desktop/tagFail.tiff
                    time.sleep (1)
                    GPIO.output(6, False)

Error thrown:

Enter your Selection [1-5]: 4
Now in Attendance Mode

USE THE ADMIN TAG TO STOP THE ATTENDANCE MODE

Traceback (most recent call last):
  File "./tagapp3.py", line 162, in <module>
    tag.ndef.message = nfc.ndef.Message(record1, record2, record3, record4)
  File "/usr/local/lib/python2.7/dist-packages/nfc/tag/__init__.py", line 181, in message
    self._write_ndef_data(data)
  File "/usr/local/lib/python2.7/dist-packages/nfc/tag/tt3.py", line 232, in _write_ndef_data
    self._write_attribute_data(attributes)
  File "/usr/local/lib/python2.7/dist-packages/nfc/tag/tt3_sony.py", line 409, in _write_attribute_data
    super(FelicaLite.NDEF, self)._write_attribute_data(attributes)
  File "/usr/local/lib/python2.7/dist-packages/nfc/tag/tt3.py", line 182, in _write_attribute_data
    self._tag.write_to_ndef_service(attribute_data, 0)
  File "/usr/local/lib/python2.7/dist-packages/nfc/tag/tt3_sony.py", line 742, in write_without_mac
    self.write_without_encryption(sc_list, bc_list, data)
  File "/usr/local/lib/python2.7/dist-packages/nfc/tag/tt3.py", line 593, in write_without_encryption
    self.send_cmd_recv_rsp(0x08, data, timeout)
  File "/usr/local/lib/python2.7/dist-packages/nfc/tag/tt3.py", line 651, in send_cmd_recv_rsp
    raise Type3TagCommandError(nfc.tag.TIMEOUT_ERROR)
nfc.tag.tt3.Type3TagCommandError: unrecoverable timeout error
pi@raspberrypi:~ $

Question information

Language:
English Edit question
Status:
Answered
For:
nfcpy Edit question
Assignee:
No assignee Edit question
Last query:
2017-01-11
Last reply:
2017-01-12
ian@iwjohnston.com (ian-5) said : #1

Some more info using a MiFare Desfire card on the same application as above. The occasional error thrown is somewhat different...
*** NEW INFO ***

The name of the new month?: Desfire

Month Desfire was created

------------------------------ MAIN MENU ------------------------------
1. Check a Tag
2. Initialise a Tag
3. Set a new Month
4. Run the Attendance System
5. Exit
-------------------------------------------------------------------
Enter your Selection [1-5]: 4
Now in Attendance Mode

USE THE ADMIN TAG TO STOP THE ATTENDANCE MODE

Traceback (most recent call last):
  File "./tagapp3.py", line 162, in <module>
    tag.ndef.message = nfc.ndef.Message(record1, record2, record3, record4)
AttributeError: 'NoneType' object has no attribute 'message'
pi@raspberrypi:~ $ ./tagapp3.py
       *** ATTENDANCE SYSTEM ***
Password:
           ATTENDANCE SYSTEM

*** CURRENT MONTH IS SET TO default.csv UNLESS SET BY Menu No 3 ***

------------------------------ MAIN MENU ------------------------------
1. Check a Tag
2. Initialise a Tag
3. Set a new Month
4. Run the Attendance System
5. Exit
-------------------------------------------------------------------
Enter your Selection [1-5]: 3

The name of the new month?: Desfire

Month Desfire was created

------------------------------ MAIN MENU ------------------------------
1. Check a Tag
2. Initialise a Tag
3. Set a new Month
4. Run the Attendance System
5. Exit
-------------------------------------------------------------------
Enter your Selection [1-5]: 4
Now in Attendance Mode

USE THE ADMIN TAG TO STOP THE ATTENDANCE MODE

Traceback (most recent call last):
  File "./tagapp3.py", line 162, in <module>
    tag.ndef.message = nfc.ndef.Message(record1, record2, record3, record4)
AttributeError: 'NoneType' object has no attribute 'message'
pi@raspberrypi:~ $

ian@iwjohnston.com (ian-5) said : #2

Stephen,

I have provided my revamped program with added "clf.close()" commands as well as using nfcpy R 0.12. As you can see I am no professional Python person looking at the code structure. However using R 0.12 and the using the clf.close() statements, it has seemed to stabilise a fair bit. I noted your comments re the use of Raspberry Pi3 USB ports and how they were not always stable re write times. Hope this helps
Ian

Program Code

#!/usr/bin/env python

## This is a Python2 program to read NFC tags (cards) that are NDEF complient.
## It is an attendance system that simply tracks tag touches as either "IN' or 'OUT'.
## The actual reading and writing of/to the tags uses the nfcpy module
## from Stephen Tieldemann. It will check (read) tags, write the tags with a predetemined
## set of records which will then be stored to a text file in .csv format such that the
## info can be read into a spreadsheet. It is set so that the admin user can set a
## particular month to be stored. (If no month is selected, a file with the name
## 'default.csv' is used.
## The reading loop (Menu 4) will continue forever until broken out by an Admin
## tag with the status record set to 'XXX' (Record3)
## It has been tested using the SONY USB tag reader (RC-S380) with a Raspberry Pi3
## as the computer on Felica Lite-S tags (Tag Type3) as well as Mifare Desfire tags
## (Tag Type 4A)
## The logic is that each card is programmed with four text records:
##
## 1. The card user name
## 2. The card user mobile phone number
## 3 The card user status as either IN or OUT
## 4. A time stamp giving year-month-day as well as time in hours-min-seconds
##
## It will reject both blank NDEF cards as well as non NDEF cards (i.e. transport cards or similar)
## It also has a very simple password system that is hard coded into the program (needs to be
## much improved as a new feature)

import nfc
import nfc.ndef
import time
import sys, select, os
import getpass
import RPi.GPIO as GPIO

from datetime import datetime

GPIO.setmode(GPIO.BCM) # Use the GPIO pin numbers
GPIO.setwarnings(False)
GPIO.setup(5, GPIO.OUT) # Set up the GREEN led
GPIO.setup(6, GPIO.OUT) # Set up the RED led
GPIO.setup(13, GPIO.OUT) # Set up the In Operation led
GPIO.setup(17, GPIO.OUT) # Set up buzzer pin

## Ask for the Password

password = 'admin'
p = getpass.getpass()

if p.lower() == password:

    _=os.system("clear")
    print " ATTENDANCE SYSTEM"
    print
    print
else:
        print 'Sorry wrong password'
        sys.exit()

## Get the menu posted

print '*** CURRENT MONTH IS SET TO default.csv UNLESS SET BY Menu No 3 ***\n'
print
print
print
global current_month
current_month = 'default'
global working_month

def print_menu():
    print 30 * "-" , "MAIN MENU" , 30 * "-"
    print "1. Check a Tag"
    print "2. Initialise a Tag"
    print "3. Set a new Month"
    print "4. Run the Attendance System"
    print "5. Exit"
    print 67 * "-"

    GPIO.output(13, False) # Turn off "In Operation" LED
loop=True

while loop: ## Stay in the Menu loop until loop = False

    GPIO.output(13, False)
    print_menu() ## Display menu
    choice = input("Enter your Selection [1-5]: ")

    if choice==1:
        with nfc.ContactlessFrontend('usb') as clf:
            print
            print "Place a tag on the reader"
            def connected(tag): return False
            tag = clf.connect(rdwr={'on-connect':connected})
            print "Tag information:"
            print
            print(tag.ndef.message.pretty() if tag.ndef else "***THIS IS NOT A RECOGNISED CARD***")
            clf.close()

    elif choice==2:
        print "Tag Creation App"
        print
        tagname = raw_input("Name of Employee: ")
        tagmob = raw_input("Mobile Number: ")
        print
        print "Default new tag status is OUT"
        tagstatus = "OUT" #Tag will be initialsed as "OUT"
        tagtime = str(datetime.now())
        tagtime1 = (tagtime[0:19])

        record1 = nfc.ndef.Record("urn:nfc:wkt:T", "Staff Member", tagname)
        record2 = nfc.ndef.Record("urn:nfc:wkt:T", "Phone Number", tagmob)
        record3 = nfc.ndef.Record("urn:nfc:wkt:T", "Access Status", tagstatus)
        record4 = nfc.ndef.Record("urn:nfc:wkt:T", "Time Stamp", tagtime1)

        print "Place a tag on the reader"

        with nfc.ContactlessFrontend('usb') as clf:
            def connected(tag): return False
            tag = clf.connect(rdwr={'on-connect':connected})
            tag.ndef.message = nfc.ndef.Message(record1, record2, record3, record4)
            clf.close()

        print
        print "Tag written with the following information:"
        print
        print(tag.ndef.message.pretty())

    elif choice==3:
        print
        #global current_month
        current_month = raw_input("The name of the new month?: ")
        #global working_month
        working_month = open ('/home/pi/data/' + current_month + '.csv', 'a')
        print
        print "Month " + current_month + " was created"
        print
    elif choice==4:
        print 'Now in Attendance Mode'
        print
        GPIO.output(13, True) # Turn on the "In Operation" LED
        print 'USE THE ADMIN TAG TO STOP THE ATTENDANCE MODE'
        print

        # This is the tag reader scan loop. Will run forever until ended by 'XXX' in the status record (record3)
        # i.e. The Admin card

        while True:

        # Scan the tag reader

            with nfc.ContactlessFrontend('usb') as clf:
                def connected(tag): return False
                tag = clf.connect(rdwr={'on-connect':connected})
                if tag.ndef:

                    record1 = tag.ndef.message[0]
                    cardcheck = record1.data
                    if cardcheck == '':
                        # A blank card has been preseted - not allowed
                        print 'THIS APPEARS TO BE A BLANK CARD'
                        clf.close()
                        time.sleep(0.5)
                        continue
                    record2 = tag.ndef.message[1]
                    record3 = tag.ndef.message[2] #Test for tag status.
                    tagcheck = record3.data

                else:
                    print 'NOT A SYSTEM TAG'
                    # Sound the buzzer for a fail read (Long beep)
                    GPIO.output(17, True)
                    time.sleep(0.5)
                    GPIO.output(17, False)
                    time.sleep(2)
                    clf.close()
                    continue

                if tagcheck == 'XXX':
                    break

                if tagcheck == 'OUT':
                    tagstatus = ' IN'

                elif tagcheck == ' IN':
                    tagstatus = 'OUT'

                record3 = nfc.ndef.Record("urn:nfc:wkt:T", "Access Status", tagstatus)
                tagtime = str(datetime.now())
                tagtime1 = (tagtime[0:19])
                record4 = nfc.ndef.Record("urn:nfc:wkt:T", "Time Stamp", tagtime1)

                 # Write the tag

                #tag = clf.connect(rdwr={'on-connect':connected})
                tag.ndef.message = nfc.ndef.Message(record1, record2, record3, record4)
                clf.close()

                # Light the correct LED

                if tagstatus == ' IN':

                    GPIO.output(5, True)
                    time.sleep (1)
                    GPIO.output (5, False)
                    # Sound a short beep to indicate ' IN' status
                    GPIO.output(17, True)
                    time.sleep(0.1)
                    GPIO.output(17, False)

                elif tagstatus == 'OUT':

                    GPIO.output(6, True)
                    time.sleep (1)
                    GPIO.output(6, False)
                    # Sound a short double beep to indicate 'OUT' status
                    GPIO.output(17, True)
                    time.sleep(0.1)
                    GPIO.output(17, False)
                    time.sleep(0.2)
                    GPIO.output(17, True)
                    time.sleep(0.1)
                    GPIO.output(17, False)

                 # Now save the new tag data to file

                file_time = tagtime1
                file_name = record1.data
                file_mob = record2.data
                file_status = tagstatus

                working_month = open ('/home/pi/data/' + current_month +'.csv', 'a')
                working_month.write (file_time + ',' + file_name + ',' + file_mob + ','+ file_status+'\n')
                working_month.close()

                 # print (file_time + ' ' + file_name + ' ' + file_mob + ' ' + file_status+'\n')

    elif choice==5:
        print "Program quit"
        loop=False #Will force end of the while loop

    else:
        print
        raw_input("Wrong option selection. Please try again\n")
        print

In your first version you've done a new search for tags right before writing the new NDEF message (now disabled in your last version). When you search for a tag there's again no guarantee that NDEF is discovered, this can indeed happen if communication is unstable. It is always good to consider that an NFC communication can be interrupted at any time by getting out of range. I'd recommend to use tag.is_present before writing (https://nfcpy.readthedocs.io/en/latest/modules/tag.html#nfc.tag.Tag.is_present).

As another note, you are creating invalid NDEF Text Records (an 'urn:nfc:wkt:T' record has a language and content field in the payload). You should either use the TextRecord class or the 'text/plain' mime type or even use your own record type identifier (if you own a domain name like I do with 'nfcpy.org' it could be like 'urn:nfc:ext:nfcpy.org:T')

As yet another note, if your script is to stay for longer you should be aware that the nfc.ndef package is going away at some future time. It will be replaced by the https://ndeflib.readthedocs.io module (installed automatically with nfcpy when using pip).

As a last note, you don't need to use clf.close() when using the 'with ... as clf:' statement. The close method is called automatically when execution leaves the 'with' context.

Can you help with this problem?

Provide an answer of your own, or ask ian@iwjohnston.com for more information if necessary.

To post a message you must log in.