diff --git a/esphome/components/chsc5816/README.md b/esphome/components/chsc5816/README.md new file mode 100644 index 0000000000..e570c5d860 --- /dev/null +++ b/esphome/components/chsc5816/README.md @@ -0,0 +1,11 @@ +# CHSC5816 work-in-progress + +*This is the start of a CHSC5816 touchscreen component.* + +So far this is non-functional, but it does at least seem to be getting *some* messages from the I2C bus, although they don't seem to be valid/correct. + +This component is based off of the `cst816` with a simple find and replace for the naming, and the commands / constants from [lewisxhe/SensorLib](https://github.com/lewisxhe/SensorLib/blob/master/src/TouchDrvCHSC5816.hpp). + +Thus far, I've only tried to read the `CHSC5816_REG_BOOT_STATE`, which should contain the `CHSC5816_SIG_VALUE`, but it does not, nor, (as far as I can tell) a version of it with various bits/bytes swapped or in different orders, but that may need to be explored further. + +The only documentation I've been able to find is [this datasheet](https://github.com/lewisxhe/SensorLib/blob/master/datasheet/CHSC5816%E8%A7%A6%E6%8E%A7%E8%8A%AF%E7%89%87%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8EV1-20221114.pdf) and there is not much info in there. diff --git a/esphome/components/chsc5816/__init__.py b/esphome/components/chsc5816/__init__.py new file mode 100644 index 0000000000..ad6332f1f8 --- /dev/null +++ b/esphome/components/chsc5816/__init__.py @@ -0,0 +1,6 @@ +import esphome.codegen as cg + +CODEOWNERS = ["@pzich"] +DEPENDENCIES = ["i2c"] + +chsc5816_ns = cg.esphome_ns.namespace("chsc5816") diff --git a/esphome/components/chsc5816/binary_sensor/__init__.py b/esphome/components/chsc5816/binary_sensor/__init__.py new file mode 100644 index 0000000000..ea7f1c5f01 --- /dev/null +++ b/esphome/components/chsc5816/binary_sensor/__init__.py @@ -0,0 +1,28 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +import esphome.config_validation as cv + +from .. import chsc5816_ns +from ..touchscreen import CHSC5816ButtonListener, CHSC5816Touchscreen + +CONF_CHSC5816_ID = "chsc5816_id" + +CHSC5816Button = chsc5816_ns.class_( + "CHSC5816Button", + binary_sensor.BinarySensor, + cg.Component, + CHSC5816ButtonListener, + cg.Parented.template(CHSC5816Touchscreen), +) + +CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(CHSC5816Button).extend( + { + cv.GenerateID(CONF_CHSC5816_ID): cv.use_id(CHSC5816Touchscreen), + } +) + + +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_CHSC5816_ID]) diff --git a/esphome/components/chsc5816/binary_sensor/chsc5816_button.h b/esphome/components/chsc5816/binary_sensor/chsc5816_button.h new file mode 100644 index 0000000000..cccc756b0f --- /dev/null +++ b/esphome/components/chsc5816/binary_sensor/chsc5816_button.h @@ -0,0 +1,27 @@ +#pragma once + +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/chsc5816/touchscreen/chsc5816_touchscreen.h" +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace chsc5816 { + +class CHSC5816Button : public binary_sensor::BinarySensor, + public Component, + public CHSC5816ButtonListener, + public Parented { + public: + void setup() override { + this->parent_->register_button_listener(this); + this->publish_initial_state(false); + } + + void dump_config() override { LOG_BINARY_SENSOR("", "CHSC5816 Button", this); } + + void update_button(bool state) override { this->publish_state(state); } +}; + +} // namespace chsc5816 +} // namespace esphome diff --git a/esphome/components/chsc5816/touchscreen/CHSC5816Constants.h b/esphome/components/chsc5816/touchscreen/CHSC5816Constants.h new file mode 100644 index 0000000000..893dbe5b0c --- /dev/null +++ b/esphome/components/chsc5816/touchscreen/CHSC5816Constants.h @@ -0,0 +1,66 @@ +/** + * + * @license MIT License + * + * Copyright (c) 2022 lewis he + * + * 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. + * + * @file CHSC5816Constants.h + * @author Lewis He (lewishe@outlook.com) + * @date 2023-04-17 + * + */ + +#pragma once + +#define CHSC5816_SLAVE_ADDRESS (0x2E) + +#define CHSC5816_REG_CMD_BUFF (0x20000000U) +#define CHSC5816_REG_RSP_BUFF (0x20000000U) +#define CHSC5816_REG_IMG_HEAD (0x20000014U) +// #define CHSC5816_REG_IMG_HEAD (0x14000020U) +#define CHSC5816_REG_POINT (0x2000002CU) +#define CHSC5816_REG_WR_BUFF (0x20002000U) +#define CHSC5816_REG_RD_BUFF (0x20002400U) +#define CHSC5816_REG_HOLD_MCU (0x40007000U) +#define CHSC5816_REG_AUTO_FEED (0x40007010U) +#define CHSC5816_REG_REMAP_MCU (0x40007000U) +#define CHSC5816_REG_RELEASE_MCU (0x40007000U) +#define CHSC5816_REG_BOOT_STATE (0x20000018U) +// #define CHSC5816_REG_BOOT_STATE (0x18000020U) + +#define CHSC5816_HOLD_MCU_VAL (0x12044000U) +#define CHSC5816_AUTO_FEED_VAL (0x0000925aU) +#define CHSC5816_REMAP_MCU_VAL (0x12044002U) +#define CHSC5816_RELEASE_MCU_VAL (0x12044003U) + +#define CHSC5816_REG_VID_PID_BACKUP (40 * 1024 + 0x10U) + +#define CHSC5816_SIG_VALUE (0x43534843U) +/*ctp work staus*/ +#define CHSC5816_POINTING_WORK (0x00000000U) +#define CHSC5816_READY_UPGRADE (1 << 1) +#define CHSC5816_UPGRAD_RUNING (1 << 2) +#define CHSC5816_SLFTEST_RUNING (1 << 3) +#define CHSC5816_SUSPEND_GATE (1 << 16) +#define CHSC5816_GUESTURE_GATE (1 << 17) +#define CHSC5816_PROXIMITY_GATE (1 << 18) +#define CHSC5816_GLOVE_GATE (1 << 19) +#define CHSC5816_ORIENTATION_GATE (1 << 20) diff --git a/esphome/components/chsc5816/touchscreen/__init__.py b/esphome/components/chsc5816/touchscreen/__init__.py new file mode 100644 index 0000000000..17276a9969 --- /dev/null +++ b/esphome/components/chsc5816/touchscreen/__init__.py @@ -0,0 +1,36 @@ +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 chsc5816_ns + +CHSC5816Touchscreen = chsc5816_ns.class_( + "CHSC5816Touchscreen", + touchscreen.Touchscreen, + i2c.I2CDevice, +) + +CHSC5816ButtonListener = chsc5816_ns.class_("CHSC5816ButtonListener") +CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(CHSC5816Touchscreen), + 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(0x15)) + + +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 only... + # cg.add_library("lewisxhe/SensorLib", None) + + 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))) diff --git a/esphome/components/chsc5816/touchscreen/chsc5816_touchscreen.cpp b/esphome/components/chsc5816/touchscreen/chsc5816_touchscreen.cpp new file mode 100644 index 0000000000..0127d8d53f --- /dev/null +++ b/esphome/components/chsc5816/touchscreen/chsc5816_touchscreen.cpp @@ -0,0 +1,137 @@ +#include "chsc5816_touchscreen.h" +#include "CHSC5816Constants.h" + +namespace esphome { +namespace chsc5816 { + +int CHSC5816Touchscreen::writeRegister(uint32_t reg, uint8_t *buf, uint8_t length, bool stop = true) { + i2c::WriteBuffer buffers[2]; + buffers[0].data = reinterpret_cast(reg); + buffers[0].len = __reg_addr_len; + buffers[1].data = buf; + buffers[1].len = length; + return this->bus_->writev(this->address_, buffers, 2, stop); +} + +int CHSC5816Touchscreen::readRegister(uint32_t reg, uint8_t *buf, uint8_t length, bool stop = true) { + this->write(reinterpret_cast(reg), __reg_addr_len, stop); + return bus_->read(address_, buf, length); +} + +void CHSC5816Touchscreen::reset() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->digital_write(false); + delay(5); + this->reset_pin_->digital_write(true); + delay(5); + } +} + +bool CHSC5816Touchscreen::checkOnline() { + CHSC5816_Header_t tmp; + memset(&tmp, 0, sizeof(CHSC5816_Header_t)); + memset(&__header, 0, sizeof(CHSC5816_Header_t)); + + uint32_t bootClean = 0x00; + writeRegister(CHSC5816_REG_BOOT_STATE, (uint8_t *) &bootClean, 4); + + this->reset(); + + for (int i = 0; i < 10; ++i) { + esph_log_config(TAG, "ATTEMPT %d", i); + delay(10); + readRegister(CHSC5816_REG_IMG_HEAD, (uint8_t *) &__header, sizeof(CHSC5816_Header_t)); + readRegister(CHSC5816_REG_IMG_HEAD, (uint8_t *) &tmp, sizeof(CHSC5816_Header_t)); + + esph_log_config(TAG, "H1 %s", + format_hex_pretty(reinterpret_cast(&__header), sizeof(CHSC5816_Header_t)).c_str()); + esph_log_config(TAG, "H2 %s", + format_hex_pretty(reinterpret_cast(&tmp), sizeof(CHSC5816_Header_t)).c_str()); + if (memcmp(&tmp, &__header, sizeof(CHSC5816_Header_t)) != 0) { + continue; + } + if (__header.sig == CHSC5816_SIG_VALUE) { + return true; + } + } + return false; +} + +void CHSC5816Touchscreen::continue_setup_() { + if (this->interrupt_pin_ != nullptr) { + this->interrupt_pin_->setup(); + this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); + } + if (!this->checkOnline()) { + esph_log_e(TAG, "Failed to setup"); + } else { + esph_log_config(TAG, "CHSC5816 Touchscreen setup complete"); + } + + return; // Remove this after we're successfully online + + this->write_byte(REG_IRQ_CTL, IRQ_EN_MOTION); + if (this->x_raw_max_ == this->x_raw_min_) { + this->x_raw_max_ = this->display_->get_native_width(); + } + if (this->y_raw_max_ == this->y_raw_min_) { + this->y_raw_max_ = this->display_->get_native_height(); + } + esph_log_config(TAG, "CHSC5816 Touchscreen setup complete"); +} + +void CHSC5816Touchscreen::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 CHSC5816Touchscreen::setup() { + esph_log_config(TAG, "Setting up CHSC5816 Touchscreen..."); + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + } + + this->reset(); + + if (this->reset_pin_ != nullptr) { + // this->set_timeout(30, [this] { this->continue_setup_(); }); + this->set_timeout(10, [this] { this->continue_setup_(); }); + } else { + this->continue_setup_(); + } +} + +void CHSC5816Touchscreen::update_touches() { + uint8_t data[13]; + if (!this->read_bytes(REG_STATUS, data, sizeof data)) { + this->status_set_warning(); + return; + } + uint8_t num_of_touches = data[REG_TOUCH_NUM] & 3; + if (num_of_touches == 0) { + this->update_button_state_(false); + return; + } + + uint16_t x = encode_uint16(data[REG_XPOS_HIGH] & 0xF, data[REG_XPOS_LOW]); + uint16_t y = encode_uint16(data[REG_YPOS_HIGH] & 0xF, data[REG_YPOS_LOW]); + esph_log_v(TAG, "Read touch %d/%d", x, y); + if (x >= this->x_raw_max_) { + this->update_button_state_(true); + } else { + this->add_raw_touch_position_(0, x, y); + } +} + +void CHSC5816Touchscreen::dump_config() { + ESP_LOGCONFIG(TAG, "CHSC5816 Touchscreen:"); + LOG_I2C_DEVICE(this); + LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); +} + +} // namespace chsc5816 +} // namespace esphome diff --git a/esphome/components/chsc5816/touchscreen/chsc5816_touchscreen.h b/esphome/components/chsc5816/touchscreen/chsc5816_touchscreen.h new file mode 100644 index 0000000000..3006069a2e --- /dev/null +++ b/esphome/components/chsc5816/touchscreen/chsc5816_touchscreen.h @@ -0,0 +1,104 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/touchscreen/touchscreen.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace chsc5816 { + +typedef struct __CHSC5816_Header { + uint16_t fw_ver; + uint16_t checksum; + uint32_t sig; + uint32_t vid_pid; + uint16_t raw_offet; + uint16_t dif_offet; +} CHSC5816_Header_t; + +/* +uint16_t fw_ver; 00 00 +uint16_t checksum; 00 00 +uint32_t sig; 00 00 00 00 +uint32_t vid_pid; 00 00 00 00 +uint16_t raw_offet; 00 00 +uint16_t dif_offet; 00 00 +*/ + +union __CHSC5816_PointReg { + struct { + uint8_t status; + uint8_t fingerNumber; + uint8_t x_l8; + uint8_t y_l8; + uint8_t z; + uint8_t x_h4 : 4; + uint8_t y_h4 : 4; + uint8_t id : 4; + uint8_t event : 4; + uint8_t p2; + } rp; + unsigned char data[8]; +}; + +static const char *const TAG = "chsc5816.touchscreen"; + +static const uint8_t REG_STATUS = 0x00; +static const uint8_t REG_TOUCH_NUM = 0x02; +static const uint8_t REG_XPOS_HIGH = 0x03; +static const uint8_t REG_XPOS_LOW = 0x04; +static const uint8_t REG_YPOS_HIGH = 0x05; +static const uint8_t REG_YPOS_LOW = 0x06; +static const uint8_t REG_DIS_AUTOSLEEP = 0xFE; +static const uint8_t REG_CHIP_ID = 0xA7; +static const uint8_t REG_FW_VERSION = 0xA9; +static const uint8_t REG_SLEEP = 0xE5; +static const uint8_t REG_IRQ_CTL = 0xFA; +static const uint8_t IRQ_EN_MOTION = 0x70; + +static const uint8_t CST826_CHIP_ID = 0x11; +static const uint8_t CST820_CHIP_ID = 0xB7; +static const uint8_t CHSC5816S_CHIP_ID = 0xB4; +static const uint8_t CHSC5816D_CHIP_ID = 0xB6; +static const uint8_t CHSC5816T_CHIP_ID = 0xB5; +static const uint8_t CST716_CHIP_ID = 0x20; + +class CHSC5816ButtonListener { + public: + virtual void update_button(bool state) = 0; +}; + +class CHSC5816Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice { + public: + void setup() override; + void update_touches() override; + void register_button_listener(CHSC5816ButtonListener *listener) { this->button_listeners_.push_back(listener); } + void dump_config() override; + + // int readRegister(int reg, uint8_t *buf, uint8_t length, bool stop); + // int writeRegister(int reg, uint8_t *buf, uint8_t length, bool stop); + int readRegister(uint32_t reg, uint8_t *buf, uint8_t length, bool stop); + int writeRegister(uint32_t reg, uint8_t *buf, uint8_t length, bool stop); + void reset(); + bool checkOnline(); + + void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; } + void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; } + + protected: + uint8_t __reg_addr_len = 4; + CHSC5816_Header_t __header; + void continue_setup_(); + void update_button_state_(bool state); + + InternalGPIOPin *interrupt_pin_{}; + GPIOPin *reset_pin_{}; + uint8_t chip_id_{}; + std::vector button_listeners_; + bool button_touched_{}; +}; + +} // namespace chsc5816 +} // namespace esphome