Difference between revisions of "S15: RFID Security Box"
Proj user6 (talk | contribs) (→Schedule) |
Proj user18 (talk | contribs) (→Shift register bus overview) |
||
(119 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | === | + | == RFID Security Box == |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | [[File:S15_244_G29_IMG_0009.png|center]] | |
== Abstract == | == Abstract == | ||
− | The RFID Security Box is a container with an | + | The RFID Security Box is a electronically locked container with an external control box that enables unlocking by using RFID tags, NFC-compatible devices, |
− | or a manually entered | + | or a manually entered PIN. |
== Objectives & Introduction == | == Objectives & Introduction == | ||
− | + | ||
+ | This is a security box for storing valuables. It can be unlocked by entering a PIN through a numeric keypad, or by communicating with a NFC-compatible device, or RFID tags, tokens, and cards. | ||
+ | |||
+ | Sensors and other components used: | ||
+ | |||
+ | * NFC/RFID Controller breakout board | ||
+ | * Lock-style Solenoid | ||
+ | * 3x4 Phone-style Matrix Keypad | ||
+ | * RGB LCD Screen | ||
=== Team Members & Responsibilities === | === Team Members & Responsibilities === | ||
* Rajwinder Ruprai | * Rajwinder Ruprai | ||
+ | ** NFC/RFID driver | ||
+ | ** Software testing | ||
* Charles MacDonald | * Charles MacDonald | ||
+ | ** Hardware and software design | ||
+ | ** Physical construction | ||
+ | ** Hardware testing | ||
== Schedule == | == Schedule == | ||
− | |||
{| class="wikitable" | {| class="wikitable" | ||
Line 35: | Line 37: | ||
|- | |- | ||
! scope="row"| 1 | ! scope="row"| 1 | ||
− | | | + | | 04/14 |
− | | | + | | Order parts needed for required functionality, work on box design<br/> (component placement, mounting requirements, etc.) |
− | | Completed | + | | Completed |
|} | |} | ||
Line 47: | Line 49: | ||
! scope="col"| Actual | ! scope="col"| Actual | ||
|- | |- | ||
− | ! scope="row"| | + | ! scope="row"| 2 |
− | | | + | | 04/21 |
− | | Task | + | | Develop keypad and LCD display drivers. |
− | | Completed | + | | Completed. |
+ | |} | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! scope="col"| Week# | ||
+ | ! scope="col"| Date | ||
+ | ! scope="col"| Task | ||
+ | ! scope="col"| Actual | ||
+ | |- | ||
+ | ! scope="row"| 3 | ||
+ | | 04/28 | ||
+ | | Develop solenoid driver and start work on RFID/NFC driver. | ||
+ | | Completed. | ||
+ | |} | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! scope="col"| Week# | ||
+ | ! scope="col"| Date | ||
+ | ! scope="col"| Task | ||
+ | ! scope="col"| Actual | ||
+ | |- | ||
+ | ! scope="row"| 4 | ||
+ | | 05/5 | ||
+ | | Finish RFID/NFC driver, finish PSU wiring. | ||
+ | | Completed. | ||
+ | |} | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! scope="col"| Week# | ||
+ | ! scope="col"| Date | ||
+ | ! scope="col"| Task | ||
+ | ! scope="col"| Actual | ||
+ | |- | ||
+ | ! scope="row"| 5 | ||
+ | | 05/12 | ||
+ | | Component integration and testing. | ||
+ | | Completed. | ||
+ | |} | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! scope="col"| Week# | ||
+ | ! scope="col"| Date | ||
+ | ! scope="col"| Task | ||
+ | ! scope="col"| Actual | ||
+ | |- | ||
+ | ! scope="row"| 6 | ||
+ | | 05/19 | ||
+ | | Tweaking and polishing implementation. | ||
+ | | Completed. | ||
|} | |} | ||
{| class="wikitable" | {| class="wikitable" | ||
Line 59: | Line 109: | ||
! scope="col"| Actual | ! scope="col"| Actual | ||
|- | |- | ||
− | ! scope="row"| | + | ! scope="row"| 7 |
− | | | + | | 05/25 |
− | | | + | | Project presentation. |
− | | | + | | Rescheduled. |
|} | |} | ||
== Parts List & Cost == | == Parts List & Cost == | ||
− | + | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! scope="col"| Quantity | ||
+ | ! scope="col"| Description | ||
+ | ! scope="col"| Cost | ||
+ | |- | ||
+ | | 1 | ||
+ | | Lock-style Solenoid - 12VDC | ||
+ | | $14.95 | ||
+ | |- | ||
+ | | 1 | ||
+ | | I2C / SPI character LCD backpack (not used) | ||
+ | | $10.00 | ||
+ | |- | ||
+ | | 1 | ||
+ | | MiFare Classic (13.56 MHz) tag assortment (1KB) | ||
+ | | $10.00 | ||
+ | |- | ||
+ | | 1 | ||
+ | | PN532 NFC/RFID controller breakout board (v1.6) | ||
+ | | $39.95 | ||
+ | |- | ||
+ | | 1 | ||
+ | | 3x4 Phone-style Matrix Keypad | ||
+ | | $7.50 | ||
+ | |- | ||
+ | | 1 | ||
+ | | RGB backlight negative LCD 16x2 + extras (RGB on black) | ||
+ | | $13.95 | ||
+ | |- | ||
+ | | 1 | ||
+ | | Extra-long break-away 0.1" 16-pin strip male header (5 pieces) | ||
+ | | $3.00 | ||
+ | |- | ||
+ | | 1 | ||
+ | | Murata OKI-78SR Fixed Output 1.5A DC/DC Converter (5.0V) | ||
+ | | $4.30 | ||
+ | |- | ||
+ | | 1 | ||
+ | | ST Microelectronics 3.3V Linear Regulator (not used) | ||
+ | | $0.64 | ||
+ | |- | ||
+ | | 1 | ||
+ | | 1N4001 Diode | ||
+ | | $0.13 | ||
+ | |- | ||
+ | | 1 | ||
+ | | Meanwell RS-15-12 +12V, 1.3A PSU (not used) | ||
+ | | $9.95 | ||
+ | |- | ||
+ | | 1 | ||
+ | | IEC 320-C14 AC power receptacle (not used) | ||
+ | | $0.79 | ||
+ | |- | ||
+ | | 1 | ||
+ | | Ikea VALJE Wall Cabinet with Door | ||
+ | | $35.00 | ||
+ | |- | ||
+ | | 1 | ||
+ | | Mounting hardware assortment (nuts, screws, standoffs, spacers) | ||
+ | | $10.00 | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | As functionality was changed, some components were not used. They may be integrated into a future revision of the device. | ||
== Design & Implementation == | == Design & Implementation == | ||
− | |||
=== Hardware Design === | === Hardware Design === | ||
− | + | ||
+ | Rough draft of system architecture / schematic diagram (external link): | ||
+ | |||
+ | * https://www.dropbox.com/s/1jhlvk78qywjkfu/244_project.jpg?dl=0 | ||
+ | |||
+ | This is to mainly illustrate an early phase of development. More detailed diagrams are provided later. | ||
=== Hardware Interface === | === Hardware Interface === | ||
− | |||
− | === | + | The various buses are allocated as follows |
− | + | ||
+ | * SSP0 Shift register chain | ||
+ | * SSP1 AT45, SD card, PN532 breakout board | ||
+ | * GPIO Various control outputs and inputs | ||
+ | * UART2 (free) | ||
+ | * UART3 (free) | ||
+ | |||
+ | ===GPIO assignments=== | ||
+ | |||
+ | * P2.0 : Keypad matrix row data, bit 0 | ||
+ | * P2.1 : Keypad matrix row data, bit 1 | ||
+ | * P2.2 : Keypad matrix row data, bit 2 | ||
+ | * P2.3 : Keypad matrix row data, bit 3 | ||
+ | * P2.4 : Piezo buzzer control | ||
+ | * P2.5 : HD47780 DB7 (input to read busy status) | ||
+ | * P2.6 : Storage register clock for shift register #1 | ||
+ | * P2.7 : Storage register clock for shift register #2 | ||
+ | * P1.19 : HD47780 RS | ||
+ | * P1.20 : HD47780 R/W# | ||
+ | * P1.22 : HD47780 EN | ||
+ | * P1.23 : Operator button input (pushbutton) | ||
+ | |||
+ | External reset button input | ||
+ | ===Shift register assignments=== | ||
+ | |||
+ | * S1.0 : LCD panel tri-color backlight LED blue cathode | ||
+ | * S1.1 : LCD panel tri-color backlight LED green cathode | ||
+ | * S1.2 : LCD panel tri-color backlight LED red cathode | ||
+ | * S1.3 : To solenoid coil connector | ||
+ | * S1.4 : (Unused) | ||
+ | * S1.5 : Keypad matrix column select, bit 0 | ||
+ | * S1.6 : Keypad matrix column select, bit 1 | ||
+ | * S1.7 : Keypad matrix column select, bit 2 | ||
+ | |||
+ | * S2.0 : LCD interface, data bit 0 | ||
+ | * S2.1 : LCD interface, data bit 1 | ||
+ | * S2.2 : LCD interface, data bit 2 | ||
+ | * S2.3 : LCD interface, data bit 3 | ||
+ | * S2.4 : LCD interface, data bit 4 | ||
+ | * S2.5 : LCD interface, data bit 5 | ||
+ | * S2.6 : LCD interface, data bit 6 | ||
+ | * S2.7 : LCD interface, data bit 7 | ||
+ | |||
+ | These connections are described in more detail later. | ||
+ | |||
+ | === Power supply === | ||
+ | |||
+ | Originally a compact MeanWell AC to DC +12V power supply was going to be fitted inside the control box. Due to space limitations we had to remove it and use an external 12V DC power supply. | ||
+ | Only the solenoid requires +12V, the rest of the system uses +5V parts and the SJOne board has its own LDO regulator to provide +3.3V. A power distribution diagram is shown below: | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0015.png|center]] | ||
+ | |||
+ | |||
+ | Due to the high voltage drop between +12V input and +5V output, a 7805 linear regulator is inappropriate to use as it will dissipate the 7-volt difference as heat. | ||
+ | Instead, a Murata OKI-78SR switching regulator was used. This device has the same pin configuration and roughly the same size as the 7805, but has very little heat dissipation even for | ||
+ | high input differential like +36V to +5V. No heatsink or passive/active cooling was needed. | ||
+ | |||
+ | ===Shift register bus overview=== | ||
+ | |||
+ | To add additional outputs to the SJ-One board, several 74HCT595 shift registers have been connected to the SPI (SSP0) bus. | ||
+ | Despite not being true SPI devices the shift registers work well for adding more I/O. | ||
+ | |||
+ | The 74HCT595 has LVCMOS compatible inputs allowing it to interface with the 3.3V I/O of the LPC1758. It consists of an 8-bit shift register which is clocked by SCLK and fed data by MOSI. The shift register output signal (Q7') can be fed to the data input of additional shift registers. The shift register output feeds the input of an 8-bit output latch, which has a dedicated clock input. In this way you can shift | ||
+ | any kind of data through the shift register chain without disturbing the actual output pins, and then only load data from the shift register to the output latch when necessary. | ||
+ | |||
+ | Here P2.6 and P2.7 are the output register clock signals, so by toggling either GPIO pin the contents of the shift register becomes the new output. | ||
+ | |||
+ | The output of the final shift register in the chain can be fed back into MISO for diagnostic purposes; while the shift registers are output-only devices, connecting the output to MISO allows you to verify that the data transmitted through the chain was correctly received. This can help identify wiring errors or other problems early on and is a good diagnostic tool. | ||
+ | |||
+ | An overview of the shift register bus is shown in the following diagram: | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0001.png|center]] | ||
+ | |||
+ | While only two shift registers are used, more could be added. There is no practical limit other than the worst-case time it takes to change the last shift register is the time it takes to shift data through all shift registers in the chain. For high speed applications the chain should be kept short, but even then the 74HCT595 is rated to run up to 25 MHz, so with good wiring even a long chain can be updated rather quickly. | ||
− | === | + | Other diagrams shown later will detail the low-level implementation between the shift registers and the peripherals they drive. |
− | This section | + | |
+ | ===Keypad implementation=== | ||
+ | |||
+ | The keypad is arranged as a 4x3 matrix. Where each row and column intersect is a switch, and a physical connection between the row and column | ||
+ | is made when the switch is depressed. Otherwise there is no connection when a switch is released, and the relationship between the row and the column at that point | ||
+ | appears as an open circuit. | ||
+ | |||
+ | As the keypad has no datasheet the first task was to disassemble the keypad and use the continuity test function of a multimeter to determine | ||
+ | the pin assignments: | ||
+ | |||
+ | * Pin 1 - Not used (goes to a test point on the PCB) | ||
+ | * Pin 2 - Row (keys *,0,#) | ||
+ | * Pin 3 - Row (keys 7,8,9) | ||
+ | * Pin 4 - Row (keys 4,5,6) | ||
+ | * Pin 5 - Row (keys 1,2,3) | ||
+ | * Pin 6 - Column (keys 3,6,9,#) | ||
+ | * Pin 7 - Column (keys 2,5,8,0) | ||
+ | * Pin 8 - Column (keys 1,4,7,*) | ||
+ | |||
+ | The notation of row vs. column is completely arbitrary, and were chosen only to have a consistent naming scheme. | ||
+ | |||
+ | Here is a diagram of the matrix inside the keypad: | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0000.png|center]] | ||
+ | |||
+ | As the rows and columns are shorted when a key is pressed, it is important to never drive the rows and columns to complementary output levels. The rows should be inputs with internal pull-ups enabled, and the columns are outputs that are driven low or high (or tri-stated). | ||
+ | |||
+ | The keypad can be scanned like so: | ||
+ | |||
+ | 1. Assign the columns as outputs (3-bit output). | ||
+ | |||
+ | 2. Assign the rows as inputs (4-bit input), with internal pull-up resistors enabled. | ||
+ | |||
+ | 3. For each column, drive the corresponding column output low (e.g. for column bits 2,1,0, the will be set to | ||
+ | |||
+ | * 1,1,0 : Select column #0 | ||
+ | * 1,0,1 : Select column #1 | ||
+ | * 0,1,1 : Select column #2 | ||
+ | |||
+ | 4. When a given column is selected, input the row data. | ||
+ | |||
+ | 5. Row bits will be 0 where the row and column intersect AND the key is pressed, or 1 where the key is released. | ||
+ | |||
+ | Note that the keypad operation is fully mechanical like any switch, there is no ground or power connections necessary. | ||
+ | |||
+ | ===LCD implementation=== | ||
+ | |||
+ | The LCD display is based around the Hitachi HD44780 LCD controller. This is a legacy part that has a 6809-alike bus as follows: | ||
+ | |||
+ | * RS - Register select (0= data, 1= control) | ||
+ | * EN - Enable signal, used as an address strobe to indicate the data bus RS, and R/W# are at valid logic levels. | ||
+ | * R/W# - Read/write strobe (0= write, 1= read) | ||
+ | * DB7-0 - Data bus (8-bits) | ||
+ | |||
+ | To support devices with less GPIO such as 4-bit microcontrollers, the HD44780 has a 4-bit mode where only DB7-4 are used, and byte-sized data is transferred by writing four-bit nibbles twice in sequence. However the documentation is sparse on the exact timing in this mode, and it was unreliable when the control signals changed slowly. To compensate, one of the shift registers was allocated to provide all 8 bits of data, and the remaining GPIO was used for the control signals. As the HD44780 is a slow device and the LPC1758 can change GPIO pins very quickly, it was critical to observe the timing characteristics in the datasheet. | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0005.png|center]] | ||
+ | |||
+ | The HD44780 needs a certain amount of time to carry out actions, and will output a busy status flag on DB7 during a read cycle. As a output-only shift register was being used to drive the data bus it was not possible to read data. A hardware solution was to place a 10K resistor in series with the shift register, such that Q7 of the shift register could pull DB7 low or high. In this way the HD44780 could still drive DB7 which was connected to a GPIO, allowing the busy status to be read regardless of how the shift register was driving the pin. | ||
+ | |||
+ | This was important as many system-level uses of HD44780 opt to disable the read function entirely (grounding R/W#) and use fixed delays instead of polling the busy flag. | ||
+ | However the time it takes to complete operations is somewhat variable, and it varies further between different manufacturers. | ||
+ | To be safe lengthy delays must be used which waste valuable time. | ||
+ | It seemed much more worthwhile to have a hardware solution to enable polling, in order to keep the software responsive. | ||
+ | |||
+ | ===LCD backlight control and contrast=== | ||
+ | |||
+ | The LCD module has a single RGB LED (comprised of a red, green, and blue LED in the same physical package) as the backlight. | ||
+ | There is a common anode that requires +5V and individual cathodes for the red, green, and blue LEDs. | ||
+ | Current limiting resistors are provided on the PCB and do not need to be added externally. Each LED draws about 20mA when turned on for 60mA total. | ||
+ | |||
+ | While the LPC1758 GPIO pins can sink up to 50mA, the ULN2803 was used to drive the cathodes instead. By doing this the available current from the MIC5219 regulator on the SJ one board isn't impacted, | ||
+ | as it is limited to 500mA, 60mA would have been a considerable portion of the current budget. | ||
+ | |||
+ | As there are three LEDs, there are 2^3 or 8 possible colors that can be generated, though the LCD display has a tint towards blue so you don't get "true" colors. | ||
+ | The available color range is black (off), red, green, blue, cyan, magenta, yellow, and white. | ||
+ | |||
+ | See the "Future work" section for information about how to get more colors out of the backlight. | ||
+ | |||
+ | The LCD display has a very finite viewing angle at which the display appears undistorted. To accommodate for other viewing angles the contrast may be adjusted using a 10K potentiometer. | ||
+ | In practice the user will be holding the control box and can tilt it for the clearest possible display, so a fixed contrast level was chosen that matched that use case. | ||
+ | |||
+ | ===Solenoid implementation=== | ||
+ | |||
+ | The solenoid used (Adafruit #1512) is designed to function similarly to the latch of a door. A spring pushes the metal slug outwards such that the door is locked when the system is turned off. | ||
+ | The solenoid contains a coil that generates a magnetic field when energized, which draws the slug inwards (against the force of the spring) allowing the door to be opened. Thus the solenoid locks the door when off, and unlocks it when on. | ||
+ | |||
+ | The solenoid requires +12V to operate and draws 680mA normally. When the solenoid is turned off after a period of activation, the magnetic field collapses, at which point the force of the internal spring overcomes the decreasing force of the magnetic field holding the slug in place, and the spring can then eject the slug to the outward position. | ||
+ | |||
+ | When the magnetic field collapses current is induced in the coil, causing a large voltage spike that can be damaging to any digital circuitry connected to it. To prevent this, a diode is placed in parallel with the solenoid coil with the cathode to the positive voltage supply (+12V). This provides a safe discharge path so that the voltage spike does not go into the digital circuitry. | ||
+ | |||
+ | Note that the ULN2803 used has a built-in diode for this purpose, but during testing it was observed that the LCD display would flicker when the solenoid was turned off. Adding a large 1N4001 diode | ||
+ | across the solenoid eliminated this problem completely. | ||
+ | |||
+ | To control the the solenoid with a GPIO port, a ULN2803 is used, which is an eight channel Darlington transistor array. A Darlington transistor is a pair of transistors in series that can sink a large amount of current, and can interface to higher voltage signals such as 12V. | ||
+ | The ULN2803 has built-in resistors to provide a TTL/CMOS compatible interface. | ||
+ | The output is inverted with respect to the input, such that a high-level input sinks current on the output, and a low-level input makes the output go high impedance. | ||
+ | Accordingly, the output must have a pull-up resistor to prevent any oscillation or noise when in the high impedance state. | ||
+ | |||
+ | Each channel of the ULN2803 can handle 500mA. As there were multiple free channels available, three were used in parallel to provide up to 1.5A sinking ability, which is a little over twice of what the solenoid requires. | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0003.png|center]] | ||
+ | |||
+ | Adafruit sells the solenoid with the slug oriented in 90 degrees from the desired position. To correct this, the housing can be lifted up after removing two screws. There is a thin D-ring around the base of the slug which is quite hard to remove. Using a flat screwdriver head to push on one side of the ring while holding the other side in place with needle-nose pliers worked eventually. There may be some kind of dedicated tool for D-ring removal which would simplify this process. | ||
+ | |||
+ | <gallery widths="200px" perrow="5" align="center"> | ||
+ | File:S15_244_G29_IMG_0010.png|Security box RCA jack | ||
+ | File:S15_244_G29_IMG_0011.png|Control box RCA jack | ||
+ | File:S15_244_G29_IMG_0012.png|Interior view of RCA jack and wiring | ||
+ | File:S15_244_G29_IMG_0013.png|Solenoid and wiring | ||
+ | File:S15_244_G29_IMG_0014.png|Solenoid | ||
+ | </gallery> | ||
+ | |||
+ | The solenoid in the security box is connected to a RCA jack, which connects to a matching jack on the control box. This was chosen as it was a inexpensive two-terminal cable that is easily available. | ||
+ | Ideally the wiring should be held in place by small fencing staples, but as a temporary solution electrical tape was used. | ||
+ | |||
+ | ===Piezo buzzer=== | ||
+ | |||
+ | The piezo buzzer is a thin plate that deflects when voltage is applied to it. This deflection is enough to create audible sound, | ||
+ | and the plate is mounted in a plastic cavity for amplification. The buzzer can be controlled by DC voltage such that a square-wave | ||
+ | of a given frequency, such as 440 Hz, creates a 440 Hz tone. At very low frequencies the buzzer can be used to make simple clicking | ||
+ | sounds, such as to augment the keypad entry with clicks as each key is pressed. This kind of feedback makes it easier for the | ||
+ | user to tell if a key press was complete or not. | ||
+ | |||
+ | A 0.1uF ceramic disc capacitor is placed across the buzzer terminals to filter out high frequency noise, and a 270-ohm resistor is | ||
+ | placed in series between the negative terminal to the Darlington transistor driving it to limit current -- not because the buzzer | ||
+ | needs it, but because the piezoelectric effect means deflection on the plate can produce voltage going back into the transistor array, | ||
+ | potentially causing damage. | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0002.png|center]] | ||
+ | |||
+ | When GPIO port P2.4 drives low, the corresponding ULN2803 output becomes high impedance, leaving the negative terminal of the buzzer floating. This makes it susceptible to noise which can be heard as audible clicks. A 10K pull-up resistor to +12V biases the negative terminal high, such that the voltage difference between both terminals is zero, thus the buzzer is in an idle state and will not oscillate. | ||
+ | |||
+ | ===Security box case=== | ||
+ | |||
+ | The security box was made from a SERPAC plastic enclosure from Fry's Electronics. Holes were drilled out and slots were removed with a Dremel tool. | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0008.png|center|500px]] | ||
+ | |||
+ | The printed circuit board was used to construct the circuit using wire-wrapping. Due to the low clearance underneath the PCB the sockets had to be cut down to fit, complicating wiring. | ||
+ | Mounting holes were made to connect the PCB to the case bottom using plastic standoffs and screws, and a similar system was employed to mount the SJ One board to the PCB. | ||
+ | |||
+ | Ideally a number of identical length jumper wires would have been used so that wires could have had zip-ties applied and been mounted out of the way. In practice the jumper wires | ||
+ | that were available were from miscellaneous sources and were hard to organize, while still allowing access to the circuitry inside the box. | ||
+ | |||
+ | == Software Design == | ||
+ | |||
+ | The project consists of several C/C++ source files, which can be divided between hardware related components and application software related components: | ||
+ | |||
+ | ====Hardware components==== | ||
+ | * mykeypad - Keypad scanning. | ||
+ | * mygpio - GPIO configuration. | ||
+ | * mylcd - LCD module interface. | ||
+ | * myspi - SSP driver for SPI communication. | ||
+ | * mysreg - Shift register abstraction. | ||
+ | |||
+ | ====Software components==== | ||
+ | * main - User interface (main application). | ||
+ | * myedlin - Line editor for user data input. | ||
+ | * mypin - PIN resource loading and validation. | ||
+ | * debug - Debugging and error handling functions. | ||
+ | |||
+ | ====Main application operations and associated tasks==== | ||
+ | 8 | ||
+ | The main application performs the following operations: | ||
+ | |||
+ | * Prompt user to input a personal identification number (PIN). | ||
+ | * Accept input used to enter or edit the PIN. | ||
+ | * Validate the PIN against a list of known-valid PINs | ||
+ | * Warn the user if the PIN was incorrect, or | ||
+ | * Inform the user the PIN was accepted and unlock the door via the solenoid for a brief time period. | ||
+ | * Play a tone to give the user auditory feedback when a button is pressed. | ||
+ | |||
+ | These are implemented with several FreeRTOS tasks: | ||
+ | |||
+ | 1. Keypad scanning task. Low priority. | ||
+ | |||
+ | This task scans the keypad matrix, storing the resulting data in a "key event" structure. When a key is pressed, the structure is loaded into the key queue. Application software can then unload key events and process them as necessary. | ||
+ | |||
+ | 2. Finite state machine task. Low priority. | ||
+ | |||
+ | This task advances the user interface through different screens based on criteria such as keypresses made, if a PIN was valid, if the door unlock countdown timer has expired, etc. Using a finite state machine makes the 'initialization' (state transition) and 'running' (work done within a state) easy to manage and trivial to expand to add more functionality. | ||
+ | |||
+ | 3. Buzzer update task. High priority. | ||
+ | |||
+ | This task generates a square wave to activate the piezo buzzer. To provide uninterrupted sound it is a high priority task. | ||
+ | |||
+ | == Implementation == | ||
+ | |||
+ | === Bus locking === | ||
+ | |||
+ | The SSP0 bus is accessed by both the keypad scanning task and the FSM task. To manage this shared resource a mutex is used such that each task cannot be pre-empted by the other during SSP0 bus access. | ||
+ | The mutex is created during startup: | ||
+ | |||
+ | /* Create SSP0 bus mutex */ | ||
+ | ssp0_bus_mutex = xSemaphoreCreateMutex(); | ||
+ | if(ssp0_bus_mutex == 0) | ||
+ | fatal_err("Can't allocate SSP0 bus lock.\n"); | ||
+ | |||
+ | Two convenience functions were written to simplify taking and giving the mutex within tasks: | ||
+ | |||
+ | /* Take mutex */ | ||
+ | bool acquire_shift_reg(const char *err_msg) | ||
+ | { | ||
+ | if(ssp0_bus_mutex && xSemaphoreTake(ssp0_bus_mutex, portMAX_DELAY) == pdTRUE) | ||
+ | return true; | ||
+ | fatal_err(err_msg); | ||
+ | return false; | ||
+ | } | ||
+ | |||
+ | /* Give mutex */ | ||
+ | void release_shift_reg(void) | ||
+ | { | ||
+ | xSemaphoreGive(ssp0_bus_mutex); | ||
+ | } | ||
+ | |||
+ | Here 'fatal_err' is a function that halts the system and prints information to the console about an unrecoverable error. | ||
+ | |||
+ | |||
+ | |||
+ | === Shift register abstraction === | ||
+ | |||
+ | The shift registers are abstracted as a chain of shift registers placed in series. The "sreg" class is used to get the state of a shift register (the last value written to it), and set the new state as well. | ||
+ | The shift register update function actually updates the shift register chain with the new values. | ||
+ | |||
+ | This allows the shift registers to be expanded to any length up to 256 devices. | ||
+ | |||
+ | === Queues and events === | ||
+ | |||
+ | When the user enters data on the keypad this creates a key event. The key event stores the current keypad state and indications of what has changed, such as if a key was pressed or released. | ||
+ | Key events are placed into the key queue for other tasks to retrieve user input. | ||
+ | |||
+ | Likewise an add_tone() function adds tone events to the tone queue. This is used to play back beeps that are generated when the user presses buttons on the keypad. | ||
+ | |||
+ | For the most part the tone and key events are tightly coupled, but the system was designed so that other sources could generate tones, such as RFID card swipe results which are independent of any key input. | ||
+ | |||
+ | === User interface === | ||
+ | |||
+ | The user interface consists of a finite state machine and a line editor module. | ||
+ | |||
+ | The finite state machine controls the different display screens and manages the PIN entry and door unlocking mechanism. | ||
+ | It can be visualized as a flowchart as follows: | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0004.png|center]] | ||
+ | |||
+ | The displays for each screen are shown below: | ||
+ | |||
+ | [[File:S15_244_G29_IMG_0006.png|center|500px]] | ||
+ | |||
+ | States from top to bottom: UI_PIN_ENTRY, UI_PIN_OK, UI_PIN_NG. There is no display associated with UI_NOP. | ||
+ | |||
+ | ==== Data entry ==== | ||
+ | |||
+ | A line editor module was developed to allow complex data entry. It support commands such as delete (backspace), cancel, and submit. Certain keys can be restricted from input if desired. Various aspects of the editor are indicated in a status code, such that unique actions (playing tones, printing warning messages, etc.) can be carried out if the user makes actions such as: | ||
+ | |||
+ | * Overwriting existing text (overflow) | ||
+ | * Deleting non-existent text (underflow) | ||
+ | * Entering unacceptable keys | ||
+ | * Trying to submit incomplete data | ||
+ | |||
+ | The line editor can be expanded to work with larger keypads or even a full keyboard. | ||
+ | |||
+ | === HD47780 display controller programming === | ||
+ | |||
+ | The LCD is programmed by writing an 8-bit value to the data register or control register. Writes to the data register can go to two types of internal RAM: | ||
+ | |||
+ | * CGRAM - Character Generator RAM, 64 bytes used to define sixteen 5x8 characters. Character data is right-justified in each byte, such that bits 7,6,5 are unused. | ||
+ | * DDRAM - Display Data RAM, 128 bytes used to define the characters shown on the display. The first 64 bytes are for row #0, the second 64 bytes are for row #2. | ||
+ | |||
+ | The DDRAM allows for a display that is wider than the physical display. To show data outside of the visible range, there is a 'shift' command that adjusts the starting address used for display. | ||
+ | Each time the shift command is sent the start address can be incremented or decremented. This allows you to pan across text data without having to physically rewrite an entire line DDRAM when scrolling across text. | ||
+ | |||
+ | To pick which type of RAM you are accessing, you need to send a command first. Commands $01, $02, $80 select DDRAM. Command $40 selects CGRAM. | ||
+ | |||
+ | Control data is as follows: | ||
+ | |||
+ | MSB LSB DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 | ||
+ | 00000001 : --- --- --- --- --- --- --- '1' : Clear DDRAM, set address counter to DDRAM[0] ("clrscr") | ||
+ | 00000010 : --- --- --- --- --- --- '1' --- : Set address counter to DDRAM[0] ("home") | ||
+ | 00000100 : --- --- --- --- --- '1' I/D -S- : Set cursor inc/dec and shift enable | ||
+ | 00001000 : --- --- --- --- '1' -D- -C- -B- : Set display on/off, cursor on/off, blink on/off | ||
+ | 00010000 : --- --- --- '1' S/C R/L --- --- : Move cursor without data port write | ||
+ | 00100000 : --- --- '1' -DL -N- -F- --- --- : Set interface size, display lines (1 or 2), font (5x8 or 5x10) | ||
+ | 01000000 : --- '1' AC5 AC4 AC3 AC2 AC1 AC0 : Set CGRAM address 00-3F + Set CGRAM access mode | ||
+ | 10000000 : '1' AD6 AD5 AD4 AD3 AD2 AD1 AD0 : Set DDRAM address 00-7F + Set DDRAM access mode | ||
+ | |||
+ | The address counter can be thought of the location where data is written to, and in the context of DDRAM it is also used as the cursor position. | ||
+ | Reading the control register returns a busy status flag in DB7 and the current address counter value in DB6-DB0, such that you can determine the current DDRAM or CGRAM offset you are accessing. | ||
+ | |||
+ | To prepare the LCD for use, the 8-bit interface initialization method proposed by Donald Weiman was used: | ||
+ | |||
+ | * http://web.alfredstate.edu/weimandn/lcd/lcd_initialization/lcd_initialization_index.html | ||
+ | |||
+ | The LCD display class provides a set of familiar functions for text output: | ||
+ | |||
+ | * clrscr() - Clear the display. | ||
+ | * gotoxy() - Set the cursor position. | ||
+ | * putch() and puts() - Print characters and formatted strings. | ||
+ | |||
+ | There are additional functions to control the blinking cursor as well. | ||
== Testing & Technical Challenges == | == Testing & Technical Challenges == | ||
− | |||
− | |||
− | + | The biggest challenge was allocating time adequately to get all the work done. This project was unexpectedly very labor intensive. | |
+ | |||
+ | ==== Issue #1 : Circuit wiring ==== | ||
+ | |||
+ | Wire-wrap construction was used to build the circuit. In retrospect it would have been wiser to have a PCB made after developing a prototype on a breadboard. | ||
+ | However this adds a significant lead time to get the PCBs, so it needed to be done at an earlier phase of the project. | ||
+ | |||
+ | ==== Issue #2 : Enclosure construction ==== | ||
+ | |||
+ | It can be difficult to visualize the size and height of components inside an enclosure. A good rule of thumb would be to anticipate that you'll need 30% more space for wiring | ||
+ | and protruding connectors than you thought you needed. | ||
+ | |||
+ | ==== Issue #3 : Software development goals ==== | ||
+ | |||
+ | It's easy to get into a trap of over-engineering software to be too modular, too abstract, and too general purpose while losing sight of the actual goals of the project. | ||
+ | A sensible cut-off point should be chosen in terms of features and flexibility. | ||
+ | |||
+ | ==== Issue #4 : Noise in circuits ==== | ||
− | === | + | Due to the wire-wrap construction there were a few times when sporadic behavior was observed, which was due to a pull-up resistor becoming loose and allowing a signal |
− | + | to float and eventually oscillate. A good first step in troubleshooting a circuit is to use a continuity test feature and verify all connections have been established. | |
+ | |||
+ | ==== Issue #5 : Software debugging ==== | ||
+ | |||
+ | It was helpful to have an optional debug mode defined (allowing for '#if DEBUG / #endif blocks' to be used) that printed status information to the console to be viewed in Hercules. | ||
+ | Aspects of normal operation were displayed instead of just error messages. This helped find bugs and unconsidered edge cases while reviewing the logs that were not immediately | ||
+ | obvious when using the system. | ||
== Conclusion == | == Conclusion == | ||
− | + | ||
+ | There was a lot to learn from this project: | ||
+ | |||
+ | 1. Physical construction takes a _lot_ of time and work. Even working with relatively soft plastic as opposed to metal or wood is still a big undertaking. At least half the team should be dedicated to this task so the electronics and software people can focus on their area. | ||
+ | |||
+ | 2. Don't underestimate how much room you need inside an enclosure. After moving from a smaller one to a bigger one, adding all the wiring took up the additional space I wanted to use for an internal power supply. | ||
+ | |||
+ | 3. It's helpful to build up complex libraries from smaller steps. The LCD interface was tested by being directly interfaced with the SJ One board, and was developed until it was fully functional. Only then was it converted to use the shift register to provide data. This simplified troubleshooting as a known-good codebase had been created before the conversion started, so it was easier to isolate faults. | ||
+ | |||
+ | 4. Work needs to be evenly divided amongst group members. Otherwise everything takes much too long to complete. | ||
+ | |||
+ | 5. I gained a lot of experience with the HD44780 which I've always read about but never actually used. Likewise it was the first time I used shift registers on the SPI bus and that was a useful experience too. | ||
+ | |||
+ | 6. More research should have been made to see how difficult the PN538 was to work with. Admittedly it was purchased because it is cheap and easily available, but that's a less useful metric than | ||
+ | the success rate of projects built around the PN538. | ||
+ | |||
+ | === Future work === | ||
+ | |||
+ | There are a number of ways to improve the project. | ||
+ | |||
+ | * Use PWM or a timer interrupt to control buzzer updates. This would allow for the frequency to be varied, allowing for frequency envelopes and sweeps, and more complex sound effects generated by linking single tone structures together. | ||
+ | |||
+ | * Use a 16x4 LCD display to display larger messages. The current 16x2 LCD is cramped, but functional. | ||
+ | |||
+ | * Use PWM to control the RGB LCD backlight cathodes. The current implementation wires these to the shift register, but if using three of the PWM channels the backlight colors could be expanded from eight to several million. | ||
+ | |||
+ | * Use a SPI GPIO expander. In particular the Microchip MCP23S17 has two 8-bit I/O ports that could have replaced the two 745HC595 shift registers. Note that the MCP23* line of GPIO expanders have rather unusual characteristics for their inputs regarding the Vil/Vih levels, so care must be taken when using their pins configured as inputs. However as outputs they function as expected. | ||
+ | |||
+ | * Use a different RFID chip other than the PN538. | ||
+ | |||
+ | * Support manual registration and management of PINs, either by using the internal operator button (more secure) or a dedicated service mode PIN (less secure). | ||
+ | |||
+ | In terms of physical security the solenoid can be triggered simply by applying +12V across the terminals with the control box disconnected. A more secure method would be to place a second SJ-One board inside the security box along with a dedicated power supply, and have the control box communicate to it over a serial link. The SJ-One in the security box can authenticate door open requests from the control box. The serial link could be protected by having data encrypted using a LFSR with a known seed shared between both systems which would make eavesdropping challenging, but would not stop a replay attack. | ||
+ | |||
+ | A further exercise in security would be to use a wireless system (XBee) for the control box and security box to communicate. | ||
+ | |||
+ | === Goals not completed === | ||
+ | |||
+ | The RFID functionality was not finished. A functioning driver for the PN538 chip could not be developed. | ||
+ | |||
+ | === Other thoughts === | ||
+ | |||
+ | It would be nice if a future revision of the SJ One board had pin headers for: | ||
+ | |||
+ | * LPC1758 RESET and RSTOUT | ||
+ | * A FT245R CBUS pin configured as VCCSW/PWREN# | ||
+ | * USB +5V | ||
=== Project Video === | === Project Video === | ||
Line 103: | Line 660: | ||
== References == | == References == | ||
=== Acknowledgement === | === Acknowledgement === | ||
− | + | ||
+ | I would like to express my thanks to Wilson Luc for extensive help constructing an early version of the control box and giving advice about power tool use and techniques. | ||
=== References Used === | === References Used === | ||
− | |||
− | + | * HD47780 datasheet | |
− | + | https://www.sparkfun.com/datasheets/LCD/HD44780.pdf | |
+ | |||
+ | * OKI-78SR datasheet | ||
+ | http://power.murata.com/data/power/oki-78sr.pdf | ||
+ | |||
+ | * 74HCT595 datasheet | ||
+ | http://www.nxp.com/documents/data_sheet/74HC_HCT595.pdf | ||
+ | |||
+ | * "Piezoelectric Sound Components Application Manual" (Murata) | ||
+ | http://www.murata.com/~/media/webrenewal/support/library/catalog/products/sound/p15e.ashx | ||
+ | |||
+ | * ULN2803 datashset | ||
+ | https://www.adafruit.com/datasheets/ULN2803A.pdf | ||
+ | |||
+ | * Adafruit PN532 tutorial | ||
+ | https://learn.adafruit.com/adafruit-pn532-rfid-nfc | ||
+ | |||
+ | * NC-C1627A datasheet (similar to, but the same as, the Adafruit GWT-C1627A-RGB LCD module) | ||
+ | http://www.futurel.bg/datasheets/5/C1627A.pdf | ||
+ | |||
+ | * GWT-C1627A-RGB mechanical data and pin assignments | ||
+ | https://www.adafruit.com/datasheets/rgblcddimensions.gif |
Latest revision as of 14:51, 3 June 2015
Contents
- 1 RFID Security Box
- 2 Abstract
- 3 Objectives & Introduction
- 4 Schedule
- 5 Parts List & Cost
- 6 Design & Implementation
- 6.1 Hardware Design
- 6.2 Hardware Interface
- 6.3 GPIO assignments
- 6.4 Shift register assignments
- 6.5 Power supply
- 6.6 Shift register bus overview
- 6.7 Keypad implementation
- 6.8 LCD implementation
- 6.9 LCD backlight control and contrast
- 6.10 Solenoid implementation
- 6.11 Piezo buzzer
- 6.12 Security box case
- 7 Software Design
- 8 Implementation
- 9 Testing & Technical Challenges
- 10 Conclusion
- 11 References
RFID Security Box
Abstract
The RFID Security Box is a electronically locked container with an external control box that enables unlocking by using RFID tags, NFC-compatible devices, or a manually entered PIN.
Objectives & Introduction
This is a security box for storing valuables. It can be unlocked by entering a PIN through a numeric keypad, or by communicating with a NFC-compatible device, or RFID tags, tokens, and cards.
Sensors and other components used:
- NFC/RFID Controller breakout board
- Lock-style Solenoid
- 3x4 Phone-style Matrix Keypad
- RGB LCD Screen
Team Members & Responsibilities
- Rajwinder Ruprai
- NFC/RFID driver
- Software testing
- Charles MacDonald
- Hardware and software design
- Physical construction
- Hardware testing
Schedule
Week# | Date | Task | Actual |
---|---|---|---|
1 | 04/14 | Order parts needed for required functionality, work on box design (component placement, mounting requirements, etc.) |
Completed |
Week# | Date | Task | Actual |
---|---|---|---|
2 | 04/21 | Develop keypad and LCD display drivers. | Completed. |
Week# | Date | Task | Actual |
---|---|---|---|
3 | 04/28 | Develop solenoid driver and start work on RFID/NFC driver. | Completed. |
Week# | Date | Task | Actual |
---|---|---|---|
4 | 05/5 | Finish RFID/NFC driver, finish PSU wiring. | Completed. |
Week# | Date | Task | Actual |
---|---|---|---|
5 | 05/12 | Component integration and testing. | Completed. |
Week# | Date | Task | Actual |
---|---|---|---|
6 | 05/19 | Tweaking and polishing implementation. | Completed. |
Week# | Date | Task | Actual |
---|---|---|---|
7 | 05/25 | Project presentation. | Rescheduled. |
Parts List & Cost
Quantity | Description | Cost |
---|---|---|
1 | Lock-style Solenoid - 12VDC | $14.95 |
1 | I2C / SPI character LCD backpack (not used) | $10.00 |
1 | MiFare Classic (13.56 MHz) tag assortment (1KB) | $10.00 |
1 | PN532 NFC/RFID controller breakout board (v1.6) | $39.95 |
1 | 3x4 Phone-style Matrix Keypad | $7.50 |
1 | RGB backlight negative LCD 16x2 + extras (RGB on black) | $13.95 |
1 | Extra-long break-away 0.1" 16-pin strip male header (5 pieces) | $3.00 |
1 | Murata OKI-78SR Fixed Output 1.5A DC/DC Converter (5.0V) | $4.30 |
1 | ST Microelectronics 3.3V Linear Regulator (not used) | $0.64 |
1 | 1N4001 Diode | $0.13 |
1 | Meanwell RS-15-12 +12V, 1.3A PSU (not used) | $9.95 |
1 | IEC 320-C14 AC power receptacle (not used) | $0.79 |
1 | Ikea VALJE Wall Cabinet with Door | $35.00 |
1 | Mounting hardware assortment (nuts, screws, standoffs, spacers) | $10.00 |
As functionality was changed, some components were not used. They may be integrated into a future revision of the device.
Design & Implementation
Hardware Design
Rough draft of system architecture / schematic diagram (external link):
This is to mainly illustrate an early phase of development. More detailed diagrams are provided later.
Hardware Interface
The various buses are allocated as follows
- SSP0 Shift register chain
- SSP1 AT45, SD card, PN532 breakout board
- GPIO Various control outputs and inputs
- UART2 (free)
- UART3 (free)
GPIO assignments
- P2.0 : Keypad matrix row data, bit 0
- P2.1 : Keypad matrix row data, bit 1
- P2.2 : Keypad matrix row data, bit 2
- P2.3 : Keypad matrix row data, bit 3
- P2.4 : Piezo buzzer control
- P2.5 : HD47780 DB7 (input to read busy status)
- P2.6 : Storage register clock for shift register #1
- P2.7 : Storage register clock for shift register #2
- P1.19 : HD47780 RS
- P1.20 : HD47780 R/W#
- P1.22 : HD47780 EN
- P1.23 : Operator button input (pushbutton)
External reset button input
Shift register assignments
- S1.0 : LCD panel tri-color backlight LED blue cathode
- S1.1 : LCD panel tri-color backlight LED green cathode
- S1.2 : LCD panel tri-color backlight LED red cathode
- S1.3 : To solenoid coil connector
- S1.4 : (Unused)
- S1.5 : Keypad matrix column select, bit 0
- S1.6 : Keypad matrix column select, bit 1
- S1.7 : Keypad matrix column select, bit 2
- S2.0 : LCD interface, data bit 0
- S2.1 : LCD interface, data bit 1
- S2.2 : LCD interface, data bit 2
- S2.3 : LCD interface, data bit 3
- S2.4 : LCD interface, data bit 4
- S2.5 : LCD interface, data bit 5
- S2.6 : LCD interface, data bit 6
- S2.7 : LCD interface, data bit 7
These connections are described in more detail later.
Power supply
Originally a compact MeanWell AC to DC +12V power supply was going to be fitted inside the control box. Due to space limitations we had to remove it and use an external 12V DC power supply. Only the solenoid requires +12V, the rest of the system uses +5V parts and the SJOne board has its own LDO regulator to provide +3.3V. A power distribution diagram is shown below:
Due to the high voltage drop between +12V input and +5V output, a 7805 linear regulator is inappropriate to use as it will dissipate the 7-volt difference as heat.
Instead, a Murata OKI-78SR switching regulator was used. This device has the same pin configuration and roughly the same size as the 7805, but has very little heat dissipation even for
high input differential like +36V to +5V. No heatsink or passive/active cooling was needed.
Shift register bus overview
To add additional outputs to the SJ-One board, several 74HCT595 shift registers have been connected to the SPI (SSP0) bus. Despite not being true SPI devices the shift registers work well for adding more I/O.
The 74HCT595 has LVCMOS compatible inputs allowing it to interface with the 3.3V I/O of the LPC1758. It consists of an 8-bit shift register which is clocked by SCLK and fed data by MOSI. The shift register output signal (Q7') can be fed to the data input of additional shift registers. The shift register output feeds the input of an 8-bit output latch, which has a dedicated clock input. In this way you can shift any kind of data through the shift register chain without disturbing the actual output pins, and then only load data from the shift register to the output latch when necessary.
Here P2.6 and P2.7 are the output register clock signals, so by toggling either GPIO pin the contents of the shift register becomes the new output.
The output of the final shift register in the chain can be fed back into MISO for diagnostic purposes; while the shift registers are output-only devices, connecting the output to MISO allows you to verify that the data transmitted through the chain was correctly received. This can help identify wiring errors or other problems early on and is a good diagnostic tool.
An overview of the shift register bus is shown in the following diagram:
While only two shift registers are used, more could be added. There is no practical limit other than the worst-case time it takes to change the last shift register is the time it takes to shift data through all shift registers in the chain. For high speed applications the chain should be kept short, but even then the 74HCT595 is rated to run up to 25 MHz, so with good wiring even a long chain can be updated rather quickly.
Other diagrams shown later will detail the low-level implementation between the shift registers and the peripherals they drive.
Keypad implementation
The keypad is arranged as a 4x3 matrix. Where each row and column intersect is a switch, and a physical connection between the row and column is made when the switch is depressed. Otherwise there is no connection when a switch is released, and the relationship between the row and the column at that point appears as an open circuit.
As the keypad has no datasheet the first task was to disassemble the keypad and use the continuity test function of a multimeter to determine the pin assignments:
- Pin 1 - Not used (goes to a test point on the PCB)
- Pin 2 - Row (keys *,0,#)
- Pin 3 - Row (keys 7,8,9)
- Pin 4 - Row (keys 4,5,6)
- Pin 5 - Row (keys 1,2,3)
- Pin 6 - Column (keys 3,6,9,#)
- Pin 7 - Column (keys 2,5,8,0)
- Pin 8 - Column (keys 1,4,7,*)
The notation of row vs. column is completely arbitrary, and were chosen only to have a consistent naming scheme.
Here is a diagram of the matrix inside the keypad:
As the rows and columns are shorted when a key is pressed, it is important to never drive the rows and columns to complementary output levels. The rows should be inputs with internal pull-ups enabled, and the columns are outputs that are driven low or high (or tri-stated).
The keypad can be scanned like so:
1. Assign the columns as outputs (3-bit output).
2. Assign the rows as inputs (4-bit input), with internal pull-up resistors enabled.
3. For each column, drive the corresponding column output low (e.g. for column bits 2,1,0, the will be set to
- 1,1,0 : Select column #0
- 1,0,1 : Select column #1
- 0,1,1 : Select column #2
4. When a given column is selected, input the row data.
5. Row bits will be 0 where the row and column intersect AND the key is pressed, or 1 where the key is released.
Note that the keypad operation is fully mechanical like any switch, there is no ground or power connections necessary.
LCD implementation
The LCD display is based around the Hitachi HD44780 LCD controller. This is a legacy part that has a 6809-alike bus as follows:
- RS - Register select (0= data, 1= control)
- EN - Enable signal, used as an address strobe to indicate the data bus RS, and R/W# are at valid logic levels.
- R/W# - Read/write strobe (0= write, 1= read)
- DB7-0 - Data bus (8-bits)
To support devices with less GPIO such as 4-bit microcontrollers, the HD44780 has a 4-bit mode where only DB7-4 are used, and byte-sized data is transferred by writing four-bit nibbles twice in sequence. However the documentation is sparse on the exact timing in this mode, and it was unreliable when the control signals changed slowly. To compensate, one of the shift registers was allocated to provide all 8 bits of data, and the remaining GPIO was used for the control signals. As the HD44780 is a slow device and the LPC1758 can change GPIO pins very quickly, it was critical to observe the timing characteristics in the datasheet.
The HD44780 needs a certain amount of time to carry out actions, and will output a busy status flag on DB7 during a read cycle. As a output-only shift register was being used to drive the data bus it was not possible to read data. A hardware solution was to place a 10K resistor in series with the shift register, such that Q7 of the shift register could pull DB7 low or high. In this way the HD44780 could still drive DB7 which was connected to a GPIO, allowing the busy status to be read regardless of how the shift register was driving the pin.
This was important as many system-level uses of HD44780 opt to disable the read function entirely (grounding R/W#) and use fixed delays instead of polling the busy flag. However the time it takes to complete operations is somewhat variable, and it varies further between different manufacturers. To be safe lengthy delays must be used which waste valuable time. It seemed much more worthwhile to have a hardware solution to enable polling, in order to keep the software responsive.
LCD backlight control and contrast
The LCD module has a single RGB LED (comprised of a red, green, and blue LED in the same physical package) as the backlight. There is a common anode that requires +5V and individual cathodes for the red, green, and blue LEDs. Current limiting resistors are provided on the PCB and do not need to be added externally. Each LED draws about 20mA when turned on for 60mA total.
While the LPC1758 GPIO pins can sink up to 50mA, the ULN2803 was used to drive the cathodes instead. By doing this the available current from the MIC5219 regulator on the SJ one board isn't impacted, as it is limited to 500mA, 60mA would have been a considerable portion of the current budget.
As there are three LEDs, there are 2^3 or 8 possible colors that can be generated, though the LCD display has a tint towards blue so you don't get "true" colors. The available color range is black (off), red, green, blue, cyan, magenta, yellow, and white.
See the "Future work" section for information about how to get more colors out of the backlight.
The LCD display has a very finite viewing angle at which the display appears undistorted. To accommodate for other viewing angles the contrast may be adjusted using a 10K potentiometer. In practice the user will be holding the control box and can tilt it for the clearest possible display, so a fixed contrast level was chosen that matched that use case.
Solenoid implementation
The solenoid used (Adafruit #1512) is designed to function similarly to the latch of a door. A spring pushes the metal slug outwards such that the door is locked when the system is turned off. The solenoid contains a coil that generates a magnetic field when energized, which draws the slug inwards (against the force of the spring) allowing the door to be opened. Thus the solenoid locks the door when off, and unlocks it when on.
The solenoid requires +12V to operate and draws 680mA normally. When the solenoid is turned off after a period of activation, the magnetic field collapses, at which point the force of the internal spring overcomes the decreasing force of the magnetic field holding the slug in place, and the spring can then eject the slug to the outward position.
When the magnetic field collapses current is induced in the coil, causing a large voltage spike that can be damaging to any digital circuitry connected to it. To prevent this, a diode is placed in parallel with the solenoid coil with the cathode to the positive voltage supply (+12V). This provides a safe discharge path so that the voltage spike does not go into the digital circuitry.
Note that the ULN2803 used has a built-in diode for this purpose, but during testing it was observed that the LCD display would flicker when the solenoid was turned off. Adding a large 1N4001 diode across the solenoid eliminated this problem completely.
To control the the solenoid with a GPIO port, a ULN2803 is used, which is an eight channel Darlington transistor array. A Darlington transistor is a pair of transistors in series that can sink a large amount of current, and can interface to higher voltage signals such as 12V. The ULN2803 has built-in resistors to provide a TTL/CMOS compatible interface. The output is inverted with respect to the input, such that a high-level input sinks current on the output, and a low-level input makes the output go high impedance. Accordingly, the output must have a pull-up resistor to prevent any oscillation or noise when in the high impedance state.
Each channel of the ULN2803 can handle 500mA. As there were multiple free channels available, three were used in parallel to provide up to 1.5A sinking ability, which is a little over twice of what the solenoid requires.
Adafruit sells the solenoid with the slug oriented in 90 degrees from the desired position. To correct this, the housing can be lifted up after removing two screws. There is a thin D-ring around the base of the slug which is quite hard to remove. Using a flat screwdriver head to push on one side of the ring while holding the other side in place with needle-nose pliers worked eventually. There may be some kind of dedicated tool for D-ring removal which would simplify this process.
The solenoid in the security box is connected to a RCA jack, which connects to a matching jack on the control box. This was chosen as it was a inexpensive two-terminal cable that is easily available. Ideally the wiring should be held in place by small fencing staples, but as a temporary solution electrical tape was used.
Piezo buzzer
The piezo buzzer is a thin plate that deflects when voltage is applied to it. This deflection is enough to create audible sound, and the plate is mounted in a plastic cavity for amplification. The buzzer can be controlled by DC voltage such that a square-wave of a given frequency, such as 440 Hz, creates a 440 Hz tone. At very low frequencies the buzzer can be used to make simple clicking sounds, such as to augment the keypad entry with clicks as each key is pressed. This kind of feedback makes it easier for the user to tell if a key press was complete or not.
A 0.1uF ceramic disc capacitor is placed across the buzzer terminals to filter out high frequency noise, and a 270-ohm resistor is placed in series between the negative terminal to the Darlington transistor driving it to limit current -- not because the buzzer needs it, but because the piezoelectric effect means deflection on the plate can produce voltage going back into the transistor array, potentially causing damage.
When GPIO port P2.4 drives low, the corresponding ULN2803 output becomes high impedance, leaving the negative terminal of the buzzer floating. This makes it susceptible to noise which can be heard as audible clicks. A 10K pull-up resistor to +12V biases the negative terminal high, such that the voltage difference between both terminals is zero, thus the buzzer is in an idle state and will not oscillate.
Security box case
The security box was made from a SERPAC plastic enclosure from Fry's Electronics. Holes were drilled out and slots were removed with a Dremel tool.
The printed circuit board was used to construct the circuit using wire-wrapping. Due to the low clearance underneath the PCB the sockets had to be cut down to fit, complicating wiring. Mounting holes were made to connect the PCB to the case bottom using plastic standoffs and screws, and a similar system was employed to mount the SJ One board to the PCB.
Ideally a number of identical length jumper wires would have been used so that wires could have had zip-ties applied and been mounted out of the way. In practice the jumper wires that were available were from miscellaneous sources and were hard to organize, while still allowing access to the circuitry inside the box.
Software Design
The project consists of several C/C++ source files, which can be divided between hardware related components and application software related components:
Hardware components
- mykeypad - Keypad scanning.
- mygpio - GPIO configuration.
- mylcd - LCD module interface.
- myspi - SSP driver for SPI communication.
- mysreg - Shift register abstraction.
Software components
- main - User interface (main application).
- myedlin - Line editor for user data input.
- mypin - PIN resource loading and validation.
- debug - Debugging and error handling functions.
Main application operations and associated tasks
8 The main application performs the following operations:
- Prompt user to input a personal identification number (PIN).
- Accept input used to enter or edit the PIN.
- Validate the PIN against a list of known-valid PINs
- Warn the user if the PIN was incorrect, or
- Inform the user the PIN was accepted and unlock the door via the solenoid for a brief time period.
- Play a tone to give the user auditory feedback when a button is pressed.
These are implemented with several FreeRTOS tasks:
1. Keypad scanning task. Low priority.
This task scans the keypad matrix, storing the resulting data in a "key event" structure. When a key is pressed, the structure is loaded into the key queue. Application software can then unload key events and process them as necessary.
2. Finite state machine task. Low priority.
This task advances the user interface through different screens based on criteria such as keypresses made, if a PIN was valid, if the door unlock countdown timer has expired, etc. Using a finite state machine makes the 'initialization' (state transition) and 'running' (work done within a state) easy to manage and trivial to expand to add more functionality.
3. Buzzer update task. High priority.
This task generates a square wave to activate the piezo buzzer. To provide uninterrupted sound it is a high priority task.
Implementation
Bus locking
The SSP0 bus is accessed by both the keypad scanning task and the FSM task. To manage this shared resource a mutex is used such that each task cannot be pre-empted by the other during SSP0 bus access. The mutex is created during startup:
/* Create SSP0 bus mutex */ ssp0_bus_mutex = xSemaphoreCreateMutex(); if(ssp0_bus_mutex == 0) fatal_err("Can't allocate SSP0 bus lock.\n");
Two convenience functions were written to simplify taking and giving the mutex within tasks:
/* Take mutex */ bool acquire_shift_reg(const char *err_msg) { if(ssp0_bus_mutex && xSemaphoreTake(ssp0_bus_mutex, portMAX_DELAY) == pdTRUE) return true; fatal_err(err_msg); return false; } /* Give mutex */ void release_shift_reg(void) { xSemaphoreGive(ssp0_bus_mutex); }
Here 'fatal_err' is a function that halts the system and prints information to the console about an unrecoverable error.
Shift register abstraction
The shift registers are abstracted as a chain of shift registers placed in series. The "sreg" class is used to get the state of a shift register (the last value written to it), and set the new state as well. The shift register update function actually updates the shift register chain with the new values.
This allows the shift registers to be expanded to any length up to 256 devices.
Queues and events
When the user enters data on the keypad this creates a key event. The key event stores the current keypad state and indications of what has changed, such as if a key was pressed or released. Key events are placed into the key queue for other tasks to retrieve user input.
Likewise an add_tone() function adds tone events to the tone queue. This is used to play back beeps that are generated when the user presses buttons on the keypad.
For the most part the tone and key events are tightly coupled, but the system was designed so that other sources could generate tones, such as RFID card swipe results which are independent of any key input.
User interface
The user interface consists of a finite state machine and a line editor module.
The finite state machine controls the different display screens and manages the PIN entry and door unlocking mechanism. It can be visualized as a flowchart as follows:
The displays for each screen are shown below:
States from top to bottom: UI_PIN_ENTRY, UI_PIN_OK, UI_PIN_NG. There is no display associated with UI_NOP.
Data entry
A line editor module was developed to allow complex data entry. It support commands such as delete (backspace), cancel, and submit. Certain keys can be restricted from input if desired. Various aspects of the editor are indicated in a status code, such that unique actions (playing tones, printing warning messages, etc.) can be carried out if the user makes actions such as:
- Overwriting existing text (overflow)
- Deleting non-existent text (underflow)
- Entering unacceptable keys
- Trying to submit incomplete data
The line editor can be expanded to work with larger keypads or even a full keyboard.
HD47780 display controller programming
The LCD is programmed by writing an 8-bit value to the data register or control register. Writes to the data register can go to two types of internal RAM:
- CGRAM - Character Generator RAM, 64 bytes used to define sixteen 5x8 characters. Character data is right-justified in each byte, such that bits 7,6,5 are unused.
- DDRAM - Display Data RAM, 128 bytes used to define the characters shown on the display. The first 64 bytes are for row #0, the second 64 bytes are for row #2.
The DDRAM allows for a display that is wider than the physical display. To show data outside of the visible range, there is a 'shift' command that adjusts the starting address used for display. Each time the shift command is sent the start address can be incremented or decremented. This allows you to pan across text data without having to physically rewrite an entire line DDRAM when scrolling across text.
To pick which type of RAM you are accessing, you need to send a command first. Commands $01, $02, $80 select DDRAM. Command $40 selects CGRAM.
Control data is as follows:
MSB LSB DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 00000001 : --- --- --- --- --- --- --- '1' : Clear DDRAM, set address counter to DDRAM[0] ("clrscr") 00000010 : --- --- --- --- --- --- '1' --- : Set address counter to DDRAM[0] ("home") 00000100 : --- --- --- --- --- '1' I/D -S- : Set cursor inc/dec and shift enable 00001000 : --- --- --- --- '1' -D- -C- -B- : Set display on/off, cursor on/off, blink on/off 00010000 : --- --- --- '1' S/C R/L --- --- : Move cursor without data port write 00100000 : --- --- '1' -DL -N- -F- --- --- : Set interface size, display lines (1 or 2), font (5x8 or 5x10) 01000000 : --- '1' AC5 AC4 AC3 AC2 AC1 AC0 : Set CGRAM address 00-3F + Set CGRAM access mode 10000000 : '1' AD6 AD5 AD4 AD3 AD2 AD1 AD0 : Set DDRAM address 00-7F + Set DDRAM access mode
The address counter can be thought of the location where data is written to, and in the context of DDRAM it is also used as the cursor position. Reading the control register returns a busy status flag in DB7 and the current address counter value in DB6-DB0, such that you can determine the current DDRAM or CGRAM offset you are accessing.
To prepare the LCD for use, the 8-bit interface initialization method proposed by Donald Weiman was used:
The LCD display class provides a set of familiar functions for text output:
- clrscr() - Clear the display.
- gotoxy() - Set the cursor position.
- putch() and puts() - Print characters and formatted strings.
There are additional functions to control the blinking cursor as well.
Testing & Technical Challenges
The biggest challenge was allocating time adequately to get all the work done. This project was unexpectedly very labor intensive.
Issue #1 : Circuit wiring
Wire-wrap construction was used to build the circuit. In retrospect it would have been wiser to have a PCB made after developing a prototype on a breadboard. However this adds a significant lead time to get the PCBs, so it needed to be done at an earlier phase of the project.
Issue #2 : Enclosure construction
It can be difficult to visualize the size and height of components inside an enclosure. A good rule of thumb would be to anticipate that you'll need 30% more space for wiring and protruding connectors than you thought you needed.
Issue #3 : Software development goals
It's easy to get into a trap of over-engineering software to be too modular, too abstract, and too general purpose while losing sight of the actual goals of the project. A sensible cut-off point should be chosen in terms of features and flexibility.
Issue #4 : Noise in circuits
Due to the wire-wrap construction there were a few times when sporadic behavior was observed, which was due to a pull-up resistor becoming loose and allowing a signal to float and eventually oscillate. A good first step in troubleshooting a circuit is to use a continuity test feature and verify all connections have been established.
Issue #5 : Software debugging
It was helpful to have an optional debug mode defined (allowing for '#if DEBUG / #endif blocks' to be used) that printed status information to the console to be viewed in Hercules. Aspects of normal operation were displayed instead of just error messages. This helped find bugs and unconsidered edge cases while reviewing the logs that were not immediately obvious when using the system.
Conclusion
There was a lot to learn from this project:
1. Physical construction takes a _lot_ of time and work. Even working with relatively soft plastic as opposed to metal or wood is still a big undertaking. At least half the team should be dedicated to this task so the electronics and software people can focus on their area.
2. Don't underestimate how much room you need inside an enclosure. After moving from a smaller one to a bigger one, adding all the wiring took up the additional space I wanted to use for an internal power supply.
3. It's helpful to build up complex libraries from smaller steps. The LCD interface was tested by being directly interfaced with the SJ One board, and was developed until it was fully functional. Only then was it converted to use the shift register to provide data. This simplified troubleshooting as a known-good codebase had been created before the conversion started, so it was easier to isolate faults.
4. Work needs to be evenly divided amongst group members. Otherwise everything takes much too long to complete.
5. I gained a lot of experience with the HD44780 which I've always read about but never actually used. Likewise it was the first time I used shift registers on the SPI bus and that was a useful experience too.
6. More research should have been made to see how difficult the PN538 was to work with. Admittedly it was purchased because it is cheap and easily available, but that's a less useful metric than the success rate of projects built around the PN538.
Future work
There are a number of ways to improve the project.
- Use PWM or a timer interrupt to control buzzer updates. This would allow for the frequency to be varied, allowing for frequency envelopes and sweeps, and more complex sound effects generated by linking single tone structures together.
- Use a 16x4 LCD display to display larger messages. The current 16x2 LCD is cramped, but functional.
- Use PWM to control the RGB LCD backlight cathodes. The current implementation wires these to the shift register, but if using three of the PWM channels the backlight colors could be expanded from eight to several million.
- Use a SPI GPIO expander. In particular the Microchip MCP23S17 has two 8-bit I/O ports that could have replaced the two 745HC595 shift registers. Note that the MCP23* line of GPIO expanders have rather unusual characteristics for their inputs regarding the Vil/Vih levels, so care must be taken when using their pins configured as inputs. However as outputs they function as expected.
- Use a different RFID chip other than the PN538.
- Support manual registration and management of PINs, either by using the internal operator button (more secure) or a dedicated service mode PIN (less secure).
In terms of physical security the solenoid can be triggered simply by applying +12V across the terminals with the control box disconnected. A more secure method would be to place a second SJ-One board inside the security box along with a dedicated power supply, and have the control box communicate to it over a serial link. The SJ-One in the security box can authenticate door open requests from the control box. The serial link could be protected by having data encrypted using a LFSR with a known seed shared between both systems which would make eavesdropping challenging, but would not stop a replay attack.
A further exercise in security would be to use a wireless system (XBee) for the control box and security box to communicate.
Goals not completed
The RFID functionality was not finished. A functioning driver for the PN538 chip could not be developed.
Other thoughts
It would be nice if a future revision of the SJ One board had pin headers for:
- LPC1758 RESET and RSTOUT
- A FT245R CBUS pin configured as VCCSW/PWREN#
- USB +5V
Project Video
Upload a video of your project and post the link here.
Project Source Code
References
Acknowledgement
I would like to express my thanks to Wilson Luc for extensive help constructing an early version of the control box and giving advice about power tool use and techniques.
References Used
- HD47780 datasheet
https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
- OKI-78SR datasheet
http://power.murata.com/data/power/oki-78sr.pdf
- 74HCT595 datasheet
http://www.nxp.com/documents/data_sheet/74HC_HCT595.pdf
- "Piezoelectric Sound Components Application Manual" (Murata)
http://www.murata.com/~/media/webrenewal/support/library/catalog/products/sound/p15e.ashx
- ULN2803 datashset
https://www.adafruit.com/datasheets/ULN2803A.pdf
- Adafruit PN532 tutorial
https://learn.adafruit.com/adafruit-pn532-rfid-nfc
- NC-C1627A datasheet (similar to, but the same as, the Adafruit GWT-C1627A-RGB LCD module)
http://www.futurel.bg/datasheets/5/C1627A.pdf
- GWT-C1627A-RGB mechanical data and pin assignments