2018-08-28 17:13:14 +01:00
/*
2019-10-27 10:13:24 +00:00
xdrv_13_display . ino - Display support for Tasmota
2018-08-28 17:13:14 +01:00
2019-12-31 13:23:34 +00:00
Copyright ( C ) 2020 Theo Arends
2018-08-28 17:13:14 +01:00
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/>.
*/
# if defined(USE_I2C) || defined(USE_SPI)
# ifdef USE_DISPLAY
2019-08-19 12:21:54 +01:00
# define XDRV_13 13
# include <renderer.h>
Renderer * renderer ;
2019-08-19 17:17:44 +01:00
enum ColorType { COLOR_BW , COLOR_COLOR } ;
2019-08-19 12:21:54 +01:00
# ifndef MAXBUTTONS
# define MAXBUTTONS 16
# endif
# ifdef USE_TOUCH_BUTTONS
VButton * buttons [ MAXBUTTONS ] ;
# endif
// drawing color is WHITE
// on epaper the whole display buffer is transfered inverted this results in white paper
uint16_t fg_color = 1 ;
uint16_t bg_color = 0 ;
uint8_t color_type = COLOR_BW ;
uint8_t auto_draw = 1 ;
2019-03-31 10:59:04 +01:00
const uint8_t DISPLAY_MAX_DRIVERS = 16 ; // Max number of display drivers/models supported by xdsp_interface.ino
2020-06-21 11:11:40 +01:00
const uint8_t DISPLAY_MAX_COLS = 64 ; // Max number of columns allowed with command DisplayCols
const uint8_t DISPLAY_MAX_ROWS = 64 ; // Max number of lines allowed with command DisplayRows
2018-08-28 17:13:14 +01:00
2019-03-31 10:59:04 +01:00
const uint8_t DISPLAY_LOG_ROWS = 32 ; // Number of lines in display log buffer
2018-08-28 17:13:14 +01:00
2019-08-11 17:12:18 +01:00
# define D_PRFX_DISPLAY "Display"
2018-08-28 17:13:14 +01:00
# define D_CMND_DISP_ADDRESS "Address"
# define D_CMND_DISP_COLS "Cols"
# define D_CMND_DISP_DIMMER "Dimmer"
# define D_CMND_DISP_MODE "Mode"
# define D_CMND_DISP_MODEL "Model"
# define D_CMND_DISP_REFRESH "Refresh"
# define D_CMND_DISP_ROWS "Rows"
# define D_CMND_DISP_SIZE "Size"
# define D_CMND_DISP_FONT "Font"
# define D_CMND_DISP_ROTATE "Rotate"
# define D_CMND_DISP_TEXT "Text"
2019-07-23 13:05:42 +01:00
# define D_CMND_DISP_WIDTH "Width"
# define D_CMND_DISP_HEIGHT "Height"
2018-08-28 17:13:14 +01:00
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 ,
FUNC_DISPLAY_CLEAR , FUNC_DISPLAY_DRAW_FRAME ,
FUNC_DISPLAY_DRAW_HLINE , FUNC_DISPLAY_DRAW_VLINE , FUNC_DISPLAY_DRAW_LINE ,
FUNC_DISPLAY_DRAW_CIRCLE , FUNC_DISPLAY_FILL_CIRCLE ,
FUNC_DISPLAY_DRAW_RECTANGLE , FUNC_DISPLAY_FILL_RECTANGLE ,
2020-08-17 13:49:24 +01:00
FUNC_DISPLAY_TEXT_SIZE , FUNC_DISPLAY_FONT_SIZE , FUNC_DISPLAY_ROTATION , FUNC_DISPLAY_DRAW_STRING } ;
2018-08-28 17:13:14 +01:00
enum DisplayInitModes { DISPLAY_INIT_MODE , DISPLAY_INIT_PARTIAL , DISPLAY_INIT_FULL } ;
2019-08-11 17:12:18 +01:00
const char kDisplayCommands [ ] PROGMEM = D_PRFX_DISPLAY " | " // Prefix
2019-08-02 16:22:38 +01:00
" | " D_CMND_DISP_MODEL " | " D_CMND_DISP_WIDTH " | " D_CMND_DISP_HEIGHT " | " D_CMND_DISP_MODE " | " D_CMND_DISP_REFRESH " | "
D_CMND_DISP_DIMMER " | " D_CMND_DISP_COLS " | " D_CMND_DISP_ROWS " | " D_CMND_DISP_SIZE " | " D_CMND_DISP_FONT " | "
D_CMND_DISP_ROTATE " | " D_CMND_DISP_TEXT " | " D_CMND_DISP_ADDRESS ;
void ( * const DisplayCommand [ ] ) ( void ) PROGMEM = {
& CmndDisplay , & CmndDisplayModel , & CmndDisplayWidth , & CmndDisplayHeight , & CmndDisplayMode , & CmndDisplayRefresh ,
& CmndDisplayDimmer , & CmndDisplayColumns , & CmndDisplayRows , & CmndDisplaySize , & CmndDisplayFont ,
& CmndDisplayRotate , & CmndDisplayText , & CmndDisplayAddress } ;
2018-08-28 17:13:14 +01:00
2019-07-01 11:54:26 +01:00
char * dsp_str ;
2018-08-28 17:13:14 +01:00
uint16_t dsp_x ;
uint16_t dsp_y ;
uint16_t dsp_x2 ;
uint16_t dsp_y2 ;
uint16_t dsp_rad ;
uint16_t dsp_color ;
int16_t dsp_len ;
2019-07-01 11:54:26 +01:00
int16_t disp_xpos = 0 ;
int16_t disp_ypos = 0 ;
uint8_t disp_power = 0 ;
uint8_t disp_device = 0 ;
uint8_t disp_refresh = 1 ;
uint8_t disp_autodraw = 1 ;
uint8_t dsp_init ;
uint8_t dsp_font ;
uint8_t dsp_flag ;
uint8_t dsp_on ;
2018-08-28 17:13:14 +01:00
# ifdef USE_DISPLAY_MODES1TO5
2019-07-01 11:54:26 +01:00
char * * disp_log_buffer ;
char * * disp_screen_buffer ;
2018-08-28 17:13:14 +01:00
char disp_temp [ 2 ] ; // C or F
2019-10-18 17:06:17 +01:00
char disp_pres [ 5 ] ; // hPa or mmHg
2018-08-28 17:13:14 +01:00
uint8_t disp_log_buffer_cols = 0 ;
uint8_t disp_log_buffer_idx = 0 ;
uint8_t disp_log_buffer_ptr = 0 ;
uint8_t disp_screen_buffer_cols = 0 ;
uint8_t disp_screen_buffer_rows = 0 ;
2019-07-01 11:54:26 +01:00
bool disp_subscribed = false ;
2018-08-28 17:13:14 +01:00
# endif // USE_DISPLAY_MODES1TO5
/*********************************************************************************************/
2019-08-19 12:21:54 +01:00
void DisplayInit ( uint8_t mode )
2018-08-28 17:13:14 +01:00
{
2019-08-19 12:21:54 +01:00
if ( renderer ) {
2019-10-19 11:46:31 +01:00
renderer - > DisplayInit ( mode , Settings . display_size , Settings . display_rotate , Settings . display_font ) ;
2019-08-19 12:21:54 +01:00
}
else {
dsp_init = mode ;
XdspCall ( FUNC_DISPLAY_INIT ) ;
}
2018-08-28 17:13:14 +01:00
}
2018-11-14 13:32:09 +00:00
void DisplayClear ( void )
2018-08-28 17:13:14 +01:00
{
XdspCall ( FUNC_DISPLAY_CLEAR ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayDrawHLine ( uint16_t x , uint16_t y , int16_t len , uint16_t color )
2018-08-28 17:13:14 +01:00
{
dsp_x = x ;
dsp_y = y ;
dsp_len = len ;
dsp_color = color ;
XdspCall ( FUNC_DISPLAY_DRAW_HLINE ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayDrawVLine ( uint16_t x , uint16_t y , int16_t len , uint16_t color )
2018-08-28 17:13:14 +01:00
{
dsp_x = x ;
dsp_y = y ;
dsp_len = len ;
dsp_color = color ;
XdspCall ( FUNC_DISPLAY_DRAW_VLINE ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayDrawLine ( uint16_t x , uint16_t y , uint16_t x2 , uint16_t y2 , uint16_t color )
2018-08-28 17:13:14 +01:00
{
dsp_x = x ;
dsp_y = y ;
dsp_x2 = x2 ;
dsp_y2 = y2 ;
dsp_color = color ;
XdspCall ( FUNC_DISPLAY_DRAW_LINE ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayDrawCircle ( uint16_t x , uint16_t y , uint16_t rad , uint16_t color )
2018-08-28 17:13:14 +01:00
{
dsp_x = x ;
dsp_y = y ;
dsp_rad = rad ;
dsp_color = color ;
XdspCall ( FUNC_DISPLAY_DRAW_CIRCLE ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayDrawFilledCircle ( uint16_t x , uint16_t y , uint16_t rad , uint16_t color )
2018-08-28 17:13:14 +01:00
{
dsp_x = x ;
dsp_y = y ;
dsp_rad = rad ;
dsp_color = color ;
XdspCall ( FUNC_DISPLAY_FILL_CIRCLE ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayDrawRectangle ( uint16_t x , uint16_t y , uint16_t x2 , uint16_t y2 , uint16_t color )
2018-08-28 17:13:14 +01:00
{
dsp_x = x ;
dsp_y = y ;
dsp_x2 = x2 ;
dsp_y2 = y2 ;
dsp_color = color ;
XdspCall ( FUNC_DISPLAY_DRAW_RECTANGLE ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayDrawFilledRectangle ( uint16_t x , uint16_t y , uint16_t x2 , uint16_t y2 , uint16_t color )
2018-08-28 17:13:14 +01:00
{
dsp_x = x ;
dsp_y = y ;
dsp_x2 = x2 ;
dsp_y2 = y2 ;
dsp_color = color ;
XdspCall ( FUNC_DISPLAY_FILL_RECTANGLE ) ;
}
2018-11-14 13:32:09 +00:00
void DisplayDrawFrame ( void )
2018-08-28 17:13:14 +01:00
{
XdspCall ( FUNC_DISPLAY_DRAW_FRAME ) ;
}
2019-08-19 12:21:54 +01:00
void DisplaySetSize ( uint8_t size )
2018-08-28 17:13:14 +01:00
{
Settings . display_size = size & 3 ;
XdspCall ( FUNC_DISPLAY_TEXT_SIZE ) ;
}
2019-08-19 12:21:54 +01:00
void DisplaySetFont ( uint8_t font )
2018-08-28 17:13:14 +01:00
{
Settings . display_font = font & 3 ;
XdspCall ( FUNC_DISPLAY_FONT_SIZE ) ;
}
2019-08-19 12:21:54 +01:00
void DisplaySetRotation ( uint8_t rotation )
2018-08-28 17:13:14 +01:00
{
Settings . display_rotate = rotation & 3 ;
XdspCall ( FUNC_DISPLAY_ROTATION ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayDrawStringAt ( uint16_t x , uint16_t y , char * str , uint16_t color , uint8_t flag )
2018-08-28 17:13:14 +01:00
{
dsp_x = x ;
dsp_y = y ;
dsp_str = str ;
dsp_color = color ;
dsp_flag = flag ;
XdspCall ( FUNC_DISPLAY_DRAW_STRING ) ;
}
2019-08-19 12:21:54 +01:00
void DisplayOnOff ( uint8_t on )
2018-08-28 17:13:14 +01:00
{
2020-08-17 13:49:24 +01:00
ExecuteCommandPower ( disp_device , on , SRC_DISPLAY ) ;
2018-08-28 17:13:14 +01:00
}
/*-------------------------------------------------------------------------------------------*/
2019-08-19 12:21:54 +01:00
// get asci float number
uint8_t fatoiv ( char * cp , float * res ) {
uint8_t index = 0 ;
* res = CharToFloat ( cp ) ;
while ( * cp ) {
if ( ( * cp > = ' 0 ' & & * cp < = ' 9 ' ) | | ( * cp = = ' - ' ) | | ( * cp = = ' . ' ) ) {
cp + + ;
index + + ;
} else {
break ;
}
}
return index ;
}
2018-08-28 17:13:14 +01:00
// get asci number until delimiter and return asci number lenght and value
uint8_t atoiv ( char * cp , int16_t * res )
{
uint8_t index = 0 ;
* res = atoi ( cp ) ;
while ( * cp ) {
if ( ( * cp > = ' 0 ' & & * cp < = ' 9 ' ) | | ( * cp = = ' - ' ) ) {
cp + + ;
index + + ;
} else {
break ;
}
}
return index ;
}
// get asci number until delimiter and return asci number lenght and value
uint8_t atoiV ( char * cp , uint16_t * res )
{
uint8_t index = 0 ;
* res = atoi ( cp ) ;
while ( * cp ) {
if ( * cp > = ' 0 ' & & * cp < = ' 9 ' ) {
cp + + ;
index + + ;
} else {
break ;
}
}
return index ;
}
2019-08-19 12:21:54 +01:00
// right align string
void alignright ( char * string ) {
uint16_t slen = strlen ( string ) ;
uint16_t len = slen ;
while ( len ) {
// count spaces to the right
if ( string [ len - 1 ] ! = ' ' ) {
break ;
}
len - - ;
}
uint16_t diff = slen - len ;
if ( diff > 0 ) {
// move string
memmove ( & string [ diff ] , string , len ) ;
memset ( string , ' ' , diff ) ;
}
}
char * get_string ( char * buff , uint8_t len , char * cp ) {
uint8_t index = 0 ;
while ( * cp ! = ' : ' ) {
buff [ index ] = * cp + + ;
index + + ;
if ( index > = len ) break ;
}
buff [ index ] = 0 ;
cp + + ;
return cp ;
}
# define ESCAPE_CHAR '~'
// decode text escapes, 1 hexbyte assumed
2020-01-01 14:13:58 +00:00
uint32_t decode_te ( char * line ) {
uint32_t skip = 0 ;
2019-08-19 12:21:54 +01:00
char sbuf [ 3 ] , * cp ;
while ( * line ) {
if ( * line = = ESCAPE_CHAR ) {
cp = line + 1 ;
if ( * cp ! = 0 & & * cp = = ESCAPE_CHAR ) {
// escape escape, discard one
memmove ( cp , cp + 1 , strlen ( cp ) ) ;
2020-01-01 14:13:58 +00:00
skip + + ;
2019-08-19 12:21:54 +01:00
} else {
// escape HH
if ( strlen ( cp ) < 2 ) {
// illegal lenght, ignore
2020-01-01 14:13:58 +00:00
return skip ;
2019-08-19 12:21:54 +01:00
}
// take 2 hex chars
sbuf [ 0 ] = * ( cp ) ;
sbuf [ 1 ] = * ( cp + 1 ) ;
sbuf [ 2 ] = 0 ;
* line = strtol ( sbuf , 0 , 16 ) ;
// must shift string 2 bytes shift zero also
memmove ( cp , cp + 2 , strlen ( cp ) - 1 ) ;
2020-01-01 14:13:58 +00:00
skip + = 2 ;
2019-08-19 12:21:54 +01:00
}
}
line + + ;
}
2020-01-01 14:13:58 +00:00
return skip ;
2019-08-19 12:21:54 +01:00
}
2018-08-28 17:13:14 +01:00
/*-------------------------------------------------------------------------------------------*/
# define DISPLAY_BUFFER_COLS 128 // Max number of characters in linebuf
2018-11-14 13:32:09 +00:00
void DisplayText ( void )
2018-08-28 17:13:14 +01:00
{
uint8_t lpos ;
uint8_t escape = 0 ;
uint8_t var ;
int16_t lin = 0 ;
int16_t col = 0 ;
int16_t fill = 0 ;
int16_t temp ;
int16_t temp1 ;
2019-08-19 12:21:54 +01:00
float ftemp ;
2018-08-28 17:13:14 +01:00
char linebuf [ DISPLAY_BUFFER_COLS ] ;
char * dp = linebuf ;
char * cp = XdrvMailbox . data ;
memset ( linebuf , ' ' , sizeof ( linebuf ) ) ;
linebuf [ sizeof ( linebuf ) - 1 ] = 0 ;
* dp = 0 ;
while ( * cp ) {
if ( ! escape ) {
// check for escape
if ( * cp = = ' [ ' ) {
escape = 1 ;
cp + + ;
// if string in buffer print it
if ( ( uint32_t ) dp - ( uint32_t ) linebuf ) {
if ( ! fill ) { * dp = 0 ; }
if ( col > 0 & & lin > 0 ) {
// use col and lin
2019-08-19 12:21:54 +01:00
if ( ! renderer ) DisplayDrawStringAt ( col , lin , linebuf , fg_color , 1 ) ;
else renderer - > DrawStringAt ( col , lin , linebuf , fg_color , 1 ) ;
2018-08-28 17:13:14 +01:00
} else {
// use disp_xpos, disp_ypos
2019-08-19 12:21:54 +01:00
if ( ! renderer ) DisplayDrawStringAt ( disp_xpos , disp_ypos , linebuf , fg_color , 0 ) ;
else renderer - > DrawStringAt ( disp_xpos , disp_ypos , linebuf , fg_color , 0 ) ;
2018-08-28 17:13:14 +01:00
}
memset ( linebuf , ' ' , sizeof ( linebuf ) ) ;
linebuf [ sizeof ( linebuf ) - 1 ] = 0 ;
dp = linebuf ;
}
} else {
// copy chars
if ( dp < ( linebuf + DISPLAY_BUFFER_COLS ) ) { * dp + + = * cp + + ; }
}
} else {
// check escapes
if ( * cp = = ' ] ' ) {
escape = 0 ;
cp + + ;
} else {
// analyze escapes
switch ( * cp + + ) {
case ' z ' :
// clear display
2019-08-19 12:21:54 +01:00
if ( ! renderer ) DisplayClear ( ) ;
else renderer - > fillScreen ( bg_color ) ;
2018-08-28 17:13:14 +01:00
disp_xpos = 0 ;
disp_ypos = 0 ;
col = 0 ;
lin = 0 ;
break ;
case ' i ' :
// init display with partial update
DisplayInit ( DISPLAY_INIT_PARTIAL ) ;
break ;
case ' I ' :
// init display with full refresh
DisplayInit ( DISPLAY_INIT_FULL ) ;
break ;
case ' o ' :
2020-08-17 13:49:24 +01:00
DisplayOnOff ( 0 ) ;
2018-08-28 17:13:14 +01:00
break ;
case ' O ' :
2020-08-17 13:49:24 +01:00
DisplayOnOff ( 1 ) ;
2018-08-28 17:13:14 +01:00
break ;
case ' x ' :
// set disp_xpos
var = atoiv ( cp , & disp_xpos ) ;
cp + = var ;
break ;
case ' y ' :
// set disp_ypos
var = atoiv ( cp , & disp_ypos ) ;
cp + = var ;
break ;
case ' l ' :
// text line lxx
var = atoiv ( cp , & lin ) ;
cp + = var ;
//display.setCursor(display.getCursorX(),(lin-1)*font_y*txtsize);
break ;
case ' c ' :
// text column cxx
var = atoiv ( cp , & col ) ;
cp + = var ;
//display.setCursor((col-1)*font_x*txtsize,display.getCursorY());
break ;
case ' C ' :
// text color cxx
2019-08-19 12:21:54 +01:00
if ( * cp = = ' i ' ) {
// color index 0-18
cp + + ;
var = atoiv ( cp , & temp ) ;
if ( renderer ) ftemp = renderer - > GetColorFromIndex ( temp ) ;
} else {
// float because it must handle unsigned 16 bit
var = fatoiv ( cp , & ftemp ) ;
}
fg_color = ftemp ;
cp + = var ;
if ( renderer ) renderer - > setTextColor ( fg_color , bg_color ) ;
break ;
case ' B ' :
// bg color Bxx
if ( * cp = = ' i ' ) {
// color index 0-18
cp + + ;
var = atoiv ( cp , & temp ) ;
if ( renderer ) ftemp = renderer - > GetColorFromIndex ( temp ) ;
} else {
var = fatoiv ( cp , & ftemp ) ;
}
bg_color = ftemp ;
2018-08-28 17:13:14 +01:00
cp + = var ;
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > setTextColor ( fg_color , bg_color ) ;
2018-08-28 17:13:14 +01:00
break ;
case ' p ' :
// pad field with spaces fxx
var = atoiv ( cp , & fill ) ;
cp + = var ;
linebuf [ fill ] = 0 ;
break ;
2020-08-09 10:08:35 +01:00
# if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)
2019-08-19 12:21:54 +01:00
case ' P ' :
{ char * ep = strchr ( cp , ' : ' ) ;
if ( ep ) {
* ep = 0 ;
ep + + ;
Draw_RGB_Bitmap ( cp , disp_xpos , disp_ypos ) ;
cp = ep ;
}
}
break ;
2020-09-11 14:45:21 +01:00
# endif // USE_SCRIPT_FATFS
2018-08-28 17:13:14 +01:00
case ' h ' :
// hor line to
var = atoiv ( cp , & temp ) ;
cp + = var ;
if ( temp < 0 ) {
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > writeFastHLine ( disp_xpos + temp , disp_ypos , - temp , fg_color ) ;
else DisplayDrawHLine ( disp_xpos + temp , disp_ypos , - temp , fg_color ) ;
2018-08-28 17:13:14 +01:00
} else {
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > writeFastHLine ( disp_xpos , disp_ypos , temp , fg_color ) ;
else DisplayDrawHLine ( disp_xpos , disp_ypos , temp , fg_color ) ;
2018-08-28 17:13:14 +01:00
}
disp_xpos + = temp ;
break ;
case ' v ' :
// vert line to
var = atoiv ( cp , & temp ) ;
cp + = var ;
if ( temp < 0 ) {
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > writeFastVLine ( disp_xpos , disp_ypos + temp , - temp , fg_color ) ;
else DisplayDrawVLine ( disp_xpos , disp_ypos + temp , - temp , fg_color ) ;
2018-08-28 17:13:14 +01:00
} else {
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > writeFastVLine ( disp_xpos , disp_ypos , temp , fg_color ) ;
else DisplayDrawVLine ( disp_xpos , disp_ypos , temp , fg_color ) ;
2018-08-28 17:13:14 +01:00
}
disp_ypos + = temp ;
break ;
case ' L ' :
// any line to
var = atoiv ( cp , & temp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & temp1 ) ;
cp + = var ;
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > writeLine ( disp_xpos , disp_ypos , temp , temp1 , fg_color ) ;
else DisplayDrawLine ( disp_xpos , disp_ypos , temp , temp1 , fg_color ) ;
2018-08-28 17:13:14 +01:00
disp_xpos + = temp ;
disp_ypos + = temp1 ;
break ;
case ' k ' :
// circle
var = atoiv ( cp , & temp ) ;
cp + = var ;
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > drawCircle ( disp_xpos , disp_ypos , temp , fg_color ) ;
else DisplayDrawCircle ( disp_xpos , disp_ypos , temp , fg_color ) ;
2018-08-28 17:13:14 +01:00
break ;
case ' K ' :
// filled circle
var = atoiv ( cp , & temp ) ;
cp + = var ;
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > fillCircle ( disp_xpos , disp_ypos , temp , fg_color ) ;
else DisplayDrawFilledCircle ( disp_xpos , disp_ypos , temp , fg_color ) ;
2018-08-28 17:13:14 +01:00
break ;
case ' r ' :
// rectangle
var = atoiv ( cp , & temp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & temp1 ) ;
cp + = var ;
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > drawRect ( disp_xpos , disp_ypos , temp , temp1 , fg_color ) ;
else DisplayDrawRectangle ( disp_xpos , disp_ypos , temp , temp1 , fg_color ) ;
2018-08-28 17:13:14 +01:00
break ;
case ' R ' :
// filled rectangle
var = atoiv ( cp , & temp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & temp1 ) ;
cp + = var ;
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > fillRect ( disp_xpos , disp_ypos , temp , temp1 , fg_color ) ;
else DisplayDrawFilledRectangle ( disp_xpos , disp_ypos , temp , temp1 , fg_color ) ;
break ;
case ' u ' :
// rounded rectangle
{ int16_t rad ;
var = atoiv ( cp , & temp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & temp1 ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & rad ) ;
cp + = var ;
if ( renderer ) renderer - > drawRoundRect ( disp_xpos , disp_ypos , temp , temp1 , rad , fg_color ) ;
//else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color);
}
break ;
case ' U ' :
// rounded rectangle
{ int16_t rad ;
var = atoiv ( cp , & temp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & temp1 ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & rad ) ;
cp + = var ;
if ( renderer ) renderer - > fillRoundRect ( disp_xpos , disp_ypos , temp , temp1 , rad , fg_color ) ;
//else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color);
}
2018-08-28 17:13:14 +01:00
break ;
2019-08-19 12:21:54 +01:00
2019-01-09 13:14:55 +00:00
case ' t ' :
2019-08-19 12:21:54 +01:00
if ( * cp = = ' S ' ) {
cp + + ;
if ( dp < ( linebuf + DISPLAY_BUFFER_COLS ) - 8 ) {
snprintf_P ( dp , 9 , PSTR ( " %02d " D_HOUR_MINUTE_SEPARATOR " %02d " D_MINUTE_SECOND_SEPARATOR " %02d " ) , RtcTime . hour , RtcTime . minute , RtcTime . second ) ;
dp + = 8 ;
}
} else {
if ( dp < ( linebuf + DISPLAY_BUFFER_COLS ) - 5 ) {
snprintf_P ( dp , 6 , PSTR ( " %02d " D_HOUR_MINUTE_SEPARATOR " %02d " ) , RtcTime . hour , RtcTime . minute ) ;
dp + = 5 ;
}
2018-08-28 17:13:14 +01:00
}
break ;
2020-09-15 11:02:27 +01:00
case ' T ' : {
uint8_t param1 = RtcTime . day_of_month ;
uint8_t param2 = RtcTime . month ;
if ( * cp = = ' U ' ) {
cp + + ;
param1 = RtcTime . month ;
param2 = RtcTime . day_of_month ;
}
2019-01-09 13:14:55 +00:00
if ( dp < ( linebuf + DISPLAY_BUFFER_COLS ) - 8 ) {
2020-09-15 11:02:27 +01:00
snprintf_P ( dp , 9 , PSTR ( " %02d " D_MONTH_DAY_SEPARATOR " %02d " D_YEAR_MONTH_SEPARATOR " %02d " ) , param1 , param2 , RtcTime . year % 2000 ) ;
2019-01-09 13:14:55 +00:00
dp + = 8 ;
}
2020-09-15 11:02:27 +01:00
break ; }
2018-08-28 17:13:14 +01:00
case ' d ' :
// force draw grafics buffer
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > Updateframe ( ) ;
else DisplayDrawFrame ( ) ;
2018-08-28 17:13:14 +01:00
break ;
case ' D ' :
// set auto draw mode
2019-08-19 12:21:54 +01:00
auto_draw = * cp & 3 ;
if ( renderer ) renderer - > setDrawMode ( auto_draw > > 1 ) ;
2018-08-28 17:13:14 +01:00
cp + = 1 ;
break ;
case ' s ' :
// size sx
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > setTextSize ( * cp & 7 ) ;
else DisplaySetSize ( * cp & 3 ) ;
2018-08-28 17:13:14 +01:00
cp + = 1 ;
break ;
case ' f ' :
// font sx
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > setTextFont ( * cp & 7 ) ;
else DisplaySetFont ( * cp & 7 ) ;
2018-08-28 17:13:14 +01:00
cp + = 1 ;
break ;
case ' a ' :
// rotation angle
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > setRotation ( * cp & 3 ) ;
else DisplaySetRotation ( * cp & 3 ) ;
2018-08-28 17:13:14 +01:00
cp + = 1 ;
break ;
2019-08-19 12:21:54 +01:00
# ifdef USE_GRAPH
case ' G ' :
// define graph
if ( * cp = = ' d ' ) {
cp + + ;
var = atoiv ( cp , & temp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & temp1 ) ;
cp + = var ;
RedrawGraph ( temp , temp1 ) ;
break ;
}
# if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)
if ( * cp = = ' s ' ) {
cp + + ;
var = atoiv ( cp , & temp ) ;
cp + = var ;
cp + + ;
// path
char bbuff [ 128 ] ;
cp = get_string ( bbuff , sizeof ( bbuff ) , cp ) ;
Save_graph ( temp , bbuff ) ;
break ;
}
if ( * cp = = ' r ' ) {
cp + + ;
var = atoiv ( cp , & temp ) ;
cp + = var ;
cp + + ;
// path
char bbuff [ 128 ] ;
cp = get_string ( bbuff , sizeof ( bbuff ) , cp ) ;
Restore_graph ( temp , bbuff ) ;
break ;
}
2020-09-11 14:45:21 +01:00
# endif // USE_SCRIPT_FATFS
2019-08-19 12:21:54 +01:00
{ int16_t num , gxp , gyp , gxs , gys , dec , icol ;
float ymin , ymax ;
var = atoiv ( cp , & num ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & gxp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & gyp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & gxs ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & gys ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & dec ) ;
cp + = var ;
cp + + ;
var = fatoiv ( cp , & ymin ) ;
cp + = var ;
cp + + ;
var = fatoiv ( cp , & ymax ) ;
cp + = var ;
if ( color_type = = COLOR_COLOR ) {
// color graph requires channel color
cp + + ;
var = atoiv ( cp , & icol ) ;
cp + = var ;
} else {
icol = 0 ;
}
DefineGraph ( num , gxp , gyp , gxs , gys , dec , ymin , ymax , icol ) ;
}
break ;
case ' g ' :
{ float temp ;
int16_t num ;
var = atoiv ( cp , & num ) ;
cp + = var ;
cp + + ;
var = fatoiv ( cp , & temp ) ;
cp + = var ;
AddValue ( num , temp ) ;
}
break ;
2020-09-11 14:45:21 +01:00
# endif // USE_GRAPH
2019-08-19 12:21:54 +01:00
# ifdef USE_AWATCH
case ' w ' :
var = atoiv ( cp , & temp ) ;
cp + = var ;
DrawAClock ( temp ) ;
break ;
2020-09-11 14:45:21 +01:00
# endif // USE_AWATCH
2019-08-19 12:21:54 +01:00
# ifdef USE_TOUCH_BUTTONS
case ' b ' :
2020-09-03 11:26:03 +01:00
{ int16_t num , gxp , gyp , gxs , gys , outline , fill , textcolor , textsize ; uint8_t dflg = 1 ;
if ( * cp = = ' e ' | | * cp = = ' d ' ) {
// enable disable
uint8_t dis = 0 ;
if ( * cp = = ' d ' ) dis = 1 ;
cp + + ;
var = atoiv ( cp , & num ) ;
num = num % MAXBUTTONS ;
cp + = var ;
if ( buttons [ num ] ) {
buttons [ num ] - > vpower . disable = dis ;
if ( ! dis ) {
if ( buttons [ num ] - > vpower . is_virtual ) buttons [ num ] - > xdrawButton ( buttons [ num ] - > vpower . on_off ) ;
2020-10-28 18:03:39 +00:00
else buttons [ num ] - > xdrawButton ( bitRead ( TasmotaGlobal . power , num ) ) ;
2020-09-03 11:26:03 +01:00
}
}
break ;
}
if ( * cp = = ' - ' ) {
cp + + ;
dflg = 0 ;
}
2019-08-19 12:21:54 +01:00
var = atoiv ( cp , & num ) ;
cp + = var ;
cp + + ;
uint8_t bflags = num > > 8 ;
num = num % MAXBUTTONS ;
var = atoiv ( cp , & gxp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & gyp ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & gxs ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & gys ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & outline ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & fill ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & textcolor ) ;
cp + = var ;
cp + + ;
var = atoiv ( cp , & textsize ) ;
cp + = var ;
cp + + ;
// text itself
char bbuff [ 32 ] ;
cp = get_string ( bbuff , sizeof ( bbuff ) , cp ) ;
if ( buttons [ num ] ) {
delete buttons [ num ] ;
}
if ( renderer ) {
buttons [ num ] = new VButton ( ) ;
if ( buttons [ num ] ) {
buttons [ num ] - > initButtonUL ( renderer , gxp , gyp , gxs , gys , renderer - > GetColorFromIndex ( outline ) , \
2020-09-03 11:26:03 +01:00
renderer - > GetColorFromIndex ( fill ) , renderer - > GetColorFromIndex ( textcolor ) , bbuff , textsize ) ;
2019-08-19 12:21:54 +01:00
if ( ! bflags ) {
// power button
2020-10-28 18:03:39 +00:00
if ( dflg ) buttons [ num ] - > xdrawButton ( bitRead ( TasmotaGlobal . power , num ) ) ;
2020-09-03 11:26:03 +01:00
buttons [ num ] - > vpower . is_virtual = 0 ;
2019-08-19 12:21:54 +01:00
} else {
// virtual button
2020-09-03 11:26:03 +01:00
buttons [ num ] - > vpower . is_virtual = 1 ;
if ( bflags = = 2 ) {
// push
buttons [ num ] - > vpower . is_pushbutton = 1 ;
} else {
// toggle
buttons [ num ] - > vpower . is_pushbutton = 0 ;
}
if ( dflg ) buttons [ num ] - > xdrawButton ( buttons [ num ] - > vpower . on_off ) ;
2020-09-11 14:45:21 +01:00
buttons [ num ] - > vpower . disable = ! dflg ;
2019-08-19 12:21:54 +01:00
}
}
}
}
break ;
2020-09-11 14:45:21 +01:00
# endif // USE_TOUCH_BUTTONS
2018-08-28 17:13:14 +01:00
default :
// unknown escape
2019-03-23 16:00:59 +00:00
Response_P ( PSTR ( " Unknown Escape " ) ) ;
2018-08-28 17:13:14 +01:00
goto exit ;
break ;
}
}
2019-01-09 13:56:16 +00:00
}
2018-08-28 17:13:14 +01:00
}
exit :
// now draw buffer
2020-01-01 14:13:58 +00:00
dp - = decode_te ( linebuf ) ;
2019-08-19 12:21:54 +01:00
if ( ( uint32_t ) dp - ( uint32_t ) linebuf ) {
2020-01-01 14:13:58 +00:00
if ( ! fill ) {
* dp = 0 ;
} else {
2020-04-10 17:24:08 +01:00
linebuf [ abs ( int ( fill ) ) ] = 0 ;
2020-01-01 14:13:58 +00:00
}
2019-08-19 12:21:54 +01:00
if ( fill < 0 ) {
// right align
alignright ( linebuf ) ;
}
if ( col > 0 & & lin > 0 ) {
// use col and lin
if ( ! renderer ) DisplayDrawStringAt ( col , lin , linebuf , fg_color , 1 ) ;
else renderer - > DrawStringAt ( col , lin , linebuf , fg_color , 1 ) ;
} else {
// use disp_xpos, disp_ypos
if ( ! renderer ) DisplayDrawStringAt ( disp_xpos , disp_ypos , linebuf , fg_color , 0 ) ;
else renderer - > DrawStringAt ( disp_xpos , disp_ypos , linebuf , fg_color , 0 ) ;
}
}
// draw buffer
if ( auto_draw & 1 ) {
if ( renderer ) renderer - > Updateframe ( ) ;
else DisplayDrawFrame ( ) ;
2018-08-28 17:13:14 +01:00
}
}
/*********************************************************************************************/
# ifdef USE_DISPLAY_MODES1TO5
2018-11-14 13:32:09 +00:00
void DisplayClearScreenBuffer ( void )
2018-08-28 17:13:14 +01:00
{
if ( disp_screen_buffer_cols ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < disp_screen_buffer_rows ; i + + ) {
2018-08-28 17:13:14 +01:00
memset ( disp_screen_buffer [ i ] , 0 , disp_screen_buffer_cols ) ;
}
}
}
2018-11-14 13:32:09 +00:00
void DisplayFreeScreenBuffer ( void )
2018-08-28 17:13:14 +01:00
{
2019-03-26 17:26:50 +00:00
if ( disp_screen_buffer ! = nullptr ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < disp_screen_buffer_rows ; i + + ) {
2019-03-26 17:26:50 +00:00
if ( disp_screen_buffer [ i ] ! = nullptr ) { free ( disp_screen_buffer [ i ] ) ; }
2018-08-28 17:13:14 +01:00
}
free ( disp_screen_buffer ) ;
disp_screen_buffer_cols = 0 ;
disp_screen_buffer_rows = 0 ;
}
}
2018-11-14 13:32:09 +00:00
void DisplayAllocScreenBuffer ( void )
2018-08-28 17:13:14 +01:00
{
if ( ! disp_screen_buffer_cols ) {
disp_screen_buffer_rows = Settings . display_rows ;
disp_screen_buffer = ( char * * ) malloc ( sizeof ( * disp_screen_buffer ) * disp_screen_buffer_rows ) ;
2019-03-26 17:26:50 +00:00
if ( disp_screen_buffer ! = nullptr ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < disp_screen_buffer_rows ; i + + ) {
2018-08-28 17:13:14 +01:00
disp_screen_buffer [ i ] = ( char * ) malloc ( sizeof ( * disp_screen_buffer [ i ] ) * ( Settings . display_cols [ 0 ] + 1 ) ) ;
2019-03-26 17:26:50 +00:00
if ( disp_screen_buffer [ i ] = = nullptr ) {
2018-08-28 17:13:14 +01:00
DisplayFreeScreenBuffer ( ) ;
break ;
}
}
}
2019-03-26 17:26:50 +00:00
if ( disp_screen_buffer ! = nullptr ) {
2018-08-28 17:13:14 +01:00
disp_screen_buffer_cols = Settings . display_cols [ 0 ] + 1 ;
DisplayClearScreenBuffer ( ) ;
}
}
}
2018-11-14 13:32:09 +00:00
void DisplayReAllocScreenBuffer ( void )
2018-08-28 17:13:14 +01:00
{
DisplayFreeScreenBuffer ( ) ;
DisplayAllocScreenBuffer ( ) ;
}
2019-08-02 16:22:38 +01:00
void DisplayFillScreen ( uint32_t line )
2018-08-31 11:17:09 +01:00
{
2019-08-02 16:22:38 +01:00
uint32_t len = disp_screen_buffer_cols - strlen ( disp_screen_buffer [ line ] ) ;
2018-08-31 11:17:09 +01:00
if ( len ) {
memset ( disp_screen_buffer [ line ] + strlen ( disp_screen_buffer [ line ] ) , 0x20 , len ) ;
disp_screen_buffer [ line ] [ disp_screen_buffer_cols - 1 ] = 0 ;
}
}
2018-08-28 17:13:14 +01:00
/*-------------------------------------------------------------------------------------------*/
2018-11-14 13:32:09 +00:00
void DisplayClearLogBuffer ( void )
2018-08-28 17:13:14 +01:00
{
if ( disp_log_buffer_cols ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < DISPLAY_LOG_ROWS ; i + + ) {
2018-08-28 17:13:14 +01:00
memset ( disp_log_buffer [ i ] , 0 , disp_log_buffer_cols ) ;
}
}
}
2018-11-14 13:32:09 +00:00
void DisplayFreeLogBuffer ( void )
2018-08-28 17:13:14 +01:00
{
2019-03-26 17:26:50 +00:00
if ( disp_log_buffer ! = nullptr ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < DISPLAY_LOG_ROWS ; i + + ) {
2019-03-26 17:26:50 +00:00
if ( disp_log_buffer [ i ] ! = nullptr ) { free ( disp_log_buffer [ i ] ) ; }
2018-08-28 17:13:14 +01:00
}
free ( disp_log_buffer ) ;
disp_log_buffer_cols = 0 ;
}
}
2018-11-14 13:32:09 +00:00
void DisplayAllocLogBuffer ( void )
2018-08-28 17:13:14 +01:00
{
if ( ! disp_log_buffer_cols ) {
disp_log_buffer = ( char * * ) malloc ( sizeof ( * disp_log_buffer ) * DISPLAY_LOG_ROWS ) ;
2019-03-26 17:26:50 +00:00
if ( disp_log_buffer ! = nullptr ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < DISPLAY_LOG_ROWS ; i + + ) {
2018-08-28 17:13:14 +01:00
disp_log_buffer [ i ] = ( char * ) malloc ( sizeof ( * disp_log_buffer [ i ] ) * ( Settings . display_cols [ 0 ] + 1 ) ) ;
2019-03-26 17:26:50 +00:00
if ( disp_log_buffer [ i ] = = nullptr ) {
2018-08-28 17:13:14 +01:00
DisplayFreeLogBuffer ( ) ;
break ;
}
}
}
2019-03-26 17:26:50 +00:00
if ( disp_log_buffer ! = nullptr ) {
2018-08-28 17:13:14 +01:00
disp_log_buffer_cols = Settings . display_cols [ 0 ] + 1 ;
DisplayClearLogBuffer ( ) ;
}
}
}
2018-11-14 13:32:09 +00:00
void DisplayReAllocLogBuffer ( void )
2018-08-28 17:13:14 +01:00
{
DisplayFreeLogBuffer ( ) ;
DisplayAllocLogBuffer ( ) ;
}
2018-08-31 11:17:09 +01:00
void DisplayLogBufferAdd ( char * txt )
2018-08-28 17:13:14 +01:00
{
2018-08-31 11:17:09 +01:00
if ( disp_log_buffer_cols ) {
strlcpy ( disp_log_buffer [ disp_log_buffer_idx ] , txt , disp_log_buffer_cols ) ; // This preserves the % sign where printf won't
disp_log_buffer_idx + + ;
if ( DISPLAY_LOG_ROWS = = disp_log_buffer_idx ) { disp_log_buffer_idx = 0 ; }
2018-08-28 17:13:14 +01:00
}
}
2018-08-31 11:17:09 +01:00
char * DisplayLogBuffer ( char temp_code )
2018-08-28 17:13:14 +01:00
{
2019-03-26 17:26:50 +00:00
char * result = nullptr ;
2018-08-31 11:17:09 +01:00
if ( disp_log_buffer_cols ) {
if ( disp_log_buffer_idx ! = disp_log_buffer_ptr ) {
result = disp_log_buffer [ disp_log_buffer_ptr ] ;
disp_log_buffer_ptr + + ;
if ( DISPLAY_LOG_ROWS = = disp_log_buffer_ptr ) { disp_log_buffer_ptr = 0 ; }
2018-08-28 17:13:14 +01:00
2018-08-31 11:17:09 +01:00
char * pch = strchr ( result , ' ~ ' ) ; // = 0x7E (~) Replace degrees character (276 octal)
2019-03-26 17:26:50 +00:00
if ( pch ! = nullptr ) { result [ pch - result ] = temp_code ; }
2018-08-28 17:13:14 +01:00
}
}
2018-08-31 11:17:09 +01:00
return result ;
2018-08-28 17:13:14 +01:00
}
2018-11-14 13:32:09 +00:00
void DisplayLogBufferInit ( void )
2018-08-28 17:13:14 +01:00
{
if ( Settings . display_mode ) {
disp_log_buffer_idx = 0 ;
disp_log_buffer_ptr = 0 ;
disp_refresh = Settings . display_refresh ;
snprintf_P ( disp_temp , sizeof ( disp_temp ) , PSTR ( " %c " ) , TempUnit ( ) ) ;
2019-10-18 17:06:17 +01:00
snprintf_P ( disp_pres , sizeof ( disp_pres ) , PressureUnit ( ) . c_str ( ) ) ;
2018-08-28 17:13:14 +01:00
2018-08-31 11:17:09 +01:00
DisplayReAllocLogBuffer ( ) ;
2018-08-28 17:13:14 +01:00
2018-09-05 14:38:48 +01:00
char buffer [ 40 ] ;
2020-10-30 11:29:48 +00:00
snprintf_P ( buffer , sizeof ( buffer ) , PSTR ( D_VERSION " %s%s " ) , TasmotaGlobal . version , TasmotaGlobal . image_name ) ;
2018-08-31 11:17:09 +01:00
DisplayLogBufferAdd ( buffer ) ;
snprintf_P ( buffer , sizeof ( buffer ) , PSTR ( " Display mode %d " ) , Settings . display_mode ) ;
DisplayLogBufferAdd ( buffer ) ;
2018-09-05 14:38:48 +01:00
2020-06-15 17:27:04 +01:00
snprintf_P ( buffer , sizeof ( buffer ) , PSTR ( D_CMND_HOSTNAME " %s " ) , NetworkHostname ( ) ) ;
2018-09-05 14:38:48 +01:00
DisplayLogBufferAdd ( buffer ) ;
2020-06-15 17:27:04 +01:00
snprintf_P ( buffer , sizeof ( buffer ) , PSTR ( D_JSON_MAC " %s " ) , NetworkMacAddress ( ) . c_str ( ) ) ;
2018-09-05 14:38:48 +01:00
DisplayLogBufferAdd ( buffer ) ;
2020-06-15 17:27:04 +01:00
snprintf_P ( buffer , sizeof ( buffer ) , PSTR ( " IP %s " ) , NetworkAddress ( ) . toString ( ) . c_str ( ) ) ;
2018-09-05 14:38:48 +01:00
DisplayLogBufferAdd ( buffer ) ;
2020-10-30 11:29:48 +00:00
if ( ! TasmotaGlobal . global_state . wifi_down ) {
2020-06-15 17:27:04 +01:00
snprintf_P ( buffer , sizeof ( buffer ) , PSTR ( D_JSON_SSID " %s " ) , SettingsText ( SET_STASSID1 + Settings . sta_active ) ) ;
2018-09-05 14:38:48 +01:00
DisplayLogBufferAdd ( buffer ) ;
snprintf_P ( buffer , sizeof ( buffer ) , PSTR ( D_JSON_RSSI " %d%% " ) , WifiGetRssiAsQuality ( WiFi . RSSI ( ) ) ) ;
DisplayLogBufferAdd ( buffer ) ;
}
2018-08-28 17:13:14 +01:00
}
}
/*********************************************************************************************\
* Sensors
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
enum SensorQuantity {
JSON_TEMPERATURE ,
JSON_HUMIDITY , JSON_LIGHT , JSON_NOISE , JSON_AIRQUALITY ,
JSON_PRESSURE , JSON_PRESSUREATSEALEVEL ,
JSON_ILLUMINANCE ,
JSON_GAS ,
JSON_YESTERDAY , JSON_TOTAL , JSON_TODAY ,
JSON_PERIOD ,
JSON_POWERFACTOR , JSON_COUNTER , JSON_ANALOG_INPUT , JSON_UV_LEVEL ,
JSON_CURRENT ,
JSON_VOLTAGE ,
JSON_POWERUSAGE ,
2018-11-04 17:00:07 +00:00
JSON_CO2 ,
JSON_FREQUENCY } ;
2018-08-28 17:13:14 +01:00
const char kSensorQuantity [ ] PROGMEM =
D_JSON_TEMPERATURE " | " // degrees
D_JSON_HUMIDITY " | " D_JSON_LIGHT " | " D_JSON_NOISE " | " D_JSON_AIRQUALITY " | " // percentage
D_JSON_PRESSURE " | " D_JSON_PRESSUREATSEALEVEL " | " // hPa
D_JSON_ILLUMINANCE " | " // lx
D_JSON_GAS " | " // kOhm
D_JSON_YESTERDAY " | " D_JSON_TOTAL " | " D_JSON_TODAY " | " // kWh
D_JSON_PERIOD " | " // Wh
D_JSON_POWERFACTOR " | " D_JSON_COUNTER " | " D_JSON_ANALOG_INPUT " | " D_JSON_UV_LEVEL " | " // No unit
D_JSON_CURRENT " | " // Ampere
D_JSON_VOLTAGE " | " // Volt
D_JSON_POWERUSAGE " | " // Watt
2018-11-04 17:00:07 +00:00
D_JSON_CO2 " | " // ppm
D_JSON_FREQUENCY ; // Hz
2018-08-28 17:13:14 +01:00
2019-02-24 16:29:28 +00:00
void DisplayJsonValue ( const char * topic , const char * device , const char * mkey , const char * value )
2018-08-28 17:13:14 +01:00
{
char quantity [ TOPSZ ] ;
2018-08-31 11:17:09 +01:00
char buffer [ Settings . display_cols [ 0 ] + 1 ] ;
2018-08-28 17:13:14 +01:00
char spaces [ Settings . display_cols [ 0 ] ] ;
char source [ Settings . display_cols [ 0 ] - Settings . display_cols [ 1 ] ] ;
char svalue [ Settings . display_cols [ 1 ] + 1 ] ;
2019-03-11 09:38:41 +00:00
# ifdef USE_DEBUG_DRIVER
2018-08-28 17:13:14 +01:00
ShowFreeMem ( PSTR ( " DisplayJsonValue " ) ) ;
2019-03-11 09:38:41 +00:00
# endif
2018-08-28 17:13:14 +01:00
memset ( spaces , 0x20 , sizeof ( spaces ) ) ;
spaces [ sizeof ( spaces ) - 1 ] = ' \0 ' ;
2019-02-24 16:29:28 +00:00
snprintf_P ( source , sizeof ( source ) , PSTR ( " %s%s%s%s " ) , topic , ( strlen ( topic ) ) ? " / " : " " , mkey , spaces ) ; // pow1/Voltage or Voltage if topic is empty (local sensor)
2018-08-28 17:13:14 +01:00
int quantity_code = GetCommandCode ( quantity , sizeof ( quantity ) , mkey , kSensorQuantity ) ;
if ( ( - 1 = = quantity_code ) | | ! strcmp_P ( mkey , S_RSLT_POWER ) ) { // Ok: Power, Not ok: POWER
return ;
}
if ( JSON_TEMPERATURE = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s~%s " ) , value , disp_temp ) ;
}
else if ( ( quantity_code > = JSON_HUMIDITY ) & & ( quantity_code < = JSON_AIRQUALITY ) ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s%% " ) , value ) ;
}
else if ( ( quantity_code > = JSON_PRESSURE ) & & ( quantity_code < = JSON_PRESSUREATSEALEVEL ) ) {
2019-10-18 17:06:17 +01:00
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s%s " ) , value , disp_pres ) ;
2018-08-28 17:13:14 +01:00
}
else if ( JSON_ILLUMINANCE = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_LUX ) , value ) ;
}
else if ( JSON_GAS = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_KILOOHM ) , value ) ;
}
else if ( ( quantity_code > = JSON_YESTERDAY ) & & ( quantity_code < = JSON_TODAY ) ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_KILOWATTHOUR ) , value ) ;
}
else if ( JSON_PERIOD = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_WATTHOUR ) , value ) ;
}
else if ( ( quantity_code > = JSON_POWERFACTOR ) & & ( quantity_code < = JSON_UV_LEVEL ) ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " ) , value ) ;
}
else if ( JSON_CURRENT = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_AMPERE ) , value ) ;
}
else if ( JSON_VOLTAGE = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_VOLT ) , value ) ;
}
else if ( JSON_POWERUSAGE = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_WATT ) , value ) ;
}
else if ( JSON_CO2 = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_PARTS_PER_MILLION ) , value ) ;
}
2018-11-04 17:00:07 +00:00
else if ( JSON_FREQUENCY = = quantity_code ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " %s " D_UNIT_HERTZ ) , value ) ;
}
2018-08-31 11:17:09 +01:00
snprintf_P ( buffer , sizeof ( buffer ) , PSTR ( " %s %s " ) , source , svalue ) ;
2018-08-28 17:13:14 +01:00
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "mkey [%s], source [%s], value [%s], quantity_code %d, log_buffer [%s]"), mkey, source, value, quantity_code, buffer);
2018-08-28 17:13:14 +01:00
2018-08-31 11:17:09 +01:00
DisplayLogBufferAdd ( buffer ) ;
2018-08-28 17:13:14 +01:00
}
void DisplayAnalyzeJson ( char * topic , char * json )
{
// //tele/pow2/STATE {"Time":"2017-09-20T11:53:03", "Uptime":10, "Vcc":3.123, "POWER":"ON", "Wifi":{"AP":2, "SSId":"indebuurt2", "RSSI":68, "APMac":"00:22:6B:FE:8E:20"}}
// //tele/pow2/ENERGY {"Time":"2017-09-20T11:53:03", "Total":6.522, "Yesterday":0.150, "Today":0.073, "Period":0.5, "Power":12.1, "Factor":0.56, "Voltage":210.1, "Current":0.102}
// tele/pow1/SENSOR = {"Time":"2018-01-02T17:13:17","ENERGY":{"Total":13.091,"Yesterday":0.060,"Today":0.046,"Period":0.2,"Power":9.8,"Factor":0.49,"Voltage":206.8,"Current":0.096}}
// tele/dual/STATE {"Time":"2017-09-20T11:53:03","Uptime":25,"Vcc":3.178,"POWER1":"OFF","POWER2":"OFF","Wifi":{"AP":2,"SSId":"indebuurt2","RSSI":100,"APMac":"00:22:6B:FE:8E:20"}}
// tele/sc/SENSOR {"Time":"2017-09-20T11:53:09","Temperature":24.0,"Humidity":16.0,"Light":30,"Noise":20,"AirQuality":100,"TempUnit":"C"}
// tele/rf1/SENSOR {"Time":"2017-09-20T11:53:23","BH1750":{"Illuminance":57}}
// tele/wemos5/SENSOR {"Time":"2017-09-20T11:53:53","SHT1X":{"Temperature":20.1,"Humidity":58.9},"HTU21":{"Temperature":20.7,"Humidity":58.5},"BMP280":{"Temperature":21.6,"Pressure":1020.3},"TempUnit":"C"}
// tele/th1/SENSOR {"Time":"2017-09-20T11:54:48","DS18B20":{"Temperature":49.7},"TempUnit":"C"}
String jsonStr = json ; // Move from stack to heap to fix watchdogs (20180626)
2020-09-24 15:40:03 +01:00
JsonParser parser ( ( char * ) jsonStr . c_str ( ) ) ;
JsonParserObject root = parser . getRootObject ( ) ;
if ( root ) { // did JSON parsing went ok?
2018-08-28 17:13:14 +01:00
2020-09-24 15:40:03 +01:00
const char * unit = root . getStr ( PSTR ( D_JSON_TEMPERATURE_UNIT ) , nullptr ) ; // nullptr if not found
2019-10-18 17:06:17 +01:00
if ( unit ) {
snprintf_P ( disp_temp , sizeof ( disp_temp ) , PSTR ( " %s " ) , unit ) ; // C or F
}
2020-09-24 15:40:03 +01:00
unit = root . getStr ( PSTR ( D_JSON_PRESSURE_UNIT ) , nullptr ) ; // nullptr if not found
2019-10-18 17:06:17 +01:00
if ( unit ) {
snprintf_P ( disp_pres , sizeof ( disp_pres ) , PSTR ( " %s " ) , unit ) ; // hPa or mmHg
2018-08-28 17:13:14 +01:00
}
2020-09-24 15:40:03 +01:00
for ( auto key1 : root ) {
JsonParserToken value1 = key1 . getValue ( ) ;
if ( value1 . isObject ( ) ) {
JsonParserObject Object2 = value1 . getObject ( ) ;
for ( auto key2 : Object2 ) {
JsonParserToken value2 = key2 . getValue ( ) ;
if ( value2 . isObject ( ) ) {
JsonParserObject Object3 = value2 . getObject ( ) ;
for ( auto key3 : Object3 ) {
const char * value3 = key3 . getValue ( ) . getStr ( nullptr ) ;
if ( value3 ! = nullptr ) { // "DHT11":{"Temperature":null,"Humidity":null} - ignore null as it will raise exception 28
DisplayJsonValue ( topic , key1 . getStr ( ) , key3 . getStr ( ) , value3 ) ; // Sensor 56%
2019-01-08 13:22:45 +00:00
}
2018-08-28 17:13:14 +01:00
}
} else {
2020-09-24 15:40:03 +01:00
const char * value = value2 . getStr ( nullptr ) ;
2019-01-09 13:56:16 +00:00
if ( value ! = nullptr ) {
2020-09-24 15:40:03 +01:00
DisplayJsonValue ( topic , key1 . getStr ( ) , key2 . getStr ( ) , value ) ; // Sensor 56%
2019-01-08 13:22:45 +00:00
}
2018-08-28 17:13:14 +01:00
}
}
} else {
2020-09-24 15:40:03 +01:00
const char * value = value1 . getStr ( nullptr ) ;
2019-01-09 13:56:16 +00:00
if ( value ! = nullptr ) {
2020-09-24 15:40:03 +01:00
DisplayJsonValue ( topic , key1 . getStr ( ) , key1 . getStr ( ) , value ) ; // Topic 56%
2019-01-08 13:22:45 +00:00
}
2018-08-28 17:13:14 +01:00
}
}
}
}
2018-11-14 13:32:09 +00:00
void DisplayMqttSubscribe ( void )
2018-08-28 17:13:14 +01:00
{
/* Subscribe to tele messages only
* Supports the following FullTopic formats
* - % prefix % / % topic %
* - home / % prefix % / % topic %
* - home / level2 / % prefix % / % topic % etc .
*/
2019-07-01 11:54:26 +01:00
if ( Settings . display_model & & ( Settings . display_mode & 0x04 ) ) {
2018-08-28 17:13:14 +01:00
char stopic [ TOPSZ ] ;
char ntopic [ TOPSZ ] ;
ntopic [ 0 ] = ' \0 ' ;
2019-12-16 14:13:57 +00:00
strlcpy ( stopic , SettingsText ( SET_MQTT_FULLTOPIC ) , sizeof ( stopic ) ) ;
2018-08-28 17:13:14 +01:00
char * tp = strtok ( stopic , " / " ) ;
2019-03-26 17:26:50 +00:00
while ( tp ! = nullptr ) {
2019-03-30 15:29:27 +00:00
if ( ! strcmp_P ( tp , MQTT_TOKEN_PREFIX ) ) {
2018-08-28 17:13:14 +01:00
break ;
}
2018-11-22 14:41:30 +00:00
strncat_P ( ntopic , PSTR ( " +/ " ) , sizeof ( ntopic ) - strlen ( ntopic ) - 1 ) ; // Add single-level wildcards
2019-03-26 17:26:50 +00:00
tp = strtok ( nullptr , " / " ) ;
2018-08-28 17:13:14 +01:00
}
2019-12-16 14:13:57 +00:00
strncat ( ntopic , SettingsText ( SET_MQTTPREFIX3 ) , sizeof ( ntopic ) - strlen ( ntopic ) - 1 ) ; // Subscribe to tele messages
2018-11-22 14:41:30 +00:00
strncat_P ( ntopic , PSTR ( " /# " ) , sizeof ( ntopic ) - strlen ( ntopic ) - 1 ) ; // Add multi-level wildcard
2018-08-28 17:13:14 +01:00
MqttSubscribe ( ntopic ) ;
2019-07-01 11:54:26 +01:00
disp_subscribed = true ;
2018-08-28 17:13:14 +01:00
} else {
2019-07-01 11:54:26 +01:00
disp_subscribed = false ;
2018-08-28 17:13:14 +01:00
}
}
2019-01-28 13:08:33 +00:00
bool DisplayMqttData ( void )
2018-08-28 17:13:14 +01:00
{
if ( disp_subscribed ) {
char stopic [ TOPSZ ] ;
2019-12-16 14:13:57 +00:00
snprintf_P ( stopic , sizeof ( stopic ) , PSTR ( " %s/ " ) , SettingsText ( SET_MQTTPREFIX3 ) ) ; // tele/
2018-08-28 17:13:14 +01:00
char * tp = strstr ( XdrvMailbox . topic , stopic ) ;
2019-11-03 14:37:33 +00:00
if ( tp ) { // tele/tasmota/SENSOR
2018-08-28 17:13:14 +01:00
if ( Settings . display_mode & 0x04 ) {
2019-11-03 14:37:33 +00:00
tp = tp + strlen ( stopic ) ; // tasmota/SENSOR
char * topic = strtok ( tp , " / " ) ; // tasmota
2018-08-28 17:13:14 +01:00
DisplayAnalyzeJson ( topic , XdrvMailbox . data ) ;
}
return true ;
}
}
return false ;
}
2018-11-14 13:32:09 +00:00
void DisplayLocalSensor ( void )
2018-08-28 17:13:14 +01:00
{
2020-10-29 12:37:09 +00:00
if ( ( Settings . display_mode & 0x02 ) & & ( 0 = = TasmotaGlobal . tele_period ) ) {
2019-02-24 16:29:28 +00:00
char no_topic [ 1 ] = { 0 } ;
2020-10-30 11:29:48 +00:00
// DisplayAnalyzeJson(TasmotaGlobal.mqtt_topic, TasmotaGlobal.mqtt_data); // Add local topic
DisplayAnalyzeJson ( no_topic , TasmotaGlobal . mqtt_data ) ; // Discard any topic
2018-08-28 17:13:14 +01:00
}
}
# endif // USE_DISPLAY_MODES1TO5
/*********************************************************************************************\
* Public
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-11-14 13:32:09 +00:00
void DisplayInitDriver ( void )
2018-08-28 17:13:14 +01:00
{
XdspCall ( FUNC_DISPLAY_INIT_DRIVER ) ;
2019-09-06 09:11:50 +01:00
if ( renderer ) {
renderer - > setTextFont ( Settings . display_font ) ;
renderer - > setTextSize ( Settings . display_size ) ;
}
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model);
2018-08-28 17:13:14 +01:00
if ( Settings . display_model ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . devices_present + + ;
2020-08-17 13:49:24 +01:00
if ( ! PinUsed ( GPIO_BACKLIGHT ) ) {
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . light_type & & ( 4 = = Settings . display_model ) ) {
TasmotaGlobal . devices_present - - ; // Assume PWM channel is used for backlight
2020-08-17 13:49:24 +01:00
}
2020-08-09 10:10:09 +01:00
}
2020-10-30 11:29:48 +00:00
disp_device = TasmotaGlobal . devices_present ;
2018-08-28 17:13:14 +01:00
# ifndef USE_DISPLAY_MODES1TO5
Settings . display_mode = 0 ;
# else
DisplayLogBufferInit ( ) ;
# endif // USE_DISPLAY_MODES1TO5
}
}
2018-11-14 13:32:09 +00:00
void DisplaySetPower ( void )
2018-08-28 17:13:14 +01:00
{
disp_power = bitRead ( XdrvMailbox . index , disp_device - 1 ) ;
2019-08-19 17:17:44 +01:00
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG, PSTR("DSP: Power %d"), disp_power);
2019-08-19 17:17:44 +01:00
2018-08-28 17:13:14 +01:00
if ( Settings . display_model ) {
2019-08-19 17:17:44 +01:00
if ( ! renderer ) {
XdspCall ( FUNC_DISPLAY_POWER ) ;
} else {
renderer - > DisplayOnff ( disp_power ) ;
}
2018-08-28 17:13:14 +01:00
}
}
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-08-02 16:22:38 +01:00
void CmndDisplay ( void )
2018-08-28 17:13:14 +01:00
{
2019-08-11 17:12:18 +01:00
Response_P ( PSTR ( " { \" " D_PRFX_DISPLAY " \" :{ \" " D_CMND_DISP_MODEL " \" :%d, \" " D_CMND_DISP_WIDTH " \" :%d, \" " D_CMND_DISP_HEIGHT " \" :%d, \" "
2019-08-02 16:22:38 +01:00
D_CMND_DISP_MODE " \" :%d, \" " D_CMND_DISP_DIMMER " \" :%d, \" " D_CMND_DISP_SIZE " \" :%d, \" " D_CMND_DISP_FONT " \" :%d, \" "
D_CMND_DISP_ROTATE " \" :%d, \" " D_CMND_DISP_REFRESH " \" :%d, \" " D_CMND_DISP_COLS " \" :[%d,%d], \" " D_CMND_DISP_ROWS " \" :%d}} " ) ,
Settings . display_model , Settings . display_width , Settings . display_height ,
Settings . display_mode , Settings . display_dimmer , Settings . display_size , Settings . display_font ,
Settings . display_rotate , Settings . display_refresh , Settings . display_cols [ 0 ] , Settings . display_cols [ 1 ] , Settings . display_rows ) ;
}
void CmndDisplayModel ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < DISPLAY_MAX_DRIVERS ) ) {
uint32_t last_display_model = Settings . display_model ;
Settings . display_model = XdrvMailbox . payload ;
if ( XdspCall ( FUNC_DISPLAY_MODEL ) ) {
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ; // Restart to re-init interface and add/Remove MQTT subscribe
2019-08-02 16:22:38 +01:00
} else {
Settings . display_model = last_display_model ;
2018-08-28 17:13:14 +01:00
}
2019-08-02 16:22:38 +01:00
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_model ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplayWidth ( void )
{
if ( XdrvMailbox . payload > 0 ) {
if ( XdrvMailbox . payload ! = Settings . display_width ) {
Settings . display_width = XdrvMailbox . payload ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ; // Restart to re-init width
2019-07-23 13:05:42 +01:00
}
2019-08-02 16:22:38 +01:00
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_width ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplayHeight ( void )
{
if ( XdrvMailbox . payload > 0 ) {
if ( XdrvMailbox . payload ! = Settings . display_height ) {
Settings . display_height = XdrvMailbox . payload ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ; // Restart to re-init height
2019-07-23 13:05:42 +01:00
}
2019-08-02 16:22:38 +01:00
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_height ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplayMode ( void )
{
2018-08-28 17:13:14 +01:00
# ifdef USE_DISPLAY_MODES1TO5
/* Matrix LCD / Oled TFT
* 1 = Text up and time Time
* 2 = Date Local sensors Local sensors
* 3 = Day Local sensors and time Local sensors and time
* 4 = Mqtt left and time Mqtt ( incl local ) sensors Mqtt ( incl local ) sensors
* 5 = Mqtt up and time Mqtt ( incl local ) sensors and time Mqtt ( incl local ) sensors and time
*/
2019-08-02 16:22:38 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 5 ) ) {
uint32_t last_display_mode = Settings . display_mode ;
Settings . display_mode = XdrvMailbox . payload ;
if ( disp_subscribed ! = ( Settings . display_mode & 0x04 ) ) {
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ; // Restart to Add/Remove MQTT subscribe
2019-08-02 16:22:38 +01:00
} else {
if ( last_display_mode & & ! Settings . display_mode ) { // Switch to mode 0
DisplayInit ( DISPLAY_INIT_MODE ) ;
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > fillScreen ( bg_color ) ;
else DisplayClear ( ) ;
2019-08-02 16:22:38 +01:00
} else {
DisplayLogBufferInit ( ) ;
DisplayInit ( DISPLAY_INIT_MODE ) ;
2018-08-28 17:13:14 +01:00
}
}
2019-08-02 16:22:38 +01:00
}
# endif // USE_DISPLAY_MODES1TO5
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_mode ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplayDimmer ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 100 ) ) {
Settings . display_dimmer = ( ( XdrvMailbox . payload + 1 ) * 100 ) / 666 ; // Correction for Domoticz (0 - 15)
if ( Settings . display_dimmer & & ! ( disp_power ) ) {
ExecuteCommandPower ( disp_device , POWER_ON , SRC_DISPLAY ) ;
2018-08-28 17:13:14 +01:00
}
2019-08-02 16:22:38 +01:00
else if ( ! Settings . display_dimmer & & disp_power ) {
ExecuteCommandPower ( disp_device , POWER_OFF , SRC_DISPLAY ) ;
2018-08-28 17:13:14 +01:00
}
2019-08-19 12:21:54 +01:00
if ( renderer ) renderer - > dim ( Settings . display_dimmer ) ;
2019-08-02 16:22:38 +01:00
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_dimmer ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplaySize ( void )
{
if ( ( XdrvMailbox . payload > 0 ) & & ( XdrvMailbox . payload < = 4 ) ) {
Settings . display_size = XdrvMailbox . payload ;
2019-09-06 09:11:50 +01:00
if ( renderer ) renderer - > setTextSize ( Settings . display_size ) ;
else DisplaySetSize ( Settings . display_size ) ;
2019-08-02 16:22:38 +01:00
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_size ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplayFont ( void )
{
2019-09-06 09:11:50 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 4 ) ) {
2019-08-02 16:22:38 +01:00
Settings . display_font = XdrvMailbox . payload ;
2019-09-06 09:11:50 +01:00
if ( renderer ) renderer - > setTextFont ( Settings . display_font ) ;
else DisplaySetFont ( Settings . display_font ) ;
2019-08-02 16:22:38 +01:00
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_font ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplayRotate ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < 4 ) ) {
if ( Settings . display_rotate ! = XdrvMailbox . payload ) {
2018-08-28 17:13:14 +01:00
/*
2019-08-02 16:22:38 +01:00
// Needs font info regarding height and width
if ( ( Settings . display_rotate & 1 ) ! = ( XdrvMailbox . payload & 1 ) ) {
uint8_t temp_rows = Settings . display_rows ;
Settings . display_rows = Settings . display_cols [ 0 ] ;
Settings . display_cols [ 0 ] = temp_rows ;
2018-08-28 17:13:14 +01:00
# ifdef USE_DISPLAY_MODES1TO5
2019-08-02 16:22:38 +01:00
DisplayReAllocScreenBuffer ( ) ;
2018-08-28 17:13:14 +01:00
# endif // USE_DISPLAY_MODES1TO5
2019-08-02 16:22:38 +01:00
}
2018-08-28 17:13:14 +01:00
*/
2019-08-02 16:22:38 +01:00
Settings . display_rotate = XdrvMailbox . payload ;
DisplayInit ( DISPLAY_INIT_MODE ) ;
2018-08-28 17:13:14 +01:00
# ifdef USE_DISPLAY_MODES1TO5
2019-08-02 16:22:38 +01:00
DisplayLogBufferInit ( ) ;
2018-08-28 17:13:14 +01:00
# endif // USE_DISPLAY_MODES1TO5
}
2019-08-02 16:22:38 +01:00
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_rotate ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplayText ( void )
{
if ( disp_device & & XdrvMailbox . data_len > 0 ) {
2018-08-28 17:13:14 +01:00
# ifndef USE_DISPLAY_MODES1TO5
2019-08-02 16:22:38 +01:00
DisplayText ( ) ;
2018-08-28 17:13:14 +01:00
# else
2019-08-02 16:22:38 +01:00
if ( ! Settings . display_mode ) {
DisplayText ( ) ;
} else {
DisplayLogBufferAdd ( XdrvMailbox . data ) ;
2018-08-28 17:13:14 +01:00
}
# endif // USE_DISPLAY_MODES1TO5
2019-08-11 17:12:18 +01:00
ResponseCmndChar ( XdrvMailbox . data ) ;
2019-08-02 16:22:38 +01:00
}
}
void CmndDisplayAddress ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = 8 ) ) {
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 255 ) ) {
Settings . display_address [ XdrvMailbox . index - 1 ] = XdrvMailbox . payload ;
2018-08-28 17:13:14 +01:00
}
2019-08-19 15:49:20 +01:00
ResponseCmndIdxNumber ( Settings . display_address [ XdrvMailbox . index - 1 ] ) ;
2019-08-02 16:22:38 +01:00
}
}
void CmndDisplayRefresh ( void )
{
if ( ( XdrvMailbox . payload > = 1 ) & & ( XdrvMailbox . payload < = 7 ) ) {
Settings . display_refresh = XdrvMailbox . payload ;
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_refresh ) ;
2019-08-02 16:22:38 +01:00
}
void CmndDisplayColumns ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = 2 ) ) {
if ( ( XdrvMailbox . payload > 0 ) & & ( XdrvMailbox . payload < = DISPLAY_MAX_COLS ) ) {
Settings . display_cols [ XdrvMailbox . index - 1 ] = XdrvMailbox . payload ;
2018-08-28 17:13:14 +01:00
# ifdef USE_DISPLAY_MODES1TO5
2019-08-02 16:22:38 +01:00
if ( 1 = = XdrvMailbox . index ) {
2018-08-31 11:17:09 +01:00
DisplayLogBufferInit ( ) ;
DisplayReAllocScreenBuffer ( ) ;
2018-08-28 17:13:14 +01:00
}
2019-08-02 16:22:38 +01:00
# endif // USE_DISPLAY_MODES1TO5
2018-08-28 17:13:14 +01:00
}
2019-08-11 17:12:18 +01:00
ResponseCmndIdxNumber ( Settings . display_cols [ XdrvMailbox . index - 1 ] ) ;
2018-08-28 17:13:14 +01:00
}
2019-08-02 16:22:38 +01:00
}
2018-08-28 17:13:14 +01:00
2019-08-02 16:22:38 +01:00
void CmndDisplayRows ( void )
{
if ( ( XdrvMailbox . payload > 0 ) & & ( XdrvMailbox . payload < = DISPLAY_MAX_ROWS ) ) {
Settings . display_rows = XdrvMailbox . payload ;
# ifdef USE_DISPLAY_MODES1TO5
DisplayLogBufferInit ( ) ;
DisplayReAllocScreenBuffer ( ) ;
# endif // USE_DISPLAY_MODES1TO5
}
2019-08-11 17:12:18 +01:00
ResponseCmndNumber ( Settings . display_rows ) ;
2018-08-28 17:13:14 +01:00
}
2019-08-19 12:21:54 +01:00
/*********************************************************************************************\
* optional drivers
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-05-18 09:25:18 +01:00
# ifdef ESP32
# ifdef JPEG_PICTS
# include "img_converters.h"
# include "esp_jpg_decode.h"
bool jpg2rgb888 ( const uint8_t * src , size_t src_len , uint8_t * out , jpg_scale_t scale ) ;
char get_jpeg_size ( unsigned char * data , unsigned int data_size , unsigned short * width , unsigned short * height ) ;
void rgb888_to_565 ( uint8_t * in , uint16_t * out , uint32_t len ) ;
2020-09-11 14:45:21 +01:00
# endif // JPEG_PICTS
# endif // ESP32
2020-05-18 09:25:18 +01:00
2020-08-09 10:08:35 +01:00
# if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)
2020-05-30 14:29:47 +01:00
extern FS * fsp ;
2020-05-18 09:25:18 +01:00
# define XBUFF_LEN 128
2019-08-19 12:21:54 +01:00
void Draw_RGB_Bitmap ( char * file , uint16_t xp , uint16_t yp ) {
if ( ! renderer ) return ;
File fp ;
2020-05-18 09:25:18 +01:00
char * ending = strrchr ( file , ' . ' ) ;
if ( ! ending ) return ;
ending + + ;
char estr [ 8 ] ;
memset ( estr , 0 , sizeof ( estr ) ) ;
for ( uint32_t cnt = 0 ; cnt < strlen ( ending ) ; cnt + + ) {
estr [ cnt ] = tolower ( ending [ cnt ] ) ;
}
2019-08-19 12:21:54 +01:00
2020-05-18 09:25:18 +01:00
if ( ! strcmp ( estr , " rgb " ) ) {
// special rgb format
2020-05-30 14:29:47 +01:00
fp = fsp - > open ( file , FILE_READ ) ;
2020-05-18 09:25:18 +01:00
if ( ! fp ) return ;
uint16_t xsize ;
fp . read ( ( uint8_t * ) & xsize , 2 ) ;
uint16_t ysize ;
fp . read ( ( uint8_t * ) & ysize , 2 ) ;
2019-08-19 12:21:54 +01:00
# if 1
2020-05-18 09:25:18 +01:00
renderer - > setAddrWindow ( xp , yp , xp + xsize , yp + ysize ) ;
2020-08-09 10:08:35 +01:00
uint16_t rgb [ xsize ] ;
for ( int16_t j = 0 ; j < ysize ; j + + ) {
// for(int16_t i=0; i<xsize; i+=XBUFF_LEN) {
fp . read ( ( uint8_t * ) rgb , xsize * 2 ) ;
renderer - > pushColors ( rgb , xsize , true ) ;
// }
2020-05-18 09:25:18 +01:00
OsWatchLoop ( ) ;
2019-08-19 12:21:54 +01:00
}
2020-05-18 09:25:18 +01:00
renderer - > setAddrWindow ( 0 , 0 , 0 , 0 ) ;
2019-08-19 12:21:54 +01:00
# else
2020-05-18 09:25:18 +01:00
for ( int16_t j = 0 ; j < ysize ; j + + ) {
for ( int16_t i = 0 ; i < xsize ; i + + ) {
uint16_t rgb ;
uint8_t res = fp . read ( ( uint8_t * ) & rgb , 2 ) ;
if ( ! res ) break ;
renderer - > writePixel ( xp + i , yp , rgb ) ;
}
delay ( 0 ) ;
OsWatchLoop ( ) ;
yp + + ;
2019-08-19 12:21:54 +01:00
}
# endif
2020-05-18 09:25:18 +01:00
fp . close ( ) ;
} else if ( ! strcmp ( estr , " jpg " ) ) {
// jpeg files on ESP32 with more memory
# ifdef ESP32
# ifdef JPEG_PICTS
if ( psramFound ( ) ) {
2020-05-30 14:29:47 +01:00
fp = fsp - > open ( file , FILE_READ ) ;
2020-05-18 09:25:18 +01:00
if ( ! fp ) return ;
uint32_t size = fp . size ( ) ;
uint8_t * mem = ( uint8_t * ) heap_caps_malloc ( size + 4 , MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT ) ;
if ( mem ) {
uint8_t res = fp . read ( mem , size ) ;
if ( res ) {
uint16_t xsize ;
uint16_t ysize ;
if ( mem [ 0 ] = = 0xff & & mem [ 1 ] = = 0xd8 ) {
get_jpeg_size ( mem , size , & xsize , & ysize ) ;
2020-05-18 14:02:27 +01:00
//Serial.printf(" x,y %d - %d\n",xsize, ysize );
2020-05-18 09:25:18 +01:00
if ( xsize & & ysize ) {
uint8_t * out_buf = ( uint8_t * ) heap_caps_malloc ( ( xsize * ysize * 3 ) + 4 , MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT ) ;
if ( out_buf ) {
uint8_t * ob = out_buf ;
jpg2rgb888 ( mem , size , out_buf , ( jpg_scale_t ) JPG_SCALE_NONE ) ;
uint16_t pixels = xsize * ysize / XBUFF_LEN ;
renderer - > setAddrWindow ( xp , yp , xp + xsize , yp + ysize ) ;
for ( int32_t j = 0 ; j < pixels ; j + + ) {
uint16_t rbuff [ XBUFF_LEN * 2 ] ;
rgb888_to_565 ( ob , rbuff , XBUFF_LEN ) ;
ob + = XBUFF_LEN * 3 ;
renderer - > pushColors ( rbuff , XBUFF_LEN , true ) ;
OsWatchLoop ( ) ;
}
renderer - > setAddrWindow ( 0 , 0 , 0 , 0 ) ;
free ( out_buf ) ;
}
}
}
}
free ( mem ) ;
}
fp . close ( ) ;
}
# endif // JPEG_PICTS
# endif // ESP32
}
2019-08-19 12:21:54 +01:00
}
2020-09-11 14:45:21 +01:00
# endif // USE_SCRIPT_FATFS
2019-08-19 12:21:54 +01:00
# ifdef USE_AWATCH
# define MINUTE_REDUCT 4
# ifndef pi
# define pi 3.14159265359
# endif
// draw analog watch, just for fun
void DrawAClock ( uint16_t rad ) {
if ( ! renderer ) return ;
float frad = rad ;
uint16_t hred = frad / 3.0 ;
renderer - > fillCircle ( disp_xpos , disp_ypos , rad , bg_color ) ;
renderer - > drawCircle ( disp_xpos , disp_ypos , rad , fg_color ) ;
renderer - > fillCircle ( disp_xpos , disp_ypos , 4 , fg_color ) ;
for ( uint8_t count = 0 ; count < 60 ; count + = 5 ) {
float p1 = ( ( float ) count * ( pi / 30 ) - ( pi / 2 ) ) ;
uint8_t len ;
if ( ( count % 15 ) = = 0 ) {
len = 4 ;
} else {
len = 2 ;
}
renderer - > writeLine ( disp_xpos + ( ( float ) ( rad - len ) * cosf ( p1 ) ) , disp_ypos + ( ( float ) ( rad - len ) * sinf ( p1 ) ) , disp_xpos + ( frad * cosf ( p1 ) ) , disp_ypos + ( frad * sinf ( p1 ) ) , fg_color ) ;
}
// hour
float hour = ( ( float ) RtcTime . hour * 60.0 + ( float ) RtcTime . minute ) / 60.0 ;
float temp = ( hour * ( pi / 6.0 ) - ( pi / 2.0 ) ) ;
renderer - > writeLine ( disp_xpos , disp_ypos , disp_xpos + ( frad - hred ) * cosf ( temp ) , disp_ypos + ( frad - hred ) * sinf ( temp ) , fg_color ) ;
// minute
temp = ( ( float ) RtcTime . minute * ( pi / 30.0 ) - ( pi / 2.0 ) ) ;
renderer - > writeLine ( disp_xpos , disp_ypos , disp_xpos + ( frad - MINUTE_REDUCT ) * cosf ( temp ) , disp_ypos + ( frad - MINUTE_REDUCT ) * sinf ( temp ) , fg_color ) ;
}
2020-09-11 14:45:21 +01:00
# endif // USE_AWATCH
2019-08-19 12:21:54 +01:00
# ifdef USE_GRAPH
typedef union {
uint8_t data ;
struct {
uint8_t overlay : 1 ;
uint8_t draw : 1 ;
uint8_t nu3 : 1 ;
uint8_t nu4 : 1 ;
uint8_t nu5 : 1 ;
uint8_t nu6 : 1 ;
uint8_t nu7 : 1 ;
uint8_t nu8 : 1 ;
} ;
} GFLAGS ;
struct GRAPH {
uint16_t xp ;
uint16_t yp ;
uint16_t xs ;
uint16_t ys ;
float ymin ;
float ymax ;
float range ;
uint32_t x_time ; // time per x slice in milliseconds
uint32_t last_ms ;
uint32_t last_ms_redrawn ;
int16_t decimation ; // decimation or graph duration in minutes
uint16_t dcnt ;
uint32_t summ ;
uint16_t xcnt ;
uint8_t * values ;
uint8_t xticks ;
uint8_t yticks ;
uint8_t last_val ;
uint8_t color_index ;
GFLAGS flags ;
} ;
struct GRAPH * graph [ NUM_GRAPHS ] ;
# define TICKLEN 4
void ClrGraph ( uint16_t num ) {
struct GRAPH * gp = graph [ num ] ;
uint16_t xticks = gp - > xticks ;
uint16_t yticks = gp - > yticks ;
uint16_t count ;
// clr inside, but only 1.graph if overlapped
if ( gp - > flags . overlay ) return ;
renderer - > fillRect ( gp - > xp + 1 , gp - > yp + 1 , gp - > xs - 2 , gp - > ys - 2 , bg_color ) ;
if ( xticks ) {
float cxp = gp - > xp , xd = ( float ) gp - > xs / ( float ) xticks ;
for ( count = 0 ; count < xticks ; count + + ) {
renderer - > writeFastVLine ( cxp , gp - > yp + gp - > ys - TICKLEN , TICKLEN , fg_color ) ;
cxp + = xd ;
}
}
if ( yticks ) {
if ( gp - > ymin < 0 & & gp - > ymax > 0 ) {
// draw zero seperator
float cxp = 0 ;
float czp = gp - > yp + ( gp - > ymax / gp - > range ) ;
while ( cxp < gp - > xs ) {
renderer - > writeFastHLine ( gp - > xp + cxp , czp , 2 , fg_color ) ;
cxp + = 6.0 ;
}
// align ticks to zero line
float cyp = 0 , yd = gp - > ys / yticks ;
for ( count = 0 ; count < yticks ; count + + ) {
if ( ( czp - cyp ) > gp - > yp ) {
renderer - > writeFastHLine ( gp - > xp , czp - cyp , TICKLEN , fg_color ) ;
renderer - > writeFastHLine ( gp - > xp + gp - > xs - TICKLEN , czp - cyp , TICKLEN , fg_color ) ;
}
if ( ( czp + cyp ) < ( gp - > yp + gp - > ys ) ) {
renderer - > writeFastHLine ( gp - > xp , czp + cyp , TICKLEN , fg_color ) ;
renderer - > writeFastHLine ( gp - > xp + gp - > xs - TICKLEN , czp + cyp , TICKLEN , fg_color ) ;
}
cyp + = yd ;
}
} else {
float cyp = gp - > yp , yd = gp - > ys / yticks ;
for ( count = 0 ; count < yticks ; count + + ) {
renderer - > writeFastHLine ( gp - > xp , cyp , TICKLEN , fg_color ) ;
renderer - > writeFastHLine ( gp - > xp + gp - > xs - TICKLEN , cyp , TICKLEN , fg_color ) ;
cyp + = yd ;
}
}
}
}
// define a graph
void DefineGraph ( uint16_t num , uint16_t xp , uint16_t yp , int16_t xs , uint16_t ys , int16_t dec , float ymin , float ymax , uint8_t icol ) {
if ( ! renderer ) return ;
uint8_t rflg = 0 ;
if ( xs < 0 ) {
rflg = 1 ;
xs = abs ( xs ) ;
}
struct GRAPH * gp ;
uint16_t count ;
uint16_t index = num % NUM_GRAPHS ;
if ( ! graph [ index ] ) {
gp = ( struct GRAPH * ) calloc ( sizeof ( struct GRAPH ) , 1 ) ;
if ( ! gp ) return ;
graph [ index ] = gp ;
} else {
gp = graph [ index ] ;
if ( rflg ) {
RedrawGraph ( index , 1 ) ;
return ;
}
}
// 6 bits per axis
gp - > xticks = ( num > > 4 ) & 0x3f ;
gp - > yticks = ( num > > 10 ) & 0x3f ;
gp - > xp = xp ;
gp - > yp = yp ;
gp - > xs = xs ;
gp - > ys = ys ;
if ( ! dec ) dec = 1 ;
gp - > decimation = dec ;
if ( dec > 0 ) {
// is minutes per sweep prepare timing parameters in ms
gp - > x_time = ( ( float ) dec * 60000.0 ) / ( float ) xs ;
gp - > last_ms = millis ( ) + gp - > x_time ;
}
gp - > ymin = ymin ;
gp - > ymax = ymax ;
gp - > range = ( ymax - ymin ) / ys ;
gp - > xcnt = 0 ;
gp - > dcnt = 0 ;
gp - > summ = 0 ;
if ( gp - > values ) free ( gp - > values ) ;
gp - > values = ( uint8_t * ) calloc ( 1 , xs + 2 ) ;
if ( ! gp - > values ) {
free ( gp ) ;
graph [ index ] = 0 ;
return ;
}
// start from zero
gp - > values [ 0 ] = 0 ;
gp - > last_ms_redrawn = millis ( ) ;
if ( ! icol ) icol = 1 ;
gp - > color_index = icol ;
gp - > flags . overlay = 0 ;
gp - > flags . draw = 1 ;
// check if previous graph has same coordinates
if ( index > 0 ) {
for ( uint8_t count = 0 ; count < index ; count + + ) {
if ( graph [ count ] ) {
// a previous graph is defined, compare
// assume the same if corner is identical
struct GRAPH * gp1 = graph [ count ] ;
if ( ( gp - > xp = = gp1 - > xp ) & & ( gp - > yp = = gp1 - > yp ) ) {
gp - > flags . overlay = 1 ;
break ;
}
}
}
}
// draw rectangle
renderer - > drawRect ( xp , yp , xs , ys , fg_color ) ;
// clr inside
ClrGraph ( index ) ;
}
// check if to advance GRAPH
void DisplayCheckGraph ( ) {
int16_t count ;
struct GRAPH * gp ;
for ( count = 0 ; count < NUM_GRAPHS ; count + + ) {
gp = graph [ count ] ;
if ( gp ) {
if ( gp - > decimation > 0 ) {
// if time over add value
while ( millis ( ) > gp - > last_ms ) {
gp - > last_ms + = gp - > x_time ;
uint8_t val ;
if ( gp - > dcnt ) {
val = gp - > summ / gp - > dcnt ;
gp - > dcnt = 0 ;
gp - > summ = 0 ;
gp - > last_val = val ;
} else {
val = gp - > last_val ;
}
AddGraph ( count , val ) ;
}
}
}
}
}
# if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)
2020-05-30 14:29:47 +01:00
# ifdef ESP32
2019-08-19 12:21:54 +01:00
# include <SD.h>
2020-05-30 14:29:47 +01:00
# endif
2019-08-19 12:21:54 +01:00
void Save_graph ( uint8_t num , char * path ) {
if ( ! renderer ) return ;
uint16_t index = num % NUM_GRAPHS ;
struct GRAPH * gp = graph [ index ] ;
if ( ! gp ) return ;
File fp ;
2020-05-30 14:29:47 +01:00
fsp - > remove ( path ) ;
fp = fsp - > open ( path , FILE_WRITE ) ;
2019-08-19 12:21:54 +01:00
if ( ! fp ) return ;
char str [ 32 ] ;
sprintf_P ( str , PSTR ( " %d \t %d \t %d \t " ) , gp - > xcnt , gp - > xs , gp - > ys ) ;
fp . print ( str ) ;
dtostrfd ( gp - > ymin , 2 , str ) ;
fp . print ( str ) ;
fp . print ( " \t " ) ;
dtostrfd ( gp - > ymax , 2 , str ) ;
fp . print ( str ) ;
fp . print ( " \t " ) ;
for ( uint32_t count = 0 ; count < gp - > xs ; count + + ) {
dtostrfd ( gp - > values [ count ] , 0 , str ) ;
fp . print ( str ) ;
fp . print ( " \t " ) ;
}
fp . print ( " \n " ) ;
fp . close ( ) ;
}
void Restore_graph ( uint8_t num , char * path ) {
if ( ! renderer ) return ;
uint16_t index = num % NUM_GRAPHS ;
struct GRAPH * gp = graph [ index ] ;
if ( ! gp ) return ;
File fp ;
2020-05-30 14:29:47 +01:00
fp = fsp - > open ( path , FILE_READ ) ;
2019-08-19 12:21:54 +01:00
if ( ! fp ) return ;
char vbuff [ 32 ] ;
char * cp = vbuff ;
2019-09-06 09:11:50 +01:00
uint8_t buf [ 2 ] ;
2019-08-19 12:21:54 +01:00
uint8_t findex = 0 ;
for ( uint32_t count = 0 ; count < = gp - > xs + 4 ; count + + ) {
cp = vbuff ;
findex = 0 ;
while ( fp . available ( ) ) {
fp . read ( buf , 1 ) ;
if ( buf [ 0 ] = = ' \t ' | | buf [ 0 ] = = ' , ' | | buf [ 0 ] = = ' \n ' | | buf [ 0 ] = = ' \r ' ) {
break ;
} else {
* cp + + = buf [ 0 ] ;
findex + + ;
if ( findex > = sizeof ( vbuff ) - 1 ) break ;
}
}
* cp = 0 ;
if ( count < = 4 ) {
if ( count = = 0 ) gp - > xcnt = atoi ( vbuff ) ;
} else {
gp - > values [ count - 5 ] = atoi ( vbuff ) ;
}
}
fp . close ( ) ;
RedrawGraph ( num , 1 ) ;
}
2020-09-11 14:45:21 +01:00
# endif // USE_SCRIPT_FATFS
2019-08-19 12:21:54 +01:00
void RedrawGraph ( uint8_t num , uint8_t flags ) {
uint16_t index = num % NUM_GRAPHS ;
struct GRAPH * gp = graph [ index ] ;
if ( ! gp ) return ;
if ( ! flags ) {
gp - > flags . draw = 0 ;
return ;
}
if ( ! renderer ) return ;
gp - > flags . draw = 1 ;
uint16_t linecol = fg_color ;
if ( color_type = = COLOR_COLOR ) {
linecol = renderer - > GetColorFromIndex ( gp - > color_index ) ;
}
if ( ! gp - > flags . overlay ) {
// draw rectangle
renderer - > drawRect ( gp - > xp , gp - > yp , gp - > xs , gp - > ys , fg_color ) ;
// clr inside
ClrGraph ( index ) ;
}
for ( uint16_t count = 0 ; count < gp - > xs - 1 ; count + + ) {
renderer - > writeLine ( gp - > xp + count , gp - > yp + gp - > ys - gp - > values [ count ] - 1 , gp - > xp + count + 1 , gp - > yp + gp - > ys - gp - > values [ count + 1 ] - 1 , linecol ) ;
}
}
// add next value to graph
void AddGraph ( uint8_t num , uint8_t val ) {
struct GRAPH * gp = graph [ num ] ;
if ( ! renderer ) return ;
uint16_t linecol = fg_color ;
if ( color_type = = COLOR_COLOR ) {
linecol = renderer - > GetColorFromIndex ( gp - > color_index ) ;
}
gp - > xcnt + + ;
if ( gp - > xcnt > gp - > xs ) {
gp - > xcnt = gp - > xs ;
int16_t count ;
// shift values
for ( count = 0 ; count < gp - > xs - 1 ; count + + ) {
gp - > values [ count ] = gp - > values [ count + 1 ] ;
}
gp - > values [ gp - > xcnt - 1 ] = val ;
if ( ! gp - > flags . draw ) return ;
// only redraw every second or longer
if ( millis ( ) - gp - > last_ms_redrawn > 1000 ) {
gp - > last_ms_redrawn = millis ( ) ;
// clr area and redraw graph
if ( ! gp - > flags . overlay ) {
// draw rectangle
renderer - > drawRect ( gp - > xp , gp - > yp , gp - > xs , gp - > ys , fg_color ) ;
// clr inner and draw ticks
ClrGraph ( num ) ;
}
for ( count = 0 ; count < gp - > xs - 1 ; count + + ) {
renderer - > writeLine ( gp - > xp + count , gp - > yp + gp - > ys - gp - > values [ count ] - 1 , gp - > xp + count + 1 , gp - > yp + gp - > ys - gp - > values [ count + 1 ] - 1 , linecol ) ;
}
}
} else {
// add value and draw a single line
gp - > values [ gp - > xcnt ] = val ;
if ( ! gp - > flags . draw ) return ;
renderer - > writeLine ( gp - > xp + gp - > xcnt - 1 , gp - > yp + gp - > ys - gp - > values [ gp - > xcnt - 1 ] - 1 , gp - > xp + gp - > xcnt , gp - > yp + gp - > ys - gp - > values [ gp - > xcnt ] - 1 , linecol ) ;
}
}
// add next value
void AddValue ( uint8_t num , float fval ) {
// not yet defined ???
num = num % NUM_GRAPHS ;
struct GRAPH * gp = graph [ num ] ;
if ( ! gp ) return ;
if ( fval > gp - > ymax ) fval = gp - > ymax ;
if ( fval < gp - > ymin ) fval = gp - > ymin ;
int16_t val ;
val = ( fval - gp - > ymin ) / gp - > range ;
if ( val > gp - > ys - 1 ) val = gp - > ys - 1 ;
if ( val < 0 ) val = 0 ;
// summ values
gp - > summ + = val ;
gp - > dcnt + + ;
// decimation option
if ( gp - > decimation < 0 ) {
if ( gp - > dcnt > = - gp - > decimation ) {
gp - > dcnt = 0 ;
// calc average
val = gp - > summ / - gp - > decimation ;
gp - > summ = 0 ;
// add to graph
AddGraph ( num , val ) ;
}
}
}
2020-09-03 11:26:03 +01:00
# endif // USE_GRAPH
# ifdef USE_FT5206
2020-09-11 14:45:21 +01:00
# include <FT5206.h>
2020-09-03 11:26:03 +01:00
// touch panel controller
# undef FT5206_address
# define FT5206_address 0x38
FT5206_Class * touchp ;
TP_Point pLoc ;
bool FT5206_found ;
bool Touch_Init ( TwoWire & i2c ) {
FT5206_found = false ;
touchp = new FT5206_Class ( ) ;
if ( touchp - > begin ( i2c , FT5206_address ) ) {
I2cSetActiveFound ( FT5206_address , " FT5206 " ) ;
FT5206_found = true ;
}
return FT5206_found ;
}
uint32_t Touch_Status ( uint32_t sel ) {
if ( FT5206_found ) {
switch ( sel ) {
case 0 :
return touchp - > touched ( ) ;
case 1 :
return pLoc . x ;
case 2 :
return pLoc . y ;
}
return 0 ;
} else {
return 0 ;
}
}
2020-09-11 14:45:21 +01:00
2020-09-03 11:26:03 +01:00
# ifdef USE_TOUCH_BUTTONS
void Touch_MQTT ( uint8_t index , const char * cp ) {
ResponseTime_P ( PSTR ( " , \" FT5206 \" :{ \" %s%d \" : \" %d \" }} " ) , cp , index + 1 , buttons [ index ] - > vpower . on_off ) ;
MqttPublishTeleSensor ( ) ;
}
void Touch_RDW_BUTT ( uint32_t count , uint32_t pwr ) {
buttons [ count ] - > xdrawButton ( pwr ) ;
if ( pwr ) buttons [ count ] - > vpower . on_off = 1 ;
else buttons [ count ] - > vpower . on_off = 0 ;
}
// check digitizer hit
void Touch_Check ( void ( * rotconvert ) ( int16_t * x , int16_t * y ) ) {
uint16_t temp ;
uint8_t rbutt = 0 ;
uint8_t vbutt = 0 ;
if ( touchp - > touched ( ) ) {
// did find a hit
pLoc = touchp - > getPoint ( 0 ) ;
if ( renderer ) {
rotconvert ( & pLoc . x , & pLoc . y ) ;
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_INFO, PSTR("touch %d - %d"), pLoc.x, pLoc.y);
2020-09-03 11:26:03 +01:00
// now must compare with defined buttons
for ( uint8_t count = 0 ; count < MAXBUTTONS ; count + + ) {
if ( buttons [ count ] & & ! buttons [ count ] - > vpower . disable ) {
if ( buttons [ count ] - > contains ( pLoc . x , pLoc . y ) ) {
// did hit
buttons [ count ] - > press ( true ) ;
if ( buttons [ count ] - > justPressed ( ) ) {
if ( ! buttons [ count ] - > vpower . is_virtual ) {
2020-10-28 18:03:39 +00:00
uint8_t pwr = bitRead ( TasmotaGlobal . power , rbutt ) ;
2020-09-03 11:26:03 +01:00
if ( ! SendKey ( KEY_BUTTON , rbutt + 1 , POWER_TOGGLE ) ) {
ExecuteCommandPower ( rbutt + 1 , POWER_TOGGLE , SRC_BUTTON ) ;
Touch_RDW_BUTT ( count , ! pwr ) ;
}
} else {
// virtual button
const char * cp ;
if ( ! buttons [ count ] - > vpower . is_pushbutton ) {
// toggle button
buttons [ count ] - > vpower . on_off ^ = 1 ;
cp = " TBT " ;
} else {
// push button
buttons [ count ] - > vpower . on_off = 1 ;
cp = " PBT " ;
}
buttons [ count ] - > xdrawButton ( buttons [ count ] - > vpower . on_off ) ;
Touch_MQTT ( count , cp ) ;
}
}
}
if ( ! buttons [ count ] - > vpower . is_virtual ) {
rbutt + + ;
} else {
vbutt + + ;
}
}
}
}
} else {
// no hit
for ( uint8_t count = 0 ; count < MAXBUTTONS ; count + + ) {
if ( buttons [ count ] ) {
buttons [ count ] - > press ( false ) ;
if ( buttons [ count ] - > justReleased ( ) ) {
if ( buttons [ count ] - > vpower . is_virtual ) {
if ( buttons [ count ] - > vpower . is_pushbutton ) {
// push button
buttons [ count ] - > vpower . on_off = 0 ;
Touch_MQTT ( count , " PBT " ) ;
buttons [ count ] - > xdrawButton ( buttons [ count ] - > vpower . on_off ) ;
}
}
}
if ( ! buttons [ count ] - > vpower . is_virtual ) {
// check if power button stage changed
2020-10-28 18:03:39 +00:00
uint8_t pwr = bitRead ( TasmotaGlobal . power , rbutt ) ;
2020-09-03 11:26:03 +01:00
uint8_t vpwr = buttons [ count ] - > vpower . on_off ;
if ( pwr ! = vpwr ) {
Touch_RDW_BUTT ( count , pwr ) ;
}
rbutt + + ;
}
}
}
pLoc . x = 0 ;
pLoc . y = 0 ;
}
}
2020-09-11 14:45:21 +01:00
2020-09-03 11:26:03 +01:00
# endif // USE_TOUCH_BUTTONS
# endif // USE_FT5206
2019-08-19 12:21:54 +01:00
2018-08-28 17:13:14 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-01-28 13:08:33 +00:00
bool Xdrv13 ( uint8_t function )
2018-08-28 17:13:14 +01:00
{
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-08-28 17:13:14 +01:00
2020-10-30 11:29:48 +00:00
if ( ( TasmotaGlobal . i2c_enabled | | TasmotaGlobal . spi_enabled | | TasmotaGlobal . soft_spi_enabled ) & & XdspPresent ( ) ) {
2018-08-28 17:13:14 +01:00
switch ( function ) {
case FUNC_PRE_INIT :
DisplayInitDriver ( ) ;
2019-08-19 12:21:54 +01:00
# ifdef USE_GRAPH
for ( uint8_t count = 0 ; count < NUM_GRAPHS ; count + + ) {
graph [ count ] = 0 ;
}
# endif
2018-08-28 17:13:14 +01:00
break ;
case FUNC_EVERY_50_MSECOND :
if ( Settings . display_model ) { XdspCall ( FUNC_DISPLAY_EVERY_50_MSECOND ) ; }
break ;
case FUNC_SET_POWER :
DisplaySetPower ( ) ;
break ;
case FUNC_EVERY_SECOND :
2019-08-19 12:21:54 +01:00
# ifdef USE_GRAPH
DisplayCheckGraph ( ) ;
# endif
# ifdef USE_DISPLAY_MODES1TO5
2018-08-28 17:13:14 +01:00
if ( Settings . display_model & & Settings . display_mode ) { XdspCall ( FUNC_DISPLAY_EVERY_SECOND ) ; }
2019-08-19 12:21:54 +01:00
# endif
2018-08-28 17:13:14 +01:00
break ;
2019-08-19 12:21:54 +01:00
# ifdef USE_DISPLAY_MODES1TO5
2018-08-28 17:13:14 +01:00
case FUNC_MQTT_SUBSCRIBE :
DisplayMqttSubscribe ( ) ;
break ;
case FUNC_MQTT_DATA :
result = DisplayMqttData ( ) ;
break ;
case FUNC_SHOW_SENSOR :
DisplayLocalSensor ( ) ;
break ;
# endif // USE_DISPLAY_MODES1TO5
2019-08-02 16:22:38 +01:00
case FUNC_COMMAND :
2019-08-11 17:12:18 +01:00
result = DecodeCommand ( kDisplayCommands , DisplayCommand ) ;
2019-08-02 16:22:38 +01:00
break ;
2018-08-28 17:13:14 +01:00
}
}
return result ;
}
# endif // USE_DISPLAY
# endif // USE_I2C or USE_SPI