#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "driver/uart.h"
#include "rom/ets_sys.h"
/* == DEFINICIONES PARA LCD I2C == */
#define LCD_20X04 1
#define I2C_ACK_CHECK_EN 1
#define I2C_ADDRESS_LCD 0x27
#define I2C_SCL_LCD 21
#define I2C_SDA_LCD 22
/* Comandos básicos LCD */
#define CLEAR_DISPLAY 0x01
#define RETURN_HOME_UNSHIFT 0x02
#define SET_4BIT_MODE 0x28
#define DISPLAY_ON_CURSOR_OFF 0x0C
#define CURSOR_RIGHT_NO_SHIFT_LEFT 0x06
#define RETURN_HOME 0x80
/* Bits en 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
/* UART */
const uart_port_t uart_num = UART_NUM_0;
const int uart_buffer_size = (1024 * 2);
QueueHandle_t uart_queue;
/* Estructura para el LCD */
typedef struct {
uint8_t i2c_address;
uint8_t i2c_port;
uint8_t screen_size;
uint8_t screen_backlight;
} lcd_i2c_device_t;
/* Prototipos */
void i2c_init(void);
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_print_msg(lcd_i2c_device_t * lcd, char * msg);
void uartInit(void);
void uartPuts(const char *data);
void uartGets(uint8_t* data);
/* =========== MATRIZ FIJA DE 8x8 =========== */
#define REN 8
#define COL 8
static char sopa[REN][COL] = {
{'L','E','D','S','I','S','A','O'},
{'W','A','T','S','O','N','O','N'},
{'O','A','H','G','T','A','C','O'},
{'L','A','T','I','G','I','D','R'},
{'X','T','R','D','I','N','A','C'},
{'A','S','L','S','T','H','I','N'},
{'B','E','O','C','K','C','O','I'},
{'R','T','O','S','E','R','S','S'}
};
/* =========== LECTURA DE PALABRAS =========== */
#define NUM_PALABRAS 7 // Número de palabras a leer
#define MAX_LEN 20 // Longitud máxima de cada palabra
char palabras[NUM_PALABRAS][MAX_LEN]; // Donde guardamos las palabras leídas
/* ===========================================
FUNCIONES DE BÚSQUEDA EN LA SOPA DE LETRAS
=========================================== */
/**
* checkDirection:
* Verifica si la palabra 'word' se encuentra iniciando en (row, col)
* avanzando en la dirección dada por (dr, dc).
* - dr, dc pueden ser -1, 0 o 1 para checar horizontal, vertical y diagonales.
* Retorna 1 si la palabra se encuentra completa en esa dirección, 0 si no.
*/
static int checkDirection(const char word[], int row, int col, int dr, int dc)
{
int len = strlen(word);
for(int i = 0; i < len; i++){
int rr = row + i*dr;
int cc = col + i*dc;
// Verificamos límites
if(rr < 0 || rr >= REN || cc < 0 || cc >= COL){
return 0; // fuera de la matriz
}
// Verificamos si coincide la letra
if(sopa[rr][cc] != word[i]){
return 0; // no coincide
}
}
return 1; // completó la coincidencia
}
/**
* findWord:
* Busca la palabra 'word' en la matriz 'sopa'.
* Si se encuentra, llena *found_row y *found_col con la coordenada (fila, col)
* donde inicia la palabra y retorna 1. Si no se encuentra, retorna 0.
*
* Se buscan TODAS las direcciones: horizontales, verticales y diagonales
* (8 direcciones en total).
*/
static int findWord(const char word[], int* found_row, int* found_col)
{
// Direcciones a probar (dr, dc)
// horizontal derecha (0,1),
// horizontal izquierda (0,-1),
// vertical abajo (1,0),
// vertical arriba (-1,0),
// diagonal bajando derecha (1,1),
// diagonal subiendo izquierda (-1,-1),
// diagonal bajando izquierda (1,-1),
// diagonal subiendo derecha (-1,1)
int directions[8][2] = {
{0, 1}, {0, -1},
{1, 0}, {-1, 0},
{1, 1}, {-1, -1},
{1, -1}, {-1, 1}
};
for(int r = 0; r < REN; r++){
for(int c = 0; c < COL; c++){
if(sopa[r][c] == word[0]) {
// La primera letra coincide, probamos direcciones
for(int d = 0; d < 8; d++){
int dr = directions[d][0];
int dc = directions[d][1];
if(checkDirection(word, r, c, dr, dc)){
*found_row = r;
*found_col = c;
return 1; // ¡Encontrada!
}
}
}
}
}
return 0; // no se encontró en ningún lado
}
/* ===========================================
INICIALIZACIÓN I2C y LCD
=========================================== */
void i2c_init(void)
{
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_SDA_LCD,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_SCL_LCD,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_1, &i2c_config));
ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_1, I2C_MODE_MASTER, 0, 0, 0));
}
void lcd_i2c_write_byte(lcd_i2c_device_t * lcd, uint8_t data)
{
i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
i2c_master_start(cmd_handle);
i2c_master_write_byte(cmd_handle, (lcd->i2c_address << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
i2c_master_write_byte(cmd_handle, data, I2C_ACK_CHECK_EN);
i2c_master_stop(cmd_handle);
i2c_master_cmd_begin(lcd->i2c_port, cmd_handle, 100 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd_handle);
}
void lcd_i2c_write_command(lcd_i2c_device_t * lcd, uint8_t register_select, uint8_t cmd)
{
// Config inicial: RS y BL
uint8_t config = (register_select)? (1 << PCF8574_RS) : 0;
if(lcd->screen_backlight){
config |= (1 << PCF8574_BL);
}
// Parte alta
uint8_t data = config | (cmd & 0xF0);
// Enable = 1
data |= (1 << PCF8574_EN);
lcd_i2c_write_byte(lcd, data);
ets_delay_us(10);
// Enable = 0
data &= ~(1 << PCF8574_EN);
lcd_i2c_write_byte(lcd, data);
ets_delay_us(50);
// Parte baja
data = config | ((cmd << 4) & 0xF0);
data |= (1 << PCF8574_EN);
lcd_i2c_write_byte(lcd, data);
ets_delay_us(10);
data &= ~(1 << PCF8574_EN);
lcd_i2c_write_byte(lcd, data);
ets_delay_us(50);
if (cmd == CLEAR_DISPLAY) {
vTaskDelay(2 / portTICK_PERIOD_MS);
}
}
void lcd_set_cursor(lcd_i2c_device_t * lcd, uint8_t column, uint8_t row)
{
static const uint8_t row_offsets[] = {0x00, 0x40, 0x14, 0x54};
if(row > 3) row = 3;
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0x80 | (column + row_offsets[row]));
}
void lcd_init(lcd_i2c_device_t * lcd)
{
// Secuencia típica de inicialización 4 bits
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_print_msg(lcd_i2c_device_t * lcd, char * msg)
{
while(*msg) {
lcd_i2c_write_command(lcd, LCD_RS_DATA, (uint8_t)*msg);
msg++;
}
}
/* ===========================================
UART
=========================================== */
void uartInit(){
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
.rx_flow_ctrl_thresh = 122,
};
// Configure UART
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
// Install UART driver
ESP_ERROR_CHECK(uart_driver_install(uart_num, uart_buffer_size,
uart_buffer_size, 10, &uart_queue, 0));
}
void uartPuts(const char *data){
uart_write_bytes(uart_num, data, strlen(data));
}
void uartGets(uint8_t* data){
// Lectura sencilla de ejemplo (bloqueante)
int length = 0;
// Espera hasta que haya algo en el buffer
while(!length){
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
}
// Lee 'length' bytes
length = uart_read_bytes(uart_num, data, length, 100 / portTICK_PERIOD_MS);
// Reemplaza \n o \r por \0 para terminar la cadena
for (int i = 0; i < length; i++){
if(data[i] == '\n' || data[i] == '\r'){
data[i] = '\0';
break;
}
}
}
/* ===========================================
TAREA PRINCIPAL
=========================================== */
static void vTaskCode(void *pvParameters)
{
// Inicializamos el LCD
lcd_i2c_device_t my_lcd = {
.i2c_port = I2C_NUM_1,
.i2c_address = I2C_ADDRESS_LCD,
.screen_size = LCD_20X04,
.screen_backlight = 1,
};
lcd_init(&my_lcd);
// Limpia la pantalla
lcd_i2c_write_command(&my_lcd, LCD_RS_CMD, CLEAR_DISPLAY);
/* 1) PEDIR LAS PALABRAS AL USUARIO */
for(int i = 0; i < NUM_PALABRAS; i++){
char buffer[64];
snprintf(buffer, sizeof(buffer), "Ingresa palabra #%d:\r\n", i+1);
uartPuts(buffer);
// Leer la palabra por UART
uint8_t temp[MAX_LEN];
memset(temp, 0, sizeof(temp));
uartGets(temp);
// Guardarla en palabras[i]
strncpy(palabras[i], (char*)temp, MAX_LEN-1);
palabras[i][MAX_LEN-1] = '\0'; // seguridad
}
/* 2) BUSCAR CADA PALABRA Y MOSTRAR EN LCD */
int index = 0;
while(1){
// Limpia la pantalla
lcd_i2c_write_command(&my_lcd, LCD_RS_CMD, CLEAR_DISPLAY);
// Imprime hasta 4 palabras con su coordenada
for(int linea = 0; linea < 4; linea++){
if(index >= NUM_PALABRAS) {
break;
}
// Busca la palabra en la sopa
int row, col;
char buffer[32];
if(findWord(palabras[index], &row, &col)){
// Si se encontró => muestra "PALABRA (r,c)"
// Nota: en la matriz está 0-based.
// Si lo quieres 1-based, usa (row+1, col+1).
snprintf(buffer, sizeof(buffer), "%s (%d,%d)",
palabras[index], row, col);
} else {
// No se encontró
snprintf(buffer, sizeof(buffer), "%s NoHallada",
palabras[index]);
}
// Ponemos cursor en la línea 'linea' y col=0
lcd_set_cursor(&my_lcd, 0, linea);
lcd_i2c_print_msg(&my_lcd, buffer);
index++;
if(index >= NUM_PALABRAS){
break;
}
}
// Si ya mostramos todas, salimos del bucle
if(index >= NUM_PALABRAS){
break;
}
// Esperamos 2 segundos, luego pasamos a la siguiente “página”
vTaskDelay(pdMS_TO_TICKS(2000));
}
// Fin: suspendemos la tarea
vTaskSuspend(NULL);
}
void app_main()
{
i2c_init();
uartInit();
uartPuts("Iniciando programa...\r\n");
xTaskCreate(vTaskCode, "vTaskCode", 2*(2*1024), NULL, 1, NULL);
}