mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	XL9535 I/O Expander (#4899)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -318,4 +318,5 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl | |||||||
| esphome/components/xiaomi_mhoc303/* @drug123 | esphome/components/xiaomi_mhoc303/* @drug123 | ||||||
| esphome/components/xiaomi_mhoc401/* @vevsvevs | esphome/components/xiaomi_mhoc401/* @vevsvevs | ||||||
| esphome/components/xiaomi_rtcgq02lm/* @jesserockz | esphome/components/xiaomi_rtcgq02lm/* @jesserockz | ||||||
|  | esphome/components/xl9535/* @mreditor97 | ||||||
| esphome/components/xpt2046/* @nielsnl68 @numo68 | esphome/components/xpt2046/* @nielsnl68 @numo68 | ||||||
|   | |||||||
							
								
								
									
										73
									
								
								esphome/components/xl9535/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								esphome/components/xl9535/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import i2c | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ID, | ||||||
|  |     CONF_INPUT, | ||||||
|  |     CONF_INVERTED, | ||||||
|  |     CONF_MODE, | ||||||
|  |     CONF_NUMBER, | ||||||
|  |     CONF_OUTPUT, | ||||||
|  | ) | ||||||
|  | from esphome import pins | ||||||
|  |  | ||||||
|  | CONF_XL9535 = "xl9535" | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["i2c"] | ||||||
|  | CODEOWNERS = ["@mreditor97"] | ||||||
|  |  | ||||||
|  | xl9535_ns = cg.esphome_ns.namespace(CONF_XL9535) | ||||||
|  |  | ||||||
|  | XL9535Component = xl9535_ns.class_("XL9535Component", cg.Component, i2c.I2CDevice) | ||||||
|  | XL9535GPIOPin = xl9535_ns.class_("XL9535GPIOPin", cg.GPIOPin) | ||||||
|  |  | ||||||
|  | MULTI_CONF = True | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     cv.Schema({cv.Required(CONF_ID): cv.declare_id(XL9535Component)}) | ||||||
|  |     .extend(cv.COMPONENT_SCHEMA) | ||||||
|  |     .extend(i2c.i2c_device_schema(0x20)) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await i2c.register_i2c_device(var, config) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def validate_mode(mode): | ||||||
|  |     if not (mode[CONF_INPUT] or mode[CONF_OUTPUT]) or ( | ||||||
|  |         mode[CONF_INPUT] and mode[CONF_OUTPUT] | ||||||
|  |     ): | ||||||
|  |         raise cv.Invalid("Mode must be either a input or a output") | ||||||
|  |     return mode | ||||||
|  |  | ||||||
|  |  | ||||||
|  | XL9535_PIN_SCHEMA = cv.All( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(): cv.declare_id(XL9535GPIOPin), | ||||||
|  |         cv.Required(CONF_XL9535): cv.use_id(XL9535Component), | ||||||
|  |         cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15), | ||||||
|  |         cv.Optional(CONF_MODE, default={}): cv.All( | ||||||
|  |             { | ||||||
|  |                 cv.Optional(CONF_INPUT, default=False): cv.boolean, | ||||||
|  |                 cv.Optional(CONF_OUTPUT, default=False): cv.boolean, | ||||||
|  |             }, | ||||||
|  |             validate_mode, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_INVERTED, default=False): cv.boolean, | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pins.PIN_SCHEMA_REGISTRY.register(CONF_XL9535, XL9535_PIN_SCHEMA) | ||||||
|  | async def xl9535_pin_to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     parent = await cg.get_variable(config[CONF_XL9535]) | ||||||
|  |  | ||||||
|  |     cg.add(var.set_parent(parent)) | ||||||
|  |     cg.add(var.set_pin(config[CONF_NUMBER])) | ||||||
|  |     cg.add(var.set_inverted(config[CONF_INVERTED])) | ||||||
|  |     cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) | ||||||
|  |  | ||||||
|  |     return var | ||||||
							
								
								
									
										122
									
								
								esphome/components/xl9535/xl9535.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								esphome/components/xl9535/xl9535.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | |||||||
|  | #include "xl9535.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace xl9535 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "xl9535"; | ||||||
|  |  | ||||||
|  | void XL9535Component::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up XL9535..."); | ||||||
|  |  | ||||||
|  |   // Check to see if the device can read from the register | ||||||
|  |   uint8_t port = 0; | ||||||
|  |   if (this->read_register(XL9535_INPUT_PORT_0_REGISTER, &port, 1) != i2c::ERROR_OK) { | ||||||
|  |     this->mark_failed(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void XL9535Component::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "XL9535:"); | ||||||
|  |   LOG_I2C_DEVICE(this); | ||||||
|  |  | ||||||
|  |   if (this->is_failed()) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with XL9535 failed!"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool XL9535Component::digital_read(uint8_t pin) { | ||||||
|  |   bool state = false; | ||||||
|  |   uint8_t port = 0; | ||||||
|  |  | ||||||
|  |   if (pin > 7) { | ||||||
|  |     if (this->read_register(XL9535_INPUT_PORT_1_REGISTER, &port, 1) != i2c::ERROR_OK) { | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       return state; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state = (port & (pin - 10)) != 0; | ||||||
|  |   } else { | ||||||
|  |     if (this->read_register(XL9535_INPUT_PORT_0_REGISTER, &port, 1) != i2c::ERROR_OK) { | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       return state; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state = (port & pin) != 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->status_clear_warning(); | ||||||
|  |   return state; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void XL9535Component::digital_write(uint8_t pin, bool value) { | ||||||
|  |   uint8_t port = 0; | ||||||
|  |   uint8_t register_data = 0; | ||||||
|  |  | ||||||
|  |   if (pin > 7) { | ||||||
|  |     if (this->read_register(XL9535_OUTPUT_PORT_1_REGISTER, ®ister_data, 1) != i2c::ERROR_OK) { | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     register_data = register_data & (~(1 << (pin - 10))); | ||||||
|  |     port = register_data | value << (pin - 10); | ||||||
|  |  | ||||||
|  |     if (this->write_register(XL9535_OUTPUT_PORT_1_REGISTER, &port, 1) != i2c::ERROR_OK) { | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     if (this->read_register(XL9535_OUTPUT_PORT_0_REGISTER, ®ister_data, 1) != i2c::ERROR_OK) { | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     register_data = register_data & (~(1 << pin)); | ||||||
|  |     port = register_data | value << pin; | ||||||
|  |  | ||||||
|  |     if (this->write_register(XL9535_OUTPUT_PORT_0_REGISTER, &port, 1) != i2c::ERROR_OK) { | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->status_clear_warning(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void XL9535Component::pin_mode(uint8_t pin, gpio::Flags mode) { | ||||||
|  |   uint8_t port = 0; | ||||||
|  |  | ||||||
|  |   if (pin > 7) { | ||||||
|  |     this->read_register(XL9535_CONFIG_PORT_1_REGISTER, &port, 1); | ||||||
|  |  | ||||||
|  |     if (mode == gpio::FLAG_INPUT) { | ||||||
|  |       port = port | (1 << (pin - 10)); | ||||||
|  |     } else if (mode == gpio::FLAG_OUTPUT) { | ||||||
|  |       port = port & (~(1 << (pin - 10))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     this->write_register(XL9535_CONFIG_PORT_1_REGISTER, &port, 1); | ||||||
|  |   } else { | ||||||
|  |     this->read_register(XL9535_CONFIG_PORT_0_REGISTER, &port, 1); | ||||||
|  |  | ||||||
|  |     if (mode == gpio::FLAG_INPUT) { | ||||||
|  |       port = port | (1 << pin); | ||||||
|  |     } else if (mode == gpio::FLAG_OUTPUT) { | ||||||
|  |       port = port & (~(1 << pin)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     this->write_register(XL9535_CONFIG_PORT_0_REGISTER, &port, 1); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void XL9535GPIOPin::setup() { this->pin_mode(this->flags_); } | ||||||
|  |  | ||||||
|  | std::string XL9535GPIOPin::dump_summary() const { return str_snprintf("%u via XL9535", 15, this->pin_); } | ||||||
|  |  | ||||||
|  | void XL9535GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } | ||||||
|  | bool XL9535GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } | ||||||
|  | void XL9535GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } | ||||||
|  |  | ||||||
|  | }  // namespace xl9535 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										54
									
								
								esphome/components/xl9535/xl9535.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								esphome/components/xl9535/xl9535.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace xl9535 { | ||||||
|  |  | ||||||
|  | enum { | ||||||
|  |   XL9535_INPUT_PORT_0_REGISTER = 0x00, | ||||||
|  |   XL9535_INPUT_PORT_1_REGISTER = 0x01, | ||||||
|  |   XL9535_OUTPUT_PORT_0_REGISTER = 0x02, | ||||||
|  |   XL9535_OUTPUT_PORT_1_REGISTER = 0x03, | ||||||
|  |   XL9535_INVERSION_PORT_0_REGISTER = 0x04, | ||||||
|  |   XL9535_INVERSION_PORT_1_REGISTER = 0x05, | ||||||
|  |   XL9535_CONFIG_PORT_0_REGISTER = 0x06, | ||||||
|  |   XL9535_CONFIG_PORT_1_REGISTER = 0x07, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class XL9535Component : public Component, public i2c::I2CDevice { | ||||||
|  |  public: | ||||||
|  |   bool digital_read(uint8_t pin); | ||||||
|  |   void digital_write(uint8_t pin, bool value); | ||||||
|  |   void pin_mode(uint8_t pin, gpio::Flags mode); | ||||||
|  |  | ||||||
|  |   void setup() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class XL9535GPIOPin : public GPIOPin { | ||||||
|  |  public: | ||||||
|  |   void set_parent(XL9535Component *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; | ||||||
|  |   bool digital_read() override; | ||||||
|  |   void digital_write(bool value) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   XL9535Component *parent_; | ||||||
|  |  | ||||||
|  |   uint8_t pin_; | ||||||
|  |   bool inverted_; | ||||||
|  |   gpio::Flags flags_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace xl9535 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -384,6 +384,15 @@ binary_sensor: | |||||||
|         pullup: true |         pullup: true | ||||||
|       inverted: false |       inverted: false | ||||||
|  |  | ||||||
|  |   - platform: gpio | ||||||
|  |     name: XL9535 Pin 0 | ||||||
|  |     pin: | ||||||
|  |       xl9535: xl9535_hub | ||||||
|  |       number: 0 | ||||||
|  |       mode: | ||||||
|  |         input: true | ||||||
|  |       inverted: false | ||||||
|  |  | ||||||
| climate: | climate: | ||||||
|   - platform: tuya |   - platform: tuya | ||||||
|     id: tuya_climate |     id: tuya_climate | ||||||
| @@ -745,3 +754,7 @@ voice_assistant: | |||||||
| max6956: | max6956: | ||||||
|   - id: max6956_1 |   - id: max6956_1 | ||||||
|     address: 0x40 |     address: 0x40 | ||||||
|  |  | ||||||
|  | xl9535: | ||||||
|  |   - id: xl9535_hub | ||||||
|  |     address: 0x20 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user