Browse Source

initial

master
Jeffrey Paul 9 months 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

131
i2c-lcd/i2c-lcd.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);
}
*/

51
infrared-read/infrared-read.ino

@ -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
}
}

82
quadalphanum/quadalphanum.ino

@ -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);
}

871
wwvb-ref/wwvb-ref.ino

@ -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);
}

175
wwvb/floats.csv

@ -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

1086
wwvb/output.txt

File diff suppressed because it is too large

482
wwvb/wwvb.ino

@ -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