Merge pull request #11665 from gemu2015/udisplay

Udisplay
This commit is contained in:
Theo Arends 2021-04-11 16:32:05 +02:00 committed by GitHub
commit 1fdcb684e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1241 additions and 17 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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=*

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#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);
}

View File

@ -0,0 +1,114 @@
#ifndef _UDISP_
#define _UDISP_
#include <Adafruit_GFX.h>
#include <renderer.h>
#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_

View File

@ -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
#

View File

@ -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
#

View File

@ -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

View File

@ -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,6 +1415,10 @@ 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);
@ -1427,7 +1433,7 @@ char *isargs(char *lp, uint32_t isind) {
}
char *sstart = lp;
uint8_t slen = 0;
for (uint32_t cnt = 0; cnt<256; cnt++) {
for (uint32_t cnt = 0; cnt < SCRIPT_IS_STRING_MAXSIZE; cnt++) {
if (*lp == '\n' || *lp == '"' || *lp == 0) {
lp++;
if (cnt > 0 && !slen) {
@ -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);

View File

@ -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
\*********************************************************************************************/

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifdef USE_DISPLAY
#ifdef USE_UNIVERSAL_DISPLAY
#define XDSP_17 17
#include <uDisplay.h>
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 (TasmotaGlobal.gpio_optiona.udisplay_driver) {
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