Hello,
I have modified the Tracker Edge firmware to batch together 10 seconds worth of location data and publish using a CloudEvent
publish as recommended in the documentation. I am running DeviceOS 6.3.0
.
To ensure I am only collecting location data when the vehicle is moving, I have enabled Motion Sensitivity
and set Maximum location update frequency
to 10 in the Fleet Config. I use the regLocGenCallback
to set a flag, so that my code in the main loop can do what it needs to do without blocking.
To make sure I'm not wasting data ops, I have commented out location_publish();
around line 1120 in tracker_location.cpp
. This prevents the normal loc
publish from happening. This is the only edit i've made outside of main.cpp
My code works well for a while. However, I'm finding that if I leave my device for a few hours, it freezes and becomes unreachable via the cloud console. The GPS lock LED remains on, and the cloud LED remains breathing, but the device doesn't publish or respond to anything.
I think I have a slow acting bug in my code, or I'm blocking an essential process. The only way to get the device back responsive it to open the case up and press the reset key on the PCB.
Does anyone have any ideas where I might have a bug? Everything compiles fine and runs well for upto a few hours before freezing. Code below:
#include "Particle.h"
#include "tracker_config.h"
#include "tracker.h"
#include "bmi160.h" // Add the IMU
#include "tracker_cellular.h"
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 },
});
// Define a cache size
const int MAX_SAMPLES = 10;
// Define a cache index
int cacheIndex = 0;
int cacheSize = 1024;
int timer = 0;
bool MOVING = FALSE;
bool CACHED = FALSE;
// Create a 1024 byte cache
char cache[1024];
// Set up a global JSON Writer
JSONBufferWriter json(cache, sizeof(cache) -1);
// 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
if (MOVING) {
// If we're caching for the first time
if (cacheIndex == 0) {
// Clear the cache
memset(cache, 0, sizeof(cache));
// Re-init the JSON Writer
json = JSONBufferWriter(cache, sizeof(cache) -1);
// Call the cache location function
cacheLocation(json);
// Increment the cache index
cacheIndex++;
// Set the timer to now
timer = millis();
} else if (millis() - timer >= 1000) {
// 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 >= MAX_SAMPLES) {
CloudEvent event;
event.clear();
event.name("loc-cache");
event.data(cache);
Particle.publish(event);
// Reset the cache index
cacheIndex = 0;
// Reset the flags
MOVING = FALSE;
CACHED = FALSE;
}
}
// New function
void locationCallback(JSONWriter &writer, LocationPoint &point, const void *context) {
// Set the moving flag true
MOVING = TRUE;
}
void cacheLocation(JSONBufferWriter &jsonWriter) {
// Create placeholder location point object
LocationPoint gpsLock;
// Retreive current location
Tracker::instance().locationService.getLocation(gpsLock);
// Set GPS lock flag
bool locked = gpsLock.locked;
// If this is the first entry into the cache
if (cacheIndex == 0) {
// Create main object for entire publish
jsonWriter.beginObject();
// Create status object
jsonWriter.name("health").beginObject();
// Add time
jsonWriter.name("time").value((unsigned int) gpsLock.epochTime);
// Add cellular signal strength
CellularSignal signal;
if(!TrackerCellular::instance().getSignal(signal)) {
jsonWriter.name("cell").value(signal.getStrength(), 1);
}
// Add battery charge state
bool batFlag = FALSE;
int batState = System.batteryState();
if ( batState == BATTERY_STATE_NOT_CHARGING ) { jsonWriter.name("chrg").value(1); batFlag = TRUE; } else
if ( batState == BATTERY_STATE_CHARGING ) { jsonWriter.name("chrg").value(2); batFlag = TRUE; } else
if ( batState == BATTERY_STATE_DISCHARGING ) { jsonWriter.name("chrg").value(3); batFlag = TRUE; } else
if ( batState == BATTERY_STATE_CHARGED ) { jsonWriter.name("chrg").value(4); batFlag = TRUE; };
// If battery is in a good state to continue
if (batFlag == TRUE) {
// Get the battery charge %
float bat = System.batteryCharge();
// Add battery charge %
if(bat >= 0 && bat <= 100) { jsonWriter.name("batt").value((int) bat); };
}
// Add SoM temperature
jsonWriter.name("temp").value(get_temperature(), 1);
// Close the status object
jsonWriter.endObject();
};
if (locked) {
// We have GPS lock, so set CACHED flag to TRUE
CACHED = TRUE;
// Set the name of this location sample to the index
String name = "";
name += cacheIndex;
// Start a new object for this location sample
jsonWriter.name(name).beginObject();
// Write the GPS coordinates
jsonWriter.name("lat").value(gpsLock.latitude, 5);
jsonWriter.name("lon").value(gpsLock.longitude, 5);
jsonWriter.name("alt").value((int) gpsLock.altitude);
// Write the speed and heading
jsonWriter.name("h").value((unsigned int) gpsLock.heading);
jsonWriter.name("s").value(gpsLock.speed, 2);
// Get the accelerometer data
Bmi160Accelerometer data;
int ret = BMI160.getAccelerometer(data);
// Write the accelerometer data
if (ret == SYSTEM_ERROR_NONE) {
jsonWriter.name("x").value(data.x,2);
jsonWriter.name("y").value(data.y,2);
jsonWriter.name("z").value(data.z,2);
}
// End the object
jsonWriter.endObject();
}
// If we've collected enough samples, close the JSON object
if (cacheIndex == (MAX_SAMPLES-1)) { jsonWriter.endObject(); };
}