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
|
// #define DEBUG 1
#include <user_settings.h>
#include <header.h>
#include <FastLED.h>
#include <ESP8266WiFi.h>
#include <ezTime.h>
// Define the array of leds
CRGB leds[LED_COUNT];
Timezone CopenhagenTime;
int hue = 0;
int max_brightness = 255;
bool on_state[SEGMENT_COUNT * DIGIT_COUNT] = {false};
typedef enum : uint8_t {
seg_a = 1<<0, // 00000001
seg_b = 1<<1, // 00000010
seg_c = 1<<2, // 00000100
seg_d = 1<<3, // ...
seg_e = 1<<4,
seg_f = 1<<5,
seg_g = 1<<6
} segments;
uint8_t symbols[] = {
/* 0 */ seg_a | seg_b | seg_c | seg_d | seg_e | seg_f,
/* 1 */ seg_b | seg_c,
/* 2 */ seg_a | seg_b | seg_g | seg_e | seg_d,
/* 3 */ seg_a | seg_b | seg_g | seg_c | seg_d,
/* 4 */ seg_f | seg_g | seg_c | seg_b,
/* 5 */ seg_a | seg_f | seg_g | seg_c | seg_d,
/* 6 */ seg_a | seg_f | seg_g | seg_c | seg_d | seg_e,
/* 7 */ seg_a | seg_b | seg_c,
/* 8 */ seg_a | seg_b | seg_c | seg_d | seg_e | seg_f | seg_g,
/* 9 */ seg_g | seg_f | seg_a | seg_b | seg_c | seg_d,
/* ° */ seg_a | seg_b | seg_g | seg_f,
/* C */ seg_a | seg_f | seg_e | seg_d
};
uint8_t spin_animation[] = {
seg_a,
seg_b,
seg_c,
seg_d,
seg_e,
seg_f,
};
uint8_t dual_spin_animation[] = {
seg_a | seg_d,
seg_b | seg_e,
seg_c | seg_f,
seg_d | seg_a,
seg_e | seg_b,
seg_f | seg_c,
};
void display_symbols(uint8_t *symbols_mask) {
// Fades all at once
for (int brightness = 0; brightness <= max_brightness; brightness ++) {
for (uint8_t digit_index = 0; digit_index < DIGIT_COUNT; digit_index++) {
// Counts up by bitshifting
uint8_t seg_counter = 1;
for (int segment_index = 0; segment_index < SEGMENT_COUNT; segment_index++) {
int digit_offset = digit_index * SEGMENT_COUNT;
int led_raw_index = digit_offset + segment_index;
int led_index = START_OFFSET + led_raw_index;
bool previously_on = on_state[led_raw_index];
// Compare the current segment bit with the mask for the symbol
bool turn_on = symbols_mask[digit_index] & seg_counter;
if (turn_on) {
// Turn on LED
if(previously_on){
// Previously on, allow hue change
leds[led_index] = CHSV(hue, SATURATION, max_brightness);
} else {
leds[led_index] = CHSV(hue, SATURATION, brightness);
}
} else {
// Turn off LED
if(previously_on) {
leds[led_index] = CHSV(hue, SATURATION, max_brightness - brightness);
}
}
if (brightness == max_brightness) {
on_state[led_raw_index] = turn_on;
}
seg_counter = seg_counter<<1;
}
}
FastLED.show();
delay(2);
}
}
void display_number(int number) {
uint8_t symbols_mask[DIGIT_COUNT];
// Start from the last digit
for (int digit_index = DIGIT_COUNT - 1; digit_index >= 0; digit_index--) {
symbols_mask[digit_index] = symbols[number % 10];
number /= 10;
}
display_symbols(symbols_mask);
}
void display_symbol(int digit_index, uint8_t symbol) {
// Counts up by bitshifting
uint8_t seg_counter = 1;
for (int segment_index = 0; segment_index < SEGMENT_COUNT; segment_index++) {
int offset = digit_index * SEGMENT_COUNT;
int led_index = START_OFFSET + offset + segment_index;
// Compare the current segment bit with the mask for the symbol
if (symbol & seg_counter) {
// Turn on LED
leds[led_index] = CHSV(hue, SATURATION, max_brightness);
FastLED.show();
}
seg_counter = seg_counter<<1;
}
}
void play_animation(uint8_t *animation, int animation_size, int segment_delay) {
for (int i = 0; i < animation_size; i++){
for (int digit_index = 0; digit_index < 4; digit_index++) {
display_symbol(digit_index, animation[i]);
}
hue += 16;
FastLED.show();
delay(segment_delay);
}
}
void update_clock() {
// Format the time as a 4-digit integer
int timeInt = CopenhagenTime.dateTime("Hi").toInt(); // Sadly string format
display_number(timeInt);
hue++;
}
void setup() {
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, LED_COUNT).setCorrection( TypicalLEDStrip );
FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, LED_COUNT); // GRB ordering is assumed
// Turn off all LEDs
FastLED.clear();
#ifdef DEBUG
Serial.begin(9600);
#endif
setServer(NTP_SERVER);
WiFi.setHostname("7-segment-esp-ws2811"); //define hostname
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED)
{
if (millis() < WIFI_CONN_ANI_TIMEOUT) {
play_animation(spin_animation, sizeof(spin_animation), 100);
} else {
// Turn off all LEDs
FastLED.clear();
}
}
delay(100);
hue = 0;
// Turn off all LEDs
FastLED.clear();
Serial.println();
Serial.print("Connected, IP address: ");
Serial.println(WiFi.localIP());
waitForSync();
Serial.println("UTC: " + UTC.dateTime());
CopenhagenTime.setLocation("Europe/Copenhagen");
Serial.println("Copenhagen time: " + CopenhagenTime.dateTime());
update_clock();
}
/*
// Test each segment
int i = 0;
void loop() {
leds[i] = CRGB::Red;
FastLED.show();
delay(500);
leds[i] = CRGB::Black;
FastLED.show();
delay(500);
i++;
if (i > LED_COUNT - 1) {
i = 0;
}
}
*/
void loop() {
// Only update on each hole minute to avoid flickering
int seconds = CopenhagenTime.dateTime("s").toInt();
int minutes = CopenhagenTime.dateTime("i").toInt();
if(minutes == 59 && seconds >= 55) { //if(minutes == 59 && seconds >= 55) {
play_animation(dual_spin_animation, sizeof(spin_animation), 50);
FastLED.clear();
}
if(seconds != 0) {
delay(10);
return;
}
update_clock();
delay(1000);
}
|