Update buildroot stuff
This commit is contained in:
parent
230397f0da
commit
9f02fba74a
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +1,6 @@
|
||||||
[submodule "buildroot"]
|
[submodule "buildroot"]
|
||||||
path = buildroot
|
path = buildroot
|
||||||
url = https://github.com/buildroot/buildroot.git
|
url = https://github.com/buildroot/buildroot.git
|
||||||
|
[submodule "door_control"]
|
||||||
|
path = door_control
|
||||||
|
url = ssh://irgit@imaginaerraum.de:2201/apo/DoorControl.git
|
||||||
|
|
2
br_external/Config.in
Normal file
2
br_external/Config.in
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
source "$BR2_EXTERNAL_DOOR_PI_PATH/package/poll_desfire/Config.in"
|
||||||
|
source "$BR2_EXTERNAL_DOOR_PI_PATH/package/libfreefare_git/Config.in"
|
1
br_external/external.desc
Normal file
1
br_external/external.desc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
name: DOOR_PI
|
1
br_external/external.mk
Normal file
1
br_external/external.mk
Normal file
|
@ -0,0 +1 @@
|
||||||
|
include $(sort $(wildcard $(BR2_EXTERNAL_DOOR_PI_PATH)/package/*/*.mk))
|
12
br_external/package/libfreefare_git/Config.in
Normal file
12
br_external/package/libfreefare_git/Config.in
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
config BR2_PACKAGE_LIBFREEFARE_GIT
|
||||||
|
bool "libfreefare (git)"
|
||||||
|
depends on BR2_TOOLCHAIN_HAS_THREADS # libusb
|
||||||
|
select BR2_PACKAGE_OPENSSL
|
||||||
|
select BR2_PACKAGE_LIBNFC
|
||||||
|
help
|
||||||
|
Library for high level manipulation of MIFARE cards.
|
||||||
|
|
||||||
|
http://nfc-tools.org/index.php?title=Libfreefare
|
||||||
|
|
||||||
|
comment "libfreefare needs a toolchain w/ threads"
|
||||||
|
depends on !BR2_TOOLCHAIN_HAS_THREADS
|
18
br_external/package/libfreefare_git/libfreefare_git.mk
Normal file
18
br_external/package/libfreefare_git/libfreefare_git.mk
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# libfreefare
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
LIBFREEFARE_GIT_VERSION = e95406c
|
||||||
|
# Do not use the github helper here, the generated tarball is *NOT*
|
||||||
|
# the same as the one uploaded by upstream for the release.
|
||||||
|
LIBFREEFARE_GIT_SITE = $(call github,nfc-tools,libfreefare,$(LIBFREEFARE_GIT_VERSION))
|
||||||
|
LIBFREEFARE_GIT_DEPENDENCIES = host-pkgconf libnfc openssl
|
||||||
|
LIBFREEFARE_GIT_INSTALL_STAGING = YES
|
||||||
|
LIBFREEFARE_GIT_LICENSE = LGPL-3.0+ with exception
|
||||||
|
LIBFREEFARE_GIT_LICENSE_FILES = COPYING
|
||||||
|
LIBFREEFARE_GIT_CONF_ENV += LIBS=`$(PKG_CONFIG_HOST_BINARY) --libs openssl`
|
||||||
|
LIBFREEFARE_GIT_AUTORECONF = YES
|
||||||
|
|
||||||
|
$(eval $(autotools-package))
|
7
br_external/package/poll_desfire/Config.in
Normal file
7
br_external/package/poll_desfire/Config.in
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
config BR2_PACKAGE_POLL_DESFIRE
|
||||||
|
bool "poll_desfire"
|
||||||
|
select BR2_PACKAGE_LIBFREEFARE_GIT
|
||||||
|
help
|
||||||
|
poll desfire cards
|
||||||
|
|
||||||
|
https://git.imaginaerraum.de/apo/PollDesfire
|
19
br_external/package/poll_desfire/poll_desfire.mk
Normal file
19
br_external/package/poll_desfire/poll_desfire.mk
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
POLL_DESFIRE_VERSION = 0.0.1
|
||||||
|
POLL_DESFIRE_SOURCE = poll_desfire_v$(POLL_DESFIRE_VERSION).tar.gz
|
||||||
|
POLL_DESFIRE_SITE = https://git.imaginaerraum.de/apo/PollDesfire/archive
|
||||||
|
POLL_DESFIRE_DEPENDENCIES = libfreefare_git host-pkgconf
|
||||||
|
POLL_DESFIRE_LICENSE = GPL-3.0
|
||||||
|
|
||||||
|
define POLL_DESFIRE_BUILD_CMDS
|
||||||
|
$(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) all
|
||||||
|
endef
|
||||||
|
|
||||||
|
define POLL_DESFIRE_INSTALL_STAGING_CMDS
|
||||||
|
$(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) PREFIX=$(STAGING_DIR) install
|
||||||
|
endef
|
||||||
|
|
||||||
|
define POLL_DESFIRE_INSTALL_TARGET_CMDS
|
||||||
|
$(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) PREFIX=$(TARGET_DIR) install
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(generic-package))
|
1
door_control
Submodule
1
door_control
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit e48f0f63385f5990440524c66ba925466f8e292f
|
Binary file not shown.
|
@ -1,340 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
import os, serial, socket, subprocess, select, datetime, sys
|
|
||||||
import paho.mqtt.client as mqcl
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
OPEN_THRESHOLD = 80
|
|
||||||
CLOSED_THRESHOLD = 100
|
|
||||||
CLOSED_WANT = 160
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("--serial_port", default="/dev/serial/by-id/usb-Imaginaerraum.de_DoorControl_43363220195053573A002C0-if01")
|
|
||||||
parser.add_argument("--nfc_fifo", default="/tmp/nfc_fifo")
|
|
||||||
parser.add_argument("--control_socket", default="/tmp/nfc.sock")
|
|
||||||
parser.add_argument("--valid_tokens", default="/etc/door_tokens")
|
|
||||||
parser.add_argument("--log_file", default="/tmp/nfc.log")
|
|
||||||
parser.add_argument("--state_timeout", type=float, default=10)
|
|
||||||
parser.add_argument("--repeat_time", type=float, default=5)
|
|
||||||
parser.add_argument("--mqtt_host", default="10.10.21.2")
|
|
||||||
|
|
||||||
config = parser.parse_args()
|
|
||||||
|
|
||||||
mqttc = mqcl.Client()
|
|
||||||
|
|
||||||
def timestamp():
|
|
||||||
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
# Log data to stdout and the log file
|
|
||||||
def log(*args):
|
|
||||||
data = "%s %s" % (timestamp(), " ".join(str(i) for i in args))
|
|
||||||
lgf.write(data + "\n")
|
|
||||||
lgf.flush()
|
|
||||||
print(data)
|
|
||||||
|
|
||||||
def mqtt(topic, msg, persistent = True):
|
|
||||||
mqttc.publish("door/" + topic, msg, qos=2, retain=persistent)
|
|
||||||
|
|
||||||
# Opens the socket that can control the daemon
|
|
||||||
# Commands are TBD
|
|
||||||
def open_control_socket():
|
|
||||||
global control_socket
|
|
||||||
global config
|
|
||||||
global comm_channels
|
|
||||||
|
|
||||||
if os.path.exists(config.control_socket):
|
|
||||||
os.unlink(config.control_socket)
|
|
||||||
|
|
||||||
control_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
||||||
control_socket.bind(config.control_socket)
|
|
||||||
control_socket.listen(5)
|
|
||||||
comm_channels = []
|
|
||||||
|
|
||||||
# Opens the serial port to talk to the lock actuator
|
|
||||||
# Might return a failure. In that case, it will be tried again later
|
|
||||||
def open_serial_port():
|
|
||||||
global serial_port
|
|
||||||
global config
|
|
||||||
|
|
||||||
try:
|
|
||||||
serial_port = serial.Serial(config.serial_port, timeout=2)
|
|
||||||
except:
|
|
||||||
serial_port = None
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Detected tokens are passed in through a FIFO
|
|
||||||
def open_nfc_fifo():
|
|
||||||
global nfc_fifo
|
|
||||||
global config
|
|
||||||
|
|
||||||
nfc_fifo = open(config.nfc_fifo, "r")
|
|
||||||
|
|
||||||
# Refresh valid tokens from the configured file
|
|
||||||
def read_valid_tokens():
|
|
||||||
global valid
|
|
||||||
global config
|
|
||||||
|
|
||||||
try:
|
|
||||||
log("Loading tokens")
|
|
||||||
valid = {}
|
|
||||||
lines =[ s.strip() for s in open(config.valid_tokens, "r").readlines() ]
|
|
||||||
for l in lines:
|
|
||||||
l = l.split(' ', 1)
|
|
||||||
if len(l) > 1:
|
|
||||||
if l[0] in valid:
|
|
||||||
log("Warning: Overwriting token %s" % (l[0],))
|
|
||||||
log("Got token: %s (%s)" % (l[0], l[1]))
|
|
||||||
valid[l[0]] = l[1]
|
|
||||||
else:
|
|
||||||
log("Got unnamed token: %s" % (l[0],))
|
|
||||||
valid[l[0]] = None
|
|
||||||
except:
|
|
||||||
valid = {}
|
|
||||||
log("Error reading token file")
|
|
||||||
raise
|
|
||||||
|
|
||||||
# Opens the log file for writing
|
|
||||||
def open_logfile():
|
|
||||||
global lgf
|
|
||||||
global config
|
|
||||||
|
|
||||||
lgf = open(config.log_file, "a+")
|
|
||||||
|
|
||||||
# Actions
|
|
||||||
IDLE, CLOSE, OPEN_THEN_CLOSE, OPEN, CLOSE_THEN_OPEN = range(5)
|
|
||||||
state_names = {
|
|
||||||
OPEN: "open",
|
|
||||||
CLOSE: "close"
|
|
||||||
}
|
|
||||||
action_names = {
|
|
||||||
IDLE: "idling",
|
|
||||||
OPEN: "waiting for open door",
|
|
||||||
CLOSE: "waiting for closed door",
|
|
||||||
OPEN_THEN_CLOSE: "waiting for open door, then closing again",
|
|
||||||
CLOSE_THEN_OPEN: "waiting for closed door, then opening again"
|
|
||||||
}
|
|
||||||
# Current door state
|
|
||||||
state = None
|
|
||||||
state_pos = 0
|
|
||||||
# Current target action
|
|
||||||
action = CLOSE
|
|
||||||
# Start time of the current action
|
|
||||||
start_time = None
|
|
||||||
# How often the action was repeated
|
|
||||||
repeats = 0
|
|
||||||
|
|
||||||
# Read the state of the door from the serial port
|
|
||||||
def poll_door_state():
|
|
||||||
global state
|
|
||||||
global state_pos
|
|
||||||
global serial_port
|
|
||||||
serial_port.reset_input_buffer()
|
|
||||||
serial_port.write(b"R")
|
|
||||||
data = serial_port.readline().strip().split()
|
|
||||||
if data[0] == b'pos:':
|
|
||||||
data = int(data[1])
|
|
||||||
if state_pos != data:
|
|
||||||
mqtt("position/value", data, True)
|
|
||||||
state_pos = data
|
|
||||||
changed = False
|
|
||||||
if data < OPEN_THRESHOLD and state != OPEN:
|
|
||||||
state = OPEN
|
|
||||||
changed = True
|
|
||||||
mqtt("state/value", "open", True)
|
|
||||||
elif data > CLOSED_THRESHOLD and state != CLOSE:
|
|
||||||
state = CLOSE
|
|
||||||
changed = True
|
|
||||||
mqtt("state/value", "closed", True)
|
|
||||||
return state
|
|
||||||
|
|
||||||
# Check the door state and send commands through a state machine
|
|
||||||
def handle_door_state():
|
|
||||||
global action, target, serial_port, start_time, repeats
|
|
||||||
|
|
||||||
# Commands associated with each target state
|
|
||||||
target_state_cmd = {
|
|
||||||
OPEN: b'O',
|
|
||||||
CLOSE: b'C'
|
|
||||||
}
|
|
||||||
|
|
||||||
old_state = state
|
|
||||||
|
|
||||||
# If no serial port, try to open it or return
|
|
||||||
if not serial_port:
|
|
||||||
try:
|
|
||||||
open_serial_port()
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get new state
|
|
||||||
poll_door_state()
|
|
||||||
|
|
||||||
# Idle + change = key?
|
|
||||||
if action == IDLE:
|
|
||||||
if state != old_state:
|
|
||||||
log("Door changed unexpectedly:", state_names[state])
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
if start_time and (datetime.datetime.now() - start_time).total_seconds() >= config.state_timeout:
|
|
||||||
start_time = None
|
|
||||||
if state_pos >= CLOSED_THRESHOLD and state_pos < CLOSED_WANT:
|
|
||||||
log("Closing door a bit more")
|
|
||||||
serial_port.write(target_state_cmd[CLOSE])
|
|
||||||
return
|
|
||||||
|
|
||||||
# Target state, next action, timeout action
|
|
||||||
actions = {
|
|
||||||
OPEN: (OPEN, IDLE, CLOSE_THEN_OPEN ),
|
|
||||||
CLOSE: (CLOSE, IDLE, OPEN_THEN_CLOSE ),
|
|
||||||
OPEN_THEN_CLOSE: (OPEN, CLOSE, CLOSE ),
|
|
||||||
CLOSE_THEN_OPEN: (CLOSE, OPEN, OPEN )
|
|
||||||
}
|
|
||||||
|
|
||||||
if start_time == None:
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
serial_port.write(target_state_cmd[actions[action][0]])
|
|
||||||
|
|
||||||
# Target state reached
|
|
||||||
if state == actions[action][0]:
|
|
||||||
# Select next action, reset start time and repetitions
|
|
||||||
action = actions[action][1]
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
repeats = 0
|
|
||||||
# On idle, we're done
|
|
||||||
if action == IDLE:
|
|
||||||
log("Reached target position:", state_names[state])
|
|
||||||
return
|
|
||||||
|
|
||||||
# Execution time
|
|
||||||
t = (datetime.datetime.now() - start_time).total_seconds()
|
|
||||||
if t >= config.state_timeout:
|
|
||||||
# Timeout -> switch to timeout action
|
|
||||||
action = actions[action][2]
|
|
||||||
start_time = None
|
|
||||||
repeats = 0
|
|
||||||
log("Timeout. Switching to", action_names[action])
|
|
||||||
elif t >= (1 + repeats) * config.repeat_time:
|
|
||||||
# Repeat every couple of seconds
|
|
||||||
repeats += 1
|
|
||||||
serial_port.write(target_state_cmd[actions[action][0]])
|
|
||||||
log("Repeating command:", target_state_cmd[actions[action][0]])
|
|
||||||
|
|
||||||
def open_door():
|
|
||||||
global action
|
|
||||||
global start_time
|
|
||||||
log("Opening the door")
|
|
||||||
action = OPEN
|
|
||||||
start_time = None
|
|
||||||
mqtt("state/target", "open", True)
|
|
||||||
handle_door_state()
|
|
||||||
|
|
||||||
def close_door():
|
|
||||||
global action
|
|
||||||
global start_time
|
|
||||||
log("Closing the door")
|
|
||||||
action = CLOSE
|
|
||||||
start_time = None
|
|
||||||
mqtt("state/target", "closed", True)
|
|
||||||
handle_door_state()
|
|
||||||
|
|
||||||
def toggle_door_state():
|
|
||||||
global state, action
|
|
||||||
if state == CLOSE:
|
|
||||||
open_door()
|
|
||||||
else:
|
|
||||||
close_door()
|
|
||||||
|
|
||||||
def handle_nfc_token(token = None):
|
|
||||||
global valid
|
|
||||||
|
|
||||||
if not token:
|
|
||||||
token = nfc_fifo.readline()
|
|
||||||
if token == "":
|
|
||||||
open_nfc_fifo()
|
|
||||||
|
|
||||||
token = token.strip()
|
|
||||||
if token in valid:
|
|
||||||
name = valid[token]
|
|
||||||
if name:
|
|
||||||
name = "%s (%s)" % (token, name)
|
|
||||||
else:
|
|
||||||
name = token
|
|
||||||
log("Valid token: %s" % name)
|
|
||||||
toggle_door_state()
|
|
||||||
else:
|
|
||||||
log("Invalid token:", token)
|
|
||||||
mqtt("token/last_invalid", "%s;%s" % (timestamp(), token))
|
|
||||||
|
|
||||||
class LineBuffer(object):
|
|
||||||
def __init__(self, f, handler):
|
|
||||||
self.data = b''
|
|
||||||
self.f = f
|
|
||||||
self.handler = handler
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
data = self.f.recv(1024)
|
|
||||||
if not data:
|
|
||||||
return False
|
|
||||||
self.data += data
|
|
||||||
d = self.data.split(b'\n')
|
|
||||||
d, self.data = d[:-1], d[-1]
|
|
||||||
for i in d:
|
|
||||||
self.handler(self.f, i)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def handle_cmd(comm, data):
|
|
||||||
cmd = data.decode('utf8').split()
|
|
||||||
cmd, args = cmd[0], cmd[1:]
|
|
||||||
log("Got command:", data)
|
|
||||||
|
|
||||||
send = lambda x: comm.send(x.encode('utf8'))
|
|
||||||
|
|
||||||
if cmd == 'fake':
|
|
||||||
log("Faking token", args[0])
|
|
||||||
send("Handling token\n")
|
|
||||||
handle_nfc_token(args[0])
|
|
||||||
elif cmd == 'open':
|
|
||||||
log("Control socket opening door")
|
|
||||||
send("Opening door")
|
|
||||||
open_door()
|
|
||||||
elif cmd == 'close':
|
|
||||||
log("Control socket closing door")
|
|
||||||
send("Closing door")
|
|
||||||
close_door()
|
|
||||||
elif cmd == 'rld':
|
|
||||||
log("Reloading tokens")
|
|
||||||
send("Reloading tokens")
|
|
||||||
read_valid_tokens()
|
|
||||||
elif cmd == 'stat':
|
|
||||||
send("Door status is %s, position is %d. Current action: %s (%g seconds ago)\n" % (
|
|
||||||
state_names.get(state, "None"),
|
|
||||||
state_pos,
|
|
||||||
action_names[action],
|
|
||||||
(datetime.datetime.now() - start_time).total_seconds()))
|
|
||||||
|
|
||||||
open_logfile()
|
|
||||||
read_valid_tokens()
|
|
||||||
open_nfc_fifo()
|
|
||||||
open_serial_port()
|
|
||||||
open_control_socket()
|
|
||||||
|
|
||||||
mqttc.on_connect = lambda client, userdata, flags, rc: log("Connected to mqtt host with result %s" % (str(rc), ))
|
|
||||||
mqttc.connect_async(config.mqtt_host, keepalive = 60)
|
|
||||||
mqttc.loop_start()
|
|
||||||
|
|
||||||
buffers = {}
|
|
||||||
while True:
|
|
||||||
readable = select.select([ nfc_fifo, control_socket ] + comm_channels, [], [], 1)[0]
|
|
||||||
|
|
||||||
for c in readable:
|
|
||||||
if c == nfc_fifo:
|
|
||||||
handle_nfc_token()
|
|
||||||
elif c == control_socket:
|
|
||||||
log("Got connection")
|
|
||||||
sock = control_socket.accept()[0]
|
|
||||||
buffers[sock] = LineBuffer(sock, handle_cmd)
|
|
||||||
comm_channels += [sock]
|
|
||||||
else:
|
|
||||||
if not buffers[c].update():
|
|
||||||
log("Lost connection")
|
|
||||||
del buffers[c]
|
|
||||||
comm_channels.remove(c)
|
|
||||||
handle_door_state()
|
|
Loading…
Reference in New Issue
Block a user