ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
tm1651.cpp
Go to the documentation of this file.
1// This Esphome TM1651 component for use with Mini Battery Displays (7 LED levels)
2// and removes the Esphome dependency on the TM1651 Arduino library.
3// It was largely based on the work of others as set out below.
4// @mrtoy-me July 2025
5// ==============================================================================================
6// Original Arduino TM1651 library:
7// Author:Fred.Chu
8// Date:14 August, 2014
9// Applicable Module: Battery Display v1.0
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU
17// Lesser General Public License for more details.
18// Modified record:
19// Author: Detlef Giessmann Germany
20// Mail: [email protected]
21// Demo for the new 7 LED Battery-Display 2017
22// IDE: Arduino-1.6.5
23// Type: OPEN-SMART CX10*4RY68 4Color
24// Date: 01.05.2017
25// ==============================================================================================
26// Esphome component using arduino TM1651 library:
27// MIT License
28// Copyright (c) 2019 freekode
29// ==============================================================================================
30// Library and command-line (python) program to control mini battery displays on Raspberry Pi:
31// MIT License
32// Copyright (c) 2020 Koen Vervloese
33// ==============================================================================================
34// MIT License
35// Permission is hereby granted, free of charge, to any person obtaining a copy
36// of this software and associated documentation files (the "Software"), to deal
37// in the Software without restriction, including without limitation the rights
38// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39// copies of the Software, and to permit persons to whom the Software is
40// furnished to do so, subject to the following conditions:
41// The above copyright notice and this permission notice shall be included in all
42// copies or substantial portions of the Software.
43// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49// SOFTWARE.
50
51#include "tm1651.h"
52#include "esphome/core/log.h"
53
54namespace esphome {
55namespace tm1651 {
56
57static const char *const TAG = "tm1651.display";
58
59static const bool LINE_HIGH = true;
60static const bool LINE_LOW = false;
61
62// TM1651 maximum frequency is 500 kHz (duty ratio 50%) = 2 microseconds / cycle
63static const uint8_t CLOCK_CYCLE = 8;
64
65static const uint8_t HALF_CLOCK_CYCLE = CLOCK_CYCLE / 2;
66static const uint8_t QUARTER_CLOCK_CYCLE = CLOCK_CYCLE / 4;
67
68static const uint8_t ADDR_FIXED = 0x44; // fixed address mode
69static const uint8_t ADDR_START = 0xC0; // address of the display register
70
71static const uint8_t DISPLAY_OFF = 0x80;
72static const uint8_t DISPLAY_ON = 0x88;
73
74static const uint8_t MAX_DISPLAY_LEVELS = 7;
75
76static const uint8_t PERCENT100 = 100;
77static const uint8_t PERCENT50 = 50;
78
79static const uint8_t TM1651_BRIGHTNESS_DARKEST = 0;
80static const uint8_t TM1651_BRIGHTNESS_TYPICAL = 2;
81static const uint8_t TM1651_BRIGHTNESS_BRIGHTEST = 7;
82
83static const uint8_t TM1651_LEVEL_TAB[] = {0b00000000, 0b00000001, 0b00000011, 0b00000111,
84 0b00001111, 0b00011111, 0b00111111, 0b01111111};
85
86// public
87
89 this->clk_pin_->setup();
91
92 this->dio_pin_->setup();
94
95 this->brightness_ = TM1651_BRIGHTNESS_TYPICAL;
96
97 // clear display
98 this->display_level_();
99 this->update_brightness_(DISPLAY_ON);
100}
101
103 ESP_LOGCONFIG(TAG, "Battery Display");
104 LOG_PIN(" CLK: ", clk_pin_);
105 LOG_PIN(" DIO: ", dio_pin_);
106}
107
108void TM1651Display::set_brightness(uint8_t new_brightness) {
109 this->brightness_ = this->remap_brightness_(new_brightness);
110 if (this->display_on_) {
111 this->update_brightness_(DISPLAY_ON);
112 }
113}
114
115void TM1651Display::set_level(uint8_t new_level) {
116 if (new_level > MAX_DISPLAY_LEVELS)
117 new_level = MAX_DISPLAY_LEVELS;
118 this->level_ = new_level;
119 if (this->display_on_) {
120 this->display_level_();
121 }
122}
123
124void TM1651Display::set_level_percent(uint8_t percentage) {
125 this->level_ = this->calculate_level_(percentage);
126 if (this->display_on_) {
127 this->display_level_();
128 }
129}
130
132 this->display_on_ = false;
133 this->update_brightness_(DISPLAY_OFF);
134}
135
137 this->display_on_ = true;
138 // display level as it could have been changed when display turned off
139 this->display_level_();
140 this->update_brightness_(DISPLAY_ON);
141}
142
143// protected
144
145uint8_t TM1651Display::calculate_level_(uint8_t percentage) {
146 if (percentage > PERCENT100)
147 percentage = PERCENT100;
148 // scale 0-100% to 0-7 display levels
149 // use integer arithmetic with rounding
150 uint16_t initial_scaling = (percentage * MAX_DISPLAY_LEVELS) + PERCENT50;
151 return (uint8_t) (initial_scaling / PERCENT100);
152}
153
155 this->start_();
156 this->write_byte_(ADDR_FIXED);
157 this->stop_();
158
159 this->start_();
160 this->write_byte_(ADDR_START);
161 this->write_byte_(TM1651_LEVEL_TAB[this->level_]);
162 this->stop_();
163}
164
165uint8_t TM1651Display::remap_brightness_(uint8_t new_brightness) {
166 if (new_brightness <= 1)
167 return TM1651_BRIGHTNESS_DARKEST;
168 if (new_brightness == 2)
169 return TM1651_BRIGHTNESS_TYPICAL;
170
171 // new_brightness >= 3
172 return TM1651_BRIGHTNESS_BRIGHTEST;
173}
174
175void TM1651Display::update_brightness_(uint8_t on_off_control) {
176 this->start_();
177 this->write_byte_(on_off_control | this->brightness_);
178 this->stop_();
179}
180
181// low level functions
182
183bool TM1651Display::write_byte_(uint8_t data) {
184 // data bit written to DIO when CLK is low
185 for (uint8_t i = 0; i < 8; i++) {
186 this->half_cycle_clock_low_((bool) (data & 0x01));
188 data >>= 1;
189 }
190
191 // start 9th cycle, setting DIO high and look for ack
192 this->half_cycle_clock_low_(LINE_HIGH);
193 return this->half_cycle_clock_high_ack_();
194}
195
197 // first half cycle, clock low and write data bit
198 this->clk_pin_->digital_write(LINE_LOW);
199 delayMicroseconds(QUARTER_CLOCK_CYCLE);
200
201 this->dio_pin_->digital_write(data_bit);
202 delayMicroseconds(QUARTER_CLOCK_CYCLE);
203}
204
206 // second half cycle, clock high
207 this->clk_pin_->digital_write(LINE_HIGH);
208 delayMicroseconds(HALF_CLOCK_CYCLE);
209}
210
212 // second half cycle, clock high and check for ack
213 this->clk_pin_->digital_write(LINE_HIGH);
214 delayMicroseconds(QUARTER_CLOCK_CYCLE);
215
217 // valid ack on DIO is low
218 bool ack = (!this->dio_pin_->digital_read());
219
221
222 // ack should be set DIO low by now
223 // if its not, set DIO low before the next cycle
224 if (!ack) {
225 this->dio_pin_->digital_write(LINE_LOW);
226 }
227 delayMicroseconds(QUARTER_CLOCK_CYCLE);
228
229 // begin next cycle
230 this->clk_pin_->digital_write(LINE_LOW);
231
232 return ack;
233}
234
236 // start data transmission
237 this->delineate_transmission_(LINE_HIGH);
238}
239
241 // stop data transmission
242 this->delineate_transmission_(LINE_LOW);
243}
244
246 // delineate data transmission
247 // DIO changes its value while CLK is high
248
249 this->dio_pin_->digital_write(dio_state);
250 delayMicroseconds(HALF_CLOCK_CYCLE);
251
252 this->clk_pin_->digital_write(LINE_HIGH);
253 delayMicroseconds(QUARTER_CLOCK_CYCLE);
254
255 this->dio_pin_->digital_write(!dio_state);
256 delayMicroseconds(QUARTER_CLOCK_CYCLE);
257}
258
259} // namespace tm1651
260} // namespace esphome
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
virtual void digital_write(bool value)=0
virtual bool digital_read()=0
InternalGPIOPin * clk_pin_
Definition tm1651.h:52
void update_brightness_(uint8_t on_off_control)
Definition tm1651.cpp:175
void set_level_percent(uint8_t percentage)
Definition tm1651.cpp:124
void set_brightness(uint8_t new_brightness)
Definition tm1651.cpp:108
void delineate_transmission_(bool dio_state)
Definition tm1651.cpp:245
uint8_t calculate_level_(uint8_t percentage)
Definition tm1651.cpp:145
bool write_byte_(uint8_t data)
Definition tm1651.cpp:183
uint8_t remap_brightness_(uint8_t new_brightness)
Definition tm1651.cpp:165
void half_cycle_clock_low_(bool data_bit)
Definition tm1651.cpp:196
InternalGPIOPin * dio_pin_
Definition tm1651.h:53
void set_level(uint8_t new_level)
Definition tm1651.cpp:115
void dump_config() override
Definition tm1651.cpp:102
@ FLAG_OUTPUT
Definition gpio.h:19
@ FLAG_INPUT
Definition gpio.h:18
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:31
uint8_t ack