master
Jeffrey Paul 2 years ago
commit 2d9aa994a8
  1. 131
      i2c-lcd/i2c-lcd.ino
  2. 51
      infrared-read/infrared-read.ino
  3. 82
      quadalphanum/quadalphanum.ino
  4. 871
      wwvb-ref/wwvb-ref.ino
  5. 175
      wwvb/floats.csv
  6. 1086
      wwvb/output.txt
  7. 482
      wwvb/wwvb.ino

@ -0,0 +1,131 @@
#define SSD1306_NO_SPLASH
// OLED display width and height, in pixels
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
// i2c address for oled
///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define SCREEN_ADDRESS 0x3C
#include <Adafruit_SSD1306.h>
#include <splash.h>
#include <Adafruit_GrayOLED.h>
#include <gfxfont.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SPITFT.h>
#include <Adafruit_SPITFT_Macros.h>
#include <LiquidCrystal_I2C.h>
// set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("0x123456789abcde");
lcd.setCursor(0, 1);
lcd.print("fghijklmnopqrstu");
Serial.begin(115200);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
display.display();
delay(50); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
testdrawstyles(); // Draw 'stylized' characters
panic();
//
// // Invert and restore display, pausing in-between
// display.invertDisplay(true);
// delay(1000);
// display.invertDisplay(false);
// delay(1000);
}
void panic() {
for (;;) {
display.invertDisplay(true);
delay(1000);
}
}
void loop()
{
}
#define LINE_HEIGHT_PX 9
void testdrawstyles(void) {
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0 * LINE_HEIGHT_PX);
display.println(F("123456789112345678921"));
display.setCursor(0, 1 * LINE_HEIGHT_PX);
display.println(F("234567893123456789412"));
display.setCursor(0, 2 * LINE_HEIGHT_PX);
display.println(F("345678951234567896123"));
display.display();
display.startscrollright(0x00, 0xFF);
/*
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
display.println(3.141592);
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.print(F("0x")); display.println(0xDEADBEEF, HEX);
display.display();
delay(2000);
*/
}
/*
void testscrolltext(void) {
display.clearDisplay();
display.setTextSize(1.5); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 0);
display.println(F("1337CAFE"));
display.display(); // Show initial text
delay(70);
// Scroll in various directions, pausing in-between:
display.startscrollright(0x00, 0x0F);
delay(2000);
display.stopscroll();
delay(1000);
display.startscrollleft(0x00, 0x0F);
delay(2000);
display.stopscroll();
delay(1000);
display.startscrolldiagright(0x00, 0x07);
delay(2000);
display.startscrolldiagleft(0x00, 0x07);
delay(2000);
display.stopscroll();
delay(1000);
}
*/

@ -0,0 +1,51 @@
/*
ESP8266 BlinkWithoutDelay by Simon Peter
Blink the blue LED on the ESP-01 module
Based on the Arduino Blink without Delay example
This example code is in the public domain
The blue LED on the ESP-01 module is connected to GPIO1
(which is also the TXD pin; so we cannot use Serial.print() at the same time)
Note that this sketch uses LED_BUILTIN to find the pin with the internal LED
*/
#define IR_RX_PIN 14
#define IR_TX_PIN 12
#define SERIAL_BPS 115200
#define POWER_BUTTON 0xFFEA15
#define EDIT_BUTTON 0xFF7887
#define EXIT_BUTTON 0xFF38C7
#define ONE_BUTTON 0xFF08F7
#define TWO_BUTTON 0xFF8877
#define THREE_BUTTON 0xFF48B7
#define FOUR_BUTTON 0xFFC837
#define FIVE_BUTTON 0xFF28D7
#define SIX_BUTTON 0xFFA857
#define SEVEN_BUTTON 0xFFE817
#define EIGHT_BUTTON 0xFF18E7
#define NINE_BUTTON 0xFF9867
#define ZERO_BUTTON 0xFFB847
#include <IRremote.h>
IRrecv irrecv(IR_RX_PIN);
decode_results results;
int ledState = LOW;
volatile byte IRInputState;
void setup() {
IrReceiver.begin(IR_RX_PIN, ENABLE_LED_FEEDBACK); // Start the receiver
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(SERIAL_BPS);
Serial.print("herro booted\n");
}
void loop() {
if (IrReceiver.decode()) {
Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
IrReceiver.printIRResultShort(&Serial); // optional use new print version
IrReceiver.resume(); // Enable receiving of the next value
}
}

@ -0,0 +1,82 @@
// Demo the quad alphanumeric display LED backpack kit
// scrolls through every character, then scrolls Serial
// input onto the display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
Adafruit_AlphaNum4 one = Adafruit_AlphaNum4();
Adafruit_AlphaNum4 two = Adafruit_AlphaNum4();
unsigned long ticks;
void setup() {
Serial.begin(9600);
one.begin(0x71); // pass in the address
two.begin(0x70);
one.writeDigitRaw(3, 0x0);
one.writeDigitRaw(0, 0xFFFF);
one.writeDisplay();
delay(200);
one.writeDigitRaw(0, 0x0);
one.writeDigitRaw(1, 0xFFFF);
one.writeDisplay();
delay(200);
one.writeDigitRaw(1, 0x0);
one.writeDigitRaw(2, 0xFFFF);
one.writeDisplay();
delay(200);
one.writeDigitRaw(2, 0x0);
one.writeDigitRaw(3, 0xFFFF);
one.writeDisplay();
delay(200);
one.clear();
one.writeDisplay();
two.clear();
two.writeDisplay();
one.writeDigitAscii(0, '1');
one.writeDigitAscii(1, '2');
one.writeDigitAscii(2, '0');
one.writeDigitAscii(3, '0');
two.writeDigitAscii(0, '0');
two.writeDigitAscii(1, '1');
two.writeDigitAscii(2, '2');
two.writeDigitAscii(3, '3');
one.writeDisplay();
two.writeDisplay();
delay(300);
Serial.println("Start typing to display!");
}
char displaybuffer[4] = {' ', ' ', ' ', ' '};
void loop() {
ticks = millis();
sprintf(displaybuffer, "%02d", ticks/1000);
sprintf(displaybuffer+2, "%02d", ticks-(ticks/1000)*1000);
Serial.println(displaybuffer);
// set every digit to the buffer
two.writeDigitAscii(0, displaybuffer[0]);
two.writeDigitAscii(1, displaybuffer[1]);
two.writeDigitAscii(2, displaybuffer[2]);
two.writeDigitAscii(3, displaybuffer[3]);
// write it out!
two.writeDisplay();
delay(10);
}

@ -0,0 +1,871 @@
/* WWVB Clock v 1.0
*
* A WWVB (NIST Time Code Broadcast) receiving clock powered by an
* Arduino (ATMega328), a DS1307 Real Time Clock, and a C-Max
* C-Max CMMR-6P-60 WWVB receiver module.
*
* This code builds on Capt.Tagon's code at duinolab.blogspot.com
* which was a great reference. Specifically, I used almost without change
* his struct / unsigned long long overlay for the received WWVB Buffer,
* which apparently in turn draws on DCF77 decoding code by Rudi Niemeijer,
* Mathias Dalheimer, and "Captain."
*
* The RTC (DS1307) interface code builds in part on code by
* Jon McPhalen (www.jonmcphalen.com)
*
* There you have it.
*
* This code supports the "Atomic Clock" article in the April 2010 issue
* of Popular Science. There is also a schematic for this project. There
* is also WWVB signal simulator code, to facilitate debugging and
* hacking on this project when the reception of the WWVB signal
* itself is less than stellar.
*
* The code for both the clock and the WWVB simulator, and the schematic
* are available online at:
* http://www.popsci.com/diy/article/2010-03/build-clock-uses-atomic-timekeeping
*
* and on GitHub at: http://github.com/vinmarshall/WWVB-Clock
*
* Version 1.0
* Notes:
* - No timezone support in this version. UTC only.
* - No explicit leapsecond (3 frame marker bits) support in this version.
* - 24 hour mode only
*
*
* Copyright (c) 2010 Vin Marshall (vlm@2552.com, www.2552.com)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#define SERIAL_RATE 115200
#define WWV_SIGNAL_PIN 14
#define PPS_OUTPUT_PIN 16
#define DEBUG1_OUTPUT_PIN 15
#include <stdio.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
void ICACHE_RAM_ATTR wwvbChange();
//
// Initializations
// Debugging mode - prints debugging information on the Serial port.
boolean debug = true;
// Front Panel Light
int lightPin = LED_BUILTIN;
// WWVB Receiver Pin
int wwvbInPin = WWV_SIGNAL_PIN;
// LCD init
//LiquidCrystal lcd(12, 11, 6, 5, 4, 3);
// RTC init
// RTC uses pins Analog4 = SDA, Analog5 = SCL
// RTC I2C Slave Address
#define DS1307 0xD0 >> 1
// RTC Memory Registers
#define RTC_SECS 0
#define RTC_MINS 1
#define RTC_HRS 2
#define RTC_DAY 3
#define RTC_DATE 4
#define RTC_MONTH 5
#define RTC_YEAR 6
#define RTC_SQW 7
// Month abbreviations
char *months[12] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
// Day of Year to month translation (thanks to Capt.Tagon)
// End of Month - to calculate Month and Day from Day of Year
int eomYear[14][2] = {
{0,0}, // Begin
{31,31}, // Jan
{59,60}, // Feb
{90,91}, // Mar
{120,121}, // Apr
{151,152}, // May
{181,182}, // Jun
{212,213}, // Jul
{243,244}, // Aug
{273,274}, // Sep
{304,305}, // Oct
{334,335}, // Nov
{365,366}, // Dec
{366,367} // overflow
};
//
// Globals
// Timing and error recording
unsigned long pulseStartTime = 0;
unsigned long pulseEndTime = 0;
unsigned long frameEndTime = 0;
unsigned long lastRtcUpdateTime = 0;
boolean bitReceived = false;
boolean wasMark = false;
int framePosition = 0;
int bitPosition = 1;
char lastTimeUpdate[17];
char lastBit = ' ';
int errors[10] = { 1,1,1,1,1,1,1,1,1,1 };
int errIdx = 0;
int bitError = 0;
boolean frameError = false;
// RTC clock variables
byte second = 0x00;
byte minute = 0x00;
byte hour = 0x00;
byte day = 0x00;
byte date = 0x01;
byte month = 0x01;
byte year = 0x00;
byte ctrl = 0x00;
// WWVB time variables
byte wwvb_hour = 0;
byte wwvb_minute = 0;
byte wwvb_day = 0;
byte wwvb_year = 0;
/* WWVB time format struct - acts as an overlay on wwvbRxBuffer to extract time/date data.
* This points to a 64 bit buffer wwvbRxBuffer that the bits get inserted into as the
* incoming data stream is received. (Thanks to Capt.Tagon @ duinolab.blogspot.com)
*/
struct wwvbBuffer {
unsigned long long U12 :4; // no value, empty four bits only 60 of 64 bits used
unsigned long long Frame :2; // framing
unsigned long long Dst :2; // dst flags
unsigned long long Leapsec :1; // leapsecond
unsigned long long Leapyear :1; // leapyear
unsigned long long U11 :1; // no value
unsigned long long YearOne :4; // year (5 -> 2005)
unsigned long long U10 :1; // no value
unsigned long long YearTen :4; // year (5 -> 2005)
unsigned long long U09 :1; // no value
unsigned long long OffVal :4; // offset value
unsigned long long U08 :1; // no value
unsigned long long OffSign :3; // offset sign
unsigned long long U07 :2; // no value
unsigned long long DayOne :4; // day ones
unsigned long long U06 :1; // no value
unsigned long long DayTen :4; // day tens
unsigned long long U05 :1; // no value
unsigned long long DayHun :2; // day hundreds
unsigned long long U04 :3; // no value
unsigned long long HourOne :4; // hours ones
unsigned long long U03 :1; // no value
unsigned long long HourTen :2; // hours tens
unsigned long long U02 :3; // no value
unsigned long long MinOne :4; // minutes ones
unsigned long long U01 :1; // no value
unsigned long long MinTen :3; // minutes tens
};
struct wwvbBuffer * wwvbFrame;
unsigned long long receiveBuffer;
unsigned long long lastFrameBuffer;
/*
* setup()
*
* uC Initialization
*/
void setup() {
Serial.begin(SERIAL_RATE);
Serial.println("*****************************************");
Serial.println("*****************************************");
Serial.println("*****************************************");
Serial.print("*** ESP8266 Ready.\n");
// Initialize the I2C Two Wire Communication for the RTC
//Wire.begin();
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("booting");
// Setup the light pin
pinMode(lightPin, OUTPUT);
digitalWrite(lightPin, LOW);
pinMode(DEBUG1_OUTPUT_PIN, OUTPUT);
// Print a message to the LCD.
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WWVB Clock v 1.0");
lcd.setCursor(0, 1);
lcd.print("PopSci Apr. 2010");
delay(5000);
// Front panel light lights for 10 seconds on a successful frame rcpt.
digitalWrite(lightPin, HIGH);
// Setup the WWVB Signal In Handling
pinMode(wwvbInPin, INPUT);
attachInterrupt(0, wwvbChange, CHANGE);
// Setup the WWVB Buffer
lastFrameBuffer = 0;
receiveBuffer = 0;
wwvbFrame = (struct wwvbBuffer *) &lastFrameBuffer;
Serial.println("*** setup() complete");
}
/*
* loop()
*
* Main program loop
*/
void loop() {
// If we've received another bit, process it
if (bitReceived == true) {
Serial.println("*** bitReceived==true");
processBit();
}
// Read from the RTC and update the display 4x per second
if (millis() - lastRtcUpdateTime > 250) {
// Snag the RTC time and store it locally
//getRTC();
// And record the time of this last update.
lastRtcUpdateTime = millis();
// Update RTC if there has been a successfully received WWVB Frame
if (frameEndTime != 0) {
//updateRTC();
frameEndTime = 0;
}
// Update the display
updateDisplay();
}
}
/*
* updateDisplay()
*
* Update the 2 line x 16 char LCD display
*/
void updateDisplay() {
// Turn off the front panel light marking a successfully
// received frame after 10 seconds of being on.
if (bcd2dec(second) >= 10) {
digitalWrite(lightPin, HIGH);
}
// Update the LCD
lcd.clear();
// Update the first row
lcd.setCursor(0,0);
char *time = buildTimeString();
lcd.print(time);
// Update the second row
// Cycle through our list of status messages
lcd.setCursor(0,1);
int cycle = bcd2dec(second) / 10; // This gives us 6 slots for messages
char msg[17]; // 16 chars per line on display
switch (cycle) {
// Show the Date
case 0:
{
sprintf(msg, "%s %0.2i 20%0.2i",
months[bcd2dec(month)-1], bcd2dec(date), bcd2dec(year));
break;
}
// Show the WWVB signal strength based on the # of recent frame errors
case 1:
{
int signal = (10 - sumFrameErrors()) / 2;
sprintf(msg, "WWVB Signal: %i", signal);
break;
}
// Show LeapYear and LeapSecond Warning bits
case 2:
{
const char *leapyear = ( ((byte) wwvbFrame->Leapyear) == 1)?"Yes":"No";
const char *leapsec = ( ((byte) wwvbFrame->Leapsec) == 1)?"Yes":"No";
sprintf(msg, "LY: %s LS: %s", leapyear, leapsec);
break;
}
// Show our Daylight Savings Time status
case 3:
{
switch((byte)wwvbFrame->Dst) {
case 0:
sprintf(msg, "DST: No");
break;
case 1:
sprintf(msg, "DST: Ending");
break;
case 2:
sprintf(msg, "DST: Starting");
break;
case 3:
sprintf(msg, "DST: Yes");
break;
}
break;
}
// Show the UT1 correction sign and amount
case 4:
{
char sign;
if ((byte)wwvbFrame->OffSign == 2) {
sign = '-';
} else if ((byte)wwvbFrame->OffSign == 5) {
sign = '+';
} else {
sign = '?';
}
sprintf(msg, "UT1 %c 0.%i", sign, (byte) wwvbFrame->OffVal);
break;
}
// Show the time and date of the last successfully received
// wwvb frame
case 5:
{
sprintf(msg, "[%s]", lastTimeUpdate);
break;
}
}
lcd.print(msg);
}
/*
* buildTimeString
*
* Prepare the string for displaying the time on line 1 of the LCD
*/
char* buildTimeString() {
char rv[255];
sprintf(rv,"%0.2i:%0.2i:%0.2i UTC %c",
bcd2dec(hour),
bcd2dec(minute),
bcd2dec(second),
lastBit);
return rv;
}
/*
* getRTC
*
* Read data from the DS1307 RTC over the I2C 2 wire interface.
* Data is stored in the uC's global clock variables.
*/
/*
void getRTC() {
// Begin the Transmission
Wire.beginTransmission(DS1307);
// Point the request at the first register (seconds)
Wire.write(RTC_SECS);
// End the Transmission and Start Listening
Wire.endTransmission();
Wire.requestFrom(DS1307, 8);
second = Wire.receive();
minute = Wire.receive();
hour = Wire.receive();
day = Wire.receive();
date = Wire.receive();
month = Wire.receive();
year = Wire.receive();
ctrl = Wire.receive();
}
*/
/*
* setRTC
*
* Set the DS1307 RTC over the I2C 2 wire interface.
* Data is read from the uC's global clock variables.
*/
/*
void setRTC() {
// Begin the Transmission
Wire.beginTransmission(DS1307);
// Start at the beginning
Wire.write(RTC_SECS);
// Send data for each register in order
Wire.write(second);
Wire.write(minute);
Wire.write(hour);
Wire.send(day);
Wire.send(date);
Wire.send(month);
Wire.send(year);
Wire.send(ctrl);
// End the transmission
Wire.endTransmission();
}
*/
/*
* updateRTC
*
* Update the time stored in the RTC to match the received WWVB frame.
*/
/*
void updateRTC() {
// Find out how long since the frame's On Time Marker (OTM)
// We'll need this adjustment when we set the time.
unsigned int timeSinceFrame = millis() - frameEndTime;
byte secondsSinceFrame = timeSinceFrame / 1000;
if (timeSinceFrame % 1000 > 500) {
secondsSinceFrame++;
}
// The OTM for a minute comes at the beginning of the frame, meaning that
// the WWVB time we have is now 1 minute + `secondsSinceFrame` seconds old.
incrementWwvbMinute();
// Set up data for the RTC clock write
second = secondsSinceFrame;
minute = ((byte) wwvbFrame->MinTen << 4) + (byte) wwvbFrame->MinOne;
hour = ((byte) wwvbFrame->HourTen << 4) + (byte) wwvbFrame->HourOne;
day = 0; // we're not using day of week at this time.
// Translate wwvb day of year into a month and a day of month
// This routine is courtesy of Capt.Tagon
int doy = ((byte) wwvbFrame->DayHun * 100) +
((byte) wwvbFrame->DayTen * 10) +
((byte) wwvbFrame->DayOne);
int i = 0;
byte isLeapyear = (byte) wwvbFrame->Leapyear;
while ( (i < 14) && (eomYear[i][isLeapyear] < doy) ) {
i++;
}
if (i>0) {
date = dec2bcd(doy - eomYear[i-1][isLeapyear]);
month = dec2bcd((i > 12)?1:i);
}
year = ((byte) wwvbFrame->YearTen << 4) + (byte) wwvbFrame->YearOne;
// And write the update to the RTC
//setRTC();
// Store the time of update for the display status line
sprintf(lastTimeUpdate, "%0.2i:%0.2i %0.2i/%0.2i/%0.2i",
bcd2dec(hour), bcd2dec(minute), bcd2dec(month),
bcd2dec(date), bcd2dec(year));
}
*/
/*
* processBit()
*
* Decode a received pulse. Pulses are decoded according to the
* length of time the pulse was in the low state.
*/
void processBit() {
Serial.println("*** in processBit()");
char buf[255];
// Clear the bitReceived flag, as we're processing that bit.
bitReceived = false;
// determine the width of the received pulse
unsigned int pulseWidth = pulseEndTime - pulseStartTime;
sprintf(buf,"got bit with pulseWidth=%d\n",pulseWidth);
Serial.print(buf);
// Attempt to decode the pulse into an Unweighted bit (0),
// a Weighted bit (1), or a Frame marker.
// Pulses < 0.2 sec are an error in reception.
if (pulseWidth < 100) {
buffer(-2);
bitError++;
wasMark = false;
// 0.2 sec pulses are an Unweighted bit (0)
} else if (pulseWidth < 400) {
buffer(0);
wasMark = false;
// 0.5 sec pulses are a Weighted bit (1)
} else if (pulseWidth < 700) {
buffer(1);
wasMark = false;
// 0.8 sec pulses are a Frame Marker
} else if (pulseWidth < 900) {
// Two Frame Position markers in a row indicate an
// end/beginning of frame marker
if (wasMark) {
// For the display update
lastBit = '*';
Serial.println("*** Frame Start");
// Verify that our position data jives with this being
// a Frame start/end marker
if ( (framePosition == 6) &&
(bitPosition == 60) &&
(bitError == 0)) {
// Process a received frame
frameEndTime = pulseStartTime;
lastFrameBuffer = receiveBuffer;
digitalWrite(lightPin, LOW);
logFrameError(false);
debugPrintFrame();
} else {
frameError = true;
}
// Reset the position counters
framePosition = 0;
bitPosition = 1;
wasMark = false;
bitError = 0;
receiveBuffer = 0;
// Otherwise, this was just a regular frame position marker
} else {
buffer(-1);
wasMark = true;
}
// Pulses > 0.8 sec are an error in reception
} else {
buffer(-2);
bitError++;
wasMark = false;
}
// Reset everything if we have more than 60 bits in the frame. This means
// the frame markers went missing somewhere along the line
if (frameError == true || bitPosition > 60) {
// Debugging
Serial.println("*** Frame Error");
// Reset all of the frame pointers and flags
frameError = false;
logFrameError(true);
framePosition = 0;
bitPosition = 1;
wasMark = false;
bitError = 0;
receiveBuffer = 0;
}
}
/*
* logFrameError
*
* Log the error in the buffer that is a window on the past 10 frames.
*/
void logFrameError(boolean err) {
// Add a 1 to log errors to the buffer
int add = err?1:0;
errors[errIdx] = add;
// and move the buffer loop-around pointer
if (++errIdx >= 10) {
errIdx = 0;
}
}
/*
* sumFrameErrors
*
* Turn the errors in the frame error buffer into a number useful to display
* the quality of reception of late in the status messages. Sums the errors
* in the frame error buffer.
*/
int sumFrameErrors() {
// Sum all of the values in our error buffer
int i, rv;
for (i=0; i< 10; i++) {
rv += errors[i];
}
return rv;
}
/*
* debugPrintFrame
*
* Print the decoded frame over the seriail port
*/
void debugPrintFrame() {
char time[255];
byte minTen = (byte) wwvbFrame->MinTen;
byte minOne = (byte) wwvbFrame->MinOne;
byte hourTen = (byte) wwvbFrame->HourTen;
byte hourOne = (byte) wwvbFrame->HourOne;
byte dayHun = (byte) wwvbFrame->DayHun;
byte dayTen = (byte) wwvbFrame->DayTen;
byte dayOne = (byte) wwvbFrame->DayOne;
byte yearOne = (byte) wwvbFrame->YearOne;
byte yearTen = (byte) wwvbFrame->YearTen;
byte wwvb_minute = (10 * minTen) + minOne;
byte wwvb_hour = (10 * hourTen) + hourOne;
byte wwvb_day = (100 * dayHun) + (10 * dayTen) + dayOne;
byte wwvb_year = (10 * yearTen) + yearOne;
sprintf(time, "\nFrame Decoded: %0.2i:%0.2i %0.3i 20%0.2i\n",
wwvb_hour, wwvb_minute, wwvb_day, wwvb_year);
Serial.print(time);
}
/*
* buffer
*
* Places the received bits in the receive buffer in the order they
* were recived. The first received bit goes in the highest order
* bit of the receive buffer.
*/
void buffer(int bit) {
// Process our bits
if (bit == 0) {
lastBit = '0';
if (debug) { Serial.println("0"); }
} else if (bit == 1) {
lastBit = '1';
if (debug) { Serial.println("1"); }
} else if (bit == -1) {
lastBit = 'M';
if (debug) { Serial.println("M"); }
framePosition++;
} else if (bit == -2) {
lastBit = 'X';
if (debug) { Serial.println("X"); }
}
// Push the bit into the buffer. The 0s and 1s are the only
// ones we care about.
if (bit < 0) { bit = 0; }
receiveBuffer = receiveBuffer | ( (unsigned long long) bit << (64 - bitPosition) );
// And increment the counters that keep track of where we are
// in the frame.
bitPosition++;
}
/*
* incrementWwvbMinute
*
* The frame On Time Marker occurs at the beginning of the frame. This means
* that the time in the frame is one minute old by the time it has been fully
* received. Adding one to the minute can be somewhat complicated, in as much
* as it can roll over the successive hours, days, and years in just the right
* circumstances. This handles that.
*/
void incrementWwvbMinute() {
// Increment the Time and Date
if (++(wwvbFrame->MinOne) == 10) {
wwvbFrame->MinOne = 0;
wwvbFrame->MinTen++;
}
if (wwvbFrame->MinTen == 6) {
wwvbFrame->MinTen = 0;
wwvbFrame->HourOne++;
}
if (wwvbFrame->HourOne == 10) {
wwvbFrame->HourOne = 0;
wwvbFrame->HourTen++;
}
if ( (wwvbFrame->HourTen == 2) && (wwvbFrame->HourOne == 4) ) {
wwvbFrame->HourTen = 0;
wwvbFrame->HourOne = 0;
wwvbFrame->DayOne++;
}
if (wwvbFrame->DayOne == 10) {
wwvbFrame->DayOne = 0;
wwvbFrame->DayTen++;
}
if (wwvbFrame->DayTen == 10) {
wwvbFrame->DayTen = 0;
wwvbFrame->DayHun++;
}
if ( (wwvbFrame->DayHun == 3) &&
(wwvbFrame->DayTen == 6) &&
(wwvbFrame->DayOne == (6 + (int) wwvbFrame->Leapyear)) ) {
// Happy New Year.
wwvbFrame->DayHun = 0;
wwvbFrame->DayTen = 0;
wwvbFrame->DayOne = 1;
wwvbFrame->YearOne++;
}
if (wwvbFrame->YearOne == 10) {
wwvbFrame->YearOne = 0;
wwvbFrame->YearTen++;
}
if (wwvbFrame->YearTen == 10) {
wwvbFrame->YearTen = 0;
}
}
/*
* wwvbChange
*
* This is the interrupt servicing routine. Changes in the level of the
* received WWVB pulse are recorded here to be processed in processBit().
*/
void wwvbChange() {
digitalWrite(DEBUG1_OUTPUT_PIN, HIGH);
int signalLevel = digitalRead(wwvbInPin);
// Determine if this was triggered by a rising or a falling edge
// and record the pulse low period start and stop times
//FIXME this might need to be HIGH
if (signalLevel == HIGH) {
pulseStartTime = millis();
} else {
pulseEndTime = millis();
bitReceived = true;
}
digitalWrite(DEBUG1_OUTPUT_PIN, LOW);
}
/*
* bcd2dec
*
* Utility function to convert 2 bcd coded hex digits into an integer
*/
int bcd2dec(int bcd) {
return ( (bcd>>4) * 10) + (bcd % 16);
}
/*
* dec2bcd
*
* Utility function to convert an integer into 2 bcd coded hex digits
*/
int dec2bcd(int dec) {
return ( (dec/10) << 4) + (dec % 10);
}

@ -0,0 +1,175 @@
94.34
39.62
67.36
48.64
71.57
78.93
86.80
37.59
41.87
76.45
65.54
70.19
77.87
73.15
70.13
33.27
27.52
87.68
74.66
49.75
74.90
68.83
69.74
58.10
49.23
61.75
94.63
85.94
46.70
68.18
48.62
75.80
50.17
52.28
66.22
68.36
70.89
46.68
71.96
90.99
74.44
97.34
22.63
25.14
54.39
47.70
80.27
75.28
73.23
63.09
71.75
73.39
29.64
60.99
78.13
73.29
52.33
68.42
71.71
60.31
54.13
76.60
58.99
75.99
85.64
74.30
77.69
79.76
79.61
60.93
54.99
45.50
81.65
74.05
67.01
85.79
83.92
76.03
72.04
32.79
33.62
67.26
71.17
78.02
63.84
67.86
58.04
71.88
72.92
72.11
68.45
68.71
93.77
65.92
78.33
63.78
58.41
48.74
71.87
53.10
80.33
58.18
72.19
79.94
85.44
78.55
82.78
71.12
63.79
62.94
63.66
58.44
50.59
91.02
89.28
68.35
86.34
31.90
69.33
65.45
76.41
83.79
73.95
73.08
60.52
51.60
59.42
82.91
82.62
64.72
69.13
67.46
71.94
78.55
73.33
70.58
80.87
77.23
75.79
87.83
88.32
79.55
60.92
71.68
69.79
83.03
89.25
76.60
55.45
67.67
82.52
77.72
65.99
60.15
73.46
68.14
61.96
85.54
64.17
83.86
61.66
79.66
64.02
45.70
76.10
63.10
64.77
94.71
76.37
75.78
76.00
35.66
65.96
68.23
91.41
1 94.34
2 39.62
3 67.36
4 48.64
5 71.57
6 78.93
7 86.80
8 37.59
9 41.87
10 76.45
11 65.54
12 70.19
13 77.87
14 73.15
15 70.13
16 33.27
17 27.52
18 87.68
19 74.66
20 49.75
21 74.90
22 68.83
23 69.74
24 58.10
25 49.23
26 61.75
27 94.63
28 85.94
29 46.70
30 68.18
31 48.62
32 75.80
33 50.17
34 52.28
35 66.22
36 68.36
37 70.89
38 46.68
39 71.96
40 90.99
41 74.44
42 97.34
43 22.63
44 25.14
45 54.39
46 47.70
47 80.27
48 75.28
49 73.23
50 63.09
51 71.75
52 73.39
53 29.64
54 60.99
55 78.13
56 73.29
57 52.33
58 68.42
59 71.71
60 60.31
61 54.13
62 76.60
63 58.99
64 75.99
65 85.64
66 74.30
67 77.69
68 79.76
69 79.61
70 60.93
71 54.99
72 45.50
73 81.65
74 74.05
75 67.01
76 85.79
77 83.92
78 76.03
79 72.04
80 32.79
81 33.62
82 67.26
83 71.17
84 78.02
85 63.84
86 67.86
87 58.04
88 71.88
89 72.92
90 72.11
91 68.45
92 68.71
93 93.77
94 65.92
95 78.33
96 63.78
97 58.41
98 48.74
99 71.87
100 53.10
101 80.33
102 58.18
103 72.19
104 79.94
105 85.44
106 78.55
107 82.78
108 71.12
109 63.79
110 62.94
111 63.66
112 58.44
113 50.59
114 91.02
115 89.28
116 68.35
117 86.34
118 31.90
119 69.33
120 65.45
121 76.41
122 83.79
123 73.95
124 73.08
125 60.52
126 51.60
127 59.42
128 82.91
129 82.62
130 64.72
131 69.13
132 67.46
133 71.94
134 78.55
135 73.33
136 70.58
137 80.87
138 77.23
139 75.79
140 87.83
141 88.32
142 79.55
143 60.92
144 71.68
145 69.79
146 83.03
147 89.25
148 76.60
149 55.45
150 67.67
151 82.52
152 77.72
153 65.99
154 60.15
155 73.46
156 68.14
157 61.96
158 85.54
159 64.17
160 83.86
161 61.66
162 79.66
163 64.02
164 45.70
165 76.10
166 63.10
167 64.77
168 94.71
169 76.37
170 75.78
171 76.00
172 35.66
173 65.96
174 68.23
175 91.41

File diff suppressed because it is too large Load Diff

@ -0,0 +1,482 @@
#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
// we sample once per ms:
#define NUM_SAMPLES_PER_FRAME 700
#define CLOCKS_PER_MS 10
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 timeToTick = 0;
volatile unsigned int oldLastLowMillis = 0;
volatile unsigned int lastLowMillis = 0;
volatile unsigned int oldLastHighMillis = 0;
volatile unsigned int lastHighMillis = 0;
volatile unsigned int frameReadyToStart = 0;
volatile unsigned int beginFrameSearch = 0;
volatile unsigned int frameSamples = 0;
volatile unsigned int lossOfSignal = 1;
volatile unsigned int displayUpdateRequired = 1;
volatile unsigned int frameSearch = 0;
volatile unsigned int frameStart = 0;
volatile unsigned int frameStartTime = 0;
volatile unsigned int frameHigh = 0;
volatile unsigned int frameHighReadOut = 0;
volatile unsigned int frameLow = 0;
volatile unsigned int frameLowReadOut = 0;
volatile unsigned int frameCounter = 0;
volatile unsigned int frameReadyForRead = 0;
volatile unsigned int lastBitReceived = 0;
volatile unsigned int millisSinceBoot = 0;
volatile unsigned int ppsActivationTime;
volatile unsigned int millisSinceSignalStateChange = 0;
volatile unsigned int minuteSync = 0;
void ICACHE_RAM_ATTR WWVFallingEdge();
void ICACHE_RAM_ATTR OSCEdge();
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.print(F("\nStarting 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"));
}
//attachInterrupt(digitalPinToInterrupt(LOSC_INPUT_PIN), OSCEdge, RISING);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("booting");
ppsActivationTime = millis();
}
void TimerHandler() {
//Serial.print(F("in timer OK, millis() = ")); Serial.println(millis());
clockCounter++;
if(clockCounter > CLOCKS_PER_MS) {
clockCounter -= CLOCKS_PER_MS;
// *****************************************************************************
// LOW LATENCY HACK to respond in 100us to a falling
// start-of-second edge
// respond really fast to a falling edge if in the frameReadyToStart
// state
lowLatencyInState = digitalRead(WWV_SIGNAL_PIN);
if(!lowLatencyInState && frameReadyToStart && !lossOfSignal && !frameStart) {
// TICK!
// falling edge, beginning of a new frame and second
digitalWrite(PPS_OUTPUT_PIN, 1);
timeToTick = 1;
frameStart = 1;
frameStartTime = millisSinceBoot;
frameReadyToStart = 0;
frameSearch = 0;
}
// *****************************************************************************
MilliEdge();
}
}
void SecondEdge() {
secondCounter++;
displayUpdateRequired = 1;
}
void MilliEdge() {
digitalWrite(DEBUG1_OUTPUT_PIN, 1);
wwvbInState = digitalRead(WWV_SIGNAL_PIN);
milliCounter++;
millisSinceBoot++;
if(milliCounter > 1000) {
milliCounter -= 1000;
SecondEdge();
}
if(wwvbInState && (wwvbInState != lastWWVBInState)) {
// we are on the rising edge, last low was 1ms ago
oldLastLowMillis = lastLowMillis;
lastLowMillis = millisSinceBoot - 1;
}
if(!wwvbInState && (wwvbInState != lastWWVBInState)) {
// we are on the falling edge, last high was 1ms ago
oldLastHighMillis = lastHighMillis;
lastHighMillis = millisSinceBoot - 1;
}
if(wwvbInState != lastWWVBInState) {
// we are on an edge
stateStableMillis = 0;
} else {
// nothing happening
stateStableMillis++;
}
if(wwvbInState && (stateStableMillis > 180) && (stateStableMillis < 2000)) {
// if we are high but for less than 2s
// main screen turn on
lossOfSignal = 0;
frameSearch = 1;
}
lastWWVBInState = wwvbInState; // copy/save for next loop
if((stateStableMillis > 2000) && !lossOfSignal) {
// we have received nothing for 2 seconds, loss of signal:
lossOfSignal = 1;
frameStart = 0;
frameCounter = 0;
minuteSync = 0;
digitalWrite(DEBUG1_OUTPUT_PIN, 0);
return;
}
if(!frameStart && frameSearch && wwvbInState) {
// if we have been high for 180ms (frameSearch) we are ready to start a new frame on the mark
frameHigh = 0;
frameLow = 0;
frameReadyToStart = 1;
digitalWrite(DEBUG1_OUTPUT_PIN, 0);
return;
}
// frameReadyToStart -> frameStart (and PPS) happens in a 100us
// ISR above.
if (frameStart && (frameSamples < NUM_SAMPLES_PER_FRAME)) {
frameSearch = 0;
//begin sampling
if (wwvbInState) {
frameHigh++;
} else {
frameLow++;
}
frameSamples++;
digitalWrite(DEBUG1_OUTPUT_PIN, 0);
return;
}
if(frameStart && (frameSamples >= NUM_SAMPLES_PER_FRAME)) {
frameReadyForRead = 1;
frameHighReadOut = frameHigh;
frameLowReadOut = frameLow;
frameStart = 0;
frameHigh = 0;
frameLow = 0;
frameSamples = 0;
}
digitalWrite(DEBUG1_OUTPUT_PIN, 0);
}
char pb[255];
void loop() {
digitalWrite(LED_BUILTIN, !wwvbInState);
if(timeToTick) {
timeToTick = 0;
TickSecond();
}
yield();
if(frameReadyForRead) {
frameReadyForRead = 0;
processFrame();
}
yield();
PPSLowIfRequired();
yield();
if (displayUpdateRequired) {
updateDisplay();
displayUpdateRequired = 0;
}
}
void SetPPSHigh() {
digitalWrite(PPS_OUTPUT_PIN, 1);
}
void SetPPSLow() {
digitalWrite(PPS_OUTPUT_PIN, 0);
}
void SendPPS() {
unsigned int tickInterval = millisSinceBoot - ppsActivationTime;
ppsActivationTime = millisSinceBoot;
SetPPSHigh();
}
void PPSLowIfRequired() {
if ((millisSinceBoot - ppsActivationTime) > 500) {
SetPPSLow();
}
}
void TickSecond() {
char buf[255];
sprintf(buf, "*** TICK(%d): WWVB going low after %d ms high (EDGE)\n", frameCounter, millisSinceBoot - lastLowMillis);
SendPPS();
Serial.print(buf);
}
void processFrame() {
char buf[255];
sprintf(buf, "end of frame summary: frameHigh: %d, frameLow: %d\n", frameHighReadOut, frameLowReadOut);
Serial.print(buf);
float rawVal =
(float)frameHighReadOut
/
( (float)frameHighReadOut + (float)frameLowReadOut );
rawVal *= 1000;
unsigned int intRawVal = (int)rawVal;
if (intRawVal > 100000) {
intRawVal = 100000;
}
displayUpdateRequired++;
registerBit(convertDutyCycleToBit(intRawVal));
}
int convertDutyCycleToBit(unsigned int rawVal) {
char buf[255];
/*
20% - marker
50% - one bit
80% - zero bit
our cutoff points will be 50% and 80%
*/
char bitbuf[20];
int output = 0;
output = ZEROBIT;
sprintf(bitbuf, "ZERO");
if (rawVal < 800) {
output = ONEBIT;
sprintf(bitbuf, "ONE");
}
if (rawVal < 680) {
output = MARKBIT;
sprintf(bitbuf, "MARK");
}
sprintf(buf, "frame rawVal=%d, bit=%s\n", rawVal, 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");
}
if (millisSinceBoot - frameStartTime < 10000) {
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,"frameReadyToStart=%d\n",frameReadyToStart);
Serial.print(buf);
sprintf(buf,"beginFrameSearch=%d\n",beginFrameSearch);
Serial.print(buf);
sprintf(buf,"frameSearch=%d\n",frameSearch);
Serial.print(buf);
sprintf(buf,"frameStart=%d\n",frameStart);
Serial.print(buf);
sprintf(buf,"frameStartTime=%d\n",frameStartTime);
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,"frameHigh=%d\n",frameHigh);
Serial.print(buf);
sprintf(buf,"frameLow=%d\n",frameLow);
Serial.print(buf);
}
Loading…
Cancel
Save