-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSingleSevenSegmentSerialBinaryClock.ino
370 lines (310 loc) · 12 KB
/
SingleSevenSegmentSerialBinaryClock.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/*
Single 7 segment display digit clock.
Hardware:
* Adafruit Bluefruit 32u4 3.3V
* Common anode 7 segment single digit
* 74HC595N 8-bit shift register
* trim pot on an analog port
* DS3231 realtime clock
* SPST switch for DST selection (optional)
This uses a shift register so I can have enough pins to include an RTC.
Here is the default bit sequence/pin mappings for lighting the segments:
0 - DP via Q0
1 - C via Q1
2 - B via Q2
3 - E via Q3
4 - F via Q4
5 - D via Q5
6 - G via Q6
7 - A via Q7
Loraxipam@github.com (c) 2020
See LICENSE.txt for details on MIT Licensing of this source code, which is
included by reference herein.
*/
// I'm not using the BLE but it could report the current pattern or update the RTC
// It's not often easy to get the serial input to change the clock on a Feather
//#include <Adafruit_BLE.h>
//#include <Adafruit_BluefruitLE_SPI.h>
//#include <Adafruit_BluefruitLE_UART.h>
//#include "BluefruitConfig.h"
#include <LowPower.h>
// for DS3231 RTC
#include <Wire.h>
#include "DS1307.h"
// Feather pins going to the 74HC595
#define CLOCK 12
#define LATCH 11
#define DATA 10
// Put a toggle switch on an digital port to signal if DST should be added.
// This will be pulled up by default so tie it to ground to signal TRUE.
#define DST_PIN 6
// This is the "LowDPR" LED-to-74HC595 mapping that I typically use
// lowDPR is the default layout but lowDPRows I think is the coolest...maybe
// Put a trim pot on an analog port and determine its low and hi analog values
#ifndef PPIN
#define PPIN 0
#define CPIN 1
#define BPIN 2
#define EPIN 3
#define FPIN 4
#define DPIN 5
#define GPIN 6
#define APIN 7
#define PATTERN 10
#define SELECTOR_ANALOG_PIN A1
#define SELECTOR_ANALOG_BITS 10
#define SELECTOR_ANALOG_LO 50
#define SELECTOR_ANALOG_HI 1010
#endif
// For convenience, here's straight mapping of LED pins to 74HC595
// These numbers skip the actual LED pins 3 and 8 which are the common lines
//#define EPIN 0
//#define DPIN 1
//#define CPIN 2
//#define PPIN 3
//#define BPIN 4
//#define APIN 5
//#define GPIN 6
//#define FPIN 7
// The "pattern" struct defines an individual segment illumination pattern. It's a name and a set of pins.
struct pattern {
char *pattname;
byte pinstep[8];
};
// The "patterns" struct holds all the known illumination patterns for the sketch
static struct pattern patterns[] = {
{"lowDPL", {PPIN, EPIN, FPIN, CPIN, BPIN, DPIN, GPIN, APIN}}, // DP is low bit, qtr hrs up left side. Hours right side & middle
{"lowDPR", {PPIN, CPIN, BPIN, EPIN, FPIN, DPIN, GPIN, APIN}}, // DP is low bit, qtr hrs up right side. Hours left, then middle
{"midSecsL", {DPIN, GPIN, APIN, EPIN, FPIN, CPIN, BPIN, PPIN}}, // center bottom to top are min/qtr, then hrs up left, right, DP is high bit
{"midSecsR", {DPIN, GPIN, APIN, CPIN, BPIN, EPIN, FPIN, PPIN}}, // center bottom to top are min/qtr, then hrs up right, left, DP is high bit
{"lowDPRows",{PPIN, CPIN, EPIN, BPIN, FPIN, DPIN, GPIN, APIN}}, // DP is low bit, outside bottom vert qtrs. Hrs outside top vert then up middle
{"topSecs", {APIN, BPIN, FPIN, CPIN, DPIN, EPIN, GPIN, PPIN}}, // A is low bit, top verts are qtrs, bottom five are hours, DP is high bit
{"lowSecs", {DPIN, CPIN, EPIN, APIN, BPIN, FPIN, GPIN, PPIN}}, // D is low bit, bottom verts are qtrs, top five are hours, DP is high bit
{"filler", {APIN, GPIN, DPIN, EPIN, CPIN, FPIN, BPIN, PPIN}}, // pour it down the center and fill in from the bottom...
{"bubble", {DPIN, GPIN, APIN, FPIN, BPIN, EPIN, CPIN, PPIN}}, // bubble up from the bottom and flow down the sides...
{"bubble2", {PPIN, DPIN, GPIN, APIN, FPIN, BPIN, EPIN, CPIN}}, // bubble up from the bottom and flow down the sides...(but DP is low bit)
{"biglyU", {PPIN, APIN, GPIN, BPIN, CPIN, DPIN, EPIN, FPIN}}, // qrts top center, hours around from top, DP is low bit
{"center", {GPIN, DPIN, APIN, BPIN, CPIN, EPIN, FPIN, PPIN}} // blinks from the center bar, qtrs bottom/top, and hrs go around. DP is high bit
};
// 7 Segment display type
// change this to false if you're using a different LED
const bool commonAnode = true;
// General debugging on serial
const bool debugging = false;
unsigned int debugDelay = 640;
// Bluetooth
//Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
// Realtime clock
DS1307 rtc;
int Hor, Sec;
// The current bit sequence pattern
byte patternIndex;
// "mapPattern" moves the bits of the time byte to the bits of the display pattern byte
uint8_t mapPattern(uint8_t inByte) {
uint8_t outByte = 0;
for (uint8_t b=0; b<8; b++){
if (bitRead(inByte,b)==0) {
bitClear(outByte,patterns[patternIndex].pinstep[b]);
} else {
bitSet(outByte,patterns[patternIndex].pinstep[b]);
}
}
return outByte;
}
// "chooseSelector" reads an analog pin and returns the relative index of the defined bit sequence patterns
byte chooseSelector(int analogPin) {
int rowSize, structSize, entries, selectorVal, lowVal, hiVal, valWidth;
byte index;
// The bracketing for the analog reading is based on the number of pattern entries.
structSize = (int)(sizeof(patterns));
rowSize = (int)(sizeof(patterns[0]));
entries = (int)(structSize / rowSize);
// Read the 10-bit analog port to determine which pattern we'll use.
selectorVal = analogRead(SELECTOR_ANALOG_PIN);
// The pot that I use goes from about 60 to about 1000, so if you calibrate your pot
// then you can and open them a bit and use those values here.
// Use your actual pot results for the LO and HI values. Probably valid for all ADC resolutions.
lowVal = SELECTOR_ANALOG_LO; hiVal = SELECTOR_ANALOG_HI;
valWidth = (hiVal - lowVal) / entries;
for (byte ent=0;ent<entries;ent++) {
if (((ent*valWidth+lowVal) <= selectorVal) && (selectorVal <= ((ent+1)*valWidth+lowVal))) {
index = ent;
break;
}
}
if (debugging) {
Serial.print("S ");
Serial.print(structSize);
Serial.print(" R ");
Serial.print(rowSize);
Serial.print(" E ");
Serial.println(entries);
Serial.print(" sV ");
Serial.print(selectorVal);
Serial.print(" I ");
Serial.println(index);
}
return index;
}
// "lightMe" sends eight bits to the shift register
void lightMe(uint8_t bits){
// throw the latch
digitalWrite(LATCH, LOW);
// send the data
shiftOut(DATA, CLOCK, MSBFIRST, (commonAnode ? ~bits : bits));
// close the latch
digitalWrite(LATCH, HIGH);
}
// The "showTimeShift" function receives the hour and the seconds of the hour
// then illuminates the correct segments
void showTimeShift(int hours, int seconds /* of the hour */) {
int eighth;
uint8_t curTime, mapTime;
if (hours < 0 || hours > 23 || seconds < 0 || seconds > 3600 ) return;
// calculate the holders
eighth=seconds/450; // 450s is 1/8 of an hour...7.5min.
// stuff the hour bits on and move them over a bit (or three)
curTime = hours << 3; // in order to fit...
curTime |= eighth;
// if you want to save processing, (say you only want to use a single pattern)
// then just comment out this mapping call and
// then change the shiftOut below to use curTime instead of mapTime.
// Make sure your pinouts from '595 to LED are okay, though.
mapTime = mapPattern(curTime);
if (debugging) {
Serial.print("H ");
Serial.print(hours);
Serial.print(" S ");
Serial.print(seconds);
Serial.print(" E ");
Serial.println(eighth);
Serial.print(" curTime ");
Serial.print(curTime,BIN);
Serial.print(" ~curTime ");
Serial.println((uint8_t)~curTime,BIN);
}
// light 'em up!
lightMe(mapTime);
}
// "patternDemo" shows a fast pattern of the bit sequence
void patternDemo() {
// show the low bits first
for (int i=0; i<24; i++) {
showTimeShift(0,(i%8)*450);
delay(150);
}
// show the hours next
for (int i=0; i<72; i++) {
showTimeShift(i%24,0);
delay(150);
}
}
// "dst" reads the toggle pin and adds an hour if it's true
int dst(int inHour) {
return (inHour+~digitalRead(DST_PIN))%24;
}
void setup() {
String t;
// set the shift register pins
pinMode(CLOCK, OUTPUT);
pinMode(LATCH, OUTPUT);
pinMode( DATA, OUTPUT);
pinMode(DST_PIN, INPUT_PULLUP);
// start the peripherals
Wire.begin();
rtc.begin();
delay(128);
rtc.getTime();
// for debugging and first setup of the RTC
if (debugging) {
Serial.begin(115200);
delay(2048);
Serial.print(F(":"));Serial.print(F(":"));Serial.print(F(":"));
Serial.println(F("SingleSevenSegBinaryClock v3, the binary sleeper version."));
Serial.print(F("Current date: "));
Serial.print(rtc.year, DEC);
Serial.print(F(":"));
Serial.print(rtc.month < 10 ? F("0") : F(""));Serial.print(rtc.month, DEC);
Serial.print(F(":"));
Serial.print(rtc.dayOfMonth < 10 ? F("0") : F(""));Serial.print(rtc.dayOfMonth, DEC);
Serial.print(F(":"));
Serial.print(rtc.dayOfWeek);Serial.println(rtc.dayOfWeek, DEC);
Serial.print(F("Current time: "));
Serial.print(rtc.hour < 10 ? F("0") : F(""));Serial.print(rtc.hour, DEC);
Serial.print(F(":"));
Serial.print(rtc.minute < 10 ? F("0") : F(""));Serial.print(rtc.minute, DEC);
Serial.print(F(":"));
Serial.print(rtc.second < 10 ? F("0") : F(""));Serial.println(rtc.second, DEC);
Serial.println(F("If datetime is wrong, enter new time as HH:MM:SS:20YY:mm:dd:dow."));
delay(18000);
Serial.println(F("...time's up. Off we go."));
t = Serial.readString();
//You'll need to fill in these if your rtc has lost power.
if (t != "") {
// add the 18ish seconds it takes to wait for the prompt
if (t.substring(6,8).toInt() <= 41 ) {
rtc.fillByHMS(t.substring(0,2).toInt(),
t.substring(3,5).toInt(),
t.substring(6,8).toInt()+19); // add 19 seconds to the current time, just to get it looking right
} else {
rtc.fillByHMS(t.substring(0,2).toInt(),
t.substring(3,5).toInt()+1,
t.substring(6,8).toInt()-41);
}
rtc.fillByYMD(t.substring(9,13).toInt(),
t.substring(14,16).toInt()+1,
t.substring(17,19).toInt()-41);
rtc.fillDayOfWeek(t.substring(20,21).toInt());
rtc.setTime(); //write time to the RTC chip
}
delay(100);
}
// A simple pot can choose the pattern for you
// get the current pattern from the analog pin
patternIndex = chooseSelector(SELECTOR_ANALOG_PIN);
// get the time once more
rtc.getTime();
Hor = (int)(rtc.hour);
Sec = (int)(rtc.minute*60+rtc.second);
showTimeShift(dst(Hor), Sec);
}
void loop() {
// DEBUG
if (debugging && debugDelay > 512 /*&& ((i%15UL)==0)*/) Serial.println(Sec, DEC);
// Choose a different pattern
byte selector = chooseSelector(SELECTOR_ANALOG_PIN);
// If the new pattern is not the same as the old one,
// flash the new one a few times before switchover
if (selector != patternIndex) {
for (int flash=0; flash<4; flash++) {
lightMe(0);
delay(100);
lightMe((uint8_t)selector+1);
delay(100);
}
// now remember the new pattern
patternIndex = selector;
// provide a short demo
patternDemo();
// show the current time
showTimeShift(dst(Hor), Sec);
}
// Read the current time
rtc.getTime();
Sec = (int)(rtc.minute*60+rtc.second);
Hor = (int)(rtc.hour);
// Show the current time
if ((Sec%30)==0) { // every 30 seconds is better than every 450 seconds
showTimeShift(dst(Hor), Sec);
}
// DEBUG
delay(debugDelay);
// For Feather32u4 MCU
// Sleep for most of a second. This gives about 70ms to do real work before the clock strikes 0
if ("sleep" == "sleep"){
LowPower.idle(SLEEP_500MS, ADC_OFF, TIMER4_OFF, TIMER3_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
LowPower.idle(SLEEP_250MS, ADC_OFF, TIMER4_OFF, TIMER3_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
//LowPower.idle(SLEEP_120MS, ADC_OFF, TIMER4_OFF, TIMER3_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
//LowPower.idle(SLEEP_60MS, ADC_OFF, TIMER4_OFF, TIMER3_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
}
}