@@ -72,23 +72,19 @@ class LGFX_ILI9342_M5StackS3 : public lgfx::LGFX_Device {
7272
7373static LGFX_ILI9342_M5StackS3 tft;
7474
75- extern int getSignalQuality (int rssi);
76-
7775
7876// class name. Use something descriptive and leave the ": public Usermod" part :)
7977class M5StackCoreS3DisplayUsermod : public Usermod {
8078 private:
8179 bool enabled = true ;
8280
83- bool displayTurnedOff = false ;
84- long lastRedraw = 0 ;
8581 // needRedraw marks if redraw is required to prevent often redrawing.
8682 bool needRedraw = true ;
8783 // Next variables hold the previous known values to determine if redraw is required.
8884 String knownSsid = " " ;
8985 IPAddress knownIp;
9086
91- long lastUpdate = 0 ;
87+ unsigned long lastUpdate = 0 ;
9288
9389 // GEQ display constants
9490 static constexpr uint8_t NUM_GEQ_BANDS = 16 ;
@@ -102,19 +98,19 @@ class M5StackCoreS3DisplayUsermod : public Usermod {
10298 case 0 : return 0xF800 ; // red
10399 case 1 : return 0xFC00 ; // orange
104100 case 2 : return 0xFC40 ; // amber
105- case 3 : return 0xFCC0 ; // yellow-orange
106- case 4 : return 0xFFE0 ; // yellow
107- case 5 : return 0xC700 ; // yellow- green
108- case 6 : return 0x07E0 ; // green
109- case 7 : return 0x07EF ; // green-cyan
110- case 8 : return 0x07FF ; // cyan
111- case 9 : return 0x041F ; // cyan-blue
112- case 10 : return 0x001F ; // blue
113- case 11 : return 0x281F ; // blue-indigo
114- case 12 : return 0x601F ; // indigo
115- case 13 : return 0x781F ; // violet
116- case 14 : return 0xF81F ; // magenta
117- case 15 : return 0xF81F ; // magenta
101+ case 3 : return 0xFFC0 ; // yellow
102+ case 4 : return 0x07E0 ; // green
103+ case 5 : return 0x07EF ; // green-cyan
104+ case 6 : return 0x07FF ; // cyan
105+ case 7 : return 0x001F ; // blue
106+ case 8 : return 0x281F ; // blue-indigo
107+ case 9 : return 0x481F ; // indigo
108+ case 10 : return 0x701F ; // violet
109+ case 11 : return 0x881F ; // violet-magenta
110+ case 12 : return 0xF81F ; // magenta
111+ case 13 : return 0xF81C ; // magenta-red
112+ case 14 : return 0xF800 ; // red
113+ case 15 : return 0xF808 ; // red-orange
118114 default : return 0xF800 ;
119115 }
120116 }
@@ -130,11 +126,13 @@ class M5StackCoreS3DisplayUsermod : public Usermod {
130126 {
131127 Serial.println (" M5StackS3Display: starting setup" );
132128
133- // Ensure Wire is initialized
129+ // Ensure Wire is initialized (safe to call even if WLED already started it)
134130 if (i2c_sda >= 0 && i2c_scl >= 0 ) {
135131 Wire.begin (i2c_sda, i2c_scl);
136- Wire.setTimeout (50 );
132+ } else {
133+ Wire.begin ();
137134 }
135+ Wire.setTimeout (50 );
138136
139137 // AXP2101 - enable DLDO1 (VCC_BL per user)
140138 // Read current state first
@@ -217,6 +215,19 @@ class M5StackCoreS3DisplayUsermod : public Usermod {
217215 Serial.println (" M5StackS3Display: init TFT" );
218216 tft.init ();
219217
218+ // Lock SPI pins so they can't be reassigned to other usermods
219+ PinManagerPinType pins[] = {
220+ { (gpio_num_t )TFT_MOSI, true },
221+ { (gpio_num_t )TFT_SCLK, true },
222+ { (gpio_num_t )TFT_CS, true },
223+ { (gpio_num_t )TFT_DC, true }
224+ };
225+ if (pinManager.allocateMultiplePins (pins, 4 , PinOwner::UM_Unspecified)) {
226+ Serial.println (" M5StackS3Display: SPI pins allocated" );
227+ } else {
228+ Serial.println (" M5StackS3Display: SPI pin allocation FAILED" );
229+ }
230+
220231 Serial.printf (" M5StackS3Display: TFT width: %d, height: %d\n " , tft.width (), tft.height ());
221232
222233 // Dynamic header sizing
@@ -243,59 +254,54 @@ class M5StackCoreS3DisplayUsermod : public Usermod {
243254 * loop() is called continuously. Here you can check for events, read sensors, etc.
244255 */
245256 void loop () {
246- // Faster refresh for bouncing bars
247- if (millis () - lastUpdate < 100 ) // 50ms = 20fps
248- {
257+ if (!enabled) return ;
258+
259+ unsigned long now = millis ();
260+
261+ // Standard max framerate cap (~100 FPS, matches audio reactive update rate)
262+ if (now - lastUpdate < 10 ) return ;
263+
264+ // Fallback: if LEDs are hogging CPU, only block if we refreshed recently enough
265+ // Otherwise punch through to guarantee minimum 5 FPS on display
266+ if (strip.isUpdating () && (now - lastUpdate < 200 )) {
249267 return ;
250268 }
251- lastUpdate = millis ();
252269
253- // Turn off display after 5 minutes with no change.
254- if (!displayTurnedOff && millis () - lastRedraw > 5 *60 *1000 )
255- {
256- displayTurnedOff = true ;
257- }
270+ lastUpdate = now;
258271
259- // Check if values which are shown on display changed from the last time.
260- #if defined(ESP8266)
272+ // Skip content/header updates while LEDs are being updated
273+ if (!strip.isUpdating ()) {
274+ // Check if values which are shown on display changed from the last time.
261275 String currentSsid = apActive ? String (apSSID) : WiFi.SSID ();
262- #else
263- String currentSsid = WiFi.SSID ();
264- #endif
265- IPAddress currentIp = apActive ? IPAddress (4 , 3 , 2 , 1 ) : Network.localIP ();
266-
267- if (currentSsid != knownSsid || currentIp != knownIp)
268- {
269- needRedraw = true ;
270- knownSsid = currentSsid;
271- knownIp = currentIp;
272- }
276+ IPAddress currentIp = apActive ? IPAddress (4 , 3 , 2 , 1 ) : Network.localIP ();
273277
274- if (displayTurnedOff)
275- {
276- displayTurnedOff = false ;
277- }
278- lastRedraw = millis ();
278+ if (currentSsid != knownSsid || currentIp != knownIp)
279+ {
280+ needRedraw = true ;
281+ knownSsid = currentSsid;
282+ knownIp = currentIp;
283+ }
279284
280- // === HEADER: Only redraw when needed ===
281- if (needRedraw) {
282- // Gray background
283- tft.fillRect (0 , 0 , tft.width (), HEADER_HEIGHT, TFT_DARKGREY);
285+ // === HEADER: Only redraw when needed ===
286+ if (needRedraw) {
287+ // Gray background
288+ tft.fillRect (0 , 0 , tft.width (), HEADER_HEIGHT, TFT_DARKGREY);
284289
285- // Text vertically centered in header
286- int16_t textY = (HEADER_HEIGHT - tft.fontHeight ()) / 2 ;
287- tft.setTextSize (2 );
288- tft.setTextColor (TFT_WHITE);
290+ // Text vertically centered in header
291+ int16_t textY = (HEADER_HEIGHT - tft.fontHeight ()) / 2 ;
292+ tft.setTextSize (2 );
293+ tft.setTextColor (TFT_WHITE);
289294
290- // SSID on left
291- tft.setTextDatum (TL_DATUM);
292- tft.drawString (knownSsid.c_str (), 10 , textY);
295+ // SSID on left
296+ tft.setTextDatum (TL_DATUM);
297+ tft.drawString (knownSsid.c_str (), 10 , textY);
293298
294- // IP on right
295- tft.setTextDatum (TR_DATUM);
296- tft.drawString (knownIp.toString ().c_str (), tft.width () - 10 , textY);
299+ // IP on right
300+ tft.setTextDatum (TR_DATUM);
301+ tft.drawString (knownIp.toString ().c_str (), tft.width () - 10 , textY);
297302
298- needRedraw = false ;
303+ needRedraw = false ;
304+ }
299305 }
300306
301307 // === 16 BOUNCING BARS - differential drawing ===
@@ -369,20 +375,12 @@ class M5StackCoreS3DisplayUsermod : public Usermod {
369375 void addToConfig (JsonObject& root)
370376 {
371377 JsonObject top = root.createNestedObject (" M5StackS3Display" );
372- top[" CS" ] = TFT_CS;
373- top[" DC" ] = TFT_DC;
374- top[" MOSI" ] = TFT_MOSI;
375- top[" SCLK" ] = TFT_SCLK;
376378 top[" enabled" ] = enabled;
377379 }
378380
379381 void appendConfigData ()
380382 {
381383 oappend (SET_F (" addHB('M5StackS3Display');" ));
382- oappend (SET_F (" addInfo('M5StackS3Display:CS',0,'SPI Chip Select (GPIO3)');" ));
383- oappend (SET_F (" addInfo('M5StackS3Display:DC',0,'Data/Command (GPIO35)');" ));
384- oappend (SET_F (" addInfo('M5StackS3Display:MOSI',0,'SPI MOSI (GPIO37)');" ));
385- oappend (SET_F (" addInfo('M5StackS3Display:SCLK',0,'SPI Clock (GPIO36)');" ));
386384 }
387385
388386 /*
0 commit comments