Automated Mailbox – Dec07‐01 Final Report – December 10 th , 2007 Client: Iowa State University Contact Name: Dr. Greg Smith Advisor: Dr. Degang James Chen Team Members: Mario Limonciello Major: Electrical Engineering Role: Team Leader Tyler Clifton Major: Electrical Engineering Role: Communication Coordinator Steve Walker Major: Electrical Engineering Role: Team Member Yee Quan Wong Major: Electrical Engineering Role: Team Member DISCLAIMER: This document was developed as a part of the requirements of an electrical and computer engineering course at Iowa State University, Ames, Iowa. This document does not constitute a professional engineering design or a professional land surveying document. Although the information is intended to be accurate, the associated students, faculty, and Iowa State University make no claims, promises, or guarantees about the accuracy, completeness, quality, or adequacy of the information. The user of this document shall ensure that any such use does not violate any laws with regard to professional licensing and certification requirements. This use includes any work resulting from this student‐prepared document that is required to be under the responsible charge of a licensed engineer or surveyor. This document is copyrighted by the students who produced this document and the associated faculty advisors. No part may be reproduced without the written permission of the senior design course coordinator.
Table of Contents 1.0
Summary ........................................................................................................................................... 5
2.0
Project Plan ....................................................................................................................................... 6
2.1
Problem Statement ....................................................................................................................... 6
2.2
Concept Sketch/Mockup ............................................................................................................... 6
2.3
System Block Diagram ................................................................................................................... 8
2.4
System Description ....................................................................................................................... 8
2.5
Operating Environment .............................................................................................................. 11
2.6
User Interface Description .......................................................................................................... 11
2.7
Functional Requirements ............................................................................................................ 12
2.8
Non‐functional Requirements .................................................................................................... 12
2.9
Market Survey ............................................................................................................................. 13
2.10
Deliverables ................................................................................................................................. 14
3.0
Project Design ................................................................................................................................. 15 3.1.1
Common Parts ..................................................................................................................... 15
3.1.2
Transmitter Specific ............................................................................................................ 16
3.1.3
Common Code Libraries ...................................................................................................... 16
3.1.4
Receiver Specific Code ........................................................................................................ 17
3.1.5
Transmitter Specific Code ................................................................................................... 17
3.2
Work Structure ............................................................................................................................ 18
3.2.1
Incremental Design ............................................................................................................. 18
3.2.2
Electrical & Mechanical Drawing ........................................................................................ 19
3.3
Resource Requirements .............................................................................................................. 19
3.4
Project Schedule ......................................................................................................................... 20
4.0
Project Testing ................................................................................................................................ 20
4.1
Transmission Test ........................................................................................................................ 20
4.2
Logic Test..................................................................................................................................... 20
4.3
LED Intensity Test ........................................................................................................................ 20
4.4
Battery Test ................................................................................................................................. 21
4.5
Temperature Test ....................................................................................................................... 21
5.0
Future Work for Commercialization ............................................................................................... 21
6.0
Closing Thoughts ............................................................................................................................. 22 ii
7.0
References ...................................................................................................................................... 23
8.0
Appendices ...................................................................................................................................... 25
8.1
Appendix A: USI‐SPI Code ........................................................................................................... 25
8.2
Appendix B: Common NRF24LO1 Code ...................................................................................... 29
8.3
Appendix C: Transmitter NRF24L01 Code ................................................................................... 37
8.4
Appendix D: Receiver NR424L01 Code ....................................................................................... 44
8.5
Appendix E: Transmitter Main Code ........................................................................................... 52
8.6
Appendix F: Receiver Main Code ................................................................................................ 55
8.7
Appendix G: Schematics .............................................................................................................. 57
8.7.1
Transmitter ......................................................................................................................... 57
8.7.2
Receiver ............................................................................................................................... 58
8.8
Appendix H: Mechanical Drawings ............................................................................................. 59
8.9
Appendix I: Project Schedules ..................................................................................................... 62
iii
Table of Figures Figure 1: Diagram of different mailbox logic states ..................................................................................... 7 Figure 2: Mailbox with mock detection and in‐home indicator ................................................................... 7 Figure 3: Block diagram of Automated Mailbox system. .............................................................................. 8 Figure 4: Visual block diagram of sending signal .......................................................................................... 9 Figure 5: Visual block diagram of receiving a signal ................................................................................... 10 Figure 6: Smart Home's Mail Chime ........................................................................................................... 13
iv
1.0 Summary For most people walking to an empty mailbox is inconvenient. The time spent, weather, or user’s disabilities are reasons why making the trip between their home and mailbox may seem undesirable. Demographically, the elderly may have health issues that benefit from the ability to predetermine the presence of mail. The mailbox may be located at the end of their driveway or across the street. The solution avoids multiple trips to their mailbox by informing the user of their mailbox’s status. This system initiates a check for mail once the door has been opens and closes in sequence. If the Automated Mailbox detects mail, the user’s in‐home receiver provides a notification that the mailbox is not empty. The Automated Mailbox system consists of two microcontrollers, two transceivers, light emitting diodes, comparators, photodetectors, switches, resistors, and power sources. The entire system is preassembled and ships with a mailbox. The microcontroller and transceiver components provide a means of control and communication. The wireless setup is a modern method for notifying users, as well as a smart feature for this product. The Automated Mailbox performs a check for the presence of mail by emitting light over an array of photodetectors. A predetermined voltage–voltage with no mail present–acts as a control voltage compared with the voltage due to the detection system. A comparator sends a resulting signal to the microcontroller, which communicates with the transceiver to perform the transmission. A switch is incorporated into the mailbox’s flag to provide a method for not alerting when a user is sending mail. The flag switch will still trigger the microcontroller to wake up, but the IR detection system will not be enabled. The system also detects when the outside power source needs a replacement. Low power circuitry performs a voltage level check during each transmitting process. This is performed by a simple comparator circuit to check if the voltage is less than 66% of the rated requirement. Once the power source reaches this level, a notification informs the user that a replacement may be in order. A microcontroller located within the in‐home receiver handles the notification for the Automated Mailbox. Once a signal reaches the transceiver in the in‐home receiver, the microcontroller handles the information and powers the corresponding LED(s). Competitors develop systems with less effective checking routines. One competitor employs a detection method to notify the user once the door of the mailbox becomes open. This product “chimes” until the door closes. This may issue false signals during harsh winds, children’s play, or faulty equipment. This project’s design process utilizes a top‐down engineering approach. The design is presented in incremental block diagrams and detailed circuit schematics. These diagrams come from design software such as Cadence Capture CIS and flowchart software such as Microsoft Visio. Using these diagrams as a blueprint, the individual components assemble easily. Software design for the microcontroller was all done in C.
5
2.0 Project Plan
2.1 Problem Statement During harsh seasons, many people, especially the elderly, do not enjoy traveling to their mailbox multiple times a day to check for mail. There may be a variety of hazards, especially during the extreme seasons. For example, in the winter the driveway may be icy, making it easy for someone to slip and fall. During the summer, the extreme heat may cause the individual to get heat sickness or poisoning. It is much easier to avoid these conditions if a system provides notification when there is mail in the mailbox before making the journey outside.
2.2 Concept Sketch/Mockup Figure 1: Diagram of different mailbox logic states is a concept sketch for receiving mail and performing next step operations. The key process after receiving mail consists of checking the status of the mailbox door and flag. Both components are monitored by magnetic switches, which can interrupt the microcontroller. The microcontroller monitors for the door to be closed. Regardless of the door’s status, if the flag is changed, the microcontroller will wake up and send an update on the battery status. If the door is closed and the flag is down, the system detects the presence of mail and transmits a signal pending the results. The detection system uses a matrix of infrared LEDs on the ceiling of the mailbox. The placement criterion for the infrared LEDs consists of maximizing the surface area coverage. The area of the ceiling panel provides a basis of coverage by mathematically spacing the LEDs given this information. The photodetectors are placed in a similar matrix, but using the area of the mailbox’s floor. As the infrared light from the diodes is detected by the photodetectors, a voltage is formed from the photons’ energy. This voltage is compared to a control voltage to determine the mailbox’s status. Figure 2: Mailbox with mock detection and in‐home indicator is a concept mockup of this process.
6
Figure 1: Diagram of different mailbox logic states
Figure 2: Mailbox with mock detection and in‐home indicator
7
2.3 Syste em Block Diaagram Figure 3: Block diagram m of Automated Mailbox system. provides an overvview of all of the steps thaat are processed d for the syste em.
Figure 3: Block diagram m of Automated d Mailbox system m.
2.4 Syste em Descriptiion As it has b been describe ed, the Autom mated Mailbo ox is broken up into two sm maller subsysttems. The in n home receiver and outtdoor transmitter both shaare common libraries and similar comp ponents. The basic subsystem co onsists of a power source,, LEDs, a micrrocontroller, aand an RF transmitter. For the specific ap pplication use ed, these are connected in n different fasshions that arre described m more in detaiil later on. The transmitter is interrupt driven b by changes upon the magn netic switch inputs. When n the interrup pts are triggeered, the micrrocontroller w will awaken. It then uses IR LEDs and ph hotodetectorrs and a voltage comparattor to determine if anythin ng is breakingg the path of IR. The micro ocontroller then communiccates with the R RF transmitte er using a stan ndard SPI protocol. The acctual handshaaking between the two RF transmitteers is then co oncealed from m the code baase, making th his a much sim mpler device.. See Figure 4 4: Visual blo ock diagram o of sending sign nal for a flow wchart of this eexchange. Like the trransmitter, th he receiver exxposes the RFF device to an n SPI interfacee. Instead of being driven by external interrupts on switches, it receives an interrupt from the RF devicee to indicate that it needs to he data receivved. Two outtput pins are then enabled d depending u upon the dataa that was process th received. See Figure 5 5: Visual blockk diagram of rreceiving a siggnal for moree information. 8
Figure 4: Visual block diagram of sending signal
9
Receiving Signal Transceiver
Microcontroller
LED
Figure 5: Visual block diagram of receiving a signal
10
2.5 Operating Environment The transceiver and detector are both installed within the mailbox. The system is durable enough to resist bending and being bumped from contents in the mailbox. The detection system withstands harsh weather conditions and is resistant to rainwater, snow, or dust when the mailbox is opened. The bottom of the mailbox is sealed to prevent exposure to these elements. Neither the transceiver nor detection system withstands being thrown or dropped, but they can endure a slip off a table from several feet off the ground and still function. The transceiver and detection system withstand temperatures from ‐10 °F to 120 °F. The operating temperature for the receiver is room temperature, 60 °F to 80 °F. The receiver's indicators are visible in both a bright and dimly lit room. The receiver is sealed such that it is resistant to light, dust, and water exposure.
2.6 User Interface Description
Mailbox Door The mailbox door is the interface for the user and mail carrier. The system is activated when the door is closed. This action is done whenever mail is inserted in or removed from the mailbox. Once the door is closed, the microcontroller activates from the falling edge of the magnetic switches and proceeds with the detection process.
Mailbox Flag The flag interface differentiates between outgoing and incoming mail. The flag must be put up whenever mail is being sent out. When the flag is in the up position, the microcontroller receives an input notifying it that the mail is outgoing. This prevents the system from sending a mail detection signal to the receiver.
In‐home Receiver The in‐home receiver interface notifies the user of the mailbox’s status: mail present or low battery. The user’s in‐home receiver is connected to a 120 VAC outlet. No further action is necessary after this connection has been made. Once a mail present or low battery status occurs, an LED illuminates on the in‐home receiver informing the user of this condition.
11
2.7 Functional Requirements The following requirements have been determined necessary for the system to function as described in the problem statement. The system shall detect the presence of incoming mail. The system must be able to sense the flag switch position to differentiate incoming or outgoing mail. The system must be able to alert users when incoming mail is present instead of outgoing mail.
2.8 Non‐functional Requirements These requirements are not absolutely necessary for this project’s functionality. They are not explicitly defined in the problem statement, but are in our problem solution. Power
1. The communication and detection system located within the mailbox shall be powered by four AA batteries. This provides a total of six volts to the system. 2. The in‐home receiving unit must be powered by standard 120 VAC outlets. Space 1. The circuits and system components must consume less than
1 of the mailbox’s total space. 5
Transmitting and Receiving 1. Transmission must be able to occur with fifty feet separation. 2. The transmission shall not interfere with other systems operating at the same frequency. Operating temperature 1. The system components must be able to withstand harsh Iowa conditions ranging from 0 °F to 120 °F. Size 1. The overall mail box dimensions shall be around 7”H x 18”L. Weight 1. The Automated Mailbox detector system must be light enough to mount on a mailbox ceiling without exerting excessive stress on the mailbox. 2. The switches must be light enough to mount on a mailbox flag without causing it to break. 3. The other mailbox mounted components must be light enough to mount within a mailbox without compromising the existing mailbox structure and post. Cost 1. The cost of building the system must not exceed $150.
The cost of the complete system must be competitively priced and profitable compared to other market alternatives.
12
2.9 Market Survey In our analysis of similar products, the only one we were able to find was the ‘Figure 6: Smart Home's Mail Chime’. This device sends a signal to the house at all times the door is left open. This has the distinct disadvantage that it will not actually detect whether an item is in the mailbox. If somehow the door is ajar, the device will continually send a signal to the house. Mail Chime ‐ $49.99 http://www.smarthome.com/74661.html Dimensions: 1. Sensor: 4‐1/4" x 1‐1/2" x 1" 2. Receiver: 4‐1/4" x 3‐1/4" x 1" Range: 300 foot What’s included: 1. Mail Chime Receiver 2. Sensor with 12‐volt A23 battery installed 3. AC adapter 4. Double‐faced tape 5. Instructions Only works on mailboxes that have a door that opens from a vertical to a horizontal position.
Figure 6: Smart Home's Mail Chime
13
2.10
Deliverables
Mailbox with detection and communication system In‐home receiver with communication and notification system User documentation The end product provides users with notification of mail presence in their mailbox. A detection system is placed within the mailbox, which uses infrared identification and photodetectors to determine their mailbox’s status. The outside unit hosts a power source within its structure. Once triggered, a signal is transmitted from the mailbox to an in‐home receiver. The in‐home receiver is powered by standard 120 VAC outlets. The receiver has two states to inform the user about the condition of the system. Notification is done by LEDs placed within the receiver housing to indicate low battery and/or mail present. User documentation is included with the purchase of each Automated Mailbox system. This documentation gives the user basic directions for operation, installation, as well as contact support.
14
3.0 Project Design Using the basic System Description, we choose adequate parts that would meet both our Functional and Non Functional requirements. Because of the way this project was designed, many of the same model numbers were used in both the receiver and the transmitter.
3.1.1 Common Parts •
Microcontroller
The microcontroller is used to control all other components in the design. For this project the team chose an Atmel AVR ATTiny2313. The team’s microcontroller has an 8‐bit architecture with 2 Kbytes of in‐system programmable Flash memory. There are also 128 Bytes of Data EPPROM and 128 Bytes of SRAM. It can operate at voltages ranging from 1.8 ‐ 5.5 V. Atmel AVR series microcontrollers support programming in either assembly or embedded C programming languages. The device is also capable of communicating in SPI format with other hardware devices indirectly via a USI interface. This will be described more in detail later. •
Transceiver
The transceivers are responsible for wireless communication between the mailbox and household. All transmissions occur on a range of channels operating at 2.4 GHz. These transceivers utilize registers when transmitting data. The transceiver used is the MiRF v2, which is based upon the Nordic nRF24L01, Single Chip 2.4 GHz Transceiver. The transceivers use an SPI interface for programming and communication, which is compatible with the microcontrollers. The SPI specification allows for two or more devices on a bus to communicate, designating one as a master with the rest being slave devices. The microcontroller is the master device, allowing it to control the timing, clocking, and functionality of the transceiver. The MiRF v2 simplifies many of the complicated steps with regard to clocking data, adding an antenna, and setting up additional features supported by the Nordic nrF24L01. The only pins available to the Nordic are the standard SPI pins and power. Upon initial power, the microcontroller sends a set of configuration data to the transceiver via SPI. The transceiver powers up after processing the configuration data. Once the transceiver is ready, it sends a signal to the microcontroller indicating that it can begin processing data. From this point, the microcontroller controls all data transfers to and from the transceiver. •
LED
LEDs are used to provide notification to a user regarding their mailbox’s status. This status is either mail is present or low batteries. These LEDs are powered by the microcontroller in the in‐ home receiver unit. The microcontroller will determine when the usage of an LED is necessary.
15
3.1.2 Transmitter Specific •
Magnetic Switches
The magnetic switches are comprised of two sections that function like the inverse of an ordinary switch. When the two contacts of the switch are touching, the circuit will appear to be an open circuit. When the two contacts are separated, the circuit will close and allow current to flow. It does not dissipate power unless the mailbox door is open. When the door is open, a minimal amount of power is dissipated. This dissipation is not enough to affect the battery significantly. It conserves the battery while not in use. •
IR LEDs
Infrared LEDs are used for the detection process. They are powered by the microcontroller within the mailbox. These LEDs are governed by the microcontroller in the detection system, thus determining when their usage is necessary. •
Photodetectors
The photodetectors will detect infrared light from the IR LEDs. The photodetectors are located on the bottom of the mailbox. When mail is present, there is blockage of photodetectors, causing a lower voltage. This lower voltage notifies the microcontroller that mail is present. •
Voltage Regulator
The voltage regulator regulates the voltage from the supply, such that the voltage seen by the devices is unaffected by the degradation of the batteries. The voltage regulator operates by stepping a 6V (4‐AA) input down to a 3.3V output. •
Comparator
The comparator interprets voltages caused by current from the photodetector over a resistor. It compares two voltages at the + and – terminal. When the comparator sees a voltage of 2.8 volts, it tells the microcontroller that there is no mail present. Once the voltage drops below 2.8 volts, it notifies the microcontroller that mail is present.
3.1.3 Common Code Libraries Because both the microcontrollers need to communicate via SPI to their respective RF Transceivers, a lot of code was shared between the two microcontrollers. The microcontrollers don’t directly have an SPI interface, so one has to be implemented via the USI (universal serial interface) available on the microcontroller. Atmel document AVR319 was very useful for this procedure. This document shows how to implement USI on some of the bigger microcontrollers such as the ATMega8. There is not documentation on the 16
internet to indicate one way or the other if this is supported on the ATTiny2313. We did manage to implement such an interface by slight modifications to Atmel’s code. See Appendix A for the USI‐>SPI Code. The other common piece of code that had to be handled was how to communicate between the SPI interfaces of the transceivers and the USI‐SPI interface of the microcontroller. Luckily, Brennen Ball provides a very rudimentary library that implements some of the more important functions available to the NRF24L01. This library is rather sizable however, and when compiled on a microcontroller takes up 15x the size of a ATTiny2313’s flash memory. To get around this, the library had to be first customized to all the pins and interrupts of the ATTiny2313, followed by being stripped of all unnecessary functionality. Because of this, both the microcontrollers end up using different versions of the library. See Appendix B Appendix C and Appendix D for this code.
3.1.4 Receiver Specific Code The code used on the receiver is very straightforward, especially after the USI‐>SPI and NRF24L01 code is properly implemented and functioning. It simply exploits the interrupt functionality of the NRF24L01 library to decide when to turn on and off different LEDs. See Appendix E for this code.
3.1.5 Transmitter Specific Code The code used on the transmitter is much more complicated. Since the transmitter needs to be able to go into a low power mode, this code needs to properly manage all the devices attached to it and be aware of what pins and components are actually active. Also, this code needs to work directly off interrupts instead of within the main while () loop. This is because normally that while () loop is not being executed. Instead the CPU clock will typically be sleeping waiting for one of the magnetic switches to trip an interrupt and wake the code back up. The details for this code can be seen in Appendix F.
17
3.2 Work Structure To accomplish this project, the different aspects of the project had to be divided among group members. Since all parts are directly related to one another, members had to be sure to share any complications encountered along the way.
3.2.1 Incremental Design During each step of the design, incremental milestones were created to verify that no errors were encountered within that subsection. For example with the code testing, a list of 4 tests was created to both ensure functionality and to debug or narrow down any possible problems. • Interrupt Test Interrupts are urgent to counter functionality as well as SPI functionality. To make sure the microcontroller’s fuses were set appropriately, code was placed inside of an interrupt service routine. The only way that this code could be executed would be if the ISR was properly being called. • Counter Test The next test was to ensure that the timer was being properly utilized. Since the USI‐>SPI code was highly dependent upon timer overflow, this was urgent. To test it, a timer was set to perform something that would have normally happened within the main loop inside an interrupt service routine instead. • SPI Test When first porting over the code from the AVR319 document, it had to be tested to make sure it was properly transmitting SPI commands. To verify this, the SPI output (MOSI pin) was connected to the SPI input (MISO pin) on the same microcontroller. When a command was written out the MOSI pin, there was verification in place to make sure it was properly read back on the MISO pin. • Debug SPI Commands As a final test, using an oscilloscope, the commands were watched to make sure that they were being properly transmitted. By following this organized design, testing, and verification structure we have been able to identify bottlenecks in the design before they actually became troublesome. The exact same procedures have been in use heavily for this design.
18
3.2.2 Electrical & Mechanical Drawing Early in the development cycle, electrical drawings were produced. As choices of components changed, so have these schematics. The most updated drawings are available in Appendix G. A mechanical drawing was not produced until the very last step in the development process. Mechanical was a very low priority, and consequently held off until the very end. The mechanical drawing is available in Appendix H.
3.3 Resource Requirements The necessary costs for all the parts are summarized in Table 1 and Table 2. The spending limit was $150, and the team is well within this limit.
Part
Cost per item
Quantity
Total Cost (with Free Parts)
Total Cost (without Free Parts)
Battery (9V)
$4.00
1
$4.00
$4.00
Microcontroller
$1.79
2
$0
$3.58
RF Transceiver
$29.95
2
$59.90
$59.90
Photodetector
$0.25
9
$0
$2.25
IR LED
$0.30
3
$0
$0.90
Magnetic Switch
$3.50
2
$0
$7.00
20-Pin Retention Socket
$0.69
1
$0.69
$0.69
25 Pin D-Sub Connector Hood
$1.79
1
$1.79
$1.79
25 Pin D-Sub Connector
$1.89
1
$1.89
$1.89
PCB
$1.79
1
$1.79
$1.79
$70.06
$83.79
Total Table 1: Electrical Part Costs
Part
Cost per item
Screws
Quantity
Total Cost
Provided
20
$0
$7.48 $8
1 1
$7.48 $8
Transmitter housing
$8
1
$8
Plexiglas
$6
1
$6
Power Cord
$5
1
$5
Mailbox Receiver housing
Total
$34.80 Table 2: Mechanical Part Costs
19
3.4 Project Schedule Throughout the project’s lifetime, the team has attempted to work as closely to the schedule as possible. During the first semester, it was followed very strictly with little deviation. For second semester, as the team got overwhelmed the schedule began to slip. Thankfully, due to all group members putting in extra time, it was recovered by the end. See Appendix I for the schedules.
4.0 Project Testing For every step of the project, testing was essential to verifying project functionality and performance. Incrementally testing each component and subsystem ensured an easy resolution. After completing the design, testing was used to determine which of the project’s functional and nonfunctional requirements had been met.
4.1 Transmission Test A test had been developed for determining the maximum range of transmission. The group attempted to use the device at different ranges in order to find where it began to drop off. By performing this test earlier in the product’s design, the group discovered that the initial range was only ten feet. This limitation was due to the on board ceramic antennas that were initially in use. To solve the problem, the group replaced these antennas with larger external antennas. This increased the range to approximately 50 feet.
4.2 Logic Test When the project was initially planned, the group made choices for how it should be performing, interacting, and reacting. This was all implemented in code throughout design. After design was complete, the group verified that each of these interactions worked correctly. To make sure these were sensible; the group asked outside members how they would expect the project to function. Using input from possible clients, some of the behavior was adjusted.
4.3 LED Intensity Test To determine how accurate the device was, the intensity of the LEDs was varied. As battery voltage decreased, the possible intensity also decreased, and results recorded. This allowed the group to determine the most ideal area to notify a user of a low battery and also verified the requirement of being able to detect mail.
20
4.4 Battery Test One of the requirements was that the end product would have a significant battery life. Unfortunately, the group didn’t have the time to verify exact numbers on battery life. Instead, approximations were made. Using the average charge of 650 mAH for each one of the three AA batteries, approximately 2.5 months of battery life was estimated. The group took this a little further then, and came up with a list of items that could be done to improve battery life: • • • • •
Switching LED types Optimized Code Adding an external clock Deeper sleep modes Integrating more functionality into microcontroller
4.5 Temperature Test Another requirement was that the mailbox be able to withstand harsh conditions of Iowa winters and summers. To test the cold environment, the mailbox was brought outdoors during the winter. To test the hot environment, a blow dryer was used to heat up components. Unfortunately, several of the semiconductors ended up being susceptible to temperature extremes on either end. To solve this, additional circuitry for temperature adjustment is needed.
5.0 Future Work for Commercialization The group conducted a usability study with an industrial engineering student. Through this study, a variety of items were found that should be done before this product is ready to be used by the general public. • • • • • • • • •
Directional high gain antennas Rubber seals for the opening of the mailbox Using set screws with o‐rings instead of Velcro to mount items Using polycarbonate for the ceiling instead of cardboard Build a custom flag with an embedded switch Use a solid foam pad instead of Styrofoam to mount devices on the bottom To avoid wire breakage, use notched plexiglass Provide insulation for battery box Create temperature hysteresis control system
21
6.0 Closing Thoughts During extreme hot or cold seasons, many people, especially the elderly, are scared to travel to their mailbox multiple times to check for mail. Rather than continuously fear encountering dangers on their trip to and from the mailbox, it is much easier for the person to be notified that there is mail in the mailbox before making the journey outside. The aim of this project is to create a product that will notify the user when an object has been placed in the mailbox. Once something has been inserted into the mailbox, the user will receive an alert on a receiver located in the safety of their household. They can then travel outside only when necessary, and only have to make a single journey. After the user obtains the item from the mailbox, the device resets the receiver in the household, indicating the mailbox is now empty. The product is easily implemented in an existing mailbox and is very easy to use. An equal participation is required among the team members to guarantee the successful completion of this project. With plenty of design time, money to purchase components, and technical resources to improve the design, an automated mailbox design has been developed. The device transmits a signal back to the house to let the user know the new status of the mailbox. The device also interprets the status of the mail flag to determine if the user is attempting to send a letter. The automated mailbox is a simple, low cost solution for alerting the user about received mail.
22
7.0 References [1] Choosing a Microcontroller. 7 Feb. 2007
. [2] Choosing a Microcontroller for Embedded System Applications. 7 Feb. 2007 . [3] Clearwater Tech. "GE Security." Clearwater Tech. Clearwater Tech Industries. 7 Feb. 2007 . [4] Fairchild Semiconductor. "Fairchild Semiconductor ‐ Site Search." Fairchild Semiconductor. 2006. 02 Feb. 2007 . [5] Fairchild Semiconductor. "QEE113 Plastic Infarred Light Emmiting Diode." Fairchild Semiconductor. 30 Apr. 2002. 2 Feb. 2007 . [6] Fairchild Semiconductor. "QSE773 Plastic Silicon Pin Photodiode." Fairchild Semiconductor. 2000. Fairchild Semiconductor Corporation. 03 Feb. 2007 . [7] Freescale. "MC33493 Product Summary Page." Freescale. Freescale Semiconductor Industries. 27 Jan. 2007 . [8] Freescale. "MC33592 Product Summary Page." Freescale. Freescale Semiconductor Industries. 7 Feb. 2007 . [9] Hewes, John. "Light Emmiting Diodes." The Electronics Club. 2007. 25 Jan. 2007 . [10] How to choose a MicroController. 7 Feb. 2007 . [11] How to choose a Microcontroller for a wireless senior Electrical and Computer Engineering Design Project . 7 Feb. 2007 . [12] Paschotta, Rüdiger. "Encyclopedia of Laser Physics and Technology ‐ Photodetectors." RP Photonics. 11 Nov. 2006. 25 Jan. 2007 .
23
[13] Real Elliot, The. Weblog entry. 16 Nov. 2006. Ghetto Programming: Getting started with AVR microprocessors on the cheap. 7 Feb. 2007 . [14] Refikh, M. "How to Build Your Own Wireless Receiver and Transmitter Device." E‐DSP. 11 Mar. 2006. 25 Jan. 2007 < http://www.e‐dsp.com/how‐to‐build‐your‐own‐wireless‐receiver‐and‐ transmitter‐device‐create‐rf‐in‐your‐embedded‐application/>. [15] SmartHome. "Basic Sensor of Alarm System." SmartHome. SmartHome Industries. 7 Feb. 2007 . [16] Pin Interrupts. 14 November 2007
[17] .d90 file format. 14 November 2007
[18] Interrupt syntax for different C compilers. 14 November 2007
[19] Using the USI Module for SPI Communication. 14 November 2007
[20] diyembedded.com. 14 November 2007
24
8.0 Appendices 8.1 Appendix A: USI‐SPI Code // This file has been prepared for Doxygen automatic documentation generation. /*! \file ******************************************************************** * * Atmel Corporation * * \li File: spi_via_usi_driver.c * \li Compiler: IAR EWAAVR 3.10c * \li Support mail: [email protected] * * \li Supported devices: All devices with Universal Serial Interface (USI) * capabilities can be used. * The example is written for ATmega169. * * \li AppNote: AVR319 - Using the USI module for SPI communication. * * \li Description: Example on how to use the USI module for communicating * with SPI compatible devices. The functions and variables * prefixed 'spiX_' can be renamed to be able to use several * spi drivers (using different interfaces) with similar names. * Some basic SPI knowledge is assumed. * * $Revision: 1.4 $ * $Date: Monday, September 13, 2004 12:08:54 UTC $ ****************************************************************************/ #include #include
/* USI port and pin definitions. */ #define USI_OUT_REG PORTB #define USI_IN_REG PINB #define USI_DIR_REG DDRB #define USI_CLOCK_PIN PB7 #define USI_DATAIN_PIN PB5 #define USI_DATAOUT_PIN PB6
//!< USI port output register. //!< USI port input register. //!< USI port direction register. //!< USI clock I/O pin. //!< USI data input pin. //!< USI data output pin.
/* Speed configuration: * Bits per second = CPUSPEED / PRESCALER / (COMPAREVALUE+1) / 2. * Maximum = CPUSPEED / 64. */ #define TC0_PRESCALER_VALUE 256 //!< Must be 1, 8, 64, 256 or 1024. #define TC0_COMPARE_VALUE 1 //!< Must be 0 to 255. Minimum 31 with prescaler CLK/1.
/* Prescaler value converted to bit settings. */ #if TC0_PRESCALER_VALUE == 1 #define TC0_PS_SETTING (1<
25
#endif
/*! \brief Data input register buffer. * * Incoming bytes are stored in this byte until the next transfer is complete. * This byte can be used the same way as the SPI data register in the native * SPI module, which means that the byte must be read before the next transfer * completes and overwrites the current value. */ unsigned char storedUSIDR;
/*! \brief Driver status bit structure. * * This struct contains status flags for the driver. * The flags have the same meaning as the corresponding status flags * for the native SPI module. The flags should not be changed by the user. * The driver takes care of updating the flags when required. */ struct usidriverStatus_t { unsigned char masterMode : 1; //!< True if in master mode. unsigned char transferComplete : 1; //!< True when transfer completed. unsigned char writeCollision : 1; //!< True if put attempted during transfer. }; volatile struct usidriverStatus_t spiX_status; //!< The driver status bits.
/*! \brief Timer/Counter 0 Compare Match Interrupt handler. * * This interrupt handler is only enabled when transferring data * in master mode. It toggles the USI clock pin, i.e. two interrupts * results in one clock period on the clock pin and for the USI counter. */ ISR(TIMER0_COMPA_vect) { USICR |= (1<
/*! \brief USI Timer Overflow Interrupt handler. * * This handler disables the compare match interrupt if in master mode. * When the USI counter overflows, a byte has been transferred, and we * have to stop the timer tick. * For all modes the USIDR contents are stored and flags are updated. */ ISR(USI_OVERFLOW_vect) { // Master must now disable the compare match interrupt // to prevent more USI counter clocks. if( spiX_status.masterMode == 1 ) { TIMSK &= ~(1<
/*! \brief
Initialize USI as SPI master.
26
* * This function sets up all pin directions and module configurations. * Use this function initially or when changing from slave to master mode. * Note that the stored USIDR value is cleared. * * \param spi_mode Required SPI mode, must be 0 or 1. */ void spiX_initmaster( char spi_mode ) { // Configure port directions. USI_DIR_REG |= (1<
/*! \brief Initialize USI as SPI slave. * * This function sets up all pin directions and module configurations. * Use this function initially or when changing from master to slave mode. * Note that the stored USIDR value is cleared. * * \param spi_mode Required SPI mode, must be 0 or 1. */ void spiX_initslave( char spi_mode ) { // Configure port directions. USI_DIR_REG |= (1<
/*! * * * * *
\brief
Put one byte on bus.
Use this function like you would write to the SPDR register in the native SPI module. Calling this function in master mode starts a transfer, while in slave mode, a byte will be prepared for the next transfer initiated by the master device. If a transfer is in progress, this function will set the write collision flag
27
* and return without altering the data registers. * * \returns 0 if a write collision occurred, 1 otherwise. */ char spiX_put( unsigned char val ) { // Check if transmission in progress, // i.e. USI counter unequal to zero. if( (USISR & 0x0F) != 0 ) { // Indicate write collision and return. spiX_status.writeCollision = 1; return; } // Reinit flags. spiX_status.transferComplete = 0; spiX_status.writeCollision = 0; // Put data in USI data register. USIDR = val; // Master should now enable compare match interrupts. if( spiX_status.masterMode == 1 ) { TIFR |= (1<
/*! \brief Get one byte from bus. * * This function only returns the previous stored USIDR value. * The transfer complete flag is not checked. Use this function * like you would read from the SPDR register in the native SPI module. */ unsigned char spiX_get() { return storedUSIDR; }
/*! \brief Wait for transfer to complete. * * This function waits until the transfer complete flag is set. * Use this function like you would wait for the native SPI interrupt flag. */ void spiX_wait() { do {} while( spiX_status.transferComplete == 0 ); }
// end of file
28
8.2 Appendix B: Common NRF24LO1 Code /***************************************************************************** * * File: spi1.h * * Copyright S. Brennen Ball, 2006-2007 * Mario Limonciello, 2007 * * The author provides no guarantees, warantees, or promises, implied or * otherwise. By using this software you agree to indemnify the author * of any damages incurred by using it. * *****************************************************************************/ #ifndef _SPI_H_ #define _SPI_H_ #include #ifndef #define #endif #ifndef #define #endif #ifndef #define #endif
bool bool unsigned char false false 0 true true !false
unsigned char spi1_send_read_byte(unsigned char byte); #endif //_SPI_H_ /****************************************************************************** * * File: nrf24l01.h * * Copyright S. Brennen Ball, 2006-2007 * Mario Limonciello, 2007 * * The author provides no guarantees, warantees, or promises, implied or * otherwise. By using this software you agree to indemnify the author * of any damages incurred by using it. * *****************************************************************************/ #ifndef NRF24L01_H_ #define NRF24L01_H_ #include #ifndef #define #endif #ifndef #define #endif #ifndef #define #endif
bool bool unsigned char false false 0 true true !false
///////////////////////////////////////////////////////////////////////////////// // SPI function requirements // // The user must define a function to send one byte of data and also return the // resulting byte of data data through the SPI port. The function used here // has the function prototype //
29
// unsigned char spi_send_read_byte(unsigned char byte); // // This function should take the argument unsigned char byte and send it through // the SPI port to the 24L01. Then, it should wait until the 24L01 has returned // its response over SPI. This received byte should be the return value of the // function. // // You should also change the include file name below to whatever the name of your // SPI include file is. ////////////////////////////////////////////////////////////////////////////////// #include "spi1.h" #define spi_send_read_byte(byte) spi1_send_read_byte(byte)
///////////////////////////////////////////////////////////////////////////////// // Delay function requirements // // The user must define a function that delays for the specified number of // microseconds. This function needs to be as precise as possible, and the use // of a timer module within your microcontroller is highly recommended. The // function used here has the prototype // // void delay_us(unsigned int microseconds); // // You should also change the include file name below to whatever the name of your // delay include file is. ////////////////////////////////////////////////////////////////////////////////// #define F_CPU 100000UL // Sets up the default speed for delay.h #include #define delay_us(microseconds) _delay_us(microseconds)
////////////////////////////////////////////////////////////////////////////////// // IO pin definitions // // Below you will find several definitions and includes. The first is an #include // for your microcontroller's include file to allow you to use register names // rather than numbers. The next three are to allow you to control the pins on // the 24L01 that aren't automatically handled by SPI. These are CE, CSN, and // IRQ. // // The general format of these defines is a define for the IO register the pin is // attached to. The second define is a mask for the pin. For example, say that // your CE pin is tied to an IO port with the register name IOPORT1. Also, let's // say that the IO port is 8-bits wide, and you have attached the pin to pin 0 of // the port. Then your define would look like this: // // #define nrf24l01_CE_IOREGISTER IOPORT1 // #define nrf24l01_CE_PINMASK 0x01 // // If you have defines in your include file for individual IO pins, you could use // this define in this file, as well. Using the previous example, assume that in // your microcontroller's include file, pin 0 of IOPORT1 has a define like this // // #define IOPORT1_PIN0 0x01 // // Then, you could make your defines for the CE pin in this file look like this: // // #define nrf24l01_CE_IOREGISTER IOPORT1 // #define nrf24l01_CE_PINMASK IOPORT1_PIN0 // // You should also change the include file name below to whatever the name of your // processor's register definition include file is. ///////////////////////////////////////////////////////////////////////////////////// #include #include //defines for uC pins CE pin is connected to //This is used so that the routines can send TX payload data and // properly initialize the nrf24l01 in TX and RX states. //Change these definitions (and then recompile) to suit your particular application.
30
//pb3 on attiny2313 #define nrf24l01_CE_IOREGISTER #define nrf24l01_CE_PINMASK
PORTB _BV(PB3)
//defines for uC pins CSN pin is connected to //This is used so that the routines can send properly operate the SPI interface // on the nrf24l01. //Change these definitions (and then recompile) to suit your particular application. //Pb4 on attiny2313 #define nrf24l01_CSN_IOREGISTER #define nrf24l01_CSN_PINMASK
PORTB _BV(PB4)
//defines for uC pins IRQ pin is connected to //This is used so that the routines can poll for IRQ or create an ISR. //Change these definitions (and then recompile) to suit your particular application. #define nrf24l01_IRQ_IOREGISTER PINB #define nrf24l01_IRQ_PINMASK _BV(PB2)
//////////////////////////////////////////////////////////////////////////////////// // SPI commands // // The following are defines for all of the commands and data masks on the SPI // interface. //////////////////////////////////////////////////////////////////////////////////// //SPI command defines #define nrf24l01_R_REGISTER 0x00 #define nrf24l01_W_REGISTER 0x20 #define nrf24l01_R_RX_PAYLOAD 0x61 #define nrf24l01_W_TX_PAYLOAD 0xA0 #define nrf24l01_FLUSH_TX 0xE1 #define nrf24l01_FLUSH_RX 0xE2 #define nrf24l01_REUSE_TX_PL 0xE3 #define nrf24l01_NOP 0xFF //SPI command data mask defines #define nrf24l01_R_REGISTER_DATA #define nrf24l01_W_REGISTER_DATA
0x1F 0x1F
//////////////////////////////////////////////////////////////////////////////////// // Register definitions // // Below are the defines for each register's address in the 24L01. //////////////////////////////////////////////////////////////////////////////////// #define nrf24l01_CONFIG 0x00 #define nrf24l01_EN_AA 0x01 #define nrf24l01_EN_RXADDR 0x02 #define nrf24l01_SETUP_AW 0x03 #define nrf24l01_SETUP_RETR 0x04 #define nrf24l01_RF_CH 0x05 #define nrf24l01_RF_SETUP 0x06 #define nrf24l01_STATUS 0x07 #define nrf24l01_OBSERVE_TX 0x08 #define nrf24l01_CD 0x09 #define nrf24l01_RX_ADDR_P0 0x0A #define nrf24l01_RX_ADDR_P1 0x0B #define nrf24l01_RX_ADDR_P2 0x0C #define nrf24l01_RX_ADDR_P3 0x0D #define nrf24l01_RX_ADDR_P4 0x0E #define nrf24l01_RX_ADDR_P5 0x0F #define nrf24l01_TX_ADDR 0x10 #define nrf24l01_RX_PW_P0 0x11 #define nrf24l01_RX_PW_P1 0x12 #define nrf24l01_RX_PW_P2 0x13 #define nrf24l01_RX_PW_P3 0x14 #define nrf24l01_RX_PW_P4 0x15 #define nrf24l01_RX_PW_P5 0x16 #define nrf24l01_FIFO_STATUS 0x17
31
//////////////////////////////////////////////////////////////////////////////////// // Default register values // // Below are the defines for each register's default value in the 24L01. Multi-byte // registers use notation B, where "B" represents "byte" and is the byte // number. //////////////////////////////////////////////////////////////////////////////////// #define nrf24l01_CONFIG_DEFAULT_VAL 0x08 #define nrf24l01_EN_AA_DEFAULT_VAL 0x3F #define nrf24l01_EN_RXADDR_DEFAULT_VAL 0x03 #define nrf24l01_SETUP_AW_DEFAULT_VAL 0x03 #define nrf24l01_SETUP_RETR_DEFAULT_VAL 0x03 #define nrf24l01_RF_CH_DEFAULT_VAL 0x02 #define nrf24l01_RF_SETUP_DEFAULT_VAL 0x0F #define nrf24l01_STATUS_DEFAULT_VAL 0x0E #define nrf24l01_OBSERVE_TX_DEFAULT_VAL 0x00 #define nrf24l01_CD_DEFAULT_VAL 0x00 #define nrf24l01_RX_ADDR_P0_B0_DEFAULT_VAL 0xE7 #define nrf24l01_RX_ADDR_P0_B1_DEFAULT_VAL 0xE7 #define nrf24l01_RX_ADDR_P0_B2_DEFAULT_VAL 0xE7 #define nrf24l01_RX_ADDR_P0_B3_DEFAULT_VAL 0xE7 #define nrf24l01_RX_ADDR_P0_B4_DEFAULT_VAL 0xE7 #define nrf24l01_RX_ADDR_P1_B0_DEFAULT_VAL 0xC2 #define nrf24l01_RX_ADDR_P1_B1_DEFAULT_VAL 0xC2 #define nrf24l01_RX_ADDR_P1_B2_DEFAULT_VAL 0xC2 #define nrf24l01_RX_ADDR_P1_B3_DEFAULT_VAL 0xC2 #define nrf24l01_RX_ADDR_P1_B4_DEFAULT_VAL 0xC2 #define nrf24l01_RX_ADDR_P2_DEFAULT_VAL 0xC3 #define nrf24l01_RX_ADDR_P3_DEFAULT_VAL 0xC4 #define nrf24l01_RX_ADDR_P4_DEFAULT_VAL 0xC5 #define nrf24l01_RX_ADDR_P5_DEFAULT_VAL 0xC6 #define nrf24l01_TX_ADDR_B0_DEFAULT_VAL 0xE7 #define nrf24l01_TX_ADDR_B1_DEFAULT_VAL 0xE7 #define nrf24l01_TX_ADDR_B2_DEFAULT_VAL 0xE7 #define nrf24l01_TX_ADDR_B3_DEFAULT_VAL 0xE7 #define nrf24l01_TX_ADDR_B4_DEFAULT_VAL 0xE7 #define nrf24l01_RX_PW_P0_DEFAULT_VAL 0x00 #define nrf24l01_RX_PW_P1_DEFAULT_VAL 0x00 #define nrf24l01_RX_PW_P2_DEFAULT_VAL 0x00 #define nrf24l01_RX_PW_P3_DEFAULT_VAL 0x00 #define nrf24l01_RX_PW_P4_DEFAULT_VAL 0x00 #define nrf24l01_RX_PW_P5_DEFAULT_VAL 0x00 #define nrf24l01_FIFO_STATUS_DEFAULT_VAL 0x11 //////////////////////////////////////////////////////////////////////////////////// // Register bitwise definitions // // Below are the defines for each register's bitwise fields in the 24L01. //////////////////////////////////////////////////////////////////////////////////// //CONFIG register bitwise definitions #define nrf24l01_CONFIG_RESERVED 0x80 #define nrf24l01_CONFIG_MASK_RX_DR 0x40 #define nrf24l01_CONFIG_MASK_TX_DS 0x20 #define nrf24l01_CONFIG_MASK_MAX_RT 0x10 #define nrf24l01_CONFIG_EN_CRC 0x08 #define nrf24l01_CONFIG_CRCO 0x04 #define nrf24l01_CONFIG_PWR_UP 0x02 #define nrf24l01_CONFIG_PRIM_RX 0x01 //EN_AA #define #define #define #define #define #define #define #define #define
register bitwise definitions nrf24l01_EN_AA_RESERVED 0xC0 nrf24l01_EN_AA_ENAA_ALL 0x3F nrf24l01_EN_AA_ENAA_P5 0x20 nrf24l01_EN_AA_ENAA_P4 0x10 nrf24l01_EN_AA_ENAA_P3 0x08 nrf24l01_EN_AA_ENAA_P2 0x04 nrf24l01_EN_AA_ENAA_P1 0x02 nrf24l01_EN_AA_ENAA_P0 0x01 nrf24l01_EN_AA_ENAA_NONE 0x00
//EN_RXADDR register bitwise definitions
32
#define #define #define #define #define #define #define #define #define
nrf24l01_EN_RXADDR_RESERVED nrf24l01_EN_RXADDR_ERX_ALL nrf24l01_EN_RXADDR_ERX_P5 nrf24l01_EN_RXADDR_ERX_P4 nrf24l01_EN_RXADDR_ERX_P3 nrf24l01_EN_RXADDR_ERX_P2 nrf24l01_EN_RXADDR_ERX_P1 nrf24l01_EN_RXADDR_ERX_P0 nrf24l01_EN_RXADDR_ERX_NONE
0xC0 0x3F 0x20 0x10 0x08 0x04 0x02 0x01 0x00
//SETUP_AW register bitwise definitions #define nrf24l01_SETUP_AW_RESERVED 0xFC #define nrf24l01_SETUP_AW 0x03 #define nrf24l01_SETUP_AW_5BYTES 0x03 #define nrf24l01_SETUP_AW_4BYTES 0x02 #define nrf24l01_SETUP_AW_3BYTES 0x01 #define nrf24l01_SETUP_AW_ILLEGAL 0x00 //SETUP_RETR register bitwise definitions #define nrf24l01_SETUP_RETR_ARD 0xF0 #define nrf24l01_SETUP_RETR_ARD_4000 0xF0 #define nrf24l01_SETUP_RETR_ARD_3750 0xE0 #define nrf24l01_SETUP_RETR_ARD_3500 0xD0 #define nrf24l01_SETUP_RETR_ARD_3250 0xC0 #define nrf24l01_SETUP_RETR_ARD_3000 0xB0 #define nrf24l01_SETUP_RETR_ARD_2750 0xA0 #define nrf24l01_SETUP_RETR_ARD_2500 0x90 #define nrf24l01_SETUP_RETR_ARD_2250 0x80 #define nrf24l01_SETUP_RETR_ARD_2000 0x70 #define nrf24l01_SETUP_RETR_ARD_1750 0x60 #define nrf24l01_SETUP_RETR_ARD_1500 0x50 #define nrf24l01_SETUP_RETR_ARD_1250 0x40 #define nrf24l01_SETUP_RETR_ARD_1000 0x30 #define nrf24l01_SETUP_RETR_ARD_750 0x20 #define nrf24l01_SETUP_RETR_ARD_500 0x10 #define nrf24l01_SETUP_RETR_ARD_250 0x00 #define nrf24l01_SETUP_RETR_ARC 0x0F #define nrf24l01_SETUP_RETR_ARC_15 0x0F #define nrf24l01_SETUP_RETR_ARC_14 0x0E #define nrf24l01_SETUP_RETR_ARC_13 0x0D #define nrf24l01_SETUP_RETR_ARC_12 0x0C #define nrf24l01_SETUP_RETR_ARC_11 0x0B #define nrf24l01_SETUP_RETR_ARC_10 0x0A #define nrf24l01_SETUP_RETR_ARC_9 0x09 #define nrf24l01_SETUP_RETR_ARC_8 0x08 #define nrf24l01_SETUP_RETR_ARC_7 0x07 #define nrf24l01_SETUP_RETR_ARC_6 0x06 #define nrf24l01_SETUP_RETR_ARC_5 0x05 #define nrf24l01_SETUP_RETR_ARC_4 0x04 #define nrf24l01_SETUP_RETR_ARC_3 0x03 #define nrf24l01_SETUP_RETR_ARC_2 0x02 #define nrf24l01_SETUP_RETR_ARC_1 0x01 #define nrf24l01_SETUP_RETR_ARC_0 0x00 //RF_CH register bitwise definitions #define nrf24l01_RF_CH_RESERVED 0x80 //RF_SETUP register bitwise definitions #define nrf24l01_RF_SETUP_RESERVED 0xE0 #define nrf24l01_RF_SETUP_PLL_LOCK 0x10 #define nrf24l01_RF_SETUP_RF_DR 0x08 #define nrf24l01_RF_SETUP_RF_PWR 0x06 #define nrf24l01_RF_SETUP_RF_PWR_0 0x06 #define nrf24l01_RF_SETUP_RF_PWR_6 0x04 #define nrf24l01_RF_SETUP_RF_PWR_12 0x02 #define nrf24l01_RF_SETUP_RF_PWR_18 0x00 #define nrf24l01_RF_SETUP_LNA_HCURR 0x01 //STATUS register bitwise definitions #define nrf24l01_STATUS_RESERVED #define nrf24l01_STATUS_RX_DR
33
0x80 0x40
#define #define #define #define #define #define #define #define #define #define #define #define
nrf24l01_STATUS_TX_DS nrf24l01_STATUS_MAX_RT nrf24l01_STATUS_RX_P_NO nrf24l01_STATUS_RX_P_NO_RX_FIFO_NOT_EMPTY nrf24l01_STATUS_RX_P_NO_UNUSED nrf24l01_STATUS_RX_P_NO_5 nrf24l01_STATUS_RX_P_NO_4 nrf24l01_STATUS_RX_P_NO_3 nrf24l01_STATUS_RX_P_NO_2 nrf24l01_STATUS_RX_P_NO_1 nrf24l01_STATUS_RX_P_NO_0 nrf24l01_STATUS_TX_FULL
0x20 0x10 0x0E 0x0E 0x0C 0x0A 0x08 0x06 0x04 0x02 0x00 0x01
//OBSERVE_TX register bitwise definitions #define nrf24l01_OBSERVE_TX_PLOS_CNT 0xF0 #define nrf24l01_OBSERVE_TX_ARC_CNT 0x0F //CD register bitwise definitions #define nrf24l01_CD_RESERVED 0xFE #define nrf24l01_CD_CD 0x01 //RX_PW_P0 register bitwise definitions #define nrf24l01_RX_PW_P0_RESERVED 0xC0 //RX_PW_P0 register bitwise definitions #define nrf24l01_RX_PW_P0_RESERVED 0xC0 //RX_PW_P1 register bitwise definitions #define nrf24l01_RX_PW_P1_RESERVED 0xC0 //RX_PW_P2 register bitwise definitions #define nrf24l01_RX_PW_P2_RESERVED 0xC0 //RX_PW_P3 register bitwise definitions #define nrf24l01_RX_PW_P3_RESERVED 0xC0 //RX_PW_P4 register bitwise definitions #define nrf24l01_RX_PW_P4_RESERVED 0xC0 //RX_PW_P5 register bitwise definitions #define nrf24l01_RX_PW_P5_RESERVED 0xC0 //FIFO_STATUS register bitwise definitions #define nrf24l01_FIFO_STATUS_RESERVED 0x8C #define nrf24l01_FIFO_STATUS_TX_REUSE 0x40 #define nrf24l01_FIFO_STATUS_TX_FULL 0x20 #define nrf24l01_FIFO_STATUS_TX_EMPTY 0x10 #define nrf24l01_FIFO_STATUS_RX_FULL 0x02 #define nrf24l01_FIFO_STATUS_RX_EMPTY 0x01 //////////////////////////////////////////////////////////////////////////////////// // Function declarations // // Below are all function definitions contained in the library. Please see // nrf24l01.c for comments regarding the usage of each function. //////////////////////////////////////////////////////////////////////////////////// //initialization functions void nrf24l01_initialize(unsigned char config, unsigned char opt_rx_standby_mode, unsigned char en_aa, unsigned char en_rxaddr, unsigned char setup_aw, unsigned char setup_retr, unsigned char rf_ch, unsigned char rf_setup, unsigned char * rx_addr_p0, unsigned char * rx_addr_p1, unsigned char rx_addr_p2, unsigned char rx_addr_p3, unsigned char rx_addr_p4, unsigned char rx_addr_p5,
34
unsigned char * tx_addr, unsigned char rx_pw_p0, unsigned char rx_pw_p1, unsigned char rx_pw_p2, unsigned char rx_pw_p3, unsigned char rx_pw_p4, unsigned char rx_pw_p5); void nrf24l01_initialize_debug(bool rx, unsigned char p0_payload_width, bool enable_auto_ack); void nrf24l01_initialize_debug_lite(bool rx, unsigned char p0_payload_width); //power-up, power-down functions void nrf24l01_power_up(bool rx_active_mode); void nrf24l01_power_up_param(bool rx_active_mode, unsigned char config); void nrf24l01_power_down(); void nrf24l01_power_down_param(unsigned char config); //SPI commands defined by the spec //for regnumber values, see section above titled "register definitions" //all functions return the STATUS register unsigned char nrf24l01_write_register(unsigned char regnumber, unsigned char * data, unsigned int len); unsigned char nrf24l01_read_register(unsigned char regnumber, unsigned char * data, unsigned int len); unsigned char nrf24l01_write_tx_payload(unsigned char * data, unsigned int len, bool transmit); unsigned char nrf24l01_read_rx_payload(unsigned char * data, unsigned int len); unsigned char nrf24l01_flush_tx(); unsigned char nrf24l01_flush_rx(); unsigned char nrf24l01_reuse_tx_pl(); unsigned char nrf24l01_nop(); //RX/TX setting functions void nrf24l01_set_as_rx(bool rx_active_mode); void nrf24l01_set_as_rx_param(bool rx_active_mode, unsigned char config); void nrf24l01_rx_standby_to_active(); void nrf24l01_rx_active_to_standby(); void nrf24l01_set_as_tx(); void nrf24l01_set_as_tx_param(unsigned char config); //register-oriented get/set functions for commonly-used registers during operation unsigned char nrf24l01_get_config(); void nrf24l01_set_config(unsigned char config); unsigned char nrf24l01_get_rf_ch(); void nrf24l01_set_rf_ch(unsigned char channel); unsigned char nrf24l01_get_status(); unsigned char nrf24l01_get_observe_tx(); void nrf24l01_set_rx_addr(unsigned char * address, unsigned int len, unsigned char rxpipenum); void nrf24l01_set_tx_addr(unsigned char * address, unsigned int len); void nrf24l01_set_rx_pw(unsigned char payloadwidth, unsigned char rxpipenum); unsigned char nrf24l01_get_rx_pw(unsigned char rxpipenum); unsigned char nrf24l01_get_fifo_status(); //auto-ack and pipe-related functions bool nrf24l01_aa_enabled(unsigned char rxpipenum); void nrf24l01_aa_enable(unsigned char rxpipenum); void nrf24l01_aa_disable(unsigned char rxpipenum); bool nrf24l01_rx_pipe_enabled(unsigned char rxpipenum); void nrf24l01_rx_pipe_enable(unsigned char rxpipenum); void nrf24l01_rx_pipe_disable(unsigned char rxpipenum); unsigned char nrf24l01_get_plos_cnt(); void nrf24l01_clear_plos_cnt(); void nrf24l01_clear_plos_cnt_param(unsigned char rf_ch); unsigned char nrf24l01_get_arc_cnt(); //utility functions bool nrf24l01_cd_active(); void nrf24l01_clear_flush(); unsigned char nrf24l01_get_rx_pipe(); unsigned char nrf24l01_get_rx_pipe_from_status(unsigned char status); void nrf24l01_get_all_registers(unsigned char * data); //interrupt check/clear functions
35
bool bool bool bool void void void void
nrf24l01_irq_pin_active(); nrf24l01_irq_rx_dr_active(); nrf24l01_irq_tx_ds_active(); nrf24l01_irq_max_rt_active(); nrf24l01_irq_clear_all(); nrf24l01_irq_clear_rx_dr(); nrf24l01_irq_clear_tx_ds(); nrf24l01_irq_clear_max_rt();
//FIFO_STATUS check functions bool nrf24l01_fifo_tx_reuse(); bool nrf24l01_fifo_tx_full(); bool nrf24l01_fifo_tx_empty(); bool nrf24l01_fifo_rx_full(); bool nrf24l01_fifo_rx_empty(); //IO void void void void void bool bool
interface-related functions nrf24l01_transmit(); nrf24l01_clear_ce(); nrf24l01_set_ce(); nrf24l01_clear_csn(); nrf24l01_set_csn(); nrf24l01_ce_pin_active(); nrf24l01_csn_pin_active();
//low-level functions for library use only unsigned char nrf24l01_execute_command(unsigned char instruction, unsigned char * data, unsigned int len, bool copydata); void nrf24l01_spi_send_read(unsigned char * data, unsigned int len, bool copydata); #endif /*NRF24L01_H_*/
36
8.3 Appendix C: Transmitter NRF24L01 Code /****************************************************************************** * * File: nrf24l01.c * * Copyright S. Brennen Ball, 2006-2007 * Mario Limonciello, 2007 * * The author provides no guarantees, warantees, or promises, implied or * otherwise. By using this software you agree to indemnify the author * of any damages incurred by using it. * *****************************************************************************/ #include "nrf24l01.h" //initializes only the CONFIG register and pipe 0's payload width //the primary purpose of this function is to allow users with microcontrollers with // extremely small program memories to still be able to init their 24L01. This code // should have a smaller footprint than the above init functions. //when using this method, the 24L01 MUST have its default configuration loaded // in all registers to work. It is recommended that the device be reset or // have its power cycled immediately before this code is run. //in normal circumstances, the user should use nrf24l01_initialize() rather than this // function, since this function does not set all of the register values. void nrf24l01_initialize_debug_lite(bool rx, unsigned char p0_payload_width) { unsigned char config; config = nrf24l01_CONFIG_DEFAULT_VAL; if(rx != false) config |= nrf24l01_CONFIG_PRIM_RX; nrf24l01_write_register(nrf24l01_RX_PW_P0, &p0_payload_width, 1); nrf24l01_power_up_param(true, config); } //powers up the 24L01 with all necessary delays //this function takes the existing contents of the CONFIG register and sets the PWR_UP //the argument rx_active_mode is only used if the user is setting up the // 24L01 as a receiver. If the argument is false, the receiver will remain in // standby mode and not monitor for packets. If the argument is true, the CE // pin will be set and the 24L01 will monitor for packets. In TX mode, the value // of this argument is insignificant. //note: if the read value of the CONFIG register already has the PWR_UP bit set, this function // exits in order to not make an unecessary register write. void nrf24l01_power_up(bool rx_active_mode) { unsigned char config; nrf24l01_read_register(nrf24l01_CONFIG, &config, 1); if((config & nrf24l01_CONFIG_PWR_UP) != 0) return; config |= nrf24l01_CONFIG_PWR_UP; nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); delay_us(1500); if((config & nrf24l01_CONFIG_PRIM_RX) == 0) nrf24l01_clear_ce(); else { if(rx_active_mode != false) nrf24l01_set_ce(); else nrf24l01_clear_ce();
37
} } //powers up the 24L01 with all necessary delays //this function allows the user to set the contents of the CONFIG register, but the function // sets the PWR_UP bit in the CONFIG register, so the user does not need to. //the argument rx_active_mode is only used if the user is setting up the // 24L01 as a receiver. If the argument is false, the receiver will remain in // standby mode and not monitor for packets. If the argument is true, the CE // pin will be set and the 24L01 will monitor for packets. In TX mode, the value // of this argument is insignificant. void nrf24l01_power_up_param(bool rx_active_mode, unsigned char config) { unsigned char test, test2; config |= nrf24l01_CONFIG_PWR_UP; nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); delay_us(1500); if((config & nrf24l01_CONFIG_PRIM_RX) == 0) nrf24l01_clear_ce(); else { if(rx_active_mode != false) nrf24l01_set_ce(); else nrf24l01_clear_ce(); } } //powers down the 24L01 //this function takes the existing contents of the CONFIG register and simply // clears the PWR_UP bit in the CONFIG register. //note: if the read value of the CONFIG register already has the PWR_UP bit cleared, this // function exits in order to not make an unecessary register write. void nrf24l01_power_down() { unsigned char config; nrf24l01_read_register(nrf24l01_CONFIG, &config, 1); if((config & nrf24l01_CONFIG_PWR_UP) == 0) return; config &= (~nrf24l01_CONFIG_PWR_UP); nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); nrf24l01_clear_ce(); } //powers down the 24L01 //this function allows the user to set the contents of the CONFIG register, but the function // clears the PWR_UP bit in the CONFIG register, so the user does not need to. void nrf24l01_power_down_param(unsigned char config) { config &= (~nrf24l01_CONFIG_PWR_UP); nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); nrf24l01_clear_ce(); }
//sets up the 24L01 as a transmitter //this function takes the existing contents of the CONFIG register and simply // clears the PRIM_RX bit in the CONFIG register. //note: if the read value of the CONFIG register already has the PRIM_RX bit cleared, this
38
// function exits in order to not make an unecessary register write. void nrf24l01_set_as_tx() { unsigned char config; nrf24l01_read_register(nrf24l01_CONFIG, &config, 1); if((config & nrf24l01_CONFIG_PRIM_RX) == 0) return; config &= (~nrf24l01_CONFIG_PRIM_RX); nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); nrf24l01_clear_ce(); } //sets up the 24L01 as a transmitter //this function allows the user to set the contents of the CONFIG register, but the function // clears the PRIM_RX bit in the CONFIG register, so the user does not need to. void nrf24l01_set_as_tx_param(unsigned char config) { config &= ~(nrf24l01_CONFIG_PRIM_RX); if((config & nrf24l01_CONFIG_PWR_UP) != 0) nrf24l01_power_up_param(false, config); else nrf24l01_power_down_param(config); } //executes the W_REGISTER SPI operation //unsigned char regnumber indicates the register number assigned by the nrf24l01 specification. // For regnumber values, see section titled "register definitions" in nrf24l01.h. //unsigned char * data should be of size 1 for all register writes except for RX_ADDR_P0, RX_ADDR_P1, // and TX_ADDR. The size of data should be set according to the user-specified size of the address // length for the register the address is being sent to. //unsigned int len is always the size of unsigned char * data. For example, if data is declared as // data[6], len should equal 6. //returns the value of the STATUS register unsigned char nrf24l01_write_register(unsigned char regnumber, unsigned char * data, unsigned int len) { return nrf24l01_execute_command(nrf24l01_W_REGISTER | (regnumber & nrf24l01_W_REGISTER_DATA), data, len, false); } //executes the R_REGISTER SPI operation //unsigned char regnumber indicates the register number assigned by the nrf24l01 specification. // For regnumber values, see section titled "register definitions" in nrf24l01.h. //unsigned char * data should be of size 1 for all register writes except for RX_ADDR_P0, RX_ADDR_P1, // and TX_ADDR. The size of data should be set according to the user-specified size of the address // length for the register the address is being read from. //unsigned int len is always the size of unsigned char * data. For example, if data is declared as // data[6], len = 6. //returns the value of the STATUS register unsigned char nrf24l01_read_register(unsigned char regnumber, unsigned char * data, unsigned int len) { return nrf24l01_execute_command(regnumber & nrf24l01_R_REGISTER_DATA, data, len, true); } //executes the W_TX_PAYLOAD operation //unsigned char * data is the actual payload to be sent to the nrf24l01. //unsigned int len is the length of the payload being sent (this should be sized // according to the payload length specified by the receiving nrf24l01).
39
//if bool transmit is true, the nrf24l01 immediately transmits the data in the payload. // if false, the user must use the nrf24l01_transmit() function to send the payload. //returns the value of the STATUS register unsigned char nrf24l01_write_tx_payload(unsigned char * data, unsigned int len, bool transmit) { unsigned char status; status = nrf24l01_execute_command(nrf24l01_W_TX_PAYLOAD, data, len, false); if(transmit == true) nrf24l01_transmit(); return status; } //executes the FLUSH_TX SPI operation //this funciton empties the contents of the TX FIFO //returns the value of the STATUS register unsigned char nrf24l01_flush_tx() { return nrf24l01_execute_command(nrf24l01_FLUSH_TX, NULL, 0, true); } //executes the REUSE_TX_PL SPI operation //this funciton allows the user to constantly send a packet repeatedly when issued. //returns the value of the STATUS register unsigned char nrf24l01_reuse_tx_pl() { return nrf24l01_execute_command(nrf24l01_REUSE_TX_PL, NULL, 0, true); } //executes the FLUSH_TX SPI operation //this funciton does nothing //returns the value of the STATUS register unsigned char nrf24l01_nop() { return nrf24l01_execute_command(nrf24l01_NOP, NULL, 0, true); } //transmits the current tx payload void nrf24l01_transmit() { nrf24l01_set_ce(); delay_us(10); nrf24l01_clear_ce(); } //clears the pin on the host microcontroller that is attached to the 24l01's CE pin void nrf24l01_clear_ce() { nrf24l01_CE_IOREGISTER &= ~nrf24l01_CE_PINMASK; } //sets the pin on the host microcontroller that is attached to the 24l01's CE pin void nrf24l01_set_ce() { nrf24l01_CE_IOREGISTER |= nrf24l01_CE_PINMASK; } //returns true if CE is high, false if not bool nrf24l01_ce_pin_active() { if((nrf24l01_CE_IOREGISTER & nrf24l01_CE_PINMASK) != 0) return true; else return false; } //sets the pin on the host microcontroller that is attached to the 24l01's CSN pin void nrf24l01_clear_csn() {
40
nrf24l01_CSN_IOREGISTER &= ~nrf24l01_CSN_PINMASK; } //clears the pin on the host microcontroller that is attached to the 24l01's CSN pin void nrf24l01_set_csn() { nrf24l01_CSN_IOREGISTER |= nrf24l01_CSN_PINMASK; } //returns true if CSN is high, false if not bool nrf24l01_csn_pin_active() { if((nrf24l01_CSN_IOREGISTER & nrf24l01_CSN_PINMASK) != 0) return true; else return false; } //sets the TX address in the TX_ADDR register //unsigned char * address is the actual address to be used. It should be sized // according to the tx_addr length specified to the nrf24l01. //unsigned int len is the length of the address. Its value should be specified // according to the tx_addr length specified to the nrf24l01. void nrf24l01_set_tx_addr(unsigned char * address, unsigned int len) { nrf24l01_write_register(nrf24l01_TX_ADDR, address, len); } //returns the value of the CONFIG register unsigned char nrf24l01_get_config() { unsigned char data; nrf24l01_read_register(nrf24l01_CONFIG, &data, 1); return data; } //sets the value of the CONFIG register void nrf24l01_set_config(unsigned char config) { nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); } //unsigned char channel is the channel to be changed to. void nrf24l01_set_rf_ch(unsigned char channel) { unsigned char data; data = channel & ~nrf24l01_RF_CH_RESERVED; nrf24l01_write_register(nrf24l01_RF_CH, &data, 1); } //clears the PLOS_CNT field of the OBSERVE_TX register //this function makes a read of the current value of RF_CH and // simply writes it back to the register, clearing PLOS_CNT void nrf24l01_clear_plos_cnt() { unsigned char data; nrf24l01_read_register(nrf24l01_RF_CH, &data, 1); nrf24l01_write_register(nrf24l01_RF_CH, &data, 1); } //returns the status of the CD register (true if carrier detect [CD] is // active, false if not) bool nrf24l01_cd_active() { unsigned char data;
41
nrf24l01_read_register(nrf24l01_CD, &data, 1); return data; } //return the value of the status register unsigned char nrf24l01_get_status() { return nrf24l01_nop(); } //returns true if TX_REUSE bit in FIFO_STATUS register is set, false otherwise bool nrf24l01_fifo_tx_reuse() { unsigned char data; nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1); return (bool)(data & nrf24l01_FIFO_STATUS_TX_REUSE); } //returns true if TX_FULL bit in FIFO_STATUS register is set, false otherwise bool nrf24l01_fifo_tx_full() { unsigned char data; nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1); return (bool)(data & nrf24l01_FIFO_STATUS_TX_FULL); } //returns true if TX_EMPTY bit in FIFO_STATUS register is set, false otherwise bool nrf24l01_fifo_tx_empty() { unsigned char data; nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1); return (bool)(data & nrf24l01_FIFO_STATUS_TX_EMPTY); } //returns true if IRQ pin is low, false otherwise bool nrf24l01_irq_pin_active() { if((nrf24l01_IRQ_IOREGISTER & nrf24l01_IRQ_PINMASK) != 0) return false; else return true; } //returns true if TX_DS interrupt is active, false otherwise bool nrf24l01_irq_tx_ds_active() { return (nrf24l01_get_status() & nrf24l01_STATUS_TX_DS); } //returns true if MAX_RT interrupt is active, false otherwise bool nrf24l01_irq_max_rt_active() { return (nrf24l01_get_status() & nrf24l01_STATUS_MAX_RT); } //clear all interrupts in the status register void nrf24l01_irq_clear_all() { unsigned char data = nrf24l01_STATUS_RX_DR | nrf24l01_STATUS_TX_DS | nrf24l01_STATUS_MAX_RT; nrf24l01_write_register(nrf24l01_STATUS, &data, 1); } //clears only the TX_DS interrupt
42
void nrf24l01_irq_clear_tx_ds() { unsigned char data = nrf24l01_STATUS_TX_DS; nrf24l01_write_register(nrf24l01_STATUS, &data, 1); } //clears only the MAX_RT interrupt void nrf24l01_irq_clear_max_rt() { unsigned char data = nrf24l01_STATUS_MAX_RT; nrf24l01_write_register(nrf24l01_STATUS, &data, 1); } //flush both fifos and clear interrupts void nrf24l01_clear_flush() { nrf24l01_flush_tx(); nrf24l01_irq_clear_all(); } //low-level spi send function for library use //the user should not call this function directly, but rather use one of the 8 SPI data instructions unsigned char nrf24l01_execute_command(unsigned char instruction, unsigned char * data, unsigned int len, bool copydata) { unsigned char status; nrf24l01_clear_csn(); status = instruction; nrf24l01_spi_send_read(&status, 1, true); nrf24l01_spi_send_read(data, len, copydata); nrf24l01_set_csn(); return status; } //low-level spi send function for library use //the user should not call this function directly, but rather use one of the 8 SPI data instructions void nrf24l01_spi_send_read(unsigned char * data, unsigned int len, bool copydata) { unsigned int count; unsigned char tempbyte; for(count = 0; count < len; count++) { if(copydata != false) data[count] = spi_send_read_byte(data[count]); else { tempbyte = data[count]; spi_send_read_byte(tempbyte); } } }
43
8.4 Appendix D: Receiver NR424L01 Code /****************************************************************************** * * File: nrf24l01.c * * Copyright S. Brennen Ball, 2006-2007 * * The author provides no guarantees, warantees, or promises, implied or * otherwise. By using this software you agree to indemnify the author * of any damages incurred by using it. * *****************************************************************************/ #include "nrf24l01.h" //initializes only the CONFIG register and pipe 0's payload width //the primary purpose of this function is to allow users with microcontrollers with // extremely small program memories to still be able to init their 24L01. This code // should have a smaller footprint than the above init functions. //when using this method, the 24L01 MUST have its default configuration loaded // in all registers to work. It is recommended that the device be reset or // have its power cycled immediately before this code is run. //in normal circumstances, the user should use nrf24l01_initialize() rather than this // function, since this function does not set all of the register values. void nrf24l01_initialize_debug_lite(bool rx, unsigned char p0_payload_width) { unsigned char config; config = nrf24l01_CONFIG_DEFAULT_VAL; if(rx != false) config |= nrf24l01_CONFIG_PRIM_RX; nrf24l01_write_register(nrf24l01_RX_PW_P0, &p0_payload_width, 1); nrf24l01_power_up_param(true, config); } //powers up the 24L01 with all necessary delays //this function takes the existing contents of the CONFIG register and sets the PWR_UP //the argument rx_active_mode is only used if the user is setting up the // 24L01 as a receiver. If the argument is false, the receiver will remain in // standby mode and not monitor for packets. If the argument is true, the CE // pin will be set and the 24L01 will monitor for packets. In TX mode, the value // of this argument is insignificant. //note: if the read value of the CONFIG register already has the PWR_UP bit set, this function // exits in order to not make an unecessary register write. void nrf24l01_power_up(bool rx_active_mode) { unsigned char config; nrf24l01_read_register(nrf24l01_CONFIG, &config, 1); if((config & nrf24l01_CONFIG_PWR_UP) != 0) return; config |= nrf24l01_CONFIG_PWR_UP; nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); delay_us(1500); if((config & nrf24l01_CONFIG_PRIM_RX) == 0) nrf24l01_clear_ce(); else { if(rx_active_mode != false) nrf24l01_set_ce(); else nrf24l01_clear_ce(); }
44
} //powers up the 24L01 with all necessary delays //this function allows the user to set the contents of the CONFIG register, but the function // sets the PWR_UP bit in the CONFIG register, so the user does not need to. //the argument rx_active_mode is only used if the user is setting up the // 24L01 as a receiver. If the argument is false, the receiver will remain in // standby mode and not monitor for packets. If the argument is true, the CE // pin will be set and the 24L01 will monitor for packets. In TX mode, the value // of this argument is insignificant. void nrf24l01_power_up_param(bool rx_active_mode, unsigned char config) { unsigned char test, test2; config |= nrf24l01_CONFIG_PWR_UP; nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); delay_us(1500); if((config & nrf24l01_CONFIG_PRIM_RX) == 0) nrf24l01_clear_ce(); else { if(rx_active_mode != false) nrf24l01_set_ce(); else nrf24l01_clear_ce(); } } //powers down the 24L01 //this function takes the existing contents of the CONFIG register and simply // clears the PWR_UP bit in the CONFIG register. //note: if the read value of the CONFIG register already has the PWR_UP bit cleared, this // function exits in order to not make an unecessary register write. void nrf24l01_power_down() { unsigned char config; nrf24l01_read_register(nrf24l01_CONFIG, &config, 1); if((config & nrf24l01_CONFIG_PWR_UP) == 0) return; config &= (~nrf24l01_CONFIG_PWR_UP); nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); nrf24l01_clear_ce(); } //powers down the 24L01 //this function allows the user to set the contents of the CONFIG register, but the function // clears the PWR_UP bit in the CONFIG register, so the user does not need to. void nrf24l01_power_down_param(unsigned char config) { config &= (~nrf24l01_CONFIG_PWR_UP); nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); nrf24l01_clear_ce(); } //takes a 24L01 that is already in RX standby mode and puts it in active RX mode void nrf24l01_rx_standby_to_active() { nrf24l01_set_ce(); } //takes a 24L01 that is already in active RX mode and puts it in RX standy mode
45
void nrf24l01_rx_active_to_standby() { nrf24l01_clear_ce(); } //executes the W_REGISTER SPI operation //unsigned char regnumber indicates the register number assigned by the nrf24l01 specification. // For regnumber values, see section titled "register definitions" in nrf24l01.h. //unsigned char * data should be of size 1 for all register writes except for RX_ADDR_P0, RX_ADDR_P1, // and TX_ADDR. The size of data should be set according to the user-specified size of the address // length for the register the address is being sent to. //unsigned int len is always the size of unsigned char * data. For example, if data is declared as // data[6], len should equal 6. //returns the value of the STATUS register unsigned char nrf24l01_write_register(unsigned char regnumber, unsigned char * data, unsigned int len) { return nrf24l01_execute_command(nrf24l01_W_REGISTER | (regnumber & nrf24l01_W_REGISTER_DATA), data, len, false); } //executes the R_REGISTER SPI operation //unsigned char regnumber indicates the register number assigned by the nrf24l01 specification. // For regnumber values, see section titled "register definitions" in nrf24l01.h. //unsigned char * data should be of size 1 for all register writes except for RX_ADDR_P0, RX_ADDR_P1, // and TX_ADDR. The size of data should be set according to the user-specified size of the address // length for the register the address is being read from. //unsigned int len is always the size of unsigned char * data. For example, if data is declared as // data[6], len = 6. //returns the value of the STATUS register unsigned char nrf24l01_read_register(unsigned char regnumber, unsigned char * data, unsigned int len) { return nrf24l01_execute_command(regnumber & nrf24l01_R_REGISTER_DATA, data, len, true); } //executes the R_RX_PAYLOAD instruction //unsigned char * data is the actual payload that has been received by the nrf24l01. // The user must size data according to the payload width specified to the nrf24l01. // This variable is filled by this function, so individual byte values need not be // initialized by the user. //unsigned int len is the length of the payload being clocked out of the nrf24l01 (this // should be sized according to the payload length specified to the nrf24l01). //returns the value of the STATUS register unsigned char nrf24l01_read_rx_payload(unsigned char * data, unsigned int len) { unsigned char status; nrf24l01_clear_ce(); status = nrf24l01_execute_command(nrf24l01_R_RX_PAYLOAD, data, len, true); nrf24l01_set_ce(); return status; } //executes the FLUSH_TX SPI operation //this funciton does nothing //returns the value of the STATUS register unsigned char nrf24l01_nop() { return nrf24l01_execute_command(nrf24l01_NOP, NULL, 0, true); } //executes the FLUSH_RX SPI operation //this funciton empties the contents of the RX FIFO
46
//returns the value of the STATUS register unsigned char nrf24l01_flush_rx() { return nrf24l01_execute_command(nrf24l01_FLUSH_RX, NULL, 0, true); } //clears the pin on the host microcontroller that is attached to the 24l01's CE pin void nrf24l01_clear_ce() { nrf24l01_CE_IOREGISTER &= ~nrf24l01_CE_PINMASK; } //sets the pin on the host microcontroller that is attached to the 24l01's CE pin void nrf24l01_set_ce() { nrf24l01_CE_IOREGISTER |= nrf24l01_CE_PINMASK; } //returns true if CE is high, false if not bool nrf24l01_ce_pin_active() { if((nrf24l01_CE_IOREGISTER & nrf24l01_CE_PINMASK) != 0) return true; else return false; } //sets the pin on the host microcontroller that is attached to the 24l01's CSN pin void nrf24l01_clear_csn() { nrf24l01_CSN_IOREGISTER &= ~nrf24l01_CSN_PINMASK; } //clears the pin on the host microcontroller that is attached to the 24l01's CSN pin void nrf24l01_set_csn() { nrf24l01_CSN_IOREGISTER |= nrf24l01_CSN_PINMASK; } //returns true if CSN is high, false if not bool nrf24l01_csn_pin_active() { if((nrf24l01_CSN_IOREGISTER & nrf24l01_CSN_PINMASK) != 0) return true; else return false; } //sets the RX address in the RX_ADDR register that is offset by rxpipenum //unsigned char * address is the actual address to be used. It should be sized // according to the rx_addr length that is being filled. //unsigned int len is the length of the address. Its value should be specified // according to the rx_addr length specified to the nrf24l01. //unsigned char rxpipenum is the pipe number (zero to five) whose address is being // specified. If an invalid address (greater than five) is supplied, the function // does nothing. void nrf24l01_set_rx_addr(unsigned char * address, unsigned int len, unsigned char rxpipenum) { if(rxpipenum > 5) return; nrf24l01_write_register(nrf24l01_RX_ADDR_P0 + rxpipenum, address, len); } //sets the RX payload width on the pipe offset by rxpipenum //unsigned char payloadwidth is the length of the payload for the pipe referenced in // rxpipenum. It must be less than or equal to 32. If an invalid payload width is // specified, the function does nothing. //unsigned char rxpipenum is the pipe number (zero to five) whose address is being // specified. If an invalid address (greater than five) is supplied, the function // does nothing.
47
void nrf24l01_set_rx_pw(unsigned char payloadwidth, unsigned char rxpipenum) { if((rxpipenum > 5) || (payloadwidth > 32)) return; nrf24l01_write_register(nrf24l01_RX_PW_P0 + rxpipenum, &payloadwidth, 1); } //gets the RX payload width on the pipe offset by rxpipenum //unsigned char rxpipenum is the pipe number (zero to five) whose address is being // specified. If an invalid address (greater than five) is supplied, the function // does nothing. unsigned char nrf24l01_get_rx_pw(unsigned char rxpipenum) { unsigned char data; if((rxpipenum > 5)) return; nrf24l01_read_register(nrf24l01_RX_PW_P0 + rxpipenum, &data, 1); return data; } //returns the value of the CONFIG register unsigned char nrf24l01_get_config() { unsigned char data; nrf24l01_read_register(nrf24l01_CONFIG, &data, 1); return data; } //sets the value of the CONFIG register void nrf24l01_set_config(unsigned char config) { nrf24l01_write_register(nrf24l01_CONFIG, &config, 1); } //returns the current RF channel in RF_CH register unsigned char nrf24l01_get_rf_ch() { unsigned char data; nrf24l01_read_register(nrf24l01_RF_CH, &data, 1); return data; } //unsigned char channel is the channel to be changed to. void nrf24l01_set_rf_ch(unsigned char channel) { unsigned char data; data = channel & ~nrf24l01_RF_CH_RESERVED; nrf24l01_write_register(nrf24l01_RF_CH, &data, 1); } //returns the current PLOS_CNT value in OBSERVE_TX register unsigned char nrf24l01_get_plos_cnt() { unsigned char data; nrf24l01_read_register(nrf24l01_OBSERVE_TX, &data, 1); return ((data & nrf24l01_OBSERVE_TX_PLOS_CNT) >> 4); } //clears the PLOS_CNT field of the OBSERVE_TX register
48
//this function makes a read of the current value of RF_CH and // simply writes it back to the register, clearing PLOS_CNT void nrf24l01_clear_plos_cnt() { unsigned char data; nrf24l01_read_register(nrf24l01_RF_CH, &data, 1); nrf24l01_write_register(nrf24l01_RF_CH, &data, 1); } //clears the PLOS_CNT field of the OBSERVE_TX register //this function allows the user to set the RF_CH register by using // the argument in the function during the PLOS_CNT clearing process void nrf24l01_clear_plos_cnt_param(unsigned char rf_ch) { nrf24l01_write_register(nrf24l01_RF_CH, &rf_ch, 1); } //returns the current ARC_CNT value in OBSERVE_TX register unsigned char nrf24l01_get_arc_cnt() { unsigned char data; nrf24l01_read_register(nrf24l01_OBSERVE_TX, &data, 1); return (data & nrf24l01_OBSERVE_TX_ARC_CNT); } //returns the status of the CD register (true if carrier detect [CD] is // active, false if not) bool nrf24l01_cd_active() { unsigned char data; nrf24l01_read_register(nrf24l01_CD, &data, 1); return data; } //return the value of the status register unsigned char nrf24l01_get_status() { return nrf24l01_nop(); } //returns true if RX_FULL bit in FIFO_STATUS register is set, false otherwise bool nrf24l01_fifo_rx_full() { unsigned char data; nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1); return (bool)(data & nrf24l01_FIFO_STATUS_RX_FULL); } //returns true if RX_EMPTYE bit in FIFO_STATUS register is set, false otherwise bool nrf24l01_fifo_rx_empty() { unsigned char data; nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1); return (bool)(data & nrf24l01_FIFO_STATUS_RX_EMPTY); } //returns true if IRQ pin is low, false otherwise bool nrf24l01_irq_pin_active() { if((nrf24l01_IRQ_IOREGISTER & nrf24l01_IRQ_PINMASK) != 0) return false; else
49
return true; } //returns true if RX_DR interrupt is active, false otherwise bool nrf24l01_irq_rx_dr_active() { return (nrf24l01_get_status() & nrf24l01_STATUS_RX_DR); } //returns true if MAX_RT interrupt is active, false otherwise bool nrf24l01_irq_max_rt_active() { return (nrf24l01_get_status() & nrf24l01_STATUS_MAX_RT); } //clear all interrupts in the status register void nrf24l01_irq_clear_all() { unsigned char data = nrf24l01_STATUS_RX_DR | nrf24l01_STATUS_TX_DS | nrf24l01_STATUS_MAX_RT; nrf24l01_write_register(nrf24l01_STATUS, &data, 1); } //clears only the RX_DR interrupt void nrf24l01_irq_clear_rx_dr() { unsigned char data = nrf24l01_STATUS_RX_DR; nrf24l01_write_register(nrf24l01_STATUS, &data, 1); } //clears only the MAX_RT interrupt void nrf24l01_irq_clear_max_rt() { unsigned char data = nrf24l01_STATUS_MAX_RT; nrf24l01_write_register(nrf24l01_STATUS, &data, 1); } //returns the current pipe in the 24L01's STATUS register unsigned char nrf24l01_get_rx_pipe() { return nrf24l01_get_rx_pipe_from_status(nrf24l01_get_status()); } unsigned char nrf24l01_get_rx_pipe_from_status(unsigned char status) { return ((status & 0xE) >> 1); } //flush both fifos and clear interrupts void nrf24l01_clear_flush() { nrf24l01_flush_rx(); nrf24l01_irq_clear_all(); } //low-level spi send function for library use //the user should not call this function directly, but rather use one of the 8 SPI data instructions unsigned char nrf24l01_execute_command(unsigned char instruction, unsigned char * data, unsigned int len, bool copydata) { unsigned char status; nrf24l01_clear_csn(); status = instruction; nrf24l01_spi_send_read(&status, 1, true); nrf24l01_spi_send_read(data, len, copydata);
50
nrf24l01_set_csn(); return status; } //low-level spi send function for library use //the user should not call this function directly, but rather use one of the 8 SPI data instructions void nrf24l01_spi_send_read(unsigned char * data, unsigned int len, bool copydata) { unsigned int count; unsigned char tempbyte; for(count = 0; count < len; count++) { if(copydata != false) data[count] = spi_send_read_byte(data[count]); else { tempbyte = data[count]; spi_send_read_byte(tempbyte); } } }
51
8.5 Appendix E: Transmitter Main Code /****************************************************************************** * * DEC0701: Automated Mailbox * Transmitter Code * File: main.c * * Copyright S. Mario Limonciello, 2007 * * The author provides no guarantees, warantees, or promises, implied or * otherwise. By using this software you agree to indemnify the author * of any damages incurred by using it. * *****************************************************************************/ #include #include #define F_CPU 100000UL #include #include
// Defines pins, ports, etc to make programs easier to read // Sets up the default speed for delay.h //power management functionality
//basic nomenclature: //Setting up a DDRX register: //When setting particular bits high, those become output.
Setting low makes them inputs
//Output on a PORTX pin: //If the DDRX register is set high, setting a pin high causes output //Sample on leading rising edge, setup on trailing falling edge #define SPIMODE 0 void void void void void
check_and_send(void); SetupInterrupts(void); InitLED(void); InitSPI(void); PowerManage(void);
static unsigned char data; int main() { /* initialized to 0x10 to send once when starting */ data = 0x10; /* Disable unnecessary parts for better power management */ PowerManage(); /* Prepare interrupts for SPI and edge detection */ SetupInterrupts(); /* SPI Driver Initialization */ spiX_initmaster(SPIMODE); //makes us master SPI device DDRB |= _BV(PB4) | _BV(PB3); //setup CSN,CE for output /* enable port D's GPIO for output on the IR LEDS */ DDRD= _BV(PD0) | _BV(PD5); while(1) { //If we have been interrupted, do stuff if (data & 0x10) check_and_send(); } return(0); } ISR(INT0_vect) { data |= 0x10;
52
} ISR_ALIAS(INT1_vect,INT0_vect); void check_and_send(void) { //Turn on the Voltage regulator PORTD |= _BV(PD0) | _BV(PD5);
//wait for 6 seconds before running this to make sure the door //is fully closed if it was opened _delay_ms(6000); //only do the processing if the mailbox is closed if (!(PIND & _BV(PD2))) { /* Turn on the transmitter */ nrf24l01_power_up(); //wait for 6 seconds before running this to make sure the //transmitter is powered up _delay_ms(6000); /* initialize our mirf v2 for tx */ nrf24l01_initialize_debug_lite(0, 1); //check what the current flag status is. //only do the IR led stuff if its down if (!(PIND & _BV(PD3))) { if (PIND & _BV(PD1)) data |= 0x01; //item else data &= ~(0x01); //no item } else { data &= ~(0x01); //no item } //always check for battery and transmit this if (PIND & _BV(PD4)) data |= 0x02; //low battery else data &= ~(0x02); //no low battery //flush the TX fifo in case we have a failed transmission from before nrf24l01_flush_tx(); //send payload _delay_us(130); //wait for the other 24L01 to come from standby to RX nrf24l01_write_tx_payload(&data, 1, 1); //wait for payload to be sent while(!(nrf24l01_irq_pin_active() && nrf24l01_irq_tx_ds_active()) && !nrf24l01_irq_max_rt_active()); nrf24l01_irq_clear_all(); //clear interrupts again //Turn off the transmitter nrf24l01_power_down(); } //don't run again until we get another interrupt data &= ~(0x10); //Turn off voltage regulator _delay_ms(1000); PORTD &= ~_BV(PD0) & ~_BV(PD5); //go into sleep mode
53
sleep_mode(); } void SetupInterrupts(void) { //Door Switch (Triggers when switch goes high->low) PCMSK |= _BV(PIND2); // tell pin change mask to listen to pd2 MCUCR |= _BV(ISC01); // interrupt on INT0 pin falling edge (sensor triggered) GIMSK |= _BV(INT0); // enable PCINT interrupt in the general interrupt mask //Flag Switch (Triggers PCMSK |= _BV(PIND3); // MCUCR |= _BV(ISC10); // GIMSK |= _BV(INT1); // sei();
when switch changes) tell pin change mask to listen to pd3 interrupt on INT1 pin any edge (sensor triggered) enable PCINT interrupt in the general interrupt mask
//turns on interrupts generally
} void PowerManage(void) { //Disable analog comparator ACSR |= (1<
54
8.6 Appendix F: Receiver Main Code /****************************************************************************** * * DEC0701: Automated Mailbox * Receiver Code * File: main.c * * Copyright S. Mario Limonciello, 2007 * * The author provides no guarantees, warantees, or promises, implied or * otherwise. By using this software you agree to indemnify the author * of any damages incurred by using it. * *****************************************************************************/
#include #include #define F_CPU 100000UL #include
// Defines pins, ports, etc to make programs easier to read // Sets up the default speed for delay.h
//basic nomenclature: //Setting up a DDRX register: //When setting particular bits high, those become output.
Setting low makes them inputs
//Output on a PORTX pin: //If the DDRX register is set high, setting a pin high causes output //Sample on leading rising edge, setup on trailing falling edge #define SPIMODE 0 int main() { unsigned char data = 0x00; /* SPI Initialization */ spiX_initmaster(SPIMODE); //makes us master SPI device sei(); //turns on interrupts for SPI DDRB |= _BV(PB4) | _BV(PB3); //setup CSN,CE for output //initialize our mirf v2 for rx nrf24l01_initialize_debug_lite(1, 1); //setup LEDs //enable port D's GPIOs for output on the LEDS DDRD= _BV(PD4) | _BV(PD5); //debugging LED. Turns on while we receive data. DDRD |= _BV(PD0); //
SetupInterrupts(); while(1) { //turn off debugging LED PORTD &= ~_BV(PD0); //wait until a packet has been received while(!(nrf24l01_irq_pin_active() && nrf24l01_irq_rx_dr_active())); //once we get an interrupt: //debug code to make sure that we are actually receiving //We will flash as data is received _delay_ms(500); if (PORTD & _BV(PD0)) PORTD &= ~_BV(PD0); else PORTD |= _BV(PD0);
55
nrf24l01_read_rx_payload(&data, 1); //read the packet into data nrf24l01_irq_clear_all(); //clear all interrupts in the 24L01 nrf24l01_flush_rx();//clear the FIFO in case we had any data sitting _delay_us(130); //wait for the other 24L01 to come from standby to RX //got mail //turn on PD4 if (data & 0x01) PORTD |= _BV(PD4); //turn off PD4 else PORTD &= ~_BV(PD4); //low battery //turn on PD5 if (data & 0x02) PORTD |= _BV(PD5); //turn off PD5 else PORTD &= ~_BV(PD5); } return(0); } unsigned char spi1_send_read_byte(unsigned char byte) { //do SPI stuff spiX_put(byte); spiX_wait(); return spiX_get(); }
56
8.7 Appendix G: Schematics 8.7.1 Transmitter
57
8.7.2 Receiver
58
8 Appendix H: Mechanical Drawings 8.8
Figure 7: Color Mailbox diagram
59
Figure 8: Mailbox Diagram
60
Figure 9: Styrofoam Mount
Figure 10: Plexiglass
61
8 Appendix I: Project Schedu 8.9 ules T The dates for the four major milesstones were as fo ollows:
Plaanning
Feb. 2 27, 2007 D Design
May 2, 2007 Implementtation & Testing
Dec. 3, 2007 Docum mentation
Dec. 1 12, 2007
62