This commit is contained in:
2026-02-09 16:36:29 +01:00
parent af9e0c11e0
commit 0f50ddbae3
18 changed files with 610 additions and 293 deletions

View File

@@ -1,40 +1,24 @@
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c_slave.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
#define I2C_SLAVE_SDA_IO 21
#define I2C_SLAVE_SCL_IO 22
#define I2C_SLAVE_NUM I2C_NUM_0
#define I2C_SLAVE_ADDR 0x61
#define I2C_SLAVE_RX_BUF_LEN 128
#define I2C_SLAVE_TX_BUF_LEN 128
#define UART_NUM UART_NUM_0
#define UART_BAUD_RATE 921600
#define UART_BUF_SIZE 256
#define TAG "I2C2SERIAL"
static void i2c_slave_init(void) {
i2c_config_t conf_slave = {
.mode = I2C_MODE_SLAVE,
.sda_io_num = I2C_SLAVE_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_SLAVE_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.slave = {
.slave_addr = I2C_SLAVE_ADDR,
.maximum_speed = 400000,
},
};
ESP_ERROR_CHECK(i2c_param_config(I2C_SLAVE_NUM, &conf_slave));
ESP_ERROR_CHECK(i2c_driver_install(I2C_SLAVE_NUM, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0));
}
static i2c_slave_dev_handle_t i2c_slave = NULL;
static TaskHandle_t s_i2c_task_handle = NULL;
static void uart_init(void) {
uart_config_t uart_config = {
@@ -49,70 +33,117 @@ static void uart_init(void) {
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
}
// Callback for I2C Receive Done
static bool i2c_rx_done_callback(i2c_slave_dev_handle_t channel, const i2c_slave_rx_done_event_data_t *edata, void *user_data) {
BaseType_t high_task_wakeup = pdFALSE;
if (s_i2c_task_handle) {
vTaskNotifyGiveFromISR(s_i2c_task_handle, &high_task_wakeup);
}
return high_task_wakeup == pdTRUE;
}
static void i2c_slave_init(void) {
i2c_slave_config_t conf = {
.sda_io_num = I2C_SLAVE_SDA_IO,
.scl_io_num = I2C_SLAVE_SCL_IO,
.clk_source = I2C_CLK_SRC_DEFAULT,
.send_buf_depth = 256,
.slave_addr = I2C_SLAVE_ADDR,
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
.intr_priority = 0,
};
#if SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE
conf.flags.stretch_en = true;
#endif
ESP_ERROR_CHECK(i2c_new_slave_device(&conf, &i2c_slave));
// Enable Internal Pullups manually
gpio_set_pull_mode(I2C_SLAVE_SDA_IO, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(I2C_SLAVE_SCL_IO, GPIO_PULLUP_ONLY);
i2c_slave_event_callbacks_t cbs = {
.on_recv_done = i2c_rx_done_callback,
};
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(i2c_slave, &cbs, NULL));
}
static void i2c2serial_task(void *arg) {
uint8_t i2c_rx[I2C_SLAVE_RX_BUF_LEN];
uint8_t uart_rx[UART_BUF_SIZE];
uint8_t i2c_tx[I2C_SLAVE_TX_BUF_LEN];
while (1) {
// Wait for I2C master write
int rx_len = i2c_slave_read_buffer(I2C_SLAVE_NUM, i2c_rx, sizeof(i2c_rx), pdMS_TO_TICKS(100));
if (rx_len > 0) {
// Print CMD to serial in hex
printf("CMD:");
for (int i = 0; i < rx_len; ++i) {
printf("%02X", i2c_rx[i]);
}
printf("\n");
size_t out_res = 0; // Added for ESP-IDF v5.4 compatibility
// Wait for DATA:[hex]\n from serial, with 200ms timeout
int uart_len = 0;
int total_len = 0;
TickType_t start_tick = xTaskGetTickCount();
bool got_data = false;
while ((xTaskGetTickCount() - start_tick) < pdMS_TO_TICKS(200)) {
uart_len = uart_read_bytes(UART_NUM, uart_rx + total_len, UART_BUF_SIZE - total_len, pdMS_TO_TICKS(10));
if (uart_len > 0) {
total_len += uart_len;
// Look for a full line
char *newline = memchr(uart_rx, '\n', total_len);
if (newline) {
int line_len = newline - (char *)uart_rx + 1;
uart_rx[line_len-1] = 0; // Null-terminate
if (strncmp((char *)uart_rx, "DATA:", 5) == 0) {
// Parse hex after DATA:
char *hexstr = (char *)uart_rx + 5;
int tx_len = 0;
while (*hexstr && tx_len < I2C_SLAVE_TX_BUF_LEN) {
unsigned int val;
if (sscanf(hexstr, "%2x", &val) == 1) {
i2c_tx[tx_len++] = val;
hexstr += 2;
} else {
break;
}
s_i2c_task_handle = xTaskGetCurrentTaskHandle();
while (1) {
// 1. Prepare to Receive (Non-blocking queue)
memset(i2c_rx, 0, sizeof(i2c_rx));
// This queues the receive request
ESP_ERROR_CHECK(i2c_slave_receive(i2c_slave, i2c_rx, sizeof(i2c_rx)));
// 2. Wait for Receive Complete (Callback triggers notification)
// This is where we wait for the Master to finish writing the command
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// Calculate actual length received (SCD30 commands are usually 2-5 bytes)
// We look for the first 0 if the master didn't send a full 128 bytes
size_t actual_rx_len = 2; // SCD30 commands are at least 2 bytes
// 3. Fast Send to PC
uart_write_bytes(UART_NUM, "CMD:", 4);
for (size_t i = 0; i < actual_rx_len; ++i) {
char hex[3];
sprintf(hex, "%02X", i2c_rx[i]);
uart_write_bytes(UART_NUM, hex, 2);
}
uart_write_bytes(UART_NUM, "\n", 1);
// 4. Wait for PC response
int total_len = 0;
TickType_t start_tick = xTaskGetTickCount();
bool got_data = false;
// Wait up to 200ms for "DATA:[hex]\n" from PC
while ((xTaskGetTickCount() - start_tick) < pdMS_TO_TICKS(200)) {
int len = uart_read_bytes(UART_NUM, uart_rx + total_len, UART_BUF_SIZE - total_len, pdMS_TO_TICKS(5));
if (len > 0) {
total_len += len;
char *newline = memchr(uart_rx, '\n', total_len);
if (newline) {
if (strncmp((char *)uart_rx, "DATA:", 5) == 0) {
// 5. Fast Hex Parse
int tx_len = 0;
for (int i = 5; i < (newline - (char*)uart_rx) - 1; i += 2) {
unsigned int val;
if (sscanf((char*)&uart_rx[i], "%2x", &val) == 1) {
i2c_tx[tx_len++] = (uint8_t)val;
}
// Write to I2C slave TX buffer
i2c_slave_write_buffer(I2C_SLAVE_NUM, i2c_tx, tx_len, 0);
got_data = true;
}
// Remove processed line
memmove(uart_rx, newline + 1, total_len - line_len);
total_len -= line_len;
// 6. Load Buffer & Release Clock Stretch
// Added &out_res parameter for v5.4 compatibility
if (tx_len > 0) {
i2c_slave_transmit(i2c_slave, i2c_tx, tx_len, &out_res, pdMS_TO_TICKS(50));
}
got_data = true;
break;
}
total_len = 0; // Clear buffer if line didn't match DATA:
}
}
if (!got_data) {
// Timeout: clear I2C TX buffer
i2c_slave_write_buffer(I2C_SLAVE_NUM, NULL, 0, 0);
}
}
vTaskDelay(pdMS_TO_TICKS(1));
if (!got_data) {
// If PC fails, send a dummy byte so the Master's read doesn't hang forever
uint8_t dummy = 0xFF;
i2c_slave_transmit(i2c_slave, &dummy, 1, &out_res, pdMS_TO_TICKS(10));
}
}
}
void app_main(void) {
i2c_slave_init();
uart_init();
xTaskCreate(i2c2serial_task, "i2c2serial_task", 4096, NULL, 10, NULL);
i2c_slave_init();
xTaskCreatePinnedToCore(i2c2serial_task, "i2c2serial_task", 4096, NULL, 10, NULL, 0);
}