Update gps coordinates inside while loop (AssetTrackerRK (0.1.3))

Hi guys,

I am stuck with an issue of a not being able to update my gps coordinates whilst in a while loop (and so being unable to break the out of it). I am using the AssettrackerRK to access the distanceBetween function from TinyGPS.
Basically I have an array with some coordinates and the program runs through to check if I am close to any of these specific points. That part works great. Whilst within a certain threshold of a certain point I need it to execute some specific code (run a dc motor and publish some stuff) and stop worrying about the other locations for the time being.
My problem is that as soon as I get into this while loop I no longer get any update on the gps position, i.e. the coordinates I get stay in the same position as when entering the loop so that it gets stuck there.
I have feeling this might be very simple but I just cant figure it out :confused:

As always very grateful for any ideas / suggestions:slight_smile:

this is the offending code:

void closeTo() {
    
int numberLocation = 5;
    
static const double POS_LAT[] = {51.508131, 56.164644, 56.171765, 56.172123, 56.171662}; 
static const double POS_LON[]= {-0.128002, 10.188129, 10.189953, 10.189050, 10.188551};
     
const char * locations[] = {"london", "botanisk have", "roof", "office window", "entrance opp"};

     
      for (int i=0; i < numberLocation; i++){
     	    
	  metersToLocation =
      (unsigned long)TinyGPSPlus::distanceBetween(gps.location.lat(), gps.location.lng(), POS_LAT[i], POS_LON[i]);
      
      digitalWrite(ledPin,LOW);
      analogWrite(transistorPin, 0);
      
      Serial.print(" distance to: ");
      Serial.print(locations[i]);
      Serial.print(" ");
      Serial.print(metersToLocation);
      Serial.println();	

      while(metersToLocation<=maxDistance){
          
          i =i;  //this might be redundant
          
          int rotateMotor = map(metersToLocation,maxDistance,0,150,255); 
          digitalWrite(ledPin,HIGH);
          analogWrite(transistorPin, rotateMotor);
          
          Serial.println("close");
          Particle.publish("close", String(metersToLocation), 60, PRIVATE);
          Particle.publish("to", String(locations[i]), 60, PRIVATE);
          
      Serial.print(" distance to: ");
      Serial.print(locations[i]);
      Serial.print(" ");
      Serial.print(metersToLocation);
      Serial.println();	
   
      metersToLocation =
      (unsigned long)TinyGPSPlus::distanceBetween(gps.location.lat(), gps.location.lng(), POS_LAT[i], POS_LON[i]);
      
      
      Serial.print(gps.location.lat());
            Serial.print(", ");
       Serial.println(gps.location.lng());
      
        }
	}  
}

and this is the entire thing (GPS is towards the end):

// This #include statement was automatically added by the Particle IDE.
#include "Particle.h"

// Port of TinyGPS for the Particle AssetTracker
// https://github.com/mikalhart/TinyGPSPlus

#include "TinyGPS++.h"


bool publish = true; // set to true if elctron should publish
const unsigned long PUBLISH_PERIOD = 10000;
const unsigned long SERIAL_PERIOD = 5000;

// The TinyGPS++ object
TinyGPSPlus gps;
unsigned long lastSerial = 0;
unsigned long lastPublish = 0;
unsigned long startFix = 0;
bool gettingFix = false;
unsigned long metersToLocation;
int maxDistance = 35;
bool close = false;


//WEATHER:
int i = 0;
bool weatherGood = false;  
char* R = "weather";
//how often to call the weather 
long interval = 60000*10;  
long previousMillis = 0;  

//conditions
int minTemp = 15;
int minHum = 30; 
int maxWind = 40;
float maxPrep = 10; 

//actuators:
const int transistorPin = D2; 
const int ledPin = D7;


void affect(void);
bool trigger = false;


SYSTEM_THREAD(ENABLED);

void displayInfo(); // forward declaration
String data = String(10);


void setup()
{
    
    // Subscribe to the integration response event
    Particle.subscribe("hook-response/WeatherAarhus", myHandler, MY_DEVICES);
    Particle.publish("WeatherAarhus", data, PRIVATE);
	Serial.begin(9600);
	// The GPS module on the AssetTracker is connected to Serial1 and D6
	Serial1.begin(9600);

	// Settings D6 LOW powers up the GPS module
    pinMode(D6, OUTPUT);
    digitalWrite(D6, LOW);
    startFix = millis();
    gettingFix = true;
    
    pinMode(transistorPin, OUTPUT);
    pinMode(ledPin, OUTPUT);
    
    pinMode(D5, INPUT_PULLUP);
    attachInterrupt(D5, affect, FALLING);
    
    
}

void loop()
{
    
// INTERRUPT:_______________________________________
      if (trigger) {
          Particle.publish("affect", NULL, PRIVATE);
          displayInfo();
        }
     trigger = false; 

  //WEATHER:_______________________________________  

    unsigned long currentMillis = millis();  
  
   if(currentMillis - previousMillis > interval) {
    // save the last time you read sensor
    previousMillis = currentMillis;  
  
  Particle.publish("WeatherAarhus", data, PRIVATE);
   }
   
   
 //GPS:____________________________________________
	while (Serial1.available() > 0) {
		if (gps.encode(Serial1.read())) {
			displayInfo();
		}
	}
	
	closeTo();

	
}



void affect()
{
  trigger = true;
}

//get weatherdata from Wunderground.api
void myHandler(const char *event, const char *data) {
    // Handle the integration response  
    
    i++;
    Serial.print(i);
    Serial.print(event);
    String str = String(data);
    char strBuffer[strlen(data)];
    str.toCharArray(strBuffer, strlen(data));
    
    char* token = strtok(strBuffer, "\"~");
    atoi(strtok(NULL, "\"~"));
    char* weather = strtok(NULL, "\"~");
    int temp = atoi(strtok(NULL, "\"~"));
    int humidity = atoi(strtok(NULL, "\"~"));
    int wind = atoi(strtok(NULL, "\"~"));
    int prep = atoi(strtok(NULL, "\"~"));
    
     if(strcmp(weather,R)!=0){
          Serial.print("strings not same");
        if (temp > minTemp && humidity > minHum && wind < maxWind && prep < maxPrep) 
        {weatherGood = true;}
        }
        else {weatherGood = false;}
   
/*    Serial.print("weather: ");
    Serial.println(weather);
    Serial.print("temp: "); 
    Serial.println(temp);
    Serial.print("humidity: "); 
    Serial.println(humidity);
    Serial.print("wind_kph: "); 
    Serial.println(wind);
    Serial.print("precip_today: "); 
    Serial.println(prep);
    Serial.print("weatherGood: "); 
    Serial.println(weatherGood);*/

  delay(1);    
}


//GPS:-----------------------------------------------------------

void closeTo() {
    
int numberLocation = 5;
    
static const double POS_LAT[] = {51.508131, 56.164644, 56.171765, 56.172123, 56.171662}; 
static const double POS_LON[]= {-0.128002, 10.188129, 10.189953, 10.189050, 10.188551};
     
const char * locations[] = {"london", "botanisk have", "roof", "office window", "entrance opp"};

     
      for (int i=0; i < numberLocation; i++){
     	    
	  metersToLocation =
      (unsigned long)TinyGPSPlus::distanceBetween(gps.location.lat(), gps.location.lng(), POS_LAT[i], POS_LON[i]);
      
      digitalWrite(ledPin,LOW);
      analogWrite(transistorPin, 0);
      
      Serial.print(" distance to: ");
      Serial.print(locations[i]);
      Serial.print(" ");
      Serial.print(metersToLocation);
      Serial.println();	

      while(metersToLocation<=maxDistance){
          
          i =i; //this might be redundant
          
          displayInfo();
          
          int rotateMotor = map(metersToLocation,maxDistance,0,150,255); 
          digitalWrite(ledPin,HIGH);
          analogWrite(transistorPin, rotateMotor);
          
          Serial.println("close");
          Particle.publish("close", String(metersToLocation), 60, PRIVATE);
          Particle.publish("to", String(locations[i]), 60, PRIVATE);
          
      Serial.print(" distance to: ");
      Serial.print(locations[i]);
      Serial.print(" ");
      Serial.print(metersToLocation);
      Serial.println();	
   
      metersToLocation =
      (unsigned long)TinyGPSPlus::distanceBetween(gps.location.lat(), gps.location.lng(), POS_LAT[i], POS_LON[i]);
      
      
      Serial.print(gps.location.lat());
            Serial.print(", ");
       Serial.println(gps.location.lng());
      
        }
	}  
}	
	
   

void displayInfo()
{
 
	if (millis() - lastSerial >= SERIAL_PERIOD) {
		lastSerial = millis();
		
		Serial.print("sat: ");
        printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
        Serial.println();	
        
		char buf[128];
		if (gps.location.isValid()) {
			snprintf(buf, sizeof(buf), "%f,%f", gps.location.lat(), gps.location.lng());
			if (gettingFix) {
				gettingFix = false;
				unsigned long elapsed = millis() - startFix;
				Serial.printlnf("%lu milliseconds to get GPS fix", elapsed);
			}
		}
		else {
			strcpy(buf, "no location");
			if (!gettingFix) {
				gettingFix = true;
				startFix = millis();
			}
		}
		Serial.println(buf);

if (publish) {
    if (Particle.connected()) {
	if (millis() - lastPublish >= PUBLISH_PERIOD) {
			
				Particle.publish("gps", buf);
                lastPublish = millis();
			}
		   }
		}
	}

}

static void printInt(unsigned long val, bool valid, int len)
{
  char sz[32] = "*****************";
  if (valid)
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
}

Keeping your code trapped in a while() like this isnā€™t good practice anyway.
You could use a FSM in loop() which acts on each iteration of loop() either upon a state like fsm_CLOSE_TO, fsm_NOT_CLOSE_TO or any list of extra fsm_XXXX states.
And common to all (or most) states would be the GPS acquisition.

This AssetTrackerRK sample does also use an FSM to do its job and may be a good place to look at.

3 Likes

ah yes! that makes a lot more sense. THANKS!!!

@ScruffR Iā€™ve been testing RKā€™s Asset Tracker Library and after playing around I have learned some about the FSM and its structure.

In your own words why/when is using the FSM coding structure a good practice.

Iā€™m asking you because you have a way of summing up complex ideas in a short explanation.

Iā€™d use an FSM in cases where a task can be split up in more than just two or three sub-steps (states) where one step will lead to the next (not necessarily in a fixed order).

In short an FSM does

  1. check current state
  2. do work for current state (which prepares next state)
  3. set state to follow
  4. leave

Before and after the FSM block you can put code common to all states (post FSM blocks could be skipped via ā€œprematureā€ return statements)

Some of the advantages of an FSM are

  • you can easily influence the code flow (proceede to a desired step, skip step, fast forward, ā€¦) by setting the state (e.g. via Particle.function())
  • they support the non-blocking coding paradigm as each step should be just a short block without trapping loops
  • they are usually self explanatory (when using descriptive state names) and lend themselves to a mental picture of the current state of the device itself
  • with switch() case FSMs you can have the standard flow of states shortened by fall through cases (not requiring to drop out of loop() and reenter)
  • additional states can easily be added

Just to name a few reasons to consider FSMs

1 Like

worked really well, for reference hereā€™s the new loop:

   void loop()
{
    
// INTERRUPT:_______________________________________
      if (trigger) {
          Particle.publish("affect", NULL, PRIVATE);
          displayInfo();
        }
     trigger = false; 

  //WEATHER:_______________________________________  

    unsigned long currentMillis = millis();  
  
   if(currentMillis - previousMillis > interval) {
    // save the last time you read sensor
    previousMillis = currentMillis;  
  
  Particle.publish("WeatherAarhus", data, PRIVATE);
   }
   
   
 //GPS:____________________________________________
	while (Serial1.available() > 0) {
		if (gps.encode(Serial1.read())) {
			displayInfo();
		}
	}
	
	
	    
	 metersToLocation =
     (unsigned long)TinyGPSPlus::distanceBetween(gps.location.lat(), gps.location.lng(), POS_LAT[locNo], POS_LON[locNo]);
     if (metersToLocation<=maxDistance) {
         
                         state = CLOSE_STATE;
                }
                     else {state = NOTCLOSE_STATE;}

	switch(state) {
	    
		case NOTCLOSE_STATE:    
		
		digitalWrite(ledPin,LOW);
        analogWrite(transistorPin, 0);
        locNo++;
        if(locNo>=numberLocation) {locNo = 0;}
      
            Serial.print(" distance to: ");
            Serial.print(locations[locNo]);
            Serial.print(" ");
            Serial.print(metersToLocation);
            Serial.println();
    
		break;

	case CLOSE_STATE: 
	
	      Particle.publish("to", String(locations[locNo]), 60, PRIVATE);
          Particle.publish("close", String(metersToLocation), 60, PRIVATE);

	digitalWrite(ledPin,HIGH);

          int rotateMotor = map(metersToLocation,maxDistance,0,150,255); 
      	  analogWrite(transistorPin, rotateMotor);

            Serial.println("close");
            Serial.print(" distance to: ");
            Serial.print(locations[locNo]);
            Serial.print(" ");
            Serial.print(metersToLocation);
            Serial.println();
            

      
		break;
	}
	
}//DO NOT EVER MESS WITH THE END OF THE LOOP!!
1 Like