discstation 0.1.23 → 0.1.27
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/README.md +43 -8
- package/arduino/c6/DiscStation_C6.ino +293 -84
- package/arduino/c6/secrets.h.example +14 -0
- package/arduino/v1/DiscStation.ino +214 -65
- package/arduino/v1/secrets.h.example +14 -0
- package/discstation.env.example +7 -0
- package/package.json +1 -1
- package/requirements.txt +1 -0
- package/src/discstation.py +150 -15
- package/src/discstation_burn.py +9 -0
- package/src/discstation_host.py +84 -0
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
#include <Wire.h>
|
|
2
2
|
#include <Adafruit_GFX.h>
|
|
3
3
|
#include <Adafruit_SSD1306.h>
|
|
4
|
+
#include <WiFi.h>
|
|
5
|
+
#include "esp_mac.h"
|
|
6
|
+
#include <WiFiManager.h>
|
|
7
|
+
#include <ESPmDNS.h>
|
|
8
|
+
#include <Preferences.h>
|
|
9
|
+
#include <ArduinoOTA.h>
|
|
4
10
|
#include <QRCode.h>
|
|
5
11
|
|
|
12
|
+
// Optional build-time Wi-Fi override for power users: copy secrets.h.example
|
|
13
|
+
// to secrets.h (git-ignored) and set WIFI_SSID / WIFI_PASS / OTA_PASSWORD.
|
|
14
|
+
#if __has_include("secrets.h")
|
|
15
|
+
#include "secrets.h"
|
|
16
|
+
#endif
|
|
17
|
+
|
|
6
18
|
#define SCREEN_WIDTH 128
|
|
7
19
|
#define SCREEN_HEIGHT 64
|
|
8
20
|
#define OLED_RESET -1
|
|
9
21
|
#define I2C_ADDRESS 0x3C
|
|
22
|
+
#define TCP_PORT 2323
|
|
10
23
|
|
|
11
24
|
#define BTN_SELECT_PIN 21 // D3
|
|
12
25
|
#define BTN_UP_PIN 20 // D9/MISO
|
|
13
26
|
#define BTN_DOWN_PIN 18 // D10/MOSI
|
|
14
|
-
#define LED_POWER_PIN 15 // Built-in user LED
|
|
15
|
-
#define LED_ACT_PIN 16 // D6/TX
|
|
16
27
|
#define POT_PIN 0 // D0/A0 (ADC)
|
|
17
28
|
|
|
18
29
|
#define DEBOUNCE_MS 50
|
|
@@ -20,8 +31,12 @@
|
|
|
20
31
|
#define POT_READ_MS 200
|
|
21
32
|
#define DONE_RESET_MS 30000
|
|
22
33
|
#define STANDBY_BLANK_MS 60000
|
|
34
|
+
#define IDLE_BLANK_MS 45000 // blank the OLED after this long with no input on HOME/STANDBY
|
|
23
35
|
#define PING_TIMEOUT_MS 30000
|
|
24
|
-
|
|
36
|
+
|
|
37
|
+
#define WIFI_RESET_HOLD_MS 10000
|
|
38
|
+
#define WIFI_CONNECT_TIMEOUT_MS 18000
|
|
39
|
+
#define WIFI_RETRY_MS 15000
|
|
25
40
|
|
|
26
41
|
#define MAX_HOME_MODES 5
|
|
27
42
|
#define BURN_COUNT 4
|
|
@@ -31,6 +46,137 @@ int homeCount = 3;
|
|
|
31
46
|
String homeModes[MAX_HOME_MODES] = {"BURN", "PLAY", "RIP"};
|
|
32
47
|
String discName = "";
|
|
33
48
|
|
|
49
|
+
// --- Wi-Fi link: mirror every outbound line to Serial AND a TCP client ---
|
|
50
|
+
WiFiServer tcpServer(TCP_PORT);
|
|
51
|
+
WiFiClient tcpClient;
|
|
52
|
+
WiFiManager wm;
|
|
53
|
+
Preferences prefs;
|
|
54
|
+
bool wifiConnected = false;
|
|
55
|
+
bool wifiInitDone = false;
|
|
56
|
+
bool portalActive = false;
|
|
57
|
+
bool otaEnabled = false;
|
|
58
|
+
bool credsJustSaved = false;
|
|
59
|
+
bool wifiResetArmed = false;
|
|
60
|
+
unsigned long wifiDropAt = 0;
|
|
61
|
+
String apName = "DiscStation";
|
|
62
|
+
String mdnsHost = "discstation";
|
|
63
|
+
|
|
64
|
+
class DualPrint : public Print {
|
|
65
|
+
public:
|
|
66
|
+
void setClient(WiFiClient* c) { _client = c; }
|
|
67
|
+
size_t write(uint8_t c) override {
|
|
68
|
+
Serial.write(c);
|
|
69
|
+
if (_client && _client->connected()) _client->write(c);
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
size_t write(const uint8_t* buf, size_t len) override {
|
|
73
|
+
Serial.write(buf, len);
|
|
74
|
+
if (_client && _client->connected()) _client->write(buf, len);
|
|
75
|
+
return len;
|
|
76
|
+
}
|
|
77
|
+
private:
|
|
78
|
+
WiFiClient* _client = nullptr;
|
|
79
|
+
} Out;
|
|
80
|
+
|
|
81
|
+
void drawSetup();
|
|
82
|
+
void drawWifiReset();
|
|
83
|
+
|
|
84
|
+
String deviceSuffix() {
|
|
85
|
+
uint8_t mac[6];
|
|
86
|
+
esp_read_mac(mac, ESP_MAC_WIFI_STA); // factory MAC from eFuse - valid before the Wi-Fi driver starts
|
|
87
|
+
char buf[5];
|
|
88
|
+
snprintf(buf, sizeof(buf), "%02X%02X", mac[4], mac[5]);
|
|
89
|
+
return String(buf);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void loadCreds(String& ssid, String& pass) {
|
|
93
|
+
prefs.begin("discstation", true);
|
|
94
|
+
ssid = prefs.getString("ssid", "");
|
|
95
|
+
pass = prefs.getString("pass", "");
|
|
96
|
+
prefs.end();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void saveCreds(const String& ssid, const String& pass) {
|
|
100
|
+
prefs.begin("discstation", false);
|
|
101
|
+
prefs.putString("ssid", ssid);
|
|
102
|
+
prefs.putString("pass", pass);
|
|
103
|
+
prefs.end();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
void clearCreds() {
|
|
107
|
+
prefs.begin("discstation", false);
|
|
108
|
+
prefs.clear();
|
|
109
|
+
prefs.end();
|
|
110
|
+
WiFi.disconnect(true, true);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
void wmSaveCallback() {
|
|
114
|
+
saveCreds(wm.getWiFiSSID(), wm.getWiFiPass());
|
|
115
|
+
credsJustSaved = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
void onWifiUp() {
|
|
119
|
+
portalActive = false;
|
|
120
|
+
wifiConnected = true;
|
|
121
|
+
wifiDropAt = 0;
|
|
122
|
+
WiFi.setSleep(WIFI_PS_MIN_MODEM);
|
|
123
|
+
tcpServer.begin();
|
|
124
|
+
if (MDNS.begin(mdnsHost.c_str())) {
|
|
125
|
+
MDNS.addService("discstation", "tcp", TCP_PORT);
|
|
126
|
+
}
|
|
127
|
+
#ifdef OTA_PASSWORD
|
|
128
|
+
ArduinoOTA.setHostname(mdnsHost.c_str());
|
|
129
|
+
ArduinoOTA.setPassword(OTA_PASSWORD);
|
|
130
|
+
ArduinoOTA.begin();
|
|
131
|
+
otaEnabled = true;
|
|
132
|
+
#endif
|
|
133
|
+
Out.print("WiFi OK "); Out.println(WiFi.localIP());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void startPortal() {
|
|
137
|
+
Out.print("WiFi: setup portal '"); Out.print(apName); Out.println("'");
|
|
138
|
+
portalActive = true;
|
|
139
|
+
WiFi.mode(WIFI_AP_STA);
|
|
140
|
+
wm.setConfigPortalBlocking(false);
|
|
141
|
+
wm.setConfigPortalTimeout(0);
|
|
142
|
+
wm.setSaveConfigCallback(wmSaveCallback);
|
|
143
|
+
wm.startConfigPortal(apName.c_str());
|
|
144
|
+
drawSetup();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
void initWiFi() {
|
|
148
|
+
apName = "DiscStation-" + deviceSuffix();
|
|
149
|
+
mdnsHost = "discstation-" + deviceSuffix();
|
|
150
|
+
mdnsHost.toLowerCase();
|
|
151
|
+
|
|
152
|
+
WiFi.mode(WIFI_STA);
|
|
153
|
+
WiFi.setAutoReconnect(true);
|
|
154
|
+
WiFi.setSleep(WIFI_PS_MIN_MODEM);
|
|
155
|
+
|
|
156
|
+
String ssid, pass;
|
|
157
|
+
#ifdef WIFI_SSID
|
|
158
|
+
ssid = WIFI_SSID;
|
|
159
|
+
pass = WIFI_PASS;
|
|
160
|
+
#else
|
|
161
|
+
loadCreds(ssid, pass);
|
|
162
|
+
#endif
|
|
163
|
+
|
|
164
|
+
if (ssid.length() > 0) {
|
|
165
|
+
Out.print("WiFi "); Out.print(ssid); Out.print("...");
|
|
166
|
+
WiFi.begin(ssid.c_str(), pass.c_str());
|
|
167
|
+
unsigned long t0 = millis();
|
|
168
|
+
while (WiFi.status() != WL_CONNECTED && millis() - t0 < WIFI_CONNECT_TIMEOUT_MS) delay(200);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (WiFi.status() == WL_CONNECTED) {
|
|
172
|
+
Out.println(" OK");
|
|
173
|
+
onWifiUp();
|
|
174
|
+
} else {
|
|
175
|
+
if (ssid.length() > 0) Out.println(" fail (2.4GHz band / wrong password?)");
|
|
176
|
+
startPortal();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
34
180
|
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
|
35
181
|
|
|
36
182
|
String ipUrl = "";
|
|
@@ -44,7 +190,8 @@ enum UiState {
|
|
|
44
190
|
UI_WAITING,
|
|
45
191
|
UI_IP,
|
|
46
192
|
UI_DISCONNECTED,
|
|
47
|
-
UI_LOADING
|
|
193
|
+
UI_LOADING,
|
|
194
|
+
UI_SETUP
|
|
48
195
|
};
|
|
49
196
|
|
|
50
197
|
const char* BURN_MODES[BURN_COUNT] = {"AUTO", "BEST", "LONG", "TEST"};
|
|
@@ -87,8 +234,6 @@ unsigned long downLastDebounce = 0;
|
|
|
87
234
|
int ffSpeedIndex = 0;
|
|
88
235
|
int rewSpeedIndex = 0;
|
|
89
236
|
|
|
90
|
-
bool actLedState = false;
|
|
91
|
-
unsigned long lastLedBlink = 0;
|
|
92
237
|
int homeIndex = 0;
|
|
93
238
|
int burnModeIndex = 0;
|
|
94
239
|
int burnSpeedIndex = 0;
|
|
@@ -96,6 +241,7 @@ bool editingSpeed = false;
|
|
|
96
241
|
int playVolume = 50;
|
|
97
242
|
unsigned long standbyStartTime = 0;
|
|
98
243
|
unsigned long lastMsgTime = 0;
|
|
244
|
+
unsigned long lastInputTime = 0; // last button press / screen change; drives OLED idle-blank
|
|
99
245
|
bool displayBlank = false;
|
|
100
246
|
bool audioPlayMode = false;
|
|
101
247
|
int displayRotation = 0;
|
|
@@ -121,14 +267,14 @@ int readPercent() {
|
|
|
121
267
|
|
|
122
268
|
void sendHomeMode() {
|
|
123
269
|
if (homeIndex >= 0 && homeIndex < homeCount) {
|
|
124
|
-
|
|
125
|
-
|
|
270
|
+
Out.print("MENU:");
|
|
271
|
+
Out.println(homeModes[homeIndex]);
|
|
126
272
|
}
|
|
127
273
|
}
|
|
128
274
|
|
|
129
275
|
void sendBurnMode() {
|
|
130
|
-
|
|
131
|
-
|
|
276
|
+
Out.print("MODE:");
|
|
277
|
+
Out.println(BURN_MODES[burnModeIndex]);
|
|
132
278
|
}
|
|
133
279
|
|
|
134
280
|
void printUpper(String value) {
|
|
@@ -162,6 +308,7 @@ void drawHeader() {
|
|
|
162
308
|
void drawHome() {
|
|
163
309
|
uiState = UI_HOME;
|
|
164
310
|
returnToHomeAt = 0;
|
|
311
|
+
lastInputTime = millis();
|
|
165
312
|
if (!displayOk) return;
|
|
166
313
|
|
|
167
314
|
display.clearDisplay();
|
|
@@ -311,9 +458,11 @@ void drawIP() {
|
|
|
311
458
|
}
|
|
312
459
|
|
|
313
460
|
void drawStandby() {
|
|
461
|
+
if (portalActive) { drawSetup(); return; }
|
|
314
462
|
uiState = UI_STANDBY;
|
|
315
463
|
returnToHomeAt = 0;
|
|
316
464
|
standbyStartTime = millis();
|
|
465
|
+
lastInputTime = millis();
|
|
317
466
|
displayBlank = false;
|
|
318
467
|
if (!displayOk) return;
|
|
319
468
|
|
|
@@ -326,6 +475,38 @@ void drawStandby() {
|
|
|
326
475
|
display.display();
|
|
327
476
|
}
|
|
328
477
|
|
|
478
|
+
void drawSetup() {
|
|
479
|
+
uiState = UI_SETUP;
|
|
480
|
+
returnToHomeAt = 0;
|
|
481
|
+
displayBlank = false;
|
|
482
|
+
lastInputTime = millis();
|
|
483
|
+
if (!displayOk) return;
|
|
484
|
+
display.clearDisplay();
|
|
485
|
+
drawHeader();
|
|
486
|
+
display.setCursor(2, 16);
|
|
487
|
+
display.print("WIFI SETUP");
|
|
488
|
+
display.setCursor(2, 28);
|
|
489
|
+
display.print("JOIN:");
|
|
490
|
+
display.setCursor(2, 38);
|
|
491
|
+
display.print(apName);
|
|
492
|
+
display.setCursor(2, 50);
|
|
493
|
+
display.print("THEN 192.168.4.1");
|
|
494
|
+
display.display();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
void drawWifiReset() {
|
|
498
|
+
uiState = UI_SETUP;
|
|
499
|
+
displayBlank = false;
|
|
500
|
+
if (!displayOk) return;
|
|
501
|
+
display.clearDisplay();
|
|
502
|
+
drawHeader();
|
|
503
|
+
display.setCursor(2, 25);
|
|
504
|
+
display.print("WIFI RESET");
|
|
505
|
+
display.setCursor(2, 40);
|
|
506
|
+
display.print("REBOOTING...");
|
|
507
|
+
display.display();
|
|
508
|
+
}
|
|
509
|
+
|
|
329
510
|
void drawDisconnected() {
|
|
330
511
|
uiState = UI_DISCONNECTED;
|
|
331
512
|
returnToHomeAt = 0;
|
|
@@ -378,6 +559,7 @@ void wakeDisplay() {
|
|
|
378
559
|
case UI_STANDBY: drawStandby(); break;
|
|
379
560
|
case UI_DISCONNECTED: drawDisconnected(); break;
|
|
380
561
|
case UI_LOADING: drawLoading(); break;
|
|
562
|
+
case UI_SETUP: drawSetup(); break;
|
|
381
563
|
}
|
|
382
564
|
}
|
|
383
565
|
|
|
@@ -410,7 +592,7 @@ void parseMessage(String msg) {
|
|
|
410
592
|
|
|
411
593
|
if (msg == "PING") {
|
|
412
594
|
lastMsgTime = millis();
|
|
413
|
-
|
|
595
|
+
Out.println("PONG");
|
|
414
596
|
if (uiState == UI_DISCONNECTED) drawStandby();
|
|
415
597
|
return;
|
|
416
598
|
}
|
|
@@ -418,8 +600,8 @@ void parseMessage(String msg) {
|
|
|
418
600
|
wakeDisplay();
|
|
419
601
|
lastMsgTime = millis();
|
|
420
602
|
|
|
421
|
-
|
|
422
|
-
|
|
603
|
+
Out.print("RCV:");
|
|
604
|
+
Out.println(msg);
|
|
423
605
|
|
|
424
606
|
if (msg.startsWith("HOME:")) {
|
|
425
607
|
homeIndex = readBucket(homeCount);
|
|
@@ -593,6 +775,7 @@ void parseMessage(String msg) {
|
|
|
593
775
|
}
|
|
594
776
|
|
|
595
777
|
void setup() {
|
|
778
|
+
setCpuFrequencyMhz(160); // 240 -> 160: ~halves CPU power; I2C/GPIO all fine at 160
|
|
596
779
|
Serial.begin(115200);
|
|
597
780
|
Wire.begin(22, 23);
|
|
598
781
|
|
|
@@ -604,10 +787,6 @@ void setup() {
|
|
|
604
787
|
pinMode(BTN_SELECT_PIN, INPUT_PULLUP);
|
|
605
788
|
pinMode(BTN_UP_PIN, INPUT_PULLUP);
|
|
606
789
|
pinMode(BTN_DOWN_PIN, INPUT_PULLUP);
|
|
607
|
-
pinMode(LED_POWER_PIN, OUTPUT);
|
|
608
|
-
pinMode(LED_ACT_PIN, OUTPUT);
|
|
609
|
-
digitalWrite(LED_POWER_PIN, HIGH);
|
|
610
|
-
digitalWrite(LED_ACT_PIN, LOW);
|
|
611
790
|
pinMode(POT_PIN, INPUT);
|
|
612
791
|
analogReadResolution(12);
|
|
613
792
|
|
|
@@ -618,37 +797,32 @@ void setup() {
|
|
|
618
797
|
displayOk = display.begin(SSD1306_SWITCHCAPVCC, I2C_ADDRESS);
|
|
619
798
|
|
|
620
799
|
if (displayOk) {
|
|
621
|
-
|
|
800
|
+
Out.println("Display OK");
|
|
622
801
|
} else {
|
|
623
|
-
|
|
802
|
+
Out.println("Display FAILED");
|
|
624
803
|
delay(3000);
|
|
625
804
|
}
|
|
626
805
|
|
|
627
|
-
for (int i = 0; i < 3; i++) {
|
|
628
|
-
digitalWrite(LED_ACT_PIN, HIGH);
|
|
629
|
-
delay(150);
|
|
630
|
-
digitalWrite(LED_ACT_PIN, LOW);
|
|
631
|
-
delay(150);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
806
|
drawStandby();
|
|
635
807
|
lastMsgTime = millis();
|
|
636
|
-
|
|
808
|
+
lastInputTime = millis();
|
|
809
|
+
Out.println("DISCSTATION_READY");
|
|
637
810
|
}
|
|
638
811
|
|
|
639
812
|
void handleSelectPress(bool longPress) {
|
|
640
813
|
wakeDisplay();
|
|
814
|
+
lastInputTime = millis();
|
|
641
815
|
if (uiState == UI_HOME) {
|
|
642
816
|
if (longPress) {
|
|
643
|
-
|
|
817
|
+
Out.println("EJECT");
|
|
644
818
|
line1 = "Ejecting...";
|
|
645
819
|
line2 = "";
|
|
646
820
|
line3 = "";
|
|
647
821
|
returnToHomeAt = millis() + 5000;
|
|
648
822
|
drawStatus();
|
|
649
823
|
} else if (homeIndex >= 0 && homeIndex < homeCount) {
|
|
650
|
-
|
|
651
|
-
|
|
824
|
+
Out.print("SELECT:");
|
|
825
|
+
Out.println(homeModes[homeIndex]);
|
|
652
826
|
line1 = "Selected";
|
|
653
827
|
line2 = homeModes[homeIndex];
|
|
654
828
|
line3 = "";
|
|
@@ -656,7 +830,7 @@ void handleSelectPress(bool longPress) {
|
|
|
656
830
|
}
|
|
657
831
|
|
|
658
832
|
} else if (uiState == UI_STANDBY) {
|
|
659
|
-
|
|
833
|
+
Out.println("EJECT");
|
|
660
834
|
line1 = "Ejecting...";
|
|
661
835
|
line2 = "";
|
|
662
836
|
line3 = "";
|
|
@@ -668,16 +842,16 @@ void handleSelectPress(bool longPress) {
|
|
|
668
842
|
|
|
669
843
|
} else if (uiState == UI_IP) {
|
|
670
844
|
if (longPress) {
|
|
671
|
-
|
|
845
|
+
Out.println("CANCEL");
|
|
672
846
|
drawHome();
|
|
673
847
|
}
|
|
674
848
|
|
|
675
849
|
} else if (uiState == UI_WAITING) {
|
|
676
850
|
if (longPress) {
|
|
677
|
-
|
|
851
|
+
Out.println("CANCEL");
|
|
678
852
|
drawHome();
|
|
679
853
|
} else {
|
|
680
|
-
|
|
854
|
+
Out.println("CONFIRM");
|
|
681
855
|
line1 = "Confirmed!";
|
|
682
856
|
line2 = "";
|
|
683
857
|
line3 = "";
|
|
@@ -686,8 +860,8 @@ void handleSelectPress(bool longPress) {
|
|
|
686
860
|
|
|
687
861
|
} else if (uiState == UI_BURN_READY) {
|
|
688
862
|
if (longPress) {
|
|
689
|
-
|
|
690
|
-
|
|
863
|
+
Out.print("START:");
|
|
864
|
+
Out.println(BURN_MODES[burnModeIndex]);
|
|
691
865
|
line1 = "Starting...";
|
|
692
866
|
line2 = BURN_MODES[burnModeIndex];
|
|
693
867
|
line3 = "";
|
|
@@ -699,7 +873,7 @@ void handleSelectPress(bool longPress) {
|
|
|
699
873
|
|
|
700
874
|
} else if (uiState == UI_STATUS) {
|
|
701
875
|
if (longPress) {
|
|
702
|
-
|
|
876
|
+
Out.println("CANCEL");
|
|
703
877
|
line1 = "Cancelling...";
|
|
704
878
|
line2 = "";
|
|
705
879
|
line3 = "";
|
|
@@ -707,22 +881,23 @@ void handleSelectPress(bool longPress) {
|
|
|
707
881
|
}
|
|
708
882
|
|
|
709
883
|
} else if (uiState == UI_DISCONNECTED) {
|
|
710
|
-
|
|
884
|
+
Out.println("CANCEL");
|
|
711
885
|
drawStandby();
|
|
712
886
|
|
|
713
887
|
} else if (uiState == UI_PLAY) {
|
|
714
888
|
ffSpeedIndex = 0;
|
|
715
889
|
rewSpeedIndex = 0;
|
|
716
890
|
if (longPress) {
|
|
717
|
-
|
|
891
|
+
Out.println("PLAY_STOP");
|
|
718
892
|
} else {
|
|
719
|
-
|
|
893
|
+
Out.println("PLAY_BUTTON");
|
|
720
894
|
}
|
|
721
895
|
}
|
|
722
896
|
}
|
|
723
897
|
|
|
724
898
|
void handleUp(bool longPress) {
|
|
725
899
|
wakeDisplay();
|
|
900
|
+
lastInputTime = millis();
|
|
726
901
|
if (uiState == UI_HOME) {
|
|
727
902
|
if (homeCount > 0) {
|
|
728
903
|
homeIndex = (homeIndex - 1 + homeCount) % homeCount;
|
|
@@ -732,8 +907,8 @@ void handleUp(bool longPress) {
|
|
|
732
907
|
} else if (uiState == UI_BURN_READY) {
|
|
733
908
|
if (editingSpeed) {
|
|
734
909
|
burnSpeedIndex = (burnSpeedIndex - 1 + SPEED_COUNT) % SPEED_COUNT;
|
|
735
|
-
|
|
736
|
-
|
|
910
|
+
Out.print("SPEED:");
|
|
911
|
+
Out.println(SPEED_MODES[burnSpeedIndex]);
|
|
737
912
|
drawBurnReady();
|
|
738
913
|
} else {
|
|
739
914
|
burnModeIndex = (burnModeIndex - 1 + BURN_COUNT) % BURN_COUNT;
|
|
@@ -742,23 +917,24 @@ void handleUp(bool longPress) {
|
|
|
742
917
|
}
|
|
743
918
|
} else if (uiState == UI_PLAY) {
|
|
744
919
|
if (audioPlayMode) {
|
|
745
|
-
|
|
920
|
+
Out.println(displayRotation == 0 ? (longPress ? "REW:BIG" : "REW:10") : (longPress ? "FF:BIG" : "FF:10"));
|
|
746
921
|
return;
|
|
747
922
|
}
|
|
748
923
|
rewSpeedIndex = 0;
|
|
749
924
|
if (longPress) {
|
|
750
|
-
|
|
925
|
+
Out.println("FF:BIG");
|
|
751
926
|
} else {
|
|
752
927
|
ffSpeedIndex = (ffSpeedIndex + 1) % 4;
|
|
753
928
|
int seek_sec[] = {10, 20, 30, 60};
|
|
754
|
-
|
|
755
|
-
|
|
929
|
+
Out.print("FF:");
|
|
930
|
+
Out.println(seek_sec[ffSpeedIndex]);
|
|
756
931
|
}
|
|
757
932
|
}
|
|
758
933
|
}
|
|
759
934
|
|
|
760
935
|
void handleDown(bool longPress) {
|
|
761
936
|
wakeDisplay();
|
|
937
|
+
lastInputTime = millis();
|
|
762
938
|
if (uiState == UI_HOME) {
|
|
763
939
|
if (homeCount > 0) {
|
|
764
940
|
homeIndex = (homeIndex + 1) % homeCount;
|
|
@@ -768,8 +944,8 @@ void handleDown(bool longPress) {
|
|
|
768
944
|
} else if (uiState == UI_BURN_READY) {
|
|
769
945
|
if (editingSpeed) {
|
|
770
946
|
burnSpeedIndex = (burnSpeedIndex + 1) % SPEED_COUNT;
|
|
771
|
-
|
|
772
|
-
|
|
947
|
+
Out.print("SPEED:");
|
|
948
|
+
Out.println(SPEED_MODES[burnSpeedIndex]);
|
|
773
949
|
drawBurnReady();
|
|
774
950
|
} else {
|
|
775
951
|
burnModeIndex = (burnModeIndex + 1) % BURN_COUNT;
|
|
@@ -778,22 +954,66 @@ void handleDown(bool longPress) {
|
|
|
778
954
|
}
|
|
779
955
|
} else if (uiState == UI_PLAY) {
|
|
780
956
|
if (audioPlayMode) {
|
|
781
|
-
|
|
957
|
+
Out.println(displayRotation == 0 ? (longPress ? "FF:BIG" : "FF:10") : (longPress ? "REW:BIG" : "REW:10"));
|
|
782
958
|
return;
|
|
783
959
|
}
|
|
784
960
|
ffSpeedIndex = 0;
|
|
785
961
|
if (longPress) {
|
|
786
|
-
|
|
962
|
+
Out.println("REW:BIG");
|
|
787
963
|
} else {
|
|
788
964
|
rewSpeedIndex = (rewSpeedIndex + 1) % 4;
|
|
789
965
|
int seek_sec[] = {10, 20, 30, 60};
|
|
790
|
-
|
|
791
|
-
|
|
966
|
+
Out.print("REW:");
|
|
967
|
+
Out.println(seek_sec[rewSpeedIndex]);
|
|
792
968
|
}
|
|
793
969
|
}
|
|
794
970
|
}
|
|
795
971
|
|
|
796
972
|
void loop() {
|
|
973
|
+
if (!wifiInitDone && millis() > 3000) {
|
|
974
|
+
wifiInitDone = true;
|
|
975
|
+
initWiFi();
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (credsJustSaved) {
|
|
979
|
+
drawWifiReset();
|
|
980
|
+
delay(600);
|
|
981
|
+
ESP.restart();
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if (portalActive) {
|
|
985
|
+
wm.process();
|
|
986
|
+
if (WiFi.status() == WL_CONNECTED) onWifiUp();
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
if (wifiConnected && WiFi.status() != WL_CONNECTED) {
|
|
990
|
+
if (wifiDropAt == 0) wifiDropAt = millis();
|
|
991
|
+
else if (millis() - wifiDropAt > WIFI_RETRY_MS) {
|
|
992
|
+
wifiDropAt = millis();
|
|
993
|
+
WiFi.disconnect();
|
|
994
|
+
WiFi.begin();
|
|
995
|
+
}
|
|
996
|
+
} else if (wifiConnected) {
|
|
997
|
+
wifiDropAt = 0;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (wifiConnected && WiFi.status() == WL_CONNECTED) {
|
|
1001
|
+
if (otaEnabled) ArduinoOTA.handle();
|
|
1002
|
+
if (tcpServer.hasClient()) {
|
|
1003
|
+
if (tcpClient) tcpClient.stop();
|
|
1004
|
+
tcpClient = tcpServer.available();
|
|
1005
|
+
Out.setClient(&tcpClient);
|
|
1006
|
+
}
|
|
1007
|
+
if (tcpClient && !tcpClient.connected()) {
|
|
1008
|
+
tcpClient.stop();
|
|
1009
|
+
Out.setClient(nullptr);
|
|
1010
|
+
}
|
|
1011
|
+
if (tcpClient && tcpClient.available()) {
|
|
1012
|
+
String tmsg = tcpClient.readStringUntil('\n');
|
|
1013
|
+
if (tmsg.length() > 0) parseMessage(tmsg);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
797
1017
|
if (Serial.available()) {
|
|
798
1018
|
String msg = Serial.readStringUntil('\n');
|
|
799
1019
|
parseMessage(msg);
|
|
@@ -814,8 +1034,8 @@ void loop() {
|
|
|
814
1034
|
int nextVolume = readPercent();
|
|
815
1035
|
if (abs(nextVolume - playVolume) >= 3) {
|
|
816
1036
|
playVolume = nextVolume;
|
|
817
|
-
|
|
818
|
-
|
|
1037
|
+
Out.print("POT:");
|
|
1038
|
+
Out.println(playVolume);
|
|
819
1039
|
drawPlay();
|
|
820
1040
|
}
|
|
821
1041
|
}
|
|
@@ -825,11 +1045,26 @@ void loop() {
|
|
|
825
1045
|
if (sel && !selectDown && millis() - selectLastDebounce > DEBOUNCE_MS) {
|
|
826
1046
|
selectDown = true;
|
|
827
1047
|
selectDownAt = millis();
|
|
1048
|
+
wifiResetArmed = false;
|
|
1049
|
+
}
|
|
1050
|
+
// 10s hold on HOME/SETUP/STANDBY = wipe Wi-Fi creds + reboot to portal.
|
|
1051
|
+
if (sel && selectDown && !wifiResetArmed &&
|
|
1052
|
+
(uiState == UI_HOME || uiState == UI_SETUP || uiState == UI_STANDBY) &&
|
|
1053
|
+
millis() - selectDownAt >= WIFI_RESET_HOLD_MS) {
|
|
1054
|
+
wifiResetArmed = true;
|
|
1055
|
+
Out.println("WiFi: creds wiped, rebooting");
|
|
1056
|
+
clearCreds();
|
|
1057
|
+
drawWifiReset();
|
|
1058
|
+
delay(800);
|
|
1059
|
+
ESP.restart();
|
|
828
1060
|
}
|
|
829
1061
|
if (!sel && selectDown) {
|
|
830
1062
|
selectDown = false;
|
|
831
1063
|
selectLastDebounce = millis();
|
|
832
|
-
|
|
1064
|
+
if (!wifiResetArmed) {
|
|
1065
|
+
handleSelectPress(millis() - selectDownAt >= LONG_PRESS_MS);
|
|
1066
|
+
}
|
|
1067
|
+
wifiResetArmed = false;
|
|
833
1068
|
}
|
|
834
1069
|
}
|
|
835
1070
|
|
|
@@ -878,14 +1113,11 @@ void loop() {
|
|
|
878
1113
|
if (!displayBlank) drawLoading();
|
|
879
1114
|
}
|
|
880
1115
|
|
|
881
|
-
if (displayOk &&
|
|
882
|
-
(
|
|
1116
|
+
if (displayOk && !displayBlank &&
|
|
1117
|
+
(uiState == UI_HOME || uiState == UI_STANDBY) &&
|
|
1118
|
+
(long)(millis() - lastInputTime) >= IDLE_BLANK_MS) {
|
|
883
1119
|
display.ssd1306_command(0xAE);
|
|
884
1120
|
displayBlank = true;
|
|
885
|
-
digitalWrite(LED_POWER_PIN, LOW);
|
|
886
|
-
}
|
|
887
|
-
if (!displayBlank) {
|
|
888
|
-
digitalWrite(LED_POWER_PIN, HIGH);
|
|
889
1121
|
}
|
|
890
1122
|
|
|
891
1123
|
if (uiState != UI_DISCONNECTED &&
|
|
@@ -893,28 +1125,5 @@ void loop() {
|
|
|
893
1125
|
drawDisconnected();
|
|
894
1126
|
}
|
|
895
1127
|
|
|
896
|
-
{
|
|
897
|
-
bool ledOn = false;
|
|
898
|
-
if (uiState == UI_BURN_READY || uiState == UI_PLAY || uiState == UI_WAITING || uiState == UI_IP) {
|
|
899
|
-
ledOn = true;
|
|
900
|
-
} else if (uiState == UI_STATUS) {
|
|
901
|
-
if (progressPercent >= 0) {
|
|
902
|
-
ledOn = (millis() - lastLedBlink > LED_BLINK_MS);
|
|
903
|
-
if (ledOn) {
|
|
904
|
-
lastLedBlink = millis();
|
|
905
|
-
actLedState = !actLedState;
|
|
906
|
-
}
|
|
907
|
-
ledOn = actLedState;
|
|
908
|
-
} else {
|
|
909
|
-
ledOn = true;
|
|
910
|
-
}
|
|
911
|
-
} else if (uiState == UI_HOME || uiState == UI_STANDBY || uiState == UI_LOADING) {
|
|
912
|
-
ledOn = false;
|
|
913
|
-
} else if (uiState == UI_DISCONNECTED) {
|
|
914
|
-
ledOn = true;
|
|
915
|
-
}
|
|
916
|
-
digitalWrite(LED_ACT_PIN, ledOn ? HIGH : LOW);
|
|
917
|
-
}
|
|
918
|
-
|
|
919
1128
|
delay(20);
|
|
920
1129
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Optional build-time Wi-Fi override for the ESP32-C6 remote.
|
|
2
|
+
//
|
|
3
|
+
// cp secrets.h.example secrets.h (secrets.h is git-ignored)
|
|
4
|
+
//
|
|
5
|
+
// If WIFI_SSID is defined, the firmware connects straight to this network and
|
|
6
|
+
// the runtime setup portal is skipped entirely. Leave secrets.h absent to use
|
|
7
|
+
// the normal captive-portal provisioning ("DiscStation-XXXX" AP).
|
|
8
|
+
|
|
9
|
+
#define WIFI_SSID "YourNetwork"
|
|
10
|
+
#define WIFI_PASS "YourPassword"
|
|
11
|
+
|
|
12
|
+
// Optional: enable Arduino OTA updates with this password. Omit to keep OTA off
|
|
13
|
+
// (recommended unless you actually use it).
|
|
14
|
+
// #define OTA_PASSWORD "change-me"
|