Code:
/*
* Made by KinCony IoT: https://www.kincony.com
*
* This program controls two PCA9555 I/O expander chips via I2C to control a total of 32 relays.
* The first PCA9555 (at I2C address 0x21) controls relays 1-16, while the second PCA9555 (at I2C address 0x22)
* controls relays 17-32. The relays are turned ON (LOW) and OFF (HIGH) in sequence.
*
* Each PCA9555 chip has two ports: port 0 controls relays 1-8 (or 17-24), and port 1 controls relays 9-16 (or 25-32).
*
* The program will initialize both chips and turn all relays OFF initially, then turn each relay ON one by one.
* After a delay, all relays are turned OFF again.
*/
#include <stdio.h>
#include <string.h>
#include "driver/i2c.h"
#include "esp_log.h"
// I2C communication settings
#define I2C_MASTER_SCL_IO 10 // GPIO number for I2C clock line (SCL)
#define I2C_MASTER_SDA_IO 11 // GPIO number for I2C data line (SDA)
#define I2C_MASTER_FREQ_HZ 100000 // Frequency for I2C clock (100kHz)
#define I2C_MASTER_PORT I2C_NUM_0 // I2C port number used in this program
// I2C write/read settings
#define WRITE_BIT I2C_MASTER_WRITE // I2C write mode
#define READ_BIT I2C_MASTER_READ // I2C read mode
#define ACK_CHECK_EN 0x1 // Enable I2C ACK checking
#define ACK_CHECK_DIS 0x0 // Disable I2C ACK checking
#define ACK_VAL 0x0 // I2C ACK value
#define NACK_VAL 0x1 // I2C NACK value
// PCA9555 I2C addresses (chip 1: controls relays 1-16, chip 2: controls relays 17-32)
#define PCA9555_ADDR_1 0x21 // I2C address for the first PCA9555 (1-16 relays)
#define PCA9555_ADDR_2 0x22 // I2C address for the second PCA9555 (17-32 relays)
// PCA9555 Register addresses
#define PCA9555_REG_OUTPUT_0 0x02 // Output register for port 0 (controls relays 1-8 or 17-24)
#define PCA9555_REG_OUTPUT_1 0x03 // Output register for port 1 (controls relays 9-16 or 25-32)
// Function declarations (prototypes)
esp_err_t i2c_master_init(); // Initializes the I2C master
esp_err_t pca9555_write_byte(uint8_t device_addr, uint8_t reg_addr, uint8_t data); // Writes a byte to a PCA9555 register
void control_relays(); // Main function to control the relays
// Main application entry point
void app_main() {
// Initialize I2C master interface
ESP_ERROR_CHECK(i2c_master_init());
// Set both PCA9555 chips' all pins as output and turn all relays OFF initially (0xFF = all HIGH = OFF)
ESP_ERROR_CHECK(pca9555_write_byte(PCA9555_ADDR_1, PCA9555_REG_OUTPUT_0, 0xFF)); // Turn off relays 1-8
ESP_ERROR_CHECK(pca9555_write_byte(PCA9555_ADDR_1, PCA9555_REG_OUTPUT_1, 0xFF)); // Turn off relays 9-16
ESP_ERROR_CHECK(pca9555_write_byte(PCA9555_ADDR_2, PCA9555_REG_OUTPUT_0, 0xFF)); // Turn off relays 17-24
ESP_ERROR_CHECK(pca9555_write_byte(PCA9555_ADDR_2, PCA9555_REG_OUTPUT_1, 0xFF)); // Turn off relays 25-32
// Start controlling relays (turn on one by one, then off)
control_relays();
}
// Function to initialize the I2C master
esp_err_t i2c_master_init() {
// I2C configuration structure
i2c_config_t conf = {
.mode = I2C_MODE_MASTER, // Set the I2C mode to master
.sda_io_num = I2C_MASTER_SDA_IO, // Set the SDA GPIO pin
.scl_io_num = I2C_MASTER_SCL_IO, // Set the SCL GPIO pin
.sda_pullup_en = GPIO_PULLUP_ENABLE, // Enable internal pull-up for SDA line
.scl_pullup_en = GPIO_PULLUP_ENABLE, // Enable internal pull-up for SCL line
.master.clk_speed = I2C_MASTER_FREQ_HZ, // Set I2C clock frequency to 100kHz
};
// Apply I2C configuration to I2C master port
i2c_param_config(I2C_MASTER_PORT, &conf);
// Install the I2C driver for the master port
return i2c_driver_install(I2C_MASTER_PORT, conf.mode, 0, 0, 0);
}
// Function to write a byte of data to a specified register of the PCA9555
// device_addr: I2C address of the PCA9555 chip (0x21 or 0x22)
// reg_addr: Register address to write to (e.g., output register 0 or 1)
// data: The byte of data to write (e.g., relay states: HIGH = OFF, LOW = ON)
esp_err_t pca9555_write_byte(uint8_t device_addr, uint8_t reg_addr, uint8_t data) {
// Create a new I2C command link
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// Start the I2C communication
i2c_master_start(cmd);
// Send the PCA9555's I2C address with the write bit
i2c_master_write_byte(cmd, (device_addr << 1) | WRITE_BIT, ACK_CHECK_EN);
// Send the register address where the data should be written
i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN);
// Send the actual data to be written to the register
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
// Stop the I2C communication
i2c_master_stop(cmd);
// Execute the I2C command and return the result
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, 1000 / portTICK_PERIOD_MS);
// Delete the I2C command link after use
i2c_cmd_link_delete(cmd);
return ret;
}
// Function to control the relays on both PCA9555 chips
void control_relays() {
uint8_t relay_state_0 = 0xFE; // Relay 1 ON (LOW), others OFF (HIGH) for port 0
uint8_t relay_state_1 = 0xFF; // All OFF (HIGH) for port 1 initially
// Control the first PCA9555 chip (relays 1-16)
// Port 0 controls relays 1-8
for (int i = 0; i < 8; ++i) {
ESP_LOGI("Relay Control", "Setting relay %d ON (chip 1, port 0)", i + 1);
// Write relay states to port 0 of the first PCA9555 (1-8 relays)
pca9555_write_byte(PCA9555_ADDR_1, PCA9555_REG_OUTPUT_0, relay_state_0);
relay_state_0 = (relay_state_0 << 1) | 0x01; // Shift to the next relay
vTaskDelay(100 / portTICK_PERIOD_MS); // Delay between relay actions
}
relay_state_0 = 0xFE; // Reset state for port 0, relay 9 ON
// Port 1 controls relays 9-16
for (int i = 0; i < 8; ++i) {
ESP_LOGI("Relay Control", "Setting relay %d ON (chip 1, port 1)", i + 9);
// Write relay states to port 1 of the first PCA9555 (9-16 relays)
pca9555_write_byte(PCA9555_ADDR_1, PCA9555_REG_OUTPUT_1, relay_state_0);
relay_state_0 = (relay_state_0 << 1) | 0x01; // Shift to the next relay
vTaskDelay(100 / portTICK_PERIOD_MS); // Delay between relay actions
}
relay_state_0 = 0xFE; // Relay 17 ON (LOW), others OFF (HIGH) for port 0
// Control the second PCA9555 chip (relays 17-32)
// Port 0 controls relays 17-24
for (int i = 0; i < 8; ++i) {
ESP_LOGI("Relay Control", "Setting relay %d ON (chip 2, port 0)", i + 17);
// Write relay states to port 0 of the second PCA9555 (17-24 relays)
pca9555_write_byte(PCA9555_ADDR_2, PCA9555_REG_OUTPUT_0, relay_state_0);
relay_state_0 = (relay_state_0 << 1) | 0x01; // Shift to the next relay
vTaskDelay(100 / portTICK_PERIOD_MS); // Delay between relay actions
}
relay_state_0 = 0xFE; // Reset state for port 1, relay 25 ON
// Port 1 controls relays 25-32
for (int i = 0; i < 8; ++i) {
ESP_LOGI("Relay Control", "Setting relay %d ON (chip 2, port 1)", i + 25);
// Write relay states to port 1 of the second PCA9555 (25-32 relays)
pca9555_write_byte(PCA9555_ADDR_2, PCA9555_REG_OUTPUT_1, relay_state_0);
relay_state_0 = (relay_state_0 << 1) | 0x01; // Shift to the next relay
vTaskDelay(100 / portTICK_PERIOD_MS); // Delay between relay actions
}
// After a short delay, turn all relays OFF
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Set all relays on both chips to OFF (HIGH)
pca9555_write_byte(PCA9555_ADDR_1, PCA9555_REG_OUTPUT_0, 0xFF); // Turn off relays 1-8
pca9555_write_byte(PCA9555_ADDR_1, PCA9555_REG_OUTPUT_1, 0xFF); // Turn off relays 9-16
pca9555_write_byte(PCA9555_ADDR_2, PCA9555_REG_OUTPUT_0, 0xFF); // Turn off relays 17-24
pca9555_write_byte(PCA9555_ADDR_2, PCA9555_REG_OUTPUT_1, 0xFF); // Turn off relays 25-32
}