Add the WeiKai SPI/I2C UART/IO Expander components to esphome (#5218)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Jean Louis-Guerin
2024-04-24 03:21:44 +02:00
committed by GitHub
parent f9ce35c894
commit f8cdb087fc
60 changed files with 2874 additions and 0 deletions
+11
View File
@@ -400,10 +400,21 @@ esphome/components/wake_on_lan/* @willwill2will54
esphome/components/waveshare_epaper/* @clydebarrow
esphome/components/web_server_base/* @OttoWinter
esphome/components/web_server_idf/* @dentra
esphome/components/weikai/* @DrCoolZic
esphome/components/weikai_i2c/* @DrCoolZic
esphome/components/weikai_spi/* @DrCoolZic
esphome/components/whirlpool/* @glmnet
esphome/components/whynter/* @aeonsablaze
esphome/components/wiegand/* @ssieb
esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
esphome/components/wk2132_i2c/* @DrCoolZic
esphome/components/wk2132_spi/* @DrCoolZic
esphome/components/wk2168_i2c/* @DrCoolZic
esphome/components/wk2168_spi/* @DrCoolZic
esphome/components/wk2204_i2c/* @DrCoolZic
esphome/components/wk2204_spi/* @DrCoolZic
esphome/components/wk2212_i2c/* @DrCoolZic
esphome/components/wk2212_spi/* @DrCoolZic
esphome/components/wl_134/* @hobbypunk90
esphome/components/x9c/* @EtienneMD
esphome/components/xgzp68xx/* @gcormier
+108
View File
@@ -0,0 +1,108 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import uart
from esphome.const import (
CONF_BAUD_RATE,
CONF_CHANNEL,
CONF_ID,
CONF_INPUT,
CONF_INVERTED,
CONF_MODE,
CONF_NUMBER,
CONF_OUTPUT,
)
CODEOWNERS = ["@DrCoolZic"]
AUTO_LOAD = ["uart"]
MULTI_CONF = True
CONF_STOP_BITS = "stop_bits"
CONF_PARITY = "parity"
CONF_CRYSTAL = "crystal"
CONF_UART = "uart"
CONF_TEST_MODE = "test_mode"
weikai_ns = cg.esphome_ns.namespace("weikai")
WeikaiComponent = weikai_ns.class_("WeikaiComponent", cg.Component)
WeikaiChannel = weikai_ns.class_("WeikaiChannel", uart.UARTComponent)
def check_channel_max(value, max):
channel_uniq = []
channel_dup = []
for x in value[CONF_UART]:
if x[CONF_CHANNEL] > max - 1:
raise cv.Invalid(f"Invalid channel number: {x[CONF_CHANNEL]}")
if x[CONF_CHANNEL] not in channel_uniq:
channel_uniq.append(x[CONF_CHANNEL])
else:
channel_dup.append(x[CONF_CHANNEL])
if len(channel_dup) > 0:
raise cv.Invalid(f"Duplicate channel list: {channel_dup}")
return value
def check_channel_max_4(value):
return check_channel_max(value, 4)
def check_channel_max_2(value):
return check_channel_max(value, 2)
WKBASE_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(WeikaiComponent),
cv.Optional(CONF_CRYSTAL, default=14745600): cv.int_,
cv.Optional(CONF_TEST_MODE, default=0): cv.int_,
cv.Required(CONF_UART): cv.ensure_list(
{
cv.Required(CONF_ID): cv.declare_id(WeikaiChannel),
cv.Optional(CONF_CHANNEL, default=0): cv.int_range(min=0, max=3),
cv.Required(CONF_BAUD_RATE): cv.int_range(min=1),
cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True),
cv.Optional(CONF_PARITY, default="NONE"): cv.enum(
uart.UART_PARITY_OPTIONS, upper=True
),
}
),
}
).extend(cv.COMPONENT_SCHEMA)
async def register_weikai(var, config):
"""Register an weikai device with the given config."""
cg.add(var.set_crystal(config[CONF_CRYSTAL]))
cg.add(var.set_test_mode(config[CONF_TEST_MODE]))
await cg.register_component(var, config)
for uart_elem in config[CONF_UART]:
chan = cg.new_Pvariable(uart_elem[CONF_ID])
cg.add(chan.set_channel_name(str(uart_elem[CONF_ID])))
cg.add(chan.set_parent(var))
cg.add(chan.set_channel(uart_elem[CONF_CHANNEL]))
cg.add(chan.set_baud_rate(uart_elem[CONF_BAUD_RATE]))
cg.add(chan.set_stop_bits(uart_elem[CONF_STOP_BITS]))
cg.add(chan.set_parity(uart_elem[CONF_PARITY]))
def validate_pin_mode(value):
"""Checks input/output mode inconsistency"""
if not (value[CONF_MODE][CONF_INPUT] or value[CONF_MODE][CONF_OUTPUT]):
raise cv.Invalid("Mode must be either input or output")
if value[CONF_MODE][CONF_INPUT] and value[CONF_MODE][CONF_OUTPUT]:
raise cv.Invalid("Mode must be either input or output")
return value
WEIKAI_PIN_SCHEMA = cv.Schema(
{
cv.Required(CONF_NUMBER): cv.int_range(min=0, max=7),
cv.Optional(CONF_MODE, default={}): cv.All(
{
cv.Optional(CONF_INPUT, default=False): cv.boolean,
cv.Optional(CONF_OUTPUT, default=False): cv.boolean,
},
),
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
}
)
File diff suppressed because it is too large Load Diff
+443
View File
@@ -0,0 +1,443 @@
/// @file weikai.h
/// @author DrCoolZic
/// @brief WeiKai component family - classes declaration
/// @date Last Modified: 2024/04/06 14:44:17
/// @details The classes declared in this file can be used by the Weikai family
/// of UART and GPIO expander components. As of today it provides support for
/// wk2124_spi, wk2132_spi, wk2168_spi, wk2204_spi, wk2212_spi,
/// wk2132_i2c, wk2168_i2c, wk2204_i2c, wk2212_i2c
#pragma once
#include <bitset>
#include <memory>
#include <cinttypes>
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
#include "wk_reg_def.h"
/// When the TEST_COMPONENT flag is defined we include some auto-test methods. Used to test the software during
/// development but can also be used in situ to test if the component is working correctly. For release we do
/// not set it by default but you can set it by using the following lines in you configuration file:
/// @code
/// esphome:
/// platformio_options:
/// build_flags:
/// - -DTEST_COMPONENT
/// @endcode
// #define TEST_COMPONENT
namespace esphome {
namespace weikai {
/// @brief XFER_MAX_SIZE defines the maximum number of bytes allowed during one transfer.
#if defined(I2C_BUFFER_LENGTH)
constexpr size_t XFER_MAX_SIZE = I2C_BUFFER_LENGTH;
#else
constexpr size_t XFER_MAX_SIZE = 128;
#endif
/// @brief size of the internal WeiKai FIFO
constexpr size_t FIFO_SIZE = 256;
/// @brief size of the ring buffer set to size of the FIFO
constexpr size_t RING_BUFFER_SIZE = FIFO_SIZE;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief This is an helper class that provides a simple ring buffers that works as a FIFO
/// @details This ring buffer is used to buffer the bytes received in the FIFO of the Weika device. The best way to read
/// characters from the device FIFO, is to first check how many bytes were received and then read them all at once.
/// Unfortunately in all the code I have reviewed the characters are read one by one in a while loop by checking if
/// bytes are available then reading the byte until no more byte available. This is pretty inefficient for two reasons:
/// - Fist you need to perform a test for each byte to read
/// - and second you call the read byte method for each character.
/// .
/// Assuming you need to read 100 bytes that results into 200 calls. This is to compare to 2 calls (one to find the
/// number of bytes available plus one to read all the bytes) in the best case! If the registers you read are located on
/// the micro-controller this is acceptable because the registers can be accessed fast. But when the registers are
/// located on a remote device accessing them requires several cycles on a slow bus. As it it not possible to fix this
/// problem by asking users to rewrite their code, I have implemented this ring buffer that store the bytes received
/// locally.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T, size_t SIZE> class WKRingBuffer {
public:
/// @brief pushes an item at the tail of the fifo
/// @param item item to push
/// @return true if item has been pushed, false il item could not pushed (buffer full)
bool push(const T item) {
if (is_full())
return false;
this->rb_[this->head_] = item;
this->head_ = (this->head_ + 1) % SIZE;
this->count_++;
return true;
}
/// @brief return and remove the item at head of the fifo
/// @param item item read
/// @return true if an item has been retrieved, false il no item available (buffer empty)
bool pop(T &item) {
if (is_empty())
return false;
item = this->rb_[this->tail_];
this->tail_ = (this->tail_ + 1) % SIZE;
this->count_--;
return true;
}
/// @brief return the value of the item at fifo's head without removing it
/// @param item pointer to item to return
/// @return true if item has been retrieved, false il no item available (buffer empty)
bool peek(T &item) {
if (is_empty())
return false;
item = this->rb_[this->tail_];
return true;
}
/// @brief test is the Ring Buffer is empty ?
/// @return true if empty
inline bool is_empty() { return (this->count_ == 0); }
/// @brief test is the ring buffer is full ?
/// @return true if full
inline bool is_full() { return (this->count_ == SIZE); }
/// @brief return the number of item in the ring buffer
/// @return the number of items
inline size_t count() { return this->count_; }
/// @brief returns the number of free positions in the buffer
/// @return how many items can be added
inline size_t free() { return SIZE - this->count_; }
/// @brief clear the buffer content
inline void clear() { this->head_ = this->tail_ = this->count_ = 0; }
protected:
std::array<T, SIZE> rb_{0}; ///< the ring buffer
int tail_{0}; ///< position of the next element to read
int head_{0}; ///< position of the next element to write
size_t count_{0}; ///< count number of element in the buffer
};
class WeikaiComponent;
// class WeikaiComponentSPI;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief WeikaiRegister objects acts as proxies to access remote register independently of the bus type.
/// @details This is an abstract interface class that provides many operations to access to registers while hiding
/// the actual implementation. This allow to accesses the registers in the Weikai component abstract class independently
/// of the actual bus (I2C, SPI). The derived classes will actually implements the specific bus operations dependant of
/// the bus used.
/// @n typical usage of WeikaiRegister:
/// @code
/// WeikaiRegister reg_X {&WeikaiComponent, ADDR_REGISTER_X, CHANNEL_NUM} // declaration
/// reg_X |= 0x01; // set bit 0 of the weikai register
/// reg_X &= ~0x01; // reset bit 0 of the weikai register
/// reg_X = 10; // Set the value of weikai register
/// uint val = reg_X; // get the value of weikai register
/// @endcode
class WeikaiRegister {
public:
/// @brief WeikaiRegister constructor.
/// @param comp our parent WeikaiComponent
/// @param reg address of the register
/// @param channel the channel of this register
WeikaiRegister(WeikaiComponent *const comp, uint8_t reg, uint8_t channel)
: comp_(comp), register_(reg), channel_(channel) {}
virtual ~WeikaiRegister() {}
/// @brief overloads the = operator. This is used to set a value into the weikai register
/// @param value to be set
/// @return this object
WeikaiRegister &operator=(uint8_t value);
/// @brief overloads the compound &= operator. This is often used to reset bits in the weikai register
/// @param value performs an & operation with value and store the result
/// @return this object
WeikaiRegister &operator&=(uint8_t value);
/// @brief overloads the compound |= operator. This is often used to set bits in the weikai register
/// @param value performs an | operation with value and store the result
/// @return this object
WeikaiRegister &operator|=(uint8_t value);
/// @brief cast operator that returns the content of the weikai register
operator uint8_t() const { return read_reg(); }
/// @brief reads the register
/// @return the value read from the register
virtual uint8_t read_reg() const = 0;
/// @brief writes the register
/// @param value to write in the register
virtual void write_reg(uint8_t value) = 0;
/// @brief read an array of bytes from the receiver fifo
/// @param data pointer to data buffer
/// @param length number of bytes to read
virtual void read_fifo(uint8_t *data, size_t length) const = 0;
/// @brief write an array of bytes to the transmitter fifo
/// @param data pointer to data buffer
/// @param length number of bytes to write
virtual void write_fifo(uint8_t *data, size_t length) = 0;
WeikaiComponent *const comp_; ///< pointer to our parent (aggregation)
uint8_t register_; ///< address of the register
uint8_t channel_; ///< channel for this register
};
class WeikaiChannel; // forward declaration
////////////////////////////////////////////////////////////////////////////////////
/// @brief The WeikaiComponent class stores the information global to the WeiKai component
/// and provides methods to set/access this information. It is also the container of
/// the WeikaiChannel children objects. This class is derived from esphome::Component
/// class.
////////////////////////////////////////////////////////////////////////////////////
class WeikaiComponent : public Component {
public:
/// @brief virtual destructor
virtual ~WeikaiComponent() {}
/// @brief store crystal frequency
/// @param crystal frequency
void set_crystal(uint32_t crystal) { this->crystal_ = crystal; }
/// @brief store if the component is in test mode
/// @param test_mode 0=normal mode any other values mean component in test mode
void set_test_mode(int test_mode) { this->test_mode_ = test_mode; }
/// @brief store the name for the component
/// @param name the name as defined by the python code generator
void set_name(std::string name) { this->name_ = std::move(name); }
/// @brief Get the name of the component
/// @return the name
const char *get_name() { return this->name_.c_str(); }
/// @brief override the Component loop()
void loop() override;
bool page1() { return page1_; }
/// @brief Factory method to create a Register object
/// @param reg address of the register
/// @param channel channel associated with this register
/// @return a reference to WeikaiRegister
virtual WeikaiRegister &reg(uint8_t reg, uint8_t channel) = 0;
protected:
friend class WeikaiChannel;
/// @brief Get the priority of the component
/// @return the priority
/// @details The priority is set below setup_priority::BUS because we use
/// the spi/i2c busses (which has a priority of BUS) to communicate and the WeiKai
/// therefore it is seen by our client almost as if it was a bus.
float get_setup_priority() const override { return setup_priority::BUS - 0.1F; }
friend class WeikaiGPIOPin;
/// Helper method to read the value of a pin.
bool read_pin_val_(uint8_t pin);
/// Helper method to write the value of a pin.
void write_pin_val_(uint8_t pin, bool value);
/// Helper method to set the pin mode of a pin.
void set_pin_direction_(uint8_t pin, gpio::Flags flags);
#ifdef TEST_COMPONENT
/// @defgroup test_ Test component information
/// @brief Contains information about the auto-tests of the component
/// @{
void test_gpio_input_();
void test_gpio_output_();
/// @}
#endif
uint8_t pin_config_{0x00}; ///< pin config mask: 1 means OUTPUT, 0 means INPUT
uint8_t output_state_{0x00}; ///< output state: 1 means HIGH, 0 means LOW
uint8_t input_state_{0x00}; ///< input pin states: 1 means HIGH, 0 means LOW
uint32_t crystal_; ///< crystal value;
int test_mode_; ///< test mode value (0 -> no tests)
bool page1_{false}; ///< set to true when in "page1 mode"
std::vector<WeikaiChannel *> children_{}; ///< the list of WeikaiChannel UART children
std::string name_; ///< name of entity
};
///////////////////////////////////////////////////////////////////////////////
/// @brief Helper class to expose a WeiKai family IO pin as an internal GPIO pin.
///////////////////////////////////////////////////////////////////////////////
class WeikaiGPIOPin : public GPIOPin {
public:
void set_parent(WeikaiComponent *parent) { this->parent_ = parent; }
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_inverted(bool inverted) { this->inverted_ = inverted; }
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
void setup() override;
std::string dump_summary() const override;
void pin_mode(gpio::Flags flags) override { this->parent_->set_pin_direction_(this->pin_, flags); }
bool digital_read() override { return this->parent_->read_pin_val_(this->pin_) != this->inverted_; }
void digital_write(bool value) override { this->parent_->write_pin_val_(this->pin_, value != this->inverted_); }
protected:
WeikaiComponent *parent_{nullptr};
uint8_t pin_;
bool inverted_;
gpio::Flags flags_;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief The WeikaiChannel class is used to implement all the virtual methods of the ESPHome
/// uart::UARTComponent virtual class. This class is common to the different members of the Weikai
/// components family and therefore avoid code duplication.
///////////////////////////////////////////////////////////////////////////////////////////////////
class WeikaiChannel : public uart::UARTComponent {
public:
/// @brief We belongs to this WeikaiComponent
/// @param parent pointer to the component we belongs to
void set_parent(WeikaiComponent *parent) {
this->parent_ = parent;
this->parent_->children_.push_back(this); // add ourself to the list (vector)
}
/// @brief Sets the channel number
/// @param channel number
void set_channel(uint8_t channel) { this->channel_ = channel; }
/// @brief The name as generated by the Python code generator
/// @param name of the channel
void set_channel_name(std::string name) { this->name_ = std::move(name); }
/// @brief Get the channel name
/// @return the name
const char *get_channel_name() { return this->name_.c_str(); }
/// @brief Setup the channel
void virtual setup_channel();
/// @brief dump channel information
void virtual dump_channel();
/// @brief Factory method to create a WeikaiRegister proxy object
/// @param reg address of the register
/// @return a reference to WeikaiRegister
WeikaiRegister &reg(uint8_t reg) { return this->parent_->reg(reg, channel_); }
//
// we implements/overrides the virtual class from UARTComponent
//
/// @brief Writes a specified number of bytes to a serial port
/// @param buffer pointer to the buffer
/// @param length number of bytes to write
/// @details This method sends 'length' characters from the buffer to the serial line. Unfortunately (unlike the
/// Arduino equivalent) this method does not return any flag and therefore it is not possible to know if any/all bytes
/// have been transmitted correctly. Another problem is that it is not possible to know ahead of time how many bytes
/// we can safely send as there is no tx_available() method provided! To avoid overrun when using the write method you
/// can use the flush() method to wait until the transmit fifo is empty.
/// @n Typical usage could be:
/// @code
/// // ...
/// uint8_t buffer[128];
/// // ...
/// write_array(&buffer, length);
/// flush();
/// // ...
/// @endcode
void write_array(const uint8_t *buffer, size_t length) override;
/// @brief Reads a specified number of bytes from a serial port
/// @param buffer buffer to store the bytes
/// @param length number of bytes to read
/// @return true if succeed, false otherwise
/// @details Typical usage:
/// @code
/// // ...
/// auto length = available();
/// uint8_t buffer[128];
/// if (length > 0) {
/// auto status = read_array(&buffer, length)
/// // test status ...
/// }
/// @endcode
bool read_array(uint8_t *buffer, size_t length) override;
/// @brief Reads the first byte in FIFO without removing it
/// @param buffer pointer to the byte
/// @return true if succeed reading one byte, false if no character available
/// @details This method returns the next byte from receiving buffer without removing it from the internal fifo. It
/// returns true if a character is available and has been read, false otherwise.\n
bool peek_byte(uint8_t *buffer) override;
/// @brief Returns the number of bytes in the receive buffer
/// @return the number of bytes available in the receiver fifo
int available() override;
/// @brief Flush the output fifo.
/// @details If we refer to Serial.flush() in Arduino it says: ** Waits for the transmission of outgoing serial data
/// to complete. (Prior to Arduino 1.0, this the method was removing any buffered incoming serial data.). ** Therefore
/// we wait until all bytes are gone with a timeout of 100 ms
void flush() override;
protected:
friend class WeikaiComponent;
/// @brief this cannot happen with external uart therefore we do nothing
void check_logger_conflict() override {}
/// @brief reset the weikai internal FIFO
void reset_fifo_();
/// @brief set the line parameters
void set_line_param_();
/// @brief set the baud rate
void set_baudrate_();
/// @brief Returns the number of bytes in the receive fifo
/// @return the number of bytes in the fifo
size_t rx_in_fifo_();
/// @brief Returns the number of bytes in the transmit fifo
/// @return the number of bytes in the fifo
size_t tx_in_fifo_();
/// @brief test if transmit buffer is not empty in the status register (optimization)
/// @return true if not emptygroup test_
bool tx_fifo_is_not_empty_();
/// @brief transfer bytes from the weikai internal FIFO to the buffer (if any)
/// @return number of bytes transferred
size_t xfer_fifo_to_buffer_();
/// @brief check if channel is alive
/// @return true if OK
bool virtual check_channel_down();
#ifdef TEST_COMPONENT
/// @ingroup test_
/// @{
/// @brief Test the write_array() method
/// @param message to display
void uart_send_test_(char *message);
/// @brief Test the read_array() method
/// @param message to display
/// @return true if success
bool uart_receive_test_(char *message);
/// @}
#endif
/// @brief the buffer where we store temporarily the bytes received
WKRingBuffer<uint8_t, RING_BUFFER_SIZE> receive_buffer_;
WeikaiComponent *parent_; ///< our WK2168component parent
uint8_t channel_; ///< our Channel number
uint8_t data_; ///< a one byte buffer for register read storage
std::string name_; ///< name of the entity
};
} // namespace weikai
} // namespace esphome
+304
View File
@@ -0,0 +1,304 @@
/// @file wk_reg_def.h
/// @author DrCoolZic
/// @brief WeiKai component family - registers' definition
/// @date Last Modified: 2024/02/18 15:49:18
#pragma once
namespace esphome {
namespace weikai {
////////////////////////////////////////////////////////////////////////////////////////
/// Definition of the WeiKai registers
////////////////////////////////////////////////////////////////////////////////////////
/// @defgroup wk2168_gr_ WeiKai Global Registers
/// This section groups all **Global Registers**: these registers are global to the
/// the WeiKai chip (i.e. independent of the UART channel used)
/// @note only registers and parameters used have been fully documented
/// @{
/// @brief Global Control Register - 00 0000
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | M0 | M1 | RSV | C4EN | C3EN | C2EN | C1EN | name
/// -------------------------------------------------------------------------
/// | R | R | R | R | W/R | W/R | W/R | W/R | type
/// -------------------------------------------------------------------------
/// | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_GENA = 0x00;
/// @brief Channel 4 enable clock (0: disable, 1: enable)
constexpr uint8_t GENA_C4EN = 1 << 3;
/// @brief Channel 3 enable clock (0: disable, 1: enable)
constexpr uint8_t GENA_C3EN = 1 << 2;
/// @brief Channel 2 enable clock (0: disable, 1: enable)
constexpr uint8_t GENA_C2EN = 1 << 1;
/// @brief Channel 1 enable clock (0: disable, 1: enable)
constexpr uint8_t GENA_C1EN = 1 << 0;
/// @brief Global Reset Register - 00 0001
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | C4SLEEP| C3SLEEP| C2SLEEP| C1SLEEP| C4RST | C3RST | C2RST | C1RST | name
/// -------------------------------------------------------------------------
/// | R | R | R | R | W1/R0 | W1/R0 | W1/R0 | W1/R0 | type
/// -------------------------------------------------------------------------
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_GRST = 0x01;
/// @brief Channel 4 soft reset (0: not reset, 1: reset)
constexpr uint8_t GRST_C4RST = 1 << 3;
/// @brief Channel 3 soft reset (0: not reset, 1: reset)
constexpr uint8_t GRST_C3RST = 1 << 2;
/// @brief Channel 2 soft reset (0: not reset, 1: reset)
constexpr uint8_t GRST_C2RST = 1 << 1;
/// @brief Channel 1 soft reset (0: not reset, 1: reset)
constexpr uint8_t GRST_C1RST = 1 << 0;
/// @brief Global Master channel control register (not used) - 000010
constexpr uint8_t WKREG_GMUT = 0x02;
/// Global interrupt register (not used) - 01 0000
constexpr uint8_t WKREG_GIER = 0x10;
/// Global interrupt flag register (not used) 01 0001
constexpr uint8_t WKREG_GIFR = 0x11;
/// @brief Global GPIO direction register - 10 0001
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | PD7 | PD6 | PD5 | PD4 | PD3 | PD2 | PD1 | PD0 | name
/// -------------------------------------------------------------------------
/// | W/R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
/// -------------------------------------------------------------------------
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_GPDIR = 0x21;
/// @brief Global GPIO data register - 11 0001
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | PV7 | PV6 | PV5 | PV4 | PV3 | PV2 | PV1 | PV0 | name
/// -------------------------------------------------------------------------
/// | W/R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
/// -------------------------------------------------------------------------
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_GPDAT = 0x31;
/// @}
/// @defgroup WeiKai_cr_ WeiKai Channel Registers
/// @brief Definition of the register linked to a particular channel
/// @details This topic groups all the **Channel Registers**: these registers are specific
/// to the a specific channel i.e. each channel has its own set of registers
/// @note only registers and parameters used have been documented
/// @{
/// @defgroup cr_p0 Channel registers when SPAGE=0
/// @brief Definition of the register linked to a particular channel when SPAGE=0
/// @details The channel registers are further splitted into two groups.
/// This first group is defined when the Global register WKREG_SPAGE is 0
/// @{
/// @brief Global Page register c0/c1 0011
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | RSV | PAGE | name
/// -------------------------------------------------------------------------
/// | R | R | R | R | R | R | R | W/R | type
/// -------------------------------------------------------------------------
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_SPAGE = 0x03;
/// @brief Serial Control Register - c0/c1 0100
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | RSV | SLPEN | TXEN | RXEN | name
/// -------------------------------------------------------------------------
/// | R | R | R | R | R | R/W | R/W | W/R | type
/// -------------------------------------------------------------------------
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_SCR = 0x04;
/// @brief transmission control (0: enable, 1: disable)
constexpr uint8_t SCR_TXEN = 1 << 1;
/// @brief receiving control (0: enable, 1: disable)
constexpr uint8_t SCR_RXEN = 1 << 0;
/// @brief Line Configuration Register - c0/c1 0101
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | RSV | BREAK | IREN | PAEN | PARITY | STPL | name
/// -------------------------------------------------------------------------
/// | W/R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
/// -------------------------------------------------------------------------
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_LCR = 0x05;
/// @brief Parity enable (0: no check, 1: check)
constexpr uint8_t LCR_PAEN = 1 << 3;
/// @brief Parity force 0
constexpr uint8_t LCR_PAR_F0 = 0 << 1;
/// @brief Parity odd
constexpr uint8_t LCR_PAR_ODD = 1 << 1;
/// @brief Parity even
constexpr uint8_t LCR_PAR_EVEN = 2 << 1;
/// @brief Parity force 1
constexpr uint8_t LCR_PAR_F1 = 3 << 1;
/// @brief Stop length (0: 1 bit, 1: 2 bits)
constexpr uint8_t LCR_STPL = 1 << 0;
/// @brief FIFO Control Register - c0/c1 0110
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | TFTRIG | RFTRIG | TFEN | RFEN | TFRST | RFRST | name
/// -------------------------------------------------------------------------
/// | W/R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
/// -------------------------------------------------------------------------
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_FCR = 0x06;
/// @brief Transmitter FIFO enable
constexpr uint8_t FCR_TFEN = 1 << 3;
/// @brief Receiver FIFO enable
constexpr uint8_t FCR_RFEN = 1 << 2;
/// @brief Transmitter FIFO reset
constexpr uint8_t FCR_TFRST = 1 << 1;
/// @brief Receiver FIFO reset
constexpr uint8_t FCR_RFRST = 1 << 0;
/// @brief Serial Interrupt Enable Register (not used) - c0/c1 0111
constexpr uint8_t WKREG_SIER = 0x07;
/// @brief Serial Interrupt Flag Register (not used) - c0/c1 1000
constexpr uint8_t WKREG_SIFR = 0x08;
/// @brief Transmitter FIFO Count - c0/c1 1001
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
/// -------------------------------------------------------------------------
/// | NUMBER OF DATA IN TRANSMITTER FIFO |
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_TFCNT = 0x09;
/// @brief Receiver FIFO count - c0/c1 1010
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
/// -------------------------------------------------------------------------
/// | NUMBER OF DATA IN RECEIVER FIFO |
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_RFCNT = 0x0A;
/// @brief FIFO Status Register - c0/c1 1011
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
/// -------------------------------------------------------------------------
/// | RFOE | RFLB | RFFE | RFPE | RFDAT | TFDAT | TFFULL | TBUSY | name
/// -------------------------------------------------------------------------
/// | R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
/// -------------------------------------------------------------------------
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
/// -------------------------------------------------------------------------
/// @endcode
/// @warning The received buffer can hold 256 bytes. However, as the RFCNT reg
/// is 8 bits, if we have 256 byte in the register this is reported as 0 ! Therefore
/// RFCNT=0 can indicate that there are 0 **or** 256 bytes in the buffer. If we
/// have RFDAT = 1 and RFCNT = 0 it should be interpreted as 256 bytes in the FIFO.
/// @note Note that in case of overflow the RFOE goes to one **but** as soon as you read
/// the FSR this bit is cleared. Therefore Overflow can be read only once.
/// @n The same problem applies to the transmit buffer but here we have to check the
/// TFFULL flag. So if TFFULL is set and TFCNT is 0 this should be interpreted as 256
constexpr uint8_t WKREG_FSR = 0x0B;
/// @brief Receiver FIFO Overflow Error (0: no OE, 1: OE)
constexpr uint8_t FSR_RFOE = 1 << 7;
/// @brief Receiver FIFO Line Break (0: no LB, 1: LB)
constexpr uint8_t FSR_RFLB = 1 << 6;
/// @brief Receiver FIFO Frame Error (0: no FE, 1: FE)
constexpr uint8_t FSR_RFFE = 1 << 5;
/// @brief Receiver Parity Error (0: no PE, 1: PE)
constexpr uint8_t FSR_RFPE = 1 << 4;
/// @brief Receiver FIFO count (0: empty, 1: not empty)
constexpr uint8_t FSR_RFDAT = 1 << 3;
/// @brief Transmitter FIFO count (0: empty, 1: not empty)
constexpr uint8_t FSR_TFDAT = 1 << 2;
/// @brief Transmitter FIFO full (0: not full, 1: full)
constexpr uint8_t FSR_TFFULL = 1 << 1;
/// @brief Transmitter busy (0 nothing to transmit, 1: transmitter busy sending)
constexpr uint8_t FSR_TBUSY = 1 << 0;
/// @brief Line Status Register (not used - using FIFO)
constexpr uint8_t WKREG_LSR = 0x0C;
/// @brief FIFO Data Register (not used - does not seems to work)
constexpr uint8_t WKREG_FDAT = 0x0D;
/// @}
/// @defgroup cr_p1 Channel registers for SPAGE=1
/// @brief Definition of the register linked to a particular channel when SPAGE=1
/// @details The channel registers are further splitted into two groups.
/// This second group is defined when the Global register WKREG_SPAGE is 1
/// @{
/// @brief Baud rate configuration register: high byte - c0/c1 0100
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
/// -------------------------------------------------------------------------
/// | High byte of the baud rate |
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_BRH = 0x04;
/// @brief Baud rate configuration register: low byte - c0/c1 0101
/// @details @code
/// -------------------------------------------------------------------------
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
/// -------------------------------------------------------------------------
/// | Low byte of the baud rate |
/// -------------------------------------------------------------------------
/// @endcode
constexpr uint8_t WKREG_BRL = 0x05;
/// @brief Baud rate configuration register decimal part - c0/c1 0110
constexpr uint8_t WKREG_BRD = 0x06;
/// @brief Receive FIFO Interrupt trigger configuration (not used) - c0/c1 0111
constexpr uint8_t WKREG_RFI = 0x07;
/// @brief Transmit FIFO interrupt trigger configuration (not used) - c0/c1 1000
constexpr uint8_t WKREG_TFI = 0x08;
/// @}
/// @}
} // namespace weikai
} // namespace esphome
@@ -0,0 +1 @@
CODEOWNERS = ["@DrCoolZic"]
@@ -0,0 +1,177 @@
/// @file weikai_i2c.cpp
/// @brief WeiKai component family - classes implementation
/// @date Last Modified: 2024/04/06 14:43:31
/// @details The classes declared in this file can be used by the Weikai family
#include "weikai_i2c.h"
namespace esphome {
namespace weikai_i2c {
static const char *const TAG = "weikai_i2c";
/// @brief Display a buffer in hexadecimal format (32 hex values / line).
void print_buffer(const uint8_t *data, size_t length) {
char hex_buffer[100];
hex_buffer[(3 * 32) + 1] = 0;
for (size_t i = 0; i < length; i++) {
snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", data[i]);
if (i % 32 == 31) {
ESP_LOGVV(TAG, " %s", hex_buffer);
}
}
if (length % 32) {
// null terminate if incomplete line
hex_buffer[3 * (length % 32) + 2] = 0;
ESP_LOGVV(TAG, " %s", hex_buffer);
}
}
static const char *const REG_TO_STR_P0[16] = {"GENA", "GRST", "GMUT", "SPAGE", "SCR", "LCR", "FCR", "SIER",
"SIFR", "TFCNT", "RFCNT", "FSR", "LSR", "FDAT", "FWCR", "RS485"};
static const char *const REG_TO_STR_P1[16] = {"GENA", "GRST", "GMUT", "SPAGE", "BAUD1", "BAUD0", "PRES", "RFTL",
"TFTL", "FWTH", "FWTL", "XON1", "XOFF1", "SADR", "SAEN", "RTSDLY"};
using namespace weikai;
// method to print a register value as text: used in the log messages ...
const char *reg_to_str(int reg, bool page1) {
if (reg == WKREG_GPDAT) {
return "GPDAT";
} else if (reg == WKREG_GPDIR) {
return "GPDIR";
} else {
return page1 ? REG_TO_STR_P1[reg & 0x0F] : REG_TO_STR_P0[reg & 0x0F];
}
}
enum RegType { REG = 0, FIFO = 1 }; ///< Register or FIFO
/// @brief Computes the I²C bus's address used to access the component
/// @param base_address the base address of the component - set by the A1 A0 pins
/// @param channel (0-3) the UART channel
/// @param fifo (0-1) 0 = access to internal register, 1 = direct access to fifo
/// @return the i2c address to use
inline uint8_t i2c_address(uint8_t base_address, uint8_t channel, RegType fifo) {
// the address of the device is:
// +----+----+----+----+----+----+----+----+
// | 0 | A1 | A0 | 1 | 0 | C1 | C0 | F |
// +----+----+----+----+----+----+----+----+
// where:
// - A1,A0 is the address read from A1,A0 switch
// - C1,C0 is the channel number (in practice only 00 or 01)
// - F is: 0 when accessing register, one when accessing FIFO
uint8_t const addr = base_address | channel << 1 | fifo << 0;
return addr;
}
///////////////////////////////////////////////////////////////////////////////
// The WeikaiRegisterI2C methods
///////////////////////////////////////////////////////////////////////////////
uint8_t WeikaiRegisterI2C::read_reg() const {
uint8_t value = 0x00;
WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_);
uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, REG);
comp_i2c->set_i2c_address(address);
auto error = comp_i2c->read_register(this->register_, &value, 1);
if (error == i2c::NO_ERROR) {
this->comp_->status_clear_warning();
ESP_LOGVV(TAG, "WeikaiRegisterI2C::read_reg() @%02X reg=%s ch=%u I2C_code:%d, buf=%02X", address,
reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value);
} else { // error
this->comp_->status_set_warning();
ESP_LOGE(TAG, "WeikaiRegisterI2C::read_reg() @%02X reg=%s ch=%u I2C_code:%d, buf=%02X", address,
reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value);
}
return value;
}
void WeikaiRegisterI2C::read_fifo(uint8_t *data, size_t length) const {
WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_);
uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, FIFO);
comp_i2c->set_i2c_address(address);
auto error = comp_i2c->read(data, length);
if (error == i2c::NO_ERROR) {
this->comp_->status_clear_warning();
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
ESP_LOGVV(TAG, "WeikaiRegisterI2C::read_fifo() @%02X ch=%d I2C_code:%d len=%d buffer", address, this->channel_,
(int) error, length);
print_buffer(data, length);
#endif
} else { // error
this->comp_->status_set_warning();
ESP_LOGE(TAG, "WeikaiRegisterI2C::read_fifo() @%02X reg=N/A ch=%d I2C_code:%d len=%d buf=%02X...", address,
this->channel_, (int) error, length, data[0]);
}
}
void WeikaiRegisterI2C::write_reg(uint8_t value) {
WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_);
uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, REG); // update the i2c bus
comp_i2c->set_i2c_address(address);
auto error = comp_i2c->write_register(this->register_, &value, 1);
if (error == i2c::NO_ERROR) {
this->comp_->status_clear_warning();
ESP_LOGVV(TAG, "WK2168Reg::write_reg() @%02X reg=%s ch=%d I2C_code:%d buf=%02X", address,
reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value);
} else { // error
this->comp_->status_set_warning();
ESP_LOGE(TAG, "WK2168Reg::write_reg() @%02X reg=%s ch=%d I2C_code:%d buf=%d", address,
reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value);
}
}
void WeikaiRegisterI2C::write_fifo(uint8_t *data, size_t length) {
WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_);
uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, FIFO); // set fifo flag
comp_i2c->set_i2c_address(address);
auto error = comp_i2c->write(data, length);
if (error == i2c::NO_ERROR) {
this->comp_->status_clear_warning();
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
ESP_LOGVV(TAG, "WK2168Reg::write_fifo() @%02X ch=%d I2C_code:%d len=%d buffer", address, this->channel_,
(int) error, length);
print_buffer(data, length);
#endif
} else { // error
this->comp_->status_set_warning();
ESP_LOGE(TAG, "WK2168Reg::write_fifo() @%02X reg=N/A, ch=%d I2C_code:%d len=%d, buf=%02X...", address,
this->channel_, (int) error, length, data[0]);
}
}
///////////////////////////////////////////////////////////////////////////////
// The WeikaiComponentI2C methods
///////////////////////////////////////////////////////////////////////////////
void WeikaiComponentI2C::setup() {
// before any manipulation we store the address to base_address_ for future use
this->base_address_ = this->address_;
ESP_LOGCONFIG(TAG, "Setting up wk2168_i2c: %s with %d UARTs at @%02X ...", this->get_name(), this->children_.size(),
this->base_address_);
// enable all channels
this->reg(WKREG_GENA, 0) = GENA_C1EN | GENA_C2EN | GENA_C3EN | GENA_C4EN;
// reset all channels
this->reg(WKREG_GRST, 0) = GRST_C1RST | GRST_C2RST | GRST_C3RST | GRST_C4RST;
// initialize the spage register to page 0
this->reg(WKREG_SPAGE, 0) = 0;
this->page1_ = false;
// we setup our children channels
for (auto *child : this->children_) {
child->setup_channel();
}
}
void WeikaiComponentI2C::dump_config() {
ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size());
ESP_LOGCONFIG(TAG, " Crystal: %" PRIu32, this->crystal_);
if (test_mode_)
ESP_LOGCONFIG(TAG, " Test mode: %d", test_mode_);
ESP_LOGCONFIG(TAG, " Transfer buffer size: %d", XFER_MAX_SIZE);
this->address_ = this->base_address_; // we restore the base_address before display (less confusing)
LOG_I2C_DEVICE(this);
for (auto *child : this->children_) {
child->dump_channel();
}
}
} // namespace weikai_i2c
} // namespace esphome
@@ -0,0 +1,61 @@
/// @file weikai_i2c.h
/// @author DrCoolZic
/// @brief WeiKai component family - classes declaration
/// @date Last Modified: 2024/03/01 13:31:57
/// @details The classes declared in this file can be used by the Weikai family
/// wk2132_i2c, wk2168_i2c, wk2204_i2c, wk2212_i2c
#pragma once
#include <bitset>
#include <memory>
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/weikai/weikai.h"
namespace esphome {
namespace weikai_i2c {
class WeikaiComponentI2C;
// using namespace weikai;
////////////////////////////////////////////////////////////////////////////////////
/// @brief WeikaiRegisterI2C objects acts as proxies to access remote register through an I2C Bus
////////////////////////////////////////////////////////////////////////////////////
class WeikaiRegisterI2C : public weikai::WeikaiRegister {
public:
uint8_t read_reg() const override;
void write_reg(uint8_t value) override;
void read_fifo(uint8_t *data, size_t length) const override;
void write_fifo(uint8_t *data, size_t length) override;
protected:
friend WeikaiComponentI2C;
WeikaiRegisterI2C(weikai::WeikaiComponent *const comp, uint8_t reg, uint8_t channel)
: weikai::WeikaiRegister(comp, reg, channel) {}
};
////////////////////////////////////////////////////////////////////////////////////
/// @brief The WeikaiComponentI2C class stores the information to the WeiKai component
/// connected through an I2C bus.
////////////////////////////////////////////////////////////////////////////////////
class WeikaiComponentI2C : public weikai::WeikaiComponent, public i2c::I2CDevice {
public:
weikai::WeikaiRegister &reg(uint8_t reg, uint8_t channel) override {
reg_i2c_.register_ = reg;
reg_i2c_.channel_ = channel;
return reg_i2c_;
}
//
// override Component methods
//
void setup() override;
void dump_config() override;
uint8_t base_address_; ///< base address of I2C device
WeikaiRegisterI2C reg_i2c_{this, 0, 0}; ///< init to this component
};
} // namespace weikai_i2c
} // namespace esphome
@@ -0,0 +1 @@
CODEOWNERS = ["@DrCoolZic"]
@@ -0,0 +1,189 @@
/// @file weikai_spi.cpp
/// @brief WeiKai component family - classes implementation
/// @date Last Modified: 2024/04/06 14:46:09
/// @details The classes declared in this file can be used by the Weikai family
#include "weikai_spi.h"
namespace esphome {
namespace weikai_spi {
using namespace weikai;
static const char *const TAG = "weikai_spi";
/// @brief convert an int to binary representation as C++ std::string
/// @param val integer to convert
/// @return a std::string
inline std::string i2s(uint8_t val) { return std::bitset<8>(val).to_string(); }
/// Convert std::string to C string
#define I2S2CS(val) (i2s(val).c_str())
/// @brief measure the time elapsed between two calls
/// @param last_time time of the previous call
/// @return the elapsed time in microseconds
uint32_t elapsed_ms(uint32_t &last_time) {
uint32_t e = millis() - last_time;
last_time = millis();
return e;
};
/// @brief Converts the parity enum value to a C string
/// @param parity enum
/// @return the string
const char *p2s(uart::UARTParityOptions parity) {
using namespace uart;
switch (parity) {
case UART_CONFIG_PARITY_NONE:
return "NONE";
case UART_CONFIG_PARITY_EVEN:
return "EVEN";
case UART_CONFIG_PARITY_ODD:
return "ODD";
default:
return "UNKNOWN";
}
}
/// @brief Display a buffer in hexadecimal format (32 hex values / line).
void print_buffer(const uint8_t *data, size_t length) {
char hex_buffer[100];
hex_buffer[(3 * 32) + 1] = 0;
for (size_t i = 0; i < length; i++) {
snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", data[i]);
if (i % 32 == 31) {
ESP_LOGVV(TAG, " %s", hex_buffer);
}
}
if (length % 32) {
// null terminate if incomplete line
hex_buffer[3 * (length % 32) + 2] = 0;
ESP_LOGVV(TAG, " %s", hex_buffer);
}
}
static const char *const REG_TO_STR_P0[16] = {"GENA", "GRST", "GMUT", "SPAGE", "SCR", "LCR", "FCR", "SIER",
"SIFR", "TFCNT", "RFCNT", "FSR", "LSR", "FDAT", "FWCR", "RS485"};
static const char *const REG_TO_STR_P1[16] = {"GENA", "GRST", "GMUT", "SPAGE", "BAUD1", "BAUD0", "PRES", "RFTL",
"TFTL", "FWTH", "FWTL", "XON1", "XOFF1", "SADR", "SAEN", "RTSDLY"};
// method to print a register value as text: used in the log messages ...
const char *reg_to_str(int reg, bool page1) {
if (reg == WKREG_GPDAT) {
return "GPDAT";
} else if (reg == WKREG_GPDIR) {
return "GPDIR";
} else {
return page1 ? REG_TO_STR_P1[reg & 0x0F] : REG_TO_STR_P0[reg & 0x0F];
}
}
enum RegType { REG = 0, FIFO = 1 }; ///< Register or FIFO
enum CmdType { WRITE_CMD = 0, READ_CMD = 1 }; ///< Read or Write transfer
/// @brief Computes the SPI command byte
/// @param transfer_type read or write command
/// @param reg (0-15) the address of the register
/// @param channel (0-3) the UART channel
/// @param fifo (0-1) 0 = access to internal register, 1 = direct access to fifo
/// @return the spi command byte
/// @details
/// +------+------+------+------+------+------+------+------+
/// | FIFO | R/W | C1-C0 | A3-A0 |
/// +------+------+-------------+---------------------------+
/// FIFO: 0 = register, 1 = FIFO
/// R/W: 0 = write, 1 = read
/// C1-C0: Channel (0-1)
/// A3-A0: Address (0-F)
inline static uint8_t cmd_byte(RegType fifo, CmdType transfer_type, uint8_t channel, uint8_t reg) {
return (fifo << 7 | transfer_type << 6 | channel << 4 | reg << 0);
}
///////////////////////////////////////////////////////////////////////////////
// The WeikaiRegisterSPI methods
///////////////////////////////////////////////////////////////////////////////
uint8_t WeikaiRegisterSPI::read_reg() const {
auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_);
uint8_t cmd = cmd_byte(REG, READ_CMD, this->channel_, this->register_);
spi_comp->enable();
spi_comp->write_byte(cmd);
uint8_t val = spi_comp->read_byte();
spi_comp->disable();
ESP_LOGVV(TAG, "WeikaiRegisterSPI::read_reg() cmd=%s(%02X) reg=%s ch=%d buf=%02X", I2S2CS(cmd), cmd,
reg_to_str(this->register_, this->comp_->page1()), this->channel_, val);
return val;
}
void WeikaiRegisterSPI::read_fifo(uint8_t *data, size_t length) const {
auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_);
uint8_t cmd = cmd_byte(FIFO, READ_CMD, this->channel_, this->register_);
spi_comp->enable();
spi_comp->write_byte(cmd);
spi_comp->read_array(data, length);
spi_comp->disable();
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
ESP_LOGVV(TAG, "WeikaiRegisterSPI::read_fifo() cmd=%s(%02X) ch=%d len=%d buffer", I2S2CS(cmd), cmd, this->channel_,
length);
print_buffer(data, length);
#endif
}
void WeikaiRegisterSPI::write_reg(uint8_t value) {
auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_);
uint8_t buf[2]{cmd_byte(REG, WRITE_CMD, this->channel_, this->register_), value};
spi_comp->enable();
spi_comp->write_array(buf, 2);
spi_comp->disable();
ESP_LOGVV(TAG, "WeikaiRegisterSPI::write_reg() cmd=%s(%02X) reg=%s ch=%d buf=%02X", I2S2CS(buf[0]), buf[0],
reg_to_str(this->register_, this->comp_->page1()), this->channel_, buf[1]);
}
void WeikaiRegisterSPI::write_fifo(uint8_t *data, size_t length) {
auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_);
uint8_t cmd = cmd_byte(FIFO, WRITE_CMD, this->channel_, this->register_);
spi_comp->enable();
spi_comp->write_byte(cmd);
spi_comp->write_array(data, length);
spi_comp->disable();
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
ESP_LOGVV(TAG, "WeikaiRegisterSPI::write_fifo() cmd=%s(%02X) ch=%d len=%d buffer", I2S2CS(cmd), cmd, this->channel_,
length);
print_buffer(data, length);
#endif
}
///////////////////////////////////////////////////////////////////////////////
// The WeikaiComponentSPI methods
///////////////////////////////////////////////////////////////////////////////
void WeikaiComponentSPI::setup() {
using namespace weikai;
ESP_LOGCONFIG(TAG, "Setting up wk2168_spi: %s with %d UARTs...", this->get_name(), this->children_.size());
this->spi_setup();
// enable all channels
this->reg(WKREG_GENA, 0) = GENA_C1EN | GENA_C2EN | GENA_C3EN | GENA_C4EN;
// reset all channels
this->reg(WKREG_GRST, 0) = GRST_C1RST | GRST_C2RST | GRST_C3RST | GRST_C4RST;
// initialize the spage register to page 0
this->reg(WKREG_SPAGE, 0) = 0;
this->page1_ = false;
// we setup our children channels
for (auto *child : this->children_) {
child->setup_channel();
}
}
void WeikaiComponentSPI::dump_config() {
ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size());
ESP_LOGCONFIG(TAG, " Crystal: %" PRIu32 "", this->crystal_);
if (test_mode_)
ESP_LOGCONFIG(TAG, " Test mode: %d", test_mode_);
ESP_LOGCONFIG(TAG, " Transfer buffer size: %d", XFER_MAX_SIZE);
LOG_PIN(" CS Pin: ", this->cs_);
for (auto *child : this->children_) {
child->dump_channel();
}
}
} // namespace weikai_spi
} // namespace esphome
@@ -0,0 +1,54 @@
/// @file weikai.h
/// @author DrCoolZic
/// @brief WeiKai component family - classes declaration
/// @date Last Modified: 2024/02/29 17:20:32
/// @details The classes declared in this file can be used by the Weikai family
/// wk2124_spi, wk2132_spi, wk2168_spi, wk2204_spi, wk2212_spi,
#pragma once
#include <bitset>
#include <memory>
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/spi/spi.h"
#include "esphome/components/weikai/weikai.h"
namespace esphome {
namespace weikai_spi {
////////////////////////////////////////////////////////////////////////////////////
/// @brief WeikaiRegisterSPI objects acts as proxies to access remote register through an SPI Bus
////////////////////////////////////////////////////////////////////////////////////
class WeikaiRegisterSPI : public weikai::WeikaiRegister {
public:
WeikaiRegisterSPI(weikai::WeikaiComponent *const comp, uint8_t reg, uint8_t channel)
: weikai::WeikaiRegister(comp, reg, channel) {}
uint8_t read_reg() const override;
void write_reg(uint8_t value) override;
void read_fifo(uint8_t *data, size_t length) const override;
void write_fifo(uint8_t *data, size_t length) override;
};
////////////////////////////////////////////////////////////////////////////////////
/// @brief The WeikaiComponentSPI class stores the information to the WeiKai component
/// connected through an SPI bus.
////////////////////////////////////////////////////////////////////////////////////
class WeikaiComponentSPI : public weikai::WeikaiComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
public:
weikai::WeikaiRegister &reg(uint8_t reg, uint8_t channel) override {
reg_spi_.register_ = reg;
reg_spi_.channel_ = channel;
return reg_spi_;
}
void setup() override;
void dump_config() override;
protected:
WeikaiRegisterSPI reg_spi_{this, 0, 0}; ///< init to this component
};
} // namespace weikai_spi
} // namespace esphome
+30
View File
@@ -0,0 +1,30 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, weikai
from esphome.const import CONF_ID
CODEOWNERS = ["@DrCoolZic"]
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["weikai", "weikai_i2c"]
MULTI_CONF = True
weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c")
WeikaiComponentI2C = weikai_i2c_ns.class_(
"WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.All(
weikai.WKBASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiComponentI2C),
}
).extend(i2c.i2c_device_schema(0x2C)),
weikai.check_channel_max_2,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_name(str(config[CONF_ID])))
await weikai.register_weikai(var, config)
await i2c.register_i2c_device(var, config)
@@ -0,0 +1,4 @@
/* compiling with esp-idf framework requires a .cpp file for some reason ? */
namespace esphome {
namespace wk2132_i2c {}
} // namespace esphome
+31
View File
@@ -0,0 +1,31 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import spi, weikai
from esphome.const import CONF_ID
CODEOWNERS = ["@DrCoolZic"]
DEPENDENCIES = ["spi"]
AUTO_LOAD = ["weikai", "weikai_spi"]
MULTI_CONF = True
weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi")
WeikaiComponentSPI = weikai_spi_ns.class_(
"WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice
)
CONFIG_SCHEMA = cv.All(
weikai.WKBASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiComponentSPI),
}
).extend(spi.spi_device_schema()),
weikai.check_channel_max_2,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_name(str(config[CONF_ID])))
await weikai.register_weikai(var, config)
await spi.register_spi_device(var, config)
+64
View File
@@ -0,0 +1,64 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import i2c, weikai
from esphome.const import (
CONF_ID,
CONF_INVERTED,
CONF_MODE,
CONF_NUMBER,
)
CODEOWNERS = ["@DrCoolZic"]
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["weikai", "weikai_i2c"]
MULTI_CONF = True
CONF_WK2168_I2C = "wk2168_i2c"
weikai_ns = cg.esphome_ns.namespace("weikai")
weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c")
WeikaiComponentI2C = weikai_i2c_ns.class_(
"WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice
)
WeikaiGPIOPin = weikai_ns.class_(
"WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentI2C)
)
CONFIG_SCHEMA = cv.All(
weikai.WKBASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiComponentI2C),
}
).extend(i2c.i2c_device_schema(0x2C)),
weikai.check_channel_max_4,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_name(str(config[CONF_ID])))
await weikai.register_weikai(var, config)
await i2c.register_i2c_device(var, config)
WK2168_PIN_SCHEMA = cv.All(
weikai.WEIKAI_PIN_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiGPIOPin),
cv.Required(CONF_WK2168_I2C): cv.use_id(WeikaiComponentI2C),
}
),
weikai.validate_pin_mode,
)
@pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2168_I2C, WK2168_PIN_SCHEMA)
async def sc16is75x_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
parent = await cg.get_variable(config[CONF_WK2168_I2C])
cg.add(var.set_parent(parent))
num = config[CONF_NUMBER]
cg.add(var.set_pin(num))
cg.add(var.set_inverted(config[CONF_INVERTED]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
return var
+62
View File
@@ -0,0 +1,62 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import spi, weikai
from esphome.const import (
CONF_ID,
CONF_INVERTED,
CONF_MODE,
CONF_NUMBER,
)
CODEOWNERS = ["@DrCoolZic"]
DEPENDENCIES = ["spi"]
AUTO_LOAD = ["weikai", "weikai_spi"]
MULTI_CONF = True
CONF_WK2168_SPI = "wk2168_spi"
weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi")
weikai_ns = cg.esphome_ns.namespace("weikai")
WeikaiComponentSPI = weikai_spi_ns.class_(
"WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice
)
WeikaiGPIOPin = weikai_ns.class_(
"WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentSPI)
)
CONFIG_SCHEMA = cv.All(
weikai.WKBASE_SCHEMA.extend(
{cv.GenerateID(): cv.declare_id(WeikaiComponentSPI)}
).extend(spi.spi_device_schema()),
weikai.check_channel_max_4,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_name(str(config[CONF_ID])))
await weikai.register_weikai(var, config)
await spi.register_spi_device(var, config)
WK2168_PIN_SCHEMA = cv.All(
weikai.WEIKAI_PIN_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiGPIOPin),
cv.Required(CONF_WK2168_SPI): cv.use_id(WeikaiComponentSPI),
},
),
weikai.validate_pin_mode,
)
@pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2168_SPI, WK2168_PIN_SCHEMA)
async def sc16is75x_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
parent = await cg.get_variable(config[CONF_WK2168_SPI])
cg.add(var.set_parent(parent))
num = config[CONF_NUMBER]
cg.add(var.set_pin(num))
cg.add(var.set_inverted(config[CONF_INVERTED]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
return var
+30
View File
@@ -0,0 +1,30 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, weikai
from esphome.const import CONF_ID
CODEOWNERS = ["@DrCoolZic"]
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["weikai", "weikai_i2c"]
MULTI_CONF = True
weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c")
WeikaiComponentI2C = weikai_i2c_ns.class_(
"WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.All(
weikai.WKBASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiComponentI2C),
}
).extend(i2c.i2c_device_schema(0x2C)),
weikai.check_channel_max_4,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_name(str(config[CONF_ID])))
await weikai.register_weikai(var, config)
await i2c.register_i2c_device(var, config)
+30
View File
@@ -0,0 +1,30 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import spi, weikai
from esphome.const import CONF_ID
CODEOWNERS = ["@DrCoolZic"]
DEPENDENCIES = ["spi"]
AUTO_LOAD = ["weikai", "weikai_spi"]
MULTI_CONF = True
weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi")
WeikaiComponentSPI = weikai_spi_ns.class_(
"WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice
)
CONFIG_SCHEMA = cv.All(
weikai.WKBASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiComponentSPI),
}
).extend(spi.spi_device_schema()),
weikai.check_channel_max_4,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_name(str(config[CONF_ID])))
await weikai.register_weikai(var, config)
await spi.register_spi_device(var, config)
+64
View File
@@ -0,0 +1,64 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import i2c, weikai
from esphome.const import (
CONF_ID,
CONF_INVERTED,
CONF_MODE,
CONF_NUMBER,
)
CODEOWNERS = ["@DrCoolZic"]
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["weikai", "weikai_i2c"]
MULTI_CONF = True
CONF_WK2212_I2C = "wk2212_i2c"
weikai_ns = cg.esphome_ns.namespace("weikai")
weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c")
WeikaiComponentI2C = weikai_i2c_ns.class_(
"WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice
)
WeikaiGPIOPin = weikai_ns.class_(
"WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentI2C)
)
CONFIG_SCHEMA = cv.All(
weikai.WKBASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiComponentI2C),
}
).extend(i2c.i2c_device_schema(0x2C)),
weikai.check_channel_max_2,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_name(str(config[CONF_ID])))
await weikai.register_weikai(var, config)
await i2c.register_i2c_device(var, config)
WK2212_PIN_SCHEMA = cv.All(
weikai.WEIKAI_PIN_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiGPIOPin),
cv.Required(CONF_WK2212_I2C): cv.use_id(WeikaiComponentI2C),
}
),
weikai.validate_pin_mode,
)
@pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2212_I2C, WK2212_PIN_SCHEMA)
async def sc16is75x_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
parent = await cg.get_variable(config[CONF_WK2212_I2C])
cg.add(var.set_parent(parent))
num = config[CONF_NUMBER]
cg.add(var.set_pin(num))
cg.add(var.set_inverted(config[CONF_INVERTED]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
return var
+62
View File
@@ -0,0 +1,62 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import spi, weikai
from esphome.const import (
CONF_ID,
CONF_INVERTED,
CONF_MODE,
CONF_NUMBER,
)
CODEOWNERS = ["@DrCoolZic"]
DEPENDENCIES = ["spi"]
AUTO_LOAD = ["weikai", "weikai_spi"]
MULTI_CONF = True
CONF_WK2212_SPI = "wk2212_spi"
weikai_ns = cg.esphome_ns.namespace("weikai")
weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi")
WeikaiComponentSPI = weikai_spi_ns.class_(
"WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice
)
WeikaiGPIOPin = weikai_ns.class_(
"WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentSPI)
)
CONFIG_SCHEMA = cv.All(
weikai.WKBASE_SCHEMA.extend(
{cv.GenerateID(): cv.declare_id(WeikaiComponentSPI)}
).extend(spi.spi_device_schema()),
weikai.check_channel_max_2,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_name(str(config[CONF_ID])))
await weikai.register_weikai(var, config)
await spi.register_spi_device(var, config)
WK2212_PIN_SCHEMA = cv.All(
weikai.WEIKAI_PIN_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WeikaiGPIOPin),
cv.Required(CONF_WK2212_SPI): cv.use_id(WeikaiComponentSPI),
},
),
weikai.validate_pin_mode,
)
@pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2212_SPI, WK2212_PIN_SCHEMA)
async def sc16is75x_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
parent = await cg.get_variable(config[CONF_WK2212_SPI])
cg.add(var.set_parent(parent))
num = config[CONF_NUMBER]
cg.add(var.set_pin(num))
cg.add(var.set_inverted(config[CONF_INVERTED]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
return var
+20
View File
@@ -0,0 +1,20 @@
i2c:
id: i2c_bus
scl: ${scl_pin}
sda: ${sda_pin}
scan: true
frequency: 600kHz
wk2132_i2c:
- id: wk2132_i2c_id
address: 0x70
i2c_id: i2c_bus
uart:
- id: wk2132_id_0
channel: 0
baud_rate: 115200
stop_bits: 1
parity: none
- id: wk2132_id_1
channel: 1
baud_rate: 19200
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO22
sda_pin: GPIO21
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO40
sda_pin: GPIO41
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO40
sda_pin: GPIO41
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO22
sda_pin: GPIO21
<<: !include common.yaml
+21
View File
@@ -0,0 +1,21 @@
spi:
id: spi_bus
clk_pin: ${clk_pin}
mosi_pin: ${mosi_pin}
miso_pin: ${miso_pin}
wk2132_spi:
- id: wk2132_spi_id
cs_pin: ${cs_pin}
spi_id: spi_bus
crystal: 11059200
data_rate: 1MHz
uart:
- id: wk2132_spi_id0
channel: 0
baud_rate: 115200
stop_bits: 1
parity: none
- id: wk2132_spi_id1
channel: 1
baud_rate: 921600
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO18
miso_pin: GPIO19
mosi_pin: GPIO23
cs_pin: GPIO5
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO40
miso_pin: GPIO41
mosi_pin: GPIO6
cs_pin: GPIO19
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO40
miso_pin: GPIO41
mosi_pin: GPIO6
cs_pin: GPIO19
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO18
miso_pin: GPIO19
mosi_pin: GPIO23
cs_pin: GPIO5
<<: !include common.yaml
+63
View File
@@ -0,0 +1,63 @@
i2c:
id: i2c_bus
scl: ${scl_pin}
sda: ${sda_pin}
scan: true
frequency: 600kHz
# component declaration
wk2168_i2c:
- id: bridge_i2c
i2c_id: i2c_bus
address: 0x70
uart:
- id: id0
channel: 0
baud_rate: 115200
stop_bits: 1
parity: none
- id: id1
channel: 1
baud_rate: 115200
- id: id2
channel: 2
baud_rate: 115200
- id: id3
channel: 3
baud_rate: 115200
# individual binary_sensor inputs
binary_sensor:
- platform: gpio
name: "pin_0"
pin:
wk2168_i2c: bridge_i2c
number: 0
mode:
input: true
- platform: gpio
name: "pin_1"
pin:
wk2168_i2c: bridge_i2c
number: 1
mode:
input: true
inverted: true
# Individual binary outputs
switch:
- platform: gpio
name: "pin_2"
pin:
wk2168_i2c: bridge_i2c
number: 2
mode:
output: true
- platform: gpio
name: "pin_3"
pin:
wk2168_i2c: bridge_i2c
number: 3
mode:
output: true
inverted: true
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO22
sda_pin: GPIO21
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO40
sda_pin: GPIO41
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO40
sda_pin: GPIO41
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO22
sda_pin: GPIO21
<<: !include common.yaml
+63
View File
@@ -0,0 +1,63 @@
spi:
id: spi_bus
clk_pin: ${clk_pin}
mosi_pin: ${mosi_pin}
miso_pin: ${miso_pin}
wk2168_spi:
- id: bridge_spi
cs_pin: ${cs_pin}
spi_id: spi_bus
crystal: 11059200
data_rate: 1MHz
uart:
- id: id0
channel: 0
baud_rate: 115200
stop_bits: 1
parity: none
- id: id1
channel: 1
baud_rate: 115200
- id: id2
channel: 2
baud_rate: 115200
- id: id3
channel: 3
baud_rate: 115200
# individual binary_sensor inputs
binary_sensor:
- platform: gpio
name: "pin_0"
pin:
wk2168_spi: bridge_spi
number: 0
mode:
input: true
- platform: gpio
name: "pin_1"
pin:
wk2168_spi: bridge_spi
number: 1
mode:
input: true
inverted: true
# Individual binary outputs
switch:
- platform: gpio
name: "pin_2"
pin:
wk2168_spi: bridge_spi
number: 2
mode:
output: true
- platform: gpio
name: "pin_3"
pin:
wk2168_spi: bridge_spi
number: 3
mode:
output: true
inverted: true
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO18
miso_pin: GPIO19
mosi_pin: GPIO23
cs_pin: GPIO5
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO40
miso_pin: GPIO41
mosi_pin: GPIO6
cs_pin: GPIO19
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO40
miso_pin: GPIO41
mosi_pin: GPIO6
cs_pin: GPIO19
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO18
miso_pin: GPIO19
mosi_pin: GPIO23
cs_pin: GPIO5
<<: !include common.yaml
+28
View File
@@ -0,0 +1,28 @@
i2c:
id: i2c_bus
scl: ${scl_pin}
sda: ${sda_pin}
scan: true
frequency: 600kHz
wk2204_i2c:
- id: wk2204_i2c_id
i2c_id: i2c_bus
address: 0x70
uart:
- id: wk2204_id_0
channel: 0
baud_rate: 115200
stop_bits: 1
parity: none
- id: wk2204_id_1
channel: 1
baud_rate: 19200
- id: wk2204_id_2
channel: 2
baud_rate: 115200
stop_bits: 1
parity: none
- id: wk2204_id_3
channel: 3
baud_rate: 19200
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO22
sda_pin: GPIO21
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO40
sda_pin: GPIO41
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO40
sda_pin: GPIO41
<<: !include common.yaml
@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO22
sda_pin: GPIO21
<<: !include common.yaml
+29
View File
@@ -0,0 +1,29 @@
spi:
id: spi_bus
clk_pin: ${clk_pin}
mosi_pin: ${mosi_pin}
miso_pin: ${miso_pin}
wk2204_spi:
- id: wk2204_spi_id
cs_pin: ${cs_pin}
spi_id: spi_bus
crystal: 11059200
data_rate: 1MHz
uart:
- id: wk2204_spi_id0
channel: 0
baud_rate: 115200
stop_bits: 1
parity: none
- id: wk2204_spi_id1
channel: 1
baud_rate: 921600
- id: wk2204_spi_id2
channel: 2
baud_rate: 115200
stop_bits: 1
parity: none
- id: wk2204_spi_id3
channel: 3
baud_rate: 921600
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO18
miso_pin: GPIO19
mosi_pin: GPIO23
cs_pin: GPIO5
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO40
miso_pin: GPIO41
mosi_pin: GPIO6
cs_pin: GPIO19
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO40
miso_pin: GPIO41
mosi_pin: GPIO6
cs_pin: GPIO19
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
clk_pin: GPIO18
miso_pin: GPIO19
mosi_pin: GPIO23
cs_pin: GPIO5
<<: !include common.yaml

Some files were not shown because too many files have changed in this diff Show More