discstation 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +137 -0
- package/arduino/c6/DiscStation_C6.ino +914 -0
- package/arduino/v1/DiscStation.ino +957 -0
- package/discstation.env.example +19 -0
- package/docs/PLATFORM_SUPPORT.md +39 -0
- package/install-macos.sh +80 -0
- package/install-windows.ps1 +24 -0
- package/install.sh +56 -0
- package/package.json +48 -0
- package/requirements.txt +20 -0
- package/scripts/setup.mjs +78 -0
- package/src/discstation.py +3984 -0
- package/src/discstation_burn.py +1697 -0
- package/src/discstation_host.py +380 -0
- package/src/discstation_meta.py +150 -0
- package/src/static/app.js +268 -0
- package/src/static/index.html +111 -0
- package/src/static/style.css +328 -0
- package/systemd/discstation.service +13 -0
|
@@ -0,0 +1,957 @@
|
|
|
1
|
+
#include <Wire.h>
|
|
2
|
+
#include <Adafruit_GFX.h>
|
|
3
|
+
#include <Adafruit_SSD1306.h>
|
|
4
|
+
#include "esp_task_wdt.h"
|
|
5
|
+
#include <WiFi.h>
|
|
6
|
+
#include <ArduinoOTA.h>
|
|
7
|
+
#include <QRCode.h>
|
|
8
|
+
|
|
9
|
+
#define SCREEN_WIDTH 128
|
|
10
|
+
#define SCREEN_HEIGHT 64
|
|
11
|
+
#define OLED_RESET -1
|
|
12
|
+
#define I2C_ADDRESS 0x3C
|
|
13
|
+
|
|
14
|
+
#define BTN_SELECT_PIN 14
|
|
15
|
+
#define BTN_UP_PIN 13
|
|
16
|
+
#define BTN_DOWN_PIN 15
|
|
17
|
+
#define LED_POWER_PIN 32
|
|
18
|
+
#define LED_ACT_PIN 33
|
|
19
|
+
#define POT_PIN 34
|
|
20
|
+
|
|
21
|
+
#define DEBOUNCE_MS 50
|
|
22
|
+
#define LONG_PRESS_MS 1000
|
|
23
|
+
#define POT_READ_MS 200
|
|
24
|
+
#define DONE_RESET_MS 30000
|
|
25
|
+
#define STANDBY_BLANK_MS 60000
|
|
26
|
+
#define PING_TIMEOUT_MS 30000
|
|
27
|
+
#define LED_BLINK_MS 250
|
|
28
|
+
|
|
29
|
+
#define MAX_HOME_MODES 5
|
|
30
|
+
#define BURN_COUNT 4
|
|
31
|
+
#define SPEED_COUNT 4
|
|
32
|
+
#define TCP_PORT 2323
|
|
33
|
+
|
|
34
|
+
int homeCount = 3;
|
|
35
|
+
String homeModes[MAX_HOME_MODES] = {"BURN", "PLAY", "RIP"};
|
|
36
|
+
|
|
37
|
+
WiFiServer tcpServer(TCP_PORT);
|
|
38
|
+
WiFiClient tcpClient;
|
|
39
|
+
bool wifiConnected = false;
|
|
40
|
+
bool wifiInitDone = false;
|
|
41
|
+
|
|
42
|
+
class DualPrint : public Print {
|
|
43
|
+
public:
|
|
44
|
+
void setClient(WiFiClient* c) { _client = c; }
|
|
45
|
+
size_t write(uint8_t c) override {
|
|
46
|
+
Serial.write(c);
|
|
47
|
+
if (_client && _client->connected()) _client->write(c);
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
size_t write(const uint8_t* buf, size_t len) override {
|
|
51
|
+
Serial.write(buf, len);
|
|
52
|
+
if (_client && _client->connected()) _client->write(buf, len);
|
|
53
|
+
return len;
|
|
54
|
+
}
|
|
55
|
+
private:
|
|
56
|
+
WiFiClient* _client = nullptr;
|
|
57
|
+
} Out;
|
|
58
|
+
|
|
59
|
+
void initWiFi() {
|
|
60
|
+
Out.print("WiFi Tez...");
|
|
61
|
+
WiFi.mode(WIFI_STA);
|
|
62
|
+
WiFi.begin("Tez", "Dellwin8#$");
|
|
63
|
+
for (int i = 0; i < 80; i++) {
|
|
64
|
+
if (WiFi.status() == WL_CONNECTED) break;
|
|
65
|
+
delay(250);
|
|
66
|
+
}
|
|
67
|
+
if (WiFi.status() == WL_CONNECTED) {
|
|
68
|
+
wifiConnected = true;
|
|
69
|
+
tcpServer.begin();
|
|
70
|
+
ArduinoOTA.begin();
|
|
71
|
+
ArduinoOTA.setHostname("discstation-v1");
|
|
72
|
+
ArduinoOTA.setPassword("dvdstation");
|
|
73
|
+
Out.print(" OK "); Out.println(WiFi.localIP());
|
|
74
|
+
} else {
|
|
75
|
+
Out.println(" fail");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
String discName = "";
|
|
79
|
+
|
|
80
|
+
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
|
81
|
+
|
|
82
|
+
String ipUrl = "";
|
|
83
|
+
|
|
84
|
+
enum UiState {
|
|
85
|
+
UI_STATUS,
|
|
86
|
+
UI_HOME,
|
|
87
|
+
UI_BURN_READY,
|
|
88
|
+
UI_PLAY,
|
|
89
|
+
UI_STANDBY,
|
|
90
|
+
UI_WAITING,
|
|
91
|
+
UI_IP,
|
|
92
|
+
UI_DISCONNECTED
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const char* BURN_MODES[BURN_COUNT] = {"AUTO", "BEST", "LONG", "TEST"};
|
|
96
|
+
const char* SPEED_MODES[SPEED_COUNT] = {"Auto", "6x", "4x", "3x"};
|
|
97
|
+
|
|
98
|
+
UiState uiState = UI_STATUS;
|
|
99
|
+
|
|
100
|
+
String titleLine = "";
|
|
101
|
+
String metaLine = "";
|
|
102
|
+
String fitLine = "";
|
|
103
|
+
String discLine = "Disc: none";
|
|
104
|
+
String line1 = "Status: READY";
|
|
105
|
+
String line2 = "";
|
|
106
|
+
String line3 = "";
|
|
107
|
+
String waitingLine1 = "";
|
|
108
|
+
String waitingLine2 = "";
|
|
109
|
+
int progressPercent = -1;
|
|
110
|
+
|
|
111
|
+
bool displayOk = false;
|
|
112
|
+
unsigned long returnToHomeAt = 0;
|
|
113
|
+
unsigned long playStatusAt = 0;
|
|
114
|
+
bool playStatusTemp = false;
|
|
115
|
+
unsigned long lastPotRead = 0;
|
|
116
|
+
|
|
117
|
+
// Select button state
|
|
118
|
+
unsigned long selectDownAt = 0;
|
|
119
|
+
bool selectDown = false;
|
|
120
|
+
unsigned long selectLastDebounce = 0;
|
|
121
|
+
|
|
122
|
+
// Up button state
|
|
123
|
+
unsigned long upDownAt = 0;
|
|
124
|
+
bool upDown = false;
|
|
125
|
+
unsigned long upLastDebounce = 0;
|
|
126
|
+
|
|
127
|
+
// Down button state
|
|
128
|
+
unsigned long downDownAt = 0;
|
|
129
|
+
bool downDown = false;
|
|
130
|
+
unsigned long downLastDebounce = 0;
|
|
131
|
+
|
|
132
|
+
// FF/REW speed state (used by UP/DOWN during playback)
|
|
133
|
+
int ffSpeedIndex = 0;
|
|
134
|
+
int rewSpeedIndex = 0;
|
|
135
|
+
|
|
136
|
+
// LED state
|
|
137
|
+
bool actLedState = false;
|
|
138
|
+
unsigned long lastLedBlink = 0;
|
|
139
|
+
int homeIndex = 0;
|
|
140
|
+
int burnModeIndex = 0;
|
|
141
|
+
int burnSpeedIndex = 0;
|
|
142
|
+
bool editingSpeed = false;
|
|
143
|
+
int playVolume = 50;
|
|
144
|
+
unsigned long standbyStartTime = 0;
|
|
145
|
+
unsigned long lastMsgTime = 0;
|
|
146
|
+
bool displayBlank = false;
|
|
147
|
+
bool audioPlayMode = false;
|
|
148
|
+
int displayRotation = 0;
|
|
149
|
+
|
|
150
|
+
// Indeterminate progress animation
|
|
151
|
+
int indeterminateCount = 0;
|
|
152
|
+
unsigned long lastIndeterminateAnim = 0;
|
|
153
|
+
|
|
154
|
+
int readBucket(int count) {
|
|
155
|
+
int raw = analogRead(POT_PIN);
|
|
156
|
+
int idx = raw * count / 4096;
|
|
157
|
+
if (idx < 0) idx = 0;
|
|
158
|
+
if (idx >= count) idx = count - 1;
|
|
159
|
+
return idx;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
int readPercent() {
|
|
163
|
+
int raw = analogRead(POT_PIN);
|
|
164
|
+
int pct = raw * 100 / 4095;
|
|
165
|
+
if (pct < 0) pct = 0;
|
|
166
|
+
if (pct > 100) pct = 100;
|
|
167
|
+
return ((pct + 2) / 5) * 5;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
void sendHomeMode() {
|
|
171
|
+
if (homeIndex >= 0 && homeIndex < homeCount) {
|
|
172
|
+
Out.print("MENU:");
|
|
173
|
+
Out.println(homeModes[homeIndex]);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
void sendBurnMode() {
|
|
178
|
+
Out.print("MODE:");
|
|
179
|
+
Out.println(BURN_MODES[burnModeIndex]);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
void printUpper(String value) {
|
|
183
|
+
value.toUpperCase();
|
|
184
|
+
display.print(value);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
void drawChrome() {
|
|
188
|
+
display.setRotation(displayRotation);
|
|
189
|
+
display.setTextSize(1);
|
|
190
|
+
display.setTextColor(SSD1306_WHITE);
|
|
191
|
+
display.drawLine(0, 0, 4, 0, SSD1306_WHITE);
|
|
192
|
+
display.drawLine(0, 0, 0, 4, SSD1306_WHITE);
|
|
193
|
+
display.drawLine(123, 0, 127, 0, SSD1306_WHITE);
|
|
194
|
+
display.drawLine(127, 0, 127, 4, SSD1306_WHITE);
|
|
195
|
+
display.drawLine(0, 63, 4, 63, SSD1306_WHITE);
|
|
196
|
+
display.drawLine(0, 59, 0, 63, SSD1306_WHITE);
|
|
197
|
+
display.drawLine(123, 63, 127, 63, SSD1306_WHITE);
|
|
198
|
+
display.drawLine(127, 59, 127, 63, SSD1306_WHITE);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
void drawHeader() {
|
|
202
|
+
display.setTextSize(1);
|
|
203
|
+
display.setTextColor(SSD1306_WHITE);
|
|
204
|
+
drawChrome();
|
|
205
|
+
display.setCursor(4, 0);
|
|
206
|
+
display.print("DISCSTATION // PANEL");
|
|
207
|
+
display.drawLine(6, 10, 121, 10, SSD1306_WHITE);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
void drawHome() {
|
|
211
|
+
uiState = UI_HOME;
|
|
212
|
+
returnToHomeAt = 0;
|
|
213
|
+
if (!displayOk) return;
|
|
214
|
+
|
|
215
|
+
display.clearDisplay();
|
|
216
|
+
drawHeader();
|
|
217
|
+
|
|
218
|
+
int y = 14;
|
|
219
|
+
if (discName.length() > 0) {
|
|
220
|
+
display.setTextSize(1);
|
|
221
|
+
display.setCursor(2, y);
|
|
222
|
+
printUpper(discName);
|
|
223
|
+
y += 10;
|
|
224
|
+
} else {
|
|
225
|
+
display.setCursor(2, y);
|
|
226
|
+
printUpper(discLine);
|
|
227
|
+
y += 10;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
for (int i = 0; i < homeCount; i++) {
|
|
231
|
+
display.setCursor(2, y);
|
|
232
|
+
display.print(i == homeIndex ? ">" : " ");
|
|
233
|
+
display.setCursor(14, y);
|
|
234
|
+
printUpper(homeModes[i]);
|
|
235
|
+
y += 10;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
display.display();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
void drawBurnReady() {
|
|
242
|
+
uiState = UI_BURN_READY;
|
|
243
|
+
returnToHomeAt = 0;
|
|
244
|
+
if (!displayOk) return;
|
|
245
|
+
|
|
246
|
+
display.clearDisplay();
|
|
247
|
+
drawHeader();
|
|
248
|
+
|
|
249
|
+
display.setCursor(2, 14);
|
|
250
|
+
printUpper(titleLine);
|
|
251
|
+
|
|
252
|
+
display.setCursor(2, 25);
|
|
253
|
+
printUpper(metaLine);
|
|
254
|
+
|
|
255
|
+
display.setCursor(2, 37);
|
|
256
|
+
printUpper(fitLine);
|
|
257
|
+
|
|
258
|
+
display.setCursor(2, 49);
|
|
259
|
+
display.print(editingSpeed ? ' ' : '>');
|
|
260
|
+
display.setCursor(14, 49);
|
|
261
|
+
display.print(BURN_MODES[burnModeIndex]);
|
|
262
|
+
|
|
263
|
+
display.setCursor(62, 49);
|
|
264
|
+
display.print(editingSpeed ? '>' : ' ');
|
|
265
|
+
display.setCursor(74, 49);
|
|
266
|
+
display.print(SPEED_MODES[burnSpeedIndex]);
|
|
267
|
+
|
|
268
|
+
display.setCursor(2, 57);
|
|
269
|
+
display.print("HOLD SELECT // GO");
|
|
270
|
+
|
|
271
|
+
display.display();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
void drawWaiting() {
|
|
275
|
+
uiState = UI_WAITING;
|
|
276
|
+
returnToHomeAt = 0;
|
|
277
|
+
if (!displayOk) return;
|
|
278
|
+
|
|
279
|
+
display.clearDisplay();
|
|
280
|
+
drawHeader();
|
|
281
|
+
|
|
282
|
+
display.setCursor(2, 16);
|
|
283
|
+
printUpper(waitingLine1);
|
|
284
|
+
|
|
285
|
+
display.setCursor(2, 30);
|
|
286
|
+
printUpper(waitingLine2);
|
|
287
|
+
|
|
288
|
+
display.setCursor(2, 49);
|
|
289
|
+
display.print("SHORT // GO");
|
|
290
|
+
|
|
291
|
+
display.display();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
void drawStatus() {
|
|
295
|
+
uiState = UI_STATUS;
|
|
296
|
+
if (!displayOk) return;
|
|
297
|
+
|
|
298
|
+
display.clearDisplay();
|
|
299
|
+
drawHeader();
|
|
300
|
+
|
|
301
|
+
display.setCursor(2, 16);
|
|
302
|
+
printUpper(line1);
|
|
303
|
+
display.setCursor(2, 30);
|
|
304
|
+
printUpper(line2);
|
|
305
|
+
display.setCursor(2, 44);
|
|
306
|
+
printUpper(line3);
|
|
307
|
+
|
|
308
|
+
if (progressPercent >= 0) {
|
|
309
|
+
int barW = map(progressPercent, 0, 100, 0, 124);
|
|
310
|
+
display.drawRect(2, 54, 124, 8, SSD1306_WHITE);
|
|
311
|
+
if (barW > 0) {
|
|
312
|
+
display.fillRect(2, 54, barW, 8, SSD1306_WHITE);
|
|
313
|
+
}
|
|
314
|
+
} else {
|
|
315
|
+
// Indeterminate progress — draw cycling dots
|
|
316
|
+
String dots = "";
|
|
317
|
+
for (int i = 0; i < indeterminateCount; i++) dots += ".";
|
|
318
|
+
display.setCursor(2, 56);
|
|
319
|
+
display.print(dots);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
display.display();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
void drawQrCode(String value) {
|
|
326
|
+
drawChrome();
|
|
327
|
+
QRCode qr;
|
|
328
|
+
uint8_t qrData[qrcode_getBufferSize(2)];
|
|
329
|
+
if (qrcode_initText(&qr, qrData, 2, ECC_LOW, value.c_str()) < 0) {
|
|
330
|
+
display.setCursor(2, 25);
|
|
331
|
+
display.print("QR ERROR // USE URL");
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const int scale = 2;
|
|
335
|
+
const int quiet = 2;
|
|
336
|
+
const int pixels = qr.size * scale;
|
|
337
|
+
const int left = (SCREEN_WIDTH - pixels) / 2;
|
|
338
|
+
const int top = 2;
|
|
339
|
+
display.fillRect(left - quiet, top - quiet, pixels + quiet * 2, pixels + quiet * 2, SSD1306_WHITE);
|
|
340
|
+
for (uint8_t y = 0; y < qr.size; y++) {
|
|
341
|
+
for (uint8_t x = 0; x < qr.size; x++) {
|
|
342
|
+
if (qrcode_getModule(&qr, x, y)) {
|
|
343
|
+
display.fillRect(left + x * scale, top + y * scale, scale, scale, SSD1306_BLACK);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
display.setCursor(2, 57);
|
|
348
|
+
display.print("SCAN // DISCSTATION");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
void drawIP() {
|
|
352
|
+
uiState = UI_IP;
|
|
353
|
+
returnToHomeAt = 0;
|
|
354
|
+
if (!displayOk) return;
|
|
355
|
+
|
|
356
|
+
display.clearDisplay();
|
|
357
|
+
drawQrCode(ipUrl);
|
|
358
|
+
|
|
359
|
+
display.display();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
void drawStandby() {
|
|
363
|
+
uiState = UI_STANDBY;
|
|
364
|
+
returnToHomeAt = 0;
|
|
365
|
+
standbyStartTime = millis();
|
|
366
|
+
displayBlank = false;
|
|
367
|
+
if (!displayOk) return;
|
|
368
|
+
|
|
369
|
+
display.clearDisplay();
|
|
370
|
+
drawHeader();
|
|
371
|
+
display.setCursor(2, 25);
|
|
372
|
+
display.print("STANDBY // READY");
|
|
373
|
+
display.setCursor(2, 40);
|
|
374
|
+
display.print("INSERT MEDIA");
|
|
375
|
+
display.display();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
void drawDisconnected() {
|
|
379
|
+
uiState = UI_DISCONNECTED;
|
|
380
|
+
returnToHomeAt = 0;
|
|
381
|
+
displayBlank = false;
|
|
382
|
+
if (!displayOk) return;
|
|
383
|
+
|
|
384
|
+
display.clearDisplay();
|
|
385
|
+
drawHeader();
|
|
386
|
+
display.setCursor(2, 25);
|
|
387
|
+
display.print("DISCONNECTED // LINK");
|
|
388
|
+
display.setCursor(2, 40);
|
|
389
|
+
display.print("CHECK USB");
|
|
390
|
+
display.display();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
void wakeDisplay() {
|
|
394
|
+
if (!displayBlank || !displayOk) return;
|
|
395
|
+
display.ssd1306_command(0xAF);
|
|
396
|
+
displayBlank = false;
|
|
397
|
+
switch (uiState) {
|
|
398
|
+
case UI_HOME: drawHome(); break;
|
|
399
|
+
case UI_STATUS: drawStatus(); break;
|
|
400
|
+
case UI_IP: drawIP(); break;
|
|
401
|
+
case UI_BURN_READY: drawBurnReady(); break;
|
|
402
|
+
case UI_PLAY: drawPlay(); break;
|
|
403
|
+
case UI_WAITING: drawWaiting(); break;
|
|
404
|
+
case UI_STANDBY: drawStandby(); break;
|
|
405
|
+
case UI_DISCONNECTED: drawDisconnected(); break;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
void drawPlay() {
|
|
410
|
+
uiState = UI_PLAY;
|
|
411
|
+
returnToHomeAt = 0;
|
|
412
|
+
if (!displayOk) return;
|
|
413
|
+
|
|
414
|
+
display.clearDisplay();
|
|
415
|
+
drawHeader();
|
|
416
|
+
|
|
417
|
+
display.setCursor(2, 16);
|
|
418
|
+
printUpper(line1);
|
|
419
|
+
display.setCursor(2, 30);
|
|
420
|
+
printUpper(line2);
|
|
421
|
+
display.setCursor(2, 44);
|
|
422
|
+
display.print("VOL // ");
|
|
423
|
+
display.print(playVolume);
|
|
424
|
+
display.print("%");
|
|
425
|
+
if (!audioPlayMode) {
|
|
426
|
+
display.setCursor(2, 56);
|
|
427
|
+
display.print("SELECT // PLAY");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
display.display();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
void parseMessage(String msg) {
|
|
434
|
+
msg.trim();
|
|
435
|
+
|
|
436
|
+
if (msg == "PING") {
|
|
437
|
+
lastMsgTime = millis();
|
|
438
|
+
Out.println("PONG");
|
|
439
|
+
if (uiState == UI_DISCONNECTED) drawStandby();
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
wakeDisplay();
|
|
444
|
+
lastMsgTime = millis();
|
|
445
|
+
|
|
446
|
+
Out.print("RCV:");
|
|
447
|
+
Out.println(msg);
|
|
448
|
+
|
|
449
|
+
if (msg.startsWith("HOME:")) {
|
|
450
|
+
homeIndex = readBucket(homeCount);
|
|
451
|
+
drawHome();
|
|
452
|
+
|
|
453
|
+
} else if (msg.startsWith("DISC:")) {
|
|
454
|
+
discLine = msg.substring(5);
|
|
455
|
+
if (discLine.length() > 20) discLine = discLine.substring(0, 20);
|
|
456
|
+
if (uiState == UI_HOME) drawHome();
|
|
457
|
+
|
|
458
|
+
} else if (msg.startsWith("DISC_NAME:")) {
|
|
459
|
+
discName = msg.substring(10);
|
|
460
|
+
if (discName.length() > 18) discName = discName.substring(0, 18);
|
|
461
|
+
if (uiState == UI_HOME) drawHome();
|
|
462
|
+
|
|
463
|
+
} else if (msg.startsWith("MENU_ITEMS:")) {
|
|
464
|
+
String items = msg.substring(11);
|
|
465
|
+
homeCount = 0;
|
|
466
|
+
while (items.length() > 0 && homeCount < MAX_HOME_MODES) {
|
|
467
|
+
int comma = items.indexOf(',');
|
|
468
|
+
if (comma < 0) {
|
|
469
|
+
homeModes[homeCount++] = items;
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
homeModes[homeCount++] = items.substring(0, comma);
|
|
473
|
+
items = items.substring(comma + 1);
|
|
474
|
+
}
|
|
475
|
+
if (homeIndex >= homeCount) homeIndex = 0;
|
|
476
|
+
if (uiState == UI_HOME) drawHome();
|
|
477
|
+
|
|
478
|
+
} else if (msg.startsWith("STANDBY:")) {
|
|
479
|
+
drawStandby();
|
|
480
|
+
|
|
481
|
+
} else if (msg.startsWith("TITLE:")) {
|
|
482
|
+
titleLine = msg.substring(6);
|
|
483
|
+
if (titleLine.length() > 20) titleLine = titleLine.substring(0, 20);
|
|
484
|
+
burnModeIndex = readBucket(BURN_COUNT);
|
|
485
|
+
drawBurnReady();
|
|
486
|
+
|
|
487
|
+
} else if (msg.startsWith("META:")) {
|
|
488
|
+
metaLine = msg.substring(5);
|
|
489
|
+
if (metaLine.length() > 20) metaLine = metaLine.substring(0, 20);
|
|
490
|
+
drawBurnReady();
|
|
491
|
+
|
|
492
|
+
} else if (msg.startsWith("FIT:")) {
|
|
493
|
+
fitLine = msg.substring(4);
|
|
494
|
+
if (fitLine.length() > 20) fitLine = fitLine.substring(0, 20);
|
|
495
|
+
drawBurnReady();
|
|
496
|
+
|
|
497
|
+
} else if (msg.startsWith("PLAY:")) {
|
|
498
|
+
line1 = msg.substring(5);
|
|
499
|
+
line2 = "Playing disc";
|
|
500
|
+
playVolume = readPercent();
|
|
501
|
+
drawPlay();
|
|
502
|
+
|
|
503
|
+
} else if (msg.startsWith("PLAY_MODE:")) {
|
|
504
|
+
audioPlayMode = msg.substring(10) == "AUDIO_CD";
|
|
505
|
+
drawPlay();
|
|
506
|
+
|
|
507
|
+
} else if (msg.startsWith("PLAY_STATUS:")) {
|
|
508
|
+
line1 = msg.substring(12);
|
|
509
|
+
if (line1.length() > 20) line1 = line1.substring(0, 20);
|
|
510
|
+
if (line1 == "PLAYING" || line1 == "PAUSED" || (audioPlayMode && line1.startsWith("TRACK "))) {
|
|
511
|
+
playStatusTemp = false;
|
|
512
|
+
} else {
|
|
513
|
+
playStatusAt = millis();
|
|
514
|
+
playStatusTemp = true;
|
|
515
|
+
}
|
|
516
|
+
drawPlay();
|
|
517
|
+
|
|
518
|
+
} else if (msg.startsWith("WAITING:")) {
|
|
519
|
+
returnToHomeAt = 0;
|
|
520
|
+
String rest = msg.substring(8);
|
|
521
|
+
int slash = rest.indexOf('/');
|
|
522
|
+
if (slash >= 0) {
|
|
523
|
+
waitingLine1 = "Data: " + rest.substring(0, slash);
|
|
524
|
+
waitingLine1.trim();
|
|
525
|
+
waitingLine2 = "Disc: " + rest.substring(slash + 1);
|
|
526
|
+
waitingLine2.trim();
|
|
527
|
+
} else {
|
|
528
|
+
waitingLine1 = rest;
|
|
529
|
+
waitingLine2 = "";
|
|
530
|
+
}
|
|
531
|
+
drawWaiting();
|
|
532
|
+
|
|
533
|
+
} else if (msg.startsWith("STATUS:")) {
|
|
534
|
+
returnToHomeAt = 0;
|
|
535
|
+
line1 = msg.substring(7);
|
|
536
|
+
line2 = "";
|
|
537
|
+
line3 = "";
|
|
538
|
+
progressPercent = -1;
|
|
539
|
+
drawStatus();
|
|
540
|
+
|
|
541
|
+
} else if (msg.startsWith("PROGRESS:")) {
|
|
542
|
+
returnToHomeAt = 0;
|
|
543
|
+
line2 = msg.substring(9);
|
|
544
|
+
{
|
|
545
|
+
String rest = msg.substring(9);
|
|
546
|
+
int pct = rest.indexOf('%');
|
|
547
|
+
if (pct > 0) {
|
|
548
|
+
String num = rest.substring(0, pct);
|
|
549
|
+
num.trim();
|
|
550
|
+
int val = num.toInt();
|
|
551
|
+
if (val >= 0 && val <= 100) {
|
|
552
|
+
progressPercent = val;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
drawStatus();
|
|
557
|
+
|
|
558
|
+
} else if (msg.startsWith("INFO:")) {
|
|
559
|
+
returnToHomeAt = 0;
|
|
560
|
+
line3 = msg.substring(5);
|
|
561
|
+
drawStatus();
|
|
562
|
+
|
|
563
|
+
} else if (msg.startsWith("DONE:")) {
|
|
564
|
+
line1 = "DONE!";
|
|
565
|
+
line2 = msg.substring(5);
|
|
566
|
+
line3 = "Returning home";
|
|
567
|
+
returnToHomeAt = millis() + DONE_RESET_MS;
|
|
568
|
+
progressPercent = -1;
|
|
569
|
+
drawStatus();
|
|
570
|
+
|
|
571
|
+
} else if (msg.startsWith("CANCELLED:")) {
|
|
572
|
+
returnToHomeAt = 0;
|
|
573
|
+
line1 = "CANCELLED";
|
|
574
|
+
line2 = msg.substring(10);
|
|
575
|
+
line3 = "Use menu";
|
|
576
|
+
progressPercent = -1;
|
|
577
|
+
drawStatus();
|
|
578
|
+
|
|
579
|
+
} else if (msg.startsWith("WARNING:")) {
|
|
580
|
+
returnToHomeAt = 0;
|
|
581
|
+
line1 = "!! WARNING !!";
|
|
582
|
+
line2 = msg.substring(8);
|
|
583
|
+
if (line2.length() > 20) line2 = line2.substring(0, 20);
|
|
584
|
+
line3 = "";
|
|
585
|
+
drawStatus();
|
|
586
|
+
|
|
587
|
+
} else if (msg.startsWith("ERROR:")) {
|
|
588
|
+
returnToHomeAt = 0;
|
|
589
|
+
line1 = "!! ERROR";
|
|
590
|
+
line2 = msg.substring(6);
|
|
591
|
+
line3 = "";
|
|
592
|
+
progressPercent = -1;
|
|
593
|
+
drawStatus();
|
|
594
|
+
|
|
595
|
+
} else if (msg.startsWith("IP:")) {
|
|
596
|
+
ipUrl = msg.substring(3);
|
|
597
|
+
drawIP();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
void setup() {
|
|
602
|
+
Serial.begin(115200);
|
|
603
|
+
esp_task_wdt_add(NULL);
|
|
604
|
+
Wire.begin(21, 22);
|
|
605
|
+
pinMode(BTN_SELECT_PIN, INPUT_PULLUP);
|
|
606
|
+
pinMode(BTN_UP_PIN, INPUT_PULLUP);
|
|
607
|
+
pinMode(BTN_DOWN_PIN, INPUT_PULLUP);
|
|
608
|
+
pinMode(LED_POWER_PIN, OUTPUT);
|
|
609
|
+
pinMode(LED_ACT_PIN, OUTPUT);
|
|
610
|
+
digitalWrite(LED_POWER_PIN, HIGH);
|
|
611
|
+
digitalWrite(LED_ACT_PIN, LOW);
|
|
612
|
+
pinMode(POT_PIN, INPUT);
|
|
613
|
+
analogReadResolution(12);
|
|
614
|
+
analogSetPinAttenuation(POT_PIN, ADC_11db);
|
|
615
|
+
|
|
616
|
+
homeIndex = readBucket(homeCount);
|
|
617
|
+
burnModeIndex = readBucket(BURN_COUNT);
|
|
618
|
+
playVolume = readPercent();
|
|
619
|
+
|
|
620
|
+
displayOk = display.begin(SSD1306_SWITCHCAPVCC, I2C_ADDRESS);
|
|
621
|
+
|
|
622
|
+
if (displayOk) {
|
|
623
|
+
Out.println("Display OK");
|
|
624
|
+
} else {
|
|
625
|
+
Out.println("Display FAILED");
|
|
626
|
+
delay(3000);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Blink activity LED 3x to confirm it works
|
|
630
|
+
for (int i = 0; i < 3; i++) {
|
|
631
|
+
digitalWrite(LED_ACT_PIN, HIGH);
|
|
632
|
+
delay(150);
|
|
633
|
+
digitalWrite(LED_ACT_PIN, LOW);
|
|
634
|
+
delay(150);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
drawStandby();
|
|
638
|
+
lastMsgTime = millis();
|
|
639
|
+
Out.println("DISCSTATION_READY");
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
void handleSelectPress(bool longPress) {
|
|
643
|
+
wakeDisplay();
|
|
644
|
+
if (uiState == UI_HOME) {
|
|
645
|
+
if (longPress) {
|
|
646
|
+
Out.println("EJECT");
|
|
647
|
+
line1 = "Ejecting...";
|
|
648
|
+
line2 = "";
|
|
649
|
+
line3 = "";
|
|
650
|
+
returnToHomeAt = millis() + 5000;
|
|
651
|
+
drawStatus();
|
|
652
|
+
} else if (homeIndex >= 0 && homeIndex < homeCount) {
|
|
653
|
+
Out.print("SELECT:");
|
|
654
|
+
Out.println(homeModes[homeIndex]);
|
|
655
|
+
line1 = "Selected";
|
|
656
|
+
line2 = homeModes[homeIndex];
|
|
657
|
+
line3 = "";
|
|
658
|
+
drawStatus();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
} else if (uiState == UI_STANDBY) {
|
|
662
|
+
Out.println("EJECT");
|
|
663
|
+
line1 = "Ejecting...";
|
|
664
|
+
line2 = "";
|
|
665
|
+
line3 = "";
|
|
666
|
+
returnToHomeAt = millis() + 5000;
|
|
667
|
+
drawStatus();
|
|
668
|
+
|
|
669
|
+
} else if (uiState == UI_STATUS && line1 == "Ejecting...") {
|
|
670
|
+
// Manual escape if stuck on eject screen with no app response
|
|
671
|
+
drawStandby();
|
|
672
|
+
|
|
673
|
+
} else if (uiState == UI_IP) {
|
|
674
|
+
if (longPress) {
|
|
675
|
+
Out.println("CANCEL");
|
|
676
|
+
drawHome();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
} else if (uiState == UI_WAITING) {
|
|
680
|
+
if (longPress) {
|
|
681
|
+
Out.println("CANCEL");
|
|
682
|
+
drawHome();
|
|
683
|
+
} else {
|
|
684
|
+
Out.println("CONFIRM");
|
|
685
|
+
line1 = "Confirmed!";
|
|
686
|
+
line2 = "";
|
|
687
|
+
line3 = "";
|
|
688
|
+
drawStatus();
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
} else if (uiState == UI_BURN_READY) {
|
|
692
|
+
if (longPress) {
|
|
693
|
+
Out.print("START:");
|
|
694
|
+
Out.println(BURN_MODES[burnModeIndex]);
|
|
695
|
+
line1 = "Starting...";
|
|
696
|
+
line2 = BURN_MODES[burnModeIndex];
|
|
697
|
+
line3 = "";
|
|
698
|
+
drawStatus();
|
|
699
|
+
} else {
|
|
700
|
+
editingSpeed = !editingSpeed;
|
|
701
|
+
drawBurnReady();
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
} else if (uiState == UI_STATUS) {
|
|
705
|
+
if (longPress) {
|
|
706
|
+
Out.println("CANCEL");
|
|
707
|
+
line1 = "Cancelling...";
|
|
708
|
+
line2 = "";
|
|
709
|
+
line3 = "";
|
|
710
|
+
drawStatus();
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
} else if (uiState == UI_DISCONNECTED) {
|
|
714
|
+
Out.println("CANCEL");
|
|
715
|
+
drawStandby();
|
|
716
|
+
|
|
717
|
+
} else if (uiState == UI_STANDBY) {
|
|
718
|
+
displayRotation = 2;
|
|
719
|
+
drawStandby();
|
|
720
|
+
} else if (uiState == UI_PLAY) {
|
|
721
|
+
ffSpeedIndex = 0;
|
|
722
|
+
rewSpeedIndex = 0;
|
|
723
|
+
if (longPress) {
|
|
724
|
+
Out.println("PLAY_STOP");
|
|
725
|
+
} else {
|
|
726
|
+
Out.println("PLAY_BUTTON");
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
void handleUp(bool longPress) {
|
|
732
|
+
wakeDisplay();
|
|
733
|
+
if (uiState == UI_HOME) {
|
|
734
|
+
if (homeCount > 0) {
|
|
735
|
+
homeIndex = (homeIndex - 1 + homeCount) % homeCount;
|
|
736
|
+
sendHomeMode();
|
|
737
|
+
drawHome();
|
|
738
|
+
}
|
|
739
|
+
} else if (uiState == UI_BURN_READY) {
|
|
740
|
+
if (editingSpeed) {
|
|
741
|
+
burnSpeedIndex = (burnSpeedIndex - 1 + SPEED_COUNT) % SPEED_COUNT;
|
|
742
|
+
Out.print("SPEED:");
|
|
743
|
+
Out.println(SPEED_MODES[burnSpeedIndex]);
|
|
744
|
+
drawBurnReady();
|
|
745
|
+
} else {
|
|
746
|
+
burnModeIndex = (burnModeIndex - 1 + BURN_COUNT) % BURN_COUNT;
|
|
747
|
+
sendBurnMode();
|
|
748
|
+
drawBurnReady();
|
|
749
|
+
}
|
|
750
|
+
} else if (uiState == UI_STANDBY) {
|
|
751
|
+
displayRotation = 0;
|
|
752
|
+
drawStandby();
|
|
753
|
+
} else if (uiState == UI_PLAY) {
|
|
754
|
+
if (audioPlayMode) {
|
|
755
|
+
Out.println(displayRotation == 0 ? (longPress ? "REW:BIG" : "REW:10") : (longPress ? "FF:BIG" : "FF:10"));
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
rewSpeedIndex = 0;
|
|
759
|
+
if (longPress) {
|
|
760
|
+
Out.println("FF:BIG");
|
|
761
|
+
} else {
|
|
762
|
+
ffSpeedIndex = (ffSpeedIndex + 1) % 4;
|
|
763
|
+
int seek_sec[] = {10, 20, 30, 60};
|
|
764
|
+
Out.print("FF:");
|
|
765
|
+
Out.println(seek_sec[ffSpeedIndex]);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
void handleDown(bool longPress) {
|
|
771
|
+
wakeDisplay();
|
|
772
|
+
if (uiState == UI_HOME) {
|
|
773
|
+
if (homeCount > 0) {
|
|
774
|
+
homeIndex = (homeIndex + 1) % homeCount;
|
|
775
|
+
sendHomeMode();
|
|
776
|
+
drawHome();
|
|
777
|
+
}
|
|
778
|
+
} else if (uiState == UI_BURN_READY) {
|
|
779
|
+
if (editingSpeed) {
|
|
780
|
+
burnSpeedIndex = (burnSpeedIndex + 1) % SPEED_COUNT;
|
|
781
|
+
Out.print("SPEED:");
|
|
782
|
+
Out.println(SPEED_MODES[burnSpeedIndex]);
|
|
783
|
+
drawBurnReady();
|
|
784
|
+
} else {
|
|
785
|
+
burnModeIndex = (burnModeIndex + 1) % BURN_COUNT;
|
|
786
|
+
sendBurnMode();
|
|
787
|
+
drawBurnReady();
|
|
788
|
+
}
|
|
789
|
+
} else if (uiState == UI_STANDBY) {
|
|
790
|
+
displayRotation = 2;
|
|
791
|
+
drawStandby();
|
|
792
|
+
} else if (uiState == UI_PLAY) {
|
|
793
|
+
if (audioPlayMode) {
|
|
794
|
+
Out.println(displayRotation == 0 ? (longPress ? "FF:BIG" : "FF:10") : (longPress ? "REW:BIG" : "REW:10"));
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
ffSpeedIndex = 0;
|
|
798
|
+
if (longPress) {
|
|
799
|
+
Out.println("REW:BIG");
|
|
800
|
+
} else {
|
|
801
|
+
rewSpeedIndex = (rewSpeedIndex + 1) % 4;
|
|
802
|
+
int seek_sec[] = {10, 20, 30, 60};
|
|
803
|
+
Out.print("REW:");
|
|
804
|
+
Out.println(seek_sec[rewSpeedIndex]);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
void loop() {
|
|
810
|
+
if (!wifiInitDone && millis() > 3000) {
|
|
811
|
+
wifiInitDone = true;
|
|
812
|
+
initWiFi();
|
|
813
|
+
}
|
|
814
|
+
if (wifiConnected) {
|
|
815
|
+
ArduinoOTA.handle();
|
|
816
|
+
if (tcpServer.hasClient()) {
|
|
817
|
+
if (tcpClient) tcpClient.stop();
|
|
818
|
+
tcpClient = tcpServer.available();
|
|
819
|
+
Out.setClient(&tcpClient);
|
|
820
|
+
}
|
|
821
|
+
if (tcpClient && !tcpClient.connected()) {
|
|
822
|
+
tcpClient.stop();
|
|
823
|
+
Out.setClient(nullptr);
|
|
824
|
+
}
|
|
825
|
+
if (tcpClient && tcpClient.available()) {
|
|
826
|
+
String msg = tcpClient.readStringUntil('\n');
|
|
827
|
+
if (msg.length() > 0) parseMessage(msg);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
esp_task_wdt_reset();
|
|
832
|
+
if (Serial.available()) {
|
|
833
|
+
String msg = Serial.readStringUntil('\n');
|
|
834
|
+
parseMessage(msg);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (returnToHomeAt != 0 && (long)(millis() - returnToHomeAt) >= 0) {
|
|
838
|
+
drawHome();
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (playStatusTemp && (long)(millis() - playStatusAt) >= 2500) {
|
|
842
|
+
playStatusTemp = false;
|
|
843
|
+
line1 = "PLAYING";
|
|
844
|
+
drawPlay();
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// --- Pot reading (PLAY only, volume) ---
|
|
848
|
+
if (uiState == UI_PLAY && millis() - lastPotRead > POT_READ_MS) {
|
|
849
|
+
lastPotRead = millis();
|
|
850
|
+
int nextVolume = readPercent();
|
|
851
|
+
if (abs(nextVolume - playVolume) >= 3) {
|
|
852
|
+
playVolume = nextVolume;
|
|
853
|
+
Out.print("POT:");
|
|
854
|
+
Out.println(playVolume);
|
|
855
|
+
drawPlay();
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// --- SELECT button ---
|
|
860
|
+
{
|
|
861
|
+
bool sel = digitalRead(BTN_SELECT_PIN) == LOW;
|
|
862
|
+
if (sel && !selectDown && millis() - selectLastDebounce > DEBOUNCE_MS) {
|
|
863
|
+
selectDown = true;
|
|
864
|
+
selectDownAt = millis();
|
|
865
|
+
}
|
|
866
|
+
if (!sel && selectDown) {
|
|
867
|
+
selectDown = false;
|
|
868
|
+
selectLastDebounce = millis();
|
|
869
|
+
handleSelectPress(millis() - selectDownAt >= LONG_PRESS_MS);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// --- UP button ---
|
|
874
|
+
{
|
|
875
|
+
bool up = digitalRead(BTN_UP_PIN) == LOW;
|
|
876
|
+
if (up && !upDown && millis() - upLastDebounce > DEBOUNCE_MS) {
|
|
877
|
+
upDown = true;
|
|
878
|
+
upDownAt = millis();
|
|
879
|
+
}
|
|
880
|
+
if (!up && upDown) {
|
|
881
|
+
upDown = false;
|
|
882
|
+
upLastDebounce = millis();
|
|
883
|
+
bool lp = millis() - upDownAt >= LONG_PRESS_MS;
|
|
884
|
+
if (uiState == UI_PLAY || !lp) {
|
|
885
|
+
handleUp(lp);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// --- DOWN button ---
|
|
891
|
+
{
|
|
892
|
+
bool dn = digitalRead(BTN_DOWN_PIN) == LOW;
|
|
893
|
+
if (dn && !downDown && millis() - downLastDebounce > DEBOUNCE_MS) {
|
|
894
|
+
downDown = true;
|
|
895
|
+
downDownAt = millis();
|
|
896
|
+
}
|
|
897
|
+
if (!dn && downDown) {
|
|
898
|
+
downDown = false;
|
|
899
|
+
downLastDebounce = millis();
|
|
900
|
+
bool lp = millis() - downDownAt >= LONG_PRESS_MS;
|
|
901
|
+
if (uiState == UI_PLAY || !lp) {
|
|
902
|
+
handleDown(lp);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// --- Indeterminate progress animation ---
|
|
908
|
+
if (uiState == UI_STATUS && progressPercent < 0 &&
|
|
909
|
+
millis() - lastIndeterminateAnim > 500) {
|
|
910
|
+
lastIndeterminateAnim = millis();
|
|
911
|
+
indeterminateCount = (indeterminateCount % 4) + 1;
|
|
912
|
+
if (!displayBlank) drawStatus();
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// --- Standby blanking ---
|
|
916
|
+
if (displayOk && uiState == UI_STANDBY && !displayBlank &&
|
|
917
|
+
(long)(millis() - standbyStartTime) >= STANDBY_BLANK_MS) {
|
|
918
|
+
display.ssd1306_command(0xAE);
|
|
919
|
+
displayBlank = true;
|
|
920
|
+
digitalWrite(LED_POWER_PIN, LOW);
|
|
921
|
+
}
|
|
922
|
+
if (!displayBlank) {
|
|
923
|
+
digitalWrite(LED_POWER_PIN, HIGH);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// --- PING timeout (disconnected) ---
|
|
927
|
+
if (uiState != UI_DISCONNECTED &&
|
|
928
|
+
(long)(millis() - lastMsgTime) >= PING_TIMEOUT_MS) {
|
|
929
|
+
drawDisconnected();
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// --- Activity LED ---
|
|
933
|
+
{
|
|
934
|
+
bool ledOn = false;
|
|
935
|
+
if (uiState == UI_BURN_READY || uiState == UI_PLAY || uiState == UI_WAITING || uiState == UI_IP) {
|
|
936
|
+
ledOn = true;
|
|
937
|
+
} else if (uiState == UI_STATUS) {
|
|
938
|
+
if (progressPercent >= 0) {
|
|
939
|
+
ledOn = (millis() - lastLedBlink > LED_BLINK_MS);
|
|
940
|
+
if (ledOn) {
|
|
941
|
+
lastLedBlink = millis();
|
|
942
|
+
actLedState = !actLedState;
|
|
943
|
+
}
|
|
944
|
+
ledOn = actLedState;
|
|
945
|
+
} else {
|
|
946
|
+
ledOn = true;
|
|
947
|
+
}
|
|
948
|
+
} else if (uiState == UI_HOME || uiState == UI_STANDBY) {
|
|
949
|
+
ledOn = false;
|
|
950
|
+
} else if (uiState == UI_DISCONNECTED) {
|
|
951
|
+
ledOn = true;
|
|
952
|
+
}
|
|
953
|
+
digitalWrite(LED_ACT_PIN, ledOn ? HIGH : LOW);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
delay(20);
|
|
957
|
+
}
|