hde free home diy electronics
TRANSCRIPT
In this issue:
1. Arduino Motion Detector Project
In issues #1 and #2 I showed you how you can use an Arduino single board microcontroller, an LCD/Keypad shield and an ultrasonic echo module to build a contactless distance measurement device. In this issue we add some code to analyze successive distance measurements and an LED to indicate motion. When a moving object is detected an LED is lit for a few seconds to let you know.
THE
Home DIY Electronics HDE Issue #3
Arduino Motion Detector
The Arduino is a microcontroller board
based on an open source design. The
hardware is low cost and available from a
number of sources. It’s perfect for your
electronics projects.
Click here to see the range of inexpensive
Arduino hardware.
Everyone has to have at least one big data
disaster.
Catastrophic loss of important information
teaches the importance of regular and frequent
backups.
Yes it’s happened to me and I know a lot of
people who have been hit even harder than I have.
Welcome to
HDE the Magazine from Home DIY
Electronics. Hi, I’m Steve.
I’m an engineer and I’ve specialised in embedded software for over 40 years. Issue #3 adds motion detection to the Arduino Distance Sensor project from previous issues. Follow along with my projects as I build them. I’m going to
release them here before I put them on the web site so be sure that you get your free subscription and you won’t miss an issue.
Subscribe To HDE Free
YOUR TITLE HERE Home Diy Electronics is a free magazine for people like you who build electronics projects. Make sure that you are on my mailing list so that you never miss an issue. Click here to subscribe to the HDE list. Click here to contact Steve.
Thoughts
Solderless Breadboards for DIY Electronics Projects
February 21st 2014
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 2 of 16
I want to use the output to fire a battery operated water gun so that it soaks the cats and drives them away before they have time to leave me any unwanted packages.
Because I’m going to be firing a water gun with a limited supply of water, I can’t afford to have it firing on false triggers very often which would waste ammunition and battery power. This means that I must manage the sensitivity carefully to make sure that it only fires when it should. A task which is easier said than done but I’ve had some success. My cat repeller must work in daylight as well as at night in the dark. Traditional passive infra-red motion detectors are not very good at this. They work by sensing heat from animals and humans so they can become much less sensitive on a bright hot day. An ultrasonic sensor should be consistent day and night because it relies on sound waves rather than electromagnetic infra-red radiation. So let’s get started.
Arduino Motion Detector
Hardware The hardware is the same as it was in issue #1 with the addition of an LED and a 470Ω resistor. Connect the resistor to the Arduino pin 13 with the other end to the Anode of the LED. Connect the cathode of the LED to Gnd. Like this:
In issue #1 I showed you how to hook up an Arduino Uno, an LCD/Keypad shield and an HC-SR04 Ultrasonic Echo module. By loading a little software code onto the Arduino you built a working Ultrasonic distance sensor. Issue #2 developed the software sketch to add more display modes and to introduce the use of the keypad on the shield to switch between modes. In this issue we takes things a little further by adding an LED indicator and a new version of the software to add motion detection to the project.
If you haven’t already built the Arduino Distance sensor from issue #1 or from the website then do it now.
Detection specification There are a number of things that you need to think about before you begin to build any project. The most important question that must be answered is “What will it be used for?” Now your reason for building this may be different to mine but I’m working towards using this system as part of an animal deterrent to stop cats pooping in my yard.
An excellent beginner’s guide to
Arduino Programming
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 3 of 16
Some processing of the readings is required to provide a reliable motion detection system that will detect when an animal wanders in front of the sensor but doesn’t trigger falsely when all is still. There are many ways to tackle this problem and my way may not be the simplest or best. What I can say however is that it works and it isn’t too difficult to understand so that’s the one I went with. To deal with the problem of distance flipping the code averages the values from multiple readings. But we still need to detect changes due to motion so two averages are computed. One with a long time constant and another with a shorter time constant. The longer average serves as a base line. A normal no-motion value will only change slowly when there is movement in view. The shorter averaged value reacts faster to movement so when the difference in the two averages is computed and it exceeds a threshold value we can determine that there is motion. To further increase the robustness of the system the code triggers only when objects are detected approaching the sensor. I figured that there is little point triggering when the cat is moving away from the sensor.
Definitions First we define how many readings to be used in each of the two averages readings.
Well there isn’t much to say about the additional hardware except to make sure that you get the LED in the correct way around. You can wire up the two components on the HC-SR04 breadboard conveniently.
Software Now for the code. If you want to get on and download the code sketch to your Arduino right now you can find it at the end of this issue or you can get the most up to date sketch from the motion detector software page. The motion detection code enhances the Arduino Distance Sensor project software described in issues #1 and #2. You may want to review those issues before proceeding to the new motion code here.
Overview The hardware detection system consists of a distance finder which delivers a new range value every 200mS. Turning this system into a motion detector is a matter of measuring the difference between readings. It isn’t sufficient however to simply take the difference between successive readings because as we have seen, the range finder can often flip Between multiple echoes from objects at different distances.
#define MOTION_BASE_SIZE 24
#define MOTION_CURRENT_SIZE 6
I’ve set the base line average at 24 readings or 4.8 seconds. The fast average is set to 6 readings or 1.2 seconds. You might want to try different values and tune the characteristics of the motion sensor to your requirements. Here are the variables used by the motion detection logic: volatile int
motion_base_array[MOTION_BASE_SI
ZE];
volatile int
motion_current_array[MOTION_CURR
ENT_SIZE];
volatile int motion_base;
volatile int motion_current;
volatile bool motion_detected =
false;
First the two averaging arrays are defined and sized accordingly. Then two variables to hold the results of the averaging followed by a Boolean flag used to indicate that motion has been detected. All these variables are declared volatile because they are being used in both the interrupt service routines and the background loop.
The first and best guide to C
programming
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 4 of 16
Declaring variables volatile guarantees that the variable storage is read each time the value is required otherwise the optimising compiler would make mistakes.
Motion detection The main motion detection logic is contained in the function motion_detector() which is called from the echo_interrupt() function. The operation of the motion_detector() is simple. First the distance value made available by echo_interrupt() is added to each of the two arrays. New values overwrite the earliest entries in the arrays. The average of each array is computed by adding all the elements and dividing by the size of the respective array. The two averages are compared and if the difference exceeds a threshold then the motion_detected flag is set. Motion triggering only happens when the current value is less than the base line value. The motion_detected flag is then processed to generate an output in the timer interrupt service routine
Motion output The task of turning on an LED for 2 seconds when motion is detected is handled by the motion_output() function.
The routine is called from the timerIsr(). This timer interrupt service routine is called every 50 micro seconds so the first thing that the motion_output() does is count 2000 calls before executing the processing code. The code is only required to run every 100mS so there is no point running it any more often than this. The LED flash is generated by a Finite State Machine in the motion_output() function. I love using state machines as I’m sure you will notice if you follow my projects. The “state” variable and the two timer counters are declared static in this function so that they hold their value between calls. The state machine is very simple:
Everything that you could ever want to know about Arduino
When motion is detected the LED is turned on and the machine enters state 1. When the “On Timer” times out the LED is turned off and the machine goes to state 2. When the “Off Timer” times out the machine reverts back to state 0 ready to react to more motion detection. State 2 has been included to provide a quiet time after the LED has flashed. This helps to prevent multiple triggering of the LED and anything else that might be controlled by the output.
Display A new display mode has been added to display the variables used in the motion detection.
The display_motion_detector() routine is called from loop() and can be selected by multiple clicks of the “Select” button on the Keypad. A new mode has been added to do_button_input() to allow the selection.
display_motion_detector() calls display_histogram() to show the histogram on the top line of the display as described in earlier issues.
The second line of the display is filled by the new function display_motion_vars(). Both the base line average distance value and the current average value are displayed.
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 5 of 16
Conclusion It works which is good enough for me. It functions in daytime just as well as it does at night. It detects when animals and people approach and it isn’t overly sensitive. The problem that I set out to solve has a solution. .
That doesn’t mean that it’s the best solution however. There is still room for improvement by tweaking some of the variables or even developing better algorithms that improve upon this simple system. Why don’t you see if you can improve the code yourself. .
In future issues I shall be connecting the motion detector to a relay to enable it to do things like sound an alarm or even fire a battery operated water gun. I have it in mind to construct a device to stop cats pooping on my property. Look out for this project. .
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 6 of 16
/*******************************************************
arduino_distance_sensor_3
Arduino Uno + 2 x 16 LCD display + HC-SR04
Displays echos from an HC-SR04 ultrasonic distance finder
on the LCD.
Motion detection added 9/2/14
9th Feb 2014
********************************************************/
#include <LiquidCrystal.h> // LCD module
#include <TimerOne.h> // Header file for TimerOne library
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // select the pins used on the LCD panel
// define some values used by the panel and buttons
#define trigPin 12 // Pin 12 trigger output
#define echoPin 2 // Pin 2 Echo input
#define echo_int 0 // Interrupt id for echo pulse
#define motionLEDPin 13 // Pin 13 motion detector indicator output
// Define constants for button presses
#define btnRIGHT 0 // Right
#define btnUP 1 // Up
#define btnDOWN 2 // Down
#define btnLEFT 3 // Left
#define btnSELECT 4 // Select
#define btnNONE 5 // None. No button pressed
#define TIMER_US 50 // 50 uS timer duration
volatile int tick_counts = 4000; // define 200mS between trigger pulses
volatile int trigger_time_count = 0; // Count down counter to trigger pulse time
int display_mode = 0; // Determine what the display does
volatile long echo_start = 0; // Records start of echo pulse
volatile long echo_end = 0; // Records end of echo pulse
volatile long echo_duration = 0; // Duration - difference between end and start
#define RANGES 16 // Number of ranges
#define MAX_ZONE 18000 // The last (maximum) zone range in uS
int zones[RANGES]; // Array to contain the zone values for each range
volatile int range_hits[RANGES]; // Array of recent zone hit counts
volatile int range_cm; // The distance detected in centimeters
#define MOTION_BASE_SIZE 24 // Length of the motion base samples array
#define MOTION_CURRENT_SIZE 6 // Length of the motion current value array
volatile int motion_base_array[MOTION_BASE_SIZE]; // Motion base array
volatile int motion_current_array[MOTION_CURRENT_SIZE];// Motion current value array
volatile int motion_base; // Long term motion base value
volatile int motion_current; // Current motion distance value
volatile bool motion_detected = false; // True when motion output triggering
// Define the special characters that will form vertical bar graph lcd charcters
byte bar0[8] = B00000, B00000, B00000, B00000, B00000, B00000, B00000, B11111;
byte bar1[8] = B00000, B00000, B00000, B00000, B00000, B00000, B11111, B11111;
byte bar2[8] = B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111;
byte bar3[8] = B00000, B00000, B00000, B00000, B11111, B11111, B11111, B11111;
byte bar4[8] = B00000, B00000, B00000, B11111, B11111, B11111, B11111, B11111;
byte bar5[8] = B00000, B00000, B11111, B11111, B11111, B11111, B11111, B11111;
byte bar6[8] = B00000, B11111, B11111, B11111, B11111, B11111, B11111, B11111;
byte bar7[8] = B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111;
// Define the special characters that will form horizontal bargraph lcd charcters
byte sbar0[8] = B00000, B00000, B00000, B00000, B00000, B00000, B00000, B00000;
byte sbar1[8] = B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000;
byte sbar2[8] = B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000;
byte sbar3[8] = B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100;
Arduino Motion Detector Sketch Download the latest Arduino Motion Detector Software Sketch
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 7 of 16
byte sbar4[8] = B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110;
byte sbar5[8] = B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111;
// --------------------------
// setup()
// Initialize i/o pins.
// Initialize the timer and attach interrupt.
// Attach interrupt to echo input.
// Initialize the LCD.
// Initialize the serial monitor.
// Create the range_hits[] array.
// Start initial display mode.
// --------------------------
void setup()
int i, zone_diff;
pinMode(trigPin, OUTPUT); // Trigger pin set to output
pinMode(echoPin, INPUT); // Echo pin set to input
pinMode(motionLEDPin, OUTPUT); // Motion detector indicator pin set to output
Timer1.initialize(TIMER_US); // Initialise timer 1
Timer1.attachInterrupt(timerIsr); // Attach interrupt to the timer service routine
attachInterrupt(echo_int, echo_interrupt, CHANGE); // Attach interrupt to the sensor echo input
lcd.begin(16, 2); // start the LCD library
Serial.begin (9600); // Initialise the serial monitor output
zone_diff = (MAX_ZONE / RANGES); // Size of a distance zone
for (i = 0; i < RANGES ; i++)
range_hits[i] = 0; // initialise the hits array
zones[i] = zone_diff * (i + 1); // Create an array of distance zones
setup_display_cm_mode(); // Initialise display mode
// --------------------------
// loop()
// Main loop.
// Keypad buttons are serviced every 50mS.
// Display is updated every 500mS.
// --------------------------
void loop()
static unsigned long print_timer = 0; // For scheduling LCD updates
static unsigned long button_timer = 0; // For scheduling button polling
int lcd_key = 0;
int i;
if ((millis() - button_timer) > 50) // Is it time to check the buttons?
button_timer = millis(); // Reset the timer
do_button_input(); // Handle user input on buttons
if ((millis() - print_timer) > 500) // Is it time to update the display?
print_timer = millis(); // Reset the timer
switch(display_mode) // Call one of the display routines
// depending on the mode.
case 0:
display_bar_distance_cm(); // Histogram + distance in cm
break;
case 1:
display_histogram(); // Histogram + scale
break;
case 2:
display_line_distance_cm(); // Line + distance in cm
break;
case 3:
display_motion_detector(); // Histogram + motion detection
break;
// --------------------------
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 8 of 16
// clear_display() Clear the display.
// --------------------------
void clear_display()
char* blank = " ";
lcd.setCursor(0, 0); // Clear the bottom row;
lcd.print(blank);
lcd.setCursor(0, 1); // Clear the bottom row;
lcd.print(blank);
// --------------------------
// create_bargraph_characters() Create the LCD bargraph characters.
// --------------------------
void create_bargraph_characters()
lcd.createChar(0, bar0); // Create 8 LCD characters to use
lcd.createChar(1, bar1); // as character sized bargraphs
lcd.createChar(2, bar2);
lcd.createChar(3, bar3);
lcd.createChar(4, bar4);
lcd.createChar(5, bar5);
lcd.createChar(6, bar6);
lcd.createChar(7, bar7);
// --------------------------
// create_side_bargraph_characters() Create sideways LCD bargraph characters.
// --------------------------
void create_side_bargraph_characters()
lcd.createChar(0, sbar0); // Create 6 LCD characters to use
lcd.createChar(1, sbar1); // as character sized bargraphs
lcd.createChar(2, sbar2);
lcd.createChar(3, sbar3);
lcd.createChar(4, sbar4);
lcd.createChar(5, sbar5);
// --------------------------
// setup_histogram_mode() Initialise for histogram mode.
// --------------------------
void setup_histogram_mode()
create_bargraph_characters(); // Create LCD mini bargraphs
lcd.setCursor(0, 1); // Print the scale on the bottom
lcd.print(0); // row of the LCD.
lcd.setCursor(6, 1);
lcd.print(150);
lcd.setCursor(13, 1);
lcd.print(300);
// --------------------------
// setup_display_cm_mode() Initialise for centimeter distance mode.
// --------------------------
void setup_display_cm_mode()
create_bargraph_characters(); // Create LCD mini bargraphs
// --------------------------
// setup_line_cm_mode() Initialise for centimeter line distance mode.
// --------------------------
void setup_line_cm_mode()
create_side_bargraph_characters(); // Create LCD vertical line graph characters
// --------------------------
// setup_motion_detect_mode()
// Initialise for displaying motion detector values;
// --------------------------
void setup_motion_detect_mode()
create_bargraph_characters(); // Create LCD mini bargraphs
// --------------------------
// display_histogram() Display the echo data in the range_hits[] array
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 9 of 16
// as a series of 16 bargraphs.
// --------------------------
void display_histogram()
int i;
for (i = 0; i < RANGES; i++) // Print each zone as a character bar code
lcd.setCursor(i, 0); // Set the cursor at the start of the top line
switch (range_hits[i]) // The number of hits in each zone or range
// determines the height of the bargraph
case 1: // The range values are 0 - 10
lcd.write(byte(1)); // The characters have 7 rows
break;
case 2:
lcd.write(byte(2));
break;
case 3:
lcd.write(byte(3));
break;
case 4:
lcd.write(byte(4));
break;
case 5:
lcd.write(byte(5));
break;
case 6:
lcd.write(byte(6));
break;
case 7:
lcd.write(byte(6));
break;
case 8:
lcd.write(byte(7));
break;
case 9:
lcd.write(byte(7));
break;
case 10:
lcd.write(byte(7));
break;
default:
lcd.write(byte(0));
break;
// --------------------------
// display_distance_cm()
// Display the echo distance in centimetres.
// --------------------------
void display_distance_cm()
int range = range_cm; // Save range in case it changes
if (range > 999) range = 999; // Limit the value to 3 digits
lcd.setCursor(0, 1); // Set the cursor at the start of the bottom line
lcd.print("Distance: "); // Print the label
lcd.print(range); // The range in centimeters
lcd.print("cm"); // Now the units
if (range < 10) // Print an appropriate number of
// spaces to clear to end of line.
lcd.print(" "); // The number of spaces varies with the
// length of the printed number value.
else
if (range < 100)
lcd.print(" ");
else
lcd.print(" ");
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 10 of 16
// --------------------------
// display_motion_vars()
// Display the motion detection variables.
// --------------------------
void display_motion_vars()
int base = motion_base; // Save base value in case it changes
int current = motion_current; // Save current value in case it changes
int diff; // Difference between base and current
if (base > 999) base = 999; // Limit the value to 3 digits
lcd.setCursor(0, 1); // Set the cursor at the start of the bottom line
display_3_digits(base);
if (current > 999) current = 999; // Limit the value to 3 digits
lcd.print(" ");
display_3_digits(current);
diff = base - current;
lcd.print(" ");
display_3_digits(diff);
// --------------------------
// display_3_digits(val)
// Print 3 digits to the display.
// Printed right justified with -ve in front.
// --------------------------
void display_3_digits(int val)
int abs_val = abs(val); // Get absolute value
bool neg = false; // True if negative
if (val < 0) neg = true; // Get +/- indicator
if (abs_val < 10) // Print an appropriate number of
// spaces to clear to end of line.
lcd.print(" "); // The number of spaces varies with the
// length of the printed number value.
else
if (abs_val < 100)
lcd.print(" ");
if (neg) // Print -ve symbol if needed
lcd.print("-");
else
lcd.print(" ");
lcd.print(abs_val); // Print the value
// --------------------------
// display_bar_distance_cm()
// Display the echo distance data and the histogram
// --------------------------
void display_bar_distance_cm()
display_histogram(); // Display the histogram on the top line
display_distance_cm(); // Display distance value on the bottom line
// --------------------------
// display_motion_detector()
// Display motion detection values
// --------------------------
void display_motion_detector()
display_histogram(); // Display the histogram on the top line
display_motion_vars(); // Display motion detection vars
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 11 of 16
// --------------------------
// display_line_distance()
// Display the echo as a single horizontal bar.
// --------------------------
void display_line_distance()
int range = range_cm; // Save range in case it changes
int range_px; // Range in horizontal pixels
int full_chars; // Number of full characters to display
int part_char; // Value of the end part character
int i;
int max_range_cm = 320; // Maximum centimetres to display
int display_px_length = 80; // Length of the bar in pixels
int char_px_length = 5; // Number of pixels per character
if (range > max_range_cm) range = max_range_cm; // Limit the value to fit the display
// Calculate the number of pixels to light
range_px = (range * display_px_length) / max_range_cm;
full_chars = range_px / char_px_length; // Calculate number of complete characters
lcd.setCursor(0, 1); // Set the cursor at the start of the top line
for (i = 0; i < full_chars; i++) // Print fully populated pixels on left most
// full_chars.
lcd.setCursor(i, 0); // Set the cursor on the top line
lcd.write(byte(5)); // All pixels on.
part_char = range_px % char_px_length; // Calculate the part character value
lcd.setCursor(i, 0); // Set the cursor to the end of the full characters.
lcd.write(byte(part_char)); // Select the special char and write it to the display.
i++;
for (; i < 16; i++) // Fill the remainder of the line with spaces.
lcd.setCursor(i, 0); // Set the cursor
lcd.print(" "); // Print a space.
// --------------------------
// display_line_distance_cm()
// Display the horizontal echo bar on the top line
// with the distance value on the bottom.
// --------------------------
void display_line_distance_cm()
display_line_distance(); // Print the horizontal bar.
display_distance_cm(); // Print the distance value.
// --------------------------
// timerIsr() 50uS second interrupt ISR()
// Called every time the hardware timer 1 times out.
// --------------------------
void timerIsr()
trigger_pulse(); // Schedule the trigger pulses
motion_output(); // Handle the motion detector output
// --------------------------
// trigger_pulse() called every 50 uS to schedule trigger pulses.
// Generates a pulse one timer tick long.
// Minimum trigger pulse width for the HC-SR04 is 10 us. This system
// delivers a 50 uS pulse.
// --------------------------
void trigger_pulse()
static volatile int state = 0; // State machine variable
if (!(--trigger_time_count)) // Count to 200mS
// Time out - Initiate trigger pulse
trigger_time_count = tick_counts; // Reload
if (digitalRead(echoPin) == LOW) // Skip trigger if echo high
state = 1; // Changing to state 1 initiates a pulse
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 12 of 16
switch(state) // State machine handles delivery of trigger pulse
case 0: // Normal state does nothing
break;
case 1: // Initiate pulse
digitalWrite(trigPin, HIGH); // Set the trigger output high
state = 2; // and set state to 2
break;
case 2: // Complete the pulse
default:
digitalWrite(trigPin, LOW); // Set the trigger output low
state = 0; // and return state to normal 0
break;
// --------------------------
// echo_interrupt() External interrupt from HC-SR04 echo signal.
// Called every time the echo signal changes state.
//
// Note: this routine does not handle the case where the timer
// counter overflows which will result in the occassional error.
// --------------------------
void echo_interrupt()
int i;
int found_hit;
switch (digitalRead(echoPin)) // Test to see if the signal is high or low
case HIGH: // High so must be the start of the echo pulse
echo_end = 0; // Clear the end time
echo_start = micros(); // Save the start time
break;
case LOW: // Low so must be the end of the echo pulse
echo_end = micros(); // Save the end time
echo_duration = echo_end - echo_start; // Calculate the echo duration
found_hit = RANGES - 1; // Default the last zone
for(i = 0; i < RANGES - 1; i++) // Determine the zone for this measurement
if ((echo_duration < zones[i]))
found_hit = i; // zone 'found_hit' found
break;
// Weight the zone corresponding to the distance measured and slowly reduce
// the other zones.
range_hits[found_hit] += 3; // Add to the isolated zone
for(i = 0; i < RANGES; i++)
// Now decrement all the other zones
if (i != found_hit)
range_hits[i]--;
// Stop the values going out of bounds
if (range_hits[i] < 0)
range_hits[i] = 0;
if (range_hits[i] > 10)
range_hits[i] = 10;
range_cm = echo_duration / 58;
break;
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 13 of 16
motion_detector();
// --------------------------
// read_LCD_buttons()
// Read the buttons on the LCD mini keypad.
// The buttons are all wired through resistors to an
// analog input channel so which button has been pressed
// can be determined by the value read from the input.
//
// Note: It may be neccessary to calibrate the keypad input as
// voltages on your board may differ from those used here.
// my buttons when read are centered at these valies: 0, 100, 250, 410, 640
// we add approx 50 to those values and check to see if we are close
// --------------------------
int read_LCD_buttons()
int adc_key_in;
adc_key_in = analogRead(0); // read the value from the sensor
// Debug code - Uncomment this to send the key value to the serial monitor for calibration.
/* *******************************
Serial.println(adc_key_in);
Debug code End **************** */
// Each key will read a different value.
if (adc_key_in > 1000) return btnNONE; // 1st option for speed reasons since it will be the
most likely result.
if (adc_key_in < 50) return btnRIGHT;
if (adc_key_in < 150) return btnUP;
if (adc_key_in < 300) return btnDOWN;
if (adc_key_in < 460) return btnLEFT;
if (adc_key_in < 690) return btnSELECT;
return btnNONE; // when all others fail, return this...
// --------------------------
// do_button_input()
// Read the buttons on the LCD mini keypad.
// Determine and take appropriate action.
// The buttons are debounced using a simple finite state
// machine which requires the routine to be called frequently.
//
// The debug code prints the state transitions to the serial monitor.
// --------------------------
void do_button_input()
static int button_state = 0; // Initialise the state machine to state 0
int button_down;
button_down = read_LCD_buttons(); // Read the keypad
switch(button_state) // Begin the finite state machine key debouncer
case 0: // 0 Normal or idle state. No buttons being pressed.
if (button_down == btnSELECT)
// SELECT button down.
//* Debug code ********************
Serial.println("button_state 0 => 1");
//Debug code End **************** */
button_state = 1; // Go to state 1 to begin the debounce
break;
case 1: // 1 SELECT was down on the previous call.
if (button_down == btnSELECT) // Is it still down?
// Yes, change mode.
if(++display_mode > 3)
display_mode = 0;
clear_display(); // Clear the display
switch(display_mode) // Change to selected mode
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 14 of 16
case 0:
setup_display_cm_mode();
break;
case 1:
setup_histogram_mode();
break;
case 2:
setup_line_cm_mode();
break;
case 3:
setup_motion_detect_mode();
break;
//* Debug code ********************
Serial.print("SELECT DETECTED button_state 1 => 2 display_mode = ");
Serial.println(display_mode);
//Debug code End **************** */
else
//* Debug code ********************
Serial.println("BOUNCED button_state 1 => 2");
//Debug code End **************** */
button_state = 2; // Go to state 2 to wait for no button down
break;
case 2: // 2 Waiting for btnNONE ensures no further action
if (button_down == btnNONE) // until the button is released and pushed again.
//* Debug code ********************
Serial.println("Reset button_state 2 => 0");
//Debug code End **************** */
button_state = 0;
break;
// --------------------------
// motion_detector()
// Detects when an object approaches the sensor and triggers the output.
//
// The distance sensor often flips between reading objects at different
// distances. This makes it difficult to use individual sensor readings
// for motion detection. To mitigate the multi reading problem the readings
// are accumulated and averaged before use.
//
// Two averages are maintained. The first average has a long time constant and
// is used as the base reading. The second average has a shorter time constant
// to react quickly to changes. The difference in the two average values can
// be used to detect motion.
// --------------------------
void motion_detector()
int i;
static int base_ind = 0; // Index into the base array
static int current_ind = 0; // Index into the current array
int base_sum; // Sum of the base array
int current_sum; // Sum of the current array
int range; // Local value of the distance
int diff; // Difference between base and current
range = range_cm; // Copy the distance value
if (range > 1000) range = 1000; // Limit the range value
motion_base_array[base_ind++] = range; // Insert value into the base array
if (base_ind >= MOTION_BASE_SIZE) // Wrap the base index to the start
base_ind = 0;
base_sum = 0; // Start the base value processing
for (i = 0; i < MOTION_BASE_SIZE; i++)
base_sum += motion_base_array[i]; // Sum the contents of the array
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 15 of 16
motion_base = base_sum / MOTION_BASE_SIZE; // Divide by the array size
motion_current_array[current_ind++] = range; // Insert value into the current array
if (current_ind >= MOTION_CURRENT_SIZE) // Wrap the current index to the start
current_ind = 0;
current_sum = 0; // Start the current value processing
for (i = 0; i < MOTION_CURRENT_SIZE; i++)
current_sum += motion_current_array[i]; // Sum the contents of the array
motion_current = current_sum / MOTION_CURRENT_SIZE;// Divide by the array size
diff = motion_base - motion_current; // Compute the difference
if (diff > 40) // Check for movement towards the sensor
motion_detected = true;
// --------------------------
// motion_output()
// Handle the output from the motion detector logic.
// Called from the 50uS interrupt routine. Divides
// down to execute every 100mS.
// --------------------------
void motion_output()
static int state = 0; // State machine variable
static int idle_timer = 0; // Time between considerations
static int timer; // Determin on/off times
int on_time = 20; // 20 ticks of 100mS = 2S
int off_time = 40; // 40 ticks of 100mS = 4S
int idle_time = 2000; // Waiting to run 2000 * 50uS = 100mS
if (--idle_timer <= 0) // Count calls to run every idle_time executions
idle_timer = idle_time; // Reset idle timer for next run
switch(state) // The finite state machine
case 0: // Waiting for motion detection
if (motion_detected)
// Motion has been detected
digitalWrite(motionLEDPin, HIGH); // so set LED high and
timer = on_time; // set timer for the time on then
state = 1; // wait for time out before resetting LED
break;
case 1: // LED is high after motion detected
if (--timer <= 0)
// On timer time out
digitalWrite(motionLEDPin, LOW); // Reset the LED low
timer = off_time; // set the timer for the off time
state = 2; // and wait before looking for motion again
break;
case 2: // LED has been reset now wait before re-arming
if (--timer <= 0)
// Waiting time over
motion_detected = false; // Reset the detector and
state = 0; // return to the motion detection state
break;
Be the first to see future issues of HDE Magazine by subscribing for free Copyright © homediyelectronics.com
Page 16 of 16
The Home DIY Electronics Magazine
is produced and edited by me, Steve Garratt. I would love to hear what you think about this
magazine or the web site so please feel free to drop me a line at
[email protected] Or use the
contact form on the web site.
Introduction To Basic Electronics An honest review of this downloadable
course
I go through all of the material in this product to highlight both bad and the good points. Overall it’s a good beginners introduction to the how and why of
electronics. Find out how this information can help you get the most from your electronic project building.