Categories
TUTORIALS

VIOLATION DETECTION (GEOFENCING WITH FONA808).

INTORDUCTION

In this tutorial, we are going to use the FONA 808 breakout board along with Arduino to make a cool geofencing project.

The geofencing violation happens when the opioid user (with opioid box) leaves the area where they are allowed to use the medicine. When that behavior happens, the LED will turn on, the alarm will sound and the FONA808 would send the detailed location, time, and violation type to the corresponding police department.

WHAT YOU NEED

  • FONA808
  • A GPRS SIM CARD INSIDE
  • ARDUINO UNO R3
  • GSM & GPS ANTENNA
  • A PIEZO BUZZER
  • A LED LIGHT
  • 330-1K Ohm RESISTOR
  • JUMP WIRES
  • 3.7V LiPo BATTERY

You also need the latest version of the Arduino IDE for this project, along with the following libraries:

  • Adafruit FONA library
  • Adafruit MQTT library
  • Adafruit SleepyDob library

THE CIRCUIT

Let’s now assemble the device. First, connect the power supply to the breadboard:

  • Connect the 5V pin from the Arduino board to the red power line on the breadboard
  • The GND pin to the blue power line.

Then, place the FONA 808 breakout on the breadboard.

  • Connect the VIO pin to the red power line
  • The GND & Key pins to the blue power line.

After that,

  • Connect the FONA RST pin to Arduino pin 4,
  • FONA TX to Arduino pin 3
  • RX to Arduino pin 2

Also connect the 3.7V LiPo battery, the GPS antenna, and the GSM antenna to the FONA 808 shield or breakout.

Also place the LED on the breadboard, in series with the 330-1K Ohm resistor. Connect it to pin number 6 of the Arduino board, and connect the other end to the ground.

Finally, place the buzzer on the breadboard, and connect it to pin 9 of the Arduino board. Connect the other pin of the buzzer to the ground.

CODE

// geofencing detection project with alarm & SMS sending
// Inspired by the code from Tony DiCola, Marco Schwartz and Deon P.

// Libraries
#include <Adafruit_SleepyDog.h>
#include <SoftwareSerial.h>
#include "Adafruit_FONA.h"

// LED & Buzzer pins
const int ledPin = 6;
const int buzzerPin = 9;

// Alarm
int counter = 0;
bool alarm = false;

// Size of the geo fence (10000m)
const float maxDistance = 10000;

// Latitude & longitude for distance measurement
float initialLatitude;
float initialLongitude;
float latitude, longitude, speed_kph, heading, altitude;

// FONA pins configuration
#define FONA_RX              2   // FONA serial RX pin (pin 2 for shield).
#define FONA_TX              3   // FONA serial TX pin (pin 3 for shield).
#define FONA_RST             4   // FONA reset pin (pin 4 for shield)

// FONA instance & configuration
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);     // FONA software serial connection.
// use this for FONA 808s
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);                 // FONA library connection.

void setup() {
  
  // Initialize serial output.
  Serial.begin(115200);
  Serial.println(F("Geofencing with Adafruit IO & FONA808"));

  // Set alarm components
  pinMode(ledPin, OUTPUT);
  pinMode(buzzerPin, OUTPUT);
  alarm = false;

  // Initialize the FONA module
  Serial.println(F("Initializing FONA....(may take 10 seconds)"));
  fonaSS.begin(4800);

  if (!fona.begin(fonaSS)) {
    halt(F("Couldn't find FONA"));
  }

  fonaSS.println("AT+CMEE=2");
  Serial.println(F("FONA is OK"));

  // Use the watchdog to simplify retry logic and make things more robust.
  // Enable this after FONA is intialized because FONA init takes about 8-9 seconds.
  Watchdog.enable(8000);
  Watchdog.reset();

  // Enable GPS.
  fona.enableGPS(true);

  // Wait a little bit to stabilize the connection.
  Watchdog.reset();
  delay(3000);

  // Initial GPS read
  bool gpsFix = fona.getGPS(&latitude, &longitude, &speed_kph, &heading, &altitude);
  initialLatitude = latitude;
  initialLongitude = longitude;

  // Init counter
  counter = millis();
  
}

void loop() {
  
  // Watchdog reset at start of loop--make sure everything below takes less than 8 seconds in normal operation!
  Watchdog.reset();

  // Grab a GPS reading.
  float latitude, longitude, speed_kph, heading, altitude;
  bool gpsFix = fona.getGPS(&latitude, &longitude, &speed_kph, &heading, &altitude);

  Serial.print("Latitude: ");
  printFloat(latitude, 5);
  Serial.println("");

  Serial.print("Longitude: ");
  printFloat(longitude, 5);
  Serial.println("");

  Serial.print("Initial latitude: ");
  printFloat(initialLatitude, 5);
  Serial.println("");

  Serial.print("Initial longitude: ");
  printFloat(initialLongitude, 5);
  Serial.println("");

  // Calculate distance between new & old coordinates
  float distance = distanceCoordinates(latitude, longitude, initialLatitude, initialLongitude);
  
  Serial.print("Distance: ");
  printFloat(distance, 5);
  Serial.println("");

  // Set alarm on & SMS to the deparment
  if (distance > maxDistance) {
    //alarm sound
    alarm = true;
    //send sms to the plice
    char message[141];  
    char LAT1[10];//string of lat and long unparsed & overflowing bound
    char LAT[10];
    char LONG[10];
    dtostrf(latitude, 10, 7, LAT1); //gathering GPS data in a format that can be sent
    dtostrf(longitude, 10, 7, LONG);
    //initialize desired array from unparsed array
    for(int i = 0; i < 9; i++) {
      LAT[i] = LAT1[i];
    }
    LAT[9] = '\0';//truncate array at last desired value
    sprintf(message, "Geofencing Violation of Opioid Happend", LAT, LONG);
    Serial.println(LAT);Serial.println(LAT1);Serial.println(LONG);
    Serial.println(message) ;    //prints the message in the serial monitor before sending
    char sendto[13] = "+1123456789"; //put the  phone number of plice station for sms here
    fona.sendSMS(sendto, message) ; //sends the message via SMS}
  }

  // Handle alarm & LED
  if (alarm == false) {
    if (millis() - counter > 5000) {
      digitalWrite(ledPin, HIGH);
    }
     if (millis() - counter > 5100) {
      digitalWrite(ledPin, LOW);
      counter = millis();
    }
    noTone(buzzerPin);
    
  } 
  else {
    if (millis() - counter > 100) {
      digitalWrite(ledPin, HIGH);
    }
     if (millis() - counter > 200) {
      digitalWrite(ledPin, LOW);
      counter = millis();
    }
    tone(buzzerPin, 1000);
  }

}

// Halt function called when an error occurs.  Will print an error and stop execution while
// doing a fast blink of the LED.  If the watchdog is enabled it will reset after 8 seconds.
void halt(const __FlashStringHelper *error) {
  Serial.println(error);
  while (1) {
    digitalWrite(ledPin, LOW);
    delay(100);
    digitalWrite(ledPin, HIGH);
    delay(100);
  }
}

void printFloat(float value, int places) {
  // this is used to cast digits 
  int digit;
  float tens = 0.1;
  int tenscount = 0;
  int i;
  float tempfloat = value;

  // make sure we round properly. this could use pow from <math.h>, but doesn't seem worth the import
  // if this rounding step isn't here, the value  54.321 prints as 54.3209

  // calculate rounding term d:   0.5/pow(10,places)  
  float d = 0.5;
  if (value < 0)
    d *= -1.0;
  // divide by ten for each decimal place
  for (i = 0; i < places; i++)
    d/= 10.0;    
  // this small addition, combined with truncation will round our values properly 
  tempfloat +=  d;

  // first get value tens to be the large power of ten less than value
  // tenscount isn't necessary but it would be useful if you wanted to know after this how many chars the number will take

  if (value < 0)
    tempfloat *= -1.0;
  while ((tens * 10.0) <= tempfloat) {
    tens *= 10.0;
    tenscount += 1;
  }


  // write out the negative if needed
  if (value < 0)
    Serial.print('-');

  if (tenscount == 0)
    Serial.print(0, DEC);

  for (i=0; i< tenscount; i++) {
    digit = (int) (tempfloat/tens);
    Serial.print(digit, DEC);
    tempfloat = tempfloat - ((float)digit * tens);
    tens /= 10.0;
  }

  // if no places after decimal, stop now and return
  if (places <= 0)
    return;

  // otherwise, write the point and continue on
  Serial.print('.');  

  // now write out each decimal place by shifting digits one by one into the ones place and writing the truncated value
  for (i = 0; i < places; i++) {
    tempfloat *= 10.0; 
    digit = (int) tempfloat;
    Serial.print(digit,DEC);  
    // once written, subtract off that digit
    tempfloat = tempfloat - (float) digit; 
  }
}

// Calculate distance between two points
float distanceCoordinates(float flat1, float flon1, float flat2, float flon2) {

  // Variables
  float dist_calc=0;
  float dist_calc2=0;
  float diflat=0;
  float diflon=0;

  // Calculations
  diflat  = radians(flat2-flat1);
  flat1 = radians(flat1);
  flat2 = radians(flat2);
  diflon = radians((flon2)-(flon1));

  dist_calc = (sin(diflat/2.0)*sin(diflat/2.0));
  dist_calc2 = cos(flat1);
  dist_calc2*=cos(flat2);
  dist_calc2*=sin(diflon/2.0);
  dist_calc2*=sin(diflon/2.0);
  dist_calc +=dist_calc2;

  dist_calc=(2*atan2(sqrt(dist_calc),sqrt(1.0-dist_calc)));
  
  dist_calc*=6371000.0; //Converting to meters

  return dist_calc;
}

Leave a Reply

Your email address will not be published. Required fields are marked *