I am trying to add a couple tracker features to one of my devices I have set up to monitor a microphone. I originally had an SD card reader for some data collection that I am no longer using. When I flash just the sound monitoring code I have no problems with the device and it does everything I expect. However, when I flash the code below it keeps resetting it self showing a “pin reset”. I cut the power and ground to the SD card reader, but I am still having the pin reset issue. I am not sure where to go from here.
The features I am adding are a GPS sync using the button on the side of the monitor one. I am also not sure if there is a better way to add these feature than starting with the tracker firmware and adding in sound monitoring code.
If anyone has an examples on bringing in the just the button and GNSS features from the tracker.h instead of what I did, I would love to see it.
Thanks in advanced
#include "Particle.h"
#include "FftComplex.h"
#include <complex>
#include <vector>
#include <cmath>
// Tracker Edge (release/v18)
#include "Tracker_Config_User.h"
#include "tracker_config.h"
#include "tracker.h"
#include "location_service.h"
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// ---------- logging ----------
SerialLogHandler logHandler(LOG_LEVEL_INFO);
// ---------- sampling / timing ----------
constexpr uint32_t SAMPLE_FREQ = 8000;
constexpr size_t NUM_SAMPLES = 1024;
constexpr uint32_t SAMPLE_INTERVAL_US = 1000000UL / SAMPLE_FREQ;
constexpr unsigned long WINDOW_MS = 2000;
// ---------- publish interval ----------
unsigned long pubIntervalMin = 10UL;
unsigned long pubIntervalMs = pubIntervalMin * 60000UL;
// ---------- FFT bands ----------
double lowHz1 = 900.0, highHz1 = 1200.0;
double lowHz2 = 1900.0, highHz2 = 2300.0;
// ---------- analog cleanup ----------
double cleanupHz = 3000.0;
static float lp_prev = 0.0f;
static float lp_alpha = 0.0f;
// ---------- pins ----------
constexpr int MIC_PIN = A0;
constexpr int GAIN_PIN = A1;
// ---------- buffers & state ----------
static uint16_t rawBuffer[NUM_SAMPLES];
static float floatBuffer[NUM_SAMPLES];
std::vector<std::complex<double>> fftBuf(NUM_SAMPLES);
double winMax1=0, winFreq1=0, winMax2=0, winFreq2=0;
double pubMax1=0, pubFreq1=0, pubMax2=0, pubFreq2=0;
unsigned long windowStart=0, publishStart=0;
uint32_t lastSampleTime=0;
bool samplingEnabled = true;
int gainState = 1;
// ---------- LED helpers ----------
static inline void led_take(){ RGB.control(true); RGB.brightness(255); }
static inline void led_free(){ RGB.control(false); }
static inline void led_red(){ RGB.color(255,0,0); }
static inline void led_green(){ RGB.color(0,255,0); }
static inline void led_off(){ RGB.color(0,0,0); }
static inline void led_yellow(){ RGB.color(255,255,0); }
// ---------- GPS state ----------
enum class GpsMode : uint8_t { Idle, Seeking };
GpsMode gpsMode = GpsMode::Idle;
LocationService& GPS = LocationService::instance();
static LocationPoint lastFix;
unsigned long gpsSeekStartMs = 0;
constexpr unsigned long GPS_TIMEOUT_MS = 5UL * 60UL * 1000UL;
// triple-press
uint8_t pressAggCount = 0;
unsigned long pressWindowStartMs = 0;
constexpr unsigned long PRESS_WINDOW_MS = 1500;
constexpr uint8_t PRESS_TRIGGER = 3;
// ---------- fwd decl ----------
static inline void computeAlpha();
static inline void peakInBand(const std::vector<std::complex<double>>&, size_t, double, double, double&, size_t&);
void gpsEnterSeek(); void gpsExitSeek(bool hadFix); bool gpsHasFix(); void handleButtonPressAggregation();
// ---------- helpers ----------
static inline void computeAlpha(){
float dt = 1.0f / SAMPLE_FREQ;
float RC = 1.0f / (2.0f * M_PI * cleanupHz);
lp_alpha = dt / (RC + dt);
}
static inline void peakInBand(const std::vector<std::complex<double>>& X, size_t N, double lo, double hi, double& outMag, size_t& outBin){
size_t s = (size_t)(lo * N / SAMPLE_FREQ);
size_t e = (size_t)(hi * N / SAMPLE_FREQ);
if (e >= N) e = N - 1;
double m=0; size_t mb=s;
for (size_t k=s; k<=e; ++k){ double a=std::abs(X[k]); if (a>m){m=a; mb=k;} }
outMag=m; outBin=mb;
}
// ---------- cloud API ----------
int startSampling(const String&){ if (gpsMode==GpsMode::Idle) samplingEnabled=true; return 1; }
int stopSampling (const String&){ samplingEnabled=false; return 1; }
int setB1L(String s){ double v=s.toFloat(); if(v>0 && v<highHz1){lowHz1=v; return 1;} return -1; }
int setB1H(String s){ double v=s.toFloat(); if(v>lowHz1 && v<SAMPLE_FREQ/2){highHz1=v; return 1;} return -1; }
int setB2L(String s){ double v=s.toFloat(); if(v>0 && v<highHz2){lowHz2=v; return 1;} return -1; }
int setB2H(String s){ double v=s.toFloat(); if(v>lowHz2 && v<SAMPLE_FREQ/2){highHz2=v; return 1;} return -1; }
int setCleanup(String s){ double v=s.toFloat(); if(v>100.0 && v<SAMPLE_FREQ/2){cleanupHz=v; computeAlpha(); return 1;} return -1; }
int setPubInterval(String s){ unsigned long v=s.toInt(); if(v>=1 && v<=1440){pubIntervalMin=v; pubIntervalMs=v*60000UL; return 1;} return -1; }
int setGain(String s){ String cmd=s; cmd.toLowerCase(); if(cmd=="high"||s.toInt()==1){digitalWrite(GAIN_PIN,HIGH); gainState=1; return 1;} if(cmd=="low"||s.toInt()==0){digitalWrite(GAIN_PIN,LOW); gainState=0; return 1;} return -1; }
int gpsStartFn(const String&){ gpsEnterSeek(); return 1; }
int gpsStopFn (const String&){ gpsExitSeek(false); return 1; }
// ---------- setup ----------
void setup(){
Serial.begin(115200);
waitFor(Serial.isConnected, 2000);
pinMode(GAIN_PIN, OUTPUT);
digitalWrite(GAIN_PIN, HIGH);
Particle.variable("lowHz1", lowHz1);
Particle.variable("highHz1", highHz1);
Particle.variable("lowHz2", lowHz2);
Particle.variable("highHz2", highHz2);
Particle.variable("cleanupHz", cleanupHz);
Particle.variable("pubIntervalMin", pubIntervalMin);
Particle.variable("gainState", gainState);
Particle.function("start", startSampling);
Particle.function("stop", stopSampling);
Particle.function("setB1L", setB1L);
Particle.function("setB1H", setB1H);
Particle.function("setB2L", setB2L);
Particle.function("setB2H", setB2H);
Particle.function("setCleanup", setCleanup);
Particle.function("setPubInterval", setPubInterval);
Particle.function("setGain", setGain);
Particle.function("gpsStart", gpsStartFn);
Particle.function("gpsStop", gpsStopFn);
fftBuf.resize(NUM_SAMPLES);
computeAlpha();
windowStart = millis();
publishStart = millis();
Particle.connect();
waitFor(Particle.connected, 15000);
// Tracker services (repo must be on release/v18 with submodules)
Tracker::instance().init();
//GPS.begin();
}
// ---------- loop ----------
void loop(){
handleButtonPressAggregation();
// --- GPS seeking state ---
if (gpsMode == GpsMode::Seeking) {
led_take(); led_red();
GPS.start();
if (gpsHasFix()){
char payload[160];
snprintf(payload, sizeof(payload),
"{\"lat\":%.7f,\"lon\":%.7f,\"alt\":%.1f}",
lastFix.latitude, lastFix.longitude, lastFix.altitude);
Particle.publish("gpsLock", payload, PRIVATE);
led_green(); delay(3000);
gpsExitSeek(true);
} else if (millis() - gpsSeekStartMs >= GPS_TIMEOUT_MS) {
for (int i=0;i<3;i++){ led_yellow(); delay(200); led_off(); delay(200); }
gpsExitSeek(false);
}
}
// --- FFT pipeline (paused during GPS seeking) ---
if (samplingEnabled && gpsMode == GpsMode::Idle){
for (size_t i=0;i<NUM_SAMPLES;i++){
while ((uint32_t)(micros()-lastSampleTime) < SAMPLE_INTERVAL_US) {}
lastSampleTime = micros();
rawBuffer[i] = analogRead(MIC_PIN);
}
for (size_t i=0;i<NUM_SAMPLES;i++){
float centered = (float)rawBuffer[i] - 2048.0f;
lp_prev = lp_prev + lp_alpha * (centered - lp_prev);
floatBuffer[i] = lp_prev;
}
double sum=0; for(size_t i=0;i<NUM_SAMPLES;i++) sum += floatBuffer[i];
double mean = sum / NUM_SAMPLES;
for (size_t i=0;i<NUM_SAMPLES;i++) fftBuf[i] = { (double)floatBuffer[i]-mean, 0.0 };
Fft::transformRadix2(fftBuf);
double mag1=0, mag2=0; size_t bin1=0, bin2=0;
peakInBand(fftBuf, NUM_SAMPLES, lowHz1, highHz1, mag1, bin1);
peakInBand(fftBuf, NUM_SAMPLES, lowHz2, highHz2, mag2, bin2);
double freq1 = (double)bin1 * SAMPLE_FREQ / NUM_SAMPLES;
double freq2 = (double)bin2 * SAMPLE_FREQ / NUM_SAMPLES;
if (mag1>winMax1){ winMax1=mag1; winFreq1=freq1; }
if (mag2>winMax2){ winMax2=mag2; winFreq2=freq2; }
}
if (millis() - windowStart >= WINDOW_MS){
if (winMax1>pubMax1){ pubMax1=winMax1; pubFreq1=winFreq1; }
if (winMax2>pubMax2){ pubMax2=winMax2; pubFreq2=winFreq2; }
winMax1=winFreq1=0; winMax2=winFreq2=0;
windowStart = millis();
}
if (millis() - publishStart >= pubIntervalMs){
char payload[192];
snprintf(payload, sizeof(payload),
"{\"b1_amp\":%.3f,\"b1_freq\":%.1f,\"b2_amp\":%.3f,\"b2_freq\":%.1f,"
"\"cleanup\":%.0f,\"bands\":\"%.0f/%.0f|%.0f/%.0f\"}",
pubMax1, pubFreq1, pubMax2, pubFreq2, cleanupHz,
lowHz1, highHz1, lowHz2, highHz2);
Particle.publish("bandPeak10m", payload, PRIVATE);
pubMax1=pubFreq1=0; pubMax2=pubFreq2=0;
publishStart = millis();
}
Particle.process();
}
// ---------- GPS helpers ----------
bool gpsHasFix() {
LocationPoint loc;
// release/v18: one-arg overload
if (!GPS.getLocation(loc)) return false;
// Optional: sanity checks instead of isValid()
if (!std::isfinite(loc.latitude) || !std::isfinite(loc.longitude)) return false;
lastFix = loc;
return true;
}
void gpsEnterSeek(){
if (gpsMode == GpsMode::Seeking) return;
samplingEnabled = false;
gpsSeekStartMs = millis();
gpsMode = GpsMode::Seeking;
}
void gpsExitSeek(bool){
GPS.stop();
led_off(); led_free();
gpsMode = GpsMode::Idle;
samplingEnabled = true;
}
// ---------- triple-press ----------
void handleButtonPressAggregation(){
int presses = System.buttonPushed();
if (presses > 0){
unsigned long now = millis();
if (pressAggCount == 0) pressWindowStartMs = now;
pressAggCount += presses;
if (pressAggCount >= PRESS_TRIGGER && (now - pressWindowStartMs) <= PRESS_WINDOW_MS){
gpsEnterSeek();
pressAggCount = 0; pressWindowStartMs = 0;
return;
}
}
if (pressAggCount > 0 && (millis() - pressWindowStartMs) > PRESS_WINDOW_MS){
pressAggCount = 0; pressWindowStartMs = 0;
}
}

