mcu/wwvb/wwvb.ino

415 lines
9.8 KiB
C++

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define SERIAL_RATE 115200
#define WWV_SIGNAL_PIN 14
#define PPS_OUTPUT_PIN 16
#define DEBUG1_OUTPUT_PIN 15
#define ONEBIT 1
#define ZEROBIT 2
#define MARKBIT 3
#define DEBUG false
#define CLOCKS_PER_MS 10
#define PPS_PULSEWIDTH_MS 100
char statusString[25] = "LOS";
volatile unsigned int lowLatencyInState;
volatile unsigned int wwvbInState;
volatile unsigned int lastWWVBInState;
volatile unsigned int stateStableCounter = 0;
volatile unsigned int stateStableMillis = 0;
volatile unsigned int lastStateStableMillis = 0;
volatile unsigned int secondCounter = 0;
volatile unsigned int milliCounter = 0;
volatile unsigned int clockCounter = 0;
volatile unsigned int highFor = 0;
volatile unsigned int lowFor = 0;
volatile unsigned int lastBitHighMS = 0;
volatile unsigned int timeToTick = 0;
volatile unsigned int frameSamples = 0;
volatile unsigned int lossOfSignal = 1;
volatile unsigned int displayUpdateRequired = 1;
volatile unsigned int waitingForSecond = 0;
volatile unsigned int frameStart = 0;
volatile unsigned int frameStartTime = 0;
volatile unsigned int frameCounter = 0;
volatile unsigned int bitReadyForRead = 0;
volatile unsigned int lastBitReceived = 0;
volatile unsigned int millisSinceBoot = 0;
volatile unsigned int ppsActivationTime = 0;
volatile unsigned int millisSinceSignalStateChange = 0;
volatile unsigned int minuteSync = 0;
void ICACHE_RAM_ATTR TimerHandler();
void ICACHE_RAM_ATTR MilliEdge();
void ICACHE_RAM_ATTR SecondEdge();
#include "ESP8266TimerInterrupt.h"
ESP8266Timer ITimer;
void setup() {
Serial.begin(SERIAL_RATE);
while (!Serial);
delay(200);
Serial.print("\n\n\n");
Serial.println("************************************************");
Serial.println("************************************************");
Serial.println("*** ESP8266 Ready.");
Serial.println("************************************************");
Serial.println("************************************************");
Serial.print("\n\n\n");
Serial.print(F("*** Starting WWVB on "));
Serial.println(ARDUINO_BOARD);
Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
Serial.print(F("*** CPU Frequency = "));
Serial.print(F_CPU / 1000000);
Serial.println(F(" MHz"));
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PPS_OUTPUT_PIN, OUTPUT);
pinMode(DEBUG1_OUTPUT_PIN, OUTPUT);
pinMode(WWV_SIGNAL_PIN, INPUT);
// 100us timer:
if (ITimer.attachInterruptInterval(100, TimerHandler)) {
Serial.print(F("Starting ITimer OK, millis() = "));
Serial.println(millis());
} else {
Serial.println(
F("Can't set ITimer. Select another freq. or timer")
);
}
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("booting");
}
void TimerHandler() {
clockCounter++;
// *****************************************************************
// *****************************************************************
// LOW LATENCY HACK to respond in 100us to a falling start-of-second
// edge respond really fast to a falling edge if in the waitingForSecond
// state
//lowLatencyInState = digitalRead(WWV_SIGNAL_PIN);
wwvbInState = digitalRead(WWV_SIGNAL_PIN);
// *****************************************************************
// *****************************************************************
if(clockCounter > CLOCKS_PER_MS) {
clockCounter -= CLOCKS_PER_MS;
MilliEdge();
}
}
void SecondEdge() {
secondCounter++;
displayUpdateRequired = 1;
}
void MilliEdge() {
milliCounter++;
millisSinceBoot++;
wwvbInState = digitalRead(WWV_SIGNAL_PIN);
if(waitingForSecond && !wwvbInState) {
// TICK! falling edge, beginning of a new frame and second
// FIXME reenable pps
//digitalWrite(PPS_OUTPUT_PIN, 1);
waitingForSecond = 0;
timeToTick = 1;
lastBitHighMS = highFor;
bitReadyForRead = 1;
return;
}
if(milliCounter > 1000) {
milliCounter -= 1000;
SecondEdge();
}
if(wwvbInState != lastWWVBInState) {
// any edge indicates we have some signal
lossOfSignal = 0;
}
if(wwvbInState && (wwvbInState != lastWWVBInState)) {
// we are on the rising edge, last low was 1ms ago
highFor = 0;
}
if(!wwvbInState && (wwvbInState != lastWWVBInState)) {
// we are on the falling edge, last high was 1ms ago
lowFor = 0;
}
if(wwvbInState == lastWWVBInState) {
if(wwvbInState) {
highFor++;
} else {
lowFor++;
}
}
if(wwvbInState && (highFor > 80) && !lossOfSignal) {
// if we are high for at least 80ms but no LOS
// main screen turn on
// this enables the falling edge low latency detector
// that happens on a tick
waitingForSecond = 1;
}
if(wwvbInState && (highFor > 2000) && !lossOfSignal) {
// we have received nothing for 2 seconds, loss of signal:
lossOfSignal = 1;
waitingForSecond = 1;
frameCounter = 0;
minuteSync = 0;
}
lastWWVBInState = wwvbInState; // copy/save for next loop
}
char pb[255];
void loop() {
digitalWrite(LED_BUILTIN, !wwvbInState);
if(timeToTick) {
timeToTick = 0;
TickSecond();
}
if(bitReadyForRead) {
bitReadyForRead = 0;
readBit();
}
PPSLowIfRequired();
if (displayUpdateRequired) {
updateDisplay();
displayUpdateRequired = 0;
}
}
void SetPPSHigh() {
digitalWrite(PPS_OUTPUT_PIN, 1);
}
void SetPPSLow() {
digitalWrite(PPS_OUTPUT_PIN, 0);
}
void SendPPS() {
ppsActivationTime = millisSinceBoot;
SetPPSHigh();
}
void PPSLowIfRequired() {
if ((millisSinceBoot - ppsActivationTime) > PPS_PULSEWIDTH_MS) {
SetPPSLow();
}
}
void TickSecond() {
char buf[255];
sprintf(buf, "*** TICK(%d): WWVB going low after %d ms high (EDGE)\n",
frameCounter, lastBitHighMS);
Serial.print(buf);
SendPPS();
}
void readBit() {
//char buf[255];
//sprintf(buf, "*** carrier was high for %sms befor \n", lastBitHighMS);
//Serial.print(buf);
displayUpdateRequired++;
registerBit(convertBit(lastBitHighMS));
}
int convertBit(unsigned int ms) {
char buf[255];
char bitbuf[20];
int output = 0;
output = ZEROBIT;
sprintf(bitbuf, "ZERO");
if (ms < 800) {
output = ONEBIT;
sprintf(bitbuf, "ONE");
}
if (ms < 400) {
output = MARKBIT;
sprintf(bitbuf, "MARK");
}
sprintf(buf, "frame prevHighMs=%s, bit=%s\n", ms, bitbuf);
Serial.print(buf);
return output;
}
void registerBit(int doot) {
if (minuteSync) {
frameCounter++;
}
if (doot == MARKBIT) {
if (lastBitReceived == MARKBIT) {
// two mark bits in a row means we are in the first second of the minute
frameCounter = 0;
minuteSync = 1;
}
}
if (!minuteSync) {
frameCounter = 0;
}
sanityCheckFrame(doot);
lastBitReceived = doot;
logBit(doot);
}
void logBit(int doot) {
}
void lossOfSync(int errorFrame) {
char buf[255];
sprintf(buf, "ERROR: %d bit incorrect for framing, loss of sync!\n", errorFrame);
Serial.print(buf);
minuteSync = 0;
displayUpdateRequired++;
}
void sanityCheckFrame(int doot) {
if (
(
(frameCounter == 9)
||
(frameCounter == 19)
||
(frameCounter == 29)
||
(frameCounter == 39)
||
(frameCounter == 49)
)
&& doot != MARKBIT) {
lossOfSync(frameCounter);
}
if (
(doot == MARKBIT)
&&
(
(frameCounter != 0)
&&
(frameCounter != 9)
&&
(frameCounter != 19)
&&
(frameCounter != 29)
&&
(frameCounter != 39)
&&
(frameCounter != 49)
)
) {
lossOfSync(frameCounter);
}
}
void updateDisplay() {
//Serial.print("updateDisplay()\n");
if (lossOfSignal) {
sprintf(statusString, "LOS");
} else {
sprintf(statusString, "RX(syncing)");
}
if (minuteSync) {
sprintf(statusString, "RX(bit %d)", frameCounter);
}
char d[20];
if (lastBitReceived == MARKBIT) {
sprintf(d, "%d=MARK", frameCounter);
}
if (lastBitReceived == ONEBIT) {
sprintf(d, "%d=ONE ", frameCounter);
}
if (lastBitReceived == ZEROBIT) {
sprintf(d, "%d=ZERO", frameCounter);
}
lcd.clear();
lcd.setCursor(0, 0);
char msg[20];
sprintf(msg, "up:%03d", secondCounter);
lcd.print(msg);
lcd.setCursor(0, 1);
sprintf(msg, "s:%s", statusString);
lcd.print(msg);
if (minuteSync) {
lcd.setCursor(10, 0);
lcd.print(d);
}
if(DEBUG) {
if(secondCounter % 10 == 0) {
serialDebug();
}
}
}
void fastSerialDebug() {
char buf[255];
sprintf(buf,"stateStableMillis=%d\n",stateStableMillis);
Serial.print(buf);
}
void serialDebug() {
char buf[255];
sprintf(buf,"lossOfSignal=%d\n",lossOfSignal);
Serial.print(buf);
sprintf(buf,"waitingForSecond=%d\n",waitingForSecond);
Serial.print(buf);
sprintf(buf,"millisSinceBoot=%d\n",millisSinceBoot);
Serial.print(buf);
sprintf(buf,"frameStartAge=%d\n",millisSinceBoot-frameStartTime);
Serial.print(buf);
sprintf(buf,"secondCounter=%d\n",secondCounter);
Serial.print(buf);
sprintf(buf,"timeToTick=%d\n",timeToTick);
Serial.print(buf);
sprintf(buf,"highFor=%d\n",highFor);
Serial.print(buf);
sprintf(buf,"lowFor=%d\n",lowFor);
Serial.print(buf);
}