initial
commit
924ef93dd8
|
@ -0,0 +1,58 @@
|
|||
# doorman2: electric boogaloo
|
||||
|
||||
- `esp32/` contains the micropython source which talks w/ NFC module and keypad over UART (two channels)
|
||||
- `keypad/` has some magical Arduino code (sorry)
|
||||
|
||||
## syncing data from LDAP
|
||||
|
||||
big TODO; currently, you need to:
|
||||
1. clone the old doorman repo and patch the `doorman_ldap_sync` file (see my shitty patch attached below)
|
||||
|
||||
```
|
||||
--- a/admin/bin/doorman_ldap_sync
|
||||
+++ b/admin/bin/doorman_ldap_sync
|
||||
@@ -63,14 +63,18 @@ def get_target_cards(c):
|
||||
|
||||
if __name__ == "__main__":
|
||||
url = argv[1] if len(argv) > 1 else options.url
|
||||
- token = get_token()
|
||||
- proto = Proto(url)
|
||||
+ #token = get_token()
|
||||
+ #proto = Proto(url)
|
||||
|
||||
c = ldap.initialize('ldap://ldap.hackerspace.pl')
|
||||
c.start_tls_s()
|
||||
- c.simple_bind_s('uid=%s,ou=People,dc=hackerspace,dc=pl' % (getpass.getuser(),), getpass.getpass('LDAP password: '))
|
||||
+ c.simple_bind_s('uid=%s,ou=People,dc=hackerspace,dc=pl' % ('sdomi',), getpass.getpass('LDAP password: '))
|
||||
target = get_target_cards(c)
|
||||
- cur = get_current_cards(token, proto)
|
||||
+ pprint.pprint(target)
|
||||
+ for h, u in target:
|
||||
+ print(h)
|
||||
+
|
||||
+ #cur = get_current_cards(token, proto)
|
||||
|
||||
to_remove = cur - target
|
||||
to_add = target - cur
|
||||
```
|
||||
|
||||
2. launch the script, copy all the lines with the hashes and save them a file
|
||||
3. `mpremote fs cp hashes :hashes`
|
||||
|
||||
plans: web UI like vuko's design
|
||||
|
||||
## esp <-> keypad protocol definition
|
||||
|
||||
- one byte per command, no delimeters, keypad is supposed to be as stateless as possible
|
||||
- the keypad can only send numbers and the hash symbol
|
||||
- ESP can send the following commands:
|
||||
- `H` ("happy" noise, success; turns on the green LED for a second and turns it back off)
|
||||
- `S` ("sad" failure noise; likewise with the red LED)
|
||||
- `F` ("flush"; turns off LEDs and tries to bring the env to a sane level)
|
||||
- `G`, `R` ("green", "red"; turns on the specific LEDs)
|
||||
- `Q` ("quiet"; turns on audible keypresses)
|
||||
|
||||
|
||||
---
|
||||
|
||||
i am so sorry
|
|
@ -0,0 +1,53 @@
|
|||
from pn532 import PN532Uart
|
||||
import utime
|
||||
import machine
|
||||
import hashlib
|
||||
|
||||
DEBUG = True
|
||||
|
||||
try:
|
||||
door = machine.Pin(2, machine.Pin.OUT)
|
||||
door.value(0)
|
||||
|
||||
uart = machine.UART(1, tx=16, rx=17, baudrate=9600)
|
||||
uart.write("F")
|
||||
rf = PN532Uart(2, tx=22, rx=19)
|
||||
rf.SAM_configuration()
|
||||
ic, ver, rev, support = rf.get_firmware_version()
|
||||
print('Found PN532 with firmware version: {0}.{1}'.format(ver, rev))
|
||||
except Exception as e:
|
||||
rf = None
|
||||
print('No NFC reader (PN532) detected')
|
||||
|
||||
while rf is not None:
|
||||
try:
|
||||
uid = rf.read_passive_target()
|
||||
print("Card UUID: " + ''.join('{:02x}'.format(x) for x in uid))
|
||||
card=''.join('{:02x}'.format(x) for x in uid)
|
||||
uart.write("QG")
|
||||
uart.flush()
|
||||
c=0
|
||||
buf=''
|
||||
success = False
|
||||
while True:
|
||||
if uart.any():
|
||||
c=c+1
|
||||
buf+=str(chr(uart.read()[0]))
|
||||
if c == 4:
|
||||
break
|
||||
hash=hashlib.sha256(str("{0:#0{1}x}".format(int(buf),10)+":"+card[6:8]+card[4:6]+card[2:4]+card[0:2])[2:].encode()).digest().hex()
|
||||
f = open("hashes")
|
||||
for user in f:
|
||||
if(user.strip() == hash):
|
||||
success = True
|
||||
if(success):
|
||||
uart.write("FH")
|
||||
door.value(1)
|
||||
utime.sleep(2)
|
||||
door.value(0)
|
||||
else:
|
||||
uart.write("FS")
|
||||
utime.sleep(2)
|
||||
buf=''
|
||||
except Exception as e:
|
||||
print('timeout!')
|
|
@ -0,0 +1,211 @@
|
|||
from micropython import const
|
||||
import machine
|
||||
import utime
|
||||
|
||||
# from pn532uart import PN532_UART
|
||||
# import PN532
|
||||
|
||||
# PN532 Commands
|
||||
_WAKEUP = b'\x55\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
_COMMAND_GETFIRMWAREVERSION = const(0x02)
|
||||
_COMMAND_INLISTPASSIVETARGET = const(0x4A)
|
||||
_COMMAND_INRELEASE = const(0x52)
|
||||
_COMMAND_SAMCONFIGURATION = const(0x14)
|
||||
|
||||
# Send Frames
|
||||
_PREAMBLE = const(0x00)
|
||||
_STARTCODE1 = const(0x00)
|
||||
_STARTCODE2 = const(0xFF)
|
||||
_POSTAMBLE = const(0x00)
|
||||
|
||||
# Message parts
|
||||
_HOSTTOPN532 = const(0xD4)
|
||||
_PN532TOHOST = const(0xD5)
|
||||
_ACK = b'\x00\x00\xFF\x00\xFF\x00'
|
||||
_FRAME_START = b'\x00\x00\xFF'
|
||||
|
||||
# Codes
|
||||
_MIFARE_ISO14443A = const(0x00)
|
||||
|
||||
class PN532Uart(object):
|
||||
"""
|
||||
Class for interacting with the PN532 via the uart interface.
|
||||
"""
|
||||
def __init__(self, uart_no, tx=None, rx=None, debug=False):
|
||||
if tx and rx:
|
||||
self.uart = machine.UART(uart_no, baudrate=115200, tx=tx, rx=rx)
|
||||
else:
|
||||
self.uart = machine.UART(uart_no, baudrate=115200)
|
||||
|
||||
self.debug = debug
|
||||
# self.swriter = asyncio.StreamWriter(self.uart, {})
|
||||
# self.sreader = asyncio.StreamReader(self.uart)
|
||||
|
||||
def wait_read_len(self, len):
|
||||
timeout_ms = 1000
|
||||
start_time = utime.ticks_ms()
|
||||
end_time = start_time + timeout_ms
|
||||
while self.uart.any() < len and utime.ticks_ms() < end_time:
|
||||
continue
|
||||
|
||||
if(utime.ticks_ms() >= end_time):
|
||||
raise RuntimeError('No response from PN532!')
|
||||
|
||||
return self.uart.read(len)
|
||||
|
||||
def _write_frame(self, data):
|
||||
"""Write a frame to the PN532 with the specified data bytearray."""
|
||||
assert data is not None and 1 < len(data) < 255, 'Data must be array of 1 to 255 bytes.'
|
||||
|
||||
# Build frame to send as:
|
||||
# - Preamble (0x00)
|
||||
# - Start code (0x00, 0xFF)
|
||||
# - Command length (1 byte)
|
||||
# - Command length checksum
|
||||
# - Command bytes
|
||||
# - Checksum
|
||||
# - Postamble (0x00)
|
||||
length = len(data)
|
||||
frame = bytearray(length+8)
|
||||
frame[0] = _PREAMBLE
|
||||
frame[1] = _STARTCODE1
|
||||
frame[2] = _STARTCODE2
|
||||
checksum = sum(frame[0:3])
|
||||
frame[3] = length & 0xFF
|
||||
frame[4] = (~length + 1) & 0xFF
|
||||
frame[5:-2] = data
|
||||
checksum += sum(data)
|
||||
frame[-2] = ~checksum & 0xFF
|
||||
frame[-1] = _POSTAMBLE
|
||||
|
||||
# Send frame.
|
||||
if self.debug:
|
||||
print('_write_frame: ', [hex(i) for i in frame])
|
||||
|
||||
# HACK! Timeouts can cause there to be data in the read buffer that was for an old command (ie read_passive_target).
|
||||
# Additonally, the device needs to be woken sometimes, and it seems safe to do that before every command.
|
||||
# TODO: Does WAKEUP cancel the passive read_command?
|
||||
# Before sending the real command, clear the read buffer
|
||||
self.uart.write(_WAKEUP)
|
||||
|
||||
waiting = self.uart.any()
|
||||
while waiting > 0:
|
||||
if self.debug:
|
||||
print("Removing %d bytes in the read buffer")
|
||||
self.uart.read(waiting)
|
||||
waiting = self.uart.any()
|
||||
|
||||
self.uart.write(bytes(frame))
|
||||
#self.uart.flush()
|
||||
|
||||
ack = self.wait_read_len(len(_ACK))
|
||||
|
||||
if self.debug:
|
||||
print('_write_frame: ACK: ', [hex(i) for i in ack])
|
||||
if ack != _ACK:
|
||||
raise RuntimeError('Did not receive expected ACK from PN532!')
|
||||
|
||||
def _read_frame(self):
|
||||
"""
|
||||
Read a response frame from the PN532 and return the data inside the frame,
|
||||
otherwise raises an exception if there is an error parsing the frame.
|
||||
"""
|
||||
# Read the Frame start and header
|
||||
response = self.wait_read_len(len(_FRAME_START)+2)
|
||||
if self.debug:
|
||||
print('_read_frame: frame_start + header:', [hex(i) for i in response])
|
||||
|
||||
if len(response) < (len(_FRAME_START) + 2) or response[:-2] != _FRAME_START:
|
||||
raise RuntimeError('Response does not begin with _FRAME_START!')
|
||||
|
||||
# Read the header (length & length checksum) and make sure they match.
|
||||
frame_len = response[-2]
|
||||
frame_checksum = response[-1]
|
||||
if (frame_len + frame_checksum) & 0xFF != 0:
|
||||
raise RuntimeError('Response length checksum did not match length!')
|
||||
|
||||
# read the frame (data + data checksum + end frame) & validate
|
||||
data = self.wait_read_len(frame_len+2)
|
||||
|
||||
if self.debug:
|
||||
print('_read_frame: data: ', [hex(i) for i in data])
|
||||
|
||||
checksum = sum(data) & 0xFF
|
||||
if checksum != 0:
|
||||
raise RuntimeError('Response checksum did not match expected value: ', checksum)
|
||||
|
||||
if data[-1] != 0x00:
|
||||
raise RuntimeError('Response does not include Frame End')
|
||||
|
||||
# Return frame data.
|
||||
return data[0:frame_len]
|
||||
|
||||
def call_function(self, command, params=[]):
|
||||
"""
|
||||
Send specified command to the PN532 and return the response.
|
||||
Note: There is no timeout option. Use async.wait_for(function(), timeout) instead
|
||||
"""
|
||||
data = bytearray(2 + len(params))
|
||||
data[0] = _HOSTTOPN532
|
||||
data[1] = command & 0xFF
|
||||
for i, val in enumerate(params):
|
||||
data[2+i] = val
|
||||
|
||||
# Send the frame and read the response
|
||||
self._write_frame(data)
|
||||
response = self._read_frame()
|
||||
|
||||
if len(response) < 2:
|
||||
raise RuntimeError('Received smaller than expected frame')
|
||||
|
||||
if not(response[0] == _PN532TOHOST and response[1] == (command+1)):
|
||||
raise RuntimeError('Received unexpected command response!')
|
||||
|
||||
# Return response data.
|
||||
return response[2:]
|
||||
|
||||
def SAM_configuration(self):
|
||||
if self.debug:
|
||||
print("Sending SAM_CONFIGURATION")
|
||||
|
||||
response = self.call_function(_COMMAND_SAMCONFIGURATION, params=[0x01, 0x14, 0x01])
|
||||
if self.debug:
|
||||
print('SAM_configuration:', [hex(i) for i in response])
|
||||
|
||||
def get_firmware_version(self):
|
||||
"""
|
||||
Call PN532 GetFirmwareVersion function and return a tuple with the IC,
|
||||
Ver, Rev, and Support values.
|
||||
"""
|
||||
if self.debug:
|
||||
print("Sending GET_FIRMWARE_VERSION")
|
||||
|
||||
response = self.call_function(_COMMAND_GETFIRMWAREVERSION)
|
||||
if response is None:
|
||||
raise RuntimeError('Failed to detect the PN532')
|
||||
return tuple(response)
|
||||
|
||||
def read_passive_target(self, card_baud=_MIFARE_ISO14443A):
|
||||
"""
|
||||
Wait for a MiFare card to be available and return its UID when found.
|
||||
Will wait up to timeout seconds and return None if no card is found,
|
||||
otherwise a bytearray with the UID of the found card is returned.
|
||||
"""
|
||||
if self.debug:
|
||||
print("Sending INIT_PASSIVE_TARGET")
|
||||
# Send passive read command for 1 card. Expect at most a 7 byte UUID.
|
||||
response = self.call_function(_COMMAND_INLISTPASSIVETARGET, params=[0x01, card_baud])
|
||||
|
||||
# Check only 1 card with up to a 7 byte UID is present.
|
||||
if response[0] != 0x01:
|
||||
raise RuntimeError('More than one card detected!')
|
||||
if response[5] > 7:
|
||||
raise RuntimeError('Found card with unexpectedly long UID!')
|
||||
|
||||
# Return UID of card.
|
||||
return response[6:6+response[5]]
|
||||
|
||||
def release_targets(self):
|
||||
if self.debug:
|
||||
print("Release Targets")
|
||||
response = self.call_function(_COMMAND_INRELEASE, params=[0x00])
|
|
@ -0,0 +1,7 @@
|
|||
# keypad howto
|
||||
|
||||
1) flash MiniCore for atmega168pa
|
||||
2) make sure you have the Keypad arduino library
|
||||
3) if re-making the hw from scratch, you need to make some HW mods to the keypad:
|
||||
- green led moved to a different pin (due to UART conflict)
|
||||
4) verify the pin IDs
|
|
@ -0,0 +1,157 @@
|
|||
/* @file HelloKeypad.pde
|
||||
|| @version 1.0
|
||||
|| @author Alexander Brevig
|
||||
|| @contact alexanderbrevig@gmail.com
|
||||
||
|
||||
|| @description
|
||||
|| | Demonstrates the simplest use of the matrix Keypad library.
|
||||
|| #
|
||||
*/
|
||||
#include <Keypad.h>
|
||||
#define BUZZER PIN_PB2
|
||||
#define LED_RED PIN_PD4
|
||||
#define LED_GREEN PIN_PC5
|
||||
|
||||
const byte ROWS = 4; //four rows
|
||||
const byte COLS = 3; //three columns
|
||||
char keys[ROWS][COLS] = {
|
||||
{'1','2','3'},
|
||||
{'4','5','6'},
|
||||
{'7','8','9'},
|
||||
{'*','0','#'}
|
||||
};
|
||||
byte rowPins[ROWS] = {PIN_PC1, PIN_PC0, PIN_PB5, PIN_PB4}; //connect to the row pinouts of the keypad
|
||||
byte colPins[COLS] = {PIN_PB3, PIN_PB0, PIN_PD5}; //connect to the column pinouts of the keypad
|
||||
|
||||
bool quiet = true;
|
||||
String buf;
|
||||
|
||||
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
|
||||
|
||||
void(* reset) (void) = 0;
|
||||
|
||||
void click() {
|
||||
if (!quiet) {
|
||||
pinMode(BUZZER, OUTPUT);
|
||||
// too tired to check how to do proper PWM lol
|
||||
for(int i=0;i<10;i++) {
|
||||
digitalWrite(BUZZER, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(BUZZER, LOW);
|
||||
delay(1);
|
||||
}
|
||||
pinMode(BUZZER, INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
void sad() {
|
||||
pinMode(BUZZER, OUTPUT);
|
||||
for(int i=0;i<75;i++) {
|
||||
digitalWrite(BUZZER, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(BUZZER, LOW);
|
||||
delay(1);
|
||||
}
|
||||
for(int i=0;i<50;i++) {
|
||||
digitalWrite(BUZZER, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(BUZZER, LOW);
|
||||
delay(2);
|
||||
}
|
||||
pinMode(BUZZER, INPUT);
|
||||
}
|
||||
|
||||
void happy() {
|
||||
pinMode(BUZZER, OUTPUT);
|
||||
for(int i=0;i<25;i++) {
|
||||
digitalWrite(BUZZER, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(BUZZER, LOW);
|
||||
delay(3);
|
||||
}
|
||||
for(int i=0;i<40;i++) {
|
||||
digitalWrite(BUZZER, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(BUZZER, LOW);
|
||||
delay(2);
|
||||
}
|
||||
for(int i=0;i<45;i++) {
|
||||
digitalWrite(BUZZER, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(BUZZER, LOW);
|
||||
delay(1);
|
||||
}
|
||||
pinMode(BUZZER, INPUT);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
Serial.begin(9600);
|
||||
keypad.setDebounceTime(1);
|
||||
pinMode(LED_RED, OUTPUT);
|
||||
pinMode(LED_GREEN, OUTPUT);
|
||||
digitalWrite(LED_GREEN, 0);
|
||||
delay(1000);
|
||||
digitalWrite(LED_GREEN, 1);
|
||||
happy();
|
||||
}
|
||||
|
||||
void loop(){
|
||||
char key = keypad.getKey();
|
||||
|
||||
if (key) {
|
||||
/*
|
||||
we don't really have a use for #/* keys; thus, we can define
|
||||
magic key combos!
|
||||
|
||||
currently:
|
||||
- *1337## resets the keypad
|
||||
*/
|
||||
if (key == '*') {
|
||||
quiet = false;
|
||||
buf="";
|
||||
int i=0;
|
||||
while (true) {
|
||||
key = keypad.getKey();
|
||||
if (key) {
|
||||
click();
|
||||
buf+=key;
|
||||
i++;
|
||||
}
|
||||
if (i>6) break;
|
||||
if (buf == "1337##") {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
quiet = true;
|
||||
}
|
||||
click();
|
||||
Serial.println(key);
|
||||
}
|
||||
while (Serial.available() > 0) {
|
||||
char a = Serial.read();
|
||||
if (a == 'H') {
|
||||
digitalWrite(LED_RED, HIGH);
|
||||
digitalWrite(LED_GREEN, LOW);
|
||||
happy();
|
||||
delay(1000);
|
||||
digitalWrite(LED_GREEN, HIGH);
|
||||
} else if (a == 'S') {
|
||||
digitalWrite(LED_GREEN, HIGH);
|
||||
digitalWrite(LED_RED, LOW);
|
||||
sad();
|
||||
delay(1000);
|
||||
digitalWrite(LED_RED, HIGH);
|
||||
} else if (a == 'F') { // flush
|
||||
digitalWrite(LED_GREEN, HIGH);
|
||||
digitalWrite(LED_RED, HIGH);
|
||||
quiet = true;
|
||||
pinMode(BUZZER, INPUT); // to be sure ;3
|
||||
} else if (a == 'G') { // green
|
||||
digitalWrite(LED_GREEN, LOW);
|
||||
} else if (a == 'R') { // red
|
||||
digitalWrite(LED_RED, LOW);
|
||||
} else if (a == 'Q') {
|
||||
quiet = false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue