2021-04-03 15:58:47 +01:00
|
|
|
/*
|
2021-04-09 15:42:51 +01:00
|
|
|
xdsp_16_esp32_epaper_47.ino - LILIGO47 e-paper support for Tasmota
|
2021-04-03 15:58:47 +01:00
|
|
|
|
|
|
|
Copyright (C) 2021 Theo Arends, Gerhard Mutz and LILIGO
|
|
|
|
|
|
|
|
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/>.
|
|
|
|
*/
|
|
|
|
|
2021-04-09 15:42:51 +01:00
|
|
|
#ifdef ESP32
|
2021-04-03 15:58:47 +01:00
|
|
|
#ifdef USE_DISPLAY
|
|
|
|
#ifdef USE_LILYGO47
|
|
|
|
|
|
|
|
#define XDSP_16 16
|
|
|
|
|
|
|
|
#define EPD47_BLACK 0
|
|
|
|
#define EPD47_WHITE 15
|
|
|
|
|
|
|
|
#include <epd4in7.h>
|
|
|
|
|
|
|
|
Epd47 *epd47;
|
|
|
|
bool epd47_init_done = false;
|
|
|
|
extern uint8_t color_type;
|
|
|
|
extern uint16_t fg_color;
|
|
|
|
extern uint16_t bg_color;
|
|
|
|
|
|
|
|
/*********************************************************************************************/
|
|
|
|
|
|
|
|
void EpdInitDriver47(void) {
|
2021-04-09 15:42:51 +01:00
|
|
|
if (PinUsed(GPIO_EPD_DATA)) {
|
2021-04-03 15:58:47 +01:00
|
|
|
|
|
|
|
Settings.display_model = XDSP_16;
|
|
|
|
|
|
|
|
if (Settings.display_width != EPD47_WIDTH) {
|
|
|
|
Settings.display_width = EPD47_WIDTH;
|
|
|
|
}
|
|
|
|
if (Settings.display_height != EPD47_HEIGHT) {
|
|
|
|
Settings.display_height = EPD47_HEIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
// init renderer
|
|
|
|
epd47 = new Epd47(Settings.display_width, Settings.display_height);
|
|
|
|
epd47->Init();
|
|
|
|
|
|
|
|
renderer = epd47;
|
2021-04-11 06:49:13 +01:00
|
|
|
renderer->DisplayInit(DISPLAY_INIT_MODE, Settings.display_size, Settings.display_rotate, Settings.display_font);
|
2021-04-03 15:58:47 +01:00
|
|
|
renderer->setTextColor(EPD47_BLACK, EPD47_WHITE);
|
|
|
|
|
|
|
|
#ifdef SHOW_SPLASH
|
|
|
|
// Welcome text
|
|
|
|
renderer->setTextFont(2);
|
|
|
|
renderer->DrawStringAt(50, 50, "LILGO 4.7 E-Paper Display!", EPD47_BLACK, 0);
|
|
|
|
renderer->Updateframe();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
fg_color = EPD47_BLACK;
|
|
|
|
bg_color = EPD47_WHITE;
|
|
|
|
color_type = COLOR_COLOR;
|
|
|
|
|
2021-04-29 13:18:28 +01:00
|
|
|
#ifdef USE_TOUCH_BUTTONS
|
|
|
|
// start digitizer
|
|
|
|
EPD47_Touch_Init();
|
|
|
|
#endif // USE_TOUCH_BUTTONS
|
|
|
|
|
2021-04-03 15:58:47 +01:00
|
|
|
epd47_init_done = true;
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("DSP: E-Paper 4.7"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************/
|
|
|
|
|
|
|
|
|
2021-04-29 13:18:28 +01:00
|
|
|
#ifdef USE_TOUCH_BUTTONS
|
|
|
|
|
|
|
|
#define TOUCH_SLAVE_ADDRESS 0x5a
|
|
|
|
class TouchClass {
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint8_t id;
|
|
|
|
uint8_t state;
|
|
|
|
uint16_t x;
|
|
|
|
uint16_t y;
|
|
|
|
} TouchData_t;
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool begin(TwoWire &port = Wire, uint8_t addr = TOUCH_SLAVE_ADDRESS);
|
|
|
|
uint8_t scanPoint();
|
|
|
|
void getPoint(int16_t &x, int16_t &y, uint8_t index);
|
|
|
|
// void sleep(void);
|
|
|
|
// void wakeup(void);
|
|
|
|
TouchData_t data[5];
|
|
|
|
|
|
|
|
private:
|
|
|
|
void clearFlags(void);
|
|
|
|
void readBytes(uint8_t *data, uint8_t nbytes);
|
|
|
|
uint8_t _address;
|
|
|
|
bool initialization = false;
|
|
|
|
TwoWire *_i2cPort;
|
|
|
|
};
|
|
|
|
|
|
|
|
void TouchClass::readBytes(uint8_t *data, uint8_t nbytes) {
|
|
|
|
_i2cPort->beginTransmission(_address); // Initialize the Tx buffer
|
|
|
|
_i2cPort->write(data, 2); // Put data in Tx buffer
|
|
|
|
if (0 != _i2cPort->endTransmission()) {
|
|
|
|
Serial.println("readBytes error!");
|
|
|
|
}
|
|
|
|
uint8_t i = 0;
|
|
|
|
_i2cPort->requestFrom(_address, nbytes); // Read bytes from slave register address
|
|
|
|
while (_i2cPort->available()) {
|
|
|
|
data[i++] = _i2cPort->read();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TouchClass::clearFlags(void) {
|
|
|
|
uint8_t buf[3] = {0xD0, 0X00, 0XAB};
|
|
|
|
_i2cPort->beginTransmission(_address);
|
|
|
|
_i2cPort->write(buf, 3);
|
|
|
|
_i2cPort->endTransmission();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TouchClass::begin(TwoWire &port, uint8_t addr) {
|
|
|
|
_i2cPort = &port;
|
|
|
|
_address = addr;
|
|
|
|
_i2cPort->beginTransmission(_address);
|
|
|
|
if (0 == _i2cPort->endTransmission()) {
|
|
|
|
// wakeup();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t TouchClass::scanPoint() {
|
|
|
|
uint8_t point = 0;
|
|
|
|
uint8_t buffer[40] = {0};
|
|
|
|
uint32_t sumL = 0, sumH = 0;
|
|
|
|
|
|
|
|
buffer[0] = 0xD0;
|
|
|
|
buffer[1] = 0x00;
|
|
|
|
readBytes(buffer, 7);
|
|
|
|
|
|
|
|
if (buffer[0] == 0xAB) {
|
|
|
|
clearFlags();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
point = buffer[5] & 0xF;
|
|
|
|
|
|
|
|
if (point == 1) {
|
|
|
|
buffer[5] = 0xD0;
|
|
|
|
buffer[6] = 0x07;
|
|
|
|
readBytes( &buffer[5], 2);
|
|
|
|
sumL = buffer[5] << 8 | buffer [6];
|
|
|
|
|
|
|
|
} else if (point > 1) {
|
|
|
|
buffer[5] = 0xD0;
|
|
|
|
buffer[6] = 0x07;
|
|
|
|
readBytes( &buffer[5], 5 * (point - 1) + 3);
|
|
|
|
sumL = buffer[5 * point + 1] << 8 | buffer[5 * point + 2];
|
|
|
|
}
|
|
|
|
clearFlags();
|
|
|
|
|
|
|
|
for (int i = 0 ; i < 5 * point; ++i) {
|
|
|
|
sumH += buffer[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sumH != sumL) {
|
|
|
|
point = 0;
|
|
|
|
}
|
|
|
|
if (point) {
|
|
|
|
uint8_t offset;
|
|
|
|
for (int i = 0; i < point; ++i) {
|
|
|
|
if (i == 0) {
|
|
|
|
offset = 0;
|
|
|
|
} else {
|
|
|
|
offset = 4;
|
|
|
|
}
|
|
|
|
data[i].id = (buffer[i * 5 + offset] >> 4) & 0x0F;
|
|
|
|
data[i].state = buffer[i * 5 + offset] & 0x0F;
|
|
|
|
if (data[i].state == 0x06) {
|
|
|
|
data[i].state = 0x07;
|
|
|
|
} else {
|
|
|
|
data[i].state = 0x06;
|
|
|
|
}
|
|
|
|
data[i].y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F));
|
|
|
|
data[i].x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
point = 1;
|
|
|
|
data[0].id = (buffer[0] >> 4) & 0x0F;
|
|
|
|
data[0].state = 0x06;
|
|
|
|
data[0].y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F));
|
|
|
|
data[0].x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F));
|
|
|
|
}
|
|
|
|
// Serial.printf("X:%d Y:%d\n", data[0].x, data[0].y);
|
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TouchClass::getPoint(int16_t &x, int16_t &y, uint8_t index) {
|
|
|
|
if (index >= 4)return;
|
|
|
|
x = data[index].x;
|
|
|
|
y = data[index].y;
|
|
|
|
}
|
|
|
|
|
|
|
|
//if (touch.scanPoint()) {
|
|
|
|
// touch.getPoint(x, y, 0);
|
|
|
|
|
|
|
|
#define EPD47_address 0x5A
|
|
|
|
|
|
|
|
TouchClass *EPD47_touchp;
|
|
|
|
|
|
|
|
void EPD47_Touch_Init(void) {
|
|
|
|
FT5206_found = false;
|
|
|
|
EPD47_touchp = new TouchClass();
|
|
|
|
if (EPD47_touchp->begin(Wire, EPD47_address)) {
|
|
|
|
I2cSetActiveFound(EPD47_address, "EPD47");
|
|
|
|
FT5206_found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t EPD47_ctouch_counter = 0;
|
|
|
|
// no rotation support
|
|
|
|
void EPD47_RotConvert(int16_t *x, int16_t *y) {
|
|
|
|
int16_t temp;
|
|
|
|
if (renderer) {
|
|
|
|
uint8_t rot=renderer->getRotation();
|
|
|
|
switch (rot) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
temp=*y;
|
|
|
|
*y=renderer->height()-*x;
|
|
|
|
*x=temp;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
*x=renderer->width()-*x;
|
|
|
|
*y=renderer->height()-*y;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
temp=*y;
|
|
|
|
*y=*x;
|
|
|
|
*x=renderer->width()-temp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check digitizer hit
|
|
|
|
void EPD47_CheckTouch(void) {
|
|
|
|
EPD47_ctouch_counter++;
|
|
|
|
if (2 == EPD47_ctouch_counter) {
|
|
|
|
// every 100 ms should be enough
|
|
|
|
EPD47_ctouch_counter = 0;
|
|
|
|
touched = EPD47_touchp->scanPoint();
|
|
|
|
if (touched) {
|
|
|
|
EPD47_touchp->getPoint(touch_xp, touch_yp, 0);
|
|
|
|
EPD47_RotConvert(&touch_xp, &touch_yp);
|
|
|
|
}
|
|
|
|
//Touch_Check(EPD47_RotConvert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // USE_TOUCH_BUTTONS
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-04-03 15:58:47 +01:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* Interface
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
bool Xdsp16(uint8_t function)
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
if (FUNC_DISPLAY_INIT_DRIVER == function) {
|
|
|
|
EpdInitDriver47();
|
|
|
|
}
|
|
|
|
else if (epd47_init_done && (XDSP_16 == Settings.display_model)) {
|
|
|
|
switch (function) {
|
|
|
|
case FUNC_DISPLAY_MODEL:
|
|
|
|
result = true;
|
|
|
|
break;
|
2021-04-29 13:18:28 +01:00
|
|
|
#ifdef USE_TOUCH_BUTTONS
|
|
|
|
case FUNC_DISPLAY_EVERY_50_MSECOND:
|
|
|
|
if (FT5206_found) {
|
|
|
|
EPD47_CheckTouch();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif // USE_TOUCH_BUTTONS
|
2021-04-03 15:58:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-04-09 15:42:51 +01:00
|
|
|
#endif // USE_LILYGO47
|
2021-04-03 15:58:47 +01:00
|
|
|
#endif // USE_DISPLAY
|
2021-04-11 06:49:13 +01:00
|
|
|
#endif // ESP32
|