From 16e397dbd74ee1007ee2b91781f1d5b71ee90307 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sun, 11 Apr 2021 12:30:50 +0200 Subject: [PATCH 1/4] udisplay lib --- .../Adafruit_GFX.cpp | 10 + .../Adafruit_GFX.h | 4 + lib/lib_display/UDisplay/keywords.txt | 30 + lib/lib_display/UDisplay/library.properties | 9 + lib/lib_display/UDisplay/uDisplay.cpp | 607 ++++++++++++++++++ lib/lib_display/UDisplay/uDisplay.h | 114 ++++ 6 files changed, 774 insertions(+) create mode 100644 lib/lib_display/UDisplay/keywords.txt create mode 100644 lib/lib_display/UDisplay/library.properties create mode 100644 lib/lib_display/UDisplay/uDisplay.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay.h diff --git a/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp index dbfd00e2d..5e88150cc 100644 --- a/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp +++ b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp @@ -1243,6 +1243,16 @@ void Adafruit_GFX::setTextSize(uint8_t s_x, uint8_t s_y) { textsize_y = (s_y > 0) ? s_y : 1; } +void Adafruit_GFX::setwidth(uint16_t w) { + WIDTH = w; + _width = w; +} + +void Adafruit_GFX::setheight(uint16_t h) { + HEIGHT = h; + _height = h; +} + /**************************************************************************/ /*! @brief Set rotation setting for display diff --git a/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.h index dc970b26d..8af31324e 100644 --- a/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.h +++ b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.h @@ -187,6 +187,10 @@ virtual void /************************************************************************/ int16_t height(void) const { return _height; } + void setwidth(uint16_t w); + + void setheight(uint16_t h); + /************************************************************************/ /*! @brief Get rotation setting for display diff --git a/lib/lib_display/UDisplay/keywords.txt b/lib/lib_display/UDisplay/keywords.txt new file mode 100644 index 000000000..bb1efcdcc --- /dev/null +++ b/lib/lib_display/UDisplay/keywords.txt @@ -0,0 +1,30 @@ +####################################### +# Syntax Coloring Map +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ST7789 KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setRotation KEYWORD2 +setAddrWindow KEYWORD2 +pushColor KEYWORD2 +drawPixel KEYWORD2 +drawFastVLine KEYWORD2 +drawFastHLine KEYWORD2 +fillRect KEYWORD2 +setRotation KEYWORD2 +setRotation KEYWORD2 +height KEYWORD2 +width KEYWORD2 +invertDisplay KEYWORD2 +drawImage KEYWORD2 +setScrollArea KEYWORD2 +scroll KEYWORD2 diff --git a/lib/lib_display/UDisplay/library.properties b/lib/lib_display/UDisplay/library.properties new file mode 100644 index 000000000..c7dd23a3a --- /dev/null +++ b/lib/lib_display/UDisplay/library.properties @@ -0,0 +1,9 @@ +name=universal display Library +version=0.1 +author=Gerhard Mutz +maintainer=Gerhard Mutz +sentence=This is a library a couple of displays. +paragraph=This is a library a couple of displays. +category=Display +url=https://github.com/arendst/Tasmota +architectures=* diff --git a/lib/lib_display/UDisplay/uDisplay.cpp b/lib/lib_display/UDisplay/uDisplay.cpp new file mode 100644 index 000000000..0c15c8712 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay.cpp @@ -0,0 +1,607 @@ +/* + uDisplay.cpp - universal display driver support for Tasmota + + Copyright (C) 2021 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include "uDisplay.h" + + +const uint16_t udisp_colors[]={UDISP_BLACK,UDISP_WHITE,UDISP_RED,UDISP_GREEN,UDISP_BLUE,UDISP_CYAN,UDISP_MAGENTA,\ + UDISP_YELLOW,UDISP_NAVY,UDISP_DARKGREEN,UDISP_DARKCYAN,UDISP_MAROON,UDISP_PURPLE,UDISP_OLIVE,\ +UDISP_LIGHTGREY,UDISP_DARKGREY,UDISP_ORANGE,UDISP_GREENYELLOW,UDISP_PINK}; + +uint16_t uDisplay::GetColorFromIndex(uint8_t index) { + if (index >= sizeof(udisp_colors) / 2) index = 0; + return udisp_colors[index]; +} + +extern uint8_t *buffer; + +uDisplay::uDisplay(char *lp) : Renderer(800, 600) { + // analyse decriptor + uint8_t section = 0; + dsp_ncmds = 0; + char linebuff[128]; + while (*lp) { + + uint16_t llen = strlen_ln(lp); + strncpy(linebuff, lp, llen); + linebuff[llen] = 0; + lp += llen; + char *lp1 = linebuff; + + if (*lp1 == '#') break; + if (*lp1 == '\n') lp1++; + while (*lp1 == ' ') lp1++; + //Serial.printf(">> %s\n",lp1); + if (*lp1 != ';') { + // check ids: + if (*lp1 == ':') { + // id line + lp1++; + section = *lp1++; + } else { + switch (section) { + case 'H': + // header line + // SD1306,128,64,1,I2C,5a,*,*,* + str2c(&lp1, dname, sizeof(dname)); + char ibuff[16]; + gxs = next_val(&lp1); + setwidth(gxs); + gys = next_val(&lp1); + setheight(gys); + bpp = next_val(&lp1); + str2c(&lp1, ibuff, sizeof(ibuff)); + if (!strncmp(ibuff, "I2C", 3)) { + interface = _UDSP_I2C; + i2caddr = next_hex(&lp1); + i2c_scl = next_val(&lp1); + i2c_sda = next_val(&lp1); + reset = next_val(&lp1); + section = 0; + } else if (!strncmp(ibuff, "SPI", 3)) { + interface = _UDSP_SPI; + spi_nr = next_val(&lp1); + spi_cs = next_val(&lp1); + spi_clk = next_val(&lp1); + spi_mosi = next_val(&lp1); + spi_dc = next_val(&lp1); + bpanel = next_val(&lp1); + reset = next_val(&lp1); + spi_miso = next_val(&lp1); + spi_speed = next_val(&lp1); + + section = 0; + Serial.printf("%d %d %d %d %d %d %d %d\n", spi_cs, spi_clk, spi_mosi, spi_dc, bpanel, reset, spi_miso, spi_speed); + } + break; + case 'S': + splash_font = next_val(&lp1); + splash_size = next_val(&lp1); + fg_col = next_val(&lp1); + if (bpp == 16) { + fg_col = GetColorFromIndex(fg_col); + } + bg_col = next_val(&lp1); + if (bpp == 16) { + bg_col = GetColorFromIndex(bg_col); + } + splash_xp = next_val(&lp1); + splash_yp = next_val(&lp1); + break; + case 'I': + // init data + if (interface == _UDSP_I2C) { + dsp_cmds[dsp_ncmds++] = next_hex(&lp1); + if (!str2c(&lp1, ibuff, sizeof(ibuff))) { + dsp_cmds[dsp_ncmds++] = strtol(ibuff, 0, 16); + } + } else { + while (1) { + if (!str2c(&lp1, ibuff, sizeof(ibuff))) { + dsp_cmds[dsp_ncmds++] = strtol(ibuff, 0, 16); + } else { + break; + } + if (dsp_ncmds >= sizeof(dsp_cmds)) break; + + } + } + break; + case 'o': + str2c(&lp1, ibuff, sizeof(ibuff)); + dsp_off = strtol(ibuff, 0, 16); + break; + case 'O': + str2c(&lp1, ibuff, sizeof(ibuff)); + dsp_on = strtol(ibuff, 0, 16); + break; + case '0': + rot_0 = next_hex(&lp1); + break; + case '1': + rot_1 = next_hex(&lp1); + break; + case '2': + rot_2 = next_hex(&lp1); + break; + case '3': + rot_3 = next_hex(&lp1); + break; + case 'A': + saw_1 = next_hex(&lp1); + saw_2 = next_hex(&lp1); + saw_3 = next_hex(&lp1); + break; + } + } + } + if (*lp == '\n') { + lp++; + } else { + lp = strchr(lp, '\n'); + if (!lp) break; + lp++; + } + } +} + + +Renderer *uDisplay::Init(void) { + + + if (reset >= 0) { + pinMode(reset, OUTPUT); + digitalWrite(reset, HIGH); + delay(50); + digitalWrite(reset, LOW); + delay(50); + digitalWrite(reset, HIGH); + delay(200); + } + + if (interface == _UDSP_I2C) { + Wire.begin(i2c_sda, i2c_scl); + if (bpp < 16) { + if (buffer) free(buffer); + buffer = (uint8_t*)calloc((width()*height()*bpp)/8, 1); + + for (uint32_t cnt = 0; cnt < dsp_ncmds; cnt++) { + i2c_command(dsp_cmds[cnt]); + } + } + } + if (interface == _UDSP_SPI) { + if (bpanel >= 0) { +#ifdef ESP32 + ledcSetup(ESP32_PWM_CHANNEL, 4000, 8); + ledcAttachPin(bpanel, ESP32_PWM_CHANNEL); + ledcWrite(ESP32_PWM_CHANNEL, 128); +#else + pinMode(bpanel, OUTPUT); + digitalWrite(bpanel, HIGH); +#endif // ESP32 + } + if (spi_dc >= 0) { + pinMode(spi_dc, OUTPUT); + digitalWrite(spi_dc, HIGH); + } + if (spi_cs >= 0) { + pinMode(spi_cs, OUTPUT); + digitalWrite(spi_cs, HIGH); + } + + spiSettings = SPISettings(spi_speed, MSBFIRST, SPI_MODE3); + +#ifdef ESP8266 + SPI.begin(); + uspi = &SPI; +#else + if (spi_nr != 1) { + uspi = new SPIClass(HSPI); + } else { + uspi = &SPI; + } + uspi->begin(spi_clk, spi_miso, spi_mosi, -1); +#endif + + uint16_t index = 0; + + SPI_BEGIN_TRANSACTION + while (1) { + uint8_t iob; + SPI_CS_LOW + SPI_DC_LOW + iob = dsp_cmds[index++]; + uspi->write(iob); + SPI_DC_HIGH + uint8_t args = dsp_cmds[index++]; + //Serial.printf("cmd, args %x, %d ", iob, args&0x7f); + for (uint32_t cnt = 0; cnt < (args & 0x7f); cnt++) { + iob = dsp_cmds[index++]; + //Serial.printf("%02x ", iob ); + uspi->write(iob); + } + SPI_CS_HIGH + //Serial.printf("\n"); + if (args & 0x80) delay(120); + if (index >= dsp_ncmds) break; + } + SPI_END_TRANSACTION + + } + return this; +} + +void uDisplay::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { + setRotation(rot); + invertDisplay(false); + setTextWrap(false); + cp437(true); + setTextFont(font); + setTextSize(size); + setTextColor(fg_col, bg_col); + setCursor(0,0); + fillScreen(bg_col); + Updateframe(); +} + +void uDisplay::spi_command(uint8_t val) { + SPI_BEGIN_TRANSACTION + SPI_DC_LOW + SPI_CS_LOW + uspi->write(val); + SPI_CS_HIGH + SPI_DC_HIGH + SPI_END_TRANSACTION +} + +void uDisplay::i2c_command(uint8_t val) { + //Serial.printf("%02x\n",val ); + Wire.beginTransmission(i2caddr); + Wire.write(0); + Wire.write(val); + Wire.endTransmission(); +} + +#define SH1106_SETLOWCOLUMN 0 +#define SH1106_SETHIGHCOLUMN 0x10 +#define SH1106_SETSTARTLINE 0x40 + + +void uDisplay::Updateframe(void) { + + if (interface == _UDSP_I2C) { + i2c_command(SH1106_SETLOWCOLUMN | 0x0); // low col = 0 + i2c_command(SH1106_SETHIGHCOLUMN | 0x0); // hi col = 0 + i2c_command(SH1106_SETSTARTLINE | 0x0); // line #0 + + uint8_t ys = gys >> 3; + uint8_t xs = gxs >> 3; + //uint8_t xs = 132 >> 3; + uint8_t m_row = 0; + uint8_t m_col = 2; + + uint16_t p = 0; + + uint8_t i, j, k = 0; + + for ( i = 0; i < ys; i++) { + // send a bunch of data in one xmission + i2c_command(0xB0 + i + m_row);//set page address + i2c_command(m_col & 0xf);//set lower column address + i2c_command(0x10 | (m_col >> 4));//set higher column address + + for( j = 0; j < 8; j++){ + Wire.beginTransmission(i2caddr); + Wire.write(0x40); + for ( k = 0; k < xs; k++, p++) { + Wire.write(buffer[p]); + } + Wire.endTransmission(); + } + } + } +} + +void uDisplay::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { + + if (interface != _UDSP_SPI) { + Renderer::drawFastVLine(x, y, h, color); + return; + } + // Rudimentary clipping + if ((x >= _width) || (y >= _height)) return; + if ((y + h - 1) >= _height) h = _height - y; + + SPI_BEGIN_TRANSACTION + + SPI_CS_LOW + + setAddrWindow_int(x, y, 1, h); + + while (h--) { + uspi->write16(color); + } + + SPI_CS_HIGH + + SPI_END_TRANSACTION +} + +void uDisplay::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { + + if (interface != _UDSP_SPI) { + Renderer::drawFastHLine(x, y, w, color); + return; + } + + // Rudimentary clipping + if((x >= _width) || (y >= _height)) return; + if((x+w-1) >= _width) w = _width-x; + + + SPI_BEGIN_TRANSACTION + + SPI_CS_LOW + + setAddrWindow_int(x, y, w, 1); + + + while (w--) { + uspi->write16(color); + } + + SPI_CS_HIGH + + SPI_END_TRANSACTION +} + +void uDisplay::fillScreen(uint16_t color) { + fillRect(0, 0, gxs, gys, color); +} + +// fill a rectangle +void uDisplay::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + + if (interface != _UDSP_SPI) { + Renderer::fillRect(x, y, w, h, color); + return; + } + + // rudimentary clipping (drawChar w/big text requires this) + if((x >= gxs) || (y >= gys)) return; + if((x + w - 1) >= gxs) w = gxs - x; + if((y + h - 1) >= gys) h = gys - y; + + + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + + setAddrWindow_int(x, y, w, h); + + for (y = h; y > 0; y--) { + for (x = w; x > 0; x--) { + uspi->write16(color); + } + } + SPI_CS_HIGH + SPI_END_TRANSACTION +} + + +void uDisplay::Splash(void) { + setTextFont(splash_font); + setTextSize(splash_size); + DrawStringAt(splash_xp, splash_yp, dname, fg_col, 0); + Updateframe(); +} + +void uDisplay::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + + if (!x0 && !y0 && !x1 && !y1) { + SPI_CS_HIGH + SPI_END_TRANSACTION + } else { + SPI_CS_LOW + SPI_BEGIN_TRANSACTION + setAddrWindow_int(x0, y0, x1 - x0, y1 - y0 ); + } +} + +void uDisplay::setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { + uint32_t xa = ((uint32_t)x << 16) | (x+w-1); + uint32_t ya = ((uint32_t)y << 16) | (y+h-1); + + SPI_DC_LOW + uspi->write(saw_1); + SPI_DC_HIGH + + uspi->write32(xa); + + SPI_DC_LOW + uspi->write(saw_2); + SPI_DC_HIGH + + uspi->write32(ya); + + SPI_DC_LOW + uspi->write(saw_3); // write to RAM + SPI_DC_HIGH + +} + +void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean first) { + uint16_t color; + + while (len--) { + color = *data++; + uspi->write16(color); + } + +} + +void uDisplay::drawPixel(int16_t x, int16_t y, uint16_t color) { + + if (interface != _UDSP_SPI) { + Renderer::drawPixel(x, y, color); + return; + } + + if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; + + + SPI_BEGIN_TRANSACTION + + SPI_CS_LOW + + setAddrWindow_int(x, y, 1, 1); + + uspi->write16(color); + + SPI_CS_HIGH + + SPI_END_TRANSACTION +} + +void uDisplay::setRotation(uint8_t m) { + if (interface != _UDSP_SPI) { + Renderer::setRotation(m); + return; + } + switch (rotation) { + case 0: + if (interface == _UDSP_SPI) spi_command(rot_0); + _width = gxs; + _height = gys; + break; + case 1: + if (interface == _UDSP_SPI) spi_command(rot_1); + _width = gys; + _height = gxs; + break; + case 2: + if (interface == _UDSP_SPI) spi_command(rot_2); + _width = gxs; + _height = gys; + break; + case 3: + if (interface == _UDSP_SPI) spi_command(rot_3); + _width = gys; + _height = gxs; + break; + } +} + +void uDisplay::DisplayOnff(int8_t on) { + + if (interface == _UDSP_I2C) { + if (on) { + i2c_command(dsp_on); + } else { + i2c_command(dsp_off); + } + } else { + if (on) { + spi_command(dsp_on); + if (bpanel >= 0) { +#ifdef ESP32 + ledcWrite(ESP32_PWM_CHANNEL, dimmer); +#else + digitalWrite(bpanel, HIGH); +#endif + } + + } else { + spi_command(dsp_off); + if (bpanel >= 0) { +#ifdef ESP32 + ledcWrite(ESP32_PWM_CHANNEL, 0); +#else + digitalWrite(bpanel, LOW); +#endif + } + } + } +} + +void uDisplay::dim(uint8_t dim) { + dimmer = dim; + if (dimmer > 15) dimmer = 15; + dimmer = ((float)dimmer / 15.0) * 255.0; +#ifdef ESP32 + ledcWrite(ESP32_PWM_CHANNEL, dimmer); +#endif +} + + + +uint8_t uDisplay::strlen_ln(char *str) { + for (uint32_t cnt = 0; cnt < 256; cnt++) { + if (!str[cnt] || str[cnt] == '\n') return cnt; + } + return 0; +} + +char *uDisplay::devname(void) { + return dname; +} + +uint32_t uDisplay::str2c(char **sp, char *vp, uint32_t len) { + char *lp = *sp; + if (len) len--; + char *cp = strchr(lp, ','); + if (cp) { + while (1) { + if (*lp == ',') { + *vp = 0; + *sp = lp + 1; + return 0; + } + if (len) { + *vp++ = *lp++; + len--; + } else { + lp++; + } + } + } else { + uint8_t slen = strlen(lp); + if (slen) { + strlcpy(vp, *sp, len); + *sp = lp + slen; + return 0; + } + } + return 1; +} + +int32_t uDisplay::next_val(char **sp) { + char ibuff[16]; + str2c(sp, ibuff, sizeof(ibuff)); + return atoi(ibuff); +} + +uint32_t uDisplay::next_hex(char **sp) { + char ibuff[16]; + str2c(sp, ibuff, sizeof(ibuff)); + return strtol(ibuff, 0, 16); +} diff --git a/lib/lib_display/UDisplay/uDisplay.h b/lib/lib_display/UDisplay/uDisplay.h new file mode 100644 index 000000000..f5f3c789d --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay.h @@ -0,0 +1,114 @@ +#ifndef _UDISP_ +#define _UDISP_ + +#include +#include + +#define _UDSP_I2C 1 +#define _UDSP_SPI 2 + +#define UDISP1_WHITE 1 +#define UDISP1_BLACK 0 + + +// Color definitions +#define UDISP_BLACK 0x0000 /* 0, 0, 0 */ +#define UDISP_NAVY 0x000F /* 0, 0, 128 */ +#define UDISP_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define UDISP_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define UDISP_MAROON 0x7800 /* 128, 0, 0 */ +#define UDISP_PURPLE 0x780F /* 128, 0, 128 */ +#define UDISP_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define UDISP_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define UDISP_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define UDISP_BLUE 0x001F /* 0, 0, 255 */ +#define UDISP_GREEN 0x07E0 /* 0, 255, 0 */ +#define UDISP_CYAN 0x07FF /* 0, 255, 255 */ +#define UDISP_RED 0xF800 /* 255, 0, 0 */ +#define UDISP_MAGENTA 0xF81F /* 255, 0, 255 */ +#define UDISP_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define UDISP_WHITE 0xFFFF /* 255, 255, 255 */ +#define UDISP_ORANGE 0xFD20 /* 255, 165, 0 */ +#define UDISP_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define UDISP_PINK 0xF81F + +#define SPI_BEGIN_TRANSACTION uspi->beginTransaction(spiSettings); +#define SPI_END_TRANSACTION uspi->endTransaction(); +#define SPI_CS_LOW if (spi_cs >= 0) digitalWrite(spi_cs, LOW); +#define SPI_CS_HIGH if (spi_cs >= 0) digitalWrite(spi_cs, HIGH); +#define SPI_DC_LOW if (spi_dc >= 0) digitalWrite(spi_dc, LOW); +#define SPI_DC_HIGH if (spi_dc >= 0) digitalWrite(spi_dc, HIGH); + +#define ESP32_PWM_CHANNEL 1 + +class uDisplay : public Renderer { + public: + uDisplay(char *); + Renderer *Init(void); + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + void Updateframe(); + void DisplayOnff(int8_t on); + void Splash(void); + char *devname(void); + uint16_t fgcol(void) const { return fg_col; }; + uint16_t bgcol(void) const { return bg_col; }; + void dim(uint8_t dim); + uint16_t GetColorFromIndex(uint8_t index); + void setRotation(uint8_t m); + void fillScreen(uint16_t color); + void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + void pushColors(uint16_t *data, uint16_t len, boolean first); + + private: + void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + uint32_t str2c(char **sp, char *vp, uint32_t len); + void i2c_command(uint8_t val); + void spi_command(uint8_t val); + uint8_t strlen_ln(char *str); + int32_t next_val(char **sp); + uint32_t next_hex(char **sp); + void setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h); + char dname[16]; + int8_t bpp; + uint8_t interface; + uint8_t i2caddr; + int8_t i2c_scl; + int8_t i2c_sda; + int8_t reset; + uint8_t dsp_cmds[128]; + uint8_t dsp_ncmds; + uint8_t dsp_on; + uint8_t dsp_off; + uint16_t splash_font; + uint16_t splash_size; + uint16_t splash_xp; + uint16_t splash_yp; + uint16_t fg_col; + uint16_t bg_col; + uint16_t gxs; + uint16_t gys; + int8_t spi_cs; + int8_t spi_clk; + int8_t spi_mosi; + int8_t spi_dc; + int8_t bpanel; + int8_t spi_miso; + uint8_t dimmer; + SPIClass *uspi; + SPISettings spiSettings; + uint32_t spi_speed; + uint8_t spi_nr = 1; + uint8_t rot_0; + uint8_t rot_1; + uint8_t rot_2; + uint8_t rot_3; + uint8_t saw_1; + uint8_t saw_2; + uint8_t saw_3; + uint8_t flags; +}; + +#endif // _UDISP_ From 0e7e956a7b8ce296926d37a744116091157e5e64 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sun, 11 Apr 2021 12:32:02 +0200 Subject: [PATCH 2/4] universal display driver --- tasmota/xdrv_10_scripter.ino | 53 ++++-- tasmota/xdrv_13_display.ino | 14 +- tasmota/xdsp_17_universal.ino | 332 ++++++++++++++++++++++++++++++++++ 3 files changed, 384 insertions(+), 15 deletions(-) create mode 100644 tasmota/xdsp_17_universal.ino diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 7731237bd..5e920afba 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -65,7 +65,9 @@ keywords if then else endif, or, and are better readable for beginners (others m #define SCRIPT_MAXPERM (PMEM_SIZE)-4/sizeof(float) #define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS +#ifndef MAX_SARRAY_NUM #define MAX_SARRAY_NUM 32 +#endif uint32_t EncodeLightId(uint8_t relay_id); uint32_t DecodeLightId(uint32_t hue_id); @@ -1413,38 +1415,42 @@ uint32_t match_vars(char *dvnam, float **fp, char **sp, uint32_t *ind) { } #endif //USE_SCRIPT_GLOBVARS +#ifndef SCRIPT_IS_STRING_MAXSIZE +#define SCRIPT_IS_STRING_MAXSIZE 256 +#endif + char *isargs(char *lp, uint32_t isind) { float fvar; lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); SCRIPT_SKIP_SPACES - if (*lp!='"') { + if (*lp != '"') { return lp; } lp++; - if (glob_script_mem.si_num[isind]>0 && glob_script_mem.last_index_string[isind]) { + if (glob_script_mem.si_num[isind] > 0 && glob_script_mem.last_index_string[isind]) { free(glob_script_mem.last_index_string[isind]); } char *sstart = lp; uint8_t slen = 0; - for (uint32_t cnt = 0; cnt<256; cnt++) { - if (*lp=='\n' || *lp=='"' || *lp==0) { + for (uint32_t cnt = 0; cnt < SCRIPT_IS_STRING_MAXSIZE; cnt++) { + if (*lp == '\n' || *lp == '"' || *lp == 0) { lp++; - if (cnt>0 && !slen) { + if (cnt > 0 && !slen) { slen++; } glob_script_mem.siro_num[isind] = slen; break; } - if (*lp=='|') { + if (*lp == '|') { slen++; } lp++; } glob_script_mem.si_num[isind] = fvar; - if (glob_script_mem.si_num[isind]>0) { - if (glob_script_mem.si_num[isind]>MAX_SARRAY_NUM) { + if (glob_script_mem.si_num[isind] > 0) { + if (glob_script_mem.si_num[isind] > MAX_SARRAY_NUM) { glob_script_mem.si_num[isind] = MAX_SARRAY_NUM; } @@ -1468,17 +1474,17 @@ float fvar; char str[SCRIPT_MAXSSIZE]; str[0] = 0; uint8_t index = fvar; - if (index<1) index = 1; + if (index < 1) index = 1; index--; if (gv) gv->strind = index; glob_script_mem.sind_num = isind; if (glob_script_mem.last_index_string[isind]) { if (!glob_script_mem.si_num[isind]) { - if (index<=glob_script_mem.siro_num[isind]) { + if (index <= glob_script_mem.siro_num[isind]) { GetTextIndexed(str, sizeof(str), index , glob_script_mem.last_index_string[isind]); } } else { - if (index>glob_script_mem.si_num[isind]) { + if (index > glob_script_mem.si_num[isind]) { index = glob_script_mem.si_num[isind]; } strlcpy(str,glob_script_mem.last_index_string[isind] + (index * glob_script_mem.max_ssize), glob_script_mem.max_ssize); @@ -1866,6 +1872,15 @@ chknext: } #endif //USE_SCRIPT_TASK #endif //ESP32 +#ifdef USE_ANGLE_FUNC + if (!strncmp(vname, "cos(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, gv); + fvar = cosf(fvar); + lp++; + len = 0; + goto exit; + } +#endif break; case 'd': if (!strncmp(vname, "day", 3)) { @@ -2196,6 +2211,22 @@ chknext: len = 0; goto exit; } + if (!strncmp(vname, "fmt(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, gv); + if (!fvar) { +#ifdef ESP8266 + LittleFS.format(); +#endif +#ifdef ESP32 + LITTLEFS.format(); +#endif + } else { + //SD.format(); + } + lp++; + len = 0; + goto exit; + } if (!strncmp(vname, "frd(", 4)) { char str[glob_script_mem.max_ssize + 1]; lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index fe599d7a7..1b8a19df9 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -80,6 +80,7 @@ const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log #define D_CMND_DISP_CLOCK "Clock" #define D_CMND_DISP_TEXTNC "TextNC" // NC - "No Clear" #define D_CMND_DISP_SCROLLTEXT "ScrollText" +#define D_CMND_DISP_REINIT "reinit" enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND, FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER, @@ -108,7 +109,7 @@ const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" // Prefix #endif D_CMND_DISP_CLEAR "|" D_CMND_DISP_NUMBER "|" D_CMND_DISP_FLOAT "|" D_CMND_DISP_NUMBERNC "|" D_CMND_DISP_FLOATNC "|" D_CMND_DISP_RAW "|" D_CMND_DISP_LEVEL "|" D_CMND_DISP_SEVENSEG_TEXT "|" D_CMND_DISP_SEVENSEG_TEXTNC "|" - D_CMND_DISP_SCROLLDELAY "|" D_CMND_DISP_CLOCK "|" D_CMND_DISP_TEXTNC "|" D_CMND_DISP_SCROLLTEXT + D_CMND_DISP_SCROLLDELAY "|" D_CMND_DISP_CLOCK "|" D_CMND_DISP_TEXTNC "|" D_CMND_DISP_SCROLLTEXT "|" D_CMND_DISP_REINIT ; void (* const DisplayCommand[])(void) PROGMEM = { @@ -120,7 +121,7 @@ void (* const DisplayCommand[])(void) PROGMEM = { #endif &CmndDisplayClear, &CmndDisplayNumber, &CmndDisplayFloat, &CmndDisplayNumberNC, &CmndDisplayFloatNC, &CmndDisplayRaw, &CmndDisplayLevel, &CmndDisplaySevensegText, &CmndDisplaySevensegTextNC, - &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, &CmndDisplayScrollText + &CmndDisplayScrollDelay, &CmndDisplayClock, &CmndDisplayTextNC, &CmndDisplayScrollText,&DisplayReInitDriver }; #ifdef USE_GRAPH @@ -989,7 +990,7 @@ void Display_Text_From_File(const char *file) { File fp; if (!ufsp) return; fp = ufsp->open(file, FS_FILE_READ); - if (fp >= 0) { + if (fp > 0) { char *savptr = XdrvMailbox.data; char linebuff[128]; while (fp.available()) { @@ -1021,7 +1022,7 @@ void Display_Text_From_File(const char *file) { fp.close(); } } -#endif +#endif // USE_UFILESYS #ifdef USE_DT_VARS @@ -2001,6 +2002,11 @@ void CmndDisplayScrollText(void) { if(result) ResponseCmndChar(XdrvMailbox.data); } +void DisplayReInitDriver(void) { + XdspCall(FUNC_DISPLAY_INIT_DRIVER); + ResponseCmndDone(); +} + /*********************************************************************************************\ * Optional drivers \*********************************************************************************************/ diff --git a/tasmota/xdsp_17_universal.ino b/tasmota/xdsp_17_universal.ino new file mode 100644 index 000000000..eb4a9dbd8 --- /dev/null +++ b/tasmota/xdsp_17_universal.ino @@ -0,0 +1,332 @@ +/* + xdsp_17_universal.ino - universal display driver support for Tasmota + + Copyright (C) 2021 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef USE_DISPLAY +#ifdef USE_UNIVERSAL_DISPLAY + +#define XDSP_17 17 + +#include + +uDisplay *udisp; +bool udisp_init_done = false; +extern uint8_t color_type; +extern uint16_t fg_color; +extern uint16_t bg_color; + +#ifdef USE_UFILESYS +extern FS *ufsp; +#endif + +#define DISPDESC_SIZE 1000 + +#define DSP_ROM_DESC + +/*********************************************************************************************/ +#ifdef DSP_ROM_DESC +/* sample descriptor */ +const char DSP_SAMPLE_DESC[] PROGMEM = +// name,xs,ys,bpp,interface, (HEX) address, scl,sda,reset +// '*' means take pin number from tasmota +":H\n" +"SH1106,128,64,1,I2C,3c,*,*,*\n" +// splash settings, font, size, fgcol, bgcol, x,y +":S\n" +"0,1,1,0,40,20\n" +// init register settings, must be in HEX +":I\n" +"AE\n" +"D5,80\n" +"A8,3f\n" +"D3,00\n" +"40\n" +"8D,14\n" +"20,00\n" +"A1\n" +"C8\n" +"DA,12\n" +"81,CF\n" +"D9F1\n" +"DB,40\n" +"A4\n" +"A6\n" +"AF\n" +// switch display off +":o\n" +"AE\n" +// switch display on +":O\n" +"AF\n" +"#\n"; + +#endif // DSP_ROM_DESC +/*********************************************************************************************/ + +void Init_uDisp(void) { +char *ddesc = 0; +char *fbuff; + + if (1) { + Settings.display_model = XDSP_17; + + fg_color = 1; + bg_color = 0; + color_type = COLOR_BW; + + fbuff = (char*)calloc(DISPDESC_SIZE, 1); + if (!fbuff) return; + +#ifdef USE_UFILESYS + if (ufsp && !TasmotaGlobal.no_autoexec) { + File fp; + fp = ufsp->open("/dispdesc.txt", "r"); + if (fp > 0) { + uint32_t size = fp.size(); + fp.read((uint8_t*)fbuff, size); + fp.close(); + ddesc = fbuff; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: File descriptor used")); + } + } +#endif + + +#ifdef USE_SCRIPT + if (bitRead(Settings.rule_enabled, 0) && !ddesc) { + uint8_t dfound = Run_Scripter(">d",-2,0); + if (dfound == 99) { + char *lp = glob_script_mem.section_ptr + 2; + while (*lp != '\n') lp++; + memcpy(fbuff, lp + 1, DISPDESC_SIZE - 1); + ddesc = fbuff; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: Script descriptor used")); + } + } +#endif // USE_SCRIPT + + +#ifdef DSP_ROM_DESC + if (!ddesc) { + memcpy_P(fbuff, DSP_SAMPLE_DESC, sizeof(DSP_SAMPLE_DESC)); + ddesc = fbuff; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: Flash descriptor used")); + } +#endif // DSP_ROM_DESC + + if (!ddesc) { + AddLog(LOG_LEVEL_INFO, PSTR("DSP: No valid descriptor found")); + if (fbuff) free(fbuff); + return; + } + // now replace tasmota vars before passing to driver + char *cp = strstr(ddesc, "I2C"); + if (cp) { + cp += 4; + //,3c,22,21,-1 + // i2c addr + //if (*cp == '*') { + // Settings.display_address + //} + uint8_t i2caddr = strtol(cp, 0, 16); + if (I2cSetDevice(i2caddr)) { + I2cSetActiveFound(i2caddr, "DSP-I2C"); + } + cp+=3; + //replacepin(&cp, Settings.display_address); + replacepin(&cp, Pin(GPIO_I2C_SCL)); + replacepin(&cp, Pin(GPIO_I2C_SDA)); + replacepin(&cp, Pin(GPIO_OLED_RESET)); + } + + cp = strstr(ddesc, "SPI"); + if (cp) { + cp += 4; + //; 7 params nr,cs,sclk,mosi,dc,bl,reset,miso + //SPI,*,*,*,*,*,*,* + if (*cp == '1') { + cp+=2; + replacepin(&cp, Pin(GPIO_SPI_CS)); + replacepin(&cp, Pin(GPIO_SPI_CLK)); + replacepin(&cp, Pin(GPIO_SPI_MOSI)); + replacepin(&cp, Pin(GPIO_SPI_DC)); + replacepin(&cp, Pin(GPIO_BACKLIGHT)); + replacepin(&cp, Pin(GPIO_OLED_RESET)); + replacepin(&cp, Pin(GPIO_SPI_MISO)); + } else { + // soft spi pins + cp+=2; + replacepin(&cp, Pin(GPIO_SSPI_CS)); + replacepin(&cp, Pin(GPIO_SSPI_SCLK)); + replacepin(&cp, Pin(GPIO_SSPI_MOSI)); + replacepin(&cp, Pin(GPIO_SSPI_DC)); + replacepin(&cp, Pin(GPIO_BACKLIGHT)); + replacepin(&cp, Pin(GPIO_OLED_RESET)); + replacepin(&cp, Pin(GPIO_SSPI_MISO)); + } + } + + // init renderer + if (udisp) delete udisp; + udisp = new uDisplay(ddesc); + +/* + File fp; + fp = ufsp->open("/dump.txt", "w"); + fp.write(ddesc, DISPDESC_SIZE); + fp.close(); +*/ + // release desc buffer + if (fbuff) free(fbuff); + + renderer = udisp->Init(); + if (!renderer) return; + + Settings.display_width = renderer->width(); + Settings.display_height = renderer->height(); + fg_color = udisp->fgcol(); + bg_color = udisp->bgcol(); + + renderer->DisplayInit(DISPLAY_INIT_MODE, Settings.display_size, Settings.display_rotate, Settings.display_font); + + +#ifdef SHOW_SPLASH + udisp->Splash(); +#endif + + udisp_init_done = true; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s!"), udisp->devname()); + } +} + + +/*********************************************************************************************/ + +void replacepin(char **cp, uint16_t pin) { + char *lp = *cp; + if (*lp == ',') lp++; + if (*lp == '*') { + char val[8]; + itoa(pin, val, 10); + uint16_t slen = strlen(val); + //AddLog(LOG_LEVEL_INFO, PSTR("replace pin: %d"), pin); + memmove(lp + slen, lp + 1, strlen(lp)); + memmove(lp, val, slen); + } + char *np = strchr(lp, ','); + if (np) { + *cp = np + 1; + } +} + +#ifdef USE_DISPLAY_MODES1TO5 + +void UDISP_PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void UDISP_Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ] + renderer->println(line); + renderer->println(); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [01-02-2018] + renderer->println(line); + renderer->Updateframe(); +} + +void UDISP_Refresh(void) // Every second +{ + if (!renderer) return; + if (Settings.display_mode) { // Mode 0 is User text + switch (Settings.display_mode) { + case 1: // Time + UDISP_Time(); + break; + case 2: // Local + case 3: // Local + case 4: // Mqtt + case 5: // Mqtt + UDISP_PrintLog(); + break; + } + } +} + +#endif // USE_DISPLAY_MODES1TO5 + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdsp17(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + Init_uDisp(); + } + else if (udisp_init_done && (XDSP_17 == Settings.display_model)) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + UDISP_Refresh(); + break; +#endif // USE_DISPLAY_MODES1TO5 + } + } + return result; +} + +#endif // USE_UNIVERSAL_DISPLAY +#endif // USE_DISPLAY From 835e7ab2e3115814b55c2d323cf2bc26a853dfe0 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sun, 11 Apr 2021 12:38:45 +0200 Subject: [PATCH 3/4] add sample descriptors --- tasmota/displaydesc/ILI9341_desc.txt | 50 ++++++++++++++++++++++++++++ tasmota/displaydesc/SH1106_desc.txt | 31 +++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tasmota/displaydesc/ILI9341_desc.txt create mode 100644 tasmota/displaydesc/SH1106_desc.txt diff --git a/tasmota/displaydesc/ILI9341_desc.txt b/tasmota/displaydesc/ILI9341_desc.txt new file mode 100644 index 000000000..6aab1b1c5 --- /dev/null +++ b/tasmota/displaydesc/ILI9341_desc.txt @@ -0,0 +1,50 @@ +; name,xs,ys,bpp,interface, spi_nr cs, sclk,mosi,dc, bp ,reset,miso,spi_speed +:H +ILI9341,240,320,16,SPI,1,*,*,*,*,*,*,*,40000000 +; splash settings, font, size, fgcol, bgcol, x,y +:S +2,1,1,0,40,20 +; initialyze +:I +EF,3,03,80,02 +CF,3,00,C1,30 +ED,4,64,03,12,81 +E8,3,85,00,78 +CB,5,39,2C,00,34,02 +F7,1,20 +EA,2,00,00 +C0,1,23 +C1,1,10 +C5,2,3e,28 +C7,1,86 +36,1,48 +37,1,00 +3A,1,55 +B1,2,00,18 +B6,3,08,82,27 +F2,1,00 +26,1,01 +E0,0F,0F,31,2B,0C,0E,08,4E,F1,37,07,10,03,0E,09,00 +E1,0F,00,0E,14,03,11,07,31,C1,48,08,0F,0C,31,36,0F +11,80 +29,80 +; off +:o +28 +; on +:O +29 +; set adress window +:A +2A,2B,2C +; rotation +:0 +48 +:1 +28 +:2 +88 +:3 +E8 +# + diff --git a/tasmota/displaydesc/SH1106_desc.txt b/tasmota/displaydesc/SH1106_desc.txt new file mode 100644 index 000000000..954cb3740 --- /dev/null +++ b/tasmota/displaydesc/SH1106_desc.txt @@ -0,0 +1,31 @@ +; name,xs,ys,bpp,interface, address, scl,sda,reset +:H +SH1106,128,64,1,I2C,3c,*,*,* +; splash settings, font, size, fgcol, bgcol, x,y +:S +0,1,1,0,40,20 +; init register settings +:I +AE +D5,80 +A8,3f +D3,00 +40 +8D,14 +20,00 +A1 +C8 +DA,12 +81,CF +D9F1 +DB,40 +A4 +A6 +AF +; switch display off +:o +AE +; switch display on +:O +AF +# \ No newline at end of file From 4805f26649ccb30105fec62de7c0d3835cf0c323 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sun, 11 Apr 2021 15:16:40 +0200 Subject: [PATCH 4/4] udisplay option a3 --- tasmota/tasmota_template.h | 4 ++-- tasmota/xdsp_17_universal.ino | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 70bcff270..9b27c12d9 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -169,14 +169,14 @@ enum ProgramSelectablePins { GPIO_USER, // User configurable needs to be 2047 GPIO_MAX }; -#define MAX_OPTIONS_A 2 // Increase if more bits are used from GpioOptionABits +#define MAX_OPTIONS_A 3 // Increase if more bits are used from GpioOptionABits typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint32_t data; // Allow bit manipulation using SetOption struct { // GPIO Option_A1 .. Option_A32 uint32_t pwm1_input : 1; // bit 0 (v9.2.0.1) - Option_A1 - (Light) Change PWM1 to input on power off and no fade running (1) uint32_t dummy_energy : 1; // bit 1 (v9.3.1.2) - Option_A2 - (Energy) Enable dummy values - uint32_t spare02 : 1; // bit 2 + uint32_t udisplay_driver : 1; // bit 2 Universal display driver uint32_t spare03 : 1; // bit 3 uint32_t spare04 : 1; // bit 4 uint32_t spare05 : 1; // bit 5 diff --git a/tasmota/xdsp_17_universal.ino b/tasmota/xdsp_17_universal.ino index eb4a9dbd8..c72b4cb85 100644 --- a/tasmota/xdsp_17_universal.ino +++ b/tasmota/xdsp_17_universal.ino @@ -83,7 +83,7 @@ void Init_uDisp(void) { char *ddesc = 0; char *fbuff; - if (1) { + if (TasmotaGlobal.gpio_optiona.udisplay_driver) { Settings.display_model = XDSP_17; fg_color = 1;