Merge branch 'dev' into store-yaml-firmware

This commit is contained in:
J. Nick Koston
2026-07-03 21:34:47 -05:00
committed by GitHub
10 changed files with 368 additions and 0 deletions
+1
View File
@@ -122,6 +122,7 @@ esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
esphome/components/cst226/* @clydebarrow
esphome/components/cst328/* @latonita
esphome/components/cst816/* @clydebarrow
esphome/components/cst9220/* @clydebarrow
esphome/components/ct_clamp/* @jesserockz
+6
View File
@@ -0,0 +1,6 @@
import esphome.codegen as cg
CODEOWNERS = ["@latonita"]
DEPENDENCIES = ["i2c"]
cst328_ns = cg.esphome_ns.namespace("cst328")
@@ -0,0 +1,28 @@
import esphome.codegen as cg
from esphome.components import binary_sensor
import esphome.config_validation as cv
from .. import cst328_ns
from ..touchscreen import CST328ButtonListener, CST328Touchscreen
CONF_CST328_ID = "cst328_id"
CST328Button = cst328_ns.class_(
"CST328Button",
binary_sensor.BinarySensor,
cg.Component,
CST328ButtonListener,
cg.Parented.template(CST328Touchscreen),
)
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(CST328Button).extend(
{
cv.GenerateID(CONF_CST328_ID): cv.use_id(CST328Touchscreen),
}
)
async def to_code(config):
var = await binary_sensor.new_binary_sensor(config)
await cg.register_component(var, config)
await cg.register_parented(var, config[CONF_CST328_ID])
@@ -0,0 +1,16 @@
#include "cst328_button.h"
#include "esphome/core/log.h"
namespace esphome::cst328 {
static const char *const TAG = "cst328.binary_sensor";
void CST328Button::setup() {
this->parent_->register_button_listener(this);
this->publish_initial_state(false);
}
void CST328Button::dump_config() { LOG_BINARY_SENSOR("", "CST328 Button", this); }
void CST328Button::update_button(bool state) { this->publish_state(state); }
} // namespace esphome::cst328
@@ -0,0 +1,20 @@
#pragma once
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "../touchscreen/cst328_touchscreen.h"
namespace esphome::cst328 {
class CST328Button : public binary_sensor::BinarySensor,
public Component,
public CST328ButtonListener,
public Parented<CST328Touchscreen> {
public:
void setup() override;
void dump_config() override;
void update_button(bool state) override;
};
} // namespace esphome::cst328
@@ -0,0 +1,38 @@
from esphome import pins
import esphome.codegen as cg
from esphome.components import i2c, touchscreen
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN
from .. import cst328_ns
CST328Touchscreen = cst328_ns.class_(
"CST328Touchscreen",
touchscreen.Touchscreen,
i2c.I2CDevice,
)
CST328ButtonListener = cst328_ns.class_("CST328ButtonListener")
CONFIG_SCHEMA = (
touchscreen.touchscreen_schema("100ms")
.extend(
{
cv.GenerateID(): cv.declare_id(CST328Touchscreen),
cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
}
)
.extend(i2c.i2c_device_schema(0x1A))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await touchscreen.register_touchscreen(var, config)
await i2c.register_i2c_device(var, config)
if interrupt_pin := config.get(CONF_INTERRUPT_PIN):
cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin)))
if reset_pin := config.get(CONF_RESET_PIN):
cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin)))
@@ -0,0 +1,168 @@
#include "cst328_touchscreen.h"
#include "esphome/core/log.h"
namespace esphome::cst328 {
static const char *const TAG = "cst328.touchscreen";
static const uint32_t CST328_BEFORE_RESET_TIMEOUT = 50; // 50 ms from datasheet
static const uint32_t CST328_TRANSITION_TIMEOUT = 300; // 200 ms from datasheet, but typically much less
static const uint16_t CST328_FW_CRC = 0xCACA; // Expected firmware CRC value
static const uint8_t CST328_SYNC_BYTE = 0xAB; // Sync byte used in communication
static const uint8_t ZERO_BYTE = 0;
#define I2C_WARN_ON_ERROR(x, log_tag, format, ...) \
do { \
i2c::ErrorCode err_rc_ = (x); \
if (err_rc_ != i2c::ERROR_OK) { \
ESP_LOGW(log_tag, "%s(%d): [error %d] " format, __FUNCTION__, __LINE__, err_rc_, ##__VA_ARGS__); \
this->status_set_warning(format); \
} \
} while (0)
#define I2C_FAIL_ON_ERROR(x, log_tag, format, ...) \
do { \
i2c::ErrorCode err_rc_ = (x); \
if (err_rc_ != i2c::ERROR_OK) { \
ESP_LOGE(log_tag, "%s(%d): [error %d] " format, __FUNCTION__, __LINE__, err_rc_, ##__VA_ARGS__); \
this->mark_failed(); \
return; \
} \
} while (0)
void CST328Touchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up CST328 Touchscreen...");
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);
this->set_timeout(CST328_BEFORE_RESET_TIMEOUT, [this] { this->reset_device_(); });
} else {
this->continue_setup_();
}
}
void CST328Touchscreen::reset_device_() {
this->reset_pin_->digital_write(false);
delay(5);
this->reset_pin_->digital_write(true);
this->set_timeout(CST328_TRANSITION_TIMEOUT, [this] { this->continue_setup_(); });
}
void CST328Touchscreen::continue_setup_() {
ESP_LOGV(TAG, "Continuing CST328 setup...");
uint8_t data_byte{0};
uint8_t buf[24]{};
I2C_FAIL_ON_ERROR(this->write_register16(CST_WM_DEBUG_INFO, buf, 0), TAG, "Failed to enter debug/info mode");
I2C_FAIL_ON_ERROR(this->read_register16(CST_REG_FW_CRC_AND_BOOT_TIME, buf, 4), TAG,
"Failed to read FW CRC and boot time");
uint16_t fw_crc = buf[2] + (buf[3] << 8);
if (fw_crc != CST328_FW_CRC) {
ESP_LOGE(TAG, "Error: Firmware CRC mismatch, expected 0x%04X but got 0x%04X", CST328_FW_CRC, fw_crc);
this->mark_failed();
return;
}
I2C_FAIL_ON_ERROR(this->read_register16(CST_REG_CHIP_TYPE_AND_PROJECT_ID, buf, 4), TAG,
"Failed to read chip and project ID");
this->chip_id_ = buf[2] + (buf[3] << 8);
this->project_id_ = buf[0] + (buf[1] << 8);
ESP_LOGD(TAG, "Chip ID %X, project ID %X", this->chip_id_, this->project_id_);
I2C_FAIL_ON_ERROR(this->read_register16(CST_REG_FW_REVISION, buf, 4), TAG, "Failed to read FW version");
this->fw_ver_major_ = buf[3];
this->fw_ver_minor_ = buf[2];
this->fw_build_ = buf[0] + (buf[1] << 8);
ESP_LOGV(TAG, "FW version %d.%d.%d", this->fw_ver_major_, this->fw_ver_minor_, this->fw_build_);
if (i2c::ERROR_OK == this->read_register16(CST_REG_X_Y_RESOLUTION, buf, 4)) {
this->x_raw_max_ = buf[0] + (buf[1] << 8);
this->y_raw_max_ = buf[2] + (buf[3] << 8);
} else {
this->x_raw_max_ = this->display_->get_native_width();
this->y_raw_max_ = this->display_->get_native_height();
}
I2C_WARN_ON_ERROR(this->write_register16(CST_WM_NORMAL, buf, 0), TAG, "Failed to enter normal mode");
I2C_WARN_ON_ERROR(this->read_register16(CST_REG_TOUCH_INFORMATION, &data_byte, 1), TAG, "Failed to read sync");
I2C_WARN_ON_ERROR(this->write_register16(CST_REG_TOUCH_INFORMATION, &CST328_SYNC_BYTE, 1), TAG,
"Failed to write sync");
if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->setup();
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
}
this->setup_complete_ = true;
ESP_LOGV(TAG, "CST328 setup complete");
}
void CST328Touchscreen::dump_config() {
ESP_LOGCONFIG(TAG, "CST328 Touchscreen:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Chip ID: 0x%04X, Project ID: 0x%04X", this->chip_id_, this->project_id_);
ESP_LOGCONFIG(TAG, " FW version: %d.%d.%d", this->fw_ver_major_, this->fw_ver_minor_, this->fw_build_);
ESP_LOGCONFIG(TAG, " X/Y resolution: %d/%d", this->x_raw_max_, this->y_raw_max_);
}
void CST328Touchscreen::update_button_state_(bool state) {
if (this->button_touched_ == state) {
return;
}
this->button_touched_ = state;
for (auto *listener : this->button_listeners_) {
listener->update_button(state);
}
}
void CST328Touchscreen::update_touches() {
if (!this->setup_complete_) {
this->skip_update_ = true;
return;
}
uint8_t touch_data[CST328_TOUCH_DATA_SIZE];
this->status_clear_warning();
if (i2c::ERROR_OK != this->read_register16(CST_REG_TOUCH_INFORMATION, touch_data, CST328_TOUCH_DATA_SIZE)) {
ESP_LOGW(TAG, "Failed to read touch data");
this->status_set_warning();
this->skip_update_ = true;
return;
}
uint8_t touch_cnt = touch_data[CST_REG_FINGER_COUNT_IDX] & 0x0F;
if (touch_cnt == 0 || touch_cnt > CST328_TOUCH_MAX_POINTS) {
this->update_button_state_(false);
} else {
this->update_button_state_(true);
uint8_t data_idx = 0;
for (uint8_t i = 0; i < touch_cnt; i++) {
uint8_t id = touch_data[data_idx] >> 4;
int16_t x = (touch_data[data_idx + 1] << 4) | ((touch_data[data_idx + 3] >> 4) & 0x0F);
int16_t y = (touch_data[data_idx + 2] << 4) | (touch_data[data_idx + 3] & 0x0F);
int16_t z = touch_data[data_idx + 4];
this->add_raw_touch_position_(id, x, y, z);
data_idx += (i == 0) ? 7 : 5;
}
}
bool cleanup_error = false;
cleanup_error |= (i2c::ERROR_OK != this->write_register16(CST_REG_TOUCH_FINGER_NUMBER, &ZERO_BYTE, 1));
cleanup_error |= (i2c::ERROR_OK != this->write_register16(CST_REG_TOUCH_INFORMATION, &CST328_SYNC_BYTE, 1));
if (cleanup_error) {
ESP_LOGW(TAG, "Failed to clean up touch registers");
}
}
} // namespace esphome::cst328
@@ -0,0 +1,61 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/touchscreen/touchscreen.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
namespace esphome::cst328 {
static const uint8_t CST328_TOUCH_MAX_POINTS = 5;
static const uint8_t CST328_TOUCH_DATA_SIZE = CST328_TOUCH_MAX_POINTS * 5 + 2;
static const uint16_t CST_REG_TOUCH_INFORMATION = 0xD000;
static const uint16_t CST_REG_TOUCH_FINGER_NUMBER = 0xD005;
static const uint16_t CST_REG_FINGER_COUNT_IDX = CST_REG_TOUCH_FINGER_NUMBER - CST_REG_TOUCH_INFORMATION;
static const uint16_t CST_REG_X_Y_RESOLUTION = 0xD1F8;
static const uint16_t CST_REG_FW_CRC_AND_BOOT_TIME = 0xD1FC;
static const uint16_t CST_REG_CHIP_TYPE_AND_PROJECT_ID = 0xD204;
static const uint16_t CST_REG_FW_REVISION = 0xD208;
static const uint16_t CST_WM_DEBUG_INFO = 0xD101;
static const uint16_t CST_WM_NORMAL = 0xD109;
class CST328ButtonListener {
public:
virtual void update_button(bool state) = 0;
};
class CST328Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice {
public:
void setup() override;
void register_button_listener(CST328ButtonListener *listener) { this->button_listeners_.push_back(listener); }
void dump_config() override;
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; }
protected:
void update_touches() override;
void reset_device_();
void continue_setup_();
void update_button_state_(bool state);
InternalGPIOPin *interrupt_pin_{};
GPIOPin *reset_pin_{};
std::vector<CST328ButtonListener *> button_listeners_;
bool button_touched_{};
uint16_t chip_id_{};
uint16_t project_id_{};
uint8_t fw_ver_major_{};
uint8_t fw_ver_minor_{};
uint16_t fw_build_{};
bool setup_complete_{};
};
} // namespace esphome::cst328
+22
View File
@@ -0,0 +1,22 @@
display:
- platform: ssd1306_i2c
i2c_id: i2c_bus
id: cst328_ssd1306_i2c_display
model: SSD1306_128X64
reset_pin: ${display_reset_pin}
pages:
- id: cst328_page1
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
touchscreen:
- platform: cst328
i2c_id: i2c_bus
id: cst328_touchscreen
display: cst328_ssd1306_i2c_display
interrupt_pin: ${interrupt_pin}
reset_pin: ${reset_pin}
binary_sensor:
- platform: cst328
id: touch_key_cst328
@@ -0,0 +1,8 @@
substitutions:
display_reset_pin: "4"
interrupt_pin: "20"
reset_pin: "21"
packages:
- !include ../../test_build_components/common/i2c/esp32-idf.yaml
- !include common.yaml