Overview

The luftschleuse2 project is the successor to our luftschleuse (air lock) project. The luftschleuse project used a Fonera (http://wiki.openwrt.org/toh/fon/fonera) wireless router, some external electronics and a motor to drive a key inside a lock (http://www.eq-3.de/produkt-detail-aktoren/items/hm-sec-key-o.html).

The system worked for a few years, but started to break down due to mechanical wear of the motor and hacked together electronics.

After moving to a new location, our hacker space needed a new system to keep control over the door. It had to fulfill the following requirements:

  • Electronic keys which can be activated and revoked as needed.
  • Support for more than one door.
  • Support for different modes of operation to change the state of multiple doors at the same time.
  • A way to determine the current state if the system to announce if the hacker space is open.
  • As part of the CCC (https://ccc.de) we oppose the collection of user data. Therefore the system should not collect data about its users.
  • A more reliable system which can operate many years.
  • A better user interface to asses the state of the system.
  • The option to open it via SSH from the outside.

The luftschleuse2 project aims at meeting these requirements.

The system consists of the following components:

  • The Master: An embedded computer which runs Linux. This can be a Raspberry Pi, a Carambola ( http://8devices.com/wiki_carambola/doku.php/carambola) or some OpenWRT capable router. The Master also provides the WiFi to control the system.
  • The Master Controller: An embedded device which interfaces the Master to the outside world. It connects to the Master via a serial link (or a USB to serial adapter). It also controls the user interface (Buttons, LEDs and an LCD).
  • One ore more Lock Controllers. The Lock Controllers actually drive the locks inside the doors.
  • Electrical door locks. These replace normal door locks completely. The contain a small electrical motor which can lock and unlock the door. An example is the Dorma SVP 2719 (http://www.terschluesen-shop.de/Dorma-SVP-2719).
  • An encrypted data bus which also supplies the Lock Controllers with a 12 V supply voltage.
  • A 12 V power supply with battery backup.

Simulation

All components of the system can be simulated on a normal PC without any additional hardware. Please have a look at simulation

Software

This section explains the basic concepts of the software which controls the system. The luftschleuse2 installation at our hacker space (the muccc) is sometimes used as an example configuration. It consists of a Master, a Master Controller and two Lock Controllers (for the front door and the back door).

The complete system is covered by the GPL 3 or similar licenses. The source code can be obtained at https://github.com/muccc/luftschleuse2. There is also an issue tracker with known bugs and feature requests at https://github.com/muccc/luftschleuse2/issues?state=open.

lockd is a daemon which runs on the Master. It connects to the Master Controller via serial connection. It contains the complete logic of the system and decides when to open or close a door, which mode is announced and what the user interface displays.

It's sources can be found inside the repository at software/lockd

lockd connects to the master controller via serial interface. It listens on UDP port 2323 on the loopback interface for commands. The origin for these command can be any other process on the Master. These commands are then parsed and executed by lockd. It is therefore important to keep the system where lockd runs as secure as possible.

The daemon also sends out the current state of the system to another network device via UDP packets. The target and port of these packets can be chosen freely. It can be used to announce the state of the access system on a website or chat.

lockd also sends out extensive log data via syslog. This can be used to debug a running the system. The target IP and port can also be configured.

The user interface connected to the master is also controlled by lockd. Lockd renders the contents of the LCD, controls the LEDs and performs actions when buttons get pressed.

To run lockd, the following libraries have to be installed on the system: python-serial and PIL must be installed. You can either chose to create a virtualenv for this or just install the libraries in the system.

To run lockd just execute it software/lockd/lockd.py with a configuration file as an argument. There are example configuration in the same directory. In the event that lockd crashes, it should be restarted. In our setup we just added it to /etc/inittab with the following line:

::respawn:/root/start-lockd

/root/start-lockd looks like this:

#!/bin/sh
sleep 5
. /root/lockd-env/bin/activate >> /tmp/crashlog
cd /root/lockd
python lockd.py /root/lockd.cfg 2>> /tmp/crashlog

It first activates a virtualenv with the correct libraries, goes to the root directory of lockd and launches it. If anything goes wrong before lockd can create a syslog client, the error message is recorded in /tmp/crashlog.

lockd is configured by a configuration file. The configuration file of our installation looks like this:

[Front Door]
type = door
address = A
key = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
inital_unlock = False
sequence_number_container_file = /root/lockd/front_door_rx_seq
rx_sequence_leap = 32768
timeout = 2

[Back Door]
type = door
address = B
key = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
inital_unlock = True
sequence_number_container_file = /root/lockd/back_door_rx_seq
rx_sequence_leap = 32768
timeout = 2

[Master Controller]
serialdevice = /dev/ttyS0
baudrate = 115200

[Master Controller Buttons]
down = 1
closed = 2
member = 4
public = 8

[Master Controller LEDs]
down = 0
closed = 1
member = 2

[Logging]
host = w.x.y.z
port = 23514

[Display]
# Can either be None, Nokia_1600, simulation or network
display_type = Nokia_1600
max_update_rate = .5

[Status Receiver]
host = localhost
port = 2081

It defines the following sections:

  1. Each door has its own section. This configuration has two doors, a back door and a front door. It contains the following entries:
    • type: marks the section as a door section. The name of the section defines the name of the door.
    • address: The address of the corresponding Lock Controller on the bus. Must be an ASCII character and not '0','9' or '\'
    • key: The AES key which is used to communicate with the Lock Controller.
    • initial_unlock: Defines if the door should be opened when someone from the outside want to get in.
    • sequence_number_container_file: A file which stores the minimum accepted sequence number, which should be used after a restart of lockd.
    • rx_sequence_leap: The number of sequence numbers to skip when it reached the persisted sequence number. Please have a look at https://github.com/muccc/luftschleuse2/blob/develop/docs/LOCK-BUS-PROTOCOL for more details on this.
    • timeout: Time in seconds after which the Lock Controller is assumed to be unreachable if it does not react to commands. This is used to inform an administrator about a malfunction.
  2. The connection to the [Master Controller]:
    • serialdevice: The serial device to be used
    • baudrate: The baud rate to be used. If this is changed, the Master Controller firmware must also be changed.
  3. The [Master Controller Buttons]:
    • Each entry represents a mapping of a physical input (for buttons and switches) to a name which will be used inside lockd. The values in this example are tailored for our system. Your system might have the need for more or less inputs.
  4. The [Master Controller LEDs]
    • Each entry represents a mapping of a physical output (for LEDs) to a name which will be used inside lockd. The values in this example are tailored for our system. Your system might have the need for more or less outputs.
  5. The [Logging] section configures the target for the syslog of lockd:
    • host: The target host where to log to. (An IP is preferred to avoid lots of DNS lookups)
    • port: The port to which the UDP log messages are sent.
  6. The [Display] section configures the display which is attached to the Master Controller:
    • display_type: Displays the type of the display. The only real display supported at the moment is the Nokia 1600.
    • max_update_rate: Rate at which the display is updated at maximum. Used to not congest the serial line.
  7. The [Status Receiver] section configures the host and port where UDP packets with the current system status are sent.

The behavior of the system is define in the following files: doorlogic.py interfacelogic.py and displaylogic.py

If you want to change the functionality of the system, you have to change the code inside these files. We might generalize the system in to future to use configuration files, but for now its easier to just change these files directly.

doorlogic.py

doorlogic.py defines how the system reacts to input to the system. It also defines the different states of the system.

The code is centered around the following function:

def policy(self, origin_name, origin_type, input_name, input_type, input_value):

It takes inputs from different named origins (like “Front Door” or “Back Door”) which are classified by a type (currently defined: DOOR, NETWORK, CONTROL_PANEL and INTERNAL), contain data about a specific input (like “public” or “member” in the case of buttons) with a specific type (currently defined BUTTON, COMMAND, SWITCH and SENSOR) and the data of the input (e.g. True or False for a switch, or a string for a command which came in via UDP)

The current behavior of the system is as follows:

  • There are two external commands:
    • “unlock” unlocks the “Back Door” for a few seconds.
    • “lock” locks all doors and puts the system into the “DOWN” state.
  • There is support for two inputs from the Lock Controllers.
    • A simple button which when pressed performs the following action:
      • If the door is currently locked, it gets unlocked.
      • If the door is currently unlocked and the state is not “PUBLIC”, the door gets locked.
      • If the door is currently unlocked and the state is “PUBLIC”, all doors get locked and the state changes to “MEMBER”.
      • In any other case, the door gets locked.
    • When the Lock Controller signals a correct bell code via the “bell_code” input:
      • If the door is locked and the state is 'MEMBER', the door gets unlocked for a few seconds.
  • If a button on the control panel (which consists of the buttons and the LEDs of the Master Controller) is pressed:
    • If the button is the “down” button, all doors get locked and the state changes to “DOWN”.
    • If the button is the “closed” button, all doors get locked and the state changes to “CLOSED”.
    • If the button is the “member” button, all doors get locked and the state changes to “MEMBER”.
    • If the button is the “public” button, all doors get unlocked and the state changes to “PUBLIC”.
  • If a button on the control panel is released:
    • If the button is the “public” button, all doors get locked and the state changes to “MEMBER”.
  • If the input is an internal command:
    • If the input value is “down”, all doors get locked and the state changes to “DOWN”.

This logic introduces four states:

  • DOWN
    • The “DOWN” state signals that no one is anymore at the hacker space and the electricity has been turned off.
  • CLOSED
    • The “CLOSED” state signals that someone might still be at the hacker space but might be sleeping.
  • MEMBER
    • The “MEMBER” state signals that people are awake and can have an eye on the doors. In this mode the bell codes of the doors are activated and anyone who known the bell code can enter the hacker space.
  • PUBLIC
    • The “PUBLIC” state signals that all doors are unlocked and everyone from the general public is invited to come to the hack space.

interfacelogic.py

The interface logic defines what the LEDs of the master controller should do. It is based around the update_state(self, state) function. This function gets called every time the state of a component of the system changes.

The current behavior is:

  • In “DOWN” mode:
    • Turn on the “down” LED if all doors are locked, turn off all other LEDs.
    • Blink the “down” LED if not all doors are locked, turn off all other LEDs.
  • In “CLOSED” mode:
    • Turn on the “closed” LED if all doors are locked, turn off all other LEDs.
    • Blink the “closed” LED if not all doors are locked, turn off all other LEDs.
  • In “MEMBER” mode:
    • Turn on the “member” LED if all doors are locked, turn off all other LEDs.
    • Blink the “member” LED if not all doors are locked, turn off all other LEDs.
  • In “PUBLIC” mode:
    • Turn on all LEDs if not all doors are unlocked.
    • Blink all LEDs if all doors are unlocked.

displaylogic.py

The display logic defines what the LCD attached to the master controller displays:

  • The first line shows the current state in red and a large font.
  • The second and following lines show the names and states of the doors:
    • If the state of the door is known and good, it is displayed in green and a small font.
    • If the state of the door is unknown or bad, it is displayed in yellow and a small font. This can be caused by confusing sensor readings.
    • If the door is not responding, it is displayed in red and a small font.
  • If any door is not responding or shows errors in the bus communication, a large and red “ATTENTION” is rendered.

In our system two system users are defined “open” and “close”. Everyone who gets access to the system gets his/her public key entered into the authorized_keys file of this user. We use git to manager the keys and a commit hook to push the keys to the device if a change to the repository is made. To send the “lock” and “unlock” commands to lockd, the login shells of the users are replaced by shell scripts which just emit the “lock” or “unlock” commands via udp to lockd.

TODO: put exact configuration of the carambola board her.

The Master Controller drives LEDs, an LCD and reads data from buttons, switches and other inputs.

To build the Master Controller you need to have avr-gcc and the avr-libc installed. Got to the software/mastercontroller directory and run make:

cd software/mastercontroller
make

To change the amount and pin configuration of buttons, change the files buttons.h and buttons.c. The order of the buttons in the buttons_button_t enum in buttons.h defines the order in which they can be accessed by the [Master Controller Buttons] section of the lockd configuration file.

To change the amount and pin configuration of the LEDs, change the files leds.h and leds.c. The order of the LEDs in the function leds_set_pin(leds_led_t led, bool on) defines the order in which they can be accessed by the [Master Controller LEDs] section of the lockd configuration file.

No further configuration should be needed for the Master Controller.

The Lock Controller drives the actual door lock as well as a few LEDs and buttons (most pins of the micro controller will be used for communication and the door lock).

The code for the Lock Controllers can be found in the software/lockcontroller directory. However, please to not try to build a Lock Controller from this directory. Instead have a look at the software/front-door directory. It contains the configuration for the front door and a link to the Makefile. You can run make in this directory:

cd software/front-door
make

The configuration is inside door-config.h:

#define DOOR_MODEL  DOOR_MODEL_2
#define NODE_ADDRESS    'A' 
#define AES_KEY 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0

This will build an image for a lock controller which controls an SVP<(Do we have the SVP2719 or the SVP3XXX in the Front Door?)> an listens to address A. The key must match the key inside the configuration fill of lockd.

The configuration of the buttons and LEDs is similar to the configuration of the Master Controller.

As different doors can have different types of locks, the Lock Controller contains a driver which abstracts the handling of a specific lock and exposes a common interface to lockd. To add support for a new lock type, add the name of the lock type to doors.h and software/common/config.h. Now you can add new door-xyz.c and door-xyz.c files and register the driver inside the Makefile.

The driver exposes the lock trough the following functions:

  • void door_init(void):
    • Initializes the driver.
  • void door_tick(void):
    • Called once every millisecond.
  • void door_process(void):
    • Called as often as possible.
  • uint8_t door_door_getState(void):
    • Return a field of flags which summarize the state of the door. The following flags are specified:
      • DOOR_DOOR_CLOSED: The door is closed and can be safely locked or unlocked.
      • DOOR_LOCK_LOCKED: The door is locked.
      • DOOR_LOCK_UNLOCKED: The door is unlocked. (Optional)
      • DOOR_LOCK_LOCKING: The door is in the process of locking. (Optional)
      • DOOR_LOCK_UNLOCKING: The door is in the process of unlocking. (Optional)
      • DOOR_HANDLE_PRESSED: The door handle is pressed. (Optional)
      • DOOR_LOCK_MANUAL_UNLOCKED: The door got manually unlocked with a key or some other way. (Optional)
  • void door_setDesiredState(uint8_t desiredState):
    • Called periodically with the currently desired state. The following states are defined:
      • DOOR_LOCK_LOCKED: The door should be locked.
      • DOOR_LOCK_UNLOCKED: The door should be unlocked.

In general, the driver has to report the state of the door via the uint8_t door_door_getState(void) function and try to reach the state given by void door_setDesiredState(uint8_t desiredState)

If the Lock Controller does not receive a valid desired door state for some time, it automatically sets the desired door state to closed. This is also the case when a Lock Controller reboots.

Bell Code

The bell code can be defined in bell-process.c. Adjust the length and values in the times[] array. The values are in milliseconds. A negative number denotes a bell press shorter that that time, a positive number indicated a bell press longer that that time. The {MIN|MAX}_{PRESS|RELEASE} macros define limits for the bell presses in milliseconds.

LED

The current code controls a single LED based on the state of the lock. The behavior is defined in command_process.h:

  • If the lock is locked, flash the LED.
  • If the lock is unlocked, turn on the LED.

Lock-Bus-Protocol

For details about the communication between the different device, please have a look at https://github.com/muccc/luftschleuse2/blob/develop/docs/LOCK-BUS-PROTOCOL.

Hardware

TODO: fill me ;)

  • luftschleuse2/overview.txt
  • Last modified: 2021/04/18 12:35
  • (external edit)