mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
parent
6a60f01753
commit
990a4d4774
6 changed files with 412 additions and 3 deletions
35
esphome/components/tm1637/display.py
Normal file
35
esphome/components/tm1637/display.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
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_CLK_PIN, CONF_DIO_PIN, CONF_ID, CONF_LAMBDA, CONF_INTENSITY
|
||||
|
||||
tm1637_ns = cg.esphome_ns.namespace('tm1637')
|
||||
TM1637Display = tm1637_ns.class_('TM1637Display', cg.PollingComponent)
|
||||
TM1637DisplayRef = TM1637Display.operator('ref')
|
||||
|
||||
CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(TM1637Display),
|
||||
|
||||
cv.Optional(CONF_INTENSITY, default=7): cv.All(cv.uint8_t, cv.Range(min=0, max=7)),
|
||||
cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema,
|
||||
}).extend(cv.polling_component_schema('1s'))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield display.register_display(var, config)
|
||||
|
||||
clk = yield cg.gpio_pin_expression(config[CONF_CLK_PIN])
|
||||
cg.add(var.set_clk_pin(clk))
|
||||
dio = yield cg.gpio_pin_expression(config[CONF_DIO_PIN])
|
||||
cg.add(var.set_dio_pin(dio))
|
||||
|
||||
cg.add(var.set_intensity(config[CONF_INTENSITY]))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(TM1637DisplayRef, 'it')],
|
||||
return_type=cg.void)
|
||||
cg.add(var.set_writer(lambda_))
|
299
esphome/components/tm1637/tm1637.cpp
Normal file
299
esphome/components/tm1637/tm1637.cpp
Normal file
|
@ -0,0 +1,299 @@
|
|||
#include "tm1637.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tm1637 {
|
||||
|
||||
const char* TAG = "display.tm1637";
|
||||
const uint8_t TM1637_I2C_COMM1 = 0x40;
|
||||
const uint8_t TM1637_I2C_COMM2 = 0xC0;
|
||||
const uint8_t TM1637_I2C_COMM3 = 0x80;
|
||||
const uint8_t TM1637_UNKNOWN_CHAR = 0b11111111;
|
||||
|
||||
//
|
||||
// A
|
||||
// ---
|
||||
// F | | B
|
||||
// -G-
|
||||
// E | | C
|
||||
// ---
|
||||
// D X
|
||||
// XABCDEFG
|
||||
const uint8_t TM1637_ASCII_TO_RAW[94] PROGMEM = {
|
||||
0b00000000, // ' ', ord 0x20
|
||||
0b10110000, // '!', ord 0x21
|
||||
0b00100010, // '"', ord 0x22
|
||||
TM1637_UNKNOWN_CHAR, // '#', ord 0x23
|
||||
TM1637_UNKNOWN_CHAR, // '$', ord 0x24
|
||||
0b01001001, // '%', ord 0x25
|
||||
TM1637_UNKNOWN_CHAR, // '&', ord 0x26
|
||||
0b00000010, // ''', ord 0x27
|
||||
0b01001110, // '(', ord 0x28
|
||||
0b01111000, // ')', ord 0x29
|
||||
0b01000000, // '*', ord 0x2A
|
||||
TM1637_UNKNOWN_CHAR, // '+', ord 0x2B
|
||||
0b00010000, // ',', ord 0x2C
|
||||
0b00000001, // '-', ord 0x2D
|
||||
0b10000000, // '.', ord 0x2E
|
||||
TM1637_UNKNOWN_CHAR, // '/', ord 0x2F
|
||||
0b01111110, // '0', ord 0x30
|
||||
0b00110000, // '1', ord 0x31
|
||||
0b01101101, // '2', ord 0x32
|
||||
0b01111001, // '3', ord 0x33
|
||||
0b00110011, // '4', ord 0x34
|
||||
0b01011011, // '5', ord 0x35
|
||||
0b01011111, // '6', ord 0x36
|
||||
0b01110000, // '7', ord 0x37
|
||||
0b01111111, // '8', ord 0x38
|
||||
0b01110011, // '9', ord 0x39
|
||||
0b01001000, // ':', ord 0x3A
|
||||
0b01011000, // ';', ord 0x3B
|
||||
TM1637_UNKNOWN_CHAR, // '<', ord 0x3C
|
||||
TM1637_UNKNOWN_CHAR, // '=', ord 0x3D
|
||||
TM1637_UNKNOWN_CHAR, // '>', ord 0x3E
|
||||
0b01100101, // '?', ord 0x3F
|
||||
0b01101111, // '@', ord 0x40
|
||||
0b01110111, // 'A', ord 0x41
|
||||
0b00011111, // 'B', ord 0x42
|
||||
0b01001110, // 'C', ord 0x43
|
||||
0b00111101, // 'D', ord 0x44
|
||||
0b01001111, // 'E', ord 0x45
|
||||
0b01000111, // 'F', ord 0x46
|
||||
0b01011110, // 'G', ord 0x47
|
||||
0b00110111, // 'H', ord 0x48
|
||||
0b00110000, // 'I', ord 0x49
|
||||
0b00111100, // 'J', ord 0x4A
|
||||
TM1637_UNKNOWN_CHAR, // 'K', ord 0x4B
|
||||
0b00001110, // 'L', ord 0x4C
|
||||
TM1637_UNKNOWN_CHAR, // 'M', ord 0x4D
|
||||
0b00010101, // 'N', ord 0x4E
|
||||
0b01111110, // 'O', ord 0x4F
|
||||
0b01100111, // 'P', ord 0x50
|
||||
0b11111110, // 'Q', ord 0x51
|
||||
0b00000101, // 'R', ord 0x52
|
||||
0b01011011, // 'S', ord 0x53
|
||||
0b00000111, // 'T', ord 0x54
|
||||
0b00111110, // 'U', ord 0x55
|
||||
0b00111110, // 'V', ord 0x56
|
||||
0b00111111, // 'W', ord 0x57
|
||||
TM1637_UNKNOWN_CHAR, // 'X', ord 0x58
|
||||
0b00100111, // 'Y', ord 0x59
|
||||
0b01101101, // 'Z', ord 0x5A
|
||||
0b01001110, // '[', ord 0x5B
|
||||
TM1637_UNKNOWN_CHAR, // '\', ord 0x5C
|
||||
0b01111000, // ']', ord 0x5D
|
||||
TM1637_UNKNOWN_CHAR, // '^', ord 0x5E
|
||||
0b00001000, // '_', ord 0x5F
|
||||
0b00100000, // '`', ord 0x60
|
||||
0b01110111, // 'a', ord 0x61
|
||||
0b00011111, // 'b', ord 0x62
|
||||
0b00001101, // 'c', ord 0x63
|
||||
0b00111101, // 'd', ord 0x64
|
||||
0b01001111, // 'e', ord 0x65
|
||||
0b01000111, // 'f', ord 0x66
|
||||
0b01011110, // 'g', ord 0x67
|
||||
0b00010111, // 'h', ord 0x68
|
||||
0b00010000, // 'i', ord 0x69
|
||||
0b00111100, // 'j', ord 0x6A
|
||||
TM1637_UNKNOWN_CHAR, // 'k', ord 0x6B
|
||||
0b00001110, // 'l', ord 0x6C
|
||||
TM1637_UNKNOWN_CHAR, // 'm', ord 0x6D
|
||||
0b00010101, // 'n', ord 0x6E
|
||||
0b00011101, // 'o', ord 0x6F
|
||||
0b01100111, // 'p', ord 0x70
|
||||
TM1637_UNKNOWN_CHAR, // 'q', ord 0x71
|
||||
0b00000101, // 'r', ord 0x72
|
||||
0b01011011, // 's', ord 0x73
|
||||
0b00000111, // 't', ord 0x74
|
||||
0b00011100, // 'u', ord 0x75
|
||||
0b00011100, // 'v', ord 0x76
|
||||
TM1637_UNKNOWN_CHAR, // 'w', ord 0x77
|
||||
TM1637_UNKNOWN_CHAR, // 'x', ord 0x78
|
||||
0b00100111, // 'y', ord 0x79
|
||||
TM1637_UNKNOWN_CHAR, // 'z', ord 0x7A
|
||||
0b00110001, // '{', ord 0x7B
|
||||
0b00000110, // '|', ord 0x7C
|
||||
0b00000111, // '}', ord 0x7D
|
||||
};
|
||||
void TM1637Display::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up TM1637...");
|
||||
|
||||
this->clk_pin_->setup(); // OUTPUT
|
||||
this->clk_pin_->digital_write(false); // LOW
|
||||
this->dio_pin_->setup(); // OUTPUT
|
||||
this->dio_pin_->digital_write(false); // LOW
|
||||
|
||||
this->display();
|
||||
}
|
||||
void TM1637Display::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "TM1637:");
|
||||
ESP_LOGCONFIG(TAG, " INTENSITY: %d", this->intensity_);
|
||||
LOG_PIN(" CLK Pin: ", this->clk_pin_);
|
||||
LOG_PIN(" DIO Pin: ", this->dio_pin_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void TM1637Display::update() {
|
||||
for (uint8_t& i : this->buffer_)
|
||||
i = 0;
|
||||
if (this->writer_.has_value())
|
||||
(*this->writer_)(*this);
|
||||
this->display();
|
||||
}
|
||||
|
||||
float TM1637Display::get_setup_priority() const { return setup_priority::PROCESSOR; }
|
||||
void TM1637Display::bit_delay_() { delayMicroseconds(100); }
|
||||
void TM1637Display::start_() {
|
||||
this->dio_pin_->pin_mode(OUTPUT);
|
||||
this->bit_delay_();
|
||||
}
|
||||
|
||||
void TM1637Display::stop_() {
|
||||
this->dio_pin_->pin_mode(OUTPUT);
|
||||
bit_delay_();
|
||||
this->clk_pin_->pin_mode(INPUT);
|
||||
bit_delay_();
|
||||
this->dio_pin_->pin_mode(INPUT);
|
||||
bit_delay_();
|
||||
}
|
||||
|
||||
void TM1637Display::display() {
|
||||
ESP_LOGVV(TAG, "Display %02X%02X%02X%02X", buffer_[0], buffer_[1], buffer_[2], buffer_[3]);
|
||||
|
||||
// Write COMM1
|
||||
this->start_();
|
||||
this->send_byte_(TM1637_I2C_COMM1);
|
||||
this->stop_();
|
||||
|
||||
// Write COMM2 + first digit address
|
||||
this->start_();
|
||||
this->send_byte_(TM1637_I2C_COMM2);
|
||||
|
||||
// Write the data bytes
|
||||
for (auto b : this->buffer_) {
|
||||
this->send_byte_(b);
|
||||
}
|
||||
|
||||
this->stop_();
|
||||
|
||||
// Write COMM3 + brightness
|
||||
this->start_();
|
||||
this->send_byte_(TM1637_I2C_COMM3 + ((this->intensity_ & 0x7) | 0x08));
|
||||
this->stop_();
|
||||
}
|
||||
bool TM1637Display::send_byte_(uint8_t b) {
|
||||
uint8_t data = b;
|
||||
|
||||
// 8 Data Bits
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
// CLK low
|
||||
this->clk_pin_->pin_mode(OUTPUT);
|
||||
this->bit_delay_();
|
||||
|
||||
// Set data bit
|
||||
if (data & 0x01)
|
||||
this->dio_pin_->pin_mode(INPUT);
|
||||
else
|
||||
this->dio_pin_->pin_mode(OUTPUT);
|
||||
|
||||
this->bit_delay_();
|
||||
|
||||
// CLK high
|
||||
this->clk_pin_->pin_mode(INPUT);
|
||||
this->bit_delay_();
|
||||
data = data >> 1;
|
||||
}
|
||||
|
||||
// Wait for acknowledge
|
||||
// CLK to zero
|
||||
this->clk_pin_->pin_mode(OUTPUT);
|
||||
this->dio_pin_->pin_mode(INPUT);
|
||||
this->bit_delay_();
|
||||
|
||||
// CLK to high
|
||||
this->clk_pin_->pin_mode(INPUT);
|
||||
this->bit_delay_();
|
||||
uint8_t ack = this->dio_pin_->digital_read();
|
||||
if (ack == 0) {
|
||||
this->dio_pin_->pin_mode(OUTPUT);
|
||||
}
|
||||
|
||||
this->bit_delay_();
|
||||
this->clk_pin_->pin_mode(OUTPUT);
|
||||
this->bit_delay_();
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
uint8_t TM1637Display::print(uint8_t start_pos, const char* str) {
|
||||
ESP_LOGV(TAG, "Print at %d: %s", start_pos, str);
|
||||
uint8_t pos = start_pos;
|
||||
for (; *str != '\0'; str++) {
|
||||
uint8_t data = TM1637_UNKNOWN_CHAR;
|
||||
if (*str >= ' ' && *str <= '}')
|
||||
data = pgm_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']);
|
||||
|
||||
if (data == TM1637_UNKNOWN_CHAR) {
|
||||
ESP_LOGW(TAG, "Encountered character '%c' with no TM1637 representation while translating string!", *str);
|
||||
}
|
||||
// Remap segments, for compatibility with MAX7219 segment definition which is
|
||||
// XABCDEFG, but TM1637 is // XGFEDCBA
|
||||
data = ((data & 0x80) ? 0x80 : 0) | // no move X
|
||||
((data & 0x40) ? 0x1 : 0) | // A
|
||||
((data & 0x20) ? 0x2 : 0) | // B
|
||||
((data & 0x10) ? 0x4 : 0) | // C
|
||||
((data & 0x8) ? 0x8 : 0) | // D
|
||||
((data & 0x4) ? 0x10 : 0) | // E
|
||||
((data & 0x2) ? 0x20 : 0) | // F
|
||||
((data & 0x1) ? 0x40 : 0); // G
|
||||
if (*str == '.') {
|
||||
if (pos != start_pos)
|
||||
pos--;
|
||||
this->buffer_[pos] |= 0b10000000;
|
||||
} else {
|
||||
if (pos >= 4) {
|
||||
ESP_LOGE(TAG, "String is too long for the display!");
|
||||
break;
|
||||
}
|
||||
this->buffer_[pos] = data;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return pos - start_pos;
|
||||
}
|
||||
uint8_t TM1637Display::print(const char* str) { return this->print(0, str); }
|
||||
uint8_t TM1637Display::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 TM1637Display::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;
|
||||
}
|
||||
|
||||
#ifdef USE_TIME
|
||||
uint8_t TM1637Display::strftime(uint8_t pos, const char* format, time::ESPTime time) {
|
||||
char buffer[64];
|
||||
size_t ret = time.strftime(buffer, sizeof(buffer), format);
|
||||
if (ret > 0)
|
||||
return this->print(pos, buffer);
|
||||
return 0;
|
||||
}
|
||||
uint8_t TM1637Display::strftime(const char* format, time::ESPTime time) { return this->strftime(0, format, time); }
|
||||
#endif
|
||||
|
||||
} // namespace tm1637
|
||||
} // namespace esphome
|
70
esphome/components/tm1637/tm1637.h
Normal file
70
esphome/components/tm1637/tm1637.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
|
||||
#ifdef USE_TIME
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace tm1637 {
|
||||
|
||||
class TM1637Display;
|
||||
|
||||
using tm1637_writer_t = std::function<void(TM1637Display &)>;
|
||||
|
||||
class TM1637Display : public PollingComponent {
|
||||
public:
|
||||
void set_writer(tm1637_writer_t &&writer) { this->writer_ = writer; }
|
||||
|
||||
void setup() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void set_clk_pin(GPIOPin *pin) { clk_pin_ = pin; }
|
||||
void set_dio_pin(GPIOPin *pin) { dio_pin_ = pin; }
|
||||
|
||||
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 set_intensity(uint8_t intensity) { this->intensity_ = intensity; }
|
||||
|
||||
void display();
|
||||
|
||||
#ifdef USE_TIME
|
||||
/// Evaluate the strftime-format and print the result at the given position.
|
||||
uint8_t strftime(uint8_t pos, const char *format, time::ESPTime time) __attribute__((format(strftime, 3, 0)));
|
||||
|
||||
/// Evaluate the strftime-format and print the result at position 0.
|
||||
uint8_t strftime(const char *format, time::ESPTime time) __attribute__((format(strftime, 2, 0)));
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void bit_delay_();
|
||||
void setup_pins_();
|
||||
bool send_byte_(uint8_t b);
|
||||
void start_();
|
||||
void stop_();
|
||||
|
||||
GPIOPin *dio_pin_;
|
||||
GPIOPin *clk_pin_;
|
||||
uint8_t intensity_;
|
||||
optional<tm1637_writer_t> writer_{};
|
||||
uint8_t buffer_[4] = {0};
|
||||
};
|
||||
|
||||
} // namespace tm1637
|
||||
} // namespace esphome
|
|
@ -1,7 +1,7 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins, automation
|
||||
from esphome.const import CONF_ID, CONF_CLK_PIN, CONF_LEVEL, CONF_BRIGHTNESS
|
||||
from esphome.const import CONF_ID, CONF_CLK_PIN, CONF_DIO_PIN, CONF_LEVEL, CONF_BRIGHTNESS
|
||||
|
||||
tm1651_ns = cg.esphome_ns.namespace('tm1651')
|
||||
TM1651Display = tm1651_ns.class_('TM1651Display', cg.Component)
|
||||
|
@ -15,8 +15,6 @@ TM1651_BRIGHTNESS_OPTIONS = {
|
|||
3: TM1651Display.TM1651_BRIGHTNESS_HIGH
|
||||
}
|
||||
|
||||
CONF_DIO_PIN = 'dio_pin'
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(TM1651Display),
|
||||
cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema,
|
||||
|
|
|
@ -125,6 +125,7 @@ CONF_DELTA = 'delta'
|
|||
CONF_DEVICE = 'device'
|
||||
CONF_DEVICE_CLASS = 'device_class'
|
||||
CONF_DIMENSIONS = 'dimensions'
|
||||
CONF_DIO_PIN = 'dio_pin'
|
||||
CONF_DIR_PIN = 'dir_pin'
|
||||
CONF_DIRECTION = 'direction'
|
||||
CONF_DISCOVERY = 'discovery'
|
||||
|
|
|
@ -1426,6 +1426,12 @@ display:
|
|||
num_chips: 1
|
||||
lambda: |-
|
||||
it.print("01234567");
|
||||
- platform: tm1637
|
||||
clk_pin: GPIO23
|
||||
dio_pin: GPIO25
|
||||
intensity: 3
|
||||
lambda: |-
|
||||
it.print("1234");
|
||||
- platform: nextion
|
||||
lambda: |-
|
||||
it.set_component_value("gauge", 50);
|
||||
|
|
Loading…
Reference in a new issue