Geofence and state tracking

Hi all,
My first post to the forum and somewhat of a newbie to the world of arduino but this forum has been extremely helpful enabling me to learn. I have, however, struck a problem with a project I just have not been able to solve despite searching this community and the web more broadly.
My project is an opener for my Garage door which I have had up and running fine as a manual operation.
I wanted to try installing a geofence to open the door automatically when I approach home. I am using Blynk as the interface with the gps widget bringing in the Lat/Long coordinates. I need to use state tracking to ensure the door doesn’t continue opening when I am at home and this is where I seem to be running into problems. I can see the two flags (gpsFlag and gpsFlagLast) change to zero when I move outside the geofence and change to 1 when I move inside but the door doesn’t actuate. If I remove the state tracking, the door actuates fine when I move inside the geofence but then it obviously keeps actuating without the state tracking.
Hoping someone is able to give me some pointer as to where to start looking.

Cheers
Steve

//***************GPS Trigger*******************************
BLYNK_WRITE(V2) {  // Virtual pin connected to gps stream widget

  GpsParam gps(param); //establish gps stream parameter

  //Get latitude and longitude values
  float Latitude = gps.getLat();
  float Longitude = gps.getLon();

  // Establish geofence and set gps variable state
  if (Latitude > -32.xxxxxx && Latitude < -32.xxxxxx && Longitude > 115.xxxxxx && Longitude < 115.xxxxxx)
  {
    gpsFlag = 1; //inside the geofence
   
    
    // If inside the geofence and the door is closed and the app is set to auto and the gps state has changed, open the door, otherwise do nothing
  if (dcstate == LOW && modeSelect == 1 && gpsFlagLast == 0)
  {
    //  Open garage door
    digitalWrite(12, HIGH);
    delay(500);
    digitalWrite(12, LOW);
   
  }
  
  }
  else
  {
    gpsFlag = 0; //outside the geofence
  }
 
 delay(50);
  gpsFlagLast = gpsFlag; //track the gps geofence variable state so that the action doesn't repeat
  }

//**************************************************************

Thanks for using code tags, but please post ALL the code. It is essential to see how you have declared variables.

Also, it would be helpful for you to explain what debugging steps you have taken, and the results.

Thanks for the response.  Full code below.

Cheers

[code/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <ArduinoOTA.h>
#include <DNSServer.h>            //Local DNS Server used for redirecting all requests to the configuration portal
#include <ESP8266WebServer.h>     //Local WebServer used to serve the configuration portal
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

// Your WiFi credentials.
// Set password to "" for open networks.
const char* host = "garage_door";
char ssid[] = "xxxxxxxxx";
char pass[] = "xxxxxxxxxxxxxx";

// Mac address should be different for each device in your LAN
//byte arduino_mac[] = { 0x39, 0x7D, 0x11, 0xB4, 0x40, 0x7A };
IPAddress arduino_ip ( 1xxxxxxxxxxxxx);
IPAddress dns_ip     (  8,   8,   8,   8);
IPAddress gateway_ip ( 192,  168,   1,   xxx);
IPAddress subnet_mask(255, 255, 255,   0);

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

//Define digital pins and assign to variables
#define btnpin 12
#define DoorOPEN 13
#define DoorCLOSED 14

#define BLYNK_GREEN     "#23C48E"
#define BLYNK_BLUE      "#04C0F8"
#define BLYNK_YELLOW    "#ED9D00"
#define BLYNK_RED       "#D3435C"
#define BLYNK_DARK_BLUE "#5F7CD8"
#define PURPLE          "#800080"
#define BLUE_VIOLET     "#8A2BE2"
#define BROWN           "#A52A2A"
#define DARK_CYAN       "#008B8B"
#define DARK_MAGENTA    "#8B008B"
#define DARK_ORCHID     "#9932CC"
#define FUCHSIA         "#FF00FF"
#define MAROON          "#800000"
#define TEAL            "#008080"
#define WHITE           "#FFFFFF"


BlynkTimer timer;

BLYNK_CONNECTED() {
  // Synchronize button state on connection
  Blynk.syncVirtual(V5);
}

//Set valve open and closed states to zero
int dostate = 0;
int dcstate = 0;
int dolaststate = 0;
int dclaststate = 0;
unsigned long startmillis;
unsigned long currentmillis;
int Flag = 0;  //variable to set state of door
int openAlarm = 1800;  //Door open alarm time in seconds
int gpsFlag = 1;  //flag to track state of gps trigger
int gpsFlagLast = 1;  //flag to track last state of gps trigger
int modeSelect;


void setup()
{
  // Debug console
  Serial.begin(115200);

  // Hostname defaults to esp8266-[ChipID]
  ArduinoOTA.setHostname("garage_door");

  pinMode (DoorOPEN, INPUT_PULLUP);
  pinMode (DoorCLOSED, INPUT_PULLUP);
  pinMode(btnpin, OUTPUT);

  WiFi.config(arduino_ip, dns_ip, gateway_ip, subnet_mask);
  WiFi.begin(ssid, pass);

  if (WiFi.status() != WL_CONNECTED) {
    WiFi.begin(ssid, pass);
  }

  while (Blynk.connect() == false) //wait for Blynk to connect before proceeding
  {
    Blynk.begin(auth, ssid, pass, "45.55.96.146");
    // You can also specify server:
    //Blynk.begin(auth, ssid, pass, "xxxxxxxxxxxxxxx", 8082);
    //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,3), 9443);
  }

  MDNS.begin(host);

  httpUpdater.setup(&httpServer);
  httpServer.begin();

  MDNS.addService("http", "tcp", 80);
  Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);

  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // Setup a function to be called every 1 second
  timer.setInterval(500L, myTimerEvent);
}

//Read button widget state
BLYNK_WRITE(V5) {
  digitalWrite(btnpin, param.asInt()); // Sets btnPin HIGH or LOW depending on state of Button Widget.
}

BLYNK_WRITE(V9) //Auto / manual selector
{
  modeSelect = param.asInt();
}

//***************GPS Trigger*******************************
BLYNK_WRITE(V2) {  // Virtual pin connected to gps stream widget

  GpsParam gps(param); //establish gps stream parameter

  //Get latitude and longitude values
  float Latitude = gps.getLat();
  float Longitude = gps.getLon();

  // Establish geofence and set gps variable state
  if (Latitude > -32.097817 && Latitude < -32.096795 && Longitude > 115.852939 && Longitude < 115.854850)
  {
    gpsFlag = 1; //inside the geofence
    //gpsMillisStart = millis();
    
    // If inside the geofence and the door is closed and the app is set to auto and the gps state has changed, open the door, otherwise do nothing
  if (dcstate == LOW && modeSelect == 1 && gpsFlagLast == 0)
  {
    //  Open garage door
    digitalWrite(12, HIGH);
    delay(500);
    digitalWrite(12, LOW);
    //gpsMillisCurrent = millis();
   // if(gpsMillisCurrent - gpsMillisStart < hold)
  //  {
   //   gpsMillisCurrent = millis();
  //  }
  }
  
  }
  else
  {
    gpsFlag = 0; //outside the geofence
  }
 // gpsMillisStart = gpsMillisCurrent;
 delay(50);
  gpsFlagLast = gpsFlag; //track the gps geofence variable state so that the action doesn't repeat
  }

//**************************************************************

BLYNK_WRITE(V1) //Manual update of door state
{
  manupdate();
}

void myTimerEvent()
{

  //Read valve states
  dostate = digitalRead(DoorOPEN);
  dcstate = digitalRead(DoorCLOSED);
  Serial.println(dostate);
  Serial.println(dcstate);

  // Check wifi signal strength
  long rssi = WiFi.RSSI();
  Serial.print("RSSI:");
  Serial.println(rssi);
  Blynk.virtualWrite(V4, rssi); //Send wifi signal strength to blynk
  Blynk.virtualWrite(V7, gpsFlag); //Send gps flag to blynk
  Blynk.virtualWrite(V10, gpsFlagLast); //Send gps flag to blynk

  //Check if the door has been opened and advise by e-mail
  if (dostate == LOW && dolaststate != LOW) {
    startmillis = millis();
    Flag = 1;
    Blynk.email("Garage Door", "The garage door has been opened");
    Blynk.notify("The garage door has been opened");
    Serial.println("Garage has been opened");
  }
  
  //Check for door open and publish state and status to blynk
  if (dostate == LOW) {
    currentmillis = millis();
    Serial.println("Garage is Open");
    Blynk.setProperty(V6, "offLabel", "Garage OPEN");
    Blynk.setProperty(V6, "offBackColor", "#cc0000");
    Blynk.setProperty(V6, "offColor", WHITE);
    if ((currentmillis - startmillis) / 1000 >= openAlarm && Flag == 1) {
      Blynk.notify("Have you left the Garage Door open?");
      Flag = 0;
    }
  }

  //Check for door closed and publish state and status to blynk
  else if (dcstate == LOW) {
    Serial.println("Garage is closed");
    Blynk.setProperty(V6, "offLabel", "Garage CLOSED");
    Blynk.setProperty(V6, "offBackColor", "#33cc33");
    Blynk.setProperty(V6, "offColor", WHITE);
  }

  //If door isn't open or closed, it is between limits
  else {
    Serial.println("Between limits");
    Blynk.setProperty(V6, "offLabel", "Between limits");
    Blynk.setProperty(V6, "offBackColor", "#ff6600");
    Blynk.setProperty(V6, "offColor", WHITE);
  }

  //Check if the door has been closed and reset alarm timer
  if (dcstate == LOW && dclaststate != LOW) {
    startmillis = 0;
    currentmillis = 0;
  }

  // Send data to Blynk
  Blynk.virtualWrite(V0, dcstate); //Send door status to blynk

  dolaststate = dostate;
  dclaststate = dcstate;
}

void manupdate()
{
  //Check for door open and publish state and status to blynk
  if (dostate == LOW) {
    Serial.println("Garage is Open");
    Blynk.setProperty(V6, "offLabel", "Garage OPEN");
    Blynk.setProperty(V6, "offBackColor", "#cc0000");
    Blynk.setProperty(V6, "offColor", WHITE);
  }

  //Check for door closed and publish state and status to blynk
  else if (dcstate == LOW) {
    Serial.println("Garage is closed");
    Blynk.setProperty(V6, "offLabel", "Garage CLOSED");
    Blynk.setProperty(V6, "offBackColor", "#33cc33");
    Blynk.setProperty(V6, "offColor", WHITE);
  }

  //If door isn't open or closed, it is between limits
  else {
    Serial.println("Garage is between limits");
    Blynk.setProperty(V6, "offLabel", "Between limits");
    Blynk.setProperty(V6, "offBackColor", "#ff6600");
    Blynk.setProperty(V6, "offColor", WHITE);
  }

  // Send data to Blynk
  Blynk.virtualWrite(V0, dostate); //Send door status to blynk
}

void loop()
{
  Blynk.run();
  timer.run(); // Initiates BlynkTimer
  ArduinoOTA.handle();
  httpServer.handleClient();

}]

In terms of debugging steps 1. Have posted the flags into the app so I can monitor their values and when they change. Can clearly see that the states change as they should when I move into and out of the geofence 2. Removed the state tracking which confirmed that the door actuation works as expected when I move into the geofence. it just keeps actuating because the state isn't tracked. 3. tried inserting some delay statements and also using millis timer to delay the setting of the last flag state in case it was occurring to quickly. Neither did anything (you can see the millis timer code in the full code posting but have comented out as it didn't work)

I tried looking through your code, but there is way too much unnecessary material to follow the logic.

I strongly suggest to get just the geofence/door control and state logic working in completely stripped down version before adding all the other stuff.

Some comments:

It takes only one state variable to track the door state. You have two.

According to your first post, the following code section should open the door, but doesn’t. Why not? What are the values of each of the variables within the three logical tests? Why is door state not updated within the code that opens it?

  if (dcstate == LOW && modeSelect == 1 && gpsFlagLast == 0)
  {
    //  Open garage door
    digitalWrite(12, HIGH);
    delay(500);
    digitalWrite(12, LOW);
    //gpsMillisCurrent = millis();
   // if(gpsMillisCurrent - gpsMillisStart < hold)
  //  {
   //   gpsMillisCurrent = millis();
  //  }
  }
 
  }

Float variables have only 6-7 digits of accuracy, and do not support the precision that your code might require. Use 64 bit double if you can (not supported on AVR based Arduinos).

If Longitude is a 32 bit float

Longitude < 115.854850)

is approximately equivalent to:

Longitude < 115.855)

Finally, consumer GPS units are accurate to 2-3 meters only under ideal conditions, with a clear view of the sky and a nearby DGPS station. Under other conditions (like under a garage roof), errors as large as 50 meters are common, and you may not get a fix at all.

Hi, Firstly, many thanks for taking a look at the code - I really appreciate it. I don't think I am that far away from success and, as I said in my de-bugging notes, I am tracking the state variable and can clearly see it change to zero when I exit the geofence and back to 1 when I re-enter. The last state also changes as it should. The first two parts of the conditional if statement I have confirmed as correct so it appears to me to have something to do with the state tracking and not seeing the last state variable as being different to the current state variable. I have confirmed this by removing the state tracking and the system works as intended. As soon as I enter the geofence, the door does actuate. Only problem is that it keeps actuating because I am not tracking the state. As soon as I re-instate the state tracking, actuation stops. Somehow, in the conditional if statement, the current and last states are registering as the same and the if statement registers as false and does nothing. I thought I might have to install some sort of delay between the gps data gathering and the if statement to allow the gps coordinates to be recorded. Not sure this makes sense to me but couldn't think of much else. I will try to scale back the code as you suggest although most of it relates to enabling me to do OTA updates and track the door position state so not sure how much I can take out.

Thanks again Steve