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