discstation 0.1.27 → 0.1.29

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.
@@ -31,7 +31,8 @@
31
31
  #define POT_READ_MS 200
32
32
  #define DONE_RESET_MS 30000
33
33
  #define STANDBY_BLANK_MS 60000
34
- #define IDLE_BLANK_MS 45000 // blank the OLED after this long with no input on HOME/STANDBY
34
+ #define IDLE_BLANK_MS 45000 // no input this long on HOME/STANDBY -> spinning-disc screensaver
35
+ #define SAVER_FRAME_MS 90 // screensaver frame interval (~11fps)
35
36
  #define PING_TIMEOUT_MS 30000
36
37
 
37
38
  #define WIFI_RESET_HOLD_MS 10000
@@ -241,8 +242,10 @@ bool editingSpeed = false;
241
242
  int playVolume = 50;
242
243
  unsigned long standbyStartTime = 0;
243
244
  unsigned long lastMsgTime = 0;
244
- unsigned long lastInputTime = 0; // last button press / screen change; drives OLED idle-blank
245
- bool displayBlank = false;
245
+ unsigned long lastInputTime = 0; // last button press / screen change; drives the idle screensaver
246
+ bool displayBlank = false; // true = real UI hidden, disc screensaver running; any input restores it
247
+ unsigned long lastSaverFrame = 0;
248
+ int saverStep = 0;
246
249
  bool audioPlayMode = false;
247
250
  int displayRotation = 0;
248
251
 
@@ -545,8 +548,50 @@ void drawLoading() {
545
548
  display.display();
546
549
  }
547
550
 
548
- void wakeDisplay() {
549
- if (!displayBlank || !displayOk) return;
551
+ // 24-step sine table x64: sin(i*15deg)*64. Drives the idle disc screensaver.
552
+ const int8_t SIN24[24] = {
553
+ 0, 17, 32, 45, 55, 62, 64, 62, 55, 45, 32, 17,
554
+ 0, -17, -32, -45, -55, -62, -64, -62, -55, -45, -32, -17
555
+ };
556
+
557
+ // Spinning-disc screensaver frame - shown after IDLE_BLANK_MS on HOME/STANDBY
558
+ // instead of powering the panel off. Keeps the OLED lit and the I2C bus busy so
559
+ // a USB power-bank feeding the remote doesn't hit its no-load auto-shutoff.
560
+ void drawDiscSaver(int step) {
561
+ if (!displayOk) return;
562
+ const int cx = 64, cy = 32, R = 30;
563
+ display.clearDisplay();
564
+ display.fillCircle(cx, cy, R, SSD1306_WHITE); // vinyl body
565
+
566
+ for (int cl = 0; cl < 2; cl++) { // 2 groove clusters, 180 apart
567
+ int base = step + cl * 12;
568
+ for (int g = 0; g < 3; g++) { // 3 nested "sound wave" grooves
569
+ for (int t = 0; t < 3; t++) { // 3px-thick stroke
570
+ int r = 16 + g * 5 + t;
571
+ int px = -100, py = -100;
572
+ for (int a = 0; a <= 4; a++) { // ~60deg arc, 4 chords
573
+ int i = (base + a) % 24;
574
+ int x = cx + r * SIN24[(i + 6) % 24] / 64;
575
+ int y = cy + r * SIN24[i] / 64;
576
+ if (px > -100) display.drawLine(px, py, x, y, SSD1306_BLACK);
577
+ px = x; py = y;
578
+ }
579
+ }
580
+ }
581
+ }
582
+
583
+ display.fillCircle(cx, cy, 13, SSD1306_BLACK); // concentric label
584
+ display.fillCircle(cx, cy, 11, SSD1306_WHITE);
585
+ display.fillCircle(cx, cy, 8, SSD1306_BLACK);
586
+ display.fillCircle(cx, cy, 5, SSD1306_WHITE);
587
+ display.fillCircle(cx, cy, 2, SSD1306_BLACK); // center hole
588
+ display.display();
589
+ }
590
+
591
+ // Returns true if this call actually woke a blanked screen - callers use that
592
+ // to swallow the wake press so it doesn't also trigger a menu action.
593
+ bool wakeDisplay() {
594
+ if (!displayBlank || !displayOk) return false;
550
595
  display.ssd1306_command(0xAF);
551
596
  displayBlank = false;
552
597
  switch (uiState) {
@@ -561,6 +606,7 @@ void wakeDisplay() {
561
606
  case UI_LOADING: drawLoading(); break;
562
607
  case UI_SETUP: drawSetup(); break;
563
608
  }
609
+ return true;
564
610
  }
565
611
 
566
612
  void drawPlay() {
@@ -655,6 +701,8 @@ void parseMessage(String msg) {
655
701
  line1 = msg.substring(5);
656
702
  line2 = "Playing disc";
657
703
  playVolume = readPercent();
704
+ Out.print("POT:"); // push the knob's current level so playback starts at it
705
+ Out.println(playVolume);
658
706
  drawPlay();
659
707
 
660
708
  } else if (msg.startsWith("PLAY_MODE:")) {
@@ -810,7 +858,7 @@ void setup() {
810
858
  }
811
859
 
812
860
  void handleSelectPress(bool longPress) {
813
- wakeDisplay();
861
+ if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
814
862
  lastInputTime = millis();
815
863
  if (uiState == UI_HOME) {
816
864
  if (longPress) {
@@ -896,7 +944,7 @@ void handleSelectPress(bool longPress) {
896
944
  }
897
945
 
898
946
  void handleUp(bool longPress) {
899
- wakeDisplay();
947
+ if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
900
948
  lastInputTime = millis();
901
949
  if (uiState == UI_HOME) {
902
950
  if (homeCount > 0) {
@@ -933,7 +981,7 @@ void handleUp(bool longPress) {
933
981
  }
934
982
 
935
983
  void handleDown(bool longPress) {
936
- wakeDisplay();
984
+ if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
937
985
  lastInputTime = millis();
938
986
  if (uiState == UI_HOME) {
939
987
  if (homeCount > 0) {
@@ -1113,11 +1161,19 @@ void loop() {
1113
1161
  if (!displayBlank) drawLoading();
1114
1162
  }
1115
1163
 
1164
+ // --- Idle disc screensaver (HOME + STANDBY) ---
1116
1165
  if (displayOk && !displayBlank &&
1117
1166
  (uiState == UI_HOME || uiState == UI_STANDBY) &&
1118
1167
  (long)(millis() - lastInputTime) >= IDLE_BLANK_MS) {
1119
- display.ssd1306_command(0xAE);
1120
1168
  displayBlank = true;
1169
+ saverStep = 0;
1170
+ lastSaverFrame = 0;
1171
+ }
1172
+ if (displayBlank && displayOk &&
1173
+ (long)(millis() - lastSaverFrame) >= SAVER_FRAME_MS) {
1174
+ lastSaverFrame = millis();
1175
+ drawDiscSaver(saverStep);
1176
+ saverStep = (saverStep + 1) % 24;
1121
1177
  }
1122
1178
 
1123
1179
  if (uiState != UI_DISCONNECTED &&
@@ -32,7 +32,8 @@
32
32
  #define POT_READ_MS 200
33
33
  #define DONE_RESET_MS 30000
34
34
  #define STANDBY_BLANK_MS 60000
35
- #define IDLE_BLANK_MS 45000 // blank the OLED after this long with no input on HOME/STANDBY
35
+ #define IDLE_BLANK_MS 45000 // no input this long on HOME/STANDBY -> spinning-disc screensaver
36
+ #define SAVER_FRAME_MS 90 // screensaver frame interval (~11fps)
36
37
  #define PING_TIMEOUT_MS 30000
37
38
 
38
39
  #define WIFI_RESET_HOLD_MS 10000 // hold SELECT this long on HOME to wipe Wi-Fi creds
@@ -245,8 +246,10 @@ bool editingSpeed = false;
245
246
  int playVolume = 50;
246
247
  unsigned long standbyStartTime = 0;
247
248
  unsigned long lastMsgTime = 0;
248
- unsigned long lastInputTime = 0; // last button press / screen change; drives OLED idle-blank
249
- bool displayBlank = false;
249
+ unsigned long lastInputTime = 0; // last button press / screen change; drives the idle screensaver
250
+ bool displayBlank = false; // true = real UI hidden, disc screensaver running; any input restores it
251
+ unsigned long lastSaverFrame = 0;
252
+ int saverStep = 0;
250
253
  bool audioPlayMode = false;
251
254
  int displayRotation = 0;
252
255
 
@@ -531,8 +534,50 @@ void drawDisconnected() {
531
534
  display.display();
532
535
  }
533
536
 
534
- void wakeDisplay() {
535
- if (!displayBlank || !displayOk) return;
537
+ // 24-step sine table x64: sin(i*15deg)*64. Drives the idle disc screensaver.
538
+ const int8_t SIN24[24] = {
539
+ 0, 17, 32, 45, 55, 62, 64, 62, 55, 45, 32, 17,
540
+ 0, -17, -32, -45, -55, -62, -64, -62, -55, -45, -32, -17
541
+ };
542
+
543
+ // Spinning-disc screensaver frame - shown after IDLE_BLANK_MS on HOME/STANDBY
544
+ // instead of powering the panel off. Keeps the OLED lit and the I2C bus busy so
545
+ // a USB power-bank feeding the remote doesn't hit its no-load auto-shutoff.
546
+ void drawDiscSaver(int step) {
547
+ if (!displayOk) return;
548
+ const int cx = 64, cy = 32, R = 30;
549
+ display.clearDisplay();
550
+ display.fillCircle(cx, cy, R, SSD1306_WHITE); // vinyl body
551
+
552
+ for (int cl = 0; cl < 2; cl++) { // 2 groove clusters, 180 apart
553
+ int base = step + cl * 12;
554
+ for (int g = 0; g < 3; g++) { // 3 nested "sound wave" grooves
555
+ for (int t = 0; t < 3; t++) { // 3px-thick stroke
556
+ int r = 16 + g * 5 + t;
557
+ int px = -100, py = -100;
558
+ for (int a = 0; a <= 4; a++) { // ~60deg arc, 4 chords
559
+ int i = (base + a) % 24;
560
+ int x = cx + r * SIN24[(i + 6) % 24] / 64;
561
+ int y = cy + r * SIN24[i] / 64;
562
+ if (px > -100) display.drawLine(px, py, x, y, SSD1306_BLACK);
563
+ px = x; py = y;
564
+ }
565
+ }
566
+ }
567
+ }
568
+
569
+ display.fillCircle(cx, cy, 13, SSD1306_BLACK); // concentric label
570
+ display.fillCircle(cx, cy, 11, SSD1306_WHITE);
571
+ display.fillCircle(cx, cy, 8, SSD1306_BLACK);
572
+ display.fillCircle(cx, cy, 5, SSD1306_WHITE);
573
+ display.fillCircle(cx, cy, 2, SSD1306_BLACK); // center hole
574
+ display.display();
575
+ }
576
+
577
+ // Returns true if this call actually woke a blanked screen - callers use that
578
+ // to swallow the wake press so it doesn't also trigger a menu action.
579
+ bool wakeDisplay() {
580
+ if (!displayBlank || !displayOk) return false;
536
581
  display.ssd1306_command(0xAF);
537
582
  displayBlank = false;
538
583
  switch (uiState) {
@@ -546,6 +591,7 @@ void wakeDisplay() {
546
591
  case UI_DISCONNECTED: drawDisconnected(); break;
547
592
  case UI_SETUP: drawSetup(); break;
548
593
  }
594
+ return true;
549
595
  }
550
596
 
551
597
  void drawPlay() {
@@ -640,6 +686,8 @@ void parseMessage(String msg) {
640
686
  line1 = msg.substring(5);
641
687
  line2 = "Playing disc";
642
688
  playVolume = readPercent();
689
+ Out.print("POT:"); // push the knob's current level so playback starts at it
690
+ Out.println(playVolume);
643
691
  drawPlay();
644
692
 
645
693
  } else if (msg.startsWith("PLAY_MODE:")) {
@@ -775,7 +823,7 @@ void setup() {
775
823
  }
776
824
 
777
825
  void handleSelectPress(bool longPress) {
778
- wakeDisplay();
826
+ if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
779
827
  lastInputTime = millis();
780
828
  if (uiState == UI_HOME) {
781
829
  if (longPress) {
@@ -865,7 +913,7 @@ void handleSelectPress(bool longPress) {
865
913
  }
866
914
 
867
915
  void handleUp(bool longPress) {
868
- wakeDisplay();
916
+ if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
869
917
  lastInputTime = millis();
870
918
  if (uiState == UI_HOME) {
871
919
  if (homeCount > 0) {
@@ -905,7 +953,7 @@ void handleUp(bool longPress) {
905
953
  }
906
954
 
907
955
  void handleDown(bool longPress) {
908
- wakeDisplay();
956
+ if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
909
957
  lastInputTime = millis();
910
958
  if (uiState == UI_HOME) {
911
959
  if (homeCount > 0) {
@@ -1091,12 +1139,19 @@ void loop() {
1091
1139
  if (!displayBlank) drawStatus();
1092
1140
  }
1093
1141
 
1094
- // --- OLED idle blanking (HOME + STANDBY) ---
1142
+ // --- Idle disc screensaver (HOME + STANDBY) ---
1095
1143
  if (displayOk && !displayBlank &&
1096
1144
  (uiState == UI_HOME || uiState == UI_STANDBY) &&
1097
1145
  (long)(millis() - lastInputTime) >= IDLE_BLANK_MS) {
1098
- display.ssd1306_command(0xAE);
1099
1146
  displayBlank = true;
1147
+ saverStep = 0;
1148
+ lastSaverFrame = 0;
1149
+ }
1150
+ if (displayBlank && displayOk &&
1151
+ (long)(millis() - lastSaverFrame) >= SAVER_FRAME_MS) {
1152
+ lastSaverFrame = millis();
1153
+ drawDiscSaver(saverStep);
1154
+ saverStep = (saverStep + 1) % 24;
1100
1155
  }
1101
1156
 
1102
1157
  // --- PING timeout (disconnected) ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discstation",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "DiscStation optical-media appliance host — one cross-OS installer",
5
5
  "bin": {
6
6
  "discstation-setup": "scripts/setup.mjs",