update
This commit is contained in:
@@ -1,12 +1,18 @@
|
|||||||
#include "scd30.h"
|
#include "scd30.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
#define SCD30_I2C_ADDRESS 0x61
|
#define SCD30_I2C_ADDRESS 0x61
|
||||||
#define SCD30_CMD_START_MEASUREMENT 0x0010
|
#define SCD30_CMD_START_MEASUREMENT 0x0010
|
||||||
#define SCD30_CMD_READ_DATA 0x0300
|
#define SCD30_CMD_READ_DATA 0x0300
|
||||||
#define SCD30_CMD_READY_STATUS 0x0202
|
#define SCD30_CMD_READY_STATUS 0x0202
|
||||||
|
|
||||||
|
#define I2C_MASTER_TIMEOUT_MS 1000
|
||||||
|
|
||||||
|
static const char *TAG = "SCD30";
|
||||||
|
|
||||||
// Private function to calculate CRC-8
|
// Private function to calculate CRC-8
|
||||||
static uint8_t calculate_crc8(const uint8_t *data, size_t len) {
|
static uint8_t calculate_crc8(const uint8_t *data, size_t len) {
|
||||||
const uint8_t polynomial = 0x31;
|
const uint8_t polynomial = 0x31;
|
||||||
@@ -27,23 +33,32 @@ static uint8_t calculate_crc8(const uint8_t *data, size_t len) {
|
|||||||
|
|
||||||
scd30_handle_t *scd30_init(int sda_gpio, int scl_gpio) {
|
scd30_handle_t *scd30_init(int sda_gpio, int scl_gpio) {
|
||||||
static scd30_handle_t handle;
|
static scd30_handle_t handle;
|
||||||
i2c_master_bus_config_t bus_config = {
|
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
||||||
.i2c_port = I2C_NUM_0,
|
|
||||||
.scl_io_num = scl_gpio,
|
|
||||||
.sda_io_num = sda_gpio,
|
|
||||||
.glitch_ignore_cnt = 7,
|
|
||||||
.flags.enable_internal_pullup = true
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &handle.bus_handle));
|
|
||||||
|
|
||||||
i2c_device_config_t dev_cfg = {
|
int i2c_master_port = I2C_NUM_0;
|
||||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
|
||||||
.device_address = SCD30_I2C_ADDRESS,
|
i2c_config_t conf = {
|
||||||
.scl_speed_hz = 100000,
|
.mode = I2C_MODE_MASTER,
|
||||||
.scl_wait_us = 150000 // Clock stretching
|
.sda_io_num = sda_gpio,
|
||||||
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.scl_io_num = scl_gpio,
|
||||||
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.master.clk_speed = 100000,
|
||||||
|
.clk_flags = 0,
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(i2c_master_bus_add_device(handle.bus_handle, &dev_cfg, &handle.dev_handle));
|
|
||||||
|
esp_err_t err = i2c_param_config(i2c_master_port, &conf);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "I2C param config failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "I2C driver install failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.i2c_port = i2c_master_port; // Store port in handle
|
||||||
return &handle;
|
return &handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +70,18 @@ void scd30_start_measurement(scd30_handle_t *handle) {
|
|||||||
0x00 // Placeholder for CRC
|
0x00 // Placeholder for CRC
|
||||||
};
|
};
|
||||||
command[4] = calculate_crc8(&command[2], 2);
|
command[4] = calculate_crc8(&command[2], 2);
|
||||||
i2c_master_transmit(handle->dev_handle, command, sizeof(command), 1000);
|
|
||||||
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, (SCD30_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
|
||||||
|
i2c_master_write(cmd, command, sizeof(command), true);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
|
||||||
|
esp_err_t ret = i2c_master_cmd_begin(handle->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Start measurement failed: %s", esp_err_to_name(ret));
|
||||||
|
}
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to convert Big Endian IEEE754 bytes to float
|
// Helper to convert Big Endian IEEE754 bytes to float
|
||||||
@@ -67,38 +93,126 @@ static float bytes_to_float(uint8_t mmsb, uint8_t mlsb, uint8_t lmsb, uint8_t ll
|
|||||||
}
|
}
|
||||||
|
|
||||||
int scd30_read_data(scd30_handle_t *handle, scd30_data_t *data) {
|
int scd30_read_data(scd30_handle_t *handle, scd30_data_t *data) {
|
||||||
uint8_t ready_command[2] = {
|
uint8_t ready_cmd_bytes[2] = {
|
||||||
(SCD30_CMD_READY_STATUS >> 8) & 0xFF,
|
(SCD30_CMD_READY_STATUS >> 8) & 0xFF,
|
||||||
SCD30_CMD_READY_STATUS & 0xFF
|
SCD30_CMD_READY_STATUS & 0xFF
|
||||||
};
|
};
|
||||||
uint8_t ready_status[3];
|
uint8_t ready_status[3]; // [MSB, LSB, CRC]
|
||||||
|
|
||||||
// Poll until data is ready
|
// Poll until data is ready
|
||||||
do {
|
int retry_count = 0;
|
||||||
i2c_master_transmit_receive(handle->dev_handle, ready_command, sizeof(ready_command), ready_status, sizeof(ready_status), 1000);
|
while (retry_count < 200) { // Increased retries, 200 * 20ms = 4 seconds
|
||||||
} while (ready_status[1] != 0x01);
|
// 1. Send Ready Command
|
||||||
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, (SCD30_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
|
||||||
|
i2c_master_write(cmd, ready_cmd_bytes, sizeof(ready_cmd_bytes), true);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
|
||||||
uint8_t read_command[2] = {
|
esp_err_t ret = i2c_master_cmd_begin(handle->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(20));
|
||||||
|
retry_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Wait for Slave to consult Python
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(20));
|
||||||
|
|
||||||
|
// 3. Read Ready Status
|
||||||
|
cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, (SCD30_I2C_ADDRESS << 1) | I2C_MASTER_READ, true);
|
||||||
|
i2c_master_read(cmd, ready_status, sizeof(ready_status), I2C_MASTER_LAST_NACK);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
|
||||||
|
ret = i2c_master_cmd_begin(handle->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
// Check if ready (0x0001)
|
||||||
|
if (ready_status[1] == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(20)); // Wait before retry
|
||||||
|
retry_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retry_count >= 200) {
|
||||||
|
ESP_LOGW(TAG, "Timeout waiting for data ready");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t read_cmd_bytes[2] = {
|
||||||
(SCD30_CMD_READ_DATA >> 8) & 0xFF,
|
(SCD30_CMD_READ_DATA >> 8) & 0xFF,
|
||||||
SCD30_CMD_READ_DATA & 0xFF
|
SCD30_CMD_READ_DATA & 0xFF
|
||||||
};
|
};
|
||||||
uint8_t buffer[18];
|
uint8_t buffer[18];
|
||||||
|
|
||||||
// Read 18 bytes of data
|
// Read 18 bytes of data with retry
|
||||||
// Format: CO2_MSB, CO2_LSB, CRC, CO2_LMSB, CO2_LLSB, CRC, ...
|
int data_retry = 0;
|
||||||
i2c_master_transmit_receive(handle->dev_handle, read_command, sizeof(read_command), buffer, sizeof(buffer), 1000);
|
bool data_valid = false;
|
||||||
|
|
||||||
// Validate CRC for each pair of data
|
while (data_retry < 5) {
|
||||||
for (int i = 0; i < 18; i += 3) {
|
// 1. Send Read Command
|
||||||
if (calculate_crc8(buffer + i, 2) != buffer[i + 2]) {
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
return -1; // CRC error
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, (SCD30_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
|
||||||
|
i2c_master_write(cmd, read_cmd_bytes, sizeof(read_cmd_bytes), true);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
|
||||||
|
esp_err_t ret = i2c_master_cmd_begin(handle->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "Command Write Failed, retrying... (%d)", data_retry);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
|
data_retry++;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Wait for Slave to consult Python
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50)); // Give ample time for UART bridge
|
||||||
|
|
||||||
|
// 3. Read Data
|
||||||
|
cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, (SCD30_I2C_ADDRESS << 1) | I2C_MASTER_READ, true);
|
||||||
|
i2c_master_read(cmd, buffer, sizeof(buffer), I2C_MASTER_LAST_NACK);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
|
||||||
|
ret = i2c_master_cmd_begin(handle->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
// Validate CRC
|
||||||
|
bool crc_ok = true;
|
||||||
|
for (int i = 0; i < 18; i += 3) {
|
||||||
|
if (calculate_crc8(buffer + i, 2) != buffer[i + 2]) {
|
||||||
|
crc_ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (crc_ok) {
|
||||||
|
data_valid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "CRC failed or Read error, retrying... (%d)", data_retry);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
|
data_retry++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data_valid) {
|
||||||
|
ESP_LOGE(TAG, "Read data failed after retries: CRC Mismatch or I2C Error");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse IEEE754 floats (Big Endian)
|
// Parse IEEE754 floats (Big Endian)
|
||||||
// Structure: [Byte1, Byte2, CRC, Byte3, Byte4, CRC]
|
|
||||||
// 0 1 2 3 4 5
|
|
||||||
|
|
||||||
data->co2 = bytes_to_float(buffer[0], buffer[1], buffer[3], buffer[4]);
|
data->co2 = bytes_to_float(buffer[0], buffer[1], buffer[3], buffer[4]);
|
||||||
data->temp = bytes_to_float(buffer[6], buffer[7], buffer[9], buffer[10]);
|
data->temp = bytes_to_float(buffer[6], buffer[7], buffer[9], buffer[10]);
|
||||||
data->humidity = bytes_to_float(buffer[12], buffer[13], buffer[15], buffer[16]);
|
data->humidity = bytes_to_float(buffer[12], buffer[13], buffer[15], buffer[16]);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#define SCD30_H
|
#define SCD30_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "driver/i2c_master.h"
|
#include "driver/i2c.h"
|
||||||
|
|
||||||
// Data structure to hold SCD30 sensor data
|
// Data structure to hold SCD30 sensor data
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -12,8 +12,7 @@ typedef struct {
|
|||||||
} scd30_data_t;
|
} scd30_data_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
i2c_master_bus_handle_t bus_handle;
|
i2c_port_t i2c_port;
|
||||||
i2c_master_dev_handle_t dev_handle;
|
|
||||||
} scd30_handle_t;
|
} scd30_handle_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,23 +2,25 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "driver/i2c_slave.h"
|
#include "driver/i2c.h"
|
||||||
#include "driver/uart.h"
|
#include "driver/uart.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
// I2C Slave Configuration
|
||||||
#define I2C_SLAVE_SDA_IO 21
|
#define I2C_SLAVE_SDA_IO 21
|
||||||
#define I2C_SLAVE_SCL_IO 22
|
#define I2C_SLAVE_SCL_IO 22
|
||||||
|
#define I2C_SLAVE_FREQ_HZ 100000
|
||||||
#define I2C_SLAVE_ADDR 0x61
|
#define I2C_SLAVE_ADDR 0x61
|
||||||
#define I2C_SLAVE_RX_BUF_LEN 128
|
#define I2C_SLAVE_RX_BUF_LEN 256
|
||||||
#define I2C_SLAVE_TX_BUF_LEN 128
|
#define I2C_SLAVE_TX_BUF_LEN 256
|
||||||
|
|
||||||
|
// UART Configuration
|
||||||
#define UART_NUM UART_NUM_0
|
#define UART_NUM UART_NUM_0
|
||||||
#define UART_BAUD_RATE 921600
|
#define UART_BAUD_RATE 921600
|
||||||
#define UART_BUF_SIZE 256
|
#define UART_BUF_SIZE 512
|
||||||
#define TAG "I2C2SERIAL"
|
|
||||||
|
|
||||||
static i2c_slave_dev_handle_t i2c_slave = NULL;
|
static const char *TAG = "I2C_SLAVE";
|
||||||
static TaskHandle_t s_i2c_task_handle = NULL;
|
|
||||||
|
|
||||||
static void uart_init(void) {
|
static void uart_init(void) {
|
||||||
uart_config_t uart_config = {
|
uart_config_t uart_config = {
|
||||||
@@ -33,117 +35,94 @@ static void uart_init(void) {
|
|||||||
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
|
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback for I2C Receive Done
|
static esp_err_t i2c_slave_init(void) {
|
||||||
static bool i2c_rx_done_callback(i2c_slave_dev_handle_t channel, const i2c_slave_rx_done_event_data_t *edata, void *user_data) {
|
int i2c_slave_port = I2C_NUM_0;
|
||||||
BaseType_t high_task_wakeup = pdFALSE;
|
i2c_config_t conf_slave = {
|
||||||
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,
|
.sda_io_num = I2C_SLAVE_SDA_IO,
|
||||||
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
.scl_io_num = I2C_SLAVE_SCL_IO,
|
.scl_io_num = I2C_SLAVE_SCL_IO,
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
.send_buf_depth = 256,
|
.mode = I2C_MODE_SLAVE,
|
||||||
.slave_addr = I2C_SLAVE_ADDR,
|
.slave.addr_10bit_en = 0,
|
||||||
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
|
.slave.slave_addr = I2C_SLAVE_ADDR,
|
||||||
.intr_priority = 0,
|
.clk_flags = 0,
|
||||||
};
|
};
|
||||||
#if SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE
|
esp_err_t err = i2c_param_config(i2c_slave_port, &conf_slave);
|
||||||
conf.flags.stretch_en = true;
|
if (err != ESP_OK) return err;
|
||||||
#endif
|
return i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0);
|
||||||
|
|
||||||
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) {
|
void app_main(void) {
|
||||||
uint8_t i2c_rx[I2C_SLAVE_RX_BUF_LEN];
|
uint8_t i2c_rx_buffer[I2C_SLAVE_RX_BUF_LEN];
|
||||||
uint8_t uart_rx[UART_BUF_SIZE];
|
uint8_t uart_rx_buffer[UART_BUF_SIZE];
|
||||||
uint8_t i2c_tx[I2C_SLAVE_TX_BUF_LEN];
|
uint8_t i2c_tx_buffer[I2C_SLAVE_TX_BUF_LEN];
|
||||||
size_t out_res = 0; // Added for ESP-IDF v5.4 compatibility
|
|
||||||
|
|
||||||
s_i2c_task_handle = xTaskGetCurrentTaskHandle();
|
uart_init();
|
||||||
|
ESP_ERROR_CHECK(i2c_slave_init());
|
||||||
|
ESP_LOGI(TAG, "I2C Slave initialized successfully (Legacy Driver)");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
// 1. Prepare to Receive (Non-blocking queue)
|
// 1. Wait for data from Master (Blocking with timeout)
|
||||||
memset(i2c_rx, 0, sizeof(i2c_rx));
|
int len = i2c_slave_read_buffer(I2C_NUM_0, i2c_rx_buffer, I2C_SLAVE_RX_BUF_LEN, 50 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
// This queues the receive request
|
if (len > 0) {
|
||||||
ESP_ERROR_CHECK(i2c_slave_receive(i2c_slave, i2c_rx, sizeof(i2c_rx)));
|
ESP_LOGI(TAG, "Received %d bytes from Master", len);
|
||||||
|
|
||||||
// 2. Wait for Receive Complete (Callback triggers notification)
|
// 2. Send CMD to Python
|
||||||
// This is where we wait for the Master to finish writing the command
|
uart_write_bytes(UART_NUM, "CMD:", 4);
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
for (int i = 0; i < len; i++) {
|
||||||
|
char hex[3];
|
||||||
|
sprintf(hex, "%02X", i2c_rx_buffer[i]);
|
||||||
|
uart_write_bytes(UART_NUM, hex, 2);
|
||||||
|
}
|
||||||
|
uart_write_bytes(UART_NUM, "\n", 1);
|
||||||
|
|
||||||
// Calculate actual length received (SCD30 commands are usually 2-5 bytes)
|
// 3. Wait for DATA from Python (Blocking with timeout)
|
||||||
// We look for the first 0 if the master didn't send a full 128 bytes
|
int total_rx = 0;
|
||||||
size_t actual_rx_len = 2; // SCD30 commands are at least 2 bytes
|
bool data_received = false;
|
||||||
|
// Simple timeout for Python response
|
||||||
|
for (int i=0; i<100; i++) { // ~1 second timeout
|
||||||
|
int ulen = uart_read_bytes(UART_NUM, uart_rx_buffer + total_rx, UART_BUF_SIZE - total_rx, 10 / portTICK_PERIOD_MS);
|
||||||
|
if (ulen > 0) {
|
||||||
|
total_rx += ulen;
|
||||||
|
// Check for newline
|
||||||
|
char *newline = memchr(uart_rx_buffer, '\n', total_rx);
|
||||||
|
if (newline) {
|
||||||
|
// Parse DATA:
|
||||||
|
if (strncmp((char*)uart_rx_buffer, "DATA:", 5) == 0) {
|
||||||
|
int tx_len = 0;
|
||||||
|
char *p = (char*)uart_rx_buffer + 5;
|
||||||
|
while (p < newline) {
|
||||||
|
unsigned int val;
|
||||||
|
if (sscanf(p, "%2x", &val) == 1) {
|
||||||
|
i2c_tx_buffer[tx_len++] = (uint8_t)val;
|
||||||
|
p += 2;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Fast Send to PC
|
if (tx_len > 0) {
|
||||||
uart_write_bytes(UART_NUM, "CMD:", 4);
|
i2c_slave_write_buffer(I2C_NUM_0, i2c_tx_buffer, tx_len, 100 / portTICK_PERIOD_MS);
|
||||||
for (size_t i = 0; i < actual_rx_len; ++i) {
|
ESP_LOGI(TAG, "Loaded %d bytes to TX buffer", tx_len);
|
||||||
char hex[3];
|
} else {
|
||||||
sprintf(hex, "%02X", i2c_rx[i]);
|
// Even if empty, we might need to clear or write 0 if Master expects read?
|
||||||
uart_write_bytes(UART_NUM, hex, 2);
|
// If Python sends empty DATA:, it means just ack/nothing to send.
|
||||||
}
|
}
|
||||||
uart_write_bytes(UART_NUM, "\n", 1);
|
data_received = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Wait for PC response
|
if (!data_received) {
|
||||||
int total_len = 0;
|
ESP_LOGW(TAG, "Timeout waiting for Python response");
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
// Clear RX buffer not needed as read_buffer consumes it, but good practice if using ring buffer?
|
||||||
// If PC fails, send a dummy byte so the Master's read doesn't hang forever
|
// i2c_reset_rx_fifo(I2C_NUM_0); // This might be too aggressive if data came in during processing?
|
||||||
uint8_t dummy = 0xFF;
|
// Legacy driver handles fifo logic.
|
||||||
i2c_slave_transmit(i2c_slave, &dummy, 1, &out_res, pdMS_TO_TICKS(10));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void app_main(void) {
|
|
||||||
uart_init();
|
|
||||||
i2c_slave_init();
|
|
||||||
xTaskCreatePinnedToCore(i2c2serial_task, "i2c2serial_task", 4096, NULL, 10, NULL, 0);
|
|
||||||
}
|
|
||||||
13
Master/.devcontainer/Dockerfile
Normal file
13
Master/.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
ARG DOCKER_TAG=latest
|
||||||
|
FROM espressif/idf:${DOCKER_TAG}
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8
|
||||||
|
ENV LANG=C.UTF-8
|
||||||
|
|
||||||
|
RUN apt-get update -y && apt-get install udev -y
|
||||||
|
|
||||||
|
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "-c"]
|
||||||
19
Master/.devcontainer/devcontainer.json
Normal file
19
Master/.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "ESP-IDF QEMU",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
},
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"settings": {
|
||||||
|
"terminal.integrated.defaultProfile.linux": "bash",
|
||||||
|
"idf.gitPath": "/usr/bin/git"
|
||||||
|
},
|
||||||
|
"extensions": [
|
||||||
|
"espressif.esp-idf-extension",
|
||||||
|
"espressif.esp-idf-web"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runArgs": ["--privileged"]
|
||||||
|
}
|
||||||
78
Master/.gitignore
vendored
Normal file
78
Master/.gitignore
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Directory metadata
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Build artifacts and directories
|
||||||
|
**/build/
|
||||||
|
build/
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.out
|
||||||
|
*.exe # For any host-side utilities compiled on Windows
|
||||||
|
|
||||||
|
# ESP-IDF specific build outputs
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.map
|
||||||
|
flasher_args.json # Generated in build directory
|
||||||
|
sdkconfig.old
|
||||||
|
sdkconfig
|
||||||
|
|
||||||
|
# ESP-IDF dependencies
|
||||||
|
# For older versions or manual component management
|
||||||
|
/components/.idf/
|
||||||
|
**/components/.idf/
|
||||||
|
# For modern ESP-IDF component manager
|
||||||
|
managed_components/
|
||||||
|
# If ESP-IDF tools are installed/referenced locally to the project
|
||||||
|
.espressif/
|
||||||
|
|
||||||
|
# CMake generated files
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles/
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
CTestTestfile.cmake
|
||||||
|
|
||||||
|
# Python environment files
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
__pycache__/
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Virtual environment folders
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# Language Servers
|
||||||
|
.clangd/
|
||||||
|
.ccls-cache/
|
||||||
|
compile_commands.json
|
||||||
|
|
||||||
|
# Windows specific
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# User-specific configuration files
|
||||||
|
*.user
|
||||||
|
*.workspace # General workspace files, can be from various tools
|
||||||
|
*.suo # Visual Studio Solution User Options
|
||||||
|
*.sln.docstates # Visual Studio
|
||||||
19
Master/.vscode/c_cpp_properties.json
vendored
Normal file
19
Master/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "ESP-IDF",
|
||||||
|
"compilerPath": "c:\\Espressif\\tools\\tools\\xtensa-esp-elf\\esp-14.2.0_20241119\\xtensa-esp-elf\\bin\\xtensa-esp32-elf-gcc.exe",
|
||||||
|
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**"
|
||||||
|
],
|
||||||
|
"browse": {
|
||||||
|
"path": [
|
||||||
|
"${workspaceFolder}"
|
||||||
|
],
|
||||||
|
"limitSymbolsToIncludedHeaders": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
10
Master/.vscode/launch.json
vendored
Normal file
10
Master/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "gdbtarget",
|
||||||
|
"request": "attach",
|
||||||
|
"name": "Eclipse CDT GDB Adapter"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
Master/.vscode/settings.json
vendored
Normal file
11
Master/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"C_Cpp.intelliSenseEngine": "default",
|
||||||
|
"idf.openOcdConfigs": [
|
||||||
|
"board/esp32-wrover-kit-3.3v.cfg"
|
||||||
|
],
|
||||||
|
"idf.portWin": "detect",
|
||||||
|
"idf.currentSetup": "C:\\Users\\mahmo\\esp\\v5.4\\esp-idf",
|
||||||
|
"idf.customExtraVars": {
|
||||||
|
"IDF_TARGET": "esp32"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Master/CMakeLists.txt
Normal file
6
Master/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# The following five lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(Master)
|
||||||
2
Master/main/CMakeLists.txt
Normal file
2
Master/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
74
Master/main/main.c
Normal file
74
Master/main/main.c
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
|
||||||
|
static const char *TAG = "i2c-master";
|
||||||
|
|
||||||
|
|
||||||
|
#define I2C_MASTER_SCL_IO 22 /*!< gpio number for I2C master clock */
|
||||||
|
#define I2C_MASTER_SDA_IO 21 /*!< gpio number for I2C master data */
|
||||||
|
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
|
||||||
|
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
||||||
|
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
||||||
|
#define SLAVE_ADDRESS 0x0A
|
||||||
|
|
||||||
|
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||||
|
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||||
|
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||||
|
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||||
|
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||||
|
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||||
|
|
||||||
|
int i2c_master_port = 0;
|
||||||
|
static esp_err_t i2c_master_init(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
i2c_config_t conf = {
|
||||||
|
.mode = I2C_MODE_MASTER,
|
||||||
|
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||||
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||||
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||||
|
// .clk_flags = 0, /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */
|
||||||
|
};
|
||||||
|
esp_err_t err = i2c_param_config(i2c_master_port, &conf);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t i2c_master_send(uint8_t message[], int len)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Sending Message = %s", message);
|
||||||
|
|
||||||
|
esp_err_t ret;
|
||||||
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, SLAVE_ADDRESS << 1 | WRITE_BIT, ACK_CHECK_EN);
|
||||||
|
i2c_master_write(cmd, message, len, ACK_CHECK_EN);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
|
||||||
|
ret = i2c_master_cmd_begin(i2c_master_port, cmd, 1000 / portTICK_PERIOD_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
const uint8_t on_command[] = "LED_ON";
|
||||||
|
const uint8_t off_command[] = "LED_OFF";
|
||||||
|
ESP_ERROR_CHECK(i2c_master_init());
|
||||||
|
ESP_LOGI(TAG, "I2C initialized successfully");
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
i2c_master_send(on_command, sizeof(on_command));
|
||||||
|
vTaskDelay(1000/ portTICK_PERIOD_MS);
|
||||||
|
i2c_master_send(off_command, sizeof(off_command));
|
||||||
|
vTaskDelay(1000/ portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
13
Slave/.devcontainer/Dockerfile
Normal file
13
Slave/.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
ARG DOCKER_TAG=latest
|
||||||
|
FROM espressif/idf:${DOCKER_TAG}
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8
|
||||||
|
ENV LANG=C.UTF-8
|
||||||
|
|
||||||
|
RUN apt-get update -y && apt-get install udev -y
|
||||||
|
|
||||||
|
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "-c"]
|
||||||
19
Slave/.devcontainer/devcontainer.json
Normal file
19
Slave/.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "ESP-IDF QEMU",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
},
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"settings": {
|
||||||
|
"terminal.integrated.defaultProfile.linux": "bash",
|
||||||
|
"idf.gitPath": "/usr/bin/git"
|
||||||
|
},
|
||||||
|
"extensions": [
|
||||||
|
"espressif.esp-idf-extension",
|
||||||
|
"espressif.esp-idf-web"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runArgs": ["--privileged"]
|
||||||
|
}
|
||||||
78
Slave/.gitignore
vendored
Normal file
78
Slave/.gitignore
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Directory metadata
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Build artifacts and directories
|
||||||
|
**/build/
|
||||||
|
build/
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.out
|
||||||
|
*.exe # For any host-side utilities compiled on Windows
|
||||||
|
|
||||||
|
# ESP-IDF specific build outputs
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.map
|
||||||
|
flasher_args.json # Generated in build directory
|
||||||
|
sdkconfig.old
|
||||||
|
sdkconfig
|
||||||
|
|
||||||
|
# ESP-IDF dependencies
|
||||||
|
# For older versions or manual component management
|
||||||
|
/components/.idf/
|
||||||
|
**/components/.idf/
|
||||||
|
# For modern ESP-IDF component manager
|
||||||
|
managed_components/
|
||||||
|
# If ESP-IDF tools are installed/referenced locally to the project
|
||||||
|
.espressif/
|
||||||
|
|
||||||
|
# CMake generated files
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles/
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
CTestTestfile.cmake
|
||||||
|
|
||||||
|
# Python environment files
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
__pycache__/
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Virtual environment folders
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# Language Servers
|
||||||
|
.clangd/
|
||||||
|
.ccls-cache/
|
||||||
|
compile_commands.json
|
||||||
|
|
||||||
|
# Windows specific
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# User-specific configuration files
|
||||||
|
*.user
|
||||||
|
*.workspace # General workspace files, can be from various tools
|
||||||
|
*.suo # Visual Studio Solution User Options
|
||||||
|
*.sln.docstates # Visual Studio
|
||||||
19
Slave/.vscode/c_cpp_properties.json
vendored
Normal file
19
Slave/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "ESP-IDF",
|
||||||
|
"compilerPath": "c:\\Espressif\\tools\\tools\\xtensa-esp-elf\\esp-14.2.0_20241119\\xtensa-esp-elf\\bin\\xtensa-esp32-elf-gcc.exe",
|
||||||
|
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**"
|
||||||
|
],
|
||||||
|
"browse": {
|
||||||
|
"path": [
|
||||||
|
"${workspaceFolder}"
|
||||||
|
],
|
||||||
|
"limitSymbolsToIncludedHeaders": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
10
Slave/.vscode/launch.json
vendored
Normal file
10
Slave/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "gdbtarget",
|
||||||
|
"request": "attach",
|
||||||
|
"name": "Eclipse CDT GDB Adapter"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
Slave/.vscode/settings.json
vendored
Normal file
11
Slave/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"C_Cpp.intelliSenseEngine": "default",
|
||||||
|
"idf.openOcdConfigs": [
|
||||||
|
"board/esp32-wrover-kit-3.3v.cfg"
|
||||||
|
],
|
||||||
|
"idf.portWin": "detect",
|
||||||
|
"idf.currentSetup": "C:\\Users\\mahmo\\esp\\v5.4\\esp-idf",
|
||||||
|
"idf.customExtraVars": {
|
||||||
|
"IDF_TARGET": "esp32"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Slave/CMakeLists.txt
Normal file
6
Slave/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# The following five lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(Slave)
|
||||||
2
Slave/main/CMakeLists.txt
Normal file
2
Slave/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
76
Slave/main/main.c
Normal file
76
Slave/main/main.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
static const char *TAG = "i2c-slave";
|
||||||
|
|
||||||
|
#define LED_PIN 2
|
||||||
|
|
||||||
|
#define I2C_SLAVE_SCL_IO 22 /*!< gpio number for I2C master clock */
|
||||||
|
#define I2C_SLAVE_SDA_IO 21 /*!< gpio number for I2C master data */
|
||||||
|
#define I2C_SLAVE_FREQ_HZ 100000 /*!< I2C master clock frequency */
|
||||||
|
#define I2C_SLAVE_TX_BUF_LEN 255 /*!< I2C master doesn't need buffer */
|
||||||
|
#define I2C_SLAVE_RX_BUF_LEN 255 /*!< I2C master doesn't need buffer */
|
||||||
|
#define ESP_SLAVE_ADDR 0x0A
|
||||||
|
|
||||||
|
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||||
|
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||||
|
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||||
|
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||||
|
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||||
|
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||||
|
|
||||||
|
int i2c_slave_port = 0;
|
||||||
|
static esp_err_t i2c_slave_init(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
i2c_config_t conf_slave = {
|
||||||
|
.sda_io_num = I2C_SLAVE_SDA_IO, // select GPIO specific to your project
|
||||||
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.scl_io_num = I2C_SLAVE_SCL_IO, // select GPIO specific to your project
|
||||||
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.mode = I2C_MODE_SLAVE,
|
||||||
|
.slave.addr_10bit_en = 0,
|
||||||
|
.slave.slave_addr = ESP_SLAVE_ADDR, // address of your project
|
||||||
|
.clk_flags = 0,
|
||||||
|
};
|
||||||
|
esp_err_t err = i2c_param_config(i2c_slave_port, &conf_slave);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
uint8_t received_data[I2C_SLAVE_RX_BUF_LEN] = {0};
|
||||||
|
uint8_t on_command[] = "LED_ON";
|
||||||
|
|
||||||
|
esp_rom_gpio_pad_select_gpio(LED_PIN);
|
||||||
|
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(i2c_slave_init());
|
||||||
|
ESP_LOGI(TAG, "I2C Slave initialized successfully");
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
i2c_slave_read_buffer(i2c_slave_port, received_data, I2C_SLAVE_RX_BUF_LEN, 100 / portTICK_PERIOD_MS);
|
||||||
|
i2c_reset_rx_fifo(i2c_slave_port);
|
||||||
|
|
||||||
|
if(strncmp((const char*)received_data, "LED_ON", 6) == 0)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Data Recived = %s", received_data);
|
||||||
|
gpio_set_level(LED_PIN, 1);
|
||||||
|
}
|
||||||
|
else if(strncmp((const char*)received_data, "LED_OFF", 7) == 0)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Data Recived = %s", received_data);
|
||||||
|
gpio_set_level(LED_PIN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(received_data, 0, I2C_SLAVE_RX_BUF_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
BIN
__pycache__/hil_lib.cpython-313.pyc
Normal file
BIN
__pycache__/hil_lib.cpython-313.pyc
Normal file
Binary file not shown.
6
hil_validation_results.csv
Normal file
6
hil_validation_results.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Timestamp,Simulated_CO2,Target_Received_Msg,Status
|
||||||
|
19:43:59,400.0,"W (4783) SCD30: Command Write Failed, retrying... (0)",CHECK
|
||||||
|
19:43:59,850.5,"W (4883) SCD30: CRC failed or Read error, retrying... (1)",CHECK
|
||||||
|
19:44:02,1200.0,"CO2: 400.00 ppm, Temp: 24.00 °C, Humidity: 50.00 %",CHECK
|
||||||
|
19:44:03,2500.0,"W (8393) SCD30: CRC failed or Read error, retrying... (0)",CHECK
|
||||||
|
19:44:06,5000.0,"CO2: 1200.00 ppm, Temp: 24.00 °C, Humidity: 50.00 %",CHECK
|
||||||
|
@@ -3,8 +3,8 @@ import time
|
|||||||
from hil_lib import SCD30_HIL_Lib
|
from hil_lib import SCD30_HIL_Lib
|
||||||
|
|
||||||
# Device paths from your environment
|
# Device paths from your environment
|
||||||
EMULATOR = '/dev/i2c_emulator'
|
EMULATOR = 'COM11'#'/dev/i2c_emulator'
|
||||||
TARGET = '/dev/esp_sensor_test'
|
TARGET = 'COM10'#'/dev/esp_sensor_test'
|
||||||
LOG_FILE = 'hil_validation_results.csv'
|
LOG_FILE = 'hil_validation_results.csv'
|
||||||
|
|
||||||
# Initialize HIL
|
# Initialize HIL
|
||||||
@@ -32,7 +32,7 @@ with open(LOG_FILE, mode='w', newline='') as f:
|
|||||||
if hil.process_bridge(co2, 24.0, 50.0):
|
if hil.process_bridge(co2, 24.0, 50.0):
|
||||||
data_served = True
|
data_served = True
|
||||||
break
|
break
|
||||||
|
#time.sleep(4)
|
||||||
if not data_served:
|
if not data_served:
|
||||||
print("Error: Target didn't request data within 10s.")
|
print("Error: Target didn't request data within 10s.")
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user