s08 - 61 - iahmctrl - Description

advertisement
Internet Accessible Home Control
Design Report
By
Saluki Engineering Company Team 61 - IAHMCTRL
Brandon Dwiel, Project Manager
Sammi Karei
Anthony Kulis
Brandon McCormack
Richard Reed
Table of Contents
1
Transmittal Letter(BD) ...........................................................................................................4
2
Executive Summary(BD) .......................................................................................................6
3
Project Description(SK) .........................................................................................................8
4
Subsystems ...........................................................................................................................11
4.1 Peripheral Hardware(BM) ....................................................................................................11
4.1.1
Overview..........................................................................................................................11
4.1.2
Single Board Computer ...................................................................................................12
4.1.3
Sensors and Sensor Housing ............................................................................................12
4.1.4
Keypad .............................................................................................................................13
4.1.5
Communication with Other Subsystems .........................................................................13
4.1.6
Fault Analysis ..................................................................................................................13
4.2 Low-Level Software(BD) .....................................................................................................15
4.2.1
Overview..........................................................................................................................16
4.2.2
Communication with Other Subsystems .........................................................................18
4.2.3
Recommendations............................................................................................................19
4.3 High-Level Software(AK) ....................................................................................................20
4.3.1
Module Control ................................................................................................................20
4.3.2
Reboot ..............................................................................................................................21
4.3.3
Text Messaging ................................................................................................................21
4.3.4
Audible ............................................................................................................................21
4.3.5
Control Expandability......................................................................................................21
4.3.6
User Administration.........................................................................................................22
4.3.7
Configuration ...................................................................................................................22
4.3.8
Security ............................................................................................................................22
4.3.9
Remote Administration and User Authentication ............................................................23
4.3.10 Fault Analysis ..................................................................................................................23
4.3.10.1 Overloading......................................................................................................................23
4.3.10.2 Boot Time Scripts ............................................................................................................24
4.3.11 Suggestions ......................................................................................................................24
4.3.11.1 SIS Argument Expansion .................................................................................................24
4.3.11.2 Customized Debian Distribution......................................................................................25
4.4 Processing Core Subsystem(RR) ..........................................................................................25
4.4.1
Overview..........................................................................................................................25
4.4.2
System Implementation ...................................................................................................26
4.4.3
Subsystem Modules .........................................................................................................26
4.4.4
State Manager ..................................................................................................................27
4.4.5
Rule Manager...................................................................................................................28
4.4.6
Socket Manager ...............................................................................................................28
4.4.7
Parsers ..............................................................................................................................28
4.4.8
Program............................................................................................................................29
4.4.9
Communication with Other Subsystems .........................................................................30
2
4.4.10 Installation Process ..........................................................................................................31
4.4.10.1 System Requirements.......................................................................................................31
4.4.10.2 Installer ............................................................................................................................31
4.4.11 Fault Analysis ..................................................................................................................31
4.4.11.1 Malformed Data ...............................................................................................................31
4.4.11.2 Malicious Data .................................................................................................................32
4.4.11.3 Physical Faults .................................................................................................................33
4.5 Graphical User Interface(SK) ...............................................................................................34
4.5.1
Overview..........................................................................................................................34
4.5.2
Further Investigations ......................................................................................................36
5
Further Design Work(RR) ....................................................................................................38
6
Implementation Schedule(AK) .............................................................................................39
Appendix ........................................................................................................................................40
A Options Considered(Team 61)..........................................................................................40
B Cost to Construct(BM)......................................................................................................40
C References(BM) ................................................................................................................42
C.1 Vendors ..........................................................................................................................42
D Source Code ......................................................................................................................43
D.1 Low Level Module Source(BD) ....................................................................................43
D.1.1 Main Program .............................................................................................................43
D.1.2 Port Libraries ..............................................................................................................46
D.1.3 Keypad Functions .......................................................................................................52
D.1.4 Packet Functions .........................................................................................................62
D.2 High Level Module Source(AK) ...................................................................................65
D.2.1 System Information Server .........................................................................................65
D.2.2 Scripts .........................................................................................................................68
D.2.2.1 Audible ....................................................................................................................68
D.2.2.2 Text Message ...........................................................................................................68
D.2.2.3 SIS Boot-up Script ...................................................................................................68
D.3 Processing Core Source(RR) .........................................................................................69
D.3.1 Parser ..........................................................................................................................69
D.3.2 Socket Manager ..........................................................................................................73
D.3.3 State Manager .............................................................................................................75
D.4 User Interface Source(SK).............................................................................................76
D.4.1 Example Configuration File .......................................................................................76
D.4.2 Result of Parsing Configuration .................................................................................77
D.5 Simulation Software(AK) ..............................................................................................77
D.5.1 SIS Client ....................................................................................................................77
D.5.2 Processing Core ..........................................................................................................78
3
1. Transmittal Letter
November 19, 2008
Saluki Engineering Company
Southern Illinois University Carbondale
College of Engineering - Mailcode 6603
Carbondale, IL 62901-6604
Brandon.Dwiel@gmail.com
Dr. F. Harackiewicz
Department of Electrical and Computer Engineering
Southern Illinois University Carbondale
Carbondale, IL 62901-6603
(618) 453 – 7031
Dear Dr. F. Harackiewicz,
On April 16, 2008 we received your acceptance of our proposal for the design of an
Internet Accessible Home Control (IAHC) system. We would like to submit to you
our design report along with our dearest gratitude.
The IAHC system offered university students the opportunity to extend classroom
teachings of computer and electrical engineering into a design. The product
includes subsystems that exercise teachings from programming, hardware
architecture, circuit analysis, and testing and debugging.
The subsystems of the design are responsible for particular tasks, and must
communicate effectively with the other subsystems as well. The design involves
complex algorithms that must be created by the students and then tested
4
thoroughly.
We would again like to thank you for this opportunity to design this product. We
are looking forward to working with your organization.
Sincerely,
Brandon Dwiel
Project Manager, Internet Accessible Home Control
Saluki Engineering Company
5
2. Executive Summary
As computers become cheaper and more plentiful, the desire to integrate these
devices into our daily lives increases. Computers are already integrated into many
aspects of lives from automobiles, cellular phones, and also as tools to aid us at
work. The implementation of the Internet Accessible Home Control (IAHC) is an
important step to towards a completely computerized home.
Most homes today were built before the digital revolution. It is paramount that
any automation of an existing home consists of a computerized system that is easily
retrofitted and simple for the homeowner to operate. It is the modularization and
distributed algorithms of our hardware that allows for utilization of the homeowner's
personal computer, which makes our design superior for retrofitting into existing
homes.
The following design report is for the Internet Accessible Home Control system.
This report contains all of the information needed to understand how this system
operates. There are detailed sections on each of the five subsystems that report
exact specifications, include engineering drawing, and contain the source code for
that subsystem. These sections highlight the learning experience and
improvements suggested for each subsystem as well as how each subsystem
contributes to the entire system. The Peripheral Hardware subsystem explains the
hardware that is interfaced with the module. This section specifies what the
operating conditions are and how to properly set up this hardware. The subsystem
titled Low-Level Software contains everything needed to link the peripheral
hardware to the rest of the system. This section contains mostly source code and
does not require any configuration. The High-Level Software subsystem
applications are installed on the module. This software is responsible for the twoway communication channel from the module to the home computer. The
Processing Core subsystem is the brains of the system and resides on the home
computer. The Processing Core is accountable for processing the data it receives
from the system and enacting on decisions based on this data. The Graphical User
6
Interface (GUI) section is in charge of providing the interface to the user. All user
configurations are done through this subsystem.
In addition, there are sections pertaining to the system as a whole. One section
analyzes the cost of designing this system as well as one for production. Team 61’s
recommendations for implementation and conclusion end this report. The
estimated cost for producing this system is $250.22.
7
3. Project Description
Figure 1 - System Block Diagram
This project called for the development of a next-generation control and monitoring
system. The Internet Accessible Home Control (IAHC) system shall provide a
complete solution that allows users to access the home control system from the
Internet. The system implores a protocol that allows for simple expandability. The
project currently consists of one module to provide peripheral data for the system to
process.
8
There are several associated subsystems, each working together to provide
functionality to the complete system.
The first subsystem is the Peripheral Hardware subsystem. A fan-style array of
sensors is used to gather data from external surroundings. The subsystem also
includes a keypad to access configuration rule assignments. This subsystem is
also responsible for any additional external devices.
Once the data has been collected and converted, the module processes the data
into a usable format for Ethernet transmission. This processing, including all
architecture programming, introduces the Low-Level Software subsystem.
To take advantage of code portability, a Linux based operating system is
deployed on the module. The code designed by the High-Level Software
subsystem is a liaison for the homeowner's personal computer and the microcontrollers functions.
To communicate with the homeowner's personal computer, standard Internet
programming techniques are used. This is normally referred to as client-server
application programming. Installed into the homeowner's personal computer is an
application that not only manages the module’s distributed computing, but also
supplies the end user's Graphical User Interface with relevant information. This
code is complex and requires a separate subsystem, hereafter called the
Processing Core Subsystem.
Finally, to allow a customizable system, an intuitive Graphical User Interface
must be present. The Graphical User Interaction Subsystem will develop this User
Interface. The User Interface allows the end user to set rules, read system
information, and manage the network of micro-controller modules throughout the
home. To simplify the system described for the homeowner, all subsystems need
minimal user interaction to function. Once the user interface is shut down, the
system should still run. This is another reason why the Processing Core Subsystem
is separate from the User Interface Subsystem. To further simplify end user
interactions, each micro-controller module is able to function without the advantages
of a distributed system and the processing core application, if the situation requires,
9
such as a PC shutdown or network failure, yet is able to link back up to the
processing core when available without the need of end user interaction.
10
4. Subsystems
4.1 Peripheral Hardware
Figure 2–Peripheral Hardware Subsystem Block Diagram
4.1.1 Overview
The Peripheral Hardware subsystem is composed of all essential hardware
components for the project. These include the Single Board Computer (SBC),
sensors and sensor housing, and keypad. To monitor movement in a room, an
array of infrared radiation (IR) sensors is mounted so that it envelops the entire
room horizontally. Three digital IR sensors are mounted to create five detection
zones. With an empty room, all three sensors are idle and a digital “0” signal is sent
to the SBC. When a sensor detects motion in the room, a digital “1” signal is sent to
the SBC. According to which sensors are active, the Processing Core can
determine in which zone there is movement and execute different control actions.
11
4.1.2 Single Board Computer
This IAHC product is designed to be installed inside a home and provide maximum
performance without requiring any extra setup or devices, all while taking up a small
amount of space. Because space is a concern, we have tried to keep the number
of hardware components to a minimum. The Technologic System TS-7300 is a fully
functional, self-contained Linux-based SBC with a small footprint. The low-wattage
200MHz ARM9 microprocessor saves space because it requires no fan or heatsink.
The SBC has two SD card flash sockets, from which it runs a Debian distribution.
For digital signal processing, there are 55 general purpose I/O (GPIO) pins
available. Also, an onboard 10/100 Ethernet port provides network access. Using
this SBC offers full capabilities of a basic PC in a small and affordable package.
4.1.3 Sensors and Sensor Housing
The sensors act as the eyes for the system within any given room. They must be
able to track occupants with as much accuracy as possible while still remaining
affordable. IR sensors provide a cost effective way to track motion in a room. This
project uses an arrangement of three AMN11112 motion sensors to map out five
zones of detection. Software on the SBC polls the sensors, and each sensor
transmits either an idle signal (“0”) or an active (“1”) detecting signal.
The AMN11112 sensor has a 100-degree horizontal detection range that
provides great coverage for monitor human movement. It has a detection distance
of up to 16.4 feet that is ideal for use in most homes, and can be used in
conjunction with additional modules if used in larger homes or businesses.
The three sensors are mounted inside a machined plastic housing. This prevents
damage to the sensors and provides stability, allowing for accurate and precise
readings.
12
4.1.4 Keypad
A16-button digital keypad is connected to the SBC to the GPIO ports. The keypad
serves as another user interface to the module. While the Graphical User Interface
acts as an interface to the module for only the owners of the house, everyone that
enters the house uses the keypad. It is mounted at the entrance of the house,
where a valid code is entered before entry.
4.1.5 Communication with Other Subsystems
The Peripheral Hardware subsystem communicates solely with the Low-Level
Software subsystem.
Eleven of the GPIO pins on the SBC are used for communication between the
subsystems. Each of the sensors gets a GPIO pin. Also, the 16-button keypad has
8 I/O pins and each pin is connected to a GPIO pin on the SBC.
4.1.6 Fault Analysis
The AMN11112 motion sensors used in this project may not work properly in cases
when a heat source other than a human being is detected. Currently, this project
does not account for the presence of animals in the monitored household. The
AMN11112 motion sensors used in the project were selected for the horizontal
coverage area they provided at a relatively inexpensive price. These sensors also
have an 82-degree vertical coverage that was not needed as the scope of this
project was aimed at detecting movement of humans. After mounting the sensors
at a height where all horizontal human movement will be detected (about 3.5 feet),
movement below (and above) will still be detected.
Direct exposure to sunlight or high-intensity lights may cause the sensor to return
faulty readings. Sudden changes in temperature in the detection range, such as
those due to air conditioning and heating unit, may also have an adverse affect on
the sensor detection.
13
Further development of the subsystem should overcome these concerns by
choosing an appropriate sensor, as there is a plethora of sensing devices available
on the market.
14
4.2 Low-Level Software
Figure 3 - Low-Level Flow Chart
15
4.2.1 Overview
The Low-Level Software subsystem is designed around the tasks of reading data
from the peripheral hardware, formatting the collected data in a defined manner for
the Processing Core, establishing a connection through TCP with the Processing
Core, and transmitting the formatted data in an efficient behavior. This subsystem
is comprised mostly of software and also includes some small circuitry. The
Processing Core and Peripheral Hardware subsystems are the subsystems that the
Low-Level Software subsystem interacts with.
The Low-Level Software subsystem is required to provide the functionality to the
sensors and the keypad, by making them available to the rest of the system. The
prototype module is delivered with pull-up resistors connected to the GPIO lines that
pull-up the voltage to 3.3 V when no other device is driving the line. Since the
sensors behave like an open circuit when not detecting, some circuitry needs to be
added to pull the GPIO lines to logic 0 when there is no movement detected. Figure
4 contains the schematic for this circuitry. A single keypad is also included for the
purpose communicating to the Processing Core who is entering the house. This
subsystem contains the required code to read the data from the sensors and from
the keypad.
The next task of the Low-Level subsystem is to format the data from the
peripheral hardware. This is done by having a predefined protocol that enables the
Processing Core to distinguish between both the peripheral hardware device the
data is associated with and module is sending the data. This allows the module to
send any data from the peripheral hardware using the same API and port. This also
enables the Processing Core to accept data formatted to this same protocol from
other modules, should there be additional modules installed, and be able to
distinguish which module the data originated from. The protocol calls for the packet
to be formatted with the 12-byte unique MAC address of the module first, followed
by one byte to denote which function of the module the data is being associated
with and ending with a 4-byte field for the actual data.
16
The last task is to establish a connection with the Processing Core. Once a
connection has been established, the module begins sending the formatted packet
through TCP to the Processing Core whenever there is new data to send. This is a
very important step for the system to function properly since without this connection,
the Processing Core will have no data to base any decisions off of. Because of the
importance of this step, the Low-Level Software subsystem will immediately send
out a text message to whomever’s cell-phone number is entered to a configuration
file if the connection between the module and the Processing Core is severed. In
the event that there is no connection with the Processing Core, the Low-Level
Software subsystem writes to a log file instead of to the socket. The Processing
Core can later retrieve the log file.
17
Figure 4 - Sensor Schematic
4.2.2 Communication with Other Subsystems
The Low-Level Software subsystem primarily communicates with two other
subsystems, the Processing Core and the Peripheral Hardware subsystems.
18
The only means of communication with the Processing Core is through a TCP
connection. The application protocol is one-way and carries data from the LowLevel Software subsystem to the Processing Core whenever there is new data to be
sent.
The Low-Level Software subsystem interacts with the Peripheral Hardware
subsystem through the GPIO pins on the module. All of the 11 GPIO pins are
configured as inputs though the software. One GPIO pin is dedicated to each of the
three sensors. The other eight GPIO pins are reserved for the keypad.
4.2.3 Recommendations
The first recommendation for this subsystem is to package this module with a
different SBC. The best way to reduce costs is to incorporate a SBC that fits the
minimum requirements. The onboard FPGA is not utilized because we eliminated
the need for more GPIO pins by using digital sensors. There is also no need for the
production board to contain UART connections. The UART connections were
important for developing software for the module but serve no purpose once the
software has been fully tested. The major requirement that the production board
needs is to have a microcontroller that can run at least a slimmed down version of
Linux since the software is written in C and Python.
19
4.3 High-Level Software
Figure 5 - System Information Server Flowchart
The main application of the High-Level Software Subsystem is the System
Information Server (SIS). The SIS is a white-listed server for the end-user providing
basic administration tasks. The name, “white-listed server” means that it only
accepts a minimal set of commands and only acts as an interface to call binaries
and scripts with a limited set of arguments. This application is strictly a network
interface and all information gathering and control capabilities are separate
applications within the operating system of the module.
4.3.1 Module Control
The module control applications are separate applications outside the domain of the
SIS. The SIS control calls are rebooting, text messaging, and an audible alert.
20
4.3.2 Reboot
The reboot command is an operating system call done by the SIS in the same way
that an administrator would do by using telnet or SSH. Having a reboot option for
control can cause disruption by an outside-attacker; however, the only disturbance
would be loss of functionality until the system is restarted.
4.3.3 Text Messaging
Text messages are created using a mail client called Mutt. Mutt utilizes a SMTP
Relay called msmtp. Once msmtp is configured to use an existing email account,
mutt generates an email that is sent to an email address of a cellular phone, eg
6181234567@vtext.com, and the cellular provider translates the email into SMS
format. The SIS is restricted from spamming abuse because it is limited to calling a
script within the Debian distribution that generates the email. It is possible for an
attacker to generate what is called a “text-bomb” by repeatedly sending text
message requests to the module.
4.3.4 Audible
The audible control feature of the SIS demonstrates the expandability of the
module. As designed, the SIS calls a distinct separate application that emits an
audible alert. Currently for the prototype, the SIS calls a script that generates a
verbose message to a log file.
Replacing this script with a binary that activates an
audio add-on to the module finalizes the extra feature, but is beyond the scope of
this project.
4.3.5 Control Expandability
The SIS is designed in such a way that a few lines of code can expand the
functionality of the SIS. Since the SIS is a white-listed server, module expansion
21
comes from creating a binary application that is called by the SIS. Not only does
this provide an opportunity for decentralized security, the production life cycle of the
source code is easily manageable. A standardized directory has been created to
contain custom control applications.
4.3.6 User Administration
A typical end-user should not have a need to handle major administrative tasks, yet
adding basic features, such as retrieving the logs, IP addresses, and host name will
be useful tools if the situation of debugging is ever needed. Unlike the control calls,
system information calls on the SIS use both system binaries and Python built-in
function calls. Since user administration calls provide no degree of control, security
concerns are limited to reconnaissance attacks. Limiting this type of attack is
discussed within the security concerns subsection.
4.3.7 Configuration
Currently, the SIS is not written to utilize the /etc/hosts.allow and /etc/hosts.deny
files, but the final release will take advantage of this security feature. To deploy this
feature, an administrator needs to configure these files to restrict access to the
module only from the PC running the Graphical User Interface and the PC
containing the Processing Core subsystem.
The module also requires a static IP address and pointed to the DNS server of
the network.
4.3.8 Security
Network security is propagated by the /etc/hosts.allow and the /etc/hosts.deny files.
An openSSL encryption algorithm should also be developed, but this has an impact
on performance.
22
The white-listed serving technique severely limits what messages can be sent to
the module.
The SIS is written in Python, which is a secure scripting language. All
applications that are called by the SIS should be written in safe languages such as
Python, ADA, or Java. Any Debian distribution binaries should be updated and
patched regularly for security updates.
4.3.9 Remote Administration and User Authentication
Debian, by default, creates a root account. This account is disabled and the
installer needs to create an administrative account with a secure user name and
password. This is because the preferred Linux remote administration tool is the
openSSH server. The module comes installed with openSSH on port 222 and root
logins are disabled. SSH also uses the /etc/hosts.allow and /etc/hosts.deny files, so
they need to be configured to the LAN and restrict access only to the PC where
remote administration will be initiated.
4.3.10 Fault Analysis
There are two currently unresolved issues with the High-Level Software Subsystem.
The first is the performance issues. Although the load on the system from the
current design is not large, the potential to overload the system exists. The second
issue occurs at boot time with the SIS failing to execute certain control calls.
4.3.10.1 Overloading
The Debian distribution installed on the module is excessively large for what is
needed. We chose to use the distribution provided by the SBC manufacturer due to
time constraints. This operating system runs at 200MB and can be reduced to
around 25MB if built from scratch. If the functionality is kept to a minimum, as in our
prototype, there will never be an issue, but adding complex encryption algorithms or
23
being deployed into a busy external environment will bring unwanted data
overloading.
4.3.10.2 Boot Time Scripts
The SIS is automatically initialized upon boot up, but when done so, two control
calls, text messaging and audible, fail to work. The SIS returns a successful call, so
the issue is not with the SIS. When started manually, these control calls work
without issue. The boot script simply starts the SIS as a user would manually. The
SIS is set user ID (SUID) to root for both manual and automatic startup. Having a
runtime issue like this indicates a permissions issue at boot up. Several Linux
Administrators that we have spoken with all agree it is a Debian build issue that the
SBC manufacturer did not document or did not realize happened.
The module's Debian operating system is built for 3 second boot times, and we
believe to accommodate this, some fundamental steps were overlooked. Solving
this issue most likely comes from building a light weight Debian distribution that is
customized for our application.
4.3.11 Suggestions
There are two main suggestions we have for the High-Level Software Subsystem
that a full production should address.

SIS Argument Expansion

Customized Debian Distribution for the Module
4.3.11.1 SIS Argument Expansion
Currently the SIS takes no start up arguments. The SIS in future development
should contain a variety of encryption algorithms so they can be suited to network
conditions and microcontroller capabilities. Also, control calls and system
24
information calls are to be manually turned on or off, further restricting functionality
for security purposes. If deployed within an enterprise environment where a
qualified Linux Administrator is employed, having basic system information calls are
not needed because all administration can be done via SSH, where in a home or
small office environment, having basic administration calls available to the
Graphical User Interface is useful. These system information calls are similar to the
ones deployed with NetBios on Microsoft Windows systems, and have similar
security issues, but the SIS information calls can be turned off or on as needed by
the overall system.
4.3.11.2 Customized Debian Distribution
The Debian Distribution currently installed runs at about 200MB. Reducing the size
of this operating system is a must. Performance issues can arise and having a
lightweight operating system on a powerful microcontroller can resolve any of these
issues.
4.4 Processing Core Subsystem
4.4.1 Overview
One of the key components of this system is the ability for users to easily set
custom rules that the system follows. Once these rules are set, the modules must
be monitored in order to see when the rules are violated.
The processing core is responsible for receiving data from the modules and
carrying out the rules specified by the user. It runs as a daemon on a normal PC
connected to the user's network. Once data from the module is received, the rule
bank is checked to see if the new data matches any of these rules, and if a violation
occurs, a control message is sent to the module.
25
4.4.2 System Implementation
The processing core is written in Erlang, which was chosen because of the
language's strong support for concurrency, reliability, and pattern matching.
The program has been divided up into four distinct Erlang modules, plus an
additional module to control the application startup sequence. Each of these
modules will spawn Erlang processes, which communicate to one another through
Erlang's built in message passing system. The source code for this subsystem may
be viewed in appendix D.3.
4.4.3 Subsystem Modules
Erlang consists of five distinct modules, which are listed in table 1 along with their
publicly exported functions. In addition to their core responsibilities, each module
also includes functions to easily make calls to the main module process via Erlang's
message passing system.
26
Table 1- Processing Core Modules
Name
State Manager
Role
Store and provide state
data
Public Functions









Rule Manager
Store and evaluate rule
configuration




Socket Manager
Handle socket
Connections



Parsers
Program
Parse configuration files
Initialize system
setPermission
currentPositions
currentPositions
adduser
leaveUser
addKeycode
revokeKeycode
start
stop
start
stop
evaluate
set
start
stop
getAddress

parseConfig
parseLine

start

4.4.4 State Manager
The State Manager is responsible for maintaining the current state of the system.
This primarily consists of the current status of the sensors, as well as the
authentication level of the house.
As seen in table 1, Erlang's built-in process dictionary is used to store the active
keycodes, current system logins, and sensor state. From there, the state manager
calculates the highest active authentication level. Other modules can then access
this data by using the public methods of the state manager modules.
In addition, the state manager is responsible for alerting the Rule Manager
whenever the state has changed and the rule-set must be re-evaluated.
27
4.4.5 Rule Manager
The rule manager is responsible for storing and testing the rules that the system
follows. Rule-sets are presented to the rule manager as a list of Erlang tuples,
where each tuple consists of a list of conditions and a list of actions.
A condition has one of two forms.

Form one consists of a list where the first member is a predicate (AND, OR,
NOT), and all remaining members are also well formed conditions

Form two consists of a simple condition, which consists of a list where the first
element is the condition type and all other elements are the condition arguments.
Appendix D.3 shows how a sample configuration file and the resulting rule structure.
Upon receiving an updated rule-set, the rules are evaluated to see if any match
the current state. This is done by recursively scanning the rule tree and calling the
state manager to evaluate all simple rules. The results of these tests are then
combined with the predicates in order to test the condition as a whole. If a condition
does evaluate to true, all actions specified in the rule are executed.
4.4.6 Socket Manager
This is the module that is responsible for maintaining communication with other
subsystems through standard TCP sockets. The main process of this module waits
for incoming TCP connections, and then spawns a new process that will take in
data over the connection and pass it to the state manager.
4.4.7 Parsers
When the processing core gets the rules, they are stored in a text file that must be
parsed into a data structure that can be used by other parts of the program. The
parser breaks the file down line by line, then recursively scans them to build a
nested list of rules. Once it has built this list, they are passed to the rule manager.
28
4.4.8 Program
This module is run at the program start. It initializes all of the other modules, and
provides the system with the initial state to use.Process Model.
Each module executes in an Erlang process. These processes communicate via
standard Erlang message passing. All processes remain sleeping in a loop, waiting
to act on messages sent to them. A diagram of these processes and the messages
they pass can be seen in 6.
Figure 6 - Processing Core Process Model
29
4.4.9 Communication with Other Subsystems
By itself, the Processing Core will loop indefinitely without doing anything. In order
to serve a useful purpose, it must communicate with the user interface and the
module software.
The processing core and modules communicate through a standard TCP socket.
These messages all take the form of a 17-byte payload. Table #XX shows a listing
of the fields in the payload ordered by byte. Payloads can be a number of different
types according to their function. At the moment, only three are defined, and are
listed in table 3.
Table 2 – Byte Fields In Data Packets
Bytes
Field
0-11
Mac address of sender
12
Type of packet
13-16
Data
Table 3 - Packet Type Identifiers
Type Identifier
Type
1
Sensor Data
2
Keypad entry
3
User Interface Update
There are two avenues through which the user interface can pass data to the
Processing Core. The primary method is via files. The configuration file is used by
the user interface to pass the current rule configuration to the processing core. The
format of this configuration file, as well as an example is provided in appendix D.3.
In addition, the processing core keeps a log file of all events that occur. Each
event in this file is placed on a separate line in the following format:
date:time<tab>event type<tab>event specific data
To alert the processing core to updates in the configuration file, the user interface
30
also sends messages in the standard payload format outlines above. When the
program receives such an alert, it parses the log file and updates the system ruleset.
4.4.10 Installation Process
4.4.10.1 System Requirements

Personal computer

GNU/Linux distribution with Erlang interpreter installed
4.4.10.2 Installer
The installer will copy processing core software onto the user's PC, along with the
user interface. Since the processing core depends on the Erlang interpreter, it must
also be installed on the system.
Due to time constraints, the Processing Core is currently only supported on Linux
systems. However, Erlang and its standard library are all cross platform, so it is
likely that the program could be easily expanded to Microsoft Windows systems to
ensure compatibility for a greater number of users.
4.4.11Fault Analysis
4.4.11.1 Malformed Data
There is currently very little validation of the configuration file. A bad configuration
file would result in an incomplete and incorrect rule-set. This will result in incorrect
evaluation of the rules that the user has specified. Depending on the error, this
might result in actions never being taken, or taken at inappropriate times.
The processing core does not currently provide any way for modules to undo or
31
“take back” data that is sent. In other words, if the module sends erroneous data
and discovers this after the data has been sent, there is no way to revoke the
previous transmission. In practice, this should be a minor matter. The greatest
problem form this would be incorrect logins with invalid key codes. However, codes
are created with the lowest privilege level by default. Therefore, it is unlikely that
there will be an erroneous escalation to a higher privilege level.
4.4.11.2 Malicious Data
A malicious attacker spoofing a module could easily cause problems for the system.
For example, a malicious user on the system could provide incorrect key code or
sensor packets that would then be accepted by the processing core. Currently, the
processing core does nothing to prevent such attacks.
However, there are a number of steps that can be taken at the network
architecture level to combat this. The most basic would be to place a firewall
preventing outside traffic on the ports used by the system. This will prevent most
remote attacks without impeding in any other way on network functionality.
This is insufficient, however, to prevent attacks from a system that has gained
access to the local area network. On a wired network, such as the one used by the
final prototype, this is not a great threat, in order to gain access to the network, a
malicious user would already require some way to bypass the home security and
gain physical access to the network. A wireless network, however, would be more
vulnerable since the system could be attacked without physical access. This can be
mitigated through strong network level encryption, but can't be fully prevented.
One method of lessening these attacks would be to use encryption to sign all
packets sent by modules. This would allow the processing core to verify the identity
of the sender. However, this system would still be vulnerable to man in the middle
attacks where the malicious system could pretend to be a valid system and present
the module with its own set of keys.
32
4.4.11.3 Physical Faults
As in any computer system, this would be vulnerable to physical or electrical failures
to the computer it is running on. For the sake of widespread adoption, we chose to
design the software to be run on an end user's own personal computer. While this
has many benefits, it does take away control over the reliability of the system it is
running on.
The most common failure in this regard is likely to be power. Few home
computer systems have interruptible power supplies designed to last longer than it
takes to complete a system shut down. During this time, any data from modules
that are still operational is not acted upon or stored.
The processing core cannot do anything to prevent these failures, but it does
have some features which allow it to gracefully recover from them. The most
important of these is that all configuration data is placed and held on the hard drive.
In the event of a power outage or any other down time, the rules will be restored
when the program loads again. Since the default option for the installer is to load
the program on boot, the processing core will be restored almost as soon as power
is regained.
Longer term plans to boost reliability would include an option for a lightweight
server which could act as a dedicated processing core that could be used either as
a backup or as the main program. Such a unit could have built in battery backup
capabilities to allow it to continue usage during outages or computer downtime. We
consider this unit to be beyond the time constraints imposed by the class since it
would require building a custom operating system distribution and changes to
communication protocols to provide more flexibility for secondary processing core
programs.
33
4.5 Graphical User Interface
Figure 7 - Sitemap
4.5.1 Overview
This subsystem is the meeting point between the users and the project as a whole.
With a graphical user interface, users will be able to monitor and control the whole
system via a web based application. Using the interface, users can monitor
connected modules, view and edit rules and even visualize the system data. This
web based application then connects to a SQL database that holds the data
collected from all the modules. The interface will format and present the data to the
users. The users will also be able to choose from a list of rules and level of
clearance as to how much movement they are allowed before the alarm
notifications are activated. This subsystem will be able to communicate with the
server software subsystem and the high level module software subsystem. They will
interact with a command list defined by each of us who are in charge of the different
subsystems.
34
First, the website will be able to get a configuration file from the server with the
list of instructions and it will check to see if there have been any changes to the file.
If not, it will provide a list of instructions that will give the user different amount of
security clearance depending on their intentions, the amount of movement that they
need in the specified house. This information is sent from the server software
subsystem. The website will also be able to tell the user who is allowed in the
house. It will do this by being able to take peoples information when they are on the
site. It will store this on the server and the user can view to see who is able to
access their residence on any particular day. The server will also keep a history of
who was in the house for future reference in case the security is breeched. If no
security was breeched, the user can delete the information as they please.
Another useful feature that will be incorporated into the User Interface is the
ability to generate key codes. This feature is useful as the users need different and
unique key codes in order to be able to gain access to the residence. The key
codes generated are unique and are not able to be duplicated. Once a key code is
generated, it is stored in the server and passed on to the system maintaining the
residence so that the person trying to access the residence has access to it. The
algorithm to generate these key codes will be such that no one key code can be
administered and then duplicated. This will eliminate the chance of two people
having the same key code and therefore having the same security allowance in the
residence. Each person trying to enter the residence should have a unique and
secret key code.
The first page that the user will see once they get on the site is the index.php file.
This is a simple webpage that contains four different links that the user can click on.
The first link that they can click on is the configuration file. This is a file that is
written in PHP language and this link will take them to another page that will have a
set of communication rules with the processing core. On this page, the user can set
the different rules and configurations for the person that they are trying to get them
to access the residence. The second link in the index.php page is the
addrecords.php link. This link will allow the user to enter in details the person that
they are trying to give access to the residence. The page will take information from
35
the person’s name, address, telephone and other details and these will be sent to
the server to be stored. This will come in handy so that we can know who is
accessing the residence and not just giving anyone a key code.
The next link of the index.php page is the viewrecords.php link. This will allow the
user to view all the records of who was given access to the residence as well as the
timestamp. It also gives the key codes that have already been used and what time
they were used. Another link that will be available on the index.php page is the
keycode.php link. This link will be used to generate a unique key code for the users
who want to gain access to the residence. It will have an algorithm that makes sure
that all key codes generated are unique and not duplicated over a period of time.
The last link available is the about.html link. This will be a basic summary of who
we are including contact numbers for any problems encountered with the software.
It will also give a basic description of how to use the site and troubleshooting.
With the Graphical User Interface being the meeting point with the users, there
are different measures that we have taken in order to ensure that there is enough
security. For instance, once a key code is generated, it cannot be used twice. If the
user enters their key code and makes a mistake, they cannot gain access to the
residence unless they contact the owner and get another key code. The algorithm of
the key code generator is such that it cannot generate the same key code within a
specific duration. Also, once a key code has been entered and verified by the
server, it cannot be entered again at another time unless this has been verified by
the owner of the residence. This is to ensure that once someone has a key code,
they cannot come back later and gain access to the residence. All the new key
codes and the used key codes are stored on the server, and thus there cannot be a
security breech using the key codes.
4.5.2 Further Investigations
Upon further tests and debugging of the Graphical User Interface subsystem, there
were some problems with the connection with the SQL database. This created a
problem as the Graphical User Interface is not able to connect with the Server
36
Subsystem via the database. Further tests and analysis are being done so that we
can make sure that it is able to communicate with the server so that the other
functions are able to work in the subsystem and the project as a whole. Once
connected, all the functions of the Graphical User Interface will be able to work with
the rest of the subsystems.
37
5. Further Design Work
This project is envisioned as a mass market consumer product. In this project, we
constructed a prototype to demonstrate and test the system, but there are still a
number of steps required to transition from our prototype to a marketable system.
To move from prototype to product, further design work is required in three main
areas: testing, security, and scalability.
In any system that a user must interact, it is essential to receive feedback from a
consumer focus group. In many areas such as use cases, software usability, and
feature priorities, user testing can provide the essential data that makes the
difference between success and failure. In transitioning from a prototype to a
product, we would have to budget time to run user tests.
While our team has tried to keep security concerns in mind throughout, time
constraints have forced us to look towards functionality over security. In order to
make this into a usable product, additional time would have to be spent identifying
and correcting security flaws in the product. A number of these issues have been
identified and mentioned in the individual subsections, and all must be addressed in
a final product.
There are also a number of scalability concerns with our current design. The
system has been designed with multiple modules in mind, but due to budget
constraints we have only been able to test with a single module. A production
system would potentially have many modules connected, so there must be further
testing in order to ensure that it can handle the loads that it would be under in actual
long term conditions.
38
6. Implementation Schedule
Table 4 - Potential Implementation Schedule
Week 0
Week 3
Week 4
Week 7
Week 9
Week 11
Week 15
Week 17
Week 24
Design work in preparation for testing
Begin First round user testing
Finalize launch capabilities
Finalize System Components
Additional user testing (including feedback on housing
design)
Internal load testing
Final round of user tests
Finalize hardware design for manufacturing
Finalize Software
39
Appendix
A. Options Considered
The options considered primarily resided within the Peripheral Hardware
subsystem. The different options considered included alternative sensors such as
heat, radon and RFID sensors.
Another consideration pertains to the layout of the sensors.
Two options were presented: grid-style and array-style.
Regarding the microcontroller processor, an ARM and a PIC were our options.
Each subsystem presented options. These included:

Hardware options:
o Sensors

Radon, Temperature, Infra-red, RFID
o Layout

Grid vs. Array
o Microcontroller Processor

PIC vs. ARM
o Development board


Technologic Systems TS-7300 vs. iPac 9302
Software
o Languages

Python, Perl, Java, C, Bash, Erlang, Bash, PHP, HTML
B. Cost to Construct
As shown in table 4 the cost to construct the prototype is $419.70. The bulk of the
40
prototype cost is the TS-7300 single board computer at $219 and companion
development kit, which includes a 512 MB SD card, USB-SD adapter, regulated DC
power supply, and connection cables,. Also included in the cost are three infrared
sensors at $25.39 each and a keypad at $16.07. Prototype housing for the sensors
was made from PVC parts costing a total of $8.46.
Table 5 - Cost to Construct, Prototype
Item
Single Board Computer [1]
Development Kit [2]
IR Sensors [3]
Keypad [4]
PVC Housing [5]
PVC Cover [5]
PVC Bushing [5]
PVC Cement [5]
Vendor
Technologic
Systems
Technologic
Systems
Newark.com
Newark.com
Menards
Menards
Menards
Menards
Cost per Unit
Quantity
$219.00
1
$100.00
$25.39
$16.07
$3.99
$1.99
$0.99
$1.49
1
3
1
1
1
1
1
Total Cost: $419.70
As shown in table 5, the cost to construct a final market product is estimated at
$250.22. This cost is significantly less than the prototype, as many of the items in
the prototype will not be needed in a final product. A single board computer, like the
iPac 9302, has all the features that this IAHC unit requires at a lower price, and will
be implemented in the final product. The development kit used in the prototype is
eliminated, as this is only used in prototype design. Since our recommended
vendors offer a discounted cost when ordering large quantities of their products,
both the sensors and keypad will be available at a lower cost. Also, mass producing
the sensor housing and mount will lower costs.
Table 6 - Cost to Construct, Product
Item
Single Board Computer [6]
IR Sensors [3]
Keypad [4]
Housing and mount
Vendor
EMAC, inc
Newark.com
Newark.com
Mass
production
Cost per Unit Quantity
$170.00
$20.75
$11.97
$6.00
1
3
1
1
41
Total Cost:
$250.22
C. References
C.1 Vendors

TS-7300 SBC with 32 MB RAM:
http://www.embeddedarm.com/products/board-detail.php?product=TS-7300#

KIT-7300 Development Kit: http://www.embeddedarm.com/products/boarddetail.php?product=KIT-ARM#

AMN11112 Sensor:
http://www.newark.com/jsp/search/productdetail.jsp?SKU=13J4190&CMP=A
FC-OP

96BB2-056-F Keypad:
http://www.newark.com/jsp/search/productdetail.jsp?SKU=97F9131&CMP=A
FC-OP

PVC parts: Menards – Marion, Illinois 62959. Phone: 618-993-0982.

iPac 9302 w/ 32MB Flash & RAM:
http://www.emacinc.com/sbc_microcontrollers/ipac_9302.htm
42
D. Source Code
D.1 Low Level Module Source
E. Main Program
/******************************************************************************
*
* Function: main
*
* Description: Reads data from peripheral hardware and sends it to
*
The Processing Core.
*
* Notes: Compile with NO_SIM defined if not intending to run as a simulation
*
* Returns: None
*
* Created by Brandon Dwiel on 10-08-2008.
*
*******************************************************************************/
#include
#include
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<string.h>
“client/packetFunctions.c"
"client/socketFunctions.c"
"portLib/portLib.c"
/* *
* Data Constants
* */
#define
#define
#define
#define
PACKET_SIZE 18
MAC "00D069411E4C"
DATA "1"
KEYCODE "2"
/* *
* For GPIO, 0 signals output and 1 signals input
* */
/* Initialize Port B as all inputs */
#define PORTB_DDR 0x00
/* Initialize Port A with bit 5 as output if running as a simulation */
#ifndef NO_SIM
#define PORTA_DDR 0x20
/* Initialize Port A as all inputs if not running as a simulation */
43
#else
#define PORTA_DDR 0xF8
#endif
int main() {
char
packet[PACKET_SIZE];
int
connected;
int
check_connected = 0;
int
a_data = 101;
int
a_data_1;
int
a_data_2 = 101;
char
*b_data_str;
unsigned int a_data_prev = 0;
unsigned int b_data
= 0;
unsigned int b_data_prev = 0;
volatile unsigned int *port_a_dr;
volatile unsigned int *port_b_dr;
/* Used until get_keycode() is implemented */
int i = 0;
/* *
* Initialize GPIO to begin reading
* */
/* Port A is for the sensors */
port_a_dr = port_A_Init(PORTA_DDR);
/* Port B is for the keypad */
port_b_dr = port_B_Init(PORTB_DDR);
/* *
* Initialize the socket
* */
connected = socket_connect("10.2.1.2");
/* Clear contents of Port A Data Register */
portWrite(port_a_dr, 0x00);
/* *
* If running a simulation, a signal must be sent to initiate it
* */
#ifndef NO_SIM
fprintf(stderr, "Starting simulation in...");
for (i = 3; i > 0; i--) {
44
fprintf(stderr, " %d", i);
sleep(1);
}
fprintf(stderr, "\n");
/* Send signal to simulator to start from LCD-header Pin 11 */
portWrite(port_a_dr, 0x20);
usleep(250);
portWrite(port_a_dr, 0x00);
usleep(250);
#endif
/* *
* Begin infinite loop
* */
a_data_prev = portRead(port_a_dr);
while(1) {
/* Check Port A Data Register to see if sensor data has changed */
a_data_1 = portRead(port_a_dr);
record_data(a_data_1);
a_data = movement_detected();
if (a_data != a_data_prev) {
/* Data has changed so format it into a packet and send */
format_packet(packet, MAC, DATA, a_data);
send_packet(packet);
a_data_prev = a_data;
i++;
}
/* Check Port B Data Register to see if keypad data has changed */
if (b_data != b_data_prev) {
/* Data has changed so format it into a packet and send */
format_packet(packet, MAC, KEYCODE, b_data);
send_packet(packet);
b_data_prev = b_data;
}
if(connected == -1 && check_connected >= 30) {
connected = socket_connect("10.2.1.2");
check_connected = 0;
}
if(connected == -1)
check_connected++;
45
usleep(100);
}
}
F. GPIO Port Library
/******************************************************************************
*
* Function: port_A_Init
*
* Description: Initializes port A as input and output
*
* Notes: 0 in the mask means input, 1 is output.
*
Data gets initialized to 0
*
Port A is found on the LCD Header
*
File Descriptor for memory is never closed. It is used in an
*
Infinite loop so it should never need to be closed
*
* Returns: 0 if successful, -1 if fail
*
* Created by Brandon Dwiel on 2008-04-27.
*
*******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
<errno.h>
<sys/mman.h>
<stdio.h>
<stdlib.h>
<fcntl.h>
<unistd.h>
<sys/ioctl.h>
<sys/types.h>
volatile unsigned int *port_A_Init(unsigned int mask) {
/* Pointers to Data Register and Data Direction Register */
volatile unsigned int *padr;
volatile unsigned int *paddr;
/* Address base of Port Registers in memory */
unsigned int
port_base = 0x80840000;
unsigned char *port_addr;
int fd;
/* *
* Get Access and Location of Port Registers in memory
* */
/* Get access to Port Registers */
fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0)
46
{
*padr = -1;
return padr;
}
/* Get pointer to address base */
port_addr = mmap(
0,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
port_base);
/* Offset for Data Register from base is 0x00, 0x10 for Data Direction Register */
padr = (unsigned int *)(port_addr + 0x00);
paddr = (unsigned int *)(port_addr + 0x10);
/* *
* Initialize Port
* */
/* Set contents of Data Register to all 0 */
*padr = 0x00;
/* Set Data Direction Register according to mask */
*paddr = mask;
return padr;
}
/******************************************************************************
*
* Function: port_B_Init
*
* Description: Initializes port B as input and output
*
* Notes: 0 in the mask means input, 1 is output.
*
Data gets initialized to 0
*
Port B is found on DIO1 Header
*
Port B is multipurpose so other functions must be disabled first
*
File Descriptor for memory is never closed. It is used in an
*
Infinite loop so it should never need to be closed
*
* Returns: 0 if successful, -1 if fail
*
* Created by Brandon Dwiel on 2008-04-27.
*
*******************************************************************************/
volatile unsigned int *port_B_Init(unsigned int mask)
{
47
/* Pointers to Data Register and Data Direction Register */
volatile unsigned int *pbdr;
volatile unsigned int *pbddr;
/* Pointers to config registers to be changed */
volatile unsigned int *devcfg, *swlock;
/* Address base of Port and Config Registers in memory */
unsigned int port_base = 0x80840000;
unsigned int syscfg_base = 0x80930000;
unsigned char *port_addr, *syscfg_addr;
int fd;
/* *
* Get Access and Location of Config Registers
* */
fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0)
return pbdr;
syscfg_addr = mmap(
0,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
syscfg_base);
devcfg = (unsigned int *)(syscfg_addr + 0x80);
swlock = (unsigned int *)(syscfg_addr + 0xC0);
/* Disable DMA on Port B */
if (*devcfg & 0x60000000)
{
*swlock = 0xAA;
*devcfg &= ~0x60000000;
}
munmap((void *)syscfg_addr, getpagesize());
close(fd);
/* *
* Get Access and Location of Port Registers
* */
fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0)
return pbdr;
port_addr = mmap(
48
0,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
port_base);
/* Offset of Port B Data Register is 0x04 and Data Direction Register is 0x14 */
pbdr = (unsigned int *)(port_addr + 0x04);
pbddr = (unsigned int *)(port_addr + 0x14);
/* *
* Initialize Port
* */
/* Clear contents of Data Register */
*pbdr = 0x00;
/* Set Data Direction Register to mask */
*pbddr = mask;
return pbdr;
}
/******************************************************************************
*
* Function: port_F_Init
*
* Description: Initializes port F as input and output
*
* Notes: 0 in the mask means input, 1 is output.
*
Data gets initialized to 0
*
Port F is found on the DIO_1 Header
*
File Descriptor for memory is never closed. It is used in an
*
Infinite loop so it should never need to be closed
*
* Returns: 0 if successful, -1 if fail
*
* Created by Brandon Dwiel on 2008-04-27.
*
*******************************************************************************/
volatile unsigned int *port_F_Init(unsigned int mask) {
/* Pointers to Data Register and Data Direction Register */
volatile unsigned int *pfdr;
volatile unsigned int *pfddr;
/* Address base of Port Registers in memory */
unsigned int port_base = 0x80840000;
unsigned char *port_addr;
49
int fd;
/* *
* Get Access and Location of Port Registers in memory
* */
/* Get access to Port Registers */
fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0)
{
*pfdr = -1;
return pfdr;
}
/* Get pointer to address base */
port_addr = mmap(
0,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
port_base);
/* Offset for Data Register from base is 0x00, 0x10 for Data Direction Register */
pfdr = (unsigned int *)(port_addr + 0x30);
pfddr = (unsigned int *)(port_addr + 0x34);
/* *
* Initialize Port
* */
/* Set contents of Data Register to all 0 */
*pfdr = 0x00;
/* Set Data Direction Register according to mask */
*pfddr = mask;
return pfdr;
}
/******************************************************************************
*
* Function: portRead
*
* Description: Reads a port's data register
*
* Notes: Only the least significant byte is read
*
Port must be initialized first
*
* Returns: Port's least significant byte if successful, -1 if fail
50
*
* Created by Brandon Dwiel on 2008-04-27.
*
*******************************************************************************/
unsigned int portRead(unsigned int *port)
{
return (*port && 0xFF);
}
/******************************************************************************
*
* Function: portWrite
*
* Description: Writes to a port's data register
*
* Notes: Port must be initialized first
*
Mask should be 8-bits
*
* Returns: Data written if successful, -1 if fail
*
* Created by Brandon Dwiel on 2008-04-27.
*
*******************************************************************************/
unsigned int portWrite(unsigned int *port, unsigned int mask)
{
*port = mask;
return (*port && 0xFF);
}
/******************************************************************************
*
* Function: record_data
*
* Description: Keeps an array of the last ARRAY_SIZE readings from the
*
Sensors.
*
* Notes:
*
* Returns: None
*
* Created by Brandon Dwiel on 2008-11-03.
*
*******************************************************************************/
#define ARRAY_SIZE 40
#define RESET_VAL 500 * ARRAY_SIZE
int data_array[ARRAY_SIZE];
int counter = 0;
void record_data(unsigned int data)
{
int index = counter % ARRAY_SIZE;
51
data_array[index] = data;
counter++;
if(counter == RESET_VAL)
counter = 0;
return;
}
/******************************************************************************
*
* Function: movement_detected
*
* Description: Used to overcome the unsteadyness of the output from the
*
Sensors.
*
* Notes:
*
* Returns: The highest value that appears at least 20% in the last
*
ARRAY_SIZE data readings. If none meets this threshold,
*
-1 is returned.
*
* Created by Brandon Dwiel on 2008-11-03.
*
*******************************************************************************/
int movement_detected()
{
int i;
int data_values[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for(i = 0; i < ARRAY_SIZE; i++) {
data_values[data_array[i]]++;
}
for(i = 7; i >= 0; i--) {
if(data_values[i] >= (ARRAY_SIZE / 4))
return i;
}
return -1;
}
G. Keypad Functions
#include
#include
#include
#include
#include
<unistd.h>
<sys/types.h>
<sys/mman.h>
<sys/time.h>
<stdio.h>
52
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#define
#define
#define
#define
#define
#define
PBDR (0x04 / sizeof(unsigned int))
PBDDR (0x14 / sizeof(unsigned int))
PBMASK 0xFB // All Outputs except for bit 2
PFDR (0x30 / sizeof(unsigned int))
PFDDR (0x34 / sizeof(unsigned int))
PFMASK 0x02 // All Inputs except for bit 1
// Use 0 for busy-wait
#define POLL_FREQ
30
// Use 0 for busy-wait
#define DEBOUNCE_POLL_FREQ
256
#define DEBOUNCE_STABLE_MS
#define REPEAT_FREQ
30
#define REPEAT_DELAY_MS 500
15//10
#define MAX_KEYCODE_LENGTH 4
// struct timeval utility macros
#define ADD_MS(x, y) { \
(x).tv_usec += (y) * 1000; \
(x).tv_sec += (x).tv_usec / 1000000; \
(x).tv_usec = (x).tv_usec % 1000000; \
}
#define GTE(x, y) ((x).tv_sec > (y).tv_sec || ((x).tv_sec == (y).tv_sec && \
(x).tv_usec >= (y).tv_usec))
#define ELAPSED_MS(x, y)
((((x).tv_sec - (y).tv_sec) * 1000000 + \
((x).tv_usec - (y).tv_usec)) / 1000)
//unsigned int get_keys(void);
//unsigned int debounce(unsigned int);
//void keypad_event(unsigned int);
//void keypad_init(void);
static unsigned int lastkey = -1;
static struct timeval repeat_time;
static unsigned char keys[] = {
'1', '2', '3', 'A',
'4', '5', '6', 'B',
'7', '8', '9', 'C',
'*', '0', '#', 'D',
};
volatile unsigned int *pfdr;
53
volatile unsigned int *pfddr;
volatile unsigned int *pbdr;
volatile unsigned int *pbddr;
char keycode[MAX_KEYCODE_LENGTH + 1];
/******************************************************************************
*
* Function: keypad
*
* Description: Polls the DIO_1 header for keypad activity
*
* Notes:
*
* Returns: none
*
* Created by Brandon Dwiel on 11-7-2008.
*
*******************************************************************************/
int main(int argc, char **argv) {
char env_var[8] = "keycode";
unsigned int k;
unsigned char l;
struct timeval now;
unsigned int changed;
unsigned int pressed;
keypad_init();
/* Infinite loop to poll the keypad */
for(;;) {
k = get_keys();
/* Has the key changed? */
changed = pressed ^ k;
if (changed) {
/* Do debouncing on the new key press */
k = debounce(changed);
changed &= pressed ^ k;
}
if (changed) {
/* Get the key that was pressed */
l = keypad_event(changed & k);
54
pressed &= ~(changed & pressed);
pressed |= changed & k;
}
// Do key repeat as necessary
if (lastkey != -1) {
gettimeofday(&now, NULL);
if (GTE(now, repeat_time)) {
update_keycode(lastkey);
ADD_MS(repeat_time, REPEAT_FREQ / 1000);
}
}
if (POLL_FREQ) usleep(1000000 / POLL_FREQ);
}
}
/******************************************************************************
*
* Function: keypad_init
*
* Description: Initializes the ports that control the keypad
*
* Notes: The keypad is connected on D0 - D7 of the DIO_1 header
*
D0, D1, D3 - D7 are those repective bits on port B.
*
D2 is bit 2 of port F.
*
* Returns: none
*
* Created by Brandon Dwiel on 11-4-2008.
*
*******************************************************************************/
void keypad_init(void) {
/* *
* Initialize Ports
* */
port_B_Init();
port_F_Init();
return;
}
/******************************************************************************
*
* Function: port_B_Init
*
* Description: Initializes port B as input and output
*
* Notes: 0 in the mask means input, 1 is output.
55
*
Data gets initialized to 0
*
Port B is found on DIO1 Header
*
Port B is multipurpose so other functions must be disabled first
*
File Descriptor for memory is never closed. It is used in an
*
Infinite loop so it should never need to be closed
*
* Returns: 0 if successful, -1 if fail
*
* Created by Brandon Dwiel on 2008-04-27.
*
*******************************************************************************/
void port_B_Init() {
/* Pointers to config registers to be changed */
volatile unsigned int *devcfg, *swlock;
/* Address base of Port and Config Registers in memory */
unsigned int port_base = 0x80840000;
unsigned int syscfg_base = 0x80930000;
unsigned char *port_addr, *syscfg_addr;
int fd;
/* *
* Get Access and Location of Config Registers
* */
fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0)
return;
syscfg_addr = mmap(
0,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
syscfg_base);
devcfg = (unsigned int *)(syscfg_addr + 0x80);
swlock = (unsigned int *)(syscfg_addr + 0xC0);
/* Disable DMA on Port B */
if (*devcfg & 0x60000000)
{
*swlock = 0xAA;
*devcfg &= ~0x60000000;
}
munmap((void *)syscfg_addr, getpagesize());
close(fd);
56
/* *
* Get Access and Location of Port Registers
* */
fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0)
return;
port_addr = mmap(
0,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
port_base);
/* Offset of Port B Data Register is 0x04 and Data Direction Register is 0x14 */
pbdr = (unsigned int *)(port_addr + 0x04);
pbddr = (unsigned int *)(port_addr + 0x14);
/* *
* Initialize Port
* */
/* Clear contents of Data Register */
*pbdr = 0x00;
/* Set Data Direction Register to mask */
*pbddr = PBMASK;
return;
}
/******************************************************************************
*
* Function: port_F_Init
*
* Description: Initializes port F as input and output
*
* Notes: 0 in the mask means input, 1 is output.
*
Data gets initialized to 0
*
Port F is found on the DIO_1 Header
*
File Descriptor for memory is never closed. It is used in an
*
Infinite loop so it should never need to be closed
*
* Returns: 0 if successful, -1 if fail
*
* Created by Brandon Dwiel on 2008-04-27.
*
*******************************************************************************/
57
void port_F_Init() {
/* Address base of Port Registers in memory */
unsigned int port_base = 0x80840000;
unsigned char *port_addr;
int fd;
/* *
* Get Access and Location of Port Registers in memory
* */
/* Get access to Port Registers */
fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0)
return;
/* Get pointer to address base */
port_addr = mmap(
0,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
port_base);
/* Offset for Data Register from base is 0x00, 0x10 for Data Direction Register */
pfdr = (unsigned int *)(port_addr + 0x30);
pfddr = (unsigned int *)(port_addr + 0x34);
/* *
* Initialize Port
* */
/* Set contents of Data Register to all 0 */
*pfdr = 0x00;
/* Set Data Direction Register according to mask */
*pfddr = PFMASK;
return;
}
/******************************************************************************
*
* Function: keypad_event
*
* Description: Process the keypad event to get the corresponding character
*
* Notes:
58
*
* Returns: The last character to be pressed on the keypad
*
* Created by Brandon Dwiel on 11-4-2008.
*
*******************************************************************************/
char keypad_event(unsigned int on) {
unsigned int i;
lastkey = -1;
/* Loop through each bit */
for(i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
/* The key pressed was found */
if (on & (1 << i)) {
fprintf(stderr, "%c", keys[i]);
update_keycode(keys[i]);
return keys[i];
}
}
/* Update timer for repeated key check */
if (REPEAT_DELAY_MS && lastkey != -1) {
gettimeofday(&repeat_time, NULL);
ADD_MS(repeat_time, REPEAT_DELAY_MS);
}
}
/******************************************************************************
*
* Function: get_keys
*
* Description: Polls the keypad ports waiting for an event
*
* Notes: Each key corresponds to one row and one column pin.
*
All connections are open until a key is pressed and
*
The circuit between that key's row and column is completed.
*
Since the DIO_1 header has pull-up resistors, an open circuit is
*
Read as a logical one. The way to detect an event is to set
*
One row to '0' at a time and check each column for a key-press
*
Which would result in a logic '0' at the column pin.
*
A zero in the data direction regiser (ddr) sets that bit to input.
*
A one will set it to output
*
* Returns: Binary representation of the key pressed
*
* Created by Brandon Dwiel on 11-4-2008.
*
*******************************************************************************/
59
unsigned int get_keys(void) {
unsigned int pos;
/* Binary representation of keypad (D#0*C987B654A321) */
unsigned int on = 0;
/* loop through each row */
for(pos = 0; pos < 4; pos++) {
/* D2 is accessed through port F */
if(pos == 2) {
/* ~PBMASK = 0x02; Set all bits as input */
*pbddr &= ~PBMASK;
/* PFMASK = 0x02; Set D2 as output */
*pfddr |= PFMASK;
/* ~PFMASK = 0xFB; Reset D2 to '0' */
*pfdr &= ~PFMASK;
}
/* Check the rows from port B */
else {
/* ~PFMASK = 0xFB; Set D2 as input */
*pfddr &= ~PFMASK;
/* ~PBMASK = 0x02; Set the rest as inputs */
*pbddr &= ~PBMASK;
/* Set pos bit as an output */
*pbddr |= (1 << pos);
/* PBMASK = 0xFB; Set all bits as 1 */
*pbdr |= PBMASK;
/* Set pos bit as '0' */
*pbdr &= ~(1 << pos);
}
/* *
* Column bits are the upper four of port B. If an event occurred,
* The bit corresponding to that key's column will be read as a '0'.
* Invert the '0' to a one and shift according to which row the event
* Relates to
*/
on |= (~(*pbdr >> 4) & 0xf) << (4 * pos);
}
return on;
}
/******************************************************************************
60
*
* Function: debounce
*
* Description: Performs debouncing of the keypad
*
* Notes: The Keypad specifies 16 ms for debouncing
*
* Returns: Settled value
*
* Created by Brandon Dwiel on 11-4-2008.
*
*******************************************************************************/
unsigned int debounce(unsigned int mask) {
struct timeval start, now;
unsigned int last_val, val;
/* Read the port for the key pressed */
last_val = get_keys();
/* Begin timer */
gettimeofday(&start, NULL);
/* Begin loop to do debouncing */
do {
/* Read the port again for a key press */
val = get_keys();
/* Get the time now */
gettimeofday(&now, NULL);
/* Check to see if val differs from last_val and is inside mask */
if ((val ^ last_val) & mask)
/* Reset counter */
memcpy(&start, &now, sizeof(struct timeval));
last_val = val;
/* Sleep until time to check again */
if (DEBOUNCE_POLL_FREQ) usleep(1000000 / DEBOUNCE_POLL_FREQ);
/* End when no change in val for DEBONCE_STABLE_MS milliseconds */
} while (ELAPSED_MS(now, start) <= DEBOUNCE_STABLE_MS);
return val;
}
/******************************************************************************
*
* Function: update_keycode
61
*
* Description: Compiles single key presses into a code
*
* Notes: Code is cleared when KEYCODE_BEGIN is pressed
*
Once a completed code if entered, it sets the "keycode" environment
*
Variable to the keycode
*
* Returns: none
*
* Created by Brandon Dwiel on 11-4-2008.
*
*******************************************************************************/
#define KEYCODE_BEGIN 'A'
int j = 0;
void update_keycode(char k) {
char *buffer;
/* KEYCODE_BEGIN designates the start of code */
if(k == KEYCODE_BEGIN)
j = 0;
else {
keycode[j%5] = k;
j++;
}
if(j == (MAX_KEYCODE_LENGTH)) {
keycode[j+1] = '\0';
memcpy(buffer, "keycode=", 8);
memcpy(buffer, keycode, 6);
fprintf(stderr, "%s\n", buffer);
putenv(buffer);
}
return;
}
H. Packet Functions
/******************************************************************************
*
* Function: format_packet
*
* Description: Formats data from peripheral hardware
*
* Notes:
*
* Returns: Formatted packet
*
62
* Created by Brandon Dwiel on 11-08-2008.
*
*******************************************************************************/
#include <string.h>
char *format_packet(char *packet, char *mac, char *type, int data) {
char data_str[5];
sprintf(data_str, "%04X", data);
strcpy(packet, mac);
strcat(packet, type);
strcat(packet, data_str);
printf("%s\n", packet);
return packet;
}
I. Socket Functions
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<string.h>
<stdio.h>
<stdlib.h>
<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<arpa/inet.h>
<unistd.h>
<fcntl.h>
<sys/stat.h>
struct sockaddr_in server_addr;
int sockfd;
int connected;
int text_sent = 0;
/******************************************************************************
*
* Function: socket_connect
*
* Description: Attempts to connect to processing core
*
* Notes:
*
* Returns: 0 if successful, -1 if fail
*
* Created by Brandon Dwiel on 11-08-2008.
*
*******************************************************************************/
int socket_connect(char *ip) {
63
/* Creates socket stream and assigns the file descriptor */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
/* Makes socket availible for other processes */
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(7980);
/* Test connection with the server. If connetion fails error messege is returned and program ends */
if(connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
close(sockfd);
perror("Connection failed.");
/* Only send one text for failure to connect */
if(text_sent == 0) {
/* Send text message to user */
text_sent = 1;
}
connected = 0;
}
return -1;
}
else {
printf("connected.\n");
connected = 1;
return 0;
}
}
int send_packet(char *packet) {
switch(connected) {
case 0:
return -1;
case 1:
if(write(sockfd, packet, strlen(packet)) == -1) {
perror("Write packet to socket failed.");
connected = 0;
return -1;
}
64
}
}
I.1 High Level Module Source
J. System Information Server
#!/usr/bin/python
#########################################################
#
#
#
System Information Server
#
#
#
#
Written by Anthony Kulis
#
#
#
#
ECE495 - Senior Design (Saluki Engineering Company)
#
#
#
#
Usage: sis <server port>
#
#
#
#
Word of Caution: This server is basically unsecure. All though it is
#
#
simple, and only takes certain arguments, its job to send system
#
#
information to whom ever requests it. This means only to use it on
#
#
trusted networks with adequate network security. If time permits, I
#
#
will lock it down more, but for a test model for ECE495, securing the
#
#
network is more than adequate.
#
#
#
#########################################################
from socket import *
import os
import sys
#who’s your port
DefaultPort = 55555
#reboot the uC
def reboot():
os.popen('reboot')
return 0
#shut down the uC
def halt():
os.popen('poweroff')
return 0
#the method to get host name of embedded system
def getHost():
hostname=gethostname()
return hostname
#the method to read the embedded systems syslog
65
def readSysLog():
syslog=os.popen('cat /var/log/syslog').read()
return syslog
#the method to report the ip address of the embedded system
def getIpAddress():
ipaddress=os.popen('ifconfig eth0').read()
return ipaddress
#the method to help those who are in need
def getHelp():
help="Options are:\n\
*****Information*****\n\
config - returns uC configuration file\n\
hostname - returns system hostname\n\
ipaddr - returns system ip address (dotted quad)\n\
syslog - returns system main log file\n\n\
*****Controls*****\n\
audible - sends audible alert to uC\n\
text - sends text message to uC operator\n\
reboot - reboots the uC\n\
halt - shuts down the uC\n"
return help
def config():
sheet=os.popen('cat /lib/sis/uC.config').read()
return sheet
def audible():
os.popen('/bin/audible')
return 0
def text():
os.popen('/bin/text_message')
return "Text Message Sent"
#check for proper usage
if len(sys.argv)-1 > 0:
print 'usage: sis'
sys.exit()
#who’s your host?
host='uC' #make sure this is on the same page as the client
#what port you want me on?
port=int(DefaultPort)
#okay, here is my address
addr=(host, port)
#lets read 1k at a time
66
bufsize=1024
#make the generic sockets
genericTCPsocket=socket(AF_INET, SOCK_STREAM)
genericTCPsocket.bind(addr)
genericTCPsocket.listen(1)
#run forever please
while 1:
#spawn client socket from generic one
clientTCPsocket,addr=genericTCPsocket.accept()
#read from socket
command=clientTCPsocket.recv(bufsize)
#read the command and do the appropiate thing
if command == "hostname":
result = getHost()
elif command == "ipaddr":
result = getIpAddress()
elif command == "syslog":
result = readSysLog()
elif command == "help":
result = getHelp()
elif command == "audible":
result = audible()
elif command == "text":
result = text()
elif command == "config":
result = config()
elif command == "reboot":
result = reboot()
elif command == "halt":
result = halt()
else:
result = 'Error, bad request. Use ./client <help> for more information'
if command != 'audible':
msglength=len(result)
totalsent=0
while totalsent<msglength:
sent = clientTCPsocket.send(result[totalsent:])
totalsent = totalsent + sent
clientTCPsocket.close()
genericTCPsocket.close()
sys.exit()
67
K. Scripts
K.1.1.1 Audible
#!/bin/bash
#About: Script to simulate audible alert
#Anthony Kulis
echo "this is an audible test done on $(date)" >>/home/tony/audible.test.file
K.1.1.2 Text Message
#!/bin/bash
#Script to generate text messages
#For demonstrative purpose, only a standard message is used. Otherwise, at $1
can be used to grab from args
#eg </lib/sis/messages/$1
#This allows for sis to call various types of email alerts
#Anthony Kulis
/usr/bin/mutt anthony.kulis@gmail.com < /lib/sis/messages/standard
/usr/bin/mutt 6182011154@vtext.com < /lib/sis/messages/standard
K.1.1.3 SIS Boot-up Script
#!/bin/bash
# /etc/init.d/sis: start and stop sis
#Anthony Kulis
PIDFILE="/var/run/sis.pid"
68
touch $PIDFILE
case "$1" in
start)
echo -n "Starting the System Information Server"
start-stop-daemon --start -b --exec /etc/sis/sis
PID=`pidof sis` || true
echo $PID > $PIDFILE
echo "."
;;
stop)
echo -n "Stopping the System Information Server"
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
echo "."
;;
*)
echo "Usage: /etc/init.d/sis {start|stop}"
exit 1
esac
K.2 Processing Core Source
L. Parser
-module(parsers).
-export([parseConfig/1, parseLine/2]).
parseConfig(FileName) when is_list(FileName) ->
{ok, File} = file:read_file(FileName),
69
parseConfig(File);
parseConfig(File) when is_binary(File) ->
io:format("File is ~p\n", [File]),
FileLines = string:tokens(binary_to_list(File), getTerminator()),
Rules = cutList(FileLines, "RULE"),
io:format("Split results in ~p\n", [Rules]),
SplitRules = lists:map(fun(X)->
io:format("splitting rules ~p\n", [X]),
Split = cutList(X, "*--*"),
io:format("~p\n", [Split]),
[RawConditions, RawActions|_] = Split,
SplitConditions = cutList(RawConditions, "*__*"),
Conditions = lists:map(fun(Condition)->
io:format("Mapping conditions ~p\n", [Condition]),
parseCondition(Condition)
end, SplitConditions),
Actions = RawActions,
{Conditions, Actions}
end, Rules),
SplitRules.
cutList(List, Value)->
io:format("Cutting ~p\n", [List]),
cutList(List, Value, [], []).
cutList([Value|[]], Value, Result, CurrentResult)->
Result ++ [CurrentResult];
cutList([First|[]], _, Result, CurrentResult)->
Result ++ [CurrentResult ++ [First]];
cutList([Value|Rest], Value, Result, []) ->
cutList(Rest, Value, Result, []);
cutList([Value|Rest], Value, Result, CurrentResult) ->
NextResult = Result ++ [CurrentResult],
cutList(Rest, Value, NextResult, []);
cutList([First|Rest], Value, Result, CurrentResult)->
NextResult = CurrentResult ++ [First],
cutList(Rest, Value, Result, NextResult).
parseCondition(FileLines)->
io:format("Parsing ~p\n", [FileLines]),
PreppedLines = lists:map(fun(X) ->
LineText = getText(X),
{countLeadingTabs(X, LineText), LineText}
end, FileLines),
{Retval, _} = parseLine(PreppedLines, []),
Retval.
parseLine([Line|[Next|Rest]], CurrentResult) ->
io:format("Current Result: ~p\nCurrent Line: ~p\nLinesLeft ~p\n",
[CurrentResult, Line, [Next|Rest]]),
{LeadingTabs, LineText} = Line,
{NextTabs,_} = Next,
io:format("Current tabs : ~p next tabs: ~p , Line text: ~p\n",
[LeadingTabs, NextTabs, LineText]),
if
(NextTabs > LeadingTabs)->
io:format("parsing child\n"),
{TempResult, NewLines} = parseLine([Next|Rest], []),
NewResult = lists:append(CurrentResult, [LineText ,
70
TempResult]),
io:format("Child result is ~p\n", [TempResult]),
NewResult;
true ->
io:format("skipping child, next lines are ~p\n",
[[Next|Rest]]),
{NewResult, NewLines} =
{lists:append(CurrentResult,[LineText]), [Next|Rest]}
end,
if
(NewLines == []) ->
{ReturnResult, ReturnLines} = {NewResult, []};
true ->
[SiblingNext| _] = NewLines,
{SiblingTabs,_} = SiblingNext,
io:format("StartingSiblingBlock ~p\n", [NewLines]),
if
(SiblingTabs == LeadingTabs)-> %This is on same level as current, thus
sibling
{ReturnResult, ReturnLines} = parseLine(NewLines,
NewResult);
true-> %Next line is sibling to an ancestor level
{ReturnResult, ReturnLines} = {NewResult, NewLines}
end
end,
io:format("Result of line ~p
{ReturnResult, ReturnLines};
is ~p\n", [LineText, ReturnResult]),
parseLine([Line|[]], CurrentResult) ->
io:format("Current Result: ~p\nCurrent Line: ~p\n", [CurrentResult,
Line]),
io:format("Last line: ~p\n", [Line]),
{_,LineText} = Line,
Result = lists:append(CurrentResult, [LineText]),
io:format("Result of line ~p is ~p\n", [LineText, Result]),
{Result, []};
parseLine(_,_)->
io:format("Should never get here\n").
getText(String)->
string:strip(String, left, $\t).
countLeadingTabs(String, "AND")->
1+countLeadingChar(String, $\t);
countLeadingTabs(String, "OR") ->
1+countLeadingChar(String, $\t);
countLeadingTabs(String, _) ->
countLeadingChar(String, $\t).
countLeadingChar([Current | String], Character) ->
if
(Current == Character) ->
1 + countLeadingChar(String, Character);
true ->
0
end;
countLeadingChar([], _) ->
0.
71
getTerminator() ->
{Major, _} = os:type(),
case Major of
unix ->
"\n";
windows ->
"\r\n";
true ->
"\n"
end.
Rule Manager
-module(ruleManager).
-export([evaluate/0, start/0, stop/0,set/1]).
start()->
spawn(fun() ->
register(ruleManager, self()),
process_flag(trap_exit, true),
put(rules, []),
dispatchLoop()
end).
stop() -> ruleManager ! stop.
set(Rules)->
ruleManager ! {set, Rules}.
evaluate()->
ruleManager ! evaluate.
dispatchLoop()->
receive
evaluate->
evaluate(get(rules)),
dispatchLoop();
{set, Rules}->
put(rules, Rules);
stop->
exit(normal)
end.
evaluate({Conditions, Actions})->
case (checkRule(Conditions)) of
true->
doActions(Actions);
false ->
false
end.
checkRule([Condition | Rest])->
case conditionTrue(Condition) of
true ->
72
checkRule(Rest);
false ->
false
end;
checkRule([])->
true.
conditionTrue([permission, Level, Test]) ->
Current = stateManager:getPermission(),
case Test of
less ->
Current < Level;
greater ->
Current > Level;
equal ->
Current == Level;
_ ->
false
end;
conditionTrue([position, P, Test]) ->
Current = stateManager:currentPositions(),
case Test of
inside ->
Current == P;
outside ->
Current /= P;
_ ->
false
end;
conditionTrue(_)->
false.
doActions([Action | Rest]) ->
doAction(Action),
doActions(Rest);
doActions([])->
true.
doAction([email, Destination, Message | Rest])->
sendMessage("email")
doAction([text, Destination, Message | Rest])->
sendMessage("text").
sendMessage(Message)->
Socket = gen_tcp:connect(foo, 5555, [binary, {packet, 0}]),
gen_tcp:send(Socket, Message),
gen_tcp:close(Socket).
M. Socket Manager
-module(socketManager).
-export([start/0, stop/0]).
start() ->
io:format("starting socket manager~n"),
spawn(fun()->
73
register(socketManager, self()),
process_flag(trap_exit, true),
{ok, Socket} = gen_tcp:open(5555, [binary]),
put(lastUser, [0,0,0,0]),
dispatchLoop(Socket)
end).
stop()->
socketManager ! stop.
dispatchLoop(Socket)->
case gen_tcp:accept(Socket, 500) of
{ok, Sock} ->
spawn(fun()->portLoop(Sock)end);
{error, timeout}->
null
end,
receive
stop->
exit(normal);
_->
dispatchLoop(Socket)
after 100.0 ->
dispatchLoop(Socket)
end.
portLoop(Socket)->
case gen_tcp:recv(Socket, 0) of
{ok, Data}->
{ok,[MessageSequence]} = io_lib:fread("~d",
lists:sublist(Data, 17,4)),
handlePacket(MessageSequence),
portLoop(Socket);
{error, closed}->
ok
end.
handlePacket(Message)->
Type = lists:sublist(Message, 13, 1),
Data = lists:sublist(Message, 14, 4),
case Type of
1->
Sensors = lists:sublist(Message, 21, 3),%need to verify
what this actually is
stateManager:currentPositions(Sensors);
2->
LastCode = get(lastUser),
if
(LastCode /= Data) ->
stateManager:adduser(Data),
stateManager:leaveUser(get(lastUser)),
put(lastUser, Data);
true ->
null
74
end;
3->
ruleManager:set(parsers:parseConfig("testIn"))
end.
N. State Manager
-module(stateManager).
-export([getPermission/0, currentPositions/0, currentPositions/1, adduser/1,
leaveUser/1,addKeycode/1, revokeKeycode/1, start/0, stop/0]).
%process dictionary has:
%positions - list of all positions by module MAC address
%permissions - list of all active permissions in house
%keycodes - list of tuples where each is keycode, permission level
start() ->
spawn(fun() ->
register(stateManger, self()),
process_flag(trap_exit, true),
put(permissions, [-1]),
put(keycodes, []),
dispatchLoop()
end).
stop() -> stateManager ! stop.
addKeycode({Code,Level})->
stateManager ! {addCode, {Code, Level}}.
revokeKeycode({Code}) ->
stateManager ! {takeCode, Code}.
adduser(Code) ->
stateManager ! {adduser, Code}.
leaveUser(Code) ->
stateManager ! {leaveUser, Code}.
getPermission()->
rpc(permission).
currentPositions() ->
rpc(position).
currentPositions(NewPositions)->
stateManager ! {positionChange, NewPositions}.
rpc(Call) ->
stateManager ! {Call, self()},
rpcWait(Call).
rpcWait(Call) ->
receive
{Call, stateManager, Response}->
Response;
Any ->
self() ! Any,
rpcWait(Call)%not what we were looking for, drop it back into message
queue for later
end.
findPermissionLevel(KeyCode)->
PermissionTupl = lists:keySearch(KeyCode, 1, get(keycodes)),
case PermissionTupl of
75
{KeyCode, PermissionLevel} ->
PermissionLevel;
false ->
-1
end.
dispatchLoop()->
receive
{permission, Sender}->
Sender ! lists:max(get(permissions)),
dispatchLoop();
{position, Sender} ->
Sender ! get(positions),
dispatchLoop();
{positionChange, Position} ->
put(positions, Position),
dispatchLoop();
{addCode, CodeTupl} ->
put(keycodes, [CodeTupl | get(keycodes) ]),
dispatchLoop();
{takeCode, Code} ->
put(keycodes, lists:filter(fun(Elem) -> {ElCode,_} =
Elem,ElCode ==Code end, get(keycodes))),
dispatchLoop();
{addUser, Code} ->
put(permissions, [findPermissionLevel(Code) |
get(permissions)]),
ruleManager:evaluate(),%state changed, reevaluate rules
dispatchLoop();
{leaveUser, Code} ->
PermissionLevel = findPermissionLevel(Code),
if(PermissionLevel >= 0) ->
put(permissions, lists:filter(fun(Elem)->
Elem == PermissionLevel end, get(permissions)))
end,
ruleManager:evaluate(),
dispatchLoop();
stop ->
exit(normal);
_ ->
dispatchLoop()
end.
N.1 User Interface Source
O. Example Configuration File
RULE
and
or
position
position
7
6
inside
inside
76
permission
2
less
*__*
text
audible
P. Result of Parsing Configuration
[[and,
[or,
{position, 7, inside}, {position, 6, inside}],
{permission, 2, less]]
P.1 Simulation Software
Q. SIS Client
#!/usr/bin/python
from socket import *
import os
import sys
host="uc"
port=55555
bufsize=1024
addr=(host,port)
testsocket=socket(AF_INET,SOCK_STREAM)
testsocket.connect(addr)
request=sys.argv[1]
77
testsocket.send(request)
totalrcvd=1
while not totalrcvd==0:
data=testsocket.recv(bufsize)
totalrcvd=len(data)
print data
testsocket.close()
R. Processing Core
#!/usr/bin/python
#PROCESSING CORE SIMULATION
#WRITTEN BY ANTHONY KULIS FOR ECE495 IAHC-TEAM 61
#USAGE: CALL PCSIM <PORT NUM> [RULE SHEET]
from socket import *
import os
import sys
#check for proper usage
if len(sys.argv) < 2:
print 'usage: pcsim <server port> [rule sheet]'
sys.exit()
78
portSis=55555
hostSIS="uc"
addrSIS=(hostSIS,portSis)
SISBufSize = 1024
bufsize=17 #what size is the tcp packet?
port=int(sys.argv[1])
host="box"
addr=(host,port)
#make the sensor sockets
genericSensorTCPsocket=socket(AF_INET, SOCK_STREAM)
#take over addr and dont allow anyone else to create one there
genericSensorTCPsocket.bind(addr)
#now pay attention, I might have a vistor
genericSensorTCPsocket.listen(1)
#spawn sensor socket from generic one
sensorTCPsocket,addr=genericSensorTCPsocket.accept()
flag=0
while 1:
79
#read from socket
data=sensorTCPsocket.recv(bufsize)
machine = data[0:10] #i have no idea what the packet looks like, adjust!
keycode = data[-5:-1] #i have no idea what the packet looks like, adjust!
sensors = data[-1:] #i have no idea what the packet looks like, adjust!
#if keypad works, then use that data to open a sheet about it
#but for here we will use the agrv[2] for that
configSheet = open(keycode).read().split()
#config sheet should look similar to this
#0 allowed
#1 allowed
#2 allowed
#3 allowed
#4 text
#5 text
#6 text
#7 text
#set up some controls
x=0
print "Data recieved: ",sensors
#iterate thru all possible combos
80
for each in configSheet:
if sensors == each:
if configSheet[x+1] == "allowed":
flag = 0
else:
if flag == 0:
flag = 1
print "Violation! Control is: ",configSheet[x+1]
#build the connection to the SIS
SISsocket=socket(AF_INET,SOCK_STREAM)
SISsocket.connect(addrSIS)
SISsocket.send(configSheet[x+1])
totalrcvd=1
while not totalrcvd==0:
SISResponse = SISsocket.recv(SISBufSize)
totalrcvd=len(SISResponse)
print SISResponse
SISsocket.close()
x=x+1
81
Download