3131 #ifndef FLD_PIN_SDA
3232 #define FLD_PIN_SDA 21
3333 #endif
34+ #ifndef FLD_PIN_CLOCKSPI
35+ #define FLD_PIN_CLOCKSPI 18
36+ #endif
37+ #ifndef FLD_PIN_DATASPI
38+ #define FLD_PIN_DATASPI 23
39+ #endif
40+ #ifndef FLD_PIN_DC
41+ #define FLD_PIN_DC 19
42+ #endif
43+ #ifndef FLD_PIN_CS
44+ #define FLD_PIN_CS 5
45+ #endif
46+ #ifndef FLD_PIN_RESET
47+ #define FLD_PIN_RESET 26
48+ #endif
3449#else
3550 #ifndef FLD_PIN_SCL
3651 #define FLD_PIN_SCL 5
3752 #endif
3853 #ifndef FLD_PIN_SDA
3954 #define FLD_PIN_SDA 4
4055 #endif
56+ #ifndef FLD_PIN_CLOCKSPI
57+ #define FLD_PIN_CLOCKSPI 14
58+ #endif
59+ #ifndef FLD_PIN_DATASPI
60+ #define FLD_PIN_DATASPI 13
61+ #endif
62+ #ifndef FLD_PIN_DC
63+ #define FLD_PIN_DC 12
64+ #endif
65+ #ifndef FLD_PIN_CS
66+ #define FLD_PIN_CS 15
67+ #endif
68+ #ifndef FLD_PIN_RESET
69+ #define FLD_PIN_RESET 16
70+ #endif
4171#endif
4272
4373// When to time out to the clock or blank the screen
@@ -64,11 +94,13 @@ typedef enum {
6494
6595typedef enum {
6696 NONE = 0 ,
67- SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
68- SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
69- SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
70- SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
71- SSD1305_64 // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
97+ SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
98+ SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
99+ SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
100+ SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
101+ SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
102+ SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
103+ SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
72104} DisplayType;
73105
74106class FourLineDisplayUsermod : public Usermod {
@@ -80,8 +112,13 @@ class FourLineDisplayUsermod : public Usermod {
80112
81113 // HW interface & configuration
82114 U8X8 *u8x8 = nullptr ; // pointer to U8X8 display object
83- int8_t sclPin=FLD_PIN_SCL, sdaPin=FLD_PIN_SDA; // I2C pins for interfacing, get initialised in readFromConfig()
115+ #ifndef FLD_SPI_DEFAULT
116+ int8_t ioPin[5 ] = {FLD_PIN_SCL, FLD_PIN_SDA, -1 , -1 , -1 }; // I2C pins: SCL, SDA
84117 DisplayType type = SSD1306; // display type
118+ #else
119+ int8_t ioPin[5 ] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
120+ DisplayType type = SSD1306_SPI; // display type
121+ #endif
85122 bool flip = false ; // flip display 180°
86123 uint8_t contrast = 10 ; // screen contrast
87124 uint8_t lineHeight = 1 ; // 1 row or 2 rows
@@ -131,53 +168,74 @@ class FourLineDisplayUsermod : public Usermod {
131168 // network here
132169 void setup () {
133170 if (type == NONE) return ;
134- if (!pinManager.allocatePin (sclPin)) { sclPin = -1 ; type = NONE; return ;}
135- if (!pinManager.allocatePin (sdaPin)) { pinManager.deallocatePin (sclPin); sclPin = sdaPin = -1 ; type = NONE; return ; }
171+ bool allocated = false ;
172+ byte i;
173+ if (type == SSD1306_SPI || type == SSD1306_SPI64) {
174+ for (i=0 ; i<5 ; i++) if (!pinManager.allocatePin (ioPin[i])) { allocated=true ; break ; }
175+ if (i<5 && allocated) { for (byte i=0 ; i<5 ; i++) pinManager.deallocatePin (ioPin[i]); type=NONE; return ; }
176+ } else {
177+ for (i=0 ; i<2 ; i++) if (!pinManager.allocatePin (ioPin[i])) { allocated=true ; break ; }
178+ if (i<2 && allocated) { for (byte i=0 ; i<5 ; i++) pinManager.deallocatePin (ioPin[i]); type=NONE; return ; }
179+ }
136180 DEBUG_PRINTLN (F (" Allocating display." ));
137181 switch (type) {
138182 case SSD1306:
139183 #ifdef ESP8266
140- if (!(sclPin ==5 && sdaPin ==4 ))
141- u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C (sclPin, sdaPin ); // SCL, SDA, reset
184+ if (!(ioPin[ 0 ] ==5 && ioPin[ 1 ] ==4 ))
185+ u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C (ioPin[ 0 ], ioPin[ 1 ] ); // SCL, SDA, reset
142186 else
143187 #endif
144- u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C (U8X8_PIN_NONE, sclPin, sdaPin ); // Pins are Reset, SCL, SDA
188+ u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C (U8X8_PIN_NONE, ioPin[ 0 ], ioPin[ 1 ] ); // Pins are Reset, SCL, SDA
145189 lineHeight = 1 ;
146190 break ;
147191 case SH1106:
148192 #ifdef ESP8266
149- if (!(sclPin ==5 && sdaPin ==4 ))
150- u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C (sclPin, sdaPin ); // SCL, SDA, reset
193+ if (!(ioPin[ 0 ] ==5 && ioPin[ 1 ] ==4 ))
194+ u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C (ioPin[ 0 ], ioPin[ 1 ] ); // SCL, SDA, reset
151195 else
152196 #endif
153- u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C (U8X8_PIN_NONE, sclPin, sdaPin ); // Pins are Reset, SCL, SDA
197+ u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C (U8X8_PIN_NONE, ioPin[ 0 ], ioPin[ 1 ] ); // Pins are Reset, SCL, SDA
154198 lineHeight = 2 ;
155199 break ;
156200 case SSD1306_64:
157201 #ifdef ESP8266
158- if (!(sclPin ==5 && sdaPin ==4 ))
159- u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C (sclPin, sdaPin ); // SCL, SDA, reset
202+ if (!(ioPin[ 0 ] ==5 && ioPin[ 1 ] ==4 ))
203+ u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C (ioPin[ 0 ], ioPin[ 1 ] ); // SCL, SDA, reset
160204 else
161205 #endif
162- u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C (U8X8_PIN_NONE, sclPin, sdaPin ); // Pins are Reset, SCL, SDA
206+ u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C (U8X8_PIN_NONE, ioPin[ 0 ], ioPin[ 1 ] ); // Pins are Reset, SCL, SDA
163207 lineHeight = 2 ;
164208 break ;
165209 case SSD1305:
166210 #ifdef ESP8266
167- if (!(sclPin ==5 && sdaPin ==4 ))
168- u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C (sclPin, sdaPin ); // SCL, SDA, reset
211+ if (!(ioPin[ 0 ] ==5 && ioPin[ 1 ] ==4 ))
212+ u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C (ioPin[ 0 ], ioPin[ 1 ] ); // SCL, SDA, reset
169213 else
170214 #endif
171- u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C (U8X8_PIN_NONE, sclPin, sdaPin ); // Pins are Reset, SCL, SDA
215+ u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C (U8X8_PIN_NONE, ioPin[ 0 ], ioPin[ 1 ] ); // Pins are Reset, SCL, SDA
172216 lineHeight = 1 ;
173217 break ;
174218 case SSD1305_64:
175219 #ifdef ESP8266
176- if (!(sclPin ==5 && sdaPin ==4 ))
177- u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C (sclPin, sdaPin ); // SCL, SDA, reset
220+ if (!(ioPin[ 0 ] ==5 && ioPin[ 1 ] ==4 ))
221+ u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C (ioPin[ 0 ], ioPin[ 1 ] ); // SCL, SDA, reset
178222 else
179223 #endif
180- u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C (U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
224+ u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C (U8X8_PIN_NONE, ioPin[0 ], ioPin[1 ]); // Pins are Reset, SCL, SDA
225+ lineHeight = 2 ;
226+ break ;
227+ case SSD1306_SPI:
228+ if (!(ioPin[0 ]==FLD_PIN_CLOCKSPI && ioPin[1 ]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
229+ u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI (ioPin[0 ], ioPin[1 ], ioPin[2 ], ioPin[3 ], ioPin[4 ]);
230+ else
231+ u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI (ioPin[2 ], ioPin[3 ], ioPin[4 ]); // Pins are cs, dc, reset
232+ lineHeight = 1 ;
233+ break ;
234+ case SSD1306_SPI64:
235+ if (!(ioPin[0 ]==FLD_PIN_CLOCKSPI && ioPin[1 ]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
236+ u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI (ioPin[0 ], ioPin[1 ], ioPin[2 ], ioPin[3 ], ioPin[4 ]);
237+ else
238+ u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI (ioPin[2 ], ioPin[3 ], ioPin[4 ]); // Pins are cs, dc, reset
181239 lineHeight = 2 ;
182240 break ;
183241 default :
@@ -255,6 +313,12 @@ class FourLineDisplayUsermod : public Usermod {
255313 (static_cast <U8X8*>(u8x8))->setPowerSave (save);
256314 }
257315
316+ void center (String &line, uint8_t width) {
317+ int len = line.length ();
318+ if (len<width) for (byte i=(width-len)/2 ; i>0 ; i--) line = ' ' + line;
319+ for (byte i=line.length (); i<width; i++) line += ' ' ;
320+ }
321+
258322 /* *
259323 * Redraw the screen (but only if things have changed
260324 * or if forceRedraw).
@@ -341,13 +405,14 @@ class FourLineDisplayUsermod : public Usermod {
341405 knownEffectIntensity = effectIntensity;
342406
343407 // Do the actual drawing
344-
408+ String line;
345409 // First row with Wifi name
346410 drawGlyph (0 , 0 , 80 , u8x8_font_open_iconic_embedded_1x1); // home icon
347- String ssidString = knownSsid.substring (0 , getCols () > 1 ? getCols () - 2 : 0 );
348- drawString (1 , 0 , ssidString.c_str ());
411+ line = knownSsid.substring (0 , getCols () > 1 ? getCols () - 2 : 0 );
412+ center (line, getCols ()-2 );
413+ drawString (1 , 0 , line.c_str ());
349414 // Print `~` char to indicate that SSID is longer, than our display
350- if (knownSsid.length () > getCols ()) {
415+ if (knownSsid.length () > getCols ()- 1 ) {
351416 drawString (getCols () - 1 , 0 , " ~" );
352417 }
353418
@@ -358,12 +423,12 @@ class FourLineDisplayUsermod : public Usermod {
358423 drawString (1 , lineHeight, apPass);
359424 } else {
360425 // alternate IP address and server name
361- String secondLine = knownIp.toString ();
426+ line = knownIp.toString ();
362427 if (showName && strcmp (serverDescription, " WLED" ) != 0 ) {
363- secondLine = serverDescription;
428+ line = serverDescription;
364429 }
365- for ( uint8_t i=secondLine. length (); i< getCols ()-1 ; i++) secondLine += ' ' ;
366- drawString (1 , lineHeight, secondLine .c_str ());
430+ center (line, getCols ()-1 ) ;
431+ drawString (1 , lineHeight, line .c_str ());
367432 }
368433
369434 // draw third and fourth row
@@ -605,10 +670,10 @@ class FourLineDisplayUsermod : public Usermod {
605670 * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
606671 */
607672 void addToConfig (JsonObject& root) {
608- JsonObject top = root.createNestedObject (FPSTR (_name));
609- JsonArray i2c_pin = top.createNestedArray (" pin" );
610- i2c_pin .add (sclPin );
611- i2c_pin. add (sdaPin);
673+ JsonObject top = root.createNestedObject (FPSTR (_name));
674+ JsonArray io_pin = top.createNestedArray (" pin" );
675+ for (byte i= 0 ; i< 5 ; i++) io_pin .add (ioPin[i] );
676+ top[ " help4PinTypes " ] = F ( " Clk,Data,CS,DC,RST " ); // help for Settings page
612677 top[" type" ] = type;
613678 top[FPSTR (_flip)] = (bool ) flip;
614679 top[FPSTR (_contrast)] = contrast;
@@ -630,8 +695,7 @@ class FourLineDisplayUsermod : public Usermod {
630695 bool readFromConfig (JsonObject& root) {
631696 bool needsRedraw = false ;
632697 DisplayType newType = type;
633- int8_t newScl = sclPin;
634- int8_t newSda = sdaPin;
698+ int8_t newPin[5 ]; for (byte i=0 ; i<5 ; i++) newPin[i] = ioPin[i];
635699
636700 JsonObject top = root[FPSTR (_name)];
637701 if (top.isNull ()) {
@@ -640,9 +704,8 @@ class FourLineDisplayUsermod : public Usermod {
640704 return false ;
641705 }
642706
643- newScl = top[" pin" ][0 ] | newScl;
644- newSda = top[" pin" ][1 ] | newSda;
645707 newType = top[" type" ] | newType;
708+ for (byte i=0 ; i<5 ; i++) newPin[i] = top[" pin" ][i] | ioPin[i];
646709 flip = top[FPSTR (_flip)] | flip;
647710 contrast = top[FPSTR (_contrast)] | contrast;
648711 refreshRate = (top[FPSTR (_refreshRate)] | refreshRate/1000 ) * 1000 ;
@@ -653,20 +716,21 @@ class FourLineDisplayUsermod : public Usermod {
653716 DEBUG_PRINT (FPSTR (_name));
654717 if (!initDone) {
655718 // first run: reading from cfg.json
656- sclPin = newScl;
657- sdaPin = newSda;
719+ for (byte i=0 ; i<5 ; i++) ioPin[i] = newPin[i];
658720 type = newType;
659721 DEBUG_PRINTLN (F (" config loaded." ));
660722 } else {
661723 DEBUG_PRINTLN (F (" config (re)loaded." ));
662724 // changing parameters from settings page
663- if (sclPin!=newScl || sdaPin!=newSda || type!=newType) {
725+ bool pinsChanged = false ;
726+ for (byte i=0 ; i<5 ; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true ; break ; }
727+ if (pinsChanged || type!=newType) {
664728 if (type != NONE) delete (static_cast <U8X8*>(u8x8));
665- pinManager. deallocatePin (sclPin);
666- pinManager.deallocatePin (sdaPin );
667- sclPin = newScl ;
668- sdaPin = newSda;
669- if (newScl <0 || newSda <0 ) {
729+ for (byte i= 0 ; i< 5 ; i++) {
730+ if (ioPin[i]>= 0 ) pinManager.deallocatePin (ioPin[i] );
731+ ioPin[i] = newPin[i] ;
732+ }
733+ if (ioPin[ 0 ] <0 || ioPin[ 1 ] <0 ) { // data & clock must be > -1
670734 type = NONE;
671735 return true ;
672736 } else type = newType;
@@ -678,7 +742,7 @@ class FourLineDisplayUsermod : public Usermod {
678742 if (needsRedraw && !wakeDisplay ()) redraw (true );
679743 }
680744 // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
681- return true ;
745+ return !(top[ " pin " ][ 2 ]). isNull () ;
682746 }
683747
684748 /*
0 commit comments