feat: initial

This commit is contained in:
Alexey Zholtikov 2025-05-04 13:05:21 +03:00
parent db1774f86e
commit 2a75773239
8 changed files with 588 additions and 3 deletions

View File

@ -1 +1,6 @@
idf_component_register(SRCS "main.c" INCLUDE_DIRS "include")
if(${IDF_TARGET} STREQUAL esp8266)
set(requires driver zh_vector)
else()
set(requires driver esp_event zh_vector)
endif()
idf_component_register(SRCS "zh_pcf8574.c" INCLUDE_DIRS "include" REQUIRES ${requires})

139
README.md
View File

@ -1,3 +1,138 @@
# esp_component_template
# ESP32 ESP-IDF and ESP8266 RTOS SDK component for PCF8574(A) 8-bit I/O expander
esp_component_template
## Tested on
1. [ESP8266 RTOS_SDK v3.4](https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/index.html#)
2. [ESP32 ESP-IDF v5.4](https://docs.espressif.com/projects/esp-idf/en/release-v5.4/esp32/index.html)
## Features
1. Support of 16 expanders on one bus.
2. Support of output and input GPIO's work mode.
3. Support of interrupts from input GPIO's.
## Note
1. Enable interrupt support only if input GPIO's are used.
2. All the INT GPIO's on the extenders must be connected to the one GPIO on ESP.
3. The input GPIO's are always pullup to the power supply. They must be connected to ground to trigger an interrupt.
## Dependencies
1. [zh_vector](http://git.zh.com.ru/alexey.zholtikov/zh_vector)
## Using
In an existing project, run the following command to install the components:
```text
cd ../your_project/components
git clone http://git.zh.com.ru/alexey.zholtikov/zh_pcf8574
git clone http://git.zh.com.ru/alexey.zholtikov/zh_vector
```
In the application, add the component:
```c
#include "zh_pcf8574.h"
```
## Examples
One expander on bus. All GPIO's as output (except P0 - input). Interrupt is enable:
```c
#include "zh_pcf8574.h"
#define I2C_PORT (I2C_NUM_MAX - 1)
#ifndef CONFIG_IDF_TARGET_ESP8266
i2c_master_bus_handle_t i2c_bus_handle = {0};
#endif
zh_pcf8574_handle_t pcf8574_handle = {0};
void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); // Required only if used input GPIO interrupts.
void print_gpio_status(const char *message, uint8_t reg)
{
printf("%s", message);
for (uint8_t i = 0; i < 8; ++i)
{
printf("%c", (reg & 0x80) ? '1' : '0');
reg <<= 1;
}
printf(".\n");
}
void app_main(void)
{
esp_log_level_set("zh_pcf8574", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig.
esp_log_level_set("zh_vector", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig.
#ifdef CONFIG_IDF_TARGET_ESP8266
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_NUM_4, // In accordance with used chip.
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = GPIO_NUM_5, // In accordance with used chip.
.scl_pullup_en = GPIO_PULLUP_ENABLE,
};
i2c_driver_install(I2C_PORT, i2c_config.mode);
i2c_param_config(I2C_PORT, &i2c_config);
#else
i2c_master_bus_config_t i2c_bus_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_PORT,
.scl_io_num = GPIO_NUM_22, // In accordance with used chip.
.sda_io_num = GPIO_NUM_21, // In accordance with used chip.
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
#endif
esp_event_loop_create_default(); // Required only if used input GPIO interrupts.
#ifdef CONFIG_IDF_TARGET_ESP8266
esp_event_handler_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &zh_pcf8574_event_handler, NULL); // Required only if used input GPIO interrupts.
#else
esp_event_handler_instance_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &zh_pcf8574_event_handler, NULL, NULL); // Required only if used input GPIO interrupts.
#endif
zh_pcf8574_init_config_t pcf8574_init_config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
#ifdef CONFIG_IDF_TARGET_ESP8266
pcf8574_init_config.i2c_port = I2C_PORT;
#else
pcf8574_init_config.i2c_handle = i2c_bus_handle;
#endif
pcf8574_init_config.i2c_address = 0x38;
pcf8574_init_config.p0_gpio_work_mode = EXP_GPIO_INPUT; // Required only for input GPIO.
pcf8574_init_config.interrupt_gpio = GPIO_NUM_14; // Required only if used input GPIO interrupts.
zh_pcf8574_init(&pcf8574_init_config, &pcf8574_handle);
uint8_t reg = 0;
zh_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg);
printf("Set P7 to 1, P1 to 1 and P0 to 0.\n");
zh_pcf8574_write(&pcf8574_handle, 0b10000010); // GPIO P0 will not be changed because it is operating in input mode.
zh_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg);
printf("Sets P0 to 0.\n");
zh_pcf8574_write_gpio(&pcf8574_handle, EXP_GPIO_NUM_P0, 0); // GPIO P0 will not be changed because it is operating in input mode.
bool gpio = 0;
zh_pcf8574_read_gpio(&pcf8574_handle, EXP_GPIO_NUM_P0, &gpio);
printf("P0 status: %d.\n", gpio);
printf("Set P1 to 0.\n");
zh_pcf8574_write_gpio(&pcf8574_handle, EXP_GPIO_NUM_P1, 0);
zh_pcf8574_read_gpio(&pcf8574_handle, EXP_GPIO_NUM_P1, &gpio);
printf("P1 status: %d.\n", gpio);
zh_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg);
printf("Reset all GPIO.\n");
zh_pcf8574_reset(&pcf8574_handle);
zh_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg);
}
void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) // Required only if used input GPIO interrupts.
{
zh_pcf8574_event_on_isr_t *event = event_data;
printf("Interrupt happened on device address 0x%02X on GPIO number %d.\n", event->i2c_address, event->gpio_number);
}
```

View File

188
include/zh_pcf8574.h Normal file
View File

@ -0,0 +1,188 @@
#pragma once
#include "esp_log.h"
#include "driver/gpio.h"
#ifdef CONFIG_IDF_TARGET_ESP8266
#include "driver/i2c.h"
#else
#include "driver/i2c_master.h"
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_event.h"
#include "zh_vector.h"
#define ZH_PCF8574_INIT_CONFIG_DEFAULT() \
{ \
.task_priority = 10, \
.stack_size = 2048, \
.i2c_address = 0xFF, \
.interrupt_gpio = GPIO_NUM_MAX}
#ifdef __cplusplus
extern "C"
{
#endif
typedef enum // Enumeration of possible GPIO work mode.
{
EXP_GPIO_OUTPUT,
EXP_GPIO_INPUT
} zh_pcf8574_gpio_work_mode_t;
typedef enum // Enumeration of possible GPIO's number.
{
EXP_GPIO_NUM_P0 = 0x01,
EXP_GPIO_NUM_P1 = 0x02,
EXP_GPIO_NUM_P2 = 0x04,
EXP_GPIO_NUM_P3 = 0x08,
EXP_GPIO_NUM_P4 = 0x10,
EXP_GPIO_NUM_P5 = 0x20,
EXP_GPIO_NUM_P6 = 0x40,
EXP_GPIO_NUM_P7 = 0x80
} zh_pcf8574_gpio_number_t;
typedef struct // Structure for initial initialization of PCF8574 expander.
{
uint8_t task_priority; // Task priority for the PCF8574 expander isr processing. @note It is not recommended to set a value less than 10.
uint16_t stack_size; // Stack size for task for the PCF8574 expander isr processing processing. @note The minimum size is 2048 bytes.
uint8_t i2c_address; // Expander I2C address.
zh_pcf8574_gpio_work_mode_t p0_gpio_work_mode; // Expander GPIO PO work mode.
zh_pcf8574_gpio_work_mode_t p1_gpio_work_mode; // Expander GPIO P1 work mode.
zh_pcf8574_gpio_work_mode_t p2_gpio_work_mode; // Expander GPIO P2 work mode.
zh_pcf8574_gpio_work_mode_t p3_gpio_work_mode; // Expander GPIO P3 work mode.
zh_pcf8574_gpio_work_mode_t p4_gpio_work_mode; // Expander GPIO P4 work mode.
zh_pcf8574_gpio_work_mode_t p5_gpio_work_mode; // Expander GPIO P5 work mode.
zh_pcf8574_gpio_work_mode_t p6_gpio_work_mode; // Expander GPIO P6 work mode.
zh_pcf8574_gpio_work_mode_t p7_gpio_work_mode; // Expander GPIO P7 work mode.
gpio_num_t interrupt_gpio; // Interrupt GPIO. @attention Must be same for all PCF8574 expanders.
bool i2c_port; // I2C port. @attention Must be same for all PCF8574 expanders.
#ifndef CONFIG_IDF_TARGET_ESP8266
i2c_master_bus_handle_t i2c_handle; // Unique I2C bus handle. @attention Must be same for all PCF8574 expanders.
#endif
} zh_pcf8574_init_config_t;
typedef struct // PCF8574 expander handle.
{
uint8_t i2c_address; // Expander I2C address.
uint8_t gpio_work_mode; // Expander GPIO's work mode.
uint8_t gpio_status; // Expander GPIO's status.
bool is_initialized; // Expander initialization flag.
bool i2c_port; // I2C port.
#ifndef CONFIG_IDF_TARGET_ESP8266
i2c_master_bus_handle_t i2c_handle; // Unique I2C bus handle.
i2c_master_dev_handle_t dev_handle; // Unique I2C device handle.
#endif
} zh_pcf8574_handle_t;
ESP_EVENT_DECLARE_BASE(ZH_PCF8574);
typedef struct // Structure for sending data to the event handler when cause an interrupt. @note Should be used with ZH_PCF8574W event base.
{
uint8_t i2c_address; // The i2c address of PCF8574 expander that caused the interrupt.
uint8_t gpio_number; // The GPIO that caused the interrupt.
} zh_pcf8574_event_on_isr_t;
/**
* @brief Initialize PCF8574 expander.
*
* @param[in] config Pointer to PCF8574 initialized configuration structure. Can point to a temporary variable.
* @param[out] handle Pointer to unique PCF8574 handle.
*
* @attention I2C driver must be initialized first.
*
* @note Before initialize the expander recommend initialize zh_pcf8574_init_config_t structure with default values.
*
* @code zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT() @endcode
*
* @return
* - ESP_OK if initialization was success
* - ESP_ERR_INVALID_ARG if parameter error or invalid i2c adress
* - ESP_ERR_NOT_FOUND if expander not connected or not responded
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
* - ESP_FAIL if internal error
*/
esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle);
/**
* @brief Read PCF8574 all GPIO's status.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[out] reg Pointer to GPIO's status.
*
* @note For input GPIO's status will be 1 (HIGH) always.
*
* @return
* - ESP_OK if read was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/
esp_err_t zh_pcf8574_read(zh_pcf8574_handle_t *handle, uint8_t *reg);
/**
* @brief Set PCF8574 all GPIO's status.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] reg GPIO's status.
*
* @attention Only the GPIO outputs are affected.
*
* @return
* - ESP_OK if write was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/
esp_err_t zh_pcf8574_write(zh_pcf8574_handle_t *handle, uint8_t reg);
/**
* @brief Reset (set to initial) PCF8574 all GPIO's.
*
* @param[in] handle Pointer to unique PCF8574 handle.
*
* @return
* - ESP_OK if reset was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t *handle);
/**
* @brief Read PCF8574 GPIO status.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] gpio GPIO number.
* @param[out] status Pointer to GPIO status.
*
* @note For input GPIO's status will be 1 (HIGH) always.
*
* @return
* - ESP_OK if read was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/
esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t *handle, zh_pcf8574_gpio_number_t gpio, bool *status);
/**
* @brief Set PCF8574 GPIO status.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] gpio GPIO number.
* @param[in] status Pointer to GPIO status.
*
* @attention Only the GPIO output is affected.
*
* @return
* - ESP_OK if write was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/
esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t *handle, zh_pcf8574_gpio_number_t gpio, bool status);
#ifdef __cplusplus
}
#endif

0
main.c
View File

View File

@ -0,0 +1 @@
1.0.0

256
zh_pcf8574.c Normal file
View File

@ -0,0 +1,256 @@
#include "zh_pcf8574.h"
static const char *TAG = "zh_pcf8574";
static gpio_num_t _interrupt_gpio = GPIO_NUM_MAX;
static SemaphoreHandle_t _interrupt_semaphore = {0};
static zh_vector_t _vector = {0};
static void _zh_isr_handler(void *arg);
static void _zh_isr_processing_task(void *pvParameter);
static esp_err_t _zh_read_register(zh_pcf8574_handle_t *handle, uint8_t *reg);
static esp_err_t _zh_write_register(zh_pcf8574_handle_t *handle, uint8_t reg);
ESP_EVENT_DEFINE_BASE(ZH_PCF8574);
esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle)
{
ESP_LOGI(TAG, "PCF8574 initialization begin.");
if (config == NULL || handle == NULL || config->i2c_address == 0xFF)
{
ESP_LOGE(TAG, "PCF8574 initialization fail. Invalid argument.");
return ESP_ERR_INVALID_ARG;
}
handle->i2c_address = config->i2c_address;
handle->gpio_work_mode = (config->p7_gpio_work_mode << 7) | (config->p6_gpio_work_mode << 6) | (config->p5_gpio_work_mode << 5) | (config->p4_gpio_work_mode << 4) |
(config->p3_gpio_work_mode << 3) | (config->p2_gpio_work_mode << 2) | (config->p1_gpio_work_mode << 1) | (config->p0_gpio_work_mode << 0);
handle->gpio_status = handle->gpio_work_mode;
handle->i2c_port = config->i2c_port;
#ifndef CONFIG_IDF_TARGET_ESP8266
handle->i2c_handle = config->i2c_handle;
i2c_device_config_t pcf8574_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = handle->i2c_address,
.scl_speed_hz = 100000,
};
i2c_master_bus_add_device(handle->i2c_handle, &pcf8574_config, &handle->dev_handle);
if (i2c_master_probe(handle->i2c_handle, handle->i2c_address, 1000 / portTICK_PERIOD_MS) != ESP_OK)
{
ESP_LOGE(TAG, "PCF8574 initialization fail. Expander not connected or not responded.");
return ESP_ERR_NOT_FOUND;
}
#endif
handle->is_initialized = true;
if (_zh_write_register(handle, handle->gpio_work_mode) != ESP_OK)
{
ESP_LOGE(TAG, "PCF8574 initialization fail. I2C driver error at line %d.", __LINE__);
handle->is_initialized = false;
return ESP_ERR_INVALID_RESPONSE;
}
if (_interrupt_gpio == GPIO_NUM_MAX && config->interrupt_gpio != GPIO_NUM_MAX)
{
zh_vector_init(&_vector, sizeof(zh_pcf8574_handle_t));
_interrupt_gpio = config->interrupt_gpio;
gpio_config_t isr_pin_config = {0};
isr_pin_config.intr_type = GPIO_INTR_NEGEDGE;
isr_pin_config.mode = GPIO_MODE_INPUT;
isr_pin_config.pin_bit_mask = (1ULL << _interrupt_gpio);
isr_pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
isr_pin_config.pull_up_en = GPIO_PULLUP_ENABLE;
if (gpio_config(&isr_pin_config) != ESP_OK)
{
ESP_LOGW(TAG, "PCF8574 initialization warning. GPIO driver error at line %d.", __LINE__);
}
else
{
_interrupt_semaphore = xSemaphoreCreateBinary();
if (xTaskCreatePinnedToCore(&_zh_isr_processing_task, "_zh_isr_processing_task", config->stack_size, NULL, config->task_priority, NULL, tskNO_AFFINITY) != pdPASS)
{
ESP_LOGE(TAG, "PCF8574 initialization warning. Internal error at line %d.", __LINE__);
return ESP_FAIL;
}
gpio_install_isr_service(0);
gpio_isr_handler_add(_interrupt_gpio, _zh_isr_handler, NULL);
}
}
if (_interrupt_gpio != GPIO_NUM_MAX && handle->gpio_work_mode != 0)
{
zh_vector_push_back(&_vector, handle);
}
ESP_LOGI(TAG, "PCF8574 initialization success.");
return ESP_OK;
}
esp_err_t zh_pcf8574_read(zh_pcf8574_handle_t *handle, uint8_t *reg)
{
return _zh_read_register(handle, reg);
}
esp_err_t zh_pcf8574_write(zh_pcf8574_handle_t *handle, uint8_t reg)
{
return _zh_write_register(handle, (reg | handle->gpio_work_mode));
}
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t *handle)
{
return _zh_write_register(handle, handle->gpio_work_mode);
}
esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t *handle, zh_pcf8574_gpio_number_t gpio, bool *status)
{
uint8_t reg_temp = 0;
esp_err_t err = _zh_read_register(handle, &reg_temp);
*status = ((reg_temp & gpio) ? 1 : 0);
return err;
}
esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t *handle, zh_pcf8574_gpio_number_t gpio, bool status)
{
if (status == true)
{
return _zh_write_register(handle, handle->gpio_status | handle->gpio_work_mode | gpio);
}
else
{
return _zh_write_register(handle, (handle->gpio_status ^ gpio) | handle->gpio_work_mode);
}
}
void IRAM_ATTR _zh_isr_handler(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD_FROM_ISR();
};
}
void _zh_isr_processing_task(void *pvParameter)
{
for (;;)
{
xSemaphoreTake(_interrupt_semaphore, portMAX_DELAY);
ESP_LOGI(TAG, "PCF8574 isr processing begin.");
for (uint16_t i = 0; i < zh_vector_get_size(&_vector); ++i)
{
zh_pcf8574_handle_t *handle = zh_vector_get_item(&_vector, i);
zh_pcf8574_event_on_isr_t event = {0};
event.i2c_address = handle->i2c_address;
event.gpio_number = 0xFF;
uint8_t reg_temp = 0;
_zh_read_register(handle, &reg_temp);
if (((handle->gpio_work_mode & EXP_GPIO_NUM_P0) != 0) && ((reg_temp & EXP_GPIO_NUM_P0) == 0))
{
event.gpio_number = 0;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P1) != 0) && ((reg_temp & EXP_GPIO_NUM_P1) == 0))
{
event.gpio_number = 1;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P2) != 0) && ((reg_temp & EXP_GPIO_NUM_P2) == 0))
{
event.gpio_number = 2;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P3) != 0) && ((reg_temp & EXP_GPIO_NUM_P3) == 0))
{
event.gpio_number = 3;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P4) != 0) && ((reg_temp & EXP_GPIO_NUM_P4) == 0))
{
event.gpio_number = 4;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P5) != 0) && ((reg_temp & EXP_GPIO_NUM_P5) == 0))
{
event.gpio_number = 5;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P6) != 0) && ((reg_temp & EXP_GPIO_NUM_P6) == 0))
{
event.gpio_number = 6;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P7) != 0) && ((reg_temp & EXP_GPIO_NUM_P7) == 0))
{
event.gpio_number = 7;
}
if (event.gpio_number != 0xFF)
{
if (esp_event_post(ZH_PCF8574, 0, &event, sizeof(event), portTICK_PERIOD_MS) != ESP_OK)
{
ESP_LOGE(TAG, "PCF8574 isr processing task internal error at line %d.", __LINE__);
}
}
}
ESP_LOGI(TAG, "PCF8574 isr processing success.");
}
vTaskDelete(NULL);
}
esp_err_t _zh_read_register(zh_pcf8574_handle_t *handle, uint8_t *reg)
{
ESP_LOGI(TAG, "PCF8574 read begin.");
if (handle == NULL)
{
ESP_LOGE(TAG, "PCF8574 read fail. Invalid argument.");
return ESP_ERR_INVALID_ARG;
}
if (handle->is_initialized == false)
{
ESP_LOGE(TAG, "PCF8574 read fail. PCF8574 not initialized.");
return ESP_ERR_NOT_FOUND;
}
esp_err_t err = ESP_OK;
#ifdef CONFIG_IDF_TARGET_ESP8266
i2c_cmd_handle_t i2c_cmd_handle = i2c_cmd_link_create();
i2c_master_start(i2c_cmd_handle);
i2c_master_write_byte(i2c_cmd_handle, handle->i2c_address << 1 | I2C_MASTER_READ, true);
i2c_master_read_byte(i2c_cmd_handle, &handle->gpio_status, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd_handle);
err = i2c_master_cmd_begin(handle->i2c_port, i2c_cmd_handle, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd_handle);
#else
err = i2c_master_receive(handle->dev_handle, &handle->gpio_status, sizeof(handle->gpio_status), 1000 / portTICK_PERIOD_MS);
#endif
if (err != ESP_OK)
{
ESP_LOGE(TAG, "PCF8574 read fail. I2C driver error at line %d.", __LINE__);
return ESP_ERR_INVALID_RESPONSE;
}
*reg = handle->gpio_status;
ESP_LOGI(TAG, "PCF8574 read success.");
return ESP_OK;
}
esp_err_t _zh_write_register(zh_pcf8574_handle_t *handle, uint8_t reg)
{
ESP_LOGI(TAG, "PCF8574 write begin.");
if (handle == NULL)
{
ESP_LOGE(TAG, "PCF8574 write fail. Invalid argument.");
return ESP_ERR_INVALID_ARG;
}
if (handle->is_initialized == false)
{
ESP_LOGE(TAG, "PCF8574 write fail. PCF8574 not initialized.");
return ESP_ERR_NOT_FOUND;
}
esp_err_t err = ESP_OK;
#ifdef CONFIG_IDF_TARGET_ESP8266
i2c_cmd_handle_t i2c_cmd_handle = i2c_cmd_link_create();
i2c_master_start(i2c_cmd_handle);
i2c_master_write_byte(i2c_cmd_handle, handle->i2c_address << 1 | I2C_MASTER_WRITE, true);
i2c_master_write_byte(i2c_cmd_handle, reg, true);
i2c_master_stop(i2c_cmd_handle);
err = i2c_master_cmd_begin(handle->i2c_port, i2c_cmd_handle, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd_handle);
#else
err = i2c_master_transmit(handle->dev_handle, &reg, sizeof(reg), 1000 / portTICK_PERIOD_MS);
#endif
if (err != ESP_OK)
{
ESP_LOGE(TAG, "PCF8574 write fail. I2C driver error at line %d.", __LINE__);
return ESP_ERR_INVALID_RESPONSE;
}
handle->gpio_status = reg;
ESP_LOGI(TAG, "PCF8574 write success.");
return ESP_OK;
}