Introduction: GSM Proximity Alert With GPS Location and TIme

About: Electrical Engineer by trade, tinkerer by heart.

What is it?

A battery movement detector which reports trigger events via SMS, along with time and location, derived from GPS. here is a video of it in action:

Where to use it?

I can think of a few use cases for this, besides just for fun an learning

  • A tamper alarm when camping. Stick it in your tent when you are away from it and be immediately notified if anyone messes with it.
  • A mobile alarm when doing work in dodgy areas
  • A wildlife alarm, to notify of nocturnal or elusive creatures, along with location

Why I made it

I have been learning the ins and outs of the Mediatek LinkIt one, and thoroughly enjoying how much is crammed into this little device.

I wanted to learn a bit more about the GPS and GSM capabilities of the LinkIt One, and have also been meaning to try out one of the ubiquitous HC-SR04 ultrasonic sensors that I had lying about, so this was a good project.

The thing that is great about the LinkIt One is that it comes with so much, the major components of my project where all in the box, GPS, GSM, and battery.

Parsing of GPS data can take a little work, so this is a good introduction to it. Sending SMS from the LinkIt is incredibly easy.

Step 1: The Circuit

The circuit is very simple, I have drawn up a schematic (side note: digikey's SchemeIt is a great free tool).

Driving Leds

The LinkIt One can only supply a couple of mA via its GPIO pins, so we use transistors to drive the LEDS. I chose a bog-standard BC5468 with a 2.2k ohm resistor on the base.

The red green and yellow LEDS all had a forward drop in the region of 1.65-1.75V, so I chose 90ohm resistors (Since I was using the 3.3V rail for supply)

Push Button

The pushbutton is connected directly from the GPIO pin to ground which is fine if we enable the internal pullup resistor (you will see "INPUT_PULLUP" in the code).

Battery

I extended the leads of the battery and put a switch in line so that the whole device can be easily turned on and off.

Pin Numbers

These are the pin numbers that I used, you can use whatever you like, just make sure it matches up with the software.

Pin (LinkitOne)Purpose
D8HC-SR04 ECHO
D9HC-SR04 Trig
D10Push Button
D11Red LED
D12Amber LED
D13Green LED

Step 2: Sending Sms (with GPS Coordinates)

Sending an SMS

Before you try to send an SMS, make sure that the GPRS antenna is attached.

The LinkIt One "LGSM.h" module makes it ridiculously easy to send an SMS, I was really blown away. The number is stored as a string of characters, as is the SMS, then it is sent, that's it!

  LSMS.beginSMS("0825551234");
  LSMS.print(smsBuffer);
  LSMS.endSMS();

Formatting the GPS

Before we can do anything with the Linkit One's GPS, we need to attach the antenna to the port marked "GPS" on the back of the board.

GPS reported in the form of comma-separated strings, of which there are a whole lot of different types, each with a focus on a different set of data. All that I was interested in for this project was time and location, so I used the "recommended minimum data for gps" or "RMC" string which looks something like this:

$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A 

This page has a great description of all the string types, here is an extract of the RMC string, you can see that time is the first reported item and the latitude and longitude are 3rd and 5th. The direction, north/south and east/west are

      RMC          Recommended Minimum sentence C
     123519       Fix taken at 12:35:19 UTC
     A            Status A=active or V=Void.
     4807.038,N   Latitude 48 deg 07.038' N
     01131.000,E  Longitude 11 deg 31.000' E
     022.4        Speed over the ground in knots
     084.4        Track angle in degrees True
     230394       Date - 23rd of March 1994
     003.1,W      Magnetic Variation
     *6A          The checksum data, always begins with *

Unfortunately the latitude and longitude are always reported in a weird format where the first two digits (for lat) or first three digits (for lon) are the degrees, then the rest are decimal minutes, this means that the strings need to be split and converted if we want to use decimal degrees.

I found these two pages very helpful in converting coordinates to decimal, they are both worth a read.

Link 1: Car Tracker Tutorial

Link 2: Forum Discussion

This is the snippet of code that handles the aforementioned coordinate conversion, the inline comments explain it in more detail.

void convertCoords(double latitude, double longitude, char n_or_s, char e_or_w, double &lat_return, double &lon_return){ 

 /*
 Latitude  5213.2930,N --> 52d 13.2930' N
 52 degrees 13.2930 minutes NORTH
 52 + (13.2930 / 60) = 52.22155
 Because it is north of the equator, the number remains positive.
 +52.22155
 */

 int lat_deg_int = int(latitude / 100); //extract the first 2 chars to get the latitudinal degrees
 double latitude_float = latitude - (lat_deg_int * 100); //remove the degrees part of the coordinates - so we are left with only minutes-seconds part of the coordinates
 lat_return = lat_deg_int + latitude_float / 60;  //add back on the degrees part, so it is decimal degrees

 //Check if it is N or S, S will turn the value negative

 if (n_or_s == 'S'){
  lat_return *= -1;
 }

 /*
 Longitude  00004.5337,W  --> 00d 04.5337' W
 00 degrees 4.5337 minutes WEST
 00 + (4.5337 / 60) = 0.0755616
 Because it is West, the number becomes negative.
 -0.0755616
 */

 int lon_deg_int = int(longitude / 100);
 double longitude_float = longitude - lon_deg_int * 100;
 lon_return = lon_deg_int + longitude_float / 60;
 if (e_or_w == 'W'){
  lon_return *= -1;
 }
}

Step 3: Ultrasonic Sensor - Detecting Motion

Intro to Ultrasonic Distance Sensors

The HC-SR04 ultrasonic sensor, and others like it, simply send out a 'ping' (at a frequency above human hearing) when triggered and store the time that it takes to receive the echo.

The time that it takes to receive the reflected signal is represented as a pulse on the "ECHO" pin.

The usual use case for one of these sensors it to measure distance, but, by comparing multiple readings, we can make a usable motion detector.

Challenges and Limitations

  • The sensor has a maximum range (about 3m allegedly)
  • There is a limit to how often we can sample (we cant sample until the pulse has returned at the very least).
  • The sensor will output strange readings every now and then, we need to handle these correctly.

Detecting Movement

I decided that the easiest way to detect movement would be to simply take a distance reading and compare it with the previous one, if it had changed by more than 30% then I would know there had been movement. With a perfect sensor in a perfect environment this would workfine, but in reality, there are odd readings every now and then which would cause false triggers.

The way around this was two-fold:

  1. For the "previous" measurements I kept a rolling average of the last ten values.
  2. For the comparison I made sure that both of the last two readings where more than 30% different than the average. In this way, if one reading was invalid, the other would still be withing 30% of the average and there would be no false trigger.

Implementing Averaging/Smoothing

This article has a nice elegant and understandable example on implementing averaging on an Arduino, I adapted their code.

Avoid Repeat Triggers

We don't want the alarm to send multiple SMS when something is moving around in front of the sensor, or while the averaging is settling after a disturbance, so I used the red LED's "blink counter" (read the LED section) to determine whether enough time has passed for the next trigger. I made sure that the number of blinks multiplied by the on+off time of the red LED was greater than the time it takes to get a new stable average reading.

Step 4: Flashing Leds (non-blocking)

I wanted to have the LEDS flash at varying intervals/speeds to indicate things. It is impractical to use the "delay()" function (as in the "blink" Arduino example), since the micro-controller cannot execute anything else while it is waiting for the next flash. The correct approach is to take note of when an LED was turned on/off, and then at each iteration through the main loop, determine whether it is time to turn it off/on.

Take a look at the Arduino "BlinkWithoutDelay" example to get an idea of how it is actually implemented. I expanded slightly on this concept to allow two extra features.

  • The LEDS can have asymmetrical on/off times The red LED can be blinked a set number of times and then stay off
  • The amber LED is not included, since it is a simple on/off, no blinking.
First we set up some variables
//red can be blinked n times instead of continuously
//this variable stores how many flashes are left
int blinksRed = 0;

//is the led on or off
int ledStateRed = LOW;
int ledStateGreen = LOW;

//when was the LED state last toggled
unsigned long previousMillisRed = 0;
unsigned long previousMillisGreen = 0;

//how long should the LED stay on
long intervalOnRed = 600;
long intervalOnGreen = 0;

//how long should the LED stay off
long intervalOffRed = 500;
long intervalOffGreen = 1000;

The following function updates the LEDS, so it can be called at each iteration of the main() loop. Read the inline comments for more details.

void updateLeds() {
  unsigned long currentMillis = millis(); //get the current time

  if ((ledStateRed == HIGH) && (currentMillis - previousMillisRed >= intervalOnRed)) {
    //if the red led is on and ontime has expired, turn it off
    blinksRed = max(0,blinksRed - 1);
    previousMillisRed = currentMillis;
    ledStateRed = LOW;
  }   
  else if ((blinksRed > 0) && (ledStateRed == LOW) && (currentMillis - previousMillisRed >= intervalOffRed)) {
    //if the red led is off and offtime has expired and there are more blinks left, turn it on
    previousMillisRed = currentMillis;
    ledStateRed = HIGH ;
  }
  

  //green is exactly the same, but without a number of blinks limitation
  if ((ledStateGreen == HIGH) && (currentMillis - previousMillisGreen >= intervalOnGreen)) {
    previousMillisGreen = currentMillis;
    ledStateGreen = LOW;
  }   
  else if ((ledStateGreen == LOW) && (currentMillis - previousMillisGreen >= intervalOffGreen)) {
    previousMillisGreen = currentMillis;
    ledStateGreen = HIGH ;
  }

  digitalWrite(pinRed,ledStateRed);
  digitalWrite(pinGreen,ledStateGreen);
}


Step 5: Using the System

We have all the building blocks now to build the whole system. I have attached my final code to this step, any functions that weren't explained in the main steps should be pretty clear from the comments, but please ask if anything doesn't make sense.

The usage of the system is extremely simple.

Turn On

Flick the power switch to turn it on The green LED begins flashing while it attempts to get a GPS lock Once the GPS is locked the green LED will stay on

Arm the Alarm

Pressing the push-button will arm the alarm (and turn on the amber LED to indicate this). To disable the alarm we just flick the power switch.

Movement Detection

The red LED will flash when movement is detected (irrespective of GPS lock).

If movement is detected once the alarm is armed, then an SMS will be sent, with GPS coordinates and time if they are available.

Tech Contest

Participated in the
Tech Contest