update
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
#include "scd30.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define SCD30_I2C_ADDRESS 0x61
|
||||
#define SCD30_CMD_START_MEASUREMENT 0x0010
|
||||
#define SCD30_CMD_READ_DATA 0x0300
|
||||
#define SCD30_CMD_READY_STATUS 0x0202
|
||||
|
||||
#define I2C_MASTER_TIMEOUT_MS 1000
|
||||
|
||||
static const char *TAG = "SCD30";
|
||||
|
||||
// Private function to calculate CRC-8
|
||||
static uint8_t calculate_crc8(const uint8_t *data, size_t len) {
|
||||
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) {
|
||||
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));
|
||||
|
||||
int i2c_master_port = I2C_NUM_0;
|
||||
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = SCD30_I2C_ADDRESS,
|
||||
.scl_speed_hz = 100000,
|
||||
.scl_wait_us = 150000 // Clock stretching
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.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;
|
||||
}
|
||||
|
||||
@@ -55,7 +70,18 @@ void scd30_start_measurement(scd30_handle_t *handle) {
|
||||
0x00 // Placeholder for CRC
|
||||
};
|
||||
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
|
||||
@@ -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) {
|
||||
uint8_t ready_command[2] = {
|
||||
uint8_t ready_cmd_bytes[2] = {
|
||||
(SCD30_CMD_READY_STATUS >> 8) & 0xFF,
|
||||
SCD30_CMD_READY_STATUS & 0xFF
|
||||
};
|
||||
uint8_t ready_status[3];
|
||||
uint8_t ready_status[3]; // [MSB, LSB, CRC]
|
||||
|
||||
// Poll until data is ready
|
||||
do {
|
||||
i2c_master_transmit_receive(handle->dev_handle, ready_command, sizeof(ready_command), ready_status, sizeof(ready_status), 1000);
|
||||
} while (ready_status[1] != 0x01);
|
||||
int retry_count = 0;
|
||||
while (retry_count < 200) { // Increased retries, 200 * 20ms = 4 seconds
|
||||
// 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint8_t read_command[2] = {
|
||||
// 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 & 0xFF
|
||||
};
|
||||
uint8_t buffer[18];
|
||||
|
||||
// Read 18 bytes of data
|
||||
// Format: CO2_MSB, CO2_LSB, CRC, CO2_LMSB, CO2_LLSB, CRC, ...
|
||||
i2c_master_transmit_receive(handle->dev_handle, read_command, sizeof(read_command), buffer, sizeof(buffer), 1000);
|
||||
|
||||
// Validate CRC for each pair of data
|
||||
for (int i = 0; i < 18; i += 3) {
|
||||
if (calculate_crc8(buffer + i, 2) != buffer[i + 2]) {
|
||||
return -1; // CRC error
|
||||
// Read 18 bytes of data with retry
|
||||
int data_retry = 0;
|
||||
bool data_valid = false;
|
||||
|
||||
while (data_retry < 5) {
|
||||
// 1. Send Read 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, 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)
|
||||
// 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->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]);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define SCD30_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/i2c.h"
|
||||
|
||||
// Data structure to hold SCD30 sensor data
|
||||
typedef struct {
|
||||
@@ -12,8 +12,7 @@ typedef struct {
|
||||
} scd30_data_t;
|
||||
|
||||
typedef struct {
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
i2c_port_t i2c_port;
|
||||
} scd30_handle_t;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user