#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c_master.h"
#include "esp_log.h"
#include "rom/ets_sys.h"
#define LCD_20X04 1
#define I2C_ADDRESS_LCD 0x27
#define I2C_MASTER_NUM I2C_NUM_1
#define I2C_SCL_LCD 21
#define I2C_SDA_LCD 22
#define I2C_MASTER_FREQ_HZ 400000
/* Comandos de la LCD */
#define CLEAR_DISPLAY 0x01
#define RETURN_HOME_UNSHIFT 0x02
#define CURSOR_RIGHT_NO_SHIFT 0x04
#define CURSOR_RIGHT_SHIFT 0x05
#define CURSOR_RIGHT_NO_SHIFT_LEFT 0x06
#define CURSOR_RIGHT_SHIFT_LEFT 0x07
#define DISPLAY_OFF 0x08
#define DISPLAY_ON_CURSOR_OFF 0x0C
#define DISPLAY_ON_CURSOR_ON_STEADY 0x0E
#define DISPLAY_ON_CURSOR_ON_BLINK 0x0F
#define SHIFT_CURSOR_LEFT 0x10
#define SHIFT_CURSOR_RIGHT 0x14
#define SHIFT_DISPLAY_LEFT 0x18
#define SHIFT_DISPLAY_RIGHT 0x1C
#define SET_4BIT_MODE 0x28
#define RETURN_HOME 0x80
/* PCF8574 */
#define PCF8574_RS 0
#define PCF8574_RW 1
#define PCF8574_EN 2
#define PCF8574_BL 3
#define LCD_RS_CMD 0
#define LCD_RS_DATA 1
#define TIMEOUT_MS 1000
static const char *TAG = "LCD_I2C";
typedef struct {
uint8_t i2c_address;
uint8_t screen_size;
uint8_t screen_backlight;
i2c_master_dev_handle_t device_handle;
} lcd_i2c_device_t;
i2c_master_bus_handle_t i2c_init(void);
i2c_master_dev_handle_t i2c_add_device(i2c_master_bus_handle_t, uint8_t);
void lcd_init(lcd_i2c_device_t * lcd);
void lcd_i2c_write_byte(lcd_i2c_device_t * lcd, uint8_t data);
void lcd_i2c_write_command(lcd_i2c_device_t * lcd, uint8_t register_select, uint8_t cmd);
void lcd_set_cursor(lcd_i2c_device_t * lcd, uint8_t column, uint8_t row);
void lcd_i2c_write_custom_char(lcd_i2c_device_t * lcd, uint8_t char_address, const uint8_t * pixels);
i2c_master_bus_handle_t i2c_init(void)
{
i2c_master_bus_handle_t bus_handle;
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_MASTER_NUM,
.scl_io_num = I2C_SCL_LCD,
.sda_io_num = I2C_SDA_LCD,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
esp_err_t error = i2c_new_master_bus(&i2c_mst_config, &bus_handle);
if (error != ESP_OK) {
ESP_LOGE(TAG, "I2C master bus initialization failed: %s", esp_err_to_name(error));
while(1);
}
return bus_handle;
}
i2c_master_dev_handle_t i2c_add_device(i2c_master_bus_handle_t bus_handle, uint8_t device_address)
{
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = device_address,
.scl_speed_hz = I2C_MASTER_FREQ_HZ,
};
i2c_master_dev_handle_t dev_handle;
esp_err_t error = i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle);
if (error != ESP_OK) {
ESP_LOGE(TAG, "Failed to add device to I2C bus: %s", esp_err_to_name(error));
while(1);
}
return dev_handle;
}
void lcd_init(lcd_i2c_device_t * lcd)
{
lcd_i2c_write_command(lcd, LCD_RS_CMD, RETURN_HOME_UNSHIFT);
lcd_i2c_write_command(lcd, LCD_RS_CMD, SET_4BIT_MODE);
lcd_i2c_write_command(lcd, LCD_RS_CMD, CLEAR_DISPLAY);
lcd_i2c_write_command(lcd, LCD_RS_CMD, DISPLAY_ON_CURSOR_OFF);
lcd_i2c_write_command(lcd, LCD_RS_CMD, CURSOR_RIGHT_NO_SHIFT_LEFT);
vTaskDelay(20 / portTICK_PERIOD_MS);
}
void lcd_i2c_write_byte(lcd_i2c_device_t * lcd, uint8_t data)
{
esp_err_t err = i2c_master_transmit(lcd->device_handle, &data, 1, TIMEOUT_MS);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write byte to LCD: %s", esp_err_to_name(err));
}
}
void lcd_i2c_write_command(lcd_i2c_device_t * lcd, uint8_t register_select, uint8_t cmd)
{
uint8_t config = (register_select) ? (1 << PCF8574_RS) : 0;
config |= (lcd->screen_backlight) ? (1 << PCF8574_BL) : 0;
config |= (config & 0x0F) | (0xF0 & cmd);
config |= (1 << PCF8574_EN);
lcd_i2c_write_byte(lcd, config);
ets_delay_us(10);
config &= ~(1 << PCF8574_EN);
lcd_i2c_write_byte(lcd, config);
ets_delay_us(50);
config = (config & 0x0F) | (cmd << 4);
config |= (1 << PCF8574_EN);
lcd_i2c_write_byte(lcd, config);
ets_delay_us(10);
config &= ~(1 << PCF8574_EN);
lcd_i2c_write_byte(lcd, config);
ets_delay_us(50);
if (cmd == CLEAR_DISPLAY)
{
vTaskDelay(20 / portTICK_PERIOD_MS);
}
}
void lcd_set_cursor(lcd_i2c_device_t * lcd, uint8_t column, uint8_t row)
{
switch (row) {
case 0:
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0x80 + column);
break;
case 1:
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0xC0 + column);
break;
case 2:
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0x94 + column);
break;
case 3:
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0xD4 + column);
break;
default:
break;
}
}
void lcd_i2c_write_custom_char(lcd_i2c_device_t * lcd, uint8_t address, const uint8_t * pixels)
{
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0x40 | (address << 3));
for (uint8_t i = 0; i < 8; i++)
{
lcd_i2c_write_command(lcd, LCD_RS_DATA, pixels[i]);
}
lcd_i2c_write_command(lcd, LCD_RS_CMD, RETURN_HOME);
}
void lcd_i2c_print_msg(lcd_i2c_device_t * lcd, char * msg)
{
uint8_t i = 0;
while (msg[i] != '\0')
{
lcd_i2c_write_command(lcd, LCD_RS_DATA, msg[i++]);
}
}
#define SMILE 5
#define DROP 2
void app_main()
{
ESP_LOGI(TAG, "Initializing I2C LCD display");
// Initialize I2C master bus
i2c_master_bus_handle_t bus_handle = i2c_init();
// Add LCD device to the I2C bus
i2c_master_dev_handle_t lcd_device = i2c_add_device(bus_handle, I2C_ADDRESS_LCD);
// Set up LCD device structure
lcd_i2c_device_t my_lcd = {
.i2c_address = I2C_ADDRESS_LCD,
.screen_size = LCD_20X04,
.screen_backlight = 1,
.device_handle = lcd_device,
};
vTaskDelay(20 / portTICK_PERIOD_MS);
lcd_init(&my_lcd);
uint8_t smile[8] = {0x00, 0x0A, 0x0A, 0x00, 0x11, 0x0E, 0x00, 0x00};
uint8_t drop[8] = {0x04, 0x04, 0x0E, 0x0E, 0x1F, 0x1F, 0x1F, 0x0E};
lcd_i2c_write_custom_char(&my_lcd, SMILE, smile);
lcd_i2c_write_custom_char(&my_lcd, DROP, drop);
bool print_msg = true;
uint8_t conteo_msg = 0;
char message[] = {'H', 'o', 'l', 'a', ' ', 'M', 'u', 'n', 'd', 'o', DROP, SMILE, 0};
char spaces_char[] = " ";
while (1) {
lcd_set_cursor(&my_lcd, 2, conteo_msg);
if (print_msg)
{
lcd_i2c_print_msg(&my_lcd, message);
}
else
{
lcd_i2c_print_msg(&my_lcd, spaces_char);
}
conteo_msg++;
if (conteo_msg > 3)
{
conteo_msg = 0;
print_msg = !print_msg;
}
vTaskDelay(250 / portTICK_PERIOD_MS);
}
}