Thanks @rickkas7. This idea works well.
My program now runs like this:
- I have the Fleet Config set to publish location updates no faster than once every 5 seconds
- I have modified the locationCallback to flip a 'moving' flag
- In the program loop I add new location data to a cache every second
- When the cache is full I publish it using cloud event publish.
This works well for the first publish. My C++ skills are limited though, and something about the way I have it set up is resulting in subsequent publishes appearing blank.
I'm not sure where the issue is. I've tried using memset to zero out the cache after each publish but that hasn't helped. My code is below.
#include "Particle.h"
#include "tracker_config.h"
#include "tracker.h"
#include "bmi160.h" // Add the IMU
SYSTEM_MODE(SEMI_AUTOMATIC);
#if TRACKER_PRODUCT_NEEDED
PRODUCT_ID(TRACKER_PRODUCT_ID);
#endif // TRACKER_PRODUCT_NEEDED
PRODUCT_VERSION(1);
STARTUP(
Tracker::startup();
);
SerialLogHandler logHandler(115200, LOG_LEVEL_TRACE, {
{ "app.gps.nmea", LOG_LEVEL_INFO },
{ "app.gps.ubx", LOG_LEVEL_INFO },
{ "ncp.at", LOG_LEVEL_INFO },
{ "net.ppp.client", LOG_LEVEL_INFO },
});
const int CACHE_SIZE = 5;
int cacheIndex = 0;
int timer = 0;
bool MOVING = FALSE;
char cache[1024];
JSONBufferWriter json(cache, sizeof(cache) -1);
CloudEvent event;
// Forward declarations
void locationCallback(JSONWriter &writer, LocationPoint &point, const void *context);
void cacheLocation(JSONBufferWriter &jsonWriter);
void setup() {
Tracker::instance().location.regLocGenCallback(locationCallback);
Tracker::instance().init();
}
void loop() {
Tracker::instance().loop();
// If we're moving and 1 second has elapsed
if (MOVING && (millis() - timer >= 1000)) {
// If we're caching for the first time
if (cacheIndex == 0) {
// Clear the cache
memset(cache, 0, sizeof(cache));
// Re-init the JSON Writer
JSONBufferWriter json(cache, sizeof(cache) -1);
}
// Call the cache location function
cacheLocation(json);
// Increment the cache index
cacheIndex++;
// Set the timer to now
timer = millis();
}
// If the cache is full
if (cacheIndex >= CACHE_SIZE) {
// Publish the cache
event.name("loc-cache");
event.data(cache);
Particle.publish(event);
// Reset the cache index
cacheIndex = 0;
// Reset the moving flag
MOVING = FALSE;
}
}
// New function
void locationCallback(JSONWriter &writer, LocationPoint &point, const void *context) {
// Set the moving flag true
MOVING = TRUE;
// Set the timer to now
timer = millis();
Bmi160Accelerometer data;
int ret = BMI160.getAccelerometer(data);
if (ret == SYSTEM_ERROR_NONE) {
writer.name("xAcc").value(data.x,3);
writer.name("yAcc").value(data.y,3);
writer.name("zAcc").value(data.z,3);
}
}
void cacheLocation(JSONBufferWriter &jsonWriter) {
// The high frequency location data is a limited dataset to conserve data allowance
// It will contain time, lon, lat, alt, spd, hd, xAcc, yAcc, zAcc
// Are time and ID contained in the publish by default?
// Gather current location information and status
LocationPoint gpsLock;
Tracker::instance().locationService.getLocation(gpsLock);
// Set GPS lock flag
bool locked = TRUE; //gpsLock.locked;
if (cacheIndex == 0) { jsonWriter.beginObject(); };
if (locked) {
String name = "loc";
name += cacheIndex;
jsonWriter.name(name).beginObject();
jsonWriter.name("time").value((unsigned int) gpsLock.epochTime);
jsonWriter.name("lat").value(gpsLock.latitude, 6);
jsonWriter.name("lon").value(gpsLock.longitude, 6);
jsonWriter.name("alt").value(gpsLock.altitude, 3);
jsonWriter.name("hd").value(gpsLock.heading, 2);
jsonWriter.name("spd").value(gpsLock.speed, 2);
Bmi160Accelerometer data;
int ret = BMI160.getAccelerometer(data);
if (ret == SYSTEM_ERROR_NONE) {
jsonWriter.name("xAcc").value(data.x,3);
jsonWriter.name("yAcc").value(data.y,3);
jsonWriter.name("zAcc").value(data.z,3);
}
jsonWriter.endObject();
}
if (cacheIndex == (CACHE_SIZE-1)) { jsonWriter.endObject(); };
}