mirror of https://github.com/arendst/Tasmota.git
parent
624ee28db3
commit
06604b7f09
|
@ -20,7 +20,7 @@
|
|||
#ifndef _SONOFF_VERSION_H_
|
||||
#define _SONOFF_VERSION_H_
|
||||
|
||||
#define VERSION 0x0601010D
|
||||
#define VERSION 0x06020000
|
||||
|
||||
#define D_PROGRAMNAME "Sonoff-Tasmota"
|
||||
#define D_AUTHOR "Theo Arends"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,402 +0,0 @@
|
|||
/*
|
||||
xdrv_99_debug.ino - debug support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2018 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/>.
|
||||
*/
|
||||
|
||||
//#define USE_DEBUG_DRIVER
|
||||
|
||||
#ifdef DEBUG_THEO
|
||||
#ifndef USE_DEBUG_DRIVER
|
||||
#define USE_DEBUG_DRIVER
|
||||
#endif // USE_DEBUG_DRIVER
|
||||
#endif // DEBUG_THEO
|
||||
|
||||
#ifdef USE_DEBUG_DRIVER
|
||||
|
||||
#ifndef CPU_LOAD_CHECK
|
||||
#define CPU_LOAD_CHECK 1 // Seconds between each CPU_LOAD log
|
||||
#endif
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Debug commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define D_CMND_CFGDUMP "CfgDump"
|
||||
#define D_CMND_CFGPOKE "CfgPoke"
|
||||
#define D_CMND_CFGPEEK "CfgPeek"
|
||||
#define D_CMND_CFGXOR "CfgXor"
|
||||
#define D_CMND_EXCEPTION "Exception"
|
||||
#define D_CMND_CPUCHECK "CpuChk"
|
||||
|
||||
enum DebugCommands { CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGXOR, CMND_EXCEPTION, CMND_CPUCHECK };
|
||||
const char kDebugCommands[] PROGMEM = D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGXOR "|" D_CMND_EXCEPTION "|" D_CMND_CPUCHECK;
|
||||
|
||||
uint32_t CPU_loops = 0;
|
||||
uint32_t CPU_last_millis = 0;
|
||||
uint32_t CPU_last_loop_time = 0;
|
||||
uint8_t CPU_load_check = CPU_LOAD_CHECK;
|
||||
|
||||
/*******************************************************************************************/
|
||||
|
||||
#ifdef DEBUG_THEO
|
||||
void ExceptionTest(byte type)
|
||||
{
|
||||
/*
|
||||
Exception (28):
|
||||
epc1=0x4000bf64 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000007 depc=0x00000000
|
||||
|
||||
ctx: cont
|
||||
sp: 3fff1f30 end: 3fff2840 offset: 01a0
|
||||
|
||||
>>>stack>>>
|
||||
3fff20d0: 202c3573 756f7247 2c302070 646e4920
|
||||
3fff20e0: 40236a6e 7954202c 45206570 00454358
|
||||
3fff20f0: 00000010 00000007 00000000 3fff2180
|
||||
3fff2100: 3fff2190 40107bfc 3fff3e4c 3fff22c0
|
||||
3fff2110: 40261934 000000f0 3fff22c0 401004d8
|
||||
3fff2120: 40238fcf 00000050 3fff2100 4021fc10
|
||||
3fff2130: 3fff32bc 4021680c 3ffeade1 4021ff7d
|
||||
3fff2140: 3fff2190 3fff2180 0000000c 7fffffff
|
||||
3fff2150: 00000019 00000000 00000000 3fff21c0
|
||||
3fff2160: 3fff23f3 3ffe8e08 00000000 4021ffb4
|
||||
3fff2170: 3fff2190 3fff2180 0000000c 40201118
|
||||
3fff2180: 3fff21c0 0000003c 3ffef840 00000007
|
||||
3fff2190: 00000000 00000000 00000000 40201128
|
||||
3fff21a0: 3fff23f3 000000f1 3fff23ec 4020fafb
|
||||
3fff21b0: 3fff23f3 3fff21c0 3fff21d0 3fff23f6
|
||||
3fff21c0: 00000000 3fff23fb 4022321b 00000000
|
||||
|
||||
Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads
|
||||
Decoding 14 results
|
||||
0x40236a6e: ets_vsnprintf at ?? line ?
|
||||
0x40107bfc: vsnprintf at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/libc_replacements.c line 387
|
||||
0x40261934: bignum_exptmod at ?? line ?
|
||||
0x401004d8: malloc at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266\umm_malloc/umm_malloc.c line 1664
|
||||
0x40238fcf: wifi_station_get_connect_status at ?? line ?
|
||||
0x4021fc10: operator new[](unsigned int) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/abi.cpp line 57
|
||||
0x4021680c: ESP8266WiFiSTAClass::status() at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\libraries\ESP8266WiFi\src/ESP8266WiFiSTA.cpp line 569
|
||||
0x4021ff7d: vsnprintf_P(char*, unsigned int, char const*, __va_list_tag) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/pgmspace.cpp line 146
|
||||
0x4021ffb4: snprintf_P(char*, unsigned int, char const*, ...) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/pgmspace.cpp line 146
|
||||
0x40201118: atol at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/core_esp8266_noniso.c line 45
|
||||
0x40201128: atoi at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/core_esp8266_noniso.c line 45
|
||||
0x4020fafb: MqttDataHandler(char*, unsigned char*, unsigned int) at R:\Arduino\Work-ESP8266\Theo\sonoff\sonoff-4\sonoff/sonoff.ino line 679 (discriminator 1)
|
||||
0x4022321b: pp_attach at ?? line ?
|
||||
|
||||
00:00:08 MQTT: tele/sonoff/INFO3 = {"Started":"Fatal exception:28 flag:2 (EXCEPTION) epc1:0x4000bf64 epc2:0x00000000 epc3:0x00000000 excvaddr:0x00000007 depc:0x00000000"}
|
||||
*/
|
||||
if (1 == type) {
|
||||
char svalue[10];
|
||||
snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 7); // Exception 28 as number in string (7 in excvaddr)
|
||||
}
|
||||
/*
|
||||
14:50:52 osWatch: FreeRam 25896, rssi 68, last_run 0
|
||||
14:51:02 osWatch: FreeRam 25896, rssi 58, last_run 0
|
||||
14:51:03 CMND: exception 2
|
||||
14:51:12 osWatch: FreeRam 25360, rssi 60, last_run 8771
|
||||
14:51:22 osWatch: FreeRam 25360, rssi 62, last_run 18771
|
||||
14:51:32 osWatch: FreeRam 25360, rssi 62, last_run 28771
|
||||
14:51:42 osWatch: FreeRam 25360, rssi 62, last_run 38771
|
||||
14:51:42 osWatch: Warning, loop blocked. Restart now
|
||||
*/
|
||||
if (2 == type) {
|
||||
while(1) delay(1000); // this will trigger the os watch
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************************/
|
||||
|
||||
void RtcSettingsDump()
|
||||
{
|
||||
#define CFG_COLS 16
|
||||
|
||||
uint16_t idx;
|
||||
uint16_t maxrow;
|
||||
uint16_t row;
|
||||
uint16_t col;
|
||||
|
||||
uint8_t *buffer = (uint8_t *) &RtcSettings;
|
||||
maxrow = ((sizeof(RTCMEM)+CFG_COLS)/CFG_COLS);
|
||||
|
||||
for (row = 0; row < maxrow; row++) {
|
||||
idx = row * CFG_COLS;
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx);
|
||||
for (col = 0; col < CFG_COLS; col++) {
|
||||
if (!(col%4)) {
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
|
||||
}
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
|
||||
}
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
|
||||
for (col = 0; col < CFG_COLS; col++) {
|
||||
// if (!(col%4)) {
|
||||
// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
|
||||
// }
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
|
||||
}
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
|
||||
AddLog(LOG_LEVEL_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DEBUG_THEO
|
||||
|
||||
/*******************************************************************************************/
|
||||
|
||||
void CpuLoadLoop()
|
||||
{
|
||||
CPU_last_loop_time = millis();
|
||||
if (CPU_load_check && CPU_last_millis) {
|
||||
CPU_loops ++;
|
||||
if ((CPU_last_millis + (CPU_load_check *1000)) <= CPU_last_loop_time) {
|
||||
#if defined(F_CPU) && (F_CPU == 160000000L)
|
||||
int CPU_load = 100 - ( (CPU_loops*(1 + 30*sleep)) / (CPU_load_check *800) );
|
||||
CPU_loops = CPU_loops / CPU_load_check;
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops);
|
||||
#else
|
||||
int CPU_load = 100 - ( (CPU_loops*(1 + 30*sleep)) / (CPU_load_check *400) );
|
||||
CPU_loops = CPU_loops / CPU_load_check;
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops);
|
||||
#endif
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
CPU_last_millis = CPU_last_loop_time;
|
||||
CPU_loops = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************************/
|
||||
|
||||
#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1)
|
||||
// All version before core 2.4.2
|
||||
|
||||
extern "C" {
|
||||
#include <cont.h>
|
||||
extern cont_t g_cont;
|
||||
}
|
||||
|
||||
void DebugFreeMem()
|
||||
{
|
||||
// https://github.com/esp8266/Arduino/issues/2557
|
||||
register uint32_t *sp asm("a1");
|
||||
|
||||
// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d, UnmodifiedStack %d (%s)"),
|
||||
// ESP.getFreeHeap(), 4 * (sp - g_cont.stack), cont_get_free_stack(&g_cont), XdrvMailbox.data);
|
||||
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"),
|
||||
ESP.getFreeHeap(), 4 * (sp - g_cont.stack), XdrvMailbox.data);
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
}
|
||||
|
||||
#else
|
||||
// All version from core 2.4.2
|
||||
// https://github.com/esp8266/Arduino/pull/5018
|
||||
// https://github.com/esp8266/Arduino/pull/4553
|
||||
|
||||
extern "C" {
|
||||
#include <cont.h>
|
||||
extern cont_t* g_pcont;
|
||||
}
|
||||
|
||||
void DebugFreeMem()
|
||||
{
|
||||
// https://github.com/esp8266/Arduino/issues/2557
|
||||
register uint32_t *sp asm("a1");
|
||||
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"),
|
||||
ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data);
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
}
|
||||
|
||||
#endif // ARDUINO_ESP8266_RELEASE_2_x_x
|
||||
|
||||
/*******************************************************************************************/
|
||||
|
||||
void DebugCfgDump(char* parms)
|
||||
{
|
||||
#define CFG_COLS 16
|
||||
|
||||
uint16_t idx;
|
||||
uint16_t maxrow;
|
||||
uint16_t row;
|
||||
uint16_t col;
|
||||
char *p;
|
||||
|
||||
uint8_t *buffer = (uint8_t *) &Settings;
|
||||
maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS);
|
||||
|
||||
uint16_t srow = strtol(parms, &p, 16) / CFG_COLS;
|
||||
uint16_t mrow = strtol(p, &p, 10);
|
||||
|
||||
// snprintf_P(log_data, sizeof(log_data), PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow);
|
||||
// AddLog(LOG_LEVEL_DEBUG);
|
||||
|
||||
if (0 == mrow) { // Default only 8 lines
|
||||
mrow = 8;
|
||||
}
|
||||
if (srow > maxrow) {
|
||||
srow = maxrow - mrow;
|
||||
}
|
||||
if (mrow < (maxrow - srow)) {
|
||||
maxrow = srow + mrow;
|
||||
}
|
||||
|
||||
for (row = srow; row < maxrow; row++) {
|
||||
idx = row * CFG_COLS;
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx);
|
||||
for (col = 0; col < CFG_COLS; col++) {
|
||||
if (!(col%4)) {
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
|
||||
}
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
|
||||
}
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
|
||||
for (col = 0; col < CFG_COLS; col++) {
|
||||
// if (!(col%4)) {
|
||||
// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
|
||||
// }
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
|
||||
}
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
|
||||
AddLog(LOG_LEVEL_INFO);
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugCfgPeek(char* parms)
|
||||
{
|
||||
char *p;
|
||||
|
||||
uint16_t address = strtol(parms, &p, 16);
|
||||
if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4;
|
||||
address = (address >> 2) << 2;
|
||||
|
||||
uint8_t *buffer = (uint8_t *) &Settings;
|
||||
uint8_t data8 = buffer[address];
|
||||
uint16_t data16 = (buffer[address +1] << 8) + buffer[address];
|
||||
uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + data16;
|
||||
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), address);
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[address +i]);
|
||||
}
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[address +i] > 0x20) && (buffer[address +i] < 0x7F)) ? (char)buffer[address +i] : ' ');
|
||||
}
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s| 0x%02X (%d), 0x%04X (%d), 0x%0LX (%lu)"), log_data, data8, data8, data16, data16, data32, data32);
|
||||
AddLog(LOG_LEVEL_INFO);
|
||||
}
|
||||
|
||||
void DebugCfgPoke(char* parms)
|
||||
{
|
||||
char *p;
|
||||
|
||||
uint16_t address = strtol(parms, &p, 16);
|
||||
if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4;
|
||||
address = (address >> 2) << 2;
|
||||
|
||||
uint32_t data = strtol(p, &p, 16);
|
||||
|
||||
uint8_t *buffer = (uint8_t *) &Settings;
|
||||
uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address];
|
||||
|
||||
uint8_t *nbuffer = (uint8_t *) &data;
|
||||
for (byte i = 0; i < 4; i++) { buffer[address +i] = nbuffer[+i]; }
|
||||
|
||||
uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address];
|
||||
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32);
|
||||
AddLog(LOG_LEVEL_INFO);
|
||||
}
|
||||
|
||||
/*******************************************************************************************/
|
||||
|
||||
boolean DebugCommand()
|
||||
{
|
||||
char command[CMDSZ];
|
||||
boolean serviced = true;
|
||||
|
||||
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kDebugCommands);
|
||||
if (-1 == command_code) {
|
||||
serviced = false; // Unknown command
|
||||
}
|
||||
else if (CMND_CFGDUMP == command_code) {
|
||||
DebugCfgDump(XdrvMailbox.data);
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
|
||||
}
|
||||
else if (CMND_CFGPEEK == command_code) {
|
||||
DebugCfgPeek(XdrvMailbox.data);
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
|
||||
}
|
||||
else if (CMND_CFGPOKE == command_code) {
|
||||
DebugCfgPoke(XdrvMailbox.data);
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
|
||||
}
|
||||
#ifdef USE_WEBSERVER
|
||||
else if (CMND_CFGXOR == command_code) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
config_xor_on_set = XdrvMailbox.payload;
|
||||
}
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, config_xor_on_set);
|
||||
}
|
||||
#endif // USE_WEBSERVER
|
||||
#ifdef DEBUG_THEO
|
||||
else if (CMND_EXCEPTION == command_code) {
|
||||
if (XdrvMailbox.data_len > 0) ExceptionTest(XdrvMailbox.payload);
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
|
||||
}
|
||||
#endif // DEBUG_THEO
|
||||
else if (CMND_CPUCHECK == command_code) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
CPU_load_check = XdrvMailbox.payload;
|
||||
CPU_last_millis = CPU_last_loop_time;
|
||||
}
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_load_check);
|
||||
}
|
||||
else serviced = false; // Unknown command
|
||||
|
||||
return serviced;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XDRV_99
|
||||
|
||||
boolean Xdrv99(byte function)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_PRE_INIT:
|
||||
CPU_last_millis = millis();
|
||||
break;
|
||||
case FUNC_LOOP:
|
||||
CpuLoadLoop();
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DebugCommand();
|
||||
break;
|
||||
case FUNC_FREE_MEM:
|
||||
DebugFreeMem();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_DEBUG_DRIVER
|
|
@ -1,262 +0,0 @@
|
|||
/*
|
||||
xdsp_01_lcd.ino - Display LCD support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2018 Theo Arends and Adafruit
|
||||
|
||||
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_I2C
|
||||
#ifdef USE_DISPLAY
|
||||
#ifdef USE_DISPLAY_LCD
|
||||
|
||||
#define XDSP_01 1
|
||||
|
||||
#define LCD_ADDRESS1 0x27 // LCD I2C address option 1
|
||||
#define LCD_ADDRESS2 0x3F // LCD I2C address option 2
|
||||
|
||||
#include <Wire.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
LiquidCrystal_I2C *lcd;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void LcdInitMode()
|
||||
{
|
||||
lcd->init();
|
||||
lcd->clear();
|
||||
}
|
||||
|
||||
void LcdInit(uint8_t mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case DISPLAY_INIT_MODE:
|
||||
LcdInitMode();
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
DisplayClearScreenBuffer();
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
break;
|
||||
case DISPLAY_INIT_PARTIAL:
|
||||
case DISPLAY_INIT_FULL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LcdInitDriver()
|
||||
{
|
||||
if (!Settings.display_model) {
|
||||
if (I2cDevice(LCD_ADDRESS1)) {
|
||||
Settings.display_address[0] = LCD_ADDRESS1;
|
||||
Settings.display_model = XDSP_01;
|
||||
}
|
||||
else if (I2cDevice(LCD_ADDRESS2)) {
|
||||
Settings.display_address[0] = LCD_ADDRESS2;
|
||||
Settings.display_model = XDSP_01;
|
||||
}
|
||||
}
|
||||
|
||||
if (XDSP_01 == Settings.display_model) {
|
||||
lcd = new LiquidCrystal_I2C(Settings.display_address[0], Settings.display_cols[0], Settings.display_rows);
|
||||
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
DisplayAllocScreenBuffer();
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
|
||||
LcdInitMode();
|
||||
}
|
||||
}
|
||||
|
||||
void LcdDrawStringAt()
|
||||
{
|
||||
lcd->setCursor(dsp_x, dsp_y);
|
||||
lcd->print(dsp_str);
|
||||
}
|
||||
|
||||
void LcdDisplayOnOff(uint8_t on)
|
||||
{
|
||||
if (on) {
|
||||
lcd->backlight();
|
||||
} else {
|
||||
lcd->noBacklight();
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
|
||||
void LcdCenter(byte row, char* txt)
|
||||
{
|
||||
int offset;
|
||||
int len;
|
||||
char line[Settings.display_cols[0] +2];
|
||||
|
||||
memset(line, 0x20, Settings.display_cols[0]);
|
||||
line[Settings.display_cols[0]] = 0;
|
||||
len = strlen(txt);
|
||||
offset = (len < Settings.display_cols[0]) ? offset = (Settings.display_cols[0] - len) / 2 : 0;
|
||||
strncpy(line +offset, txt, len);
|
||||
lcd->setCursor(0, row);
|
||||
lcd->print(line);
|
||||
}
|
||||
|
||||
void LcdPrintLogLine()
|
||||
{
|
||||
if (!disp_screen_buffer_cols) {
|
||||
DisplayAllocScreenBuffer();
|
||||
} else {
|
||||
uint8_t last_row = Settings.display_rows -1;
|
||||
|
||||
for (byte i = 0; i < last_row; i++) {
|
||||
strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols);
|
||||
lcd->setCursor(0, i); // Col 0, Row i
|
||||
lcd->print(disp_screen_buffer[i +1]);
|
||||
}
|
||||
|
||||
char *pch = strchr(disp_log_buffer[disp_log_buffer_ptr],'~'); // = 0x7E (~) Replace degrees character (276 octal)
|
||||
if (pch != NULL) {
|
||||
disp_log_buffer[disp_log_buffer_ptr][pch - disp_log_buffer[disp_log_buffer_ptr]] = '\337'; // = 0xDF
|
||||
}
|
||||
strlcpy(disp_screen_buffer[last_row], disp_log_buffer[disp_log_buffer_ptr], disp_screen_buffer_cols);
|
||||
|
||||
// Fill with spaces
|
||||
byte len = disp_screen_buffer_cols - strlen(disp_screen_buffer[last_row]);
|
||||
if (len) {
|
||||
memset(disp_screen_buffer[last_row] + strlen(disp_screen_buffer[last_row]), 0x20, len);
|
||||
disp_screen_buffer[last_row][disp_screen_buffer_cols -1] = 0;
|
||||
}
|
||||
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]);
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
|
||||
lcd->setCursor(0, last_row);
|
||||
lcd->print(disp_screen_buffer[last_row]);
|
||||
}
|
||||
}
|
||||
|
||||
void LcdPrintLog()
|
||||
{
|
||||
disp_refresh--;
|
||||
if (!disp_refresh) {
|
||||
disp_refresh = Settings.display_refresh;
|
||||
disp_log_buffer_active = (disp_log_buffer_idx != disp_log_buffer_ptr);
|
||||
if (disp_log_buffer_active) {
|
||||
LcdPrintLogLine();
|
||||
DisplayLogBufferPtrInc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LcdTime()
|
||||
{
|
||||
char line[Settings.display_cols[0] +1];
|
||||
|
||||
snprintf_P(line, sizeof(line), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second);
|
||||
LcdCenter(0, line);
|
||||
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);
|
||||
LcdCenter(1, line);
|
||||
}
|
||||
|
||||
void LcdRefresh() // Every second
|
||||
{
|
||||
if (Settings.display_mode) { // Mode 0 is User text
|
||||
switch (Settings.display_mode) {
|
||||
case 1: // Time
|
||||
LcdTime();
|
||||
break;
|
||||
case 2: // Local
|
||||
case 4: // Mqtt
|
||||
LcdPrintLog();
|
||||
break;
|
||||
case 3: // Local
|
||||
case 5: { // Mqtt
|
||||
LcdPrintLog();
|
||||
if (!disp_log_buffer_active) {
|
||||
LcdTime();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
boolean Xdsp01(byte function)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
if (i2c_flg) {
|
||||
if (FUNC_DISPLAY_INIT_DRIVER == function) {
|
||||
LcdInitDriver();
|
||||
}
|
||||
else if (XDSP_01 == Settings.display_model) {
|
||||
switch (function) {
|
||||
case FUNC_DISPLAY_MODEL:
|
||||
result = true;
|
||||
break;
|
||||
case FUNC_DISPLAY_INIT:
|
||||
LcdInit(dsp_init);
|
||||
break;
|
||||
case FUNC_DISPLAY_POWER:
|
||||
LcdDisplayOnOff(disp_power);
|
||||
break;
|
||||
case FUNC_DISPLAY_CLEAR:
|
||||
lcd->clear();
|
||||
break;
|
||||
// case FUNC_DISPLAY_DRAW_HLINE:
|
||||
// break;
|
||||
// case FUNC_DISPLAY_DRAW_VLINE:
|
||||
// break;
|
||||
// case FUNC_DISPLAY_DRAW_CIRCLE:
|
||||
// break;
|
||||
// case FUNC_DISPLAY_FILL_CIRCLE:
|
||||
// break;
|
||||
// case FUNC_DISPLAY_DRAW_RECTANGLE:
|
||||
// break;
|
||||
// case FUNC_DISPLAY_FILL_RECTANGLE:
|
||||
// break;
|
||||
// case FUNC_DISPLAY_DRAW_FRAME:
|
||||
// break;
|
||||
// case FUNC_DISPLAY_TEXT_SIZE:
|
||||
// break;
|
||||
// case FUNC_DISPLAY_FONT_SIZE:
|
||||
// break;
|
||||
case FUNC_DISPLAY_DRAW_STRING:
|
||||
LcdDrawStringAt();
|
||||
break;
|
||||
case FUNC_DISPLAY_ONOFF:
|
||||
LcdDisplayOnOff(dsp_on);
|
||||
break;
|
||||
// case FUNC_DISPLAY_ROTATION:
|
||||
// break;
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
case FUNC_DISPLAY_EVERY_SECOND:
|
||||
LcdRefresh();
|
||||
break;
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_LCD
|
||||
#endif // USE_DISPLAY
|
||||
#endif // USE_I2C
|
|
@ -1,300 +0,0 @@
|
|||
/*
|
||||
xdsp_02_ssd1306.ino - Display Oled ssd1306 support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2018 Theo Arends and Adafruit
|
||||
|
||||
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_I2C
|
||||
#ifdef USE_DISPLAY
|
||||
#ifdef USE_DISPLAY_SSD1306
|
||||
|
||||
#define XDSP_02 2
|
||||
|
||||
#define OLED_ADDRESS1 0x3C // Oled 128x32 I2C address
|
||||
#define OLED_ADDRESS2 0x3D // Oled 128x64 I2C address
|
||||
|
||||
#define OLED_FONT_WIDTH 6
|
||||
#define OLED_FONT_HEIGTH 8
|
||||
|
||||
//#define OLED_BUFFER_COLS 21 or 11 // Max number of columns in display shadow buffer
|
||||
//#define OLED_BUFFER_ROWS 8 or 16 // Max number of lines in display shadow buffer
|
||||
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
|
||||
Adafruit_SSD1306 *oled;
|
||||
|
||||
uint8_t ssd1306_font_x = OLED_FONT_WIDTH;
|
||||
uint8_t ssd1306_font_y = OLED_FONT_HEIGTH;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void Ssd1306InitMode()
|
||||
{
|
||||
oled->setRotation(Settings.display_rotate); // 0
|
||||
oled->invertDisplay(false);
|
||||
oled->clearDisplay();
|
||||
oled->setTextWrap(false); // Allow text to run off edges
|
||||
oled->cp437(true);
|
||||
|
||||
oled->setTextSize(Settings.display_size);
|
||||
oled->setTextColor(WHITE);
|
||||
oled->setCursor(0,0);
|
||||
oled->display();
|
||||
}
|
||||
|
||||
void Ssd1306Init(uint8_t mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case DISPLAY_INIT_MODE:
|
||||
Ssd1306InitMode();
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
DisplayClearScreenBuffer();
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
break;
|
||||
case DISPLAY_INIT_PARTIAL:
|
||||
case DISPLAY_INIT_FULL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Ssd1306InitDriver()
|
||||
{
|
||||
if (!Settings.display_model) {
|
||||
if (I2cDevice(OLED_ADDRESS1)) {
|
||||
Settings.display_address[0] = OLED_ADDRESS1;
|
||||
Settings.display_model = XDSP_02;
|
||||
}
|
||||
else if (I2cDevice(OLED_ADDRESS2)) {
|
||||
Settings.display_address[0] = OLED_ADDRESS2;
|
||||
Settings.display_model = XDSP_02;
|
||||
}
|
||||
}
|
||||
|
||||
if (XDSP_02 == Settings.display_model) {
|
||||
oled = new Adafruit_SSD1306();
|
||||
oled->begin(SSD1306_SWITCHCAPVCC, Settings.display_address[0]);
|
||||
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
DisplayAllocScreenBuffer();
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
|
||||
Ssd1306InitMode();
|
||||
}
|
||||
}
|
||||
|
||||
void Ssd1306Clear()
|
||||
{
|
||||
oled->clearDisplay();
|
||||
oled->setCursor(0, 0);
|
||||
oled->display();
|
||||
}
|
||||
|
||||
void Ssd1306DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag)
|
||||
{
|
||||
if (!flag) {
|
||||
oled->setCursor(x, y);
|
||||
} else {
|
||||
oled->setCursor((x-1) * ssd1306_font_x * Settings.display_size, (y-1) * ssd1306_font_y * Settings.display_size);
|
||||
}
|
||||
oled->println(str);
|
||||
}
|
||||
|
||||
void Ssd1306DisplayOnOff(uint8_t on)
|
||||
{
|
||||
if (on) {
|
||||
oled->ssd1306_command(SSD1306_DISPLAYON);
|
||||
} else {
|
||||
oled->ssd1306_command(SSD1306_DISPLAYOFF);
|
||||
}
|
||||
}
|
||||
|
||||
void Ssd1306OnOff()
|
||||
{
|
||||
Ssd1306DisplayOnOff(disp_power);
|
||||
oled->display();
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
|
||||
void Ssd1306PrintLogLine()
|
||||
{
|
||||
if (!disp_screen_buffer_cols) {
|
||||
DisplayAllocScreenBuffer();
|
||||
} else {
|
||||
uint8_t last_row = Settings.display_rows -1;
|
||||
|
||||
oled->clearDisplay();
|
||||
oled->setTextSize(Settings.display_size);
|
||||
oled->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);
|
||||
oled->println(disp_screen_buffer[i]);
|
||||
}
|
||||
|
||||
char *pch = strchr(disp_log_buffer[disp_log_buffer_ptr],'~'); // = 0x7E (~) Replace degrees character (276 octal)
|
||||
if (pch != NULL) {
|
||||
disp_log_buffer[disp_log_buffer_ptr][pch - disp_log_buffer[disp_log_buffer_ptr]] = '\370'; // = 0xF8
|
||||
}
|
||||
strlcpy(disp_screen_buffer[last_row], disp_log_buffer[disp_log_buffer_ptr], disp_screen_buffer_cols);
|
||||
|
||||
// Fill with spaces
|
||||
byte len = disp_screen_buffer_cols - strlen(disp_screen_buffer[last_row]);
|
||||
if (len) {
|
||||
memset(disp_screen_buffer[last_row] + strlen(disp_screen_buffer[last_row]), 0x20, len);
|
||||
disp_screen_buffer[last_row][disp_screen_buffer_cols -1] = 0;
|
||||
}
|
||||
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]);
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
|
||||
|
||||
oled->println(disp_screen_buffer[last_row]);
|
||||
oled->display();
|
||||
}
|
||||
}
|
||||
|
||||
void Ssd1306PrintLog()
|
||||
{
|
||||
disp_refresh--;
|
||||
if (!disp_refresh) {
|
||||
disp_refresh = Settings.display_refresh;
|
||||
disp_log_buffer_active = (disp_log_buffer_idx != disp_log_buffer_ptr);
|
||||
if (disp_log_buffer_active) {
|
||||
Ssd1306PrintLogLine();
|
||||
DisplayLogBufferPtrInc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Ssd1306Time()
|
||||
{
|
||||
char line[12];
|
||||
|
||||
oled->clearDisplay();
|
||||
oled->setTextSize(2);
|
||||
oled->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 ]
|
||||
oled->println(line);
|
||||
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]
|
||||
oled->println(line);
|
||||
oled->display();
|
||||
}
|
||||
|
||||
void Ssd1306Refresh() // Every second
|
||||
{
|
||||
if (Settings.display_mode) { // Mode 0 is User text
|
||||
switch (Settings.display_mode) {
|
||||
case 1: // Time
|
||||
Ssd1306Time();
|
||||
break;
|
||||
case 2: // Local
|
||||
case 3: // Local
|
||||
case 4: // Mqtt
|
||||
case 5: // Mqtt
|
||||
Ssd1306PrintLog();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
boolean Xdsp02(byte function)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
if (i2c_flg) {
|
||||
if (FUNC_DISPLAY_INIT_DRIVER == function) {
|
||||
Ssd1306InitDriver();
|
||||
}
|
||||
else if (XDSP_02 == Settings.display_model) {
|
||||
|
||||
if (!dsp_color) { dsp_color = WHITE; }
|
||||
|
||||
switch (function) {
|
||||
case FUNC_DISPLAY_MODEL:
|
||||
result = true;
|
||||
break;
|
||||
case FUNC_DISPLAY_INIT:
|
||||
Ssd1306Init(dsp_init);
|
||||
break;
|
||||
case FUNC_DISPLAY_POWER:
|
||||
Ssd1306OnOff();
|
||||
break;
|
||||
case FUNC_DISPLAY_CLEAR:
|
||||
Ssd1306Clear();
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_HLINE:
|
||||
oled->writeFastHLine(dsp_x, dsp_y, dsp_len, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_VLINE:
|
||||
oled->writeFastVLine(dsp_x, dsp_y, dsp_len, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_LINE:
|
||||
oled->writeLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_CIRCLE:
|
||||
oled->drawCircle(dsp_x, dsp_y, dsp_rad, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_FILL_CIRCLE:
|
||||
oled->fillCircle(dsp_x, dsp_y, dsp_rad, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_RECTANGLE:
|
||||
oled->drawRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_FILL_RECTANGLE:
|
||||
oled->fillRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_FRAME:
|
||||
oled->display();
|
||||
break;
|
||||
case FUNC_DISPLAY_TEXT_SIZE:
|
||||
oled->setTextSize(Settings.display_size);
|
||||
break;
|
||||
case FUNC_DISPLAY_FONT_SIZE:
|
||||
// oled->setTextSize(Settings.display_font);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_STRING:
|
||||
Ssd1306DrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag);
|
||||
break;
|
||||
case FUNC_DISPLAY_ONOFF:
|
||||
Ssd1306DisplayOnOff(dsp_on);
|
||||
break;
|
||||
case FUNC_DISPLAY_ROTATION:
|
||||
oled->setRotation(Settings.display_rotate);
|
||||
break;
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
case FUNC_DISPLAY_EVERY_SECOND:
|
||||
Ssd1306Refresh();
|
||||
break;
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_SSD1306
|
||||
#endif // USE_DISPLAY
|
||||
#endif // USE_I2C
|
|
@ -1,361 +0,0 @@
|
|||
/*
|
||||
xdsp_03_matrix.ino - Display 8x8 matrix support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2018 Theo Arends and Adafruit
|
||||
|
||||
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_I2C
|
||||
#ifdef USE_DISPLAY
|
||||
#ifdef USE_DISPLAY_MATRIX
|
||||
|
||||
#define XDSP_03 3
|
||||
|
||||
#define MTX_MAX_SCREEN_BUFFER 80
|
||||
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_LEDBackpack.h> // 8x8 Matrix
|
||||
|
||||
Adafruit_8x8matrix *matrix[8];
|
||||
uint8_t mtx_matrices = 0;
|
||||
uint8_t mtx_state = 0;
|
||||
uint8_t mtx_counter = 0;
|
||||
int16_t mtx_x = 0;
|
||||
int16_t mtx_y = 0;
|
||||
|
||||
char mtx_buffer[MTX_MAX_SCREEN_BUFFER];
|
||||
uint8_t mtx_mode = 0;
|
||||
uint8_t mtx_loop = 0;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void MatrixWrite()
|
||||
{
|
||||
for (byte i = 0; i < mtx_matrices; i++) {
|
||||
matrix[i]->writeDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixClear()
|
||||
{
|
||||
for (byte i = 0; i < mtx_matrices; i++) {
|
||||
matrix[i]->clear();
|
||||
}
|
||||
MatrixWrite();
|
||||
}
|
||||
|
||||
void MatrixFixed(char* txt)
|
||||
{
|
||||
for (byte i = 0; i < mtx_matrices; i++) {
|
||||
matrix[i]->clear();
|
||||
matrix[i]->setCursor(-i *8, 0);
|
||||
matrix[i]->print(txt);
|
||||
matrix[i]->setBrightness(Settings.display_dimmer);
|
||||
}
|
||||
MatrixWrite();
|
||||
}
|
||||
|
||||
void MatrixCenter(char* txt)
|
||||
{
|
||||
int offset;
|
||||
|
||||
int len = strlen(txt);
|
||||
offset = (len < 8) ? offset = ((mtx_matrices *8) - (len *6)) / 2 : 0;
|
||||
for (byte i = 0; i < mtx_matrices; i++) {
|
||||
matrix[i]->clear();
|
||||
matrix[i]->setCursor(-(i *8)+offset, 0);
|
||||
matrix[i]->print(txt);
|
||||
matrix[i]->setBrightness(Settings.display_dimmer);
|
||||
}
|
||||
MatrixWrite();
|
||||
}
|
||||
|
||||
void MatrixScrollLeft(char* txt, int loop)
|
||||
{
|
||||
switch (mtx_state) {
|
||||
case 1:
|
||||
mtx_state = 2;
|
||||
// Horiz. position of text -- starts off right edge
|
||||
mtx_x = 8 * mtx_matrices;
|
||||
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), txt);
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
|
||||
disp_refresh = Settings.display_refresh;
|
||||
case 2:
|
||||
disp_refresh--;
|
||||
if (!disp_refresh) {
|
||||
disp_refresh = Settings.display_refresh;
|
||||
for (byte i = 0; i < mtx_matrices; i++) {
|
||||
matrix[i]->clear();
|
||||
matrix[i]->setCursor(mtx_x - i *8, 0);
|
||||
matrix[i]->print(txt);
|
||||
matrix[i]->setBrightness(Settings.display_dimmer);
|
||||
}
|
||||
MatrixWrite();
|
||||
// Move text position left by 1 pixel.
|
||||
mtx_x--;
|
||||
int16_t len = strlen(txt);
|
||||
if (mtx_x < -(len *6)) {
|
||||
mtx_state = loop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixScrollUp(char* txt, int loop)
|
||||
{
|
||||
int wordcounter = 0;
|
||||
char tmpbuf[200];
|
||||
char *words[100];
|
||||
// char separators[] = " ,.;:!?";
|
||||
// char separators[] = " ";
|
||||
// char separators[] = " /|";
|
||||
char separators[] = " /";
|
||||
|
||||
switch (mtx_state) {
|
||||
case 1:
|
||||
mtx_state = 2;
|
||||
// Vertical position of text -- starts off left bottom edge
|
||||
mtx_y = 8;
|
||||
mtx_counter = 0;
|
||||
disp_refresh = Settings.display_refresh;
|
||||
case 2:
|
||||
disp_refresh--;
|
||||
if (!disp_refresh) {
|
||||
disp_refresh = Settings.display_refresh;
|
||||
strlcpy(tmpbuf, txt, sizeof(tmpbuf));
|
||||
char *p = strtok(tmpbuf, separators);
|
||||
while (p != NULL && wordcounter < 40) {
|
||||
words[wordcounter++] = p;
|
||||
p = strtok(NULL, separators);
|
||||
}
|
||||
for (byte i = 0; i < mtx_matrices; i++) {
|
||||
matrix[i]->clear();
|
||||
for (byte j = 0; j < wordcounter; j++) {
|
||||
matrix[i]->setCursor(-i *8, mtx_y + (j *8));
|
||||
matrix[i]->println(words[j]);
|
||||
}
|
||||
matrix[i]->setBrightness(Settings.display_dimmer);
|
||||
}
|
||||
MatrixWrite();
|
||||
if (((mtx_y %8) == 0) && mtx_counter) {
|
||||
mtx_counter--;
|
||||
} else {
|
||||
mtx_y--; // Move text position up by 1 pixel.
|
||||
mtx_counter = STATES * 1; // Hold text for 1 seconds
|
||||
}
|
||||
if (mtx_y < -(wordcounter *8)) {
|
||||
mtx_state = loop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void MatrixInitMode()
|
||||
{
|
||||
for (byte i = 0; i < mtx_matrices; i++) {
|
||||
matrix[i]->setRotation(Settings.display_rotate); // 1
|
||||
matrix[i]->setBrightness(Settings.display_dimmer);
|
||||
matrix[i]->blinkRate(0); // 0 - 3
|
||||
matrix[i]->setTextWrap(false); // Allow text to run off edges
|
||||
// matrix[i]->setTextSize(Settings.display_size);
|
||||
// matrix[i]->setTextColor(LED_RED);
|
||||
matrix[i]->cp437(true);
|
||||
}
|
||||
MatrixClear();
|
||||
}
|
||||
|
||||
void MatrixInit(uint8_t mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case DISPLAY_INIT_MODE:
|
||||
MatrixInitMode();
|
||||
break;
|
||||
case DISPLAY_INIT_PARTIAL:
|
||||
case DISPLAY_INIT_FULL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixInitDriver()
|
||||
{
|
||||
if (!Settings.display_model) {
|
||||
if (I2cDevice(Settings.display_address[1])) {
|
||||
Settings.display_model = XDSP_03;
|
||||
}
|
||||
}
|
||||
|
||||
if (XDSP_03 == Settings.display_model) {
|
||||
mtx_state = 1;
|
||||
for (mtx_matrices = 0; mtx_matrices < 8; mtx_matrices++) {
|
||||
if (Settings.display_address[mtx_matrices]) {
|
||||
matrix[mtx_matrices] = new Adafruit_8x8matrix();
|
||||
matrix[mtx_matrices]->begin(Settings.display_address[mtx_matrices]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MatrixInitMode();
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixOnOff()
|
||||
{
|
||||
if (!disp_power) {
|
||||
MatrixClear();
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag)
|
||||
{
|
||||
snprintf(mtx_buffer, sizeof(mtx_buffer), str);
|
||||
mtx_mode = x;
|
||||
mtx_loop = y;
|
||||
if (!mtx_state) { mtx_state = 1; }
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
|
||||
void MatrixBufferScroll(uint8_t direction)
|
||||
{
|
||||
if (disp_log_buffer_idx != disp_log_buffer_ptr) {
|
||||
if (!mtx_state) {
|
||||
mtx_state = 1;
|
||||
}
|
||||
|
||||
char *pch = strchr(disp_log_buffer[disp_log_buffer_ptr],'~'); // = 0x7E (~) Replace degrees character (276 octal)
|
||||
if (pch != NULL) {
|
||||
disp_log_buffer[disp_log_buffer_ptr][pch - disp_log_buffer[disp_log_buffer_ptr]] = '\370'; // = 0xF8
|
||||
}
|
||||
|
||||
if (direction) {
|
||||
MatrixScrollUp(disp_log_buffer[disp_log_buffer_ptr], 0);
|
||||
} else {
|
||||
// Remove extra spaces
|
||||
uint8_t space = 0;
|
||||
uint8_t max_cols = (disp_log_buffer_cols < MTX_MAX_SCREEN_BUFFER) ? disp_log_buffer_cols : MTX_MAX_SCREEN_BUFFER;
|
||||
mtx_buffer[0] = '\0';
|
||||
for (byte i = 0; i < max_cols; i++) {
|
||||
if (disp_log_buffer[disp_log_buffer_ptr][i] == ' ') {
|
||||
space++;
|
||||
} else {
|
||||
space = 0;
|
||||
}
|
||||
if (space < 2) {
|
||||
strncat(mtx_buffer, (const char*)disp_log_buffer[disp_log_buffer_ptr] +i, 1);
|
||||
}
|
||||
}
|
||||
MatrixScrollLeft(mtx_buffer, 0);
|
||||
}
|
||||
if (!mtx_state) {
|
||||
DisplayLogBufferPtrInc();
|
||||
}
|
||||
} else {
|
||||
char disp_time[9]; // 13:45:43
|
||||
|
||||
snprintf_P(disp_time, sizeof(disp_time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second);
|
||||
MatrixFixed(disp_time);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
|
||||
void MatrixRefresh() // Every second
|
||||
{
|
||||
if (disp_power) {
|
||||
switch (Settings.display_mode) {
|
||||
case 0: {
|
||||
switch (mtx_mode) {
|
||||
case 0:
|
||||
MatrixScrollLeft(mtx_buffer, mtx_loop);
|
||||
break;
|
||||
case 1:
|
||||
MatrixScrollUp(mtx_buffer, mtx_loop);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
case 2: {
|
||||
char disp_date[9]; // 24-04-17
|
||||
snprintf_P(disp_date, sizeof(disp_date), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year -2000);
|
||||
MatrixFixed(disp_date);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
char disp_day[10]; // Mon
|
||||
snprintf_P(disp_day, sizeof(disp_day), PSTR("%d %s"), RtcTime.day_of_month, RtcTime.name_of_month);
|
||||
MatrixCenter(disp_day);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
MatrixBufferScroll(0);
|
||||
break;
|
||||
case 1: // Time and user text
|
||||
case 5: // Time, user text and MQTT
|
||||
MatrixBufferScroll(1);
|
||||
break;
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
boolean Xdsp03(byte function)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
if (i2c_flg) {
|
||||
if (FUNC_DISPLAY_INIT_DRIVER == function) {
|
||||
MatrixInitDriver();
|
||||
}
|
||||
else if (XDSP_03 == Settings.display_model) {
|
||||
switch (function) {
|
||||
case FUNC_DISPLAY_MODEL:
|
||||
result = true;
|
||||
break;
|
||||
case FUNC_DISPLAY_INIT:
|
||||
MatrixInit(dsp_init);
|
||||
break;
|
||||
case FUNC_DISPLAY_EVERY_50_MSECOND:
|
||||
MatrixRefresh();
|
||||
break;
|
||||
case FUNC_DISPLAY_POWER:
|
||||
MatrixOnOff();
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_STRING:
|
||||
MatrixDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_MATRIX
|
||||
#endif // USE_DISPLAY
|
||||
#endif // USE_I2C
|
|
@ -1,279 +0,0 @@
|
|||
/*
|
||||
xdsp_04_ili9341.ino - Display Tft Ili9341 support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2018 Theo Arends and Adafruit
|
||||
|
||||
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_SPI
|
||||
#ifdef USE_DISPLAY
|
||||
#ifdef USE_DISPLAY_ILI9341
|
||||
|
||||
#define XDSP_04 4
|
||||
|
||||
#define TFT_TOP 16
|
||||
#define TFT_BOTTOM 16
|
||||
#define TFT_FONT_WIDTH 6
|
||||
#define TFT_FONT_HEIGTH 8 // Adafruit minimal font heigth pixels
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_ILI9341.h> // TFT 320x240 and 480x320
|
||||
|
||||
Adafruit_ILI9341 *tft;
|
||||
|
||||
uint16_t tft_scroll;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void Ili9341InitMode()
|
||||
{
|
||||
tft->setRotation(Settings.display_rotate); // 0
|
||||
tft->invertDisplay(0);
|
||||
tft->fillScreen(ILI9341_BLACK);
|
||||
tft->setTextWrap(false); // Allow text to run off edges
|
||||
tft->cp437(true);
|
||||
if (!Settings.display_mode) {
|
||||
tft->setCursor(0, 0);
|
||||
tft->setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
||||
tft->setTextSize(1);
|
||||
} else {
|
||||
tft->setScrollMargins(TFT_TOP, TFT_BOTTOM);
|
||||
tft->setCursor(0, 0);
|
||||
tft->setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
|
||||
tft->setTextSize(2);
|
||||
tft->println("HEADER");
|
||||
|
||||
tft_scroll = TFT_TOP;
|
||||
}
|
||||
}
|
||||
|
||||
void Ili9341Init(uint8_t mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case DISPLAY_INIT_MODE:
|
||||
Ili9341InitMode();
|
||||
break;
|
||||
case DISPLAY_INIT_PARTIAL:
|
||||
case DISPLAY_INIT_FULL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Ili9341InitDriver()
|
||||
{
|
||||
if (!Settings.display_model) {
|
||||
Settings.display_model = XDSP_04;
|
||||
}
|
||||
|
||||
if (XDSP_04 == Settings.display_model) {
|
||||
tft = new Adafruit_ILI9341(pin[GPIO_SPI_CS], pin[GPIO_SPI_DC]);
|
||||
tft->begin();
|
||||
|
||||
Ili9341InitMode();
|
||||
}
|
||||
}
|
||||
|
||||
void Ili9341Clear()
|
||||
{
|
||||
tft->fillScreen(ILI9341_BLACK);
|
||||
tft->setCursor(0, 0);
|
||||
}
|
||||
|
||||
void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag)
|
||||
{
|
||||
uint16_t active_color = ILI9341_WHITE;
|
||||
|
||||
tft->setTextSize(Settings.display_size);
|
||||
if (!flag) {
|
||||
tft->setCursor(x, y);
|
||||
} else {
|
||||
tft->setCursor((x-1) * TFT_FONT_WIDTH * Settings.display_size, (y-1) * TFT_FONT_HEIGTH * Settings.display_size);
|
||||
}
|
||||
if (color) { active_color = color; }
|
||||
tft->setTextColor(active_color, ILI9341_BLACK);
|
||||
tft->println(str);
|
||||
}
|
||||
|
||||
void Ili9341DisplayOnOff(uint8_t on)
|
||||
{
|
||||
// tft->showDisplay(on);
|
||||
// tft->invertDisplay(on);
|
||||
if (pin[GPIO_BACKLIGHT] < 99) {
|
||||
pinMode(pin[GPIO_BACKLIGHT], OUTPUT);
|
||||
digitalWrite(pin[GPIO_BACKLIGHT], on);
|
||||
}
|
||||
}
|
||||
|
||||
void Ili9341OnOff()
|
||||
{
|
||||
Ili9341DisplayOnOff(disp_power);
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
|
||||
void Ili9341PrintLogLine()
|
||||
{
|
||||
tft->setTextColor(ILI9341_CYAN, ILI9341_BLACK); // Add background color to solve flicker
|
||||
tft->setCursor(0, tft_scroll);
|
||||
byte size = Settings.display_size;
|
||||
tft->setTextSize(size);
|
||||
uint16_t theight = size * TFT_FONT_HEIGTH;
|
||||
|
||||
tft->fillRect(0, tft_scroll, tft->width(), theight, ILI9341_BLACK); // Erase line
|
||||
|
||||
char *pch = strchr(disp_log_buffer[disp_log_buffer_ptr],'~'); // = 0x7E (~) Replace degrees character (276 octal)
|
||||
if (pch != NULL) {
|
||||
disp_log_buffer[disp_log_buffer_ptr][pch - disp_log_buffer[disp_log_buffer_ptr]] = '\370'; // = 0xF8
|
||||
}
|
||||
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION "[%s]"), disp_log_buffer[disp_log_buffer_ptr]);
|
||||
AddLog(LOG_LEVEL_DEBUG);
|
||||
|
||||
tft->print(disp_log_buffer[disp_log_buffer_ptr]);
|
||||
tft_scroll += theight;
|
||||
if (tft_scroll >= (tft->height() - TFT_BOTTOM)) {
|
||||
tft_scroll = TFT_TOP;
|
||||
}
|
||||
tft->scrollTo(tft_scroll);
|
||||
}
|
||||
|
||||
void Ili9341PrintLog()
|
||||
{
|
||||
disp_refresh--;
|
||||
if (!disp_refresh) {
|
||||
disp_refresh = Settings.display_refresh;
|
||||
disp_log_buffer_active = (disp_log_buffer_idx != disp_log_buffer_ptr);
|
||||
if (disp_log_buffer_active) {
|
||||
Ili9341PrintLogLine();
|
||||
DisplayLogBufferPtrInc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Ili9341Refresh() // Every second
|
||||
{
|
||||
if (Settings.display_mode) { // Mode 0 is User text
|
||||
char tftdt[21];
|
||||
char disp_time[9]; // 13:45:43
|
||||
char disp_date4[11]; // 24-04-2017
|
||||
|
||||
snprintf_P(disp_time, sizeof(disp_time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second);
|
||||
snprintf_P(disp_date4, sizeof(disp_date4), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year);
|
||||
|
||||
tft->setTextSize(2);
|
||||
tft->setTextColor(ILI9341_YELLOW, ILI9341_BLACK); // Add background color to solve flicker
|
||||
tft->setCursor(0, 0);
|
||||
snprintf_P(tftdt, sizeof(tftdt), PSTR("%s %s"), disp_date4, disp_time);
|
||||
tft->print(tftdt);
|
||||
|
||||
switch (Settings.display_mode) {
|
||||
case 1: // Text
|
||||
case 2: // Local
|
||||
case 3: // Local
|
||||
case 4: // Mqtt
|
||||
case 5: // Mqtt
|
||||
Ili9341PrintLog();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
boolean Xdsp04(byte function)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
if (spi_flg) {
|
||||
if (FUNC_DISPLAY_INIT_DRIVER == function) {
|
||||
Ili9341InitDriver();
|
||||
}
|
||||
else if (XDSP_04 == Settings.display_model) {
|
||||
|
||||
if (!dsp_color) { dsp_color = ILI9341_WHITE; }
|
||||
|
||||
switch (function) {
|
||||
case FUNC_DISPLAY_MODEL:
|
||||
result = true;
|
||||
break;
|
||||
case FUNC_DISPLAY_INIT:
|
||||
Ili9341Init(dsp_init);
|
||||
break;
|
||||
case FUNC_DISPLAY_POWER:
|
||||
Ili9341OnOff();
|
||||
break;
|
||||
case FUNC_DISPLAY_CLEAR:
|
||||
Ili9341Clear();
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_HLINE:
|
||||
tft->writeFastHLine(dsp_x, dsp_y, dsp_len, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_VLINE:
|
||||
tft->writeFastVLine(dsp_x, dsp_y, dsp_len, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_LINE:
|
||||
tft->writeLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_CIRCLE:
|
||||
tft->drawCircle(dsp_x, dsp_y, dsp_rad, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_FILL_CIRCLE:
|
||||
tft->fillCircle(dsp_x, dsp_y, dsp_rad, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_RECTANGLE:
|
||||
tft->drawRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_FILL_RECTANGLE:
|
||||
tft->fillRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color);
|
||||
break;
|
||||
// case FUNC_DISPLAY_DRAW_FRAME:
|
||||
// oled->display();
|
||||
// break;
|
||||
case FUNC_DISPLAY_TEXT_SIZE:
|
||||
tft->setTextSize(Settings.display_size);
|
||||
break;
|
||||
case FUNC_DISPLAY_FONT_SIZE:
|
||||
// tft->setTextSize(Settings.display_font);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_STRING:
|
||||
Ili9341DrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag);
|
||||
break;
|
||||
case FUNC_DISPLAY_ONOFF:
|
||||
Ili9341DisplayOnOff(dsp_on);
|
||||
break;
|
||||
case FUNC_DISPLAY_ROTATION:
|
||||
tft->setRotation(Settings.display_rotate);
|
||||
break;
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
case FUNC_DISPLAY_EVERY_SECOND:
|
||||
Ili9341Refresh();
|
||||
break;
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_ILI9341
|
||||
#endif // USE_DISPLAY
|
||||
#endif // USE_SPI
|
|
@ -1,261 +0,0 @@
|
|||
/*
|
||||
xdsp_05_epaper.ino - Display e-paper support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2018 Theo Arends, Gerhard Mutz and Waveshare
|
||||
|
||||
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_SPI
|
||||
#ifdef USE_DISPLAY
|
||||
#ifdef USE_DISPLAY_EPAPER
|
||||
|
||||
#define XDSP_05 5
|
||||
|
||||
#define COLORED 0
|
||||
#define UNCOLORED 1
|
||||
|
||||
// using font 8 is opional (num=3)
|
||||
// very badly readable, but may be useful for graphs
|
||||
#define USE_TINY_FONT
|
||||
|
||||
#include <epd2in9.h>
|
||||
#include <epdpaint.h>
|
||||
|
||||
unsigned char image[(EPD_HEIGHT * EPD_WIDTH) / 8];
|
||||
|
||||
Paint paint(image, EPD_WIDTH, EPD_HEIGHT); // width should be the multiple of 8
|
||||
Epd epd;
|
||||
sFONT *selected_font;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void EpdInitMode()
|
||||
{
|
||||
// whiten display with full update
|
||||
epd.Init(lut_full_update);
|
||||
|
||||
epd.ClearFrameMemory(0xFF); // bit set = white, bit reset = black
|
||||
epd.DisplayFrame();
|
||||
delay(3000);
|
||||
|
||||
// switch to partial update
|
||||
epd.Init(lut_partial_update);
|
||||
|
||||
// Clear image memory
|
||||
epd.ClearFrameMemory(0xFF); // bit set = white, bit reset = black
|
||||
epd.DisplayFrame();
|
||||
delay(500);
|
||||
|
||||
selected_font = &Font12;
|
||||
|
||||
paint.SetRotate(Settings.display_rotate);
|
||||
/*
|
||||
// Welcome text
|
||||
paint.Clear(UNCOLORED);
|
||||
paint.DrawStringAt(50, 50, "Waveshare E-Paper Display!", selected_font, COLORED);
|
||||
epd.SetFrameMemory(paint.GetImage(), 0, 0, paint.GetWidth(), paint.GetHeight());
|
||||
epd.DisplayFrame();
|
||||
delay(1000);
|
||||
*/
|
||||
paint.Clear(UNCOLORED);
|
||||
}
|
||||
|
||||
void EpdInitPartial()
|
||||
{
|
||||
epd.Init(lut_partial_update);
|
||||
//paint.Clear(UNCOLORED);
|
||||
epd.DisplayFrame();
|
||||
delay(500);
|
||||
}
|
||||
|
||||
void EpdInitFull()
|
||||
{
|
||||
epd.Init(lut_full_update);
|
||||
//paint.Clear(UNCOLORED);
|
||||
//epd.ClearFrameMemory(0xFF);
|
||||
epd.DisplayFrame();
|
||||
delay(3000);
|
||||
}
|
||||
|
||||
void EpdInit(uint8_t mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case DISPLAY_INIT_MODE:
|
||||
EpdInitMode();
|
||||
break;
|
||||
case DISPLAY_INIT_PARTIAL:
|
||||
EpdInitPartial();
|
||||
break;
|
||||
case DISPLAY_INIT_FULL:
|
||||
EpdInitFull();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EpdInitDriver()
|
||||
{
|
||||
if (!Settings.display_model) {
|
||||
Settings.display_model = XDSP_05;
|
||||
}
|
||||
|
||||
if (XDSP_05 == Settings.display_model) {
|
||||
epd.cs_pin = pin[GPIO_SPI_CS];
|
||||
epd.mosi_pin = pin[GPIO_SPI_MOSI]; // 13
|
||||
epd.sclk_pin = pin[GPIO_SPI_CLK]; // 14
|
||||
|
||||
EpdInitMode();
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void EpdClear()
|
||||
{
|
||||
paint.Clear(UNCOLORED);
|
||||
}
|
||||
|
||||
void EpdSetFont(uint8_t font)
|
||||
{
|
||||
if (1 == font) {
|
||||
selected_font = &Font12;
|
||||
} else {
|
||||
#ifdef USE_TINY_FONT
|
||||
if (2 == font) {
|
||||
selected_font = &Font24;
|
||||
} else {
|
||||
selected_font = &Font8;
|
||||
}
|
||||
#else
|
||||
selected_font = &Font24;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void EpdDrawStringAt(uint16_t x, uint16_t y, char *str, uint8_t color, uint8_t flag)
|
||||
{
|
||||
if (!flag) {
|
||||
paint.DrawStringAt(x, y, str, selected_font, color);
|
||||
} else {
|
||||
paint.DrawStringAt((x-1) * selected_font->Width, (y-1) * selected_font->Height, str, selected_font, color);
|
||||
}
|
||||
}
|
||||
|
||||
// not needed
|
||||
void EpdDisplayOnOff(uint8_t on)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void EpdOnOff()
|
||||
{
|
||||
EpdDisplayOnOff(disp_power);
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
|
||||
void EpdRefresh() // Every second
|
||||
{
|
||||
if (Settings.display_mode) { // Mode 0 is User text
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
boolean Xdsp05(byte function)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
if (spi_flg) {
|
||||
if (FUNC_DISPLAY_INIT_DRIVER == function) {
|
||||
EpdInitDriver();
|
||||
}
|
||||
else if (XDSP_04 == Settings.display_model) {
|
||||
|
||||
if (!dsp_color) { dsp_color = COLORED; }
|
||||
|
||||
switch (function) {
|
||||
case FUNC_DISPLAY_MODEL:
|
||||
result = true;
|
||||
break;
|
||||
case FUNC_DISPLAY_INIT:
|
||||
EpdInit(dsp_init);
|
||||
break;
|
||||
case FUNC_DISPLAY_POWER:
|
||||
EpdOnOff();
|
||||
break;
|
||||
case FUNC_DISPLAY_CLEAR:
|
||||
EpdClear();
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_HLINE:
|
||||
paint.DrawHorizontalLine(dsp_x, dsp_y, dsp_len, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_VLINE:
|
||||
paint.DrawVerticalLine(dsp_x, dsp_y, dsp_len, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_LINE:
|
||||
paint.DrawLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_CIRCLE:
|
||||
paint.DrawCircle(dsp_x, dsp_y, dsp_rad, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_FILL_CIRCLE:
|
||||
paint.DrawFilledCircle(dsp_x, dsp_y, dsp_rad, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_RECTANGLE:
|
||||
paint.DrawRectangle(dsp_x, dsp_y, dsp_x + dsp_x2, dsp_y + dsp_y2, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_FILL_RECTANGLE:
|
||||
paint.DrawFilledRectangle(dsp_x, dsp_y, dsp_x + dsp_x2, dsp_y + dsp_y2, dsp_color);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_FRAME:
|
||||
epd.SetFrameMemory(paint.GetImage(), 0, 0, paint.GetWidth(), paint.GetHeight());
|
||||
epd.DisplayFrame();
|
||||
break;
|
||||
case FUNC_DISPLAY_TEXT_SIZE:
|
||||
// EpdSetFontorSize(Settings.display_size);
|
||||
break;
|
||||
case FUNC_DISPLAY_FONT_SIZE:
|
||||
EpdSetFont(Settings.display_font);
|
||||
break;
|
||||
case FUNC_DISPLAY_DRAW_STRING:
|
||||
EpdDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag);
|
||||
break;
|
||||
case FUNC_DISPLAY_ONOFF:
|
||||
EpdDisplayOnOff(dsp_on);
|
||||
break;
|
||||
case FUNC_DISPLAY_ROTATION:
|
||||
paint.SetRotate(Settings.display_rotate);
|
||||
break;
|
||||
#ifdef USE_DISPLAY_MODES1TO5
|
||||
case FUNC_DISPLAY_EVERY_SECOND:
|
||||
EpdRefresh();
|
||||
break;
|
||||
#endif // USE_DISPLAY_MODES1TO5
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_EPAPER
|
||||
#endif // USE_DISPLAY
|
||||
#endif // USE_SPI
|
|
@ -1,125 +0,0 @@
|
|||
/*
|
||||
xdsp_interface.ino - Display interface support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2018 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/>.
|
||||
*/
|
||||
|
||||
boolean (* const xdsp_func_ptr[])(byte) PROGMEM = { // Display Function Pointers
|
||||
#ifdef XDSP_01
|
||||
&Xdsp01,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_02
|
||||
&Xdsp02,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_03
|
||||
&Xdsp03,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_04
|
||||
&Xdsp04,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_05
|
||||
&Xdsp05,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_06
|
||||
&Xdsp06,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_07
|
||||
&Xdsp07,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_08
|
||||
&Xdsp08,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_09
|
||||
&Xdsp09,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_10
|
||||
&Xdsp10,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_11
|
||||
&Xdsp11,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_12
|
||||
&Xdsp12,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_13
|
||||
&Xdsp13,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_14
|
||||
&Xdsp14,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_15
|
||||
&Xdsp15,
|
||||
#endif
|
||||
|
||||
#ifdef XDSP_16
|
||||
&Xdsp16
|
||||
#endif
|
||||
};
|
||||
|
||||
const uint8_t xdsp_present = sizeof(xdsp_func_ptr) / sizeof(xdsp_func_ptr[0]); // Number of drivers found
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Function call to all xdsp
|
||||
*
|
||||
* FUNC_DISPLAY_INIT_DRIVER
|
||||
* FUNC_DISPLAY_INIT
|
||||
* FUNC_DISPLAY_EVERY_50_MSECOND
|
||||
* FUNC_DISPLAY_EVERY_SECOND
|
||||
* FUNC_DISPLAY_POWER
|
||||
* FUNC_DISPLAY_CLEAR
|
||||
* FUNC_DISPLAY_DRAW_FRAME
|
||||
* FUNC_DISPLAY_DRAW_HLINE
|
||||
* FUNC_DISPLAY_DRAW_VLINE
|
||||
* FUNC_DISPLAY_DRAW_CIRCLE
|
||||
* FUNC_DISPLAY_FILL_CIRCLE
|
||||
* FUNC_DISPLAY_DRAW_RECTANGLE
|
||||
* FUNC_DISPLAY_FILL_RECTANGLE
|
||||
* FUNC_DISPLAY_FONT_SIZE
|
||||
* FUNC_DISPLAY_ROTATION
|
||||
* FUNC_DISPLAY_DRAW_STRING
|
||||
* FUNC_DISPLAY_ONOFF
|
||||
\*********************************************************************************************/
|
||||
|
||||
uint8_t XdspPresent()
|
||||
{
|
||||
return xdsp_present;
|
||||
}
|
||||
|
||||
boolean XdspCall(byte Function)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
for (byte x = 0; x < xdsp_present; x++) {
|
||||
result = xdsp_func_ptr[x](Function);
|
||||
if (result) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue