mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	[tm1651] Remove dependency on Arduino Library (#9645)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
		| @@ -472,7 +472,7 @@ esphome/components/tlc5971/* @IJIJI | |||||||
| esphome/components/tm1621/* @Philippe12 | esphome/components/tm1621/* @Philippe12 | ||||||
| esphome/components/tm1637/* @glmnet | esphome/components/tm1637/* @glmnet | ||||||
| esphome/components/tm1638/* @skykingjwc | esphome/components/tm1638/* @skykingjwc | ||||||
| esphome/components/tm1651/* @freekode | esphome/components/tm1651/* @mrtoy-me | ||||||
| esphome/components/tmp102/* @timsavage | esphome/components/tmp102/* @timsavage | ||||||
| esphome/components/tmp1075/* @sybrenstuvel | esphome/components/tmp1075/* @sybrenstuvel | ||||||
| esphome/components/tmp117/* @Azimath | esphome/components/tmp117/* @Azimath | ||||||
|   | |||||||
| @@ -10,26 +10,28 @@ from esphome.const import ( | |||||||
|     CONF_LEVEL, |     CONF_LEVEL, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| CODEOWNERS = ["@freekode"] | CODEOWNERS = ["@mrtoy-me"] | ||||||
|  |  | ||||||
|  | CONF_LEVEL_PERCENT = "level_percent" | ||||||
|  |  | ||||||
| tm1651_ns = cg.esphome_ns.namespace("tm1651") | tm1651_ns = cg.esphome_ns.namespace("tm1651") | ||||||
| TM1651Brightness = tm1651_ns.enum("TM1651Brightness") | TM1651Brightness = tm1651_ns.enum("TM1651Brightness") | ||||||
| TM1651Display = tm1651_ns.class_("TM1651Display", cg.Component) | TM1651Display = tm1651_ns.class_("TM1651Display", cg.Component) | ||||||
|  |  | ||||||
| SetLevelPercentAction = tm1651_ns.class_("SetLevelPercentAction", automation.Action) |  | ||||||
| SetLevelAction = tm1651_ns.class_("SetLevelAction", automation.Action) |  | ||||||
| SetBrightnessAction = tm1651_ns.class_("SetBrightnessAction", automation.Action) | SetBrightnessAction = tm1651_ns.class_("SetBrightnessAction", automation.Action) | ||||||
| TurnOnAction = tm1651_ns.class_("SetLevelPercentAction", automation.Action) | SetLevelAction = tm1651_ns.class_("SetLevelAction", automation.Action) | ||||||
| TurnOffAction = tm1651_ns.class_("SetLevelPercentAction", automation.Action) | SetLevelPercentAction = tm1651_ns.class_("SetLevelPercentAction", automation.Action) | ||||||
|  | TurnOnAction = tm1651_ns.class_("TurnOnAction", automation.Action) | ||||||
| CONF_LEVEL_PERCENT = "level_percent" | TurnOffAction = tm1651_ns.class_("TurnOffAction", automation.Action) | ||||||
|  |  | ||||||
| TM1651_BRIGHTNESS_OPTIONS = { | TM1651_BRIGHTNESS_OPTIONS = { | ||||||
|     1: TM1651Brightness.TM1651_BRIGHTNESS_LOW, |     1: TM1651Brightness.TM1651_DARKEST, | ||||||
|     2: TM1651Brightness.TM1651_BRIGHTNESS_MEDIUM, |     2: TM1651Brightness.TM1651_TYPICAL, | ||||||
|     3: TM1651Brightness.TM1651_BRIGHTNESS_HIGH, |     3: TM1651Brightness.TM1651_BRIGHTEST, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | MULTI_CONF = True | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.All( | CONFIG_SCHEMA = cv.All( | ||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
| @@ -38,26 +40,21 @@ CONFIG_SCHEMA = cv.All( | |||||||
|             cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, |             cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, | ||||||
|         } |         } | ||||||
|     ), |     ), | ||||||
|     cv.only_with_arduino, |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| validate_level_percent = cv.All(cv.int_range(min=0, max=100)) |  | ||||||
| validate_level = cv.All(cv.int_range(min=0, max=7)) |  | ||||||
| validate_brightness = cv.enum(TM1651_BRIGHTNESS_OPTIONS, int=True) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|     clk_pin = await cg.gpio_pin_expression(config[CONF_CLK_PIN]) |     clk_pin = await cg.gpio_pin_expression(config[CONF_CLK_PIN]) | ||||||
|     cg.add(var.set_clk_pin(clk_pin)) |     cg.add(var.set_clk_pin(clk_pin)) | ||||||
|     dio_pin = await cg.gpio_pin_expression(config[CONF_DIO_PIN]) |     dio_pin = await cg.gpio_pin_expression(config[CONF_DIO_PIN]) | ||||||
|     cg.add(var.set_dio_pin(dio_pin)) |     cg.add(var.set_dio_pin(dio_pin)) | ||||||
|  |  | ||||||
|     # https://platformio.org/lib/show/6865/TM1651 |  | ||||||
|     cg.add_library("freekode/TM1651", "1.0.1") |  | ||||||
|  |  | ||||||
|  | validate_brightness = cv.enum(TM1651_BRIGHTNESS_OPTIONS, int=True) | ||||||
|  | validate_level = cv.All(cv.int_range(min=0, max=7)) | ||||||
|  | validate_level_percent = cv.All(cv.int_range(min=0, max=100)) | ||||||
|  |  | ||||||
| BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id( | BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id( | ||||||
|     { |     { | ||||||
| @@ -66,38 +63,22 @@ BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @automation.register_action("tm1651.turn_on", TurnOnAction, BINARY_OUTPUT_ACTION_SCHEMA) |  | ||||||
| async def output_turn_on_to_code(config, action_id, template_arg, args): |  | ||||||
|     var = cg.new_Pvariable(action_id, template_arg) |  | ||||||
|     await cg.register_parented(var, config[CONF_ID]) |  | ||||||
|     return var |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @automation.register_action( | @automation.register_action( | ||||||
|     "tm1651.turn_off", TurnOffAction, BINARY_OUTPUT_ACTION_SCHEMA |     "tm1651.set_brightness", | ||||||
| ) |     SetBrightnessAction, | ||||||
| async def output_turn_off_to_code(config, action_id, template_arg, args): |  | ||||||
|     var = cg.new_Pvariable(action_id, template_arg) |  | ||||||
|     await cg.register_parented(var, config[CONF_ID]) |  | ||||||
|     return var |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @automation.register_action( |  | ||||||
|     "tm1651.set_level_percent", |  | ||||||
|     SetLevelPercentAction, |  | ||||||
|     cv.maybe_simple_value( |     cv.maybe_simple_value( | ||||||
|         { |         { | ||||||
|             cv.GenerateID(): cv.use_id(TM1651Display), |             cv.GenerateID(): cv.use_id(TM1651Display), | ||||||
|             cv.Required(CONF_LEVEL_PERCENT): cv.templatable(validate_level_percent), |             cv.Required(CONF_BRIGHTNESS): cv.templatable(validate_brightness), | ||||||
|         }, |         }, | ||||||
|         key=CONF_LEVEL_PERCENT, |         key=CONF_BRIGHTNESS, | ||||||
|     ), |     ), | ||||||
| ) | ) | ||||||
| async def tm1651_set_level_percent_to_code(config, action_id, template_arg, args): | async def tm1651_set_brightness_to_code(config, action_id, template_arg, args): | ||||||
|     var = cg.new_Pvariable(action_id, template_arg) |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|     await cg.register_parented(var, config[CONF_ID]) |     await cg.register_parented(var, config[CONF_ID]) | ||||||
|     template_ = await cg.templatable(config[CONF_LEVEL_PERCENT], args, cg.uint8) |     template_ = await cg.templatable(config[CONF_BRIGHTNESS], args, cg.uint8) | ||||||
|     cg.add(var.set_level_percent(template_)) |     cg.add(var.set_brightness(template_)) | ||||||
|     return var |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -121,19 +102,35 @@ async def tm1651_set_level_to_code(config, action_id, template_arg, args): | |||||||
|  |  | ||||||
|  |  | ||||||
| @automation.register_action( | @automation.register_action( | ||||||
|     "tm1651.set_brightness", |     "tm1651.set_level_percent", | ||||||
|     SetBrightnessAction, |     SetLevelPercentAction, | ||||||
|     cv.maybe_simple_value( |     cv.maybe_simple_value( | ||||||
|         { |         { | ||||||
|             cv.GenerateID(): cv.use_id(TM1651Display), |             cv.GenerateID(): cv.use_id(TM1651Display), | ||||||
|             cv.Required(CONF_BRIGHTNESS): cv.templatable(validate_brightness), |             cv.Required(CONF_LEVEL_PERCENT): cv.templatable(validate_level_percent), | ||||||
|         }, |         }, | ||||||
|         key=CONF_BRIGHTNESS, |         key=CONF_LEVEL_PERCENT, | ||||||
|     ), |     ), | ||||||
| ) | ) | ||||||
| async def tm1651_set_brightness_to_code(config, action_id, template_arg, args): | async def tm1651_set_level_percent_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     await cg.register_parented(var, config[CONF_ID]) | ||||||
|  |     template_ = await cg.templatable(config[CONF_LEVEL_PERCENT], args, cg.uint8) | ||||||
|  |     cg.add(var.set_level_percent(template_)) | ||||||
|  |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "tm1651.turn_off", TurnOffAction, BINARY_OUTPUT_ACTION_SCHEMA | ||||||
|  | ) | ||||||
|  | async def output_turn_off_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     await cg.register_parented(var, config[CONF_ID]) | ||||||
|  |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action("tm1651.turn_on", TurnOnAction, BINARY_OUTPUT_ACTION_SCHEMA) | ||||||
|  | async def output_turn_on_to_code(config, action_id, template_arg, args): | ||||||
|     var = cg.new_Pvariable(action_id, template_arg) |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|     await cg.register_parented(var, config[CONF_ID]) |     await cg.register_parented(var, config[CONF_ID]) | ||||||
|     template_ = await cg.templatable(config[CONF_BRIGHTNESS], args, cg.uint8) |  | ||||||
|     cg.add(var.set_brightness(template_)) |  | ||||||
|     return var |     return var | ||||||
|   | |||||||
| @@ -1,7 +1,54 @@ | |||||||
| #ifdef USE_ARDUINO | // This Esphome TM1651 component for use with Mini Battery Displays (7 LED levels) | ||||||
|  | // and removes the Esphome dependency on the TM1651 Arduino library. | ||||||
|  | // It was largely based on the work of others as set out below. | ||||||
|  | // @mrtoy-me July 2025 | ||||||
|  | // ============================================================================================== | ||||||
|  | // Original Arduino TM1651 library: | ||||||
|  | // Author:Fred.Chu | ||||||
|  | // Date:14 August, 2014 | ||||||
|  | // Applicable Module: Battery Display v1.0 | ||||||
|  | // This library is free software; you can redistribute it and/or | ||||||
|  | // modify it under the terms of the GNU Lesser General Public | ||||||
|  | // License as published by the Free Software Foundation; either | ||||||
|  | // version 2.1 of the License, or (at your option) any later version. | ||||||
|  | // This library 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 | ||||||
|  | // Lesser General Public License for more details. | ||||||
|  | // Modified record: | ||||||
|  | // Author:  Detlef Giessmann Germany | ||||||
|  | // Mail:    mydiyp@web.de | ||||||
|  | // Demo for the new 7 LED Battery-Display 2017 | ||||||
|  | // IDE:     Arduino-1.6.5 | ||||||
|  | // Type:    OPEN-SMART CX10*4RY68  4Color | ||||||
|  | // Date:    01.05.2017 | ||||||
|  | // ============================================================================================== | ||||||
|  | // Esphome component using arduino TM1651 library: | ||||||
|  | // MIT License | ||||||
|  | // Copyright (c) 2019 freekode | ||||||
|  | // ============================================================================================== | ||||||
|  | // Library and command-line (python) program to control mini battery displays on Raspberry Pi: | ||||||
|  | // MIT License | ||||||
|  | // Copyright (c) 2020 Koen Vervloese | ||||||
|  | // ============================================================================================== | ||||||
|  | // MIT License | ||||||
|  | // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | // of this software and associated documentation files (the "Software"), to deal | ||||||
|  | // in the Software without restriction, including without limitation the rights | ||||||
|  | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | // copies of the Software, and to permit persons to whom the Software is | ||||||
|  | // furnished to do so, subject to the following conditions: | ||||||
|  | // The above copyright notice and this permission notice shall be included in all | ||||||
|  | // copies or substantial portions of the Software. | ||||||
|  | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | // SOFTWARE. | ||||||
|  |  | ||||||
| #include "tm1651.h" | #include "tm1651.h" | ||||||
| #include "esphome/core/helpers.h" |  | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| @@ -9,84 +56,205 @@ namespace tm1651 { | |||||||
|  |  | ||||||
| static const char *const TAG = "tm1651.display"; | static const char *const TAG = "tm1651.display"; | ||||||
|  |  | ||||||
| static const uint8_t MAX_INPUT_LEVEL_PERCENT = 100; | static const bool LINE_HIGH = true; | ||||||
| static const uint8_t TM1651_MAX_LEVEL = 7; | static const bool LINE_LOW = false; | ||||||
|  |  | ||||||
| static const uint8_t TM1651_BRIGHTNESS_LOW_HW = 0; | // TM1651 maximum frequency is 500 kHz (duty ratio 50%) = 2 microseconds / cycle | ||||||
| static const uint8_t TM1651_BRIGHTNESS_MEDIUM_HW = 2; | static const uint8_t CLOCK_CYCLE = 8; | ||||||
| static const uint8_t TM1651_BRIGHTNESS_HIGH_HW = 7; |  | ||||||
|  | static const uint8_t HALF_CLOCK_CYCLE = CLOCK_CYCLE / 2; | ||||||
|  | static const uint8_t QUARTER_CLOCK_CYCLE = CLOCK_CYCLE / 4; | ||||||
|  |  | ||||||
|  | static const uint8_t ADDR_FIXED = 0x44;  // fixed address mode | ||||||
|  | static const uint8_t ADDR_START = 0xC0;  // address of the display register | ||||||
|  |  | ||||||
|  | static const uint8_t DISPLAY_OFF = 0x80; | ||||||
|  | static const uint8_t DISPLAY_ON = 0x88; | ||||||
|  |  | ||||||
|  | static const uint8_t MAX_DISPLAY_LEVELS = 7; | ||||||
|  |  | ||||||
|  | static const uint8_t PERCENT100 = 100; | ||||||
|  | static const uint8_t PERCENT50 = 50; | ||||||
|  |  | ||||||
|  | static const uint8_t TM1651_BRIGHTNESS_DARKEST = 0; | ||||||
|  | static const uint8_t TM1651_BRIGHTNESS_TYPICAL = 2; | ||||||
|  | static const uint8_t TM1651_BRIGHTNESS_BRIGHTEST = 7; | ||||||
|  |  | ||||||
|  | static const uint8_t TM1651_LEVEL_TAB[] = {0b00000000, 0b00000001, 0b00000011, 0b00000111, | ||||||
|  |                                            0b00001111, 0b00011111, 0b00111111, 0b01111111}; | ||||||
|  |  | ||||||
|  | // public | ||||||
|  |  | ||||||
| void TM1651Display::setup() { | void TM1651Display::setup() { | ||||||
|   uint8_t clk = clk_pin_->get_pin(); |   this->clk_pin_->setup(); | ||||||
|   uint8_t dio = dio_pin_->get_pin(); |   this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); | ||||||
|  |  | ||||||
|   battery_display_ = make_unique<TM1651>(clk, dio); |   this->dio_pin_->setup(); | ||||||
|   battery_display_->init(); |   this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); | ||||||
|   battery_display_->clearDisplay(); |  | ||||||
|  |   this->brightness_ = TM1651_BRIGHTNESS_TYPICAL; | ||||||
|  |  | ||||||
|  |   // clear display | ||||||
|  |   this->display_level_(); | ||||||
|  |   this->update_brightness_(DISPLAY_ON); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TM1651Display::dump_config() { | void TM1651Display::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "TM1651 Battery Display"); |   ESP_LOGCONFIG(TAG, "Battery Display"); | ||||||
|   LOG_PIN("  CLK: ", clk_pin_); |   LOG_PIN("  CLK: ", clk_pin_); | ||||||
|   LOG_PIN("  DIO: ", dio_pin_); |   LOG_PIN("  DIO: ", dio_pin_); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TM1651Display::set_level_percent(uint8_t new_level) { | void TM1651Display::set_brightness(uint8_t new_brightness) { | ||||||
|   this->level_ = calculate_level_(new_level); |   this->brightness_ = this->remap_brightness_(new_brightness); | ||||||
|   this->repaint_(); |   if (this->display_on_) { | ||||||
|  |     this->update_brightness_(DISPLAY_ON); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void TM1651Display::set_level(uint8_t new_level) { | void TM1651Display::set_level(uint8_t new_level) { | ||||||
|  |   if (new_level > MAX_DISPLAY_LEVELS) | ||||||
|  |     new_level = MAX_DISPLAY_LEVELS; | ||||||
|   this->level_ = new_level; |   this->level_ = new_level; | ||||||
|   this->repaint_(); |   if (this->display_on_) { | ||||||
|  |     this->display_level_(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void TM1651Display::set_brightness(uint8_t new_brightness) { | void TM1651Display::set_level_percent(uint8_t percentage) { | ||||||
|   this->brightness_ = calculate_brightness_(new_brightness); |   this->level_ = this->calculate_level_(percentage); | ||||||
|   this->repaint_(); |   if (this->display_on_) { | ||||||
| } |     this->display_level_(); | ||||||
|  |   } | ||||||
| void TM1651Display::turn_on() { |  | ||||||
|   this->is_on_ = true; |  | ||||||
|   this->repaint_(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void TM1651Display::turn_off() { | void TM1651Display::turn_off() { | ||||||
|   this->is_on_ = false; |   this->display_on_ = false; | ||||||
|   battery_display_->displayLevel(0); |   this->update_brightness_(DISPLAY_OFF); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TM1651Display::repaint_() { | void TM1651Display::turn_on() { | ||||||
|   if (!this->is_on_) { |   this->display_on_ = true; | ||||||
|     return; |   // display level as it could have been changed when display turned off | ||||||
|   } |   this->display_level_(); | ||||||
|  |   this->update_brightness_(DISPLAY_ON); | ||||||
|   battery_display_->set(this->brightness_); |  | ||||||
|   battery_display_->displayLevel(this->level_); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t TM1651Display::calculate_level_(uint8_t new_level) { | // protected | ||||||
|   if (new_level == 0) { |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   float calculated_level = TM1651_MAX_LEVEL / (float) (MAX_INPUT_LEVEL_PERCENT / (float) new_level); | uint8_t TM1651Display::calculate_level_(uint8_t percentage) { | ||||||
|   return (uint8_t) roundf(calculated_level); |   if (percentage > PERCENT100) | ||||||
|  |     percentage = PERCENT100; | ||||||
|  |   // scale 0-100% to 0-7 display levels | ||||||
|  |   // use integer arithmetic with rounding | ||||||
|  |   uint16_t initial_scaling = (percentage * MAX_DISPLAY_LEVELS) + PERCENT50; | ||||||
|  |   return (uint8_t) (initial_scaling / PERCENT100); | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t TM1651Display::calculate_brightness_(uint8_t new_brightness) { | void TM1651Display::display_level_() { | ||||||
|   if (new_brightness <= 1) { |   this->start_(); | ||||||
|     return TM1651_BRIGHTNESS_LOW_HW; |   this->write_byte_(ADDR_FIXED); | ||||||
|   } else if (new_brightness == 2) { |   this->stop_(); | ||||||
|     return TM1651_BRIGHTNESS_MEDIUM_HW; |  | ||||||
|   } else if (new_brightness >= 3) { |   this->start_(); | ||||||
|     return TM1651_BRIGHTNESS_HIGH_HW; |   this->write_byte_(ADDR_START); | ||||||
|  |   this->write_byte_(TM1651_LEVEL_TAB[this->level_]); | ||||||
|  |   this->stop_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t TM1651Display::remap_brightness_(uint8_t new_brightness) { | ||||||
|  |   if (new_brightness <= 1) | ||||||
|  |     return TM1651_BRIGHTNESS_DARKEST; | ||||||
|  |   if (new_brightness == 2) | ||||||
|  |     return TM1651_BRIGHTNESS_TYPICAL; | ||||||
|  |  | ||||||
|  |   // new_brightness >= 3 | ||||||
|  |   return TM1651_BRIGHTNESS_BRIGHTEST; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TM1651Display::update_brightness_(uint8_t on_off_control) { | ||||||
|  |   this->start_(); | ||||||
|  |   this->write_byte_(on_off_control | this->brightness_); | ||||||
|  |   this->stop_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // low level functions | ||||||
|  |  | ||||||
|  | bool TM1651Display::write_byte_(uint8_t data) { | ||||||
|  |   // data bit written to DIO when CLK is low | ||||||
|  |   for (uint8_t i = 0; i < 8; i++) { | ||||||
|  |     this->half_cycle_clock_low_((bool) (data & 0x01)); | ||||||
|  |     this->half_cycle_clock_high_(); | ||||||
|  |     data >>= 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return TM1651_BRIGHTNESS_LOW_HW; |   // start 9th cycle, setting DIO high and look for ack | ||||||
|  |   this->half_cycle_clock_low_(LINE_HIGH); | ||||||
|  |   return this->half_cycle_clock_high_ack_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TM1651Display::half_cycle_clock_low_(bool data_bit) { | ||||||
|  |   // first half cycle, clock low and write data bit | ||||||
|  |   this->clk_pin_->digital_write(LINE_LOW); | ||||||
|  |   delayMicroseconds(QUARTER_CLOCK_CYCLE); | ||||||
|  |  | ||||||
|  |   this->dio_pin_->digital_write(data_bit); | ||||||
|  |   delayMicroseconds(QUARTER_CLOCK_CYCLE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TM1651Display::half_cycle_clock_high_() { | ||||||
|  |   // second half cycle, clock high | ||||||
|  |   this->clk_pin_->digital_write(LINE_HIGH); | ||||||
|  |   delayMicroseconds(HALF_CLOCK_CYCLE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool TM1651Display::half_cycle_clock_high_ack_() { | ||||||
|  |   // second half cycle, clock high and check for ack | ||||||
|  |   this->clk_pin_->digital_write(LINE_HIGH); | ||||||
|  |   delayMicroseconds(QUARTER_CLOCK_CYCLE); | ||||||
|  |  | ||||||
|  |   this->dio_pin_->pin_mode(gpio::FLAG_INPUT); | ||||||
|  |   // valid ack on DIO is low | ||||||
|  |   bool ack = (!this->dio_pin_->digital_read()); | ||||||
|  |  | ||||||
|  |   this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); | ||||||
|  |  | ||||||
|  |   // ack should be set DIO low by now | ||||||
|  |   // if its not, set DIO low before the next cycle | ||||||
|  |   if (!ack) { | ||||||
|  |     this->dio_pin_->digital_write(LINE_LOW); | ||||||
|  |   } | ||||||
|  |   delayMicroseconds(QUARTER_CLOCK_CYCLE); | ||||||
|  |  | ||||||
|  |   // begin next cycle | ||||||
|  |   this->clk_pin_->digital_write(LINE_LOW); | ||||||
|  |  | ||||||
|  |   return ack; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TM1651Display::start_() { | ||||||
|  |   // start data transmission | ||||||
|  |   this->delineate_transmission_(LINE_HIGH); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TM1651Display::stop_() { | ||||||
|  |   // stop data transmission | ||||||
|  |   this->delineate_transmission_(LINE_LOW); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TM1651Display::delineate_transmission_(bool dio_state) { | ||||||
|  |   // delineate data transmission | ||||||
|  |   // DIO changes its value while CLK is high | ||||||
|  |  | ||||||
|  |   this->dio_pin_->digital_write(dio_state); | ||||||
|  |   delayMicroseconds(HALF_CLOCK_CYCLE); | ||||||
|  |  | ||||||
|  |   this->clk_pin_->digital_write(LINE_HIGH); | ||||||
|  |   delayMicroseconds(QUARTER_CLOCK_CYCLE); | ||||||
|  |  | ||||||
|  |   this->dio_pin_->digital_write(!dio_state); | ||||||
|  |   delayMicroseconds(QUARTER_CLOCK_CYCLE); | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace tm1651 | }  // namespace tm1651 | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
| #endif  // USE_ARDUINO |  | ||||||
|   | |||||||
| @@ -1,22 +1,16 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #ifdef USE_ARDUINO | #include "esphome/core/automation.h" | ||||||
|  |  | ||||||
| #include <memory> |  | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
| #include "esphome/core/automation.h" |  | ||||||
|  |  | ||||||
| #include <TM1651.h> |  | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace tm1651 { | namespace tm1651 { | ||||||
|  |  | ||||||
| enum TM1651Brightness : uint8_t { | enum TM1651Brightness : uint8_t { | ||||||
|   TM1651_BRIGHTNESS_LOW = 1, |   TM1651_DARKEST = 1, | ||||||
|   TM1651_BRIGHTNESS_MEDIUM = 2, |   TM1651_TYPICAL = 2, | ||||||
|   TM1651_BRIGHTNESS_HIGH = 3, |   TM1651_BRIGHTEST = 3, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class TM1651Display : public Component { | class TM1651Display : public Component { | ||||||
| @@ -27,36 +21,49 @@ class TM1651Display : public Component { | |||||||
|   void setup() override; |   void setup() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|  |  | ||||||
|   void set_level_percent(uint8_t new_level); |  | ||||||
|   void set_level(uint8_t new_level); |  | ||||||
|   void set_brightness(uint8_t new_brightness); |   void set_brightness(uint8_t new_brightness); | ||||||
|   void set_brightness(TM1651Brightness new_brightness) { this->set_brightness(static_cast<uint8_t>(new_brightness)); } |   void set_brightness(TM1651Brightness new_brightness) { this->set_brightness(static_cast<uint8_t>(new_brightness)); } | ||||||
|  |  | ||||||
|   void turn_on(); |   void set_level(uint8_t new_level); | ||||||
|  |   void set_level_percent(uint8_t percentage); | ||||||
|  |  | ||||||
|   void turn_off(); |   void turn_off(); | ||||||
|  |   void turn_on(); | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   std::unique_ptr<TM1651> battery_display_; |   uint8_t calculate_level_(uint8_t percentage); | ||||||
|  |   void display_level_(); | ||||||
|  |  | ||||||
|  |   uint8_t remap_brightness_(uint8_t new_brightness); | ||||||
|  |   void update_brightness_(uint8_t on_off_control); | ||||||
|  |  | ||||||
|  |   // low level functions | ||||||
|  |   bool write_byte_(uint8_t data); | ||||||
|  |  | ||||||
|  |   void half_cycle_clock_low_(bool data_bit); | ||||||
|  |   void half_cycle_clock_high_(); | ||||||
|  |   bool half_cycle_clock_high_ack_(); | ||||||
|  |  | ||||||
|  |   void start_(); | ||||||
|  |   void stop_(); | ||||||
|  |  | ||||||
|  |   void delineate_transmission_(bool dio_state); | ||||||
|  |  | ||||||
|   InternalGPIOPin *clk_pin_; |   InternalGPIOPin *clk_pin_; | ||||||
|   InternalGPIOPin *dio_pin_; |   InternalGPIOPin *dio_pin_; | ||||||
|   bool is_on_ = true; |  | ||||||
|  |  | ||||||
|   uint8_t brightness_; |   bool display_on_{true}; | ||||||
|   uint8_t level_; |   uint8_t brightness_{}; | ||||||
|  |   uint8_t level_{0}; | ||||||
|   void repaint_(); |  | ||||||
|  |  | ||||||
|   uint8_t calculate_level_(uint8_t new_level); |  | ||||||
|   uint8_t calculate_brightness_(uint8_t new_brightness); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| template<typename... Ts> class SetLevelPercentAction : public Action<Ts...>, public Parented<TM1651Display> { | template<typename... Ts> class SetBrightnessAction : public Action<Ts...>, public Parented<TM1651Display> { | ||||||
|  public: |  public: | ||||||
|   TEMPLATABLE_VALUE(uint8_t, level_percent) |   TEMPLATABLE_VALUE(uint8_t, brightness) | ||||||
|  |  | ||||||
|   void play(Ts... x) override { |   void play(Ts... x) override { | ||||||
|     auto level_percent = this->level_percent_.value(x...); |     auto brightness = this->brightness_.value(x...); | ||||||
|     this->parent_->set_level_percent(level_percent); |     this->parent_->set_brightness(brightness); | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -70,13 +77,13 @@ template<typename... Ts> class SetLevelAction : public Action<Ts...>, public Par | |||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| template<typename... Ts> class SetBrightnessAction : public Action<Ts...>, public Parented<TM1651Display> { | template<typename... Ts> class SetLevelPercentAction : public Action<Ts...>, public Parented<TM1651Display> { | ||||||
|  public: |  public: | ||||||
|   TEMPLATABLE_VALUE(uint8_t, brightness) |   TEMPLATABLE_VALUE(uint8_t, level_percent) | ||||||
|  |  | ||||||
|   void play(Ts... x) override { |   void play(Ts... x) override { | ||||||
|     auto brightness = this->brightness_.value(x...); |     auto level_percent = this->level_percent_.value(x...); | ||||||
|     this->parent_->set_brightness(brightness); |     this->parent_->set_level_percent(level_percent); | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -92,5 +99,3 @@ template<typename... Ts> class TurnOffAction : public Action<Ts...>, public Pare | |||||||
|  |  | ||||||
| }  // namespace tm1651 | }  // namespace tm1651 | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
| #endif  // USE_ARDUINO |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								tests/components/tm1651/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/components/tm1651/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										1
									
								
								tests/components/tm1651/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/components/tm1651/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <<: !include common.yaml | ||||||
		Reference in New Issue
	
	Block a user