Fall 2025 Project
Speed Trap
Table of Contents
Introduction
Overview
Operation of Temperature Monitor
Getting Started
Which Port Bits to Use
Object Detector
Time and Speed Calculations
Timing Indicator
Speed Limit Select Buttons
Adjusting Speed Limits
Non-volatile EEPROM Memory
Speed Range LEDs
Buzzer
Servo Motor Indicating Dial
Software Issues
Building Your Design
Introduction
This semester’s class project is to build a device that measures how fast an object is moving as it passes two sensors and display the speed on a dial-type indicator.
Note: This is a non-trivial project that requires you to not only use the concepts and skills you’ve learned from past labs but to extend that knowledge and use the higher-level understanding of how embedded systems and microcontrollers work to include use of new components and modules.
Getting a full score is not a guarantee. It will require time and intelligent work (not just guess, but verifying on paper and using some of the approaches outlined below). But it is definitely doable. We have broken the project into 2 checkpoints to help limit the scope of your work to more manageable deliverables. Also, note that while we will still provide help in terms of concept and approach, we will leave it to you to do most of the debugging. When you do need help debugging you should have already used the tools at your disposal: the DMM or scope to verify basic signals are as you expect, LCD print statements to see where your code is executing, whether you are getting into an ISR at all, or what the value of various readings/variables is. Test your code little by little (this is the reason we do checkpoints) and do not implement EVERYTHING and only then try to compile. In addition, when something doesn’t work, back out any changes you’ve made (comment certain portions, etc.) to get to a point where you can indicate what does work and then slowly reintroduce the new changes to see where an fissue might lie. If you have not done your own debugging work and just ask for help, we will direct you to go back and perform some of these tasks.
To start, please read the overall project a few times before you even consider thinking about the many details. Get the big picture, hear the terms we use, look for the requirements and/or limitations we specify. Only then consider starting on your project.
For a copy of the project grading sheet, click here
Speed Trap Overview
In its simplest form the speed trap measures how long it takes for an object to pass two sensors and calculates the speed it was travelling. The speed in cm/sec is shown on the LCD display and also on an indicator dial similar to an analog speedometer used on many cars where a pointer rotates to point to the speed. A block diagram of our speed trap is shown below and it will have the following features.
-
Two LED light sources, and two phototransistor light detectors for determining the time it takes for an object to go from the first sensor to the second sensor.
-
An LED to indicate that a speed measurement is in progress.
-
An LCD display for showing the measured time to pass the sensors in msec and the speed in cm/sec. The display is also used for setting two speed limits.
-
A dial-type speedometer display for indicating the speed.
-
Two buttons for selecting which speed limit to adjust
-
A knob for adjusting the high or low speed limits.
-
An RGB LED for comparing the speed measured with the two speed limits.
-
A buzzer for playing an alarm tone.

Operation of the Speed Trap
The following is a description of the operation of the speed trap.
-
The speed trap device is always monitoring the state of the first of the two LED-phototransistor pairs.
-
When the first sensor indicates that the path between its LED and phototransistor has been blocked, one of the Arduino’s timers is started counting at a known rate. It also lights up the yellow timing indicator LED to indicate that a timing event is in progress. When the second sensor shows that the path between its LED and phototransistor has been blocked, the timing stops, and the indicator LED is turned off. The elapsed time should not be shown on the LCD while a timing is going on.
-
Whenever a timing event has completed, the unit converts the timer’s count value to time and displays this time in milliseconds on the LCD display. It also uses this value to determine the speed in cm/sec that the object was moving when traveling between the two sensors and displays this on the LCD. There is no specified distance that you must place your two sensors apart on your breadboard but they must be at least 1.5 inches apart. The holes on the breadboard are 0.1 inches apart and wherever you place the sensors, you will need this distance in order to make the speed calculation.
-
In addition to the speed being displayed on the LCD, the servo motor is used to rotate a pointer to indicate the speed on a dial that shows speeds from 0 to 100 cm/sec. If the speed is above 100 cm/sec it should point to 100.
-
If a timing event starts but the stop sensor has not been activated after 4 seconds, the timing should be terminated and a message is printed on the LCD stating that the timing was terminated.
-
Two buttons are used to select whether the rotary encoder adjusts either the high speed limit or the low speed limit.
-
If the user turns the rotary encoder knob, the selected speed limit is changed up or down and displayed on the LCD. The speed limits are integer values and can change from 1 to 99 cm/sec. The limit values should not go above 99 or below 1 when adjusting the value. In addition, the low limit can not be higher than the high limit, and the high limit can not be lower than the low limit. Both limits can be set to the same value.
-
An RGB LED is used to show how the most rescently measured speed compares to the two speed limits. If it is higher than the high limit the red segment should be lit. If it is lower than the low limit then the blue segment should be lit. If it is equal to or above the low and equal to or below high limits then the green segment should be illuminated. If there has not yet been a speed measurement, the green LED should be on.
-
Whenever either of the speed limit numbers are changed, the new values are stored in the Arduino’s EEPROM non-volatile memory so the values are retained even if the power is turned off. Whenever the speed trap is turned on or restarted, the speed limit values are read from the EEPROM memory making it unnecessary for the user to set the speed limits each time the device is turned on.
-
The buzzer is used to indicate if the last three speed measurements were all too fast or too slow. If the speed just measured, the previous one, and the one before that were all above the high speed limit or were all below the low speed limit then the device plays a short alarm tone on using the buzzer.
-
The LCD display should show the following five items but they can arranged in any manner on the screen. They don’t have to look exactly like the sample shown above or in the video.
-
Elapsed time in milliseconds for the last speed measurement to 1 ms precision (e.g. the 171 on the picture above)
-
The speed calculated for the most recent timing event to 0.1 cm/sec precision. (e.g. the 25.2 on the picture above)
-
The low speed limit as set using the rotary encoder to 1 cm/sec precision.
-
The high speed limit as set using the rotary encode to 1cm/sec precision.
-
An indication as to which speed limit, high or low, will be changed by the rotary encoder.
-
-
The LCD display should always be showing the elapsed time and speed values of the most recent speed measurement even when one of the speed limits is being adjusted. It should not switch to a separate screen for adjusting a speed limit and then switch back when done.
Getting Started
We suggest starting with a copy of your code from Lab 7 since this project uses
the rotary encoder and PWM signals. Download from the class web site the file
project.zip that contains the Project_Answers.txt file with information about
what needs to be demonstrated to show the project is working.
Most of the components used in this project have been used in previous labs, and your C code from the other labs can be reused if that helps. The project involves the use of two new elements:
- the phototransistor light sensor
- the non-volatile memory (EEPROM) on the Arduino
Both are described below.
Which Port Bits to Use
Since the CPs grading the project may wish to try running the code on their project board, it is very important (and required) that all students use the port connections shown below to connect their buttons, rotary encoder, LEDs, etc. If other port bits are used the project will not operate when the program is run on another board.
| Port Bit | Function |
| PORTB, bit 3 (PB3) | PWM signal to servo motor |
| PORTB, bit 4 (PB4) | High limit set button |
| PORTB, bit 5 (PB5) | Low limit set button |
| PORTC, bit 1 (PC1) | Rotary encoder |
| PORTC, bit 2 (PC2) | Rotary encoder |
| PORTC, bit 3 (PC3) | Buzzer |
| PORTC, bit 4 (PC4) | RGB LED's red segment |
| PORTC, bit 5 (PC5) | RGB LED's blue segment |
| PORTD, bit 0 (PD0) | Do not use. Interferes with flash operation. |
| PORTD, bit 1 (PD1) | Yellow LED to indicate timing in progress |
| PORTD, bit 2 (PD2) | Start sensor |
| PORTD, bit 3 (PD3) | Stop sensor |
Hardware Construction Tips
The buttons, LEDs, rotary encoder, buzzer and phototransistors, and the various resistors should all be mounted on your breadboard. It’s strongly recommended that you try to wire them in a clean and orderly fashion. Don’t use long wires that loop all over the place to make connections. You will have about 12 wires going from the Arduino to the breadboard so don’t make matters worse by having a rat’s nest of other wires running around on the breadboard. You can cut off the leads of the LEDs, phototransistors and resistors so they fit down close to the board when installed.
Make use of the bus strips along each side of the breadboard for your ground and +5V connections. Use the red for power, blue for ground. There should only be one wire for ground and one for +5V coming from your Arduino to the breadboard. All the components that need ground and/or +5V connections on the breadboard should make connections to the bus strips, not wired back to the Arduino.
Checkpoint 1
The sections below describe the parts of the project that are part of Checkpoint 1 and are due 11/19 or 11/21. Students are strongly advised to get these parts of the project working before attempting to complete any of the rest of the project.
Object Detector
The two detectors are made from two white LEDs and two phototransistors. The LEDs and phototransistors are positioned so the light from the LED is illuminating the phototransistor unless some object blocks the path as shown below. The LEDs don’t have to be controlled by the Arduino and can be wired to be on all the time. The phototransistors should each be connected to a digital input port bit and monitored using the pin change interrupts similar to how you handle the rotary encoder inputs. When the phototransistor can “see” the light from the LED the digital input will be a one. When an object blocks the light path, the input will go to a zero.

Installing LEDs and Phototransistors
The LEDs should be installed on one side of the center channel of the breadboard, and the phototransistors opposite them on the other side of the channel. For the LED the longer lead (on the side of the case that is rounded) should go to the positive voltage. For the phototransistor, the shorter lead should go to the positive voltage. The curved top of the LEDs should be pointing at the phototransistors, and their curved tops should be pointing at the LEDS. Both components should be positioned so that some small object about the size of a credit card can be slid along the channel past both sensors without striking them.

Testing the speed trap requires passing an object about the size of a USCard between both the start sensor and the stop sensor so it’s important that these be mounted on your breadboard in a position that will allow this to be done withoug hitting any of the other components on the breadboard. It’s recommended that the stop sensor be installed close to one end of the breadboard, and the start sensor installed closer to the middle of the breadboard as shown below. That way you can swipe a card between them starting towards the middle of the breadboard and passing it out past the end of the breadboard.
The distance between the two sensors will be used in the speed calculation. You can put them wherever you want on the board depending on where your other components are, but the sensors should be at least 1.5 inches apart.

Test It!!!
When writing the software to implement the object detector, it’s a big waste of time to do any software debugging until you are certain the hardware providing the input signal is working correctly. We strongly recommend testing the output of the phototransistor using an oscilloscope to make sure it changes when an object moves between it and the LED.
The scope probe can be connected to the phototransistor where it shows in the above diagram to connect it to the Arduino. With the LED on and illuminating the phototransistor the scope should show about 5V. If you block the light from hitting the phototransistor, the scope trace should drop to close to zero volts. The test can also be made using the DMM.
Measuring Time and Speed
For measuring the time between when the start sensor is blocked and when the stop sensor is blocked, it is required that you use the 16-bit TIMER1 in the manner described here. The TIMER1 prescalar should be selected that allows the timer to count for over 4 seconds before reaching its maximum 16-bit value.
-
When the start sensor is actuated, clear the timer’s count register (
TCNT1) to zero and start the timer so it is counting. -
When the stop sensor is actuated, stop the timer and the 16-bit value of the counter can be read from the
TCNT1register.
Remember that the counting action of the timer can be turned on and off by setting or clearing the prescalar bits. Hint: The sensing of the start and stop inputs, turning TIMER1 on and off, and retrieving the count value can all be done in the Pin Change Interrupt ISR for the two sensors.
Since the count value, and the frequency that the counter was counting at are now both known, your program can calculate the time that elapsed between the start and stop events. It’s recommended that this time be calculated as an integer number of milliseconds since the time value should be displayed in that form. Once the time is known, use the distance between the start and stop sensors to calculate the speed the object was moving.
IMPORTANT: It is required that you implement the timing measurement as described above. Do not do the timing by having TIMER1 interrupt every millisecond, and then counting how many interrupts occured between the start and stop signals.
WARNING: The above calculations can be a bit tricky. All the calculations in the program must be done in fixed-point arithmetic. No floating point operations are allowed. When doing the calculations, always be aware of the size of the numbers the program will be working with and use large enough variables to avoid overflows.
HINT: Work out the calculation that needs to be done by hand before writing it as code in the program. See if there are terms in the numerator and denominator that cancel out and simplify the calculation. Always keep in mind the maximum values that a variable can contain. If you are multiplying two numbers together, can the result exceed the maximum value the variable can hold resulting in an overflow?
Terminating a Timing Operation
Your project needs to have a way to terminate the timing operation if it is taking too long. This might occur if the start sensor is activated but the stop sensor never is. This can done by using TIMER1 which is counting up during the timing event. TIMER1 should be configured to invoke its ISR after 4 seconds has gone by.
Note that for a successful timing event, the 4 second limit will never be reached so the ISR will never be run. The ISR will only run if the stop sensor is never activated and the 4 second limit is reached. The ISR routine can terminate the timing and set a flag to indicate that the main program should print a message on the LCD stating that the timing expired.
Timing Indicator
The LED on PORTD, bit 1 is used to indicate that a timing event is in progress. Your code should turn this LED on when the “start” sensor is blocked, and then turn it off after the “stop” sensor is blocked. It should also go off if the timing event has been terminated due to it going past the 4 second limit.
The project steps above are Checkpoint 1. For full credit, demonstrate these to a CP by 11/19 or 11/21.
To get checked off you must first upload and submit the code you have written to this point on Vocareum. The CP/TA who does your Checkpoint will view the code submission on Vocareum and you will be asked to explain how your code works in addition to demonstrating that it works on your project board.
You can request to have the checkpoint done multiple times, but all checkpoint items will be evaluated each time, not just the items that were not working the last time you asked to have something checked off.
Checkpoint 2
The parts of the project described below are part of Checkpoint 2 and are due 12/3 or 12/5.
Speed Limit Select Buttons
The speedtrap has two buttons for selecting whether the rotary encoder adjusts the low speed limit or the high speed limit.
-
If the low speed limit button is pressed, rotating the encoder will then adjust the setting for the lower speed limit.
-
If the high speed limit button is pressed, rotating the encoder will then adjust the setting for the higher speed limit.
It’s recommended that the button inputs are detected by using the pin change interrupt for PORTB similar to how the start/stop sensors and rotary encoder inputs are handled.
The selected mode must be indicated in some way on the LCD screen so the user will know which setting will be adjusted if they rotate the encoder. The recommended way is to put a character of some type next to the low or high speed limit that is shown on the LCD.
Once a button is pressed to change to the low or high mode, it should stay in the selected mode until the other button is pressed to switch the mode. The user does not have to hold the button down to make it stay in either the low or high mode.
You can choose to have the program start in either of the two modes. It is not required that it start in the mode it was last in before being powered down.
Speed Limit
The rotary encoder is used to set two high and low speed limits from 1 cm/sec to 99 cm/sec as an integer value. Whenever a new speed value has been measured, it is compared to the two speed limit values to determine which color LED to illuminate, and possibly whether to sound the alert using the buzzer.
As the user rotates the knob the speed limit should be shown on the LCD display. The software must ensure that the setting is never adjusted to be outside those limits. If the user adjusts the speed to 99 and continues to rotate the knob to go higher, the speed should stay at 99. The same thing should happen if the speed is adjusted to 1, it should stay at 1 even if the knob is rotated to go lower. The software must also ensure that the low limit can never be greater than the high limit, and the high limit can not be lower than the low limit. Whenever a new limit value is set, it is stored in the non-volatile EEPROM (see below).
Non-volatile EEPROM Memory
The ATmega328P microcontroller contains 1024 bytes of memory that retains its data when the power is removed from the chip. It’s similar to the FLASH memory where your program is stored but intended to be written to and read from your program rather than just during the “make flash” operation. In this project we will be using the EEPROM to store the two speed limit values. In this way when the device is turned on it will have the same settings as it had previously.
The avr-gcc software includes several routines that you can use for accessing the EEPROM. To use these functions, your program must have this “include” statement at the beginning of the file that uses the routines.
#include <avr/eeprom.h>
The two routines that you should use for your program are described below.
-
eeprom_read_byte- This function reads one bytes from the EEPROM at the address specified and returns the 8-bit value. It takes one argument, the the EEPROM address (0-1023) to read from. For example to read a byte from EEPROM address 100:unsigned char x; x = eeprom_read_byte((void *) 100); -
eeprom_update_byte- This function writes one byte to the EEPROM at the address specified. It takes two arguments, the address to write to and the 8-bit value of the byte to be stored there. For example to write the byte 0x47 to address 200 in the EEPROM:eeprom_update_byte((void *) 200, 0x47);
Your code should use the above routines to store the two speed limits in the EEPROM whenever they have been changed. You can assume the limits will always be in the range of 1 to 99 cm/sec. The number can be stored in an 8-bit variable and only requires writing a single “unsigned char” variable to the EEPROM. You can choose any address in the EEPROM address range (0 to 1023) to store the value. When your program starts up it should read the values from the EEPROM, but it must then test the values to see if they are valid limit values. If the EEPROM has never been programmed, it contains all 0xFF values. If you read the EEPROM data and the value is not in the range 1 to 99, then your program should ignore this number and revert to using default limit values that are defined in your source code.
Speed Range LEDs
An RGB LED is used to show how the last speed measurement compared to the high and low speed limits that were set with the rotary encoder.
This is the same RGB LED that was used in Lab 8 and should be hooked up the same way. The RGB LED is a “common anode” device. The anode (positive side) of all three LED segments are tied together and should be connected to the +5V supply. The cathodes (negative side) of the individual segments are connected to I/O pins or logic through a current limiting resistor. A low voltage on the segment’s cathode is used to turn the segment on. It’s recommended that the same current limiting resistor values be used here as were used in Lab 8.
Due to a lack of I/O pins on the microcontroller, only the red and blue segments are controlled directly by the microcontroller on ports PC4 and PC5 respectively. The green segment has to be controlled by these red and blue signals and some additional logic added to the circuit. The logic should be designed so when the red and blue segments are off, the green segment goes on. The circuit does not have to have the ability to turn all three segments off at the same time.
The speed values should be compared to an accuracy of 1 cm/sec. The results of the comparison are shown on the LED segments as follows.
-
If the speed is higher than the high limit, the red segment should be lit.
-
If the speed is lower than the low limit, the blue segment should be lit.
-
If the speed it equal to or higher than the low limit and equal to or lower than the high limit, the green segment of the RGB LED should be lit.
-
If there has not yet been a speed measurement the green segment should be lit.
Buzzer
The buzzer is used to indicate that the last three speed measusurement all were outside the selected speed range, either all above the high limit or all below the low limit. When this occurs the buzzer should sound a short alert signal. When doing the comparison, only the integer part of the speed need be used, the fractional part can be ignored.
In Lab 6 you worked with producing tones of different frequencies from the buzzer. Those tones were done with code that used delays of half the desired output period between operations to make the output signal go high or low. The result was a squarewave signal at the desired frequency.
The problem with this method is that the program is locked into the delay routines while they measure out the selected delay time. A better way to create the tones is by using a timer to generate interrupts at the desired rate. TIMER0, an 8-bit timer, should be used to generate a one half second tone of whatever frequency you choose. When the program wants to sound the buzzer it can start the timer running. Each time an interrupt occurs the ISR changes the state of the output bit driving the buzzer. After the required number of transitions have occured, the ISR can shut the timer off to end the output. Do not use the delay functions to drive the buzzer.
The tones produced in Lab 6 were a single frequency signal of a short duration. For this project we want the alert signal to be more distinctive than that. The alert signal should consist of at least four tones played one after the other, each individual tone lasting 1/4 second. You can select whatever frequencies you wish for the tones. The only requirement is that it should be possible for the listener to hear that multiple tones were played. For example, four tones could all be different at frequencies of 250Hz, 600H, 300Hz and 550Hz, or they could repeat as with 300Hz, 500Hz, 300Hz and 500Hz. There should be no gaps between the four tones.
Servo Motor Indicator Dial
Besides displaying the speed on the LCD, the speed is also shown on a round dial by using a servo motor to rotate an indicator to point to the speed. The servo is controlled by TIMER2 since this is the only timer with an output signal that is not blocked by the LCD panel. Servos normally operate with PWM signals with a period of 20ms, but with TIMER2 the longest period that can be achieved is 16.4ms and the servos will work properly with this period PWM signal.
The width of the PWM pulse must vary between 0.75ms and 2.25ms. Assuming TIMER2 is using the largest prescalar so as to have the maximum period, use that to determine the values that must go in the OCR2A register for the minimum pulse width of 0.75ms, and the value for the maximum pulse width of 2.25ms. Note: The calculation of these numbers was done in review question 2 of Lab 7.
Once you have determined the two OCR2A values for the minimum and maximum pulse width, you can use those to develop an equation to map the speed values to servo positions. The number that goes in the OCR2A register has a linear relationship with the speed value. Linear equations are based on two points, and you have the two points:
-
To indicate a speed of 0 cm/sec, the motor should rotate fully to the left which will happen if the PWM signal is at it’s maximum width: (0, max OCR2A).
-
To indicate a speed of 100 cm/sec, the motor should rotate fully to the right which will happen if the PWM signal is at it’s minimum width: (100, min OCR2A)</li>
Using these two points, determine a linear equation for mapping the speed values to the OCR2A values. Once you have figured out the equation, write code to implement it, but remember to only use fixed point variables and operations. Do not use floating point operations or variables. Make sure the calculations do not result in overflows of the fixed point variables.
Every time the speed to be displayed changes, a new value for OCR2A should be calculated using the equation you developed above and stored in that register.
Reminder: Any use of Floating Point variables OR constants will result in large deductions in the visual grading.a
Installing the Dial
At the the instructor’s podium you can find sheets of small paper indicator dials that show the speed from 0 to 100 cm/sec. These can be cut out and taped to the bottom of the servo. To align the servo motor’s pointer properly we suggest the following procedure. Temporarily change your program to show fixed speed of 50 cm/sec, the midpoint of the rotation, and have the servo point there. Pull the indicator arm off the servo and reinstall it so it is pointing parallel to the two long sides of the servo which should be at the 50 cm/sec speed. After doing this it should correctly show the speeds from 0 to 100 cm/sec.
The project steps above are Checkpoint 2. For full credit, demonstrate these to a CP by 12/3 or 12/5.
To get checked off you must first upload and submit the code you have written to this point on Vocareum. The CP/TA who does your Checkpoint will view the code submission on Vocareum and you will be asked to explain how your code works in addition to demonstrating that it works on your project board.
You can request to have the checkpoint done multiple times, but all checkpoint items will be evaluated each time, not just the items that were not working the last time you asked to have something checked off.
Software Issues
Multiple Source Code Files
Your software should be designed in a way that makes testing the components of
the project easy and efficient. In previous labs we worked on putting all the
LCD routines in a separate file and this practice should be continued here.
Other parts of the code should be divided into separate source code files based
on the purpose of that part of program. Consider having a separate file for
the encoder routines and its ISR. Code to handle all three of the timers and
their associated ISRs can be placed in a separate file. Code to handle the
timing LED, the RGB LED and the servo can either be in separate files or in the
main program. All separate code files must be listed on the OBJECTS line of
the Makefile to make sure everything gets linked together properly.
To get full credit for this project, your source code must be in at least four “.c” files. This can include your main file, lcd.c, and a minimum of two others.
WARNING: All the files containing the project’s C code must compile separately when doing “make”. The compiling process must create a “.o” file for each source code file, and these are all linked together as part of the “make” process. Do not have one main file, and then use “#include” lines in that file to pull in the contents of the other files at the time the main file is compiled.
Accessing Global Variables
In the project you may need to use global variables that are accessed in multiple source files. A global variable can be used in multiple files but it must be defined in one place. We define variables with lines like
char a, b;
int x, y;
Global variables must be defined outside of any function, such as at the start of a file before any of the functions (like “main()”). Variables can only be defined once since when a function is defined, the compiler allocates space for the variable and you can’t have a variable stored in multiple places.
If you want to use that global variable in another source code file, the compiler needs to be told about that variable when compiling the other file. You can’t put another definition of the variable in the file for the reason given above. Instead we use a variable declaration. The declaration of a global variable (one that’s defined in a different file) is done using the “extern” keyword. The “extern” declaration tells the compiler the name of the variable and what type it is but does not cause any space to be allocated when doing the compilation of the file.
For example, let’s say the global variable “result” is accessed in two files, project.c and stuff.c, and you decide to define it in the project.c file. The project.c file would contain the line defining the variable
int result;
and the stuff.c file would need to have the line
extern int result;
declaring it in order to use the variable. If the “extern” keyword was omitted, both files would compile correctly, but when they are linked together to create one executable, you would get an error about “result” being multiply defined.
If you have global variables that need to be accessed in multiple files it’s recommended that you put the declarations in a “.h” file that can be included in all the places where they may be needed. For the example above, create a “project.h” file that contains the line
extern int result;
and then in both project.c and stuff.c add the line
#include "project.h"
It doesn’t cause problems to have the declaration of a variable, like from an “.h” file, included in the file that also contains the definition of the same variable. The compiler knows how to handle this correctly.
Improving Your Makefile
In class we discussed how the “make” program uses the data in the
“Makefile” various modules that make up a program. This project may require
several source code files, some with accompanying “.h” header files, so the
generic Makefile should be modified to describe this. For example, let’s say
you have four C files for the project and four header files:
-
The main program is in
project.cand has some global variables and functions declared inproject.h. -
The LCD routines are in
lcd.cwith global declarations inlcd.h. -
The functions to handle the rotary encoder are in
encoder.cwith global declarations inencoder.h. -
The functions to handle the timers are in
timers.cwith global declarations intimers.h.
Let’s also say that project.h is “included” in the encoder.c file, and the
header files for the LCD, encoder and timer routines are included in
the project.c file. In this situation, the following lines should be added to
the Makefile after the “all: main.hex” and before the “.c.o” line as
shown below.
all: main.hex
project.o: project.c project.h lcd.h encoder.h timers.h
encoder.o: encoder.c encoder.h project.h
timers.o: timers.c timers.h
lcd.o: lcd.c lcd.h
.c.o
These lines define the dependencies of the files. If, for example, a change is
made to the project.h file, the make program will know that the project.o
and encoder.o files need to be recreated since they are dependant on
project.h. Recreating these “.o” files will then require recreating the full
project file that gets execucted. The other .o files (lcd.o and timers.o)
do not have to be recompiled since they are not dependant on the contents of
project.h.
Adding all the dependencies to the Makefile will make sure that any time a file
is edited, all the affected files will be recompiled the next time you type
make.
Building Your Design
It’s important that you test the hardware and software components individually before expecting them to all work together. Here’s a one possible plan for putting it together and testing it.
The project should be built in two stages with it checked off at each of the two stages.
For checkpoint 1:
-
Install the LCD shield and write code to put a splash screen on the LCD. This will confirm that you can write messages on both lines of the display. Use your file of LCD routines from the previous labs to implement this.
-
Build the two sensors by installing the two LEDs and the two phototransistors. Write code to test the sensors. If the first sensor shows the light it blocked write “Start” somewhere on the LCD. If the second sensor is blocked write “Stop” on the LCD.
-
Write code to start the timing when the first sensor is activated, and stop when the second is activated. Write the timer count value to the LCD and check that it is larger for longer lenghts of time between blocking the start and stop sensor.
-
Install the timing LED and add code to make it go on when timing starts and go off when it stops.
-
Add code to the timer ISR to handle the case of the timing event exceeding the specified time limit. Confirm that the timing stops on its own after the correct amount of time.
-
Convert the timer count value to milliseconds and to speed in cm/sec. This must be done without using any floating point arithmetic. Write these values to the LCD after each timing event.
For checkpoint 2:
-
Install the two buttons to select the high or low speed limit. Use the “Pin Change Interrupts” to read the buttons and indicate on the LCD which limit has been selected for changing.
-
Install the rotary encoder. Define the speed limit variables and use your encoder routines from Lab 6 to change the selected limit number up and down. Display the number on the LCD. Add code to make sure the number stays between 1 and 99 cm/sec. Trying to go beyond these limits should keep the value at limit.
-
Write code to store the two speed limits as set with the rotary encoder in the EEPROM, and read the EEPROM values when the program starts. Confirm that this is working by adjusting the speed limit values and cycling the power on the project. It should start up and display the valuesyou had set before. Make sure to add code that checks that the speeds you read from the EEPROM are valid values.
-
Add the RGB LED, the three resistors and any logic needed to handle the third color segment. Add code to turn the appropriate segment on based on the comparison between the measured speed and the two speed limits.
-
Install the buzzer and add code to play a tone if there are three consectutive speed measurements that went above the high limit or below the low limit. Use the concept of how to generate a tone from the encoder lab but use TIMER0 to generate the tone on the buzzer. Don’t use delay functions like you did in the encoder lab.
-
Add the servo motor. Write code to convert the measured speed to the value to generate the correct PWM pulse using TIMER2 like you did in Lab 7.
Results
Getting your project checked off can be done in multiple checkpoints to ensure you earn partial credit for parts that are working. At each checkpoint we will confirm operation of all the features from the previous checkpoint plus the new features added in the current checkpoint.
Checkpoint 1:
- Splash screen with your name shown at start.
- Speed measurement working and displaying speed in cm/sec on the LCD.
- Timing LED goes on and off properly.
- Measurement times out after 4 seconds.
Checkpoint 2:
Demonstrate items from the previous checkpoint. Then demonstrate:
- Button select which speed limit to adjust.
- Rotary encoder can adjust selected speed limit.
- Speed limits stored in EEPROM and retrieved when Arduino restarted (power cycled).
- RGB LED segments indicate comparison between the speed and the two limits.
- Buzzer sounds warning if 3 consectutive speeds outside one of the limits.
- Servo motor positions pointer to correct position for the measured speed.
Review Questions
Be sure to answer the two review questions in Project_Answers.txt and
reprinted below:
-
Cost Analysis: Assume we are building 1000 units of this system. Use the provided part numbers (see the webpage) and the digikey.com or jameco.com website to find the total cost per unit (again assume we build a quantity of 1000 units) for these speed traps. Itemize the part costs (list each part and its unit cost when bought in units of 1000) and then show the final cost per unit below. Note: You only need to price the components used in the project (not all the components in your lab kit. Also, you do not need to account for the cost of the circuit board or wires. In a real environment we would manufacture a PCB (Printed Circuit Board). As an optional task, you can search online for PCB manufacturers and what the costs and options are in creating a PCB.
-
Reliability, Health, Safety: Assume this system was to be sold to consumers for use at their home.
-
What scenarios might you suggest testing (considering both HW and SW) before releasing the product for use?
-
How might you make your design more reliable? By reliability we don’t just mean keeping it running correctly (though you should consider that), but also how you can detect that a connected component has failed to gracefully handle such situations. You can consider both HW and SW points of failure, issues related to the operational environment, etc. and how to mitigate those cases.
-
Submission
Make sure to comment your code with enough information to convey your approach and intentions. Try to organize your code in a coherent fashion.
The Project_Answers.txt file and all source code (all .c and .h files and the
Makefile) must be uploaded to the Vocareum web site by the due date. Make sure
you have included all files since the graders may download your submission,
compile it on their computer and download it to their board for checking the
operation. See the Assignments page of the class web site for a link for
uploading.
Please make sure to save all the parts used in this lab in your project box. All the contents of the project box and components will need to be returned at the end the semester.

