Compare commits

...
Author SHA1 Message Date
J. Nick Koston 7f5be74b73 Include helpers.h where assign_bits is used 2026-10-20 04:05:36 -10:00
J. Nick Koston 7531fd4240 Move the entity, socket and sensor conversions to their own PR 2026-10-20 03:55:10 -10:00
J. Nick Koston 0a830a60f6 Merge remote-tracking branch 'origin/assign-bits-helper' into assign-bits-migrate-other 2026-10-20 03:52:11 -10:00
J. Nick Koston 29259ca74e Always inline assign_bits 2026-10-20 03:52:03 -10:00
J. Nick Koston f6c1d87b1b Leave the IRAM ds2484 search unchanged 2026-10-20 03:46:26 -10:00
J. Nick Koston 829287ec28 Set or clear register bits with assign_bits
Replace the set-or-clear if/else blocks in components with assign_bits, and
drop the local write_bit (opentherm) and set_bit (hitachi_ac344, hitachi_ac424)
copies of the same helper.
2026-10-20 03:40:03 -10:00
J. Nick Koston 6a1b5d517b Note the unsigned mask for wide registers 2026-10-20 03:36:19 -10:00
J. Nick Koston 9687c0e0a8 Take the mask at the target width 2026-10-20 03:31:51 -10:00
J. Nick Koston 230c120898 Add assign_bits to set or clear register bits from a bool 2026-10-20 03:28:19 -10:00
17 changed files with 81 additions and 106 deletions
+2 -2
View File
@@ -211,8 +211,8 @@ file does, and it is the authority when they disagree. The most useful starting
logged with `get_log_str` and `LOG_STR_ARG`, copied out with `ESPHOME_strncpy_P` before `publish_state`.
A `dump_config` helper that picks a name returns `const LogString *` from `LOG_STR(...)`.
* **CRCs and bit tricks** come from `esphome/core/helpers.h` (`crc8`, `crc16`, `xor_bytes`, `sum16_bytes`,
`reverse_bits`, `encode_uint16`, `encode_value_le`, `sign_extend<BITS>`, `bcd_to_uint8`); pick the helper's
arguments instead of writing a loop. Epson style 7-byte RTC calendars (one-hot weekday) use
`reverse_bits`, `encode_uint16`, `encode_value_le`, `sign_extend<BITS>`, `bcd_to_uint8`, `assign_bits`); pick
the helper's arguments instead of writing a loop. Epson style 7-byte RTC calendars (one-hot weekday) use
`time::decode_bcd_calendar`.
* **Bus helpers:** UART drains with `read_available(on_byte)` and `discard_available()`, text lines with
`LineReader<N>`; I2C has `update_register`, `write_registers` (PROGMEM `I2CRegValue` table), `check_chip_id`
@@ -1,4 +1,5 @@
#include "epaper_waveshare_bwr.h"
#include "esphome/core/helpers.h"
#include <algorithm>
@@ -36,17 +37,9 @@ void EPaperWaveshareBWR::draw_pixel_at(int x, int y, Color color) {
const auto bwr = color_to_bwr(color);
if (bwr == BwrState::BWR_BLACK) {
this->buffer_[pos] |= bit;
} else {
this->buffer_[pos] &= ~bit;
}
assign_bits(this->buffer_[pos], bit, bwr == BwrState::BWR_BLACK);
if (bwr == BwrState::BWR_RED) {
this->buffer_[red_offset + pos] |= bit;
} else {
this->buffer_[red_offset + pos] &= ~bit;
}
assign_bits(this->buffer_[red_offset + pos], bit, bwr == BwrState::BWR_RED);
}
void EPaperWaveshareBWR::fill(Color color) {
+2 -5
View File
@@ -1,6 +1,7 @@
#include "es8156.h"
#include "es8156_const.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cinttypes>
@@ -100,11 +101,7 @@ bool ES8156::set_mute_state_(bool mute_state) {
ESP_LOGV(TAG, "Read ES8156_REG13_DAC_MUTE: %u", reg13);
if (mute_state) {
reg13 |= BIT(1) | BIT(2);
} else {
reg13 &= ~(BIT(1) | BIT(2));
}
assign_bits(reg13, BIT(1) | BIT(2), mute_state);
ESP_LOGV(TAG, "Setting ES8156_REG13_DAC_MUTE to %u (muted: %s)", reg13, YESNO(mute_state));
return this->write_byte(ES8156_REG13_DAC_MUTE, reg13);
+3 -10
View File
@@ -1,6 +1,7 @@
#include "es8311.h"
#include "es8311_const.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cinttypes>
@@ -154,11 +155,7 @@ bool ES8311::configure_clock_() {
// Register 0x06
uint8_t reg06;
ES8311_ERROR_CHECK(this->read_byte(ES8311_REG06_CLK_MANAGER, &reg06));
if (this->sclk_inverted_) {
reg06 |= BIT(5);
} else {
reg06 &= ~BIT(5);
}
assign_bits(reg06, BIT(5), this->sclk_inverted_);
reg06 &= 0xE0;
if (coefficient->bclk_div < 19) {
reg06 |= (coefficient->bclk_div - 1) << 0;
@@ -223,11 +220,7 @@ bool ES8311::set_mute_state_(bool mute_state) {
return false;
}
if (mute_state) {
reg31 |= BIT(6) | BIT(5);
} else {
reg31 &= ~(BIT(6) | BIT(5));
}
assign_bits(reg31, BIT(6) | BIT(5), mute_state);
return this->write_byte(ES8311_REG31_DAC, reg31);
}
+2 -5
View File
@@ -2,6 +2,7 @@
#include <cinttypes>
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::es8388 {
@@ -184,11 +185,7 @@ bool ES8388::set_mute_state_(bool mute_state) {
// DAC settings that must be preserved. Previously muting overwrote the whole
// register with 0x3C and unmuting never cleared the bit, so once muted the DAC
// could not be unmuted again.
if (mute_state) {
value |= ES8388_DACCONTROL3_DAC_MUTE;
} else {
value &= ~ES8388_DACCONTROL3_DAC_MUTE;
}
assign_bits(value, ES8388_DACCONTROL3_DAC_MUTE, mute_state);
ESP_LOGV(TAG, "Setting ES8388_DACCONTROL3 to 0x%02X (muted: %s)", value, YESNO(mute_state));
return this->write_byte(ES8388_DACCONTROL3, value);
+2 -5
View File
@@ -1,5 +1,6 @@
#include "gree.h"
#include "esphome/components/remote_base/remote_base.h"
#include "esphome/core/helpers.h"
namespace esphome::gree {
@@ -21,11 +22,7 @@ void GreeClimate::set_model(Model model) {
}
void GreeClimate::set_mode_bit(uint8_t bit_mask, bool enabled) {
if (enabled) {
this->mode_bits_ |= bit_mask;
} else {
this->mode_bits_ &= ~bit_mask;
}
assign_bits(this->mode_bits_, bit_mask, enabled);
this->transmit_state();
}
@@ -1,4 +1,5 @@
#include "hitachi_ac344.h"
#include "esphome/core/helpers.h"
namespace esphome::hitachi_ac344 {
@@ -16,15 +17,6 @@ void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, con
*dst |= ((data & mask) << offset);
}
void set_bit(uint8_t *const data, const uint8_t position, const bool on) {
uint8_t mask = 1 << position;
if (on) {
*data |= mask;
} else {
*data &= ~mask;
}
}
uint8_t *invert_byte_pairs(uint8_t *ptr, const uint16_t length) {
for (uint16_t i = 1; i < length; i += 2) {
// Code done this way to avoid a compiler warning bug.
@@ -124,7 +116,7 @@ bool HitachiClimate::get_swing_v_toggle_() { return get_button_() == HITACHI_AC3
void HitachiClimate::set_swing_v_(bool on) {
set_swing_v_toggle_(on); // Set the button value.
set_bit(&remote_state_[HITACHI_AC344_SWINGV_BYTE], HITACHI_AC344_SWINGV_OFFSET, on);
assign_bits(this->remote_state_[HITACHI_AC344_SWINGV_BYTE], 1 << HITACHI_AC344_SWINGV_OFFSET, on);
}
bool HitachiClimate::get_swing_v_() {
@@ -1,4 +1,5 @@
#include "hitachi_ac424.h"
#include "esphome/core/helpers.h"
namespace esphome::hitachi_ac424 {
@@ -16,15 +17,6 @@ void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, con
*dst |= ((data & mask) << offset);
}
void set_bit(uint8_t *const data, const uint8_t position, const bool on) {
uint8_t mask = 1 << position;
if (on) {
*data |= mask;
} else {
*data &= ~mask;
}
}
uint8_t *invert_byte_pairs(uint8_t *ptr, const uint16_t length) {
for (uint16_t i = 1; i < length; i += 2) {
// Code done this way to avoid a compiler warning bug.
@@ -124,7 +116,7 @@ bool HitachiClimate::get_swing_v_toggle_() { return get_button_() == HITACHI_AC4
void HitachiClimate::set_swing_v_(bool on) {
set_swing_v_toggle_(on); // Set the button value.
set_bit(&remote_state_[HITACHI_AC424_SWINGV_BYTE], HITACHI_AC424_SWINGV_OFFSET, on);
assign_bits(this->remote_state_[HITACHI_AC424_SWINGV_BYTE], 1 << HITACHI_AC424_SWINGV_OFFSET, on);
}
bool HitachiClimate::get_swing_v_() {
+16 -16
View File
@@ -33,22 +33,22 @@ uint16_t parse_u8_hb_60(OpenthermData &data) { return data.valueHB * 60; }
int16_t parse_s16(OpenthermData &data) { return data.get_s16(); }
float parse_f88(OpenthermData &data) { return data.get_f88(); }
void write_flag8_lb_0(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 0, value); }
void write_flag8_lb_1(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 1, value); }
void write_flag8_lb_2(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 2, value); }
void write_flag8_lb_3(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 3, value); }
void write_flag8_lb_4(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 4, value); }
void write_flag8_lb_5(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 5, value); }
void write_flag8_lb_6(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 6, value); }
void write_flag8_lb_7(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 7, value); }
void write_flag8_hb_0(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 0, value); }
void write_flag8_hb_1(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 1, value); }
void write_flag8_hb_2(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 2, value); }
void write_flag8_hb_3(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 3, value); }
void write_flag8_hb_4(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 4, value); }
void write_flag8_hb_5(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 5, value); }
void write_flag8_hb_6(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 6, value); }
void write_flag8_hb_7(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 7, value); }
void write_flag8_lb_0(const bool value, OpenthermData &data) { assign_bits(data.valueLB, 1 << 0, value); }
void write_flag8_lb_1(const bool value, OpenthermData &data) { assign_bits(data.valueLB, 1 << 1, value); }
void write_flag8_lb_2(const bool value, OpenthermData &data) { assign_bits(data.valueLB, 1 << 2, value); }
void write_flag8_lb_3(const bool value, OpenthermData &data) { assign_bits(data.valueLB, 1 << 3, value); }
void write_flag8_lb_4(const bool value, OpenthermData &data) { assign_bits(data.valueLB, 1 << 4, value); }
void write_flag8_lb_5(const bool value, OpenthermData &data) { assign_bits(data.valueLB, 1 << 5, value); }
void write_flag8_lb_6(const bool value, OpenthermData &data) { assign_bits(data.valueLB, 1 << 6, value); }
void write_flag8_lb_7(const bool value, OpenthermData &data) { assign_bits(data.valueLB, 1 << 7, value); }
void write_flag8_hb_0(const bool value, OpenthermData &data) { assign_bits(data.valueHB, 1 << 0, value); }
void write_flag8_hb_1(const bool value, OpenthermData &data) { assign_bits(data.valueHB, 1 << 1, value); }
void write_flag8_hb_2(const bool value, OpenthermData &data) { assign_bits(data.valueHB, 1 << 2, value); }
void write_flag8_hb_3(const bool value, OpenthermData &data) { assign_bits(data.valueHB, 1 << 3, value); }
void write_flag8_hb_4(const bool value, OpenthermData &data) { assign_bits(data.valueHB, 1 << 4, value); }
void write_flag8_hb_5(const bool value, OpenthermData &data) { assign_bits(data.valueHB, 1 << 5, value); }
void write_flag8_hb_6(const bool value, OpenthermData &data) { assign_bits(data.valueHB, 1 << 6, value); }
void write_flag8_hb_7(const bool value, OpenthermData &data) { assign_bits(data.valueHB, 1 << 7, value); }
void write_u8_lb(const uint8_t value, OpenthermData &data) { data.valueLB = value; }
void write_u8_hb(const uint8_t value, OpenthermData &data) { data.valueHB = value; }
void write_s8_lb(const int8_t value, OpenthermData &data) { data.valueLB = (uint8_t) value; }
-8
View File
@@ -20,14 +20,6 @@ namespace esphome::opentherm {
template<class T> constexpr T read_bit(T value, uint8_t bit) { return (value >> bit) & 0x01; }
template<class T> constexpr T set_bit(T value, uint8_t bit) { return value |= (1UL << bit); }
template<class T> constexpr T clear_bit(T value, uint8_t bit) { return value &= ~(1UL << bit); }
template<class T> constexpr T write_bit(T value, uint8_t bit, uint8_t bit_value) {
return bit_value ? set_bit(value, bit) : clear_bit(value, bit);
}
enum OperationMode {
IDLE = 0, // no operation
+1 -5
View File
@@ -90,11 +90,7 @@ void HOT PCD8544::draw_absolute_pixel_internal(int x, int y, Color color) {
uint16_t pos = x + (y / 8) * this->get_width_internal();
uint8_t subpos = y % 8;
if (color.is_on()) {
this->buffer_[pos] |= (1 << subpos);
} else {
this->buffer_[pos] &= ~(1 << subpos);
}
assign_bits(this->buffer_[pos], 1 << subpos, color.is_on());
}
void PCD8544::dump_config() {
@@ -1,5 +1,6 @@
#include "runtime_image.h"
#include "image_decoder.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <algorithm>
#include <cstdint>
@@ -91,11 +92,7 @@ void RuntimeImage::draw_pixel(int x, int y, const Color &color) {
auto on = is_color_on(color);
if (this->has_transparency() && color.w < 0x80)
on = false;
if (on) {
this->buffer_[pos] |= bitno;
} else {
this->buffer_[pos] &= ~bitno;
}
assign_bits(this->buffer_[pos], bitno, on);
break;
}
case image::IMAGE_TYPE_RGB111: {
@@ -342,11 +342,7 @@ void HOT SSD1306::draw_absolute_pixel_internal(int x, int y, Color color) {
uint16_t pos = x + (y / 8) * this->get_width_internal();
uint8_t subpos = y & 0x07;
if (color.is_on()) {
this->buffer_[pos] |= (1 << subpos);
} else {
this->buffer_[pos] &= ~(1 << subpos);
}
assign_bits(this->buffer_[pos], 1 << subpos, color.is_on());
}
void SSD1306::fill(Color color) {
// If clipping is active, fall back to base implementation
+2 -5
View File
@@ -3,6 +3,7 @@
#include <algorithm>
#include <cstring>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::st7305 {
@@ -191,11 +192,7 @@ void HOT ST7305::draw_absolute_pixel_internal(int x, int y, Color color) {
// The init sequence enables INVON, so a set bit is white and a clear bit is black.
uint8_t &value = this->buffer_[index];
const uint8_t previous = value;
if (color.is_on()) {
value |= mask;
} else {
value &= ~mask;
}
assign_bits(value, mask, color.is_on());
if (value == previous)
return;
this->extend_dirty_(x, y, x, y);
@@ -123,11 +123,7 @@ void HOT ST7567::draw_absolute_pixel_internal(int x, int y, Color color) {
uint16_t pos = x + (y / 8) * this->get_width_internal();
uint8_t subpos = y & 0x07;
if (color.is_on()) {
this->buffer_[pos] |= (1 << subpos);
} else {
this->buffer_[pos] &= ~(1 << subpos);
}
assign_bits(this->buffer_[pos], 1 << subpos, color.is_on());
}
void ST7567::fill(Color color) {
+10
View File
@@ -1067,6 +1067,16 @@ constexpr uint8_t bcd_to_uint8(uint8_t bcd) { return (bcd >> 4) * 10 + (bcd & 0x
/// Encode a value from 0 to 99 as a two-digit packed BCD byte, e.g. 59 to 0x59.
constexpr uint8_t uint8_to_bcd(uint8_t value) { return ((value / 10) << 4) | (value % 10); }
/// Set the bits of \p mask in \p value when \p on is true, clear them otherwise.
/// Use an unsigned mask for a register wider than int; a negative int mask is sign-extended.
template<typename T> ESPHOME_ALWAYS_INLINE constexpr void assign_bits(T &value, std::type_identity_t<T> mask, bool on) {
if (on) {
value |= mask;
} else {
value &= ~mask;
}
}
/// Reverse the order of 8 bits.
inline uint8_t reverse_bits(uint8_t x) {
x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
@@ -0,0 +1,30 @@
#include <gtest/gtest.h>
#include "esphome/core/helpers.h"
namespace esphome::core::testing {
static_assert([] {
uint8_t v = 0;
assign_bits(v, 1 << 3, true);
return v;
}() == 0x08);
TEST(AssignBits, SetsAndClearsOnlyTheMask) {
uint8_t reg = 0b1010'0001;
assign_bits(reg, 0b0000'0110, true);
EXPECT_EQ(reg, 0b1010'0111);
assign_bits(reg, 0b1000'0010, false);
EXPECT_EQ(reg, 0b0010'0101);
}
TEST(AssignBits, ClearsAtTheTargetWidth) {
uint16_t reg16 = 0xFFFF;
assign_bits(reg16, 1 << 15, false);
EXPECT_EQ(reg16, 0x7FFF);
uint64_t reg64 = UINT64_MAX;
assign_bits(reg64, uint32_t{1} << 31, false);
EXPECT_EQ(reg64, UINT64_MAX & ~(uint64_t{1} << 31));
}
} // namespace esphome::core::testing