Added SSD1306 SPI display option to 4 Line Display

This commit is contained in:
Blaz Kristan 2021-08-20 23:58:09 +02:00
parent 50aeee288b
commit 530e8b39e5

View File

@ -31,6 +31,21 @@
#ifndef FLD_PIN_SDA #ifndef FLD_PIN_SDA
#define FLD_PIN_SDA 21 #define FLD_PIN_SDA 21
#endif #endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI 18
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI 23
#endif
#ifndef FLD_PIN_DC
#define FLD_PIN_DC 19
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS 5
#endif
#ifndef FLD_PIN_RESET
#define FLD_PIN_RESET 26
#endif
#else #else
#ifndef FLD_PIN_SCL #ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 5 #define FLD_PIN_SCL 5
@ -38,6 +53,21 @@
#ifndef FLD_PIN_SDA #ifndef FLD_PIN_SDA
#define FLD_PIN_SDA 4 #define FLD_PIN_SDA 4
#endif #endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI 14
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI 13
#endif
#ifndef FLD_PIN_DC
#define FLD_PIN_DC 12
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS 15
#endif
#ifndef FLD_PIN_RESET
#define FLD_PIN_RESET 16
#endif
#endif #endif
// When to time out to the clock or blank the screen // When to time out to the clock or blank the screen
@ -68,7 +98,9 @@ typedef enum {
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
SSD1305_64 // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
} DisplayType; } DisplayType;
class FourLineDisplayUsermod : public Usermod { class FourLineDisplayUsermod : public Usermod {
@ -80,8 +112,13 @@ class FourLineDisplayUsermod : public Usermod {
// HW interface & configuration // HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
int8_t sclPin=FLD_PIN_SCL, sdaPin=FLD_PIN_SDA; // I2C pins for interfacing, get initialised in readFromConfig() #ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
DisplayType type = SSD1306; // display type DisplayType type = SSD1306; // display type
#else
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
DisplayType type = SSD1306_SPI; // display type
#endif
bool flip = false; // flip display 180° bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast uint8_t contrast = 10; // screen contrast
uint8_t lineHeight = 1; // 1 row or 2 rows uint8_t lineHeight = 1; // 1 row or 2 rows
@ -131,53 +168,74 @@ class FourLineDisplayUsermod : public Usermod {
// network here // network here
void setup() { void setup() {
if (type == NONE) return; if (type == NONE) return;
if (!pinManager.allocatePin(sclPin)) { sclPin = -1; type = NONE; return;} bool allocated = false;
if (!pinManager.allocatePin(sdaPin)) { pinManager.deallocatePin(sclPin); sclPin = sdaPin = -1; type = NONE; return; } byte i;
if (type == SSD1306_SPI || type == SSD1306_SPI64) {
for (i=0; i<5; i++) if (!pinManager.allocatePin(ioPin[i])) { allocated=true; break; }
if (i<5 && allocated) { for (byte i=0; i<5; i++) pinManager.deallocatePin(ioPin[i]); type=NONE; return; }
} else {
for (i=0; i<2; i++) if (!pinManager.allocatePin(ioPin[i])) { allocated=true; break; }
if (i<2 && allocated) { for (byte i=0; i<5; i++) pinManager.deallocatePin(ioPin[i]); type=NONE; return; }
}
DEBUG_PRINTLN(F("Allocating display.")); DEBUG_PRINTLN(F("Allocating display."));
switch (type) { switch (type) {
case SSD1306: case SSD1306:
#ifdef ESP8266 #ifdef ESP8266
if (!(sclPin==5 && sdaPin==4)) if (!(ioPin[0]==5 && ioPin[1]==4))
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else else
#endif #endif
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 1; lineHeight = 1;
break; break;
case SH1106: case SH1106:
#ifdef ESP8266 #ifdef ESP8266
if (!(sclPin==5 && sdaPin==4)) if (!(ioPin[0]==5 && ioPin[1]==4))
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else else
#endif #endif
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2; lineHeight = 2;
break; break;
case SSD1306_64: case SSD1306_64:
#ifdef ESP8266 #ifdef ESP8266
if (!(sclPin==5 && sdaPin==4)) if (!(ioPin[0]==5 && ioPin[1]==4))
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else else
#endif #endif
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2; lineHeight = 2;
break; break;
case SSD1305: case SSD1305:
#ifdef ESP8266 #ifdef ESP8266
if (!(sclPin==5 && sdaPin==4)) if (!(ioPin[0]==5 && ioPin[1]==4))
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else else
#endif #endif
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 1; lineHeight = 1;
break; break;
case SSD1305_64: case SSD1305_64:
#ifdef ESP8266 #ifdef ESP8266
if (!(sclPin==5 && sdaPin==4)) if (!(ioPin[0]==5 && ioPin[1]==4))
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else else
#endif #endif
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2;
break;
case SSD1306_SPI:
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
lineHeight = 1;
break;
case SSD1306_SPI64:
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
lineHeight = 2; lineHeight = 2;
break; break;
default: default:
@ -255,6 +313,12 @@ class FourLineDisplayUsermod : public Usermod {
(static_cast<U8X8*>(u8x8))->setPowerSave(save); (static_cast<U8X8*>(u8x8))->setPowerSave(save);
} }
void center(String &line, uint8_t width) {
int len = line.length();
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
for (byte i=line.length(); i<width; i++) line += ' ';
}
/** /**
* Redraw the screen (but only if things have changed * Redraw the screen (but only if things have changed
* or if forceRedraw). * or if forceRedraw).
@ -341,13 +405,14 @@ class FourLineDisplayUsermod : public Usermod {
knownEffectIntensity = effectIntensity; knownEffectIntensity = effectIntensity;
// Do the actual drawing // Do the actual drawing
String line;
// First row with Wifi name // First row with Wifi name
drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // home icon drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // home icon
String ssidString = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0); line = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0);
drawString(1, 0, ssidString.c_str()); center(line, getCols()-2);
drawString(1, 0, line.c_str());
// Print `~` char to indicate that SSID is longer, than our display // Print `~` char to indicate that SSID is longer, than our display
if (knownSsid.length() > getCols()) { if (knownSsid.length() > getCols()-1) {
drawString(getCols() - 1, 0, "~"); drawString(getCols() - 1, 0, "~");
} }
@ -358,12 +423,12 @@ class FourLineDisplayUsermod : public Usermod {
drawString(1, lineHeight, apPass); drawString(1, lineHeight, apPass);
} else { } else {
// alternate IP address and server name // alternate IP address and server name
String secondLine = knownIp.toString(); line = knownIp.toString();
if (showName && strcmp(serverDescription, "WLED") != 0) { if (showName && strcmp(serverDescription, "WLED") != 0) {
secondLine = serverDescription; line = serverDescription;
} }
for (uint8_t i=secondLine.length(); i<getCols()-1; i++) secondLine += ' '; center(line, getCols()-1);
drawString(1, lineHeight, secondLine.c_str()); drawString(1, lineHeight, line.c_str());
} }
// draw third and fourth row // draw third and fourth row
@ -606,9 +671,9 @@ class FourLineDisplayUsermod : public Usermod {
*/ */
void addToConfig(JsonObject& root) { void addToConfig(JsonObject& root) {
JsonObject top = root.createNestedObject(FPSTR(_name)); JsonObject top = root.createNestedObject(FPSTR(_name));
JsonArray i2c_pin = top.createNestedArray("pin"); JsonArray io_pin = top.createNestedArray("pin");
i2c_pin.add(sclPin); for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
i2c_pin.add(sdaPin); top["help4PinTypes"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
top["type"] = type; top["type"] = type;
top[FPSTR(_flip)] = (bool) flip; top[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast; top[FPSTR(_contrast)] = contrast;
@ -630,8 +695,7 @@ class FourLineDisplayUsermod : public Usermod {
bool readFromConfig(JsonObject& root) { bool readFromConfig(JsonObject& root) {
bool needsRedraw = false; bool needsRedraw = false;
DisplayType newType = type; DisplayType newType = type;
int8_t newScl = sclPin; int8_t newPin[5]; for (byte i=0; i<5; i++) newPin[i] = ioPin[i];
int8_t newSda = sdaPin;
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) { if (top.isNull()) {
@ -640,9 +704,8 @@ class FourLineDisplayUsermod : public Usermod {
return false; return false;
} }
newScl = top["pin"][0] | newScl;
newSda = top["pin"][1] | newSda;
newType = top["type"] | newType; newType = top["type"] | newType;
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
flip = top[FPSTR(_flip)] | flip; flip = top[FPSTR(_flip)] | flip;
contrast = top[FPSTR(_contrast)] | contrast; contrast = top[FPSTR(_contrast)] | contrast;
refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000; refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000;
@ -653,20 +716,21 @@ class FourLineDisplayUsermod : public Usermod {
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
// first run: reading from cfg.json // first run: reading from cfg.json
sclPin = newScl; for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
sdaPin = newSda;
type = newType; type = newType;
DEBUG_PRINTLN(F(" config loaded.")); DEBUG_PRINTLN(F(" config loaded."));
} else { } else {
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page // changing parameters from settings page
if (sclPin!=newScl || sdaPin!=newSda || type!=newType) { bool pinsChanged = false;
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
if (pinsChanged || type!=newType) {
if (type != NONE) delete (static_cast<U8X8*>(u8x8)); if (type != NONE) delete (static_cast<U8X8*>(u8x8));
pinManager.deallocatePin(sclPin); for (byte i=0; i<5; i++) {
pinManager.deallocatePin(sdaPin); if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i]);
sclPin = newScl; ioPin[i] = newPin[i];
sdaPin = newSda; }
if (newScl<0 || newSda<0) { if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
type = NONE; type = NONE;
return true; return true;
} else type = newType; } else type = newType;
@ -678,7 +742,7 @@ class FourLineDisplayUsermod : public Usermod {
if (needsRedraw && !wakeDisplay()) redraw(true); if (needsRedraw && !wakeDisplay()) redraw(true);
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true; return !(top["pin"][2]).isNull();
} }
/* /*