aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
blob: 4f18fe31632fe22dfbc97a9fbe2b5a1dee348a69 (plain)
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
#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;

// Used for hue shift each minute
// https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors
int hue = 0;
int max_brightness = 255;
// Hold state if an led is on, to check if it should be faded on / off, or if it already is in desired state
bool on_state[SEGMENT_COUNT * DIGIT_COUNT] = {false};

// Segment naming scheme, corresponds to LED strip soldering order, standardized on Wikipeadia
// https://en.wikipedia.org/wiki/Seven-segment_display#/media/File:7_Segment_Display_with_Labeled_Segments.svg
// Last bit unused
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, // 00111111
	/* 1 */ seg_b | seg_c,                                 // 00000110
	/* 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
};

// Which segments should change color in this frame of the animation
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,
};

// Fades all at once
void display_symbols(uint8_t *symbols_mask) {
	// Loop over each brightness state and apply it to all elements if it's not already the desired state
	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;
				// LEDS can be offsed by START_INDEX, state array isn't
				int led_raw_index = digit_offset + segment_index;
				int led_index = START_OFFSET + led_raw_index;
				// Get this LEDs previous state to see if it should be changed
				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 {
						// Previously off, gradually increase brightness with loop e.g. 0-255
						leds[led_index] = CHSV(hue, SATURATION, brightness);
					}
				} else {
					// Turn off LED
					if(previously_on) {
						// Previously on, gradually decrease brightness with a negative of the loop e.g. 255-0
						leds[led_index] = CHSV(hue, SATURATION, max_brightness - brightness);
					}
				}

				// Last loop itteration, save current LED state
				if (brightness == max_brightness) {
					on_state[led_raw_index] = turn_on;
				}
				
				// Increment segment counter used with bit mask comparison by bitshifting
				seg_counter = seg_counter<<1;
			}
		}

		// Push changes of entire LED array to LEDs
		FastLED.show();
		delay(2);
	}
}

// Show a four digit number, by fading on LEDs
void display_number(int number) {
	// Build up entire bit mask, so all can be faded on at once
	uint8_t symbols_mask[DIGIT_COUNT];
	// Start from the last digit
	for (int digit_index = DIGIT_COUNT - 1; digit_index >= 0; digit_index--) {
		// Get only last digit
		symbols_mask[digit_index] = symbols[number % 10];
		// Pop last digit
		number /= 10;
	}
	display_symbols(symbols_mask);
}

// Instantly changes. Binary, no fade
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 bit mask for the symbol
		if (symbol & seg_counter) {
			// Turn on LED
			leds[led_index] = CHSV(hue, SATURATION, max_brightness);
			FastLED.show();
		}

		// Increment segment counter used with bit mask comparison by bitshifting
		seg_counter = seg_counter<<1;
	}
}

// Change hue of 0-7 segments per frame each digit displays the same
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; // Increment HSV hue a lot for rainbow vomit
		FastLED.show();
		delay(segment_delay);
	}
}

// Check the time, and display it on the clock
void update_clock() {
	// Format the time as a 4-digit integer
	int timeInt = CopenhagenTime.dateTime("Hi").toInt(); // Sadly string format
	display_number(timeInt);
	hue++; // Increment HSV hue 
}

void setup() {
	// Setup LEDs
	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();

	Serial.begin(9600);
	
	setServer(NTP_SERVER);

	WiFi.setHostname(HOSTNAME);
	WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
	Serial.print("Connecting");
	while (WiFi.status() != WL_CONNECTED)
	{
		// Play animation untill connected to WiFi, or animation timeout is reached
		// Will continue attempting to connect to WiFi
		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();
	
	// Play animation 5 seconds before hour change
	if(minutes == 59 && seconds >= 55) {
		play_animation(dual_spin_animation, sizeof(spin_animation), 50);
  	FastLED.clear();
	}

	// Do nothing untill minute change
	if(seconds != 0) {
		delay(10);
		return;
	}
	
	// Minute changed
	update_clock();
	
	// Wait untill minute change check won't be triggered again
	delay(1000);
}