Comunicação Serial e Protocolos 📡
UART - Comunicação Serial Assíncrona
O UART representa um dos protocolos de comunicação mais fundamentais e amplamente utilizados em sistemas embarcados. Sua simplicidade e confiabilidade o tornam escolha ideal para comunicação ponto-a-ponto entre microcontroladores, sensores inteligentes e computadores host durante desenvolvimento e debug.
O ESP32 possui três UARTs independentes, cada um com buffers de transmissão e recepção em hardware. UART0 é tipicamente reservado para comunicação com o console de debug e programação, enquanto UART1 e UART2 estão disponíveis para uso pela aplicação. Cada UART pode operar em velocidades configuráveis de poucos bauds até vários megabauds.
A natureza assíncrona do UART significa que transmissor e receptor não compartilham clock comum, dependendo de baudrates precisamente configurados em ambos os lados. Bits de start e stop delimitam cada byte transmitido, permitindo que receptor sincronize com início de cada caractere. Bits de paridade opcionais fornecem verificação básica de erros.
🔌 Configuração de Parâmetros UART
A configuração completa de uma porta UART envolve vários parâmetros que devem corresponder entre transmissor e receptor para comunicação bem-sucedida. O baudrate determina velocidade de transmissão em bits por segundo e deve ser idêntico em ambos os lados dentro de margem de tolerância de poucos por cento.
O número de bits de dados é tipicamente 8, mas pode ser configurado para 5, 6, 7 ou 8 bits dependendo dos requisitos da aplicação. Protocolos mais antigos às vezes usam 7 bits para texto ASCII, mas 8 bits é padrão universal para dados binários modernos.
Bits de paridade oferecem verificação rudimentar de erros onde bit adicional é calculado baseado nos bits de dados, permitindo detecção de erros de bit único. Paridade pode ser par, ímpar ou desabilitada completamente. Para aplicações críticas, protocolos de camada superior tipicamente implementam checksums mais robustos.
Bits de stop marcam fim de cada caractere e podem ser configurados como 1, 1.5 ou 2 bits. Um bit de stop é mais comum e suficiente para a maioria das aplicações. Bits de stop adicionais eram usados em sistemas antigos mais lentos mas raramente são necessários em hardware moderno.
#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include <string.h>
static const char *TAG = "UART_EXAMPLE";
// Configuração dos pinos e UART
#define UART_NUM UART_NUM_1
#define UART_TX_PIN GPIO_NUM_17
#define UART_RX_PIN GPIO_NUM_16
#define UART_BAUD_RATE 115200
#define UART_BUF_SIZE (1024)
// Fila para eventos UART
static QueueHandle_t uart_queue;
/**
* Inicializar e configurar UART
*
* Configura todos os parâmetros necessários para comunicação
* serial confiável incluindo buffers, pins e formato de dados
*/
esp_err_t initialize_uart(void)
{
ESP_LOGI(TAG, "Inicializando UART%d", UART_NUM);
// Configurar parâmetros UART
uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 122,
.source_clk = UART_SCLK_APB,
};
// Aplicar configuração
esp_err_t ret = uart_param_config(UART_NUM, &uart_config);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Falha ao configurar parâmetros UART: %s",
esp_err_to_name(ret));
return ret;
}
// Configurar pinos TX e RX
ret = uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Falha ao configurar pinos UART: %s",
esp_err_to_name(ret));
return ret;
}
// Instalar driver UART com buffers
// RX buffer, TX buffer, fila de eventos
ret = uart_driver_install(UART_NUM, UART_BUF_SIZE * 2,
UART_BUF_SIZE * 2, 20,
&uart_queue, 0);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Falha ao instalar driver UART: %s",
esp_err_to_name(ret));
return ret;
}
ESP_LOGI(TAG, "UART inicializado: %d baud, 8N1", UART_BAUD_RATE);
return ESP_OK;
}
/**
* Enviar string via UART
*
* Função helper que simplifica envio de strings terminadas em null
*/
int uart_send_string(const char *str)
{
int len = strlen(str);
int bytes_written = uart_write_bytes(UART_NUM, str, len);
if (bytes_written < 0) {
ESP_LOGE(TAG, "Erro ao enviar dados");
return -1;
}
if (bytes_written < len) {
ESP_LOGW(TAG, "Enviados apenas %d de %d bytes", bytes_written, len);
}
return bytes_written;
}
/**
* Enviar dados binários via UART
*
* Para transmissão de dados binários onde zeros são válidos
*/
int uart_send_binary(const uint8_t *data, size_t length)
{
int bytes_written = uart_write_bytes(UART_NUM, (const char *)data, length);
if (bytes_written > 0) {
// Aguardar transmissão completar
uart_wait_tx_done(UART_NUM, pdMS_TO_TICKS(1000));
}
return bytes_written;
}
/**
* Ler linha completa da UART com timeout
*
* Lê até encontrar newline ou atingir tamanho máximo/timeout
*/
int uart_read_line(char *buffer, size_t max_len, TickType_t timeout_ticks)
{
size_t bytes_read = 0;
TickType_t start_time = xTaskGetTickCount();
while (bytes_read < max_len - 1) {
uint8_t c;
int len = uart_read_bytes(UART_NUM, &c, 1, timeout_ticks);
if (len <= 0) {
// Timeout ou erro
break;
}
// Verificar timeout total
if ((xTaskGetTickCount() - start_time) >= timeout_ticks) {
ESP_LOGW(TAG, "Timeout lendo linha");
break;
}
buffer[bytes_read++] = c;
// Parar em newline
if (c == '\n' || c == '\r') {
break;
}
}
buffer[bytes_read] = '\0';
return bytes_read;
}
/**
* Task de recepção UART usando eventos
*
* Demonstra padrão eficiente usando fila de eventos do driver
* ao invés de polling contínuo
*/
void uart_event_task(void *pvParameters)
{
ESP_LOGI(TAG, "Task de eventos UART iniciada");
uart_event_t event;
uint8_t *rx_buffer = malloc(UART_BUF_SIZE);
if (rx_buffer == NULL) {
ESP_LOGE(TAG, "Falha ao alocar buffer RX");
vTaskDelete(NULL);
return;
}
while (1) {
// Aguardar evento do driver UART
if (xQueueReceive(uart_queue, &event, portMAX_DELAY)) {
switch (event.type) {
case UART_DATA:
// Dados disponíveis para leitura
ESP_LOGD(TAG, "Evento: %d bytes recebidos", event.size);
int len = uart_read_bytes(UART_NUM, rx_buffer,
event.size, portMAX_DELAY);
if (len > 0) {
// Processar dados recebidos
ESP_LOGI(TAG, "Recebido: %.*s", len, rx_buffer);
// Echo de volta
uart_write_bytes(UART_NUM, (const char *)rx_buffer, len);
}
break;
case UART_FIFO_OVF:
// Buffer FIFO de hardware transbordou
ESP_LOGW(TAG, "UART FIFO overflow");
uart_flush_input(UART_NUM);
xQueueReset(uart_queue);
break;
case UART_BUFFER_FULL:
// Buffer de software do driver transbordou
ESP_LOGW(TAG, "Buffer UART cheio");
uart_flush_input(UART_NUM);
xQueueReset(uart_queue);
break;
case UART_BREAK:
// Condição de break detectada
ESP_LOGW(TAG, "UART break detectado");
break;
case UART_PARITY_ERR:
// Erro de paridade
ESP_LOGE(TAG, "Erro de paridade UART");
break;
case UART_FRAME_ERR:
// Erro de frame (bit de stop incorreto)
ESP_LOGE(TAG, "Erro de frame UART");
break;
default:
ESP_LOGW(TAG, "Evento UART desconhecido: %d", event.type);
break;
}
}
}
free(rx_buffer);
vTaskDelete(NULL);
}
/**
* Implementar protocolo simples de comando-resposta
*
* Demonstra padrão comum onde comandos de texto são parseados
* e respostas apropriadas são enviadas
*/
void uart_command_processor_task(void *pvParameters)
{
ESP_LOGI(TAG, "Processador de comandos UART iniciado");
char command_buffer[128];
// Enviar prompt inicial
uart_send_string("\r\n=== Sistema ESP32 ===\r\n");
uart_send_string("Digite 'help' para lista de comandos\r\n");
uart_send_string("> ");
while (1) {
// Ler comando completo
int len = uart_read_line(command_buffer, sizeof(command_buffer),
pdMS_TO_TICKS(100));
if (len > 0) {
// Remover caracteres de fim de linha
while (len > 0 && (command_buffer[len-1] == '\r' ||
command_buffer[len-1] == '\n')) {
command_buffer[--len] = '\0';
}
if (len == 0) {
uart_send_string("> ");
continue;
}
ESP_LOGI(TAG, "Comando recebido: '%s'", command_buffer);
// Processar comandos
if (strcmp(command_buffer, "help") == 0) {
uart_send_string("Comandos disponíveis:\r\n");
uart_send_string(" help - Mostra esta mensagem\r\n");
uart_send_string(" status - Mostra status do sistema\r\n");
uart_send_string(" reset - Reinicia o sistema\r\n");
uart_send_string(" echo <msg> - Faz eco da mensagem\r\n");
} else if (strcmp(command_buffer, "status") == 0) {
char response[256];
int free_heap = esp_get_free_heap_size();
int min_free = esp_get_minimum_free_heap_size();
snprintf(response, sizeof(response),
"Status do Sistema:\r\n"
" Heap livre: %d bytes\r\n"
" Heap mínimo: %d bytes\r\n"
" Uptime: %lu segundos\r\n",
free_heap, min_free,
(unsigned long)(xTaskGetTickCount() * portTICK_PERIOD_MS / 1000));
uart_send_string(response);
} else if (strcmp(command_buffer, "reset") == 0) {
uart_send_string("Reiniciando sistema...\r\n");
vTaskDelay(pdMS_TO_TICKS(100));
esp_restart();
} else if (strncmp(command_buffer, "echo ", 5) == 0) {
// Eco da mensagem após "echo "
uart_send_string("Echo: ");
uart_send_string(command_buffer + 5);
uart_send_string("\r\n");
} else {
uart_send_string("Comando desconhecido. Digite 'help' para ajuda.\r\n");
}
// Mostrar prompt novamente
uart_send_string("> ");
}
}
}
/**
* Demonstrar transmissão de dados estruturados via UART
*
* Envia estrutura binária com header e checksum
*/
void uart_send_structured_data(void)
{
// Definir formato de pacote
typedef struct __attribute__((packed)) {
uint8_t header[2]; // 0xAA, 0x55
uint16_t packet_id;
uint32_t timestamp;
float sensor_value;
uint8_t checksum;
} sensor_packet_t;
sensor_packet_t packet;
// Preencher pacote
packet.header[0] = 0xAA;
packet.header[1] = 0x55;
packet.packet_id = 1234;
packet.timestamp = xTaskGetTickCount();
packet.sensor_value = 25.5f;
// Calcular checksum simples (XOR de todos os bytes)
uint8_t *packet_bytes = (uint8_t *)&packet;
uint8_t checksum = 0;
for (size_t i = 0; i < sizeof(packet) - 1; i++) {
checksum ^= packet_bytes[i];
}
packet.checksum = checksum;
// Enviar pacote binário
ESP_LOGI(TAG, "Enviando pacote estruturado (%d bytes)", sizeof(packet));
uart_send_binary((uint8_t *)&packet, sizeof(packet));
}
void app_main(void)
{
ESP_LOGI(TAG, "Demonstrando comunicação UART");
// Inicializar UART
esp_err_t ret = initialize_uart();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Falha na inicialização UART, abortando");
return;
}
// Criar task de processamento de eventos
xTaskCreate(uart_event_task, "UART_Events",
4096, NULL, 10, NULL);
// Criar task de processador de comandos
xTaskCreate(uart_command_processor_task, "UART_Commands",
4096, NULL, 5, NULL);
// Demonstrar envio periódico de dados estruturados
while (1) {
vTaskDelay(pdMS_TO_TICKS(5000));
uart_send_structured_data();
}
}I2C - Barramento de Comunicação Multi-Master
O protocolo I2C, também conhecido como TWI em algumas implementações, oferece barramento de comunicação serial síncrono que permite conectar múltiplos dispositivos usando apenas dois fios. Esta economia de pinos torna I2C extremamente popular para comunicação com sensores, displays e outros periféricos em sistemas embarcados com recursos limitados de GPIO.
O barramento I2C opera com arquitetura master-slave onde um ou mais masters controlam comunicação com múltiplos slaves. Cada dispositivo slave possui endereço único de 7 ou 10 bits que o identifica no barramento. O ESP32 pode operar como master ou slave, embora uso como master seja muito mais comum em aplicações típicas de IoT.
A comunicação I2C utiliza dois sinais: SCL para clock e SDA para dados bidirecionais. Ambas as linhas requerem resistores pull-up externos, tipicamente entre 2.2kΩ e 10kΩ dependendo da velocidade do barramento e capacitância total dos dispositivos conectados. O ESP32 possui pull-ups internos fracos que podem ser suficientes para aplicações simples, mas resistores externos oferecem maior confiabilidade.
⚡ Velocidades e Modos I2C
O protocolo I2C suporta várias velocidades padronizadas que oferecem trade-offs entre throughput e compatibilidade com dispositivos. O modo standard opera a 100kHz e é suportado por praticamente todos os dispositivos I2C, oferecendo compatibilidade máxima embora com throughput limitado.
O modo fast aumenta velocidade para 400kHz, quadruplicando o throughput comparado ao modo standard. A maioria dos sensores modernos suporta fast mode, tornando-o escolha comum para aplicações que precisam de maior taxa de dados sem complexidade adicional. Tenha em mente que velocidades mais altas são mais sensíveis a ruído e qualidade de conexões.
O modo fast plus estende velocidade para 1MHz, oferecendo throughput ainda maior para aplicações exigentes. Nem todos os dispositivos suportam esta velocidade, então você deve verificar datasheets cuidadosamente. Resistores pull-up precisam ser escolhidos mais cuidadosamente para suportar fast plus devido a correntes maiores necessárias para carregar capacitância do barramento rapidamente.
O modo high speed pode alcançar até 3.4MHz mas requer hardware especializado e raramente é usado em aplicações de microcontroladores típicas. Para a maioria dos projetos IoT, fast mode de 400kHz oferece melhor equilíbrio entre performance e simplicidade.
#include "driver/i2c.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include <string.h>
static const char *TAG = "I2C_EXAMPLE";
// Configuração I2C
#define I2C_MASTER_SCL_IO 22 // GPIO para clock
#define I2C_MASTER_SDA_IO 21 // GPIO para dados
#define I2C_MASTER_NUM I2C_NUM_0 // Número do port I2C
#define I2C_MASTER_FREQ_HZ 400000 // 400kHz (fast mode)
#define I2C_MASTER_TX_BUF_DISABLE 0
#define I2C_MASTER_RX_BUF_DISABLE 0
#define I2C_MASTER_TIMEOUT_MS 1000
// Endereços de dispositivos comuns
#define BMP280_ADDR 0x76 // Sensor de pressão
#define MPU6050_ADDR 0x68 // IMU
#define OLED_SSD1306_ADDR 0x3C // Display OLED
/**
* Inicializar barramento I2C como master
*
* Configura pinos, velocidade e parâmetros de timing
* para comunicação confiável com dispositivos I2C
*/
esp_err_t i2c_master_init(void)
{
ESP_LOGI(TAG, "Inicializando barramento I2C");
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE, // Habilitar pull-up interno
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
esp_err_t ret = i2c_param_config(I2C_MASTER_NUM, &conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Falha ao configurar I2C: %s", esp_err_to_name(ret));
return ret;
}
// Instalar driver I2C
ret = i2c_driver_install(I2C_MASTER_NUM, conf.mode,
I2C_MASTER_RX_BUF_DISABLE,
I2C_MASTER_TX_BUF_DISABLE, 0);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Falha ao instalar driver I2C: %s",
esp_err_to_name(ret));
return ret;
}
ESP_LOGI(TAG, "I2C inicializado: %dkHz, SCL=%d, SDA=%d",
I2C_MASTER_FREQ_HZ / 1000, I2C_MASTER_SCL_IO,
I2C_MASTER_SDA_IO);
return ESP_OK;
}
/**
* Escanear barramento I2C procurando dispositivos
*
* Tenta comunicar com todos os endereços possíveis
* para descobrir quais dispositivos estão conectados
*/
void i2c_scan_bus(void)
{
ESP_LOGI(TAG, "Escaneando barramento I2C...");
int devices_found = 0;
// Endereços válidos vão de 0x03 a 0x77
// (0x00-0x02 e 0x78-0x7F são reservados)
for (uint8_t addr = 0x03; addr <= 0x77; addr++) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd,
pdMS_TO_TICKS(50));
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) {
ESP_LOGI(TAG, " Dispositivo encontrado em 0x%02X", addr);
devices_found++;
}
}
if (devices_found == 0) {
ESP_LOGW(TAG, "Nenhum dispositivo I2C encontrado!");
ESP_LOGW(TAG, "Verifique conexões e pull-ups");
} else {
ESP_LOGI(TAG, "Scan completo: %d dispositivo(s) encontrado(s)",
devices_found);
}
}
/**
* Ler registrador de dispositivo I2C
*
* Padrão comum: enviar endereço do registrador, depois ler resposta
* Este é o protocolo usado pela maioria dos sensores I2C
*/
esp_err_t i2c_read_register(uint8_t device_addr, uint8_t reg_addr,
uint8_t *data, size_t len)
{
// Criar handle de comando I2C
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// Fase de escrita: enviar endereço do registrador
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (device_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
// Repeated start para mudar para leitura
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (device_addr << 1) | I2C_MASTER_READ, true);
// Fase de leitura: ler bytes de dados
if (len > 1) {
i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
// Executar transação completa
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd,
pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGD(TAG, "Erro ao ler registrador 0x%02X de 0x%02X: %s",
reg_addr, device_addr, esp_err_to_name(ret));
}
return ret;
}
/**
* Escrever registrador de dispositivo I2C
*
* Envia endereço do registrador seguido de dados a escrever
*/
esp_err_t i2c_write_register(uint8_t device_addr, uint8_t reg_addr,
const uint8_t *data, size_t len)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (device_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_write(cmd, data, len, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd,
pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGD(TAG, "Erro ao escrever registrador 0x%02X de 0x%02X: %s",
reg_addr, device_addr, esp_err_to_name(ret));
}
return ret;
}
/**
* Exemplo de comunicação com sensor BMP280
*
* Demonstra padrão típico de inicialização e leitura de sensor I2C
*/
typedef struct {
uint8_t device_addr;
bool initialized;
} bmp280_dev_t;
esp_err_t bmp280_init(bmp280_dev_t *dev)
{
ESP_LOGI(TAG, "Inicializando BMP280");
dev->device_addr = BMP280_ADDR;
dev->initialized = false;
// Ler ID do chip para verificar comunicação
uint8_t chip_id;
esp_err_t ret = i2c_read_register(dev->device_addr, 0xD0, &chip_id, 1);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Falha ao comunicar com BMP280");
return ret;
}
if (chip_id != 0x58) {
ESP_LOGE(TAG, "ID do chip incorreto: 0x%02X (esperado 0x58)", chip_id);
return ESP_FAIL;
}
ESP_LOGI(TAG, "BMP280 detectado (chip_id=0x%02X)", chip_id);
// Configurar sensor: modo normal, oversampling 16x
uint8_t config_data[] = {0xB7}; // ctrl_meas register
ret = i2c_write_register(dev->device_addr, 0xF4, config_data, 1);
if (ret == ESP_OK) {
dev->initialized = true;
ESP_LOGI(TAG, "BMP280 inicializado com sucesso");
}
return ret;
}
esp_err_t bmp280_read_temperature(bmp280_dev_t *dev, float *temperature)
{
if (!dev->initialized) {
return ESP_ERR_INVALID_STATE;
}
// Ler dados brutos de temperatura (3 bytes, 0xFA-0xFC)
uint8_t temp_data[3];
esp_err_t ret = i2c_read_register(dev->device_addr, 0xFA, temp_data, 3);
if (ret != ESP_OK) {
return ret;
}
// Converter dados brutos em temperatura
// (simplificado - versão real requer calibração)
int32_t adc_temp = ((uint32_t)temp_data[0] << 12) |
((uint32_t)temp_data[1] << 4) |
((uint32_t)temp_data[2] >> 4);
// Fórmula simplificada (requer dados de calibração reais)
*temperature = (float)adc_temp / 5120.0f;
return ESP_OK;
}
/**
* Task de monitoramento de sensores I2C
*
* Demonstra leitura periódica de múltiplos sensores
*/
void i2c_sensor_monitoring_task(void *pvParameters)
{
ESP_LOGI(TAG, "Task de monitoramento de sensores iniciada");
bmp280_dev_t bmp280;
// Inicializar sensor
if (bmp280_init(&bmp280) != ESP_OK) {
ESP_LOGE(TAG, "Falha ao inicializar BMP280, task terminando");
vTaskDelete(NULL);
return;
}
while (1) {
float temperature;
if (bmp280_read_temperature(&bmp280, &temperature) == ESP_OK) {
ESP_LOGI(TAG, "Temperatura: %.2f°C", temperature);
} else {
ESP_LOGW(TAG, "Falha ao ler temperatura");
}
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
/**
* Demonstrar recuperação de erros I2C
*
* Implementa retry logic para lidar com falhas temporárias
*/
esp_err_t i2c_read_with_retry(uint8_t device_addr, uint8_t reg_addr,
uint8_t *data, size_t len, int max_retries)
{
esp_err_t ret;
int retry_count = 0;
do {
ret = i2c_read_register(device_addr, reg_addr, data, len);
if (ret == ESP_OK) {
if (retry_count > 0) {
ESP_LOGI(TAG, "Leitura bem-sucedida após %d tentativa(s)",
retry_count);
}
return ESP_OK;
}
retry_count++;
if (retry_count < max_retries) {
ESP_LOGW(TAG, "Tentativa %d/%d falhou, tentando novamente...",
retry_count, max_retries);
vTaskDelay(pdMS_TO_TICKS(10));
}
} while (retry_count < max_retries);
ESP_LOGE(TAG, "Falha após %d tentativas", max_retries);
return ret;
}
void app_main(void)
{
ESP_LOGI(TAG, "Demonstrando comunicação I2C");
// Inicializar barramento I2C
esp_err_t ret = i2c_master_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Falha na inicialização I2C, abortando");
return;
}
// Escanear barramento
i2c_scan_bus();
// Criar task de monitoramento
xTaskCreate(i2c_sensor_monitoring_task, "I2C_Monitor",
4096, NULL, 5, NULL);
ESP_LOGI(TAG, "Sistema I2C inicializado");
}