mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 03:06:33 +00:00
Add initial Sonoff POWR3xxD display support
Add initial Sonoff POWR3xxD display support (#15856)
This commit is contained in:
parent
53d2c76cec
commit
2c0f003898
265
tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino
Normal file
265
tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
xdrv_87_tm1621_sonoff.ino - Sonoff POWR3xxD and THR3xxD display support for Tasmota
|
||||
|
||||
SPDX-FileCopyrightText: 2022 Theo Arends
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#ifdef USE_DISPLAY_TM1621_SONOFF
|
||||
/*********************************************************************************************\
|
||||
* Sonoff POWR3xxD and THR3xxD LCD support
|
||||
*
|
||||
* {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
|
||||
* {"NAME":"Sonoff POWR320D","GPIO":[32,0,224,0,225,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XDRV_87 87
|
||||
|
||||
#define TM1621_PULSE_WIDTH 10 // microseconds (Sonoff = 100)
|
||||
|
||||
#define TM1621_SYS_EN 0x01 // 0b00000001
|
||||
#define TM1621_LCD_ON 0x03 // 0b00000011
|
||||
#define TM1621_TIMER_DIS 0x04 // 0b00000100
|
||||
#define TM1621_WDT_DIS 0x05 // 0b00000101
|
||||
#define TM1621_TONE_OFF 0x08 // 0b00001000
|
||||
#define TM1621_BIAS 0x29 // 0b00101001 = LCD 1/3 bias 4 commons option
|
||||
#define TM1621_IRQ_DIS 0x80 // 0b100x0xxx
|
||||
|
||||
const uint8_t tm1621_commands[] = { TM1621_SYS_EN, TM1621_LCD_ON, TM1621_BIAS, TM1621_TIMER_DIS, TM1621_WDT_DIS, TM1621_TONE_OFF, TM1621_IRQ_DIS };
|
||||
|
||||
const char tm1621_kchar[] PROGMEM = { "0|1|2|3|4|5|6|7|8|9|-| " };
|
||||
// 0 1 2 3 4 5 6 7 8 9 - off
|
||||
const uint8_t tm1621_digit_row[2][12] = {{ 0x5F, 0x50, 0x3D, 0x79, 0x72, 0x6B, 0x6F, 0x51, 0x7F, 0x7B, 0x20, 0x00 },
|
||||
{ 0xF5, 0x05, 0xB6, 0x97, 0x47, 0xD3, 0xF3, 0x85, 0xF7, 0xD7, 0x02, 0x00 }};
|
||||
|
||||
struct Tm1621 {
|
||||
uint8_t buffer[8];
|
||||
char row[2][12];
|
||||
uint8_t pin_da;
|
||||
uint8_t pin_cs;
|
||||
uint8_t pin_rd;
|
||||
uint8_t pin_wr;
|
||||
uint8_t state;
|
||||
bool celsius;
|
||||
bool fahrenheit;
|
||||
bool humidity;
|
||||
bool voltage;
|
||||
bool kwh;
|
||||
bool present;
|
||||
} Tm1621;
|
||||
|
||||
void TM1621StopSequence(void) {
|
||||
digitalWrite(Tm1621.pin_cs, 1); // Stop command sequence
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH / 2);
|
||||
digitalWrite(Tm1621.pin_da, 1); // Reset data
|
||||
}
|
||||
|
||||
void TM1621SendCmnd(uint16_t command) {
|
||||
uint16_t full_command = (0x0400 | command) << 5; // 0b100cccccccc00000
|
||||
digitalWrite(Tm1621.pin_cs, 0); // Start command sequence
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH / 2);
|
||||
for (uint32_t i = 0; i < 12; i++) {
|
||||
digitalWrite(Tm1621.pin_wr, 0); // Start write sequence
|
||||
if (full_command & 0x8000) {
|
||||
digitalWrite(Tm1621.pin_da, 1); // Set data
|
||||
} else {
|
||||
digitalWrite(Tm1621.pin_da, 0); // Set data
|
||||
}
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
digitalWrite(Tm1621.pin_wr, 1); // Read data
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
full_command <<= 1;
|
||||
}
|
||||
TM1621StopSequence();
|
||||
}
|
||||
|
||||
void TM1621SendAddress(uint16_t address) {
|
||||
uint16_t full_address = (address | 0x0140) << 7; // 0b101aaaaaa0000000
|
||||
digitalWrite(Tm1621.pin_cs, 0); // Start command sequence
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH / 2);
|
||||
for (uint32_t i = 0; i < 9; i++) {
|
||||
digitalWrite(Tm1621.pin_wr, 0); // Start write sequence
|
||||
if (full_address & 0x8000) {
|
||||
digitalWrite(Tm1621.pin_da, 1); // Set data
|
||||
} else {
|
||||
digitalWrite(Tm1621.pin_da, 0); // Set data
|
||||
}
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
digitalWrite(Tm1621.pin_wr, 1); // Read data
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
full_address <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void TM1621SendCommon(uint8_t common) {
|
||||
for (uint32_t i = 0; i < 8; i++) {
|
||||
digitalWrite(Tm1621.pin_wr, 0); // Start write sequence
|
||||
if (common & 1) {
|
||||
digitalWrite(Tm1621.pin_da, 1); // Set data
|
||||
} else {
|
||||
digitalWrite(Tm1621.pin_da, 0); // Set data
|
||||
}
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
digitalWrite(Tm1621.pin_wr, 1); // Read data
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
common >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void TM1621SendRows(void) {
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Row1 '%s', Row2 '%s'"), Tm1621.row[0], Tm1621.row[1]);
|
||||
|
||||
uint8_t buffer[8] = { 0 }; // TM1621 16-segment 4-bit common buffer
|
||||
char row[4];
|
||||
for (uint32_t j = 0; j < 2; j++) {
|
||||
// 0.4V => " 04", 0.0A => " ", 1234.5V => "1234"
|
||||
uint32_t len = strlen(Tm1621.row[j]);
|
||||
char *dp = nullptr;
|
||||
int row_idx = len -3;
|
||||
if (len <= 5) {
|
||||
dp = strchr(Tm1621.row[j], '.');
|
||||
row_idx = len -1;
|
||||
}
|
||||
row[3] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' ';
|
||||
if ((row_idx >= 0) && dp) { row_idx--; }
|
||||
row[2] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' ';
|
||||
row[1] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' ';
|
||||
row[0] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' ';
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump%d %4_H"), j +1, row);
|
||||
|
||||
char command[10];
|
||||
char needle[2] = { 0 };
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
needle[0] = row[i];
|
||||
int index = GetCommandCode(command, sizeof(command), (const char*)needle, tm1621_kchar);
|
||||
if (-1 == index) { index = 11; }
|
||||
uint32_t bidx = (0 == j) ? i : 7 -i;
|
||||
buffer[bidx] = tm1621_digit_row[j][index];
|
||||
}
|
||||
if (dp) {
|
||||
if (0 == j) {
|
||||
buffer[2] |= 0x80; // Row 1 decimal point
|
||||
} else {
|
||||
buffer[5] |= 0x08; // Row 2 decimal point
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Tm1621.fahrenheit) { buffer[1] |= 0x80; }
|
||||
if (Tm1621.celsius) { buffer[3] |= 0x80; }
|
||||
if (Tm1621.kwh) { buffer[4] |= 0x08; }
|
||||
if (Tm1621.humidity) { buffer[6] |= 0x08; }
|
||||
if (Tm1621.voltage) { buffer[7] |= 0x08; }
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump3 %8_H"), buffer);
|
||||
|
||||
TM1621SendAddress(0x10); // Sonoff only uses the upper 16 Segments
|
||||
for (uint32_t i = 0; i < 8; i++) {
|
||||
TM1621SendCommon(buffer[i]);
|
||||
}
|
||||
TM1621StopSequence();
|
||||
}
|
||||
|
||||
void TM1621PreInit(void) {
|
||||
if (!PinUsed(GPIO_TM1621_CS) || !PinUsed(GPIO_TM1621_WR) || !PinUsed(GPIO_TM1621_RD) || !PinUsed(GPIO_TM1621_DAT)) { return; }
|
||||
|
||||
Tm1621.present = true;
|
||||
Tm1621.pin_da = Pin(GPIO_TM1621_DAT);
|
||||
Tm1621.pin_cs = Pin(GPIO_TM1621_CS);
|
||||
Tm1621.pin_rd = Pin(GPIO_TM1621_RD);
|
||||
Tm1621.pin_wr = Pin(GPIO_TM1621_WR);
|
||||
pinMode(Tm1621.pin_da, OUTPUT);
|
||||
digitalWrite(Tm1621.pin_da, 1);
|
||||
pinMode(Tm1621.pin_cs, OUTPUT);
|
||||
digitalWrite(Tm1621.pin_cs, 1);
|
||||
pinMode(Tm1621.pin_rd, OUTPUT);
|
||||
digitalWrite(Tm1621.pin_rd, 1);
|
||||
pinMode(Tm1621.pin_wr, OUTPUT);
|
||||
digitalWrite(Tm1621.pin_wr, 1);
|
||||
|
||||
Tm1621.state = 100;
|
||||
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("DSP: TM1621"));
|
||||
}
|
||||
|
||||
void TM1621Init(void) {
|
||||
digitalWrite(Tm1621.pin_cs, 0);
|
||||
delayMicroseconds(80);
|
||||
digitalWrite(Tm1621.pin_rd, 0);
|
||||
delayMicroseconds(15);
|
||||
digitalWrite(Tm1621.pin_wr, 0);
|
||||
delayMicroseconds(25);
|
||||
digitalWrite(Tm1621.pin_da, 0);
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
digitalWrite(Tm1621.pin_da, 1);
|
||||
|
||||
for (uint32_t command = 0; command < sizeof(tm1621_commands); command++) {
|
||||
TM1621SendCmnd(tm1621_commands[command]);
|
||||
}
|
||||
|
||||
TM1621SendAddress(0x00);
|
||||
for (uint32_t segment = 0; segment < 16; segment++) {
|
||||
TM1621SendCommon(0);
|
||||
}
|
||||
TM1621StopSequence();
|
||||
|
||||
snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("----"));
|
||||
snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("----"));
|
||||
TM1621SendRows();
|
||||
}
|
||||
|
||||
void TM1621Show(void) {
|
||||
static uint32_t display = 0;
|
||||
|
||||
if (0 == display) {
|
||||
Tm1621.kwh = false;
|
||||
ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &Energy.voltage[0]);
|
||||
ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &Energy.current[0]);
|
||||
Tm1621.voltage = true;
|
||||
display = 1;
|
||||
} else {
|
||||
Tm1621.voltage = false;
|
||||
ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &Energy.total[0]);
|
||||
ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &Energy.active_power[0]);
|
||||
Tm1621.kwh = true;
|
||||
display = 0;
|
||||
}
|
||||
|
||||
TM1621SendRows();
|
||||
}
|
||||
|
||||
void TM1621EverySecond(void) {
|
||||
Tm1621.state++;
|
||||
if (5 == Tm1621.state) {
|
||||
TM1621Show();
|
||||
Tm1621.state = 0;
|
||||
}
|
||||
if (102 == Tm1621.state) {
|
||||
TM1621Init();
|
||||
Tm1621.state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xdrv87(uint8_t function) {
|
||||
bool result = false;
|
||||
|
||||
if (FUNC_INIT == function) {
|
||||
TM1621PreInit();
|
||||
}
|
||||
else if (Tm1621.present) {
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
TM1621EverySecond();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_DISPLAY_TM1621_SONOFF
|
Loading…
x
Reference in New Issue
Block a user