155 lines
4.6 KiB
Lua
155 lines
4.6 KiB
Lua
|
-- Copyright (c) 2013 Sergiusz 'q3k' Bazański <q3k@q3k.org>
|
||
|
--
|
||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
-- of this software and associated documentation files (the "Software"), to deal
|
||
|
-- in the Software without restriction, including without limitation the rights
|
||
|
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
-- copies of the Software, and to permit persons to whom the Software is
|
||
|
-- furnished to do so, subject to the following conditions:
|
||
|
--
|
||
|
-- The above copyright notice and this permission notice shall be included in
|
||
|
-- all copies or substantial portions of the Software.
|
||
|
--
|
||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
-- THE SOFTWARE.
|
||
|
|
||
|
|
||
|
-- NFC-related functions.
|
||
|
-- This implementation is for the PN532 chip.
|
||
|
|
||
|
require('socket')
|
||
|
require('bit')
|
||
|
|
||
|
q3k.NFC = {}
|
||
|
|
||
|
local PN532_PREAMBLE = 0x00
|
||
|
local PN532_STARTCODE1 = 0x00
|
||
|
local PN532_STARTCODE2 = 0xFF
|
||
|
local PN532_POSTAMBLE = 0x00
|
||
|
|
||
|
local PN532_HOSTTOPN532 = 0xD4
|
||
|
local PN532_PN532TOHOST = 0xD5
|
||
|
|
||
|
------------------------
|
||
|
-- Internal functions --
|
||
|
------------------------
|
||
|
|
||
|
-- sigh. luaposix on openwrt doesn't have nanosleep()
|
||
|
local Sleep = function(Seconds)
|
||
|
socket.select(nil, nil, Seconds)
|
||
|
end
|
||
|
|
||
|
-- Get the IRQ pin status
|
||
|
local IRQRead = function()
|
||
|
local Data = q3k.I2C.Read(q3k.Config.I2CGPIO, 1, 0x2C)
|
||
|
if Data then
|
||
|
return Data[1]
|
||
|
else
|
||
|
return 1
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Wait „Timeout” seconds for IRQ (or 5, if not given)
|
||
|
local WaitForIRQ = function(Timeout)
|
||
|
local Timeout = Timeout or 5
|
||
|
local WaitStart = os.time()
|
||
|
while os.time() < WaitStart + Timeout do
|
||
|
local IRQStatus = IRQRead()
|
||
|
if IRQStatus == 0 then
|
||
|
break
|
||
|
end
|
||
|
Sleep(0.2)
|
||
|
end
|
||
|
local IRQStatus = IRQRead()
|
||
|
if IRQStatus ~= 0 then
|
||
|
return false
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
-- Write a PN532 command
|
||
|
local WriteCommand = function(...)
|
||
|
local Command = {...}
|
||
|
local Checksum = PN532_PREAMBLE + PN532_STARTCODE1 + PN532_STARTCODE2
|
||
|
local WireCommand = { PN532_PREAMBLE, PN532_STARTCODE1, PN532_STARTCODE2 }
|
||
|
|
||
|
WireCommand[#WireCommand+1] = (#Command + 1)
|
||
|
WireCommand[#WireCommand+1] = bit.band(bit.bnot(#Command +1) + 1, 0xFF)
|
||
|
|
||
|
Checksum = Checksum + PN532_HOSTTOPN532
|
||
|
WireCommand[#WireCommand+1] = PN532_HOSTTOPN532
|
||
|
|
||
|
for _, Byte in pairs(Command) do
|
||
|
Checksum = Checksum + Byte
|
||
|
WireCommand[#WireCommand+1] = Byte
|
||
|
end
|
||
|
|
||
|
WireCommand[#WireCommand+1] = bit.band(bit.bnot(Checksum), 0xFF)
|
||
|
WireCommand[#WireCommand+1] = PN532_POSTAMBLE
|
||
|
|
||
|
q3k.I2C.Write(q3k.Config.I2CNFC, unpack(WireCommand))
|
||
|
end
|
||
|
|
||
|
-- Send a PN532 and see if we get an ACK
|
||
|
local SendAndAck = function(...)
|
||
|
WriteCommand(...)
|
||
|
local IRQArrived = WaitForIRQ()
|
||
|
if not IRQArrived then
|
||
|
return false
|
||
|
end
|
||
|
local ACK = q3k.I2C.Read(q3k.Config.I2CNFC, 8)
|
||
|
if ACK[1] == 1 and ACK[2] == 0 and ACK[3] == 0 and ACK[4] == 255
|
||
|
and ACK[5] == 0 and ACK[6] == 255 and ACK[7] == 0 then
|
||
|
return true
|
||
|
else
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Read a PN532 frame
|
||
|
local ReadFrame = function(Count)
|
||
|
local Bytes = q3k.I2C.Read(q3k.Config.I2CNFC, Count+2)
|
||
|
table.remove(Bytes, 1)
|
||
|
table.remove(Bytes, #Bytes)
|
||
|
return Bytes
|
||
|
end
|
||
|
|
||
|
----------------
|
||
|
-- Public API --
|
||
|
----------------
|
||
|
|
||
|
-- Call this once the I2C bus and GPIO multiplexer are ready
|
||
|
q3k.NFC.Setup = function()
|
||
|
-- enable SAM with stuff.
|
||
|
if SendAndAck(0x14, 0x01, 0x14, 0x01) == false then
|
||
|
print "SAM configuration failed!"
|
||
|
return false
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
-- Waits for a NFC card to appear in the field
|
||
|
-- „Seconds” is how long the reader will block
|
||
|
-- „Callback” is a callback which is called when a card appears, and takes
|
||
|
-- a single argument which is the NFC ID like this: { 0x00, 0x01, 0x02, 0x03 }
|
||
|
q3k.NFC.WaitForCard = function(Seconds, Callback)
|
||
|
if SendAndAck(0x4A, 0x01, 0x0) == false then
|
||
|
print "Sending card read command failed!"
|
||
|
return false
|
||
|
end
|
||
|
if WaitForIRQ(Seconds) then
|
||
|
local Bytes = ReadFrame(20)
|
||
|
if Bytes ~= nil and #Bytes == 20 then
|
||
|
local NFCID = { Bytes[14], Bytes[15], Bytes[16], Bytes[17] }
|
||
|
Callback(NFCID)
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
return true
|
||
|
end
|