-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLEDManager.ino
More file actions
406 lines (335 loc) · 12.3 KB
/
LEDManager.ino
File metadata and controls
406 lines (335 loc) · 12.3 KB
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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
// led_manager.ino - LED Level Indication for Songbird
// Part of USB Audio Loopback Demo
//
// This module handles LED brightness control for audio level indication:
// - PWM-based smooth brightness control
// - Audio level to brightness mapping
// - State-dependent LED behavior
// - Smooth transitions and visual feedback
#include "config.h"
// =============================================================================
// LED CONTROL VARIABLES
// =============================================================================
// Current LED brightness values (0-255 PWM range)
int current_led1_brightness = 0; // Blue LED (input level indicator)
int current_led2_brightness = 0; // Pink LED (output level indicator)
// Target brightness values for smooth transitions
int target_led1_brightness = 0;
int target_led2_brightness = 0;
// LED update timing
unsigned long last_led_update = 0;
// LED system status
bool led_system_ready = false;
// Smoothing parameters for LED transitions
const float LED_SMOOTHING_FACTOR = 0.15f; // How quickly LEDs respond to changes (0.0-1.0)
// =============================================================================
// LED INITIALIZATION
// =============================================================================
/**
* Initialize the LED control system
* Sets up PWM pins and prepares LEDs for operation
* Must be called once during system startup
*/
void setupLEDs() {
Serial.println("Initializing Songbird LED system...");
// Configure LED pins as outputs
pinMode(LED_1_PIN, OUTPUT);
pinMode(LED_2_PIN, OUTPUT);
// Initialize LEDs to off state
analogWrite(LED_1_PIN, 0);
analogWrite(LED_2_PIN, 0);
// Reset brightness values
current_led1_brightness = 0;
current_led2_brightness = 0;
target_led1_brightness = 0;
target_led2_brightness = 0;
led_system_ready = true;
Serial.println("LED system initialized successfully");
// Perform startup LED test
testLEDs();
}
// =============================================================================
// LED BRIGHTNESS CONTROL
// =============================================================================
/**
* Update LED brightness based on current system state and audio levels
* Should be called regularly (10Hz) for smooth transitions
* @param system_state Current system state (STANDBY, ACTIVE, MUTED)
*/
void updateLEDs(SystemState system_state) {
// Check if it's time to update LEDs
if (millis() - last_led_update < LED_UPDATE_INTERVAL_MS) {
return; // Too soon for next update
}
if (!led_system_ready) {
return; // LED system not initialized
}
last_led_update = millis();
// Calculate target brightness based on system state
calculateTargetBrightness(system_state);
// Apply smoothing to brightness transitions
applySmoothTransition();
// Update physical LED outputs
updatePhysicalLEDs();
}
/**
* Calculate target LED brightness based on system state and audio levels
* @param system_state Current system state
*/
void calculateTargetBrightness(SystemState system_state) {
switch (system_state) {
case STATE_STANDBY:
// LEDs off in standby mode
target_led1_brightness = LED_BRIGHTNESS_OFF;
target_led2_brightness = LED_BRIGHTNESS_OFF;
break;
case STATE_ACTIVE:
// Both LEDs respond to audio levels in active mode
target_led1_brightness = mapLevelToBrightness(getInputLevel());
target_led2_brightness = mapLevelToBrightness(getOutputLevel());
break;
case STATE_MUTED:
// LED1 responds to input, LED2 is off (muted output)
target_led1_brightness = mapLevelToBrightness(getInputLevel());
target_led2_brightness = LED_BRIGHTNESS_OFF;
break;
}
}
/**
* Apply smooth transitions between current and target brightness
* Uses exponential smoothing for natural-looking transitions
*/
void applySmoothTransition() {
// Apply smoothing to LED1 (Blue - Input Level)
float led1_diff = target_led1_brightness - current_led1_brightness;
current_led1_brightness += (int)(led1_diff * LED_SMOOTHING_FACTOR);
// Apply smoothing to LED2 (Pink - Output Level)
float led2_diff = target_led2_brightness - current_led2_brightness;
current_led2_brightness += (int)(led2_diff * LED_SMOOTHING_FACTOR);
// Ensure values stay within valid PWM range
current_led1_brightness = constrain(current_led1_brightness, 0, 255);
current_led2_brightness = constrain(current_led2_brightness, 0, 255);
}
/**
* Update the physical LED outputs with current brightness values
*/
void updatePhysicalLEDs() {
analogWrite(LED_1_PIN, current_led1_brightness);
analogWrite(LED_2_PIN, current_led2_brightness);
}
// =============================================================================
// AUDIO LEVEL TO BRIGHTNESS MAPPING
// =============================================================================
/**
* Map audio level (0.0-1.0) to LED brightness (0-255)
* Uses logarithmic mapping to match human perception of audio levels
* @param level Audio level from 0.0 to 1.0
* @return LED brightness value from 0 to 255
*/
int mapLevelToBrightness(float level) {
// Ensure level is in valid range
level = constrain(level, 0.0f, 1.0f);
// Apply minimum threshold - very quiet audio doesn't light LEDs
if (level < LEVEL_THRESHOLD) {
return LED_BRIGHTNESS_OFF;
}
// Apply logarithmic scaling to match human perception
// Audio levels are perceived logarithmically, so we use a power function
float scaled_level = pow(level, 0.5f); // Square root gives good perceptual mapping
// Map to LED brightness range with minimum visible brightness
int brightness = (int)(scaled_level * (LED_BRIGHTNESS_MAX - LED_BRIGHTNESS_MIN) + LED_BRIGHTNESS_MIN);
// Ensure result is within valid PWM range
return constrain(brightness, LED_BRIGHTNESS_OFF, LED_BRIGHTNESS_MAX);
}
// =============================================================================
// LED CONTROL UTILITY FUNCTIONS
// =============================================================================
/**
* Set LED brightness directly (bypasses audio level mapping)
* Useful for testing and special effects
* @param led1_brightness Brightness for LED1 (0-255)
* @param led2_brightness Brightness for LED2 (0-255)
*/
void setLEDBrightness(int led1_brightness, int led2_brightness) {
if (!led_system_ready) return;
target_led1_brightness = constrain(led1_brightness, 0, 255);
target_led2_brightness = constrain(led2_brightness, 0, 255);
// Apply immediately without smoothing
current_led1_brightness = target_led1_brightness;
current_led2_brightness = target_led2_brightness;
updatePhysicalLEDs();
}
/**
* Turn off all LEDs immediately
*/
void turnOffLEDs() {
setLEDBrightness(0, 0);
}
/**
* Turn on all LEDs to maximum brightness
*/
void turnOnLEDs() {
setLEDBrightness(LED_BRIGHTNESS_MAX, LED_BRIGHTNESS_MAX);
}
/**
* Get current LED1 brightness
* @return current brightness value (0-255)
*/
int getLED1Brightness() {
return current_led1_brightness;
}
/**
* Get current LED2 brightness
* @return current brightness value (0-255)
*/
int getLED2Brightness() {
return current_led2_brightness;
}
/**
* Check if LED system is ready
* @return true if LED system initialized successfully
*/
bool isLEDSystemReady() {
return led_system_ready;
}
// =============================================================================
// LED TESTING AND DIAGNOSTICS
// =============================================================================
/**
* Test LED functionality
* Cycles through different brightness levels and patterns
*/
void testLEDs() {
if (!led_system_ready) return;
Serial.println("Testing LED functionality...");
// Test 1: Both LEDs fade up and down
Serial.println("LED Test 1: Fade up/down");
for (int brightness = 0; brightness <= 255; brightness += 5) {
setLEDBrightness(brightness, brightness);
delay(20);
}
for (int brightness = 255; brightness >= 0; brightness -= 5) {
setLEDBrightness(brightness, brightness);
delay(20);
}
// Test 2: Alternating LEDs
Serial.println("LED Test 2: Alternating pattern");
for (int i = 0; i < 5; i++) {
setLEDBrightness(255, 0);
delay(200);
setLEDBrightness(0, 255);
delay(200);
}
// Test 3: Different brightness levels
Serial.println("LED Test 3: Brightness levels");
int test_levels[] = {32, 64, 128, 192, 255};
for (int i = 0; i < 5; i++) {
setLEDBrightness(test_levels[i], test_levels[i]);
delay(300);
}
// Return to off state
turnOffLEDs();
Serial.println("LED test complete");
}
/**
* Demonstrate LED response to simulated audio levels
* Shows how LEDs respond to different audio input levels
*/
void demonstrateLEDResponse() {
if (!led_system_ready) return;
Serial.println("Demonstrating LED audio response...");
// Simulate various audio levels
float test_levels[] = {0.0f, 0.1f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f};
int num_levels = sizeof(test_levels) / sizeof(test_levels[0]);
for (int i = 0; i < num_levels; i++) {
float level = test_levels[i];
int brightness = mapLevelToBrightness(level);
Serial.print("Audio level ");
Serial.print(level, 2);
Serial.print(" -> LED brightness ");
Serial.println(brightness);
setLEDBrightness(brightness, brightness);
delay(1000);
}
turnOffLEDs();
Serial.println("LED response demonstration complete");
}
/**
* Create a breathing effect on LEDs
* Smooth fade in/out pattern for visual appeal
* @param duration_ms Duration of breathing effect in milliseconds
*/
void breathingEffect(unsigned long duration_ms) {
if (!led_system_ready) return;
unsigned long start_time = millis();
while (millis() - start_time < duration_ms) {
// Calculate breathing brightness using sine wave
float time_ratio = (float)(millis() - start_time) / duration_ms;
float breathing_level = (sin(time_ratio * 2 * PI * 2) + 1.0f) / 2.0f; // 2 cycles over duration
int brightness = (int)(breathing_level * LED_BRIGHTNESS_MAX);
setLEDBrightness(brightness, brightness);
delay(50); // 20 Hz update rate for smooth breathing
}
turnOffLEDs();
}
/**
* Display LED system diagnostics
* Shows current LED states and system information
*/
void printLEDDiagnostics() {
Serial.println("=== Songbird LED Diagnostics ===");
Serial.print("LED System Ready: "); Serial.println(led_system_ready ? "YES" : "NO");
Serial.println("\n--- Current LED States ---");
Serial.print("LED1 (Blue) - Current: "); Serial.print(current_led1_brightness);
Serial.print(" | Target: "); Serial.println(target_led1_brightness);
Serial.print("LED2 (Pink) - Current: "); Serial.print(current_led2_brightness);
Serial.print(" | Target: "); Serial.println(target_led2_brightness);
Serial.println("\n--- Audio Level Mapping ---");
Serial.print("Input Level: "); Serial.print(getInputLevel(), 3);
Serial.print(" -> Brightness: "); Serial.println(mapLevelToBrightness(getInputLevel()));
Serial.print("Output Level: "); Serial.print(getOutputLevel(), 3);
Serial.print(" -> Brightness: "); Serial.println(mapLevelToBrightness(getOutputLevel()));
Serial.println("==================================\n");
}
// =============================================================================
// SPECIAL LED EFFECTS
// =============================================================================
/**
* LED startup sequence
* Attractive power-on animation to show system is ready
*/
void ledStartupSequence() {
if (!led_system_ready) return;
Serial.println("LED startup sequence");
// Sequence 1: Quick flash both LEDs
setLEDBrightness(255, 255);
delay(100);
setLEDBrightness(0, 0);
delay(100);
// Sequence 2: Fade in both LEDs
for (int brightness = 0; brightness <= 128; brightness += 4) {
setLEDBrightness(brightness, brightness);
delay(30);
}
// Sequence 3: Fade out both LEDs
for (int brightness = 128; brightness >= 0; brightness -= 4) {
setLEDBrightness(brightness, brightness);
delay(30);
}
Serial.println("LED startup sequence complete");
}
/**
* LED error indication
* Distinctive pattern to indicate system errors
*/
void ledErrorIndication() {
if (!led_system_ready) return;
// Rapid alternating flash pattern
for (int i = 0; i < 6; i++) {
setLEDBrightness(255, 0);
delay(150);
setLEDBrightness(0, 255);
delay(150);
}
turnOffLEDs();
}