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.
1 Like
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.