Machine Shop RFID:rfid access.pl

From Dallas Makerspace
Jump to: navigation, search
#!/usr/bin/perl
#
# RFID access control program v1.0
#
# Copyright (c) 2016, Zach Metzinger
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions 
# are met:
#
# 1. Redistributions of source code must retain the above copyright 
#    notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in 
#    the documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# $Id: rfid_access.pl,v 1.1 2016/01/02 23:15:31 zmetzing Exp $

use warnings;
use strict;
use DB_File;
use Device::SerialPort qw( :PARAM :STAT 0.07 );

# Configuration parameters
my $db_fn = "nfc_auth_tags.db";
my $serial_port_device = "/dev/ttyO2";
my $relay_on_time = 10; # Seconds
# Note: See http://www.armhf.com/using-beaglebone-black-gpios/ for details on the following
my $sys_class_path = "/sys/class/gpio";
my $sys_class_export = "$sys_class_path/export";
my $sys_class_gpio = "$sys_class_path/gpio";
my $red_led_gpio = 32+12;
my $yel_led_gpio = 32+13;
my $grn_led_gpio = 32+15;
my $off_btn_gpio = 32+14; # Not currently used
my $on_relay_gpio = 48;

# Local vars
my %auth_tags;
my $tag_id;
my $nbytes;
my $nfc;
my $sysfh;

# Debugging hexdump subr
sub hexdump($)
{
    my $offset = 0;
        
    foreach my $chunk (unpack "(a16)*", $_[0])
    {
        my $hex = unpack "H*", $chunk; # hexadecimal magic
        $chunk =~ tr/ -~/./c;          # replace unprintables
        $hex   =~ s/(.{1,8})/$1 /gs;   # insert spaces
        printf "0x%08x (%05u)  %-*s %s\n",
            $offset, $offset, 36, $hex, $chunk;
        $offset += 16;
    }
}

# Banner
printf "DMS HAAS RFID access control program v1.0\n";

# Verify existance of database before starting
if (! -e $db_fn) {
    die "Unable to find database nfc_auth_tags.db\n";
}

# Enable LEDs
if (! -e $sys_class_export) {
    die "sysfs path $sys_class_export does not exist, are we on BBB?\n";
}
system("sudo chmod 222 $sys_class_export");
system("echo $red_led_gpio > $sys_class_export");
system("echo $yel_led_gpio > $sys_class_export");
system("echo $grn_led_gpio > $sys_class_export");
system("echo $on_relay_gpio > $sys_class_export");
system("sudo chmod -R go=rwX $sys_class_gpio$red_led_gpio/*");
system("sudo chmod -R go=rwX $sys_class_gpio$yel_led_gpio/*");
system("sudo chmod -R go=rwX $sys_class_gpio$grn_led_gpio/*");
system("sudo chmod -R go=rwX $sys_class_gpio$on_relay_gpio/*");
system("echo high > $sys_class_gpio$red_led_gpio/direction");
system("echo high > $sys_class_gpio$yel_led_gpio/direction");
system("echo high > $sys_class_gpio$grn_led_gpio/direction");
system("echo low > $sys_class_gpio$on_relay_gpio/direction");

# Initialize serial port
$nfc = new Device::SerialPort ($serial_port_device)
    || die "Can't open $serial_port_device: $!\n";

$nfc->user_msg(1);
$nfc->databits(8);
$nfc->baudrate(9600);
$nfc->parity("none");
$nfc->stopbits(1);
$nfc->handshake("none");
$nfc->read_char_time(0);
$nfc->read_const_time(250);
$nfc->write_settings || undef $nfc;

if (!defined($nfc)) {
    die "Unable to open serial port: $!\n";
}

system("stty -F $serial_port_device raw");

printf "Ready to read tags\n";

while (1) {
# Light red LED, others off
    system("echo 0 > $sys_class_gpio$red_led_gpio/value");
    system("echo 1 > $sys_class_gpio$grn_led_gpio/value");
    
# Wait for serial port data to come in
  ($nbytes, $tag_id) = $nfc->read(255);
  if ($nbytes == -1) {
      die "EOF on serial port read\n";
  }

  # Only process if we got after the 1 second read timeout
  if ($nbytes > 0) {
      # FIXME: Dump for debug
      #hexdump($tag_id);
      if (($nbytes != 16) || (ord(substr($tag_id, 0, 1)) != 0x02)) {
	  printf "Got data on serial port, but does not fit tag format. Ignoring.\n";
      } else {
	  # Extract tag id from framing
	  $tag_id = substr($tag_id, 1, 12);
	  # FIXME: Dump for debug
	  #hexdump($tag_id);
	  # Look up NFC tag ID in database
	  # Note: Only opening database long enough to look this up. That way, we don't have to
	  #  manage a multi-access database right now. Could be solved with DB v2-v6 or mysql ..later
	  if (!defined(tie %auth_tags, "DB_File", $db_fn, O_RDONLY | O_EXCL, 0600, $DB_HASH)) {
	      printf "Cannot open database: $!\n";
	  } else {
	      if ($auth_tags{$tag_id}) {
		  printf "Tag $tag_id is authorized\n";
		  # If matched, close relay and light green LED
		  system("echo 1 > $sys_class_gpio$on_relay_gpio/value");
		  system("echo 1 > $sys_class_gpio$red_led_gpio/value");
		  system("echo 0 > $sys_class_gpio$grn_led_gpio/value");
		  sleep($relay_on_time);
	      } else {
		  printf "Tag $tag_id is NOT authorized\n";
	      }
	      untie %auth_tags;
	  }
	  # Release relay and light red RED
	  system("echo 0 > $sys_class_gpio$on_relay_gpio/value");
	  system("echo 0 > $sys_class_gpio$red_led_gpio/value");
	  system("echo 1 > $sys_class_gpio$grn_led_gpio/value");
      }
  }
}