Syncing Sensor With PPS and Artificial NMEA Sentences
Customers may want to sync devices with a pulse without a GPS but still supply UTC time.
Sample C++ code that was used with ESP32
int64_t last = 0;
int RXPIN = 27;
int TXPIN = 14;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
last = micros();
pinMode(12, OUTPUT);
Serial1.begin(9600, SERIAL_8N1, RXPIN, TXPIN);
}
char to_hex_char(uint8_t x)
{
if (x < 10)
{
return '0' + x;
}
return 'A' + (x - 10);
}
void fill_checksum(uint8_t packet[], const uint8_t packetEndIndex)
{
uint8_t checksum = 0;
for(uint8_t i=1; i<packetEndIndex-4; ++i)
{
checksum = checksum^packet[i];
}
auto nibble1 = (checksum & 0xF0) >> 4;
auto nibble2 = checksum & 0xF;
packet[packetEndIndex-4] = to_hex_char(nibble1);
packet[packetEndIndex-3] = to_hex_char(nibble2);
}
std::string to_pair(uint8_t num)
{
std::string s;
s += num / 10 + '0';
s += num % 10 + '0';
return s;
}
int secs = 0;
bool stopped = true;
bool output = true;
void loop() {
// put your main code here, to run repeatedly:
auto time = micros();
auto delta = time - last;
if (delta >= 1000000) {
digitalWrite(12, HIGH);
last = time;
delta = 0;
stopped = false;
output = false;
Serial.println("high");
secs++;
}
if (delta > 10000 && !stopped) {
stopped = true;
digitalWrite(12, LOW);
Serial.println("low");
}
if (!output && delta > 50000)
{
output = true;
// now come up with the sentence
int h = secs/(60*60);
int m = (secs/60)%60;
int s = secs%60;
std::string sentence = "$GPRMC," + to_pair(h) + to_pair(m) + to_pair(s) + ".00,A,4807.038,N,01131.00,E,022.4,084.4,220125,0.0,E,D*XX\r\n";
fill_checksum((uint8_t*)sentence.data(), sentence.length());
Serial.print(sentence.c_str());
Serial1.println(sentence.c_str());
}
}Note: This setup currently only works reliably over a span of about 24 hours, since full date handling hasn’t been implemented yet.
Additionally, while the sensor responds to external timing pulses, it doesn’t appear to fully synchronize its internal clock. Instead, it steps the seconds value forward based on the incoming pulse — effectively updating the time, but not adjusting the clock's rate.
For instance, if a pulse is sent every 900 milliseconds, the sensor doesn't treat that as a faster second. Instead, it jumps to the next second as soon as the pulse is received. So, the time might jump from 101.9 to 102.0 seconds, rather than progressing smoothly — treating the pulse as a step trigger rather than a timebase.
For more information on this article, please reach out to Matthew Bries and Danielle Wheeler.