mirror of
https://github.com/esphome/esphome.git
synced 2024-11-09 16:57:47 +01:00
Add support to tm1621 display (#3737)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
635851807a
commit
f77118a90c
7 changed files with 421 additions and 0 deletions
|
@ -225,6 +225,7 @@ esphome/components/teleinfo/* @0hax
|
|||
esphome/components/thermostat/* @kbx81
|
||||
esphome/components/time/* @OttoWinter
|
||||
esphome/components/tlc5947/* @rnauber
|
||||
esphome/components/tm1621/* @Philippe12
|
||||
esphome/components/tm1637/* @glmnet
|
||||
esphome/components/tmp102/* @timsavage
|
||||
esphome/components/tmp117/* @Azimath
|
||||
|
|
1
esphome/components/tm1621/__init__.py
Normal file
1
esphome/components/tm1621/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
CODEOWNERS = ["@Philippe12"]
|
47
esphome/components/tm1621/display.py
Normal file
47
esphome/components/tm1621/display.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import display
|
||||
from esphome.const import (
|
||||
CONF_DATA_PIN,
|
||||
CONF_CS_PIN,
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_READ_PIN,
|
||||
CONF_WRITE_PIN,
|
||||
)
|
||||
|
||||
tm1621_ns = cg.esphome_ns.namespace("tm1621")
|
||||
TM1621Display = tm1621_ns.class_("TM1621Display", cg.PollingComponent)
|
||||
TM1621DisplayRef = TM1621Display.operator("ref")
|
||||
|
||||
CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TM1621Display),
|
||||
cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_READ_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_WRITE_PIN): pins.gpio_output_pin_schema,
|
||||
}
|
||||
).extend(cv.polling_component_schema("1s"))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await display.register_display(var, config)
|
||||
|
||||
cs = await cg.gpio_pin_expression(config[CONF_CS_PIN])
|
||||
cg.add(var.set_cs_pin(cs))
|
||||
data = await cg.gpio_pin_expression(config[CONF_DATA_PIN])
|
||||
cg.add(var.set_data_pin(data))
|
||||
read = await cg.gpio_pin_expression(config[CONF_READ_PIN])
|
||||
cg.add(var.set_read_pin(read))
|
||||
write = await cg.gpio_pin_expression(config[CONF_WRITE_PIN])
|
||||
cg.add(var.set_write_pin(write))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(TM1621DisplayRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
283
esphome/components/tm1621/tm1621.cpp
Normal file
283
esphome/components/tm1621/tm1621.cpp
Normal file
|
@ -0,0 +1,283 @@
|
|||
#include "tm1621.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tm1621 {
|
||||
|
||||
static const char *const TAG = "tm1621";
|
||||
|
||||
const uint8_t TM1621_PULSE_WIDTH = 10; // microseconds (Sonoff = 100)
|
||||
|
||||
const uint8_t TM1621_SYS_EN = 0x01; // 0b00000001
|
||||
const uint8_t TM1621_LCD_ON = 0x03; // 0b00000011
|
||||
const uint8_t TM1621_TIMER_DIS = 0x04; // 0b00000100
|
||||
const uint8_t TM1621_WDT_DIS = 0x05; // 0b00000101
|
||||
const uint8_t TM1621_TONE_OFF = 0x08; // 0b00001000
|
||||
const uint8_t TM1621_BIAS = 0x29; // 0b00101001 = LCD 1/3 bias 4 commons option
|
||||
const uint8_t TM1621_IRQ_DIS = 0x80; // 0b100x0xxx
|
||||
|
||||
enum Tm1621Device { TM1621_USER, TM1621_POWR316D, TM1621_THR316D };
|
||||
|
||||
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}};
|
||||
|
||||
void TM1621Display::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up TM1621...");
|
||||
|
||||
this->cs_pin_->setup(); // OUTPUT
|
||||
this->cs_pin_->digital_write(true);
|
||||
this->data_pin_->setup(); // OUTPUT
|
||||
this->data_pin_->digital_write(true);
|
||||
this->read_pin_->setup(); // OUTPUT
|
||||
this->read_pin_->digital_write(true);
|
||||
this->write_pin_->setup(); // OUTPUT
|
||||
this->write_pin_->digital_write(true);
|
||||
|
||||
this->state_ = 100;
|
||||
|
||||
this->cs_pin_->digital_write(false);
|
||||
delayMicroseconds(80);
|
||||
this->read_pin_->digital_write(false);
|
||||
delayMicroseconds(15);
|
||||
this->write_pin_->digital_write(false);
|
||||
delayMicroseconds(25);
|
||||
this->data_pin_->digital_write(false);
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
this->data_pin_->digital_write(true);
|
||||
|
||||
for (uint8_t tm1621_command : TM1621_COMMANDS) {
|
||||
this->send_command_(tm1621_command);
|
||||
}
|
||||
|
||||
this->send_address_(0x00);
|
||||
for (uint32_t segment = 0; segment < 16; segment++) {
|
||||
this->send_common_(0);
|
||||
}
|
||||
this->stop_();
|
||||
|
||||
snprintf(this->row_[0], sizeof(this->row_[0]), "----");
|
||||
snprintf(this->row_[1], sizeof(this->row_[1]), "----");
|
||||
|
||||
this->display();
|
||||
}
|
||||
void TM1621Display::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "TM1621:");
|
||||
LOG_PIN(" CS Pin: ", this->cs_pin_);
|
||||
LOG_PIN(" DATA Pin: ", this->data_pin_);
|
||||
LOG_PIN(" READ Pin: ", this->read_pin_);
|
||||
LOG_PIN(" WRITE Pin: ", this->write_pin_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void TM1621Display::update() {
|
||||
// memset(this->row, 0, sizeof(this->row));
|
||||
if (this->writer_.has_value())
|
||||
(*this->writer_)(*this);
|
||||
this->display();
|
||||
}
|
||||
|
||||
float TM1621Display::get_setup_priority() const { return setup_priority::PROCESSOR; }
|
||||
void TM1621Display::bit_delay_() { delayMicroseconds(100); }
|
||||
|
||||
void TM1621Display::stop_() {
|
||||
this->cs_pin_->digital_write(true); // Stop command sequence
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH / 2);
|
||||
this->data_pin_->digital_write(true); // Reset data
|
||||
}
|
||||
|
||||
void TM1621Display::display() {
|
||||
// Tm1621.row[x] = "text", "----", " " or a number with one decimal like "0.4", "237.5", "123456.7"
|
||||
// "123456.7" will be shown as "9999" being a four digit overflow
|
||||
|
||||
// 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(this->row_[j]);
|
||||
char *dp = nullptr; // Expect number larger than "123"
|
||||
int row_idx = len - 3; // "1234.5"
|
||||
if (len <= 5) { // "----", " ", "0.4", "237.5"
|
||||
dp = strchr(this->row_[j], '.');
|
||||
row_idx = len - 1;
|
||||
} else if (len > 6) { // "12345.6"
|
||||
snprintf(this->row_[j], sizeof(this->row_[j]), "9999");
|
||||
row_idx = 3;
|
||||
}
|
||||
row[3] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' ';
|
||||
if ((row_idx >= 0) && dp) {
|
||||
row_idx--;
|
||||
}
|
||||
row[2] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' ';
|
||||
row[1] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' ';
|
||||
row[0] = (row_idx >= 0) ? this->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 = this->get_command_code_(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 (this->fahrenheit_) {
|
||||
buffer[1] |= 0x80;
|
||||
}
|
||||
if (this->celsius_) {
|
||||
buffer[3] |= 0x80;
|
||||
}
|
||||
if (this->kwh_) {
|
||||
buffer[4] |= 0x08;
|
||||
}
|
||||
if (this->humidity_) {
|
||||
buffer[6] |= 0x08;
|
||||
}
|
||||
if (this->voltage_) {
|
||||
buffer[7] |= 0x08;
|
||||
}
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump3 %8_H"), buffer);
|
||||
|
||||
this->send_address_(0x10); // Sonoff only uses the upper 16 Segments
|
||||
for (uint8_t i : buffer) {
|
||||
this->send_common_(i);
|
||||
}
|
||||
this->stop_();
|
||||
}
|
||||
|
||||
bool TM1621Display::send_command_(uint16_t command) {
|
||||
uint16_t full_command = (0x0400 | command) << 5; // 0b100cccccccc00000
|
||||
this->cs_pin_->digital_write(false); // Start command sequence
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH / 2);
|
||||
for (uint32_t i = 0; i < 12; i++) {
|
||||
this->write_pin_->digital_write(false); // Start write sequence
|
||||
if (full_command & 0x8000) {
|
||||
this->data_pin_->digital_write(true); // Set data
|
||||
} else {
|
||||
this->data_pin_->digital_write(false); // Set data
|
||||
}
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
this->write_pin_->digital_write(true); // Read data
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
full_command <<= 1;
|
||||
}
|
||||
this->stop_();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TM1621Display::send_common_(uint8_t common) {
|
||||
for (uint32_t i = 0; i < 8; i++) {
|
||||
this->write_pin_->digital_write(false); // Start write sequence
|
||||
if (common & 1) {
|
||||
this->data_pin_->digital_write(true); // Set data
|
||||
} else {
|
||||
this->data_pin_->digital_write(false); // Set data
|
||||
}
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
this->write_pin_->digital_write(true); // Read data
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
common >>= 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TM1621Display::send_address_(uint16_t address) {
|
||||
uint16_t full_address = (address | 0x0140) << 7; // 0b101aaaaaa0000000
|
||||
this->cs_pin_->digital_write(false); // Start command sequence
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH / 2);
|
||||
for (uint32_t i = 0; i < 9; i++) {
|
||||
this->write_pin_->digital_write(false); // Start write sequence
|
||||
if (full_address & 0x8000) {
|
||||
this->data_pin_->digital_write(true); // Set data
|
||||
} else {
|
||||
this->data_pin_->digital_write(false); // Set data
|
||||
}
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
this->write_pin_->digital_write(true); // Read data
|
||||
delayMicroseconds(TM1621_PULSE_WIDTH);
|
||||
full_address <<= 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t TM1621Display::print(uint8_t start_pos, const char *str) {
|
||||
// ESP_LOGD(TAG, "Print at %d: %s", start_pos, str);
|
||||
return snprintf(this->row_[start_pos], sizeof(this->row_[start_pos]), "%s", str);
|
||||
}
|
||||
uint8_t TM1621Display::print(const char *str) { return this->print(0, str); }
|
||||
uint8_t TM1621Display::printf(uint8_t pos, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
char buffer[64];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
||||
va_end(arg);
|
||||
if (ret > 0)
|
||||
return this->print(pos, buffer);
|
||||
return 0;
|
||||
}
|
||||
uint8_t TM1621Display::printf(const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
char buffer[64];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
||||
va_end(arg);
|
||||
if (ret > 0)
|
||||
return this->print(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TM1621Display::get_command_code_(char *destination, size_t destination_size, const char *needle,
|
||||
const char *haystack) {
|
||||
// Returns -1 of not found
|
||||
// Returns index and command if found
|
||||
int result = -1;
|
||||
const char *read = haystack;
|
||||
char *write = destination;
|
||||
|
||||
while (true) {
|
||||
result++;
|
||||
size_t size = destination_size - 1;
|
||||
write = destination;
|
||||
char ch = '.';
|
||||
while ((ch != '\0') && (ch != '|')) {
|
||||
ch = *(read++);
|
||||
if (size && (ch != '|')) {
|
||||
*write++ = ch;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
*write = '\0';
|
||||
if (!strcasecmp(needle, destination)) {
|
||||
break;
|
||||
}
|
||||
if (0 == ch) {
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace tm1621
|
||||
} // namespace esphome
|
74
esphome/components/tm1621/tm1621.h
Normal file
74
esphome/components/tm1621/tm1621.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tm1621 {
|
||||
|
||||
class TM1621Display;
|
||||
|
||||
using tm1621_writer_t = std::function<void(TM1621Display &)>;
|
||||
|
||||
class TM1621Display : public PollingComponent {
|
||||
public:
|
||||
void set_writer(tm1621_writer_t &&writer) { this->writer_ = writer; }
|
||||
|
||||
void setup() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void set_cs_pin(GPIOPin *pin) { cs_pin_ = pin; }
|
||||
void set_data_pin(GPIOPin *pin) { data_pin_ = pin; }
|
||||
void set_read_pin(GPIOPin *pin) { read_pin_ = pin; }
|
||||
void set_write_pin(GPIOPin *pin) { write_pin_ = pin; }
|
||||
|
||||
void display_celsius(bool d) { celsius_ = d; }
|
||||
void display_fahrenheit(bool d) { fahrenheit_ = d; }
|
||||
void display_humidity(bool d) { humidity_ = d; }
|
||||
void display_voltage(bool d) { voltage_ = d; }
|
||||
void display_kwh(bool d) { kwh_ = d; }
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
void update() override;
|
||||
|
||||
/// Evaluate the printf-format and print the result at the given position.
|
||||
uint8_t printf(uint8_t pos, const char *format, ...) __attribute__((format(printf, 3, 4)));
|
||||
/// Evaluate the printf-format and print the result at position 0.
|
||||
uint8_t printf(const char *format, ...) __attribute__((format(printf, 2, 3)));
|
||||
|
||||
/// Print `str` at the given position.
|
||||
uint8_t print(uint8_t pos, const char *str);
|
||||
/// Print `str` at position 0.
|
||||
uint8_t print(const char *str);
|
||||
|
||||
void display();
|
||||
|
||||
protected:
|
||||
void bit_delay_();
|
||||
void setup_pins_();
|
||||
bool send_command_(uint16_t command);
|
||||
bool send_common_(uint8_t common);
|
||||
bool send_address_(uint16_t address);
|
||||
void stop_();
|
||||
int get_command_code_(char *destination, size_t destination_size, const char *needle, const char *haystack);
|
||||
|
||||
GPIOPin *data_pin_;
|
||||
GPIOPin *cs_pin_;
|
||||
GPIOPin *read_pin_;
|
||||
GPIOPin *write_pin_;
|
||||
optional<tm1621_writer_t> writer_{};
|
||||
char row_[2][12];
|
||||
uint8_t state_;
|
||||
uint8_t device_;
|
||||
bool celsius_;
|
||||
bool fahrenheit_;
|
||||
bool humidity_;
|
||||
bool voltage_;
|
||||
bool kwh_;
|
||||
};
|
||||
|
||||
} // namespace tm1621
|
||||
} // namespace esphome
|
|
@ -558,6 +558,7 @@ CONF_RAW_DATA_ID = "raw_data_id"
|
|||
CONF_RC_CODE_1 = "rc_code_1"
|
||||
CONF_RC_CODE_2 = "rc_code_2"
|
||||
CONF_REACTIVE_POWER = "reactive_power"
|
||||
CONF_READ_PIN = "read_pin"
|
||||
CONF_REBOOT_TIMEOUT = "reboot_timeout"
|
||||
CONF_RECEIVE_TIMEOUT = "receive_timeout"
|
||||
CONF_RED = "red"
|
||||
|
@ -769,6 +770,7 @@ CONF_WILL_MESSAGE = "will_message"
|
|||
CONF_WIND_DIRECTION_DEGREES = "wind_direction_degrees"
|
||||
CONF_WIND_SPEED = "wind_speed"
|
||||
CONF_WINDOW_SIZE = "window_size"
|
||||
CONF_WRITE_PIN = "write_pin"
|
||||
CONF_X_GRID = "x_grid"
|
||||
CONF_Y_GRID = "y_grid"
|
||||
CONF_ZERO = "zero"
|
||||
|
|
|
@ -547,8 +547,10 @@ sensor:
|
|||
- platform: dht
|
||||
pin: GPIO26
|
||||
temperature:
|
||||
id: dht_temperature
|
||||
name: Living Room Temperature 3
|
||||
humidity:
|
||||
id: dht_humidity
|
||||
name: Living Room Humidity 3
|
||||
model: AM2302
|
||||
update_interval: 15s
|
||||
|
@ -2514,6 +2516,17 @@ display:
|
|||
it.print_sad(true);
|
||||
it.print_bracket(true);
|
||||
it.print_battery(true);
|
||||
- platform: tm1621
|
||||
id: tm1621_display
|
||||
cs_pin: GPIO17
|
||||
data_pin: GPIO5
|
||||
read_pin: GPIO23
|
||||
write_pin: GPIO18
|
||||
lambda: |-
|
||||
it.printf(0, "%.1f", id(dht_temperature).state);
|
||||
it.display_celsius(true);
|
||||
it.printf(1, "%.1f", id(dht_humidity).state);
|
||||
it.display_humidity(true);
|
||||
|
||||
tm1651:
|
||||
id: tm1651_battery
|
||||
|
|
Loading…
Reference in a new issue