diff --git a/.gitignore b/.gitignore index b004947390..fa4670769b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ __pycache__/ *.sublime-project *.sublime-workspace +# Intellij Idea +.idea + # Hide some OS X stuff .DS_Store .AppleDouble diff --git a/esphome/components/tm1651/__init__.py b/esphome/components/tm1651/__init__.py new file mode 100644 index 0000000000..1c49287878 --- /dev/null +++ b/esphome/components/tm1651/__init__.py @@ -0,0 +1,61 @@ +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 + +tm1651_ns = cg.esphome_ns.namespace('tm1651') +TM1651Display = tm1651_ns.class_('TM1651Display', cg.Component) +SetLevelAction = tm1651_ns.class_('SetLevelAction', automation.Action) +SetBrightnessAction = tm1651_ns.class_('SetBrightnessAction', automation.Action) +validate_level = cv.All(cv.int_range(min=0, max=100)) + +TM1651_BRIGHTNESS_OPTIONS = { + 1: TM1651Display.TM1651_BRIGHTNESS_LOW, + 2: TM1651Display.TM1651_BRIGHTNESS_MEDIUM, + 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, + cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, +}) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + + clk_pin = yield cg.gpio_pin_expression(config[CONF_CLK_PIN]) + cg.add(var.set_clk_pin(clk_pin)) + dio_pin = yield cg.gpio_pin_expression(config[CONF_DIO_PIN]) + cg.add(var.set_dio_pin(dio_pin)) + + # https://platformio.org/lib/show/6865/TM1651 + cg.add_library('6865', '1.0.0') + + +@automation.register_action('tm1651.set_level', SetLevelAction, cv.maybe_simple_value({ + cv.GenerateID(): cv.use_id(TM1651Display), + cv.Required(CONF_LEVEL): cv.templatable(validate_level), +}, key=CONF_LEVEL)) +def tm1651_set_level_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_parented(var, config[CONF_ID]) + template_ = yield cg.templatable(config[CONF_LEVEL], args, cg.uint8) + cg.add(var.set_level(template_)) + yield var + + +@automation.register_action('tm1651.set_brightness', SetBrightnessAction, cv.maybe_simple_value({ + cv.GenerateID(): cv.use_id(TM1651Display), + cv.Required(CONF_BRIGHTNESS): cv.templatable(validate_level), +}, key=CONF_BRIGHTNESS)) +def tm1651_set_brightness_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_parented(var, config[CONF_ID]) + template_ = yield cg.templatable(config[CONF_BRIGHTNESS], args, cg.uint8) + cg.add(var.set_brightness(template_)) + yield var diff --git a/esphome/components/tm1651/tm1651.cpp b/esphome/components/tm1651/tm1651.cpp new file mode 100644 index 0000000000..594ebe9db9 --- /dev/null +++ b/esphome/components/tm1651/tm1651.cpp @@ -0,0 +1,69 @@ +#include "tm1651.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace tm1651 { + +static const char *TAG = "tm1651.display"; +static const uint8_t TM1651_MAX_LEVEL = 7; +static const uint8_t MAX_INPUT_LEVEL = 100; + +static const uint8_t TM1651_BRIGHTNESS_LOW = 0; +static const uint8_t TM1651_BRIGHTNESS_MEDIUM = 2; +static const uint8_t TM1651_BRIGHTNESS_HIGH = 7; + +void TM1651Display::setup() { + ESP_LOGCONFIG(TAG, "Setting up TM1651..."); + + uint8_t clk = clk_pin_->get_pin(); + uint8_t dio = dio_pin_->get_pin(); + + battery_display_ = new TM1651(clk, dio); + battery_display_->init(); + battery_display_->clearDisplay(); +} + +void TM1651Display::dump_config() { + ESP_LOGCONFIG(TAG, "TM1651 Battery Display"); + LOG_PIN(" CLK: ", clk_pin_); + LOG_PIN(" DIO: ", dio_pin_); +} + +void TM1651Display::set_level(uint8_t new_level) { + this->level_ = calculate_level_(new_level); + this->repaint_(); +} + +void TM1651Display::set_brightness(uint8_t new_brightness) { + this->brightness_ = calculate_brightness_(new_brightness); + this->repaint_(); +} + +void TM1651Display::repaint_() { + battery_display_->set(this->brightness_); + battery_display_->displayLevel(this->level_); +} + +uint8_t TM1651Display::calculate_level_(uint8_t new_level) { + if (new_level == 0) { + return 0; + } + + float calculated_level = TM1651_MAX_LEVEL / (float) (MAX_INPUT_LEVEL / (float) new_level); + return (uint8_t) roundf(calculated_level); +} + +uint8_t TM1651Display::calculate_brightness_(uint8_t new_brightness) { + if (new_brightness <= 1) { + return TM1651_BRIGHTNESS_LOW; + } else if (new_brightness == 2) { + return TM1651_BRIGHTNESS_MEDIUM; + } else if (new_brightness >= 3) { + return TM1651_BRIGHTNESS_HIGH; + } + + return TM1651_BRIGHTNESS_LOW; +} + +} // namespace tm1651 +} // namespace esphome diff --git a/esphome/components/tm1651/tm1651.h b/esphome/components/tm1651/tm1651.h new file mode 100644 index 0000000000..d75c2adb62 --- /dev/null +++ b/esphome/components/tm1651/tm1651.h @@ -0,0 +1,56 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/esphal.h" +#include "esphome/core/automation.h" + +#include + +namespace esphome { +namespace tm1651 { + +class TM1651Display : public Component { + public: + void set_clk_pin(GPIOPin *pin) { clk_pin_ = pin; } + void set_dio_pin(GPIOPin *pin) { dio_pin_ = pin; } + + void setup() override; + void dump_config() override; + + void set_level(uint8_t); + void set_brightness(uint8_t); + + protected: + TM1651 *battery_display_; + GPIOPin *clk_pin_; + GPIOPin *dio_pin_; + + uint8_t brightness_; + uint8_t level_; + + void repaint_(); + + uint8_t calculate_level_(uint8_t); + uint8_t calculate_brightness_(uint8_t); +}; + +template class SetLevelAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(uint8_t, level) + void play(Ts... x) override { + auto level = this->level_.value(x...); + this->parent_->set_level(level); + } +}; + +template class SetBrightnessAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(uint8_t, brightness) + void play(Ts... x) override { + auto brightness = this->brightness_.value(x...); + this->parent_->set_brightness(brightness); + } +}; + +} // namespace tm1651 +} // namespace esphome diff --git a/platformio.ini b/platformio.ini index 4e1ef1d294..408e5af1ce 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,6 +18,7 @@ lib_deps = NeoPixelBus-esphome@2.5.2 ESPAsyncTCP-esphome@1.2.2 1655@1.0.2 ; TinyGPSPlus (has name conflict) + 6865@1.0.0 ; TM1651 Battery Display build_flags = -Wno-reorder -DUSE_WEB_SERVER diff --git a/tests/test1.yaml b/tests/test1.yaml index f4d6e5dcfe..192d7a2fc9 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1454,6 +1454,11 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); +tm1651: + id: tm1651_battery + clk_pin: GPIO23 + dio_pin: GPIO23 + remote_receiver: pin: GPIO32 dump: all diff --git a/tests/test3.yaml b/tests/test3.yaml index bbfcc72880..2703621752 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -138,6 +138,21 @@ api: then: - dfplayer.random + - service: battery_level + variables: + level: int + then: + - tm1651.set_level: + id: tm1651_battery + level: !lambda 'return level;' + - service: battery_brightness + variables: + brightness: int + then: + - tm1651.set_brightness: + id: tm1651_battery + brightness: !lambda 'return brightness;' + wifi: ssid: 'MySSID' password: 'password1' @@ -651,3 +666,8 @@ dfplayer: dfplayer.is_playing then: logger.log: 'Playback finished event' + +tm1651: + id: tm1651_battery + clk_pin: D6 + dio_pin: D5