hde free home diy electronics

16
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. 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

Upload: others

Post on 19-Feb-2022

16 views

Category:

Documents


0 download

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.