From 467e2f9c96396bf20a756a38e7be988d9bf30edd Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Tue, 18 Feb 2020 16:42:59 -0500 Subject: [PATCH] the rest of the commit --- display_test/display_test.ino | 88 ++++++ ...he_perfect_clock-oldversionwithseconds.ino | 271 ++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100755 display_test/display_test.ino create mode 100644 old_version_with_seconds/the_perfect_clock-oldversionwithseconds.ino diff --git a/display_test/display_test.ino b/display_test/display_test.ino new file mode 100755 index 0000000..3a585b9 --- /dev/null +++ b/display_test/display_test.ino @@ -0,0 +1,88 @@ +#include + +#include +#include +#include +#include + +/*************************************************** + This is a library for our I2C LED Backpacks + + Designed specifically to work with the Adafruit LED 7-Segment backpacks + ----> http://www.adafruit.com/products/881 + ----> http://www.adafruit.com/products/880 + ----> http://www.adafruit.com/products/879 + ----> http://www.adafruit.com/products/878 + + These displays use I2C to communicate, 2 pins are required to + interface. There are multiple selectable I2C addresses. For backpacks + with 2 Address Select pins: 0x70, 0x71, 0x72 or 0x73. For backpacks + with 3 Address Select pins: 0x70 thru 0x77 + + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +//#include // Enable this line if using Arduino Uno, Mega, etc. +#include +#include "Adafruit_LEDBackpack.h" + +Adafruit_7segment matrix = Adafruit_7segment(); + +void setup() { +#ifndef __AVR_ATtiny85__ + Serial.begin(9600); + Serial.println("7 Segment Backpack Test"); +#endif + matrix.begin(0x70); +} + +void loop() { + // try to print a number thats too long + matrix.print(10000, DEC); + matrix.writeDisplay(); + delay(500); + + // print a hex number + matrix.print(0xBEEF, HEX); + matrix.writeDisplay(); + delay(500); + + // print a floating point + matrix.print(12.34); + matrix.writeDisplay(); + delay(500); + + // print with print/println + for (uint16_t counter = 0; counter < 9999; counter++) { + matrix.println(counter); + matrix.writeDisplay(); + delay(10); + } + + // method #2 - draw each digit + uint16_t blinkcounter = 0; + boolean drawDots = false; + for (uint16_t counter = 0; counter < 9999; counter ++) { + matrix.writeDigitNum(0, (counter / 1000), drawDots); + matrix.writeDigitNum(1, (counter / 100) % 10, drawDots); + matrix.drawColon(drawDots); + matrix.writeDigitNum(3, (counter / 10) % 10, drawDots); + matrix.writeDigitNum(4, counter % 10, drawDots); + + blinkcounter+=50; + if (blinkcounter < 500) { + drawDots = false; + } else if (blinkcounter < 1000) { + drawDots = true; + } else { + blinkcounter = 0; + } + matrix.writeDisplay(); + delay(10); + } +} diff --git a/old_version_with_seconds/the_perfect_clock-oldversionwithseconds.ino b/old_version_with_seconds/the_perfect_clock-oldversionwithseconds.ino new file mode 100644 index 0000000..3e1c7bc --- /dev/null +++ b/old_version_with_seconds/the_perfect_clock-oldversionwithseconds.ino @@ -0,0 +1,271 @@ +// "The Perfect Clock" + +// Copyright (C) 2019 by Art Cancro + +// My perfect clock has no buttons and cannot be set manually. This version uses a WWVB receiver module +// attached to pin D9 of the Arduino, and sets the clock any time it receives a complete frame. The clock +// is kept without an RTC, simply using the millis() timer. When time is set, it is displayed on +// a 7-segment array connected using an HT16K33 decoder/driver (yes, an Adafruit backpack). + +// The clock is hard coded to use US Eastern time with DST in effect whenever WWVB is announcing it. + +const uint8_t wwvb = 9; // pin on which WWVB signal will be received +const uint8_t greenled = 2; // Attach a green LED to this pin +const uint8_t yellowled = 3; // Attach a yellow LED to this pin +const uint8_t redled = 4; // Attach a red LED to this pin +const uint8_t addr = 0x70; // I2C address of HT16K33 (using Adafruit backpack with digits on 0,1,3,4; dots on 2) + +#define MILLISECONDS_PER_SECOND 1002 // Nominally 1000, but the timer on my Nano runs fast and we don't have an RTC + +// This is a simple BCD-to-7-segment font. It includes 0x0A through 0x0F even though they're not needed for a time clock. +const uint8_t sevensegfont[] = { 63, 6, 91, 79, 102, 109, 125, 7, 127, 111, 119, 124, 57, 94, 121, 113 }; +const uint8_t firstcolfont[] = { 0, 6, 91 }; // this version of the font is for the first position + +#include // I2C library + +int hour = 0; +int minute = 0; +int second = 0; +unsigned long millisecond = 0; +unsigned long previous_millis = 0; +unsigned long last_sync = -86398000; +uint16_t displayBuffer[8]; // Digit buffer for HT16K33 +int previous_minute = 61; // What the minute was previously; we use this to detect whether an update is needed +int this_pulse = 0; // Value of the current pulse received +int previous_pulse = 0; // Value of the previous pulse received (two "mark" bits == new frame) +int start_of_pulse = 0; // The value of the millis() timer when the current pulse began +uint8_t framebuf[60]; // We store the entire 60-bit frame here +uint8_t framesync = 0; // Nonzero if we've received all good pulses since the start of the frame +int position_in_frame = 0; // Where we are in the frame (this happens to also be the second of the minute) +int previous_signal = 0; // "high" or "low" received on the previous cycle (so we can do edge detection) +int time_is_set = 0; // nonzero when time has been set at least once + +void setup() +{ + int i; + + pinMode(LED_BUILTIN, OUTPUT); // The built-in LED will display the raw WWVB signal pulses + pinMode(greenled, OUTPUT); + pinMode(yellowled, OUTPUT); + pinMode(redled, OUTPUT); + pinMode(wwvb, INPUT); // Input pin for WWVB receiver signal + + Wire.begin(); // Initialize I2C + + Wire.beginTransmission(addr); + Wire.write(0x21); // turn on oscillator + Wire.endTransmission(); + + Wire.beginTransmission(addr); + Wire.write(0xE1); // brightness (max is 15) + Wire.endTransmission(); + + Wire.beginTransmission(addr); + Wire.write(0x81); // no blinking or blanking + Wire.endTransmission(); + + displayBuffer[0] = 0; + displayBuffer[1] = 0; + displayBuffer[2] = 16; + displayBuffer[3] = 0; + displayBuffer[4] = 0; + show(); +} + + +// Note: only write to the display when the readout needs to be updated. +// Speaking I2C on every loop iteration jams the WWVB receiver. +void loop() +{ + unsigned long m = millis(); + digitalWrite(redled, (m%1000) ? LOW : HIGH); + if (m != previous_millis) { + millisecond += (m - previous_millis); + if (millisecond >= MILLISECONDS_PER_SECOND) { + millisecond -= MILLISECONDS_PER_SECOND; + ++second; + if (second > 59) { + second = 0; + ++minute; + if (minute > 59) { + minute = 0; + ++hour; + if (hour > 23) { + hour = 0; + } + } + } + } + } + previous_millis = m; + + int pulse_length; + int signal = digitalRead(wwvb); // is the input high or low right now? + digitalWrite(LED_BUILTIN, signal); // use the onboard LED to show the signal + + if (signal && (!previous_signal)) { + start_of_pulse = millis(); + } + else if ((!signal) && (previous_signal)) { + pulse_length = millis() - start_of_pulse; + + if (pulse_length > 150 && pulse_length < 250) { // "0" bit ~= 200 ms (represented as "0") + this_pulse = 0; + } else if (pulse_length > 450 && pulse_length < 550) { // "1" bit ~= 500 ms (represented as "1") + this_pulse = 1; + } else if (pulse_length > 750 && pulse_length < 850) { // marker bit ~= 800 ms (represented as "2") + this_pulse = 2; + } else { + this_pulse = 15; // bad pulse (represented as "15") + framesync = 0; // throw the whole frame away + } + + // BEGIN -- THINGS TO DO AT THE END OF A PULSE + + if ((this_pulse == 2) && (previous_pulse == 2)) { // start of a new frame! + + if (framesync == 1) { + set_the_time(); // We have a whole good frame. Set the clock! + } + + framesync = 1; + position_in_frame = 0; + } + + if (framesync) { // yellow LED = we currently have frame sync + analogWrite(yellowled, 10); // (we run it at a low intensity) + } + else { + digitalWrite(yellowled, LOW); + } + + if ((framesync) && (position_in_frame < 60)) { + framebuf[position_in_frame++] = this_pulse; + } + + previous_pulse = this_pulse; + + // END -- THINGS TO DO AT THE END OF A PULSE + } + + previous_signal = signal; + + // Update the display only if it's a new minute. + + if (time_is_set && (minute != previous_minute)) { + previous_minute = minute; + int h12 = (hour % 12) ; + if (h12 == 0) h12 = 12; + displayBuffer[0] = firstcolfont[h12 / 10]; + displayBuffer[1] = sevensegfont[h12 % 10]; + displayBuffer[2] = (hour<12) ? 0x06 : 0x0a; // AM or PM dot , colon always on + displayBuffer[3] = sevensegfont[minute / 10]; + displayBuffer[4] = sevensegfont[minute % 10]; + show(); + } + + if ((m - last_sync) < 86400000) { // green LED = got a good sync in the last 24 hours + digitalWrite(greenled, HIGH); + } + else { + digitalWrite(greenled, LOW); + } +} + + +// Write the display buffer to the display +void show() +{ + Wire.beginTransmission(addr); + Wire.write(0x00); // start at address 0x0 + for (int i = 0; i < 5; i++) { + Wire.write(displayBuffer[i] & 0xFF); + Wire.write(displayBuffer[i] >> 8); + } + Wire.endTransmission(); +} + + +// Set the software clock to the WWVB time currently in the buffer +void set_the_time() +{ + int i, newhour, newminute, dst; + + // These six positions MUST contain marker bits. + // If any of them do not, we are looking at a corrupt frame. + int markers[] = { 0, 9, 19, 39, 49, 59 }; + for (i=0; i<6; ++i) { + if (framebuf[markers[i]] != 2) { + return; + } + } + + newhour = (framebuf[12] ? 20 : 0); + newhour += (framebuf[13] ? 10 : 0); + newhour += (framebuf[15] ? 8 : 0); + newhour += (framebuf[16] ? 4 : 0); + newhour += (framebuf[17] ? 2 : 0); + newhour += (framebuf[18] ? 1 : 0); + if ((newhour < 0) || (newhour > 23)) { + return; // reject impossible hours + } + + newminute = (framebuf[1] ? 40 : 0); + newminute += (framebuf[2] ? 20 : 0); + newminute += (framebuf[3] ? 10 : 0); + newminute += (framebuf[5] ? 8 : 0); + newminute += (framebuf[6] ? 4 : 0); + newminute += (framebuf[7] ? 2 : 0); + newminute += (framebuf[8] ? 1 : 0); + if ((newminute < 0) || (newminute > 59)) { + return; // reject impossible minutes + } + + // advance 60 seconds because WWVB gives the *previous* minute + newminute += 1; + if (newminute >= 60) { + newminute = newminute % 60; + newhour += 1; + } + + // US Eastern time (FIXME make this adjustable) + newhour -= 5; + + // DST (FIXME make this adjustable) + dst = (framebuf[57] ? 2 : 0); + dst += (framebuf[58] ? 1 : 0); + switch(dst) { + case 0: // dst not in effect (make no adjustments) + break; + case 2: // dst begins today (adjust if local hour > 2) + if (newhour > 2) { + ++newhour; + } + break; + case 3: // dst is in effect (always adjust) + ++newhour; + break; + case 1: // dst ends today (adjust if local hour < 2) + if (hour < 2) { + ++newhour; + } + break; + } + + // If we went back to the previous day, adjust so that hour > 0 + if (newhour < 0) { + newhour += 24; + } + + // Set the software clock: + // * We have decoded the hour and minute from the signal + // * This function always gets called *after* the first pulse at :00, so we set the second to :00 and millisecond to 800 + hour = newhour; + minute = newminute; + second = 0; + millisecond = 800; + time_is_set = 1; + + // Let's remember the last time we synced the clock + last_sync = millis(); +} -- 2.30.2