changeset 1199:87283af13f15

starting pn532 rfid reader in nim Ignore-this: ee51d4acec4b7f88bb7f0e7491da7b8f darcs-hash:43821dc27f532f22fe549ad05e5cd15e08395c52
author drewp <drewp@bigasterisk.com>
date Tue, 19 Feb 2019 12:08:22 -0800
parents 971462233d15
children fe97a15ddd6e
files service/rfid_pn532/graphserver.nim service/rfid_pn532/rfid.nim service/rfid_pn532/tags.nim
diffstat 3 files changed, 263 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid_pn532/graphserver.nim	Tue Feb 19 12:08:22 2019 -0800
@@ -0,0 +1,89 @@
+import asynchttpserver, asyncdispatch, asyncnet, strtabs, sequtils, times, os, strutils, strformat
+
+type
+  GraphServer* = ref object of RootObj
+    port*: int
+    clients: seq[AsyncSocket]
+    previousUpdateTime: int
+    httpServer: AsyncHttpServer
+    serverReady: Future[void]
+    
+proc handleCORS(req: Request) {.async.} =
+  await req.respond(Http204, "", newHttpHeaders({
+    "Access-Control-Allow-Origin": "*",
+    "Connection": "close"}))
+
+proc handle404(req: Request) {.async.} =
+  let headers = newHttpHeaders({"Content-Type": "text/plain",
+                                "Connection": "close"})
+
+  await req.respond(Http404, "File not found", headers)
+  req.client.close()
+
+proc handleSSE(self: GraphServer, req: Request) {.async.} =
+  let headers = newHttpHeaders({"Content-Type": "text/event-stream",
+                                "Access-Control-Allow-Origin": "*",
+                                "Cache-Control": "no-cache",
+                                "Connection": "keep-alive"})
+
+  await req.client.send("HTTP/1.1 200 OK\c\L")
+  await req.sendHeaders(headers)
+  await req.client.send("\c\L:ok\n\n")
+  self.clients.add(req.client)
+
+proc handleConnections(self: GraphServer, req: Request) {.async.} =
+  let clientCount = self.clients.len
+  let headers = newHttpHeaders({"Content-Type": "text/plain",
+                                "Access-Control-Allow-Origin": "*",
+                                "Cache-Control": "no-cache",
+                                "Connection": "close"})
+
+  await req.respond(Http200, $clientCount, headers)
+  req.client.close()
+
+proc requestCallback(self: GraphServer, req: Request) {.async.} =
+  if req.reqMethod == HttpOptions:
+    asyncCheck handleCORS(req)
+  else:
+    case req.url.path
+    of "/connections": asyncCheck self.handleConnections(req)
+    of "/sse": asyncCheck self.handleSSE(req)
+    else: asyncCheck handle404(req)
+
+proc newGraphServer*(port: int): GraphServer =
+  new(result)
+  result.port = port
+  result.previousUpdateTime = toInt(epochTime() * 1000)
+
+  result.httpServer = newAsyncHttpServer(true)
+  let self = result
+  self.serverReady = self.httpServer.serve(
+    Port(self.port),
+    proc (req: Request): Future[void] = self.requestCallback(req),
+    address="0.0.0.0")
+  asyncCheck self.serverReady
+  echo "Listening on " & $self.port
+  
+
+proc checkClients(self: GraphServer) =
+  self.clients = self.clients.filterIt(not it.isClosed())
+
+proc pingClients(self: GraphServer) {.async.} =
+  let currentTime = toInt(epochTime() * 1000)
+
+  if currentTime - self.previousUpdateTime < 1000:
+    return
+
+  for client in self.clients:
+    if not client.isClosed():
+      asyncCheck client.send("data: " & $currentTime & "\n\n")
+
+  self.previousUpdateTime = toInt(epochTime() * 1000)
+
+proc run*(self: GraphServer) =
+  while true:
+    self.checkClients()
+    asyncCheck self.pingClients()
+    poll()
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid_pn532/rfid.nim	Tue Feb 19 12:08:22 2019 -0800
@@ -0,0 +1,41 @@
+# make rfid && make build_image_pi
+# docker pull bang6:5000/rfid_pn532_pi && docker run --rm -it --name rfid --net=host --privileged bang6:5000/rfid_pn532_pi
+
+import nfc-nim/freefare
+import strformat
+import strutils
+import graphserver
+import tags
+
+var nn = newNfcDevice()
+
+while true:
+  echo "loop"
+
+  nn.forAllTags proc (tag: NfcTag) = 
+    if tag.tagType() == freefare.MIFARE_CLASSIC_1K:
+      echo &"found mifare 1k"
+    else:
+      echo &" unknown tag type {freefare.freefare_get_tag_friendly_name(tag.tag)}"
+      return
+
+    echo &"  uid {tag.uid()}"
+
+    tag.connect()
+    try:
+      echo &" block1: {tag.readBlock(1).escape}"
+      #tag.writeBlock(1, toBlock("helloworld"))
+    finally:
+      tag.disconnect()
+
+    if false:
+      var data: freefare.MifareClassicBlock
+      data[0] = cast[cuchar](5)
+      data[1] = cast[cuchar](6)
+      data[2] = cast[cuchar](7)
+      tag.writeBlock(1, data)
+
+nn.destroy()
+
+let server = newGraphServer(port = 10012)
+server.run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid_pn532/tags.nim	Tue Feb 19 12:08:22 2019 -0800
@@ -0,0 +1,133 @@
+import logging
+import sequtils
+import strformat
+import strutils
+import sugar
+import times
+
+import nfc-nim/nfc, nfc-nim/freefare
+
+var L = newConsoleLogger()
+addHandler(L)
+
+proc check(succeed: bool, msg: string) =
+  if not succeed:
+    let e = new(IOError)
+    e.msg = msg
+    raise e
+
+proc check(p: ptr, msg: string) =
+  check(not isNil(p), msg)
+    
+proc check(ret: int, msg: string) =
+  check(ret == 0, &"{msg} ({ret})")
+
+type NfcDevice = ref object of RootObj
+  context: ptr nfc.context
+  dev*: ptr nfc.device
+  
+type NfcTag* {.byref.} = object 
+  tag*: FreefareTag
+
+proc toBlock*(s: string): MifareClassicBlock =
+  for i in 0..result.high:
+    if i < s.len:
+      result[i] = s[i]
+    else:
+      result[i] = '\x00'
+
+proc toString*(b: MifareClassicBlock): string =
+  cast[array[16,char]](b).join
+  
+proc newNfcDevice*(): NfcDevice =
+  new result
+  nfc.init(addr result.context)
+
+  var connstrings: array[10, nfc.connstring]
+  var n = nfc.list_devices(result.context,
+                           cast[ptr nfc.connstring](addr connstrings),
+                           len(connstrings))
+  info(&"{n} connection strings")
+  for i in 0 ..< n:
+    info(&"  dev {i}: {join(connstrings[i])}")
+
+  info("open dev")
+  result.dev = nfc.open(result.context, connstrings[0])
+  let dev = result.dev
+  check(device_get_last_error(dev),
+        &"nfc.open failed on {join(connstrings[0])}")
+
+type TagArray {.unchecked.} = array[999, ptr FreefareTag]
+
+proc getTags(dev: ptr nfc.device): ptr FreefareTag =
+  info("getting tags")
+  let t0 = epochTime()
+  var ret: ptr FreefareTag = freefare.freefare_get_tags(dev)
+  check(ret, "freefare_get_tags returned null")
+  info(&"found tags in {epochTime() - t0}")
+  return ret
+
+# freefare lib wants to free all the tag memory, so process them in a
+# callback and don't keep them outside that.
+proc forAllTags*(self: var NfcDevice, onTag: (NfcTag) -> void) =
+  var ret = getTags(self.dev)
+  var tagList = cast[TagArray](ret)
+  for tagp in tagList:
+    if isNil(tagp):
+      break
+    if cast[int](tagp) < 10:
+      # pointer value looks wrong
+      break
+ 
+    let tag: FreefareTag = tagp[]
+    if isNil(tag):
+      break
+    onTag(NfcTag(tag: tag))
+  freefare.freefare_free_tags(ret)
+
+proc tagType*(self: NfcTag): freefare.freefare_tag_type =
+  freefare.freefare_get_tag_type(self.tag)
+
+proc uid*(self: NfcTag): cstring =
+  freefare.freefare_get_tag_uid(self.tag)
+
+proc connect*(self: NfcTag) =
+  check(freefare.mifare_classic_connect(self.tag), "connect")
+     
+proc disconnect*(self: NfcTag) =
+  check(freefare.mifare_classic_disconnect(self.tag), "disconnect")
+  
+proc destroy*(self: var NfcDevice) =
+  nfc.close(self.dev)
+  nfc.exit(self.context)
+
+var pubkey: MifareClassicKey = [cast[cuchar](0xff),
+                                cast[cuchar](0xff),
+                                cast[cuchar](0xff),
+                                cast[cuchar](0xff),
+                                cast[cuchar](0xff),
+                                cast[cuchar](0xff)]
+    
+proc readBlock*(self: NfcTag, blockNumber: int): string =
+  var blockNum = cast[freefare.MifareClassicBlockNumber](blockNumber)
+  check(freefare.mifare_classic_authenticate(
+    self.tag, blockNum, pubkey, freefare.MFC_KEY_A),
+        &"mifare_classic_authenticate() failed")
+
+  var data: freefare.MifareClassicBlock
+
+  check(mifare_classic_read(self.tag, blockNum, addr data),
+        "classic_read() failed")
+  return toString(data)
+
+proc writeBlock*(self: NfcTag,
+                blockNumber: int,
+                data: freefare.MifareClassicBlock) =
+  var blocknum = cast[freefare.MifareClassicBlockNumber](blockNumber)
+  check(freefare.mifare_classic_authenticate(
+    self.tag, blocknum, pubkey, freefare.MFC_KEY_A),
+        &"mifare_classic_authenticate() failed")
+
+  check(mifare_classic_write(self.tag, blocknum, data),
+        "classic_write() failed")
+  info(&"  wrote block {blocknum}: {data}")