Error with GeoLocation

Hello,

I have been experimenting with the GeoLocation API recently, and was attempting to use the locationCallback to store the result as a Particle.variable. My code is here:

#include <google-maps-device-locator.h>

GoogleMapsDeviceLocator locator;
String locationInfo;
void setup() {
  Serial.begin(9600);
  // Scan for visible networks and publish to the cloud every 30 seconds
  // Pass the returned location to be handled by the locationCallback() method
  locator.withSubscribe(locationCallback).withLocatePeriodic(30);
}

void locationCallback(float lat, float lon, float accuracy) {
    locationInfo = String(lat)+","+String(lon)+","+String(accuracy);
  Particle.variable("locationInfo", locationInfo);
}

void loop() {
  locator.loop();
}

However, the locator works fine but locationCallback is never entered, and thus the variable is never posted.

Hi there,

I am having the same problem; can’t get the coordinates because the locationCallback loop doesn’t trigger. Any ideas?

Check the event log at https://console.particle.io. If there are error code 404 entries in the event log after the raw data is published, then the location is not known, and the callback will not be called.

Actually, I’m getting a hook sent to the console with correct coordinates:

{“data”:“43.661738199999995,-79.3951125,654”,“ttl”:60,“published_at”:“2017-10-17T18:01:40.456Z”,“coreid”:“particle-internal”,“name”:“hook-response/deviceLocator/22002c001951353338363036/0”}

So it is finding a location, right?

To add some clarification:

Basically, if you run the code @aj8uppal posted, in the event log, you will always see this:

Presumably meaning the locator found a position and sent the hook with the coordinates.

How can I get fetch these coordinates from the hook without using the locationcallback() function? Because, as explained above, that function doesn’t seem to trigger reliably, and without it, I can’t fetch the coordinates.

Thanks again!

All,

I figured it out…well, a workaround…

I realized through testing that locationcallback() triggers after locator.loop() triggers the deviceLocator webhook. If the callback doesn’t trigger, it’s likely a connection issue - i.e. the particle is unable to fetch data from the particle cloud…presumably that is where it is fetching the data because I assume it’s the same as Particle.subscribe(). I tried wrapping the locator.loop() function with a while loop to give the callback function time to trigger, but that didn’t do much. What worked for me was the following:

void setup() {
    locator.withSubscribe(locationCallback).withLocatePeriodic(5); //withLocateOnce will NOT work!
}

void loop() {
    uint32_t ms = millis();
    while(millis()-ms < 18500){
        locator.loop();
        delay(1500);
    }
    Particle.disconnect();
    delay(3000);
    Particle.connect();
}

void locationCallback(float lat, float lon, float accuracy) {
    
    latitude = lat;
    longitude = lon;
    
    //put your code here
    
    //Deep Sleep
    System.sleep(SLEEP_MODE_DEEP, 7200); 
}

If the callback doesn’t trigger, locator.loop() will keep executing (about 2 or 3 times); if callback still doesn’t trigger, the particle will disconnect and reconnect, and after the first hook is fetched after that it ALWAYS enter the callback function (at least from my 4 hours of testing today that seems to be the case). Will provide an update if things change.

Alternatively, you can just only have locator.loop(); delay(2000); inside void loop(). In this case, the loop will keep fetching webhooks for the GPS repeatedly. After a couple of fetches, if the callback doesn’t trigger, it will flash cyan quickly, the event log will say ‘device came online’ and almost always the callback will trigger after the next hook.

So these are two workarounds to the problem.

The last workaround is to retain the first lat/lon value in eeprom and re-use that if callback doesn’t trigger.

There you go…3 workarounds!

Sorry, I’m late to the show here, but the actual reason why the variable is not “posted” is that in the OP Particle.variable() is used wrong - Particle.variable()s are never posted.
Try this instead

#include <google-maps-device-locator.h>

GoogleMapsDeviceLocator locator;
String locationInfo;
void setup() {
  Serial.begin(9600);
  
  // this is where cloud variables are REGISTERED, they don't get posted by user code
  Particle.variable("locationInfo", locationInfo);

  // Scan for visible networks and publish to the cloud every 30 seconds
  // Pass the returned location to be handled by the locationCallback() method
  locator.withSubscribe(locationCallback).withLocatePeriodic(30);
}

void locationCallback(float lat, float lon, float accuracy) {
    locationInfo = String(lat)+","+String(lon)+","+String(accuracy);
    // this is NOT the place to put that statement
    // Particle.variable("locationInfo", locationInfo);
}

void loop() {
  locator.loop();
}

You only register a Particle.variable() once (most commonly) in setup() and from then on the underlying variable (in this case String locationInfo) will be accessible from the cloud.

Thanks Scruff, but do you know why, in my case, locationcallback() doesn’t always trigger reliably? I haven’t tested it enough, but I’ve seen one case where the code got stuck in locator.loop() for a while. It must’ve triggered at least 10 webhooks in a row without a single callback.

This is the implementation of locator.loop()

void GoogleMapsDeviceLocator::loop() {
	switch(state) {
	case CONNECT_WAIT_STATE:
		if (Particle.connected()) {
			state = CONNECTED_WAIT_STATE;
			stateTime = millis();
		}
		break;

	case CONNECTED_WAIT_STATE:
		if (millis() - stateTime >= waitAfterConnect) {
			// Wait several seconds after connecting before doing the location
			if (locatorMode == LOCATOR_MODE_ONCE) {
				publishLocation();

				state = IDLE_STATE;
			}
			else
			if (locatorMode == LOCATOR_MODE_MANUAL) {
				state = IDLE_STATE;
			}
			else {
				state = CONNECTED_STATE;
				stateTime = millis() - periodMs;
			}
		}
		break;

	case CONNECTED_STATE:
		if (Particle.connected()) {
			if (millis() - stateTime >= periodMs) {
				stateTime = millis();
				publishLocation();
			}
		}
		else {
			// We have disconnected, rec
			state = CONNECT_WAIT_STATE;
		}
		break;


	case IDLE_STATE:
		// Just hang out here forever (entered only on LOCATOR_MODE_ONCE)
		break;
	}

}

And here is the internal handler that only calls the callback when all conditions are satisfied

void GoogleMapsDeviceLocator::subscriptionHandler(const char *event, const char *data) {
	// event: hook-response/deviceLocator/<deviceid>/0

	if (callback) {
		// float lat, float lon, float accuracy
		char *mutableCopy = strdup(data);
		char *part, *end;
		float lat, lon, accuracy;

		part = strtok_r(mutableCopy, ",", &end);
		if (part) {
			lat = atof(part);
			part = strtok_r(NULL, ",", &end);
			if (part) {
				lon = atof(part);
				part = strtok_r(NULL, ",", &end);
				if (part) {
					accuracy = atof(part);

					(*callback)(lat, lon, accuracy);
				}
			}
		}

		free(mutableCopy);
	}
}

If you can post the responses for the instances where the callback wasn’t triggered we might see which of the conditions might have failed and hence baild out before calling the callback.
You can also see what the serial output of the library tells you about its internal proceedings.