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,5 +1,4 @@
#include "scd30.h"
#include "driver/i2c_master.h"
#include <string.h>
#include <stdio.h>
@@ -26,14 +25,29 @@ static uint8_t calculate_crc8(const uint8_t *data, size_t len) {
return crc;
}
void scd30_init(void) {
i2c_master_config_t i2c_config = {
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));
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_master_init(I2C_NUM_0, &i2c_config);
ESP_ERROR_CHECK(i2c_master_bus_add_device(handle.bus_handle, &dev_cfg, &handle.dev_handle));
return &handle;
}
void scd30_start_measurement(void) {
void scd30_start_measurement(scd30_handle_t *handle) {
uint8_t command[5] = {
(SCD30_CMD_START_MEASUREMENT >> 8) & 0xFF,
SCD30_CMD_START_MEASUREMENT & 0xFF,
@@ -41,11 +55,18 @@ void scd30_start_measurement(void) {
0x00 // Placeholder for CRC
};
command[4] = calculate_crc8(&command[2], 2);
i2c_master_write_to_device(I2C_NUM_0, SCD30_I2C_ADDRESS, command, sizeof(command), 1000 / portTICK_PERIOD_MS);
i2c_master_transmit(handle->dev_handle, command, sizeof(command), 1000);
}
int scd30_read_data(scd30_data_t *data) {
// Helper to convert Big Endian IEEE754 bytes to float
static float bytes_to_float(uint8_t mmsb, uint8_t mlsb, uint8_t lmsb, uint8_t llsb) {
uint32_t val = ((uint32_t)mmsb << 24) | ((uint32_t)mlsb << 16) | ((uint32_t)lmsb << 8) | (uint32_t)llsb;
float f;
memcpy(&f, &val, 4);
return f;
}
int scd30_read_data(scd30_handle_t *handle, scd30_data_t *data) {
uint8_t ready_command[2] = {
(SCD30_CMD_READY_STATUS >> 8) & 0xFF,
SCD30_CMD_READY_STATUS & 0xFF
@@ -54,7 +75,7 @@ int scd30_read_data(scd30_data_t *data) {
// Poll until data is ready
do {
i2c_master_write_read_device(I2C_NUM_0, SCD30_I2C_ADDRESS, ready_command, sizeof(ready_command), ready_status, sizeof(ready_status), 1000 / portTICK_PERIOD_MS);
i2c_master_transmit_receive(handle->dev_handle, ready_command, sizeof(ready_command), ready_status, sizeof(ready_status), 1000);
} while (ready_status[1] != 0x01);
uint8_t read_command[2] = {
@@ -64,7 +85,8 @@ int scd30_read_data(scd30_data_t *data) {
uint8_t buffer[18];
// Read 18 bytes of data
i2c_master_write_read_device(I2C_NUM_0, SCD30_I2C_ADDRESS, read_command, sizeof(read_command), buffer, sizeof(buffer), 1000 / portTICK_PERIOD_MS);
// 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) {
@@ -73,10 +95,13 @@ int scd30_read_data(scd30_data_t *data) {
}
}
// Parse data
data->co2 = (float)((buffer[0] << 8) | buffer[1]);
data->temp = (float)((buffer[3] << 8) | buffer[4]);
data->humidity = (float)((buffer[6] << 8) | buffer[7]);
// 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]);
return 0; // Success
}

View File

@@ -2,6 +2,7 @@
#define SCD30_H
#include <stdint.h>
#include "driver/i2c_master.h"
// Data structure to hold SCD30 sensor data
typedef struct {
@@ -10,9 +11,34 @@ typedef struct {
float humidity; // Relative humidity in percentage
} scd30_data_t;
// Function prototypes
void scd30_init(void);
void scd30_start_measurement(void);
int scd30_read_data(scd30_data_t *data);
typedef struct {
i2c_master_bus_handle_t bus_handle;
i2c_master_dev_handle_t dev_handle;
} scd30_handle_t;
/**
* @brief Initialize the SCD30 sensor
*
* @param sda_gpio SDA GPIO number
* @param scl_gpio SCL GPIO number
* @return scd30_handle_t* Pointer to the sensor handle
*/
scd30_handle_t *scd30_init(int sda_gpio, int scl_gpio);
/**
* @brief Start continuous measurement
*
* @param handle Sensor handle
*/
void scd30_start_measurement(scd30_handle_t *handle);
/**
* @brief Read data from the SCD30 sensor
*
* @param handle Sensor handle
* @param data Pointer to store the read data
* @return int 0 on success, -1 on error
*/
int scd30_read_data(scd30_handle_t *handle, scd30_data_t *data);
#endif // SCD30_H

View File

@@ -1,2 +1,2 @@
idf_component_register(SRCS "blink_example_main.c"
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -1,104 +0,0 @@
/* Blink Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "led_strip.h"
#include "sdkconfig.h"
static const char *TAG = "example";
/* Use project configuration menu (idf.py menuconfig) to choose the GPIO to blink,
or you can edit the following line and set a number here.
*/
#define BLINK_GPIO CONFIG_BLINK_GPIO
static uint8_t s_led_state = 0;
#ifdef CONFIG_BLINK_LED_STRIP
static led_strip_handle_t led_strip;
static void blink_led(void)
{
/* If the addressable LED is enabled */
if (s_led_state) {
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
led_strip_set_pixel(led_strip, 0, 16, 16, 16);
/* Refresh the strip to send data */
led_strip_refresh(led_strip);
} else {
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
}
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink addressable LED!");
/* LED strip initialization with the GPIO and pixels number*/
led_strip_config_t strip_config = {
.strip_gpio_num = BLINK_GPIO,
.max_leds = 1, // at least one LED on board
};
#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT
led_strip_rmt_config_t rmt_config = {
.resolution_hz = 10 * 1000 * 1000, // 10MHz
.flags.with_dma = false,
};
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI
led_strip_spi_config_t spi_config = {
.spi_bus = SPI2_HOST,
.flags.with_dma = true,
};
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
#else
#error "unsupported LED strip backend"
#endif
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
}
#elif CONFIG_BLINK_LED_GPIO
static void blink_led(void)
{
/* Set the GPIO level according to the state (LOW or HIGH)*/
gpio_set_level(BLINK_GPIO, s_led_state);
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
gpio_reset_pin(BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}
#else
#error "unsupported LED type"
#endif
void app_main(void)
{
/* Configure the peripheral according to the LED type */
configure_led();
while (1) {
ESP_LOGI(TAG, "Turning the LED %s!", s_led_state == true ? "ON" : "OFF");
blink_led();
/* Toggle the LED state */
s_led_state = !s_led_state;
vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS);
}
}

View File

@@ -5,21 +5,16 @@
void app_main(void) {
scd30_data_t sensor_data;
// Initialize the SCD30 sensor
scd30_init();
scd30_start_measurement();
// Example: SDA = 21, SCL = 22 (change as needed)
scd30_handle_t *scd30 = scd30_init(21, 22);
scd30_start_measurement(scd30);
while (1) {
// Read data from the sensor
if (scd30_read_data(&sensor_data) == 0) {
if (scd30_read_data(scd30, &sensor_data) == 0) {
printf("CO2: %.2f ppm, Temp: %.2f °C, Humidity: %.2f %%\n",
sensor_data.co2, sensor_data.temp, sensor_data.humidity);
} else {
printf("Failed to read data from SCD30 sensor\n");
}
// Wait for 2 seconds
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
}

View File

@@ -1,16 +0,0 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import logging
import os
import pytest
from pytest_embedded_idf.dut import IdfDut
@pytest.mark.supported_targets
@pytest.mark.generic
def test_blink(dut: IdfDut) -> None:
# check and log bin size
binary_file = os.path.join(dut.app.binary_path, 'blink.bin')
bin_size = os.path.getsize(binary_file)
logging.info('blink_bin_size : {}KB'.format(bin_size // 1024))