feat: 添加电容式土壤湿度传感器支持,更新相关命令和初始化逻辑
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "capactive_soil_moisture_sensor_V2.0.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_adc)
|
||||
@@ -0,0 +1,172 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "esp_adc/adc_cali.h"
|
||||
#include "esp_adc/adc_cali_scheme.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "capactive_soil_moisture_sensor_V2.0.h"
|
||||
|
||||
static const char *TAG = "cap_soil_v2";
|
||||
|
||||
static adc_oneshot_unit_handle_t s_adc_handle = NULL;
|
||||
static adc_cali_handle_t s_cali_handle = NULL;
|
||||
static bool s_cali_enabled = false;
|
||||
static bool s_inited = false;
|
||||
|
||||
static cap_soil_sensor_config_t s_cfg = {
|
||||
.unit = CAP_SOIL_SENSOR_DEFAULT_UNIT,
|
||||
.channel = CAP_SOIL_SENSOR_DEFAULT_CHANNEL,
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
.air_raw = 620,
|
||||
.water_raw = 308,
|
||||
};
|
||||
|
||||
static float map_raw_to_percent(int raw)
|
||||
{
|
||||
// 教程经验:湿度与输出值成反比,air_raw(干) > water_raw(湿)。
|
||||
int span = s_cfg.air_raw - s_cfg.water_raw;
|
||||
if (span <= 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float percent = ((float)(s_cfg.air_raw - raw) * 100.0f) / (float)span;
|
||||
if (percent < 0.0f) {
|
||||
percent = 0.0f;
|
||||
}
|
||||
if (percent > 100.0f) {
|
||||
percent = 100.0f;
|
||||
}
|
||||
return percent;
|
||||
}
|
||||
|
||||
static cap_soil_level_t map_percent_to_level(float percent)
|
||||
{
|
||||
// 三段划分:0~33 干燥,34~66 湿润,67~100 非常潮湿。
|
||||
if (percent < 33.34f) {
|
||||
return CAP_SOIL_LEVEL_DRY;
|
||||
}
|
||||
if (percent < 66.67f) {
|
||||
return CAP_SOIL_LEVEL_MOIST;
|
||||
}
|
||||
return CAP_SOIL_LEVEL_WET;
|
||||
}
|
||||
|
||||
static esp_err_t try_create_adc_calibration(const cap_soil_sensor_config_t *cfg)
|
||||
{
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_curve_fitting_config_t cali_cfg = {
|
||||
.unit_id = cfg->unit,
|
||||
.chan = cfg->channel,
|
||||
.atten = cfg->atten,
|
||||
.bitwidth = cfg->bitwidth,
|
||||
};
|
||||
esp_err_t ret = adc_cali_create_scheme_curve_fitting(&cali_cfg, &s_cali_handle);
|
||||
if (ret == ESP_OK) {
|
||||
s_cali_enabled = true;
|
||||
ESP_LOGI(TAG, "ADC calibration enabled");
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGW(TAG, "ADC calibration unavailable: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
#else
|
||||
(void)cfg;
|
||||
ESP_LOGW(TAG, "ADC calibration scheme not supported on this target");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t cap_soil_sensor_init(const cap_soil_sensor_config_t *config)
|
||||
{
|
||||
if (s_inited) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (config != NULL) {
|
||||
s_cfg = *config;
|
||||
}
|
||||
|
||||
adc_oneshot_unit_init_cfg_t unit_cfg = {
|
||||
.unit_id = s_cfg.unit,
|
||||
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(adc_oneshot_new_unit(&unit_cfg, &s_adc_handle), TAG, "adc unit init failed");
|
||||
|
||||
adc_oneshot_chan_cfg_t chan_cfg = {
|
||||
.atten = s_cfg.atten,
|
||||
.bitwidth = s_cfg.bitwidth,
|
||||
};
|
||||
esp_err_t ret = adc_oneshot_config_channel(s_adc_handle, s_cfg.channel, &chan_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
adc_oneshot_del_unit(s_adc_handle);
|
||||
s_adc_handle = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
(void)try_create_adc_calibration(&s_cfg);
|
||||
s_inited = true;
|
||||
ESP_LOGI(TAG,
|
||||
"sensor init ok (unit=%d, ch=%d, air_raw=%d, water_raw=%d)",
|
||||
(int)s_cfg.unit,
|
||||
(int)s_cfg.channel,
|
||||
s_cfg.air_raw,
|
||||
s_cfg.water_raw);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t cap_soil_sensor_read(cap_soil_sensor_data_t *out_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(out_data != NULL, ESP_ERR_INVALID_ARG, TAG, "out_data is null");
|
||||
ESP_RETURN_ON_FALSE(s_inited && s_adc_handle != NULL, ESP_ERR_INVALID_STATE, TAG, "sensor not initialized");
|
||||
|
||||
int raw = 0;
|
||||
ESP_RETURN_ON_ERROR(adc_oneshot_read(s_adc_handle, s_cfg.channel, &raw), TAG, "adc read failed");
|
||||
|
||||
int mv = -1;
|
||||
if (s_cali_enabled && s_cali_handle != NULL) {
|
||||
if (adc_cali_raw_to_voltage(s_cali_handle, raw, &mv) != ESP_OK) {
|
||||
mv = -1;
|
||||
}
|
||||
}
|
||||
|
||||
float moisture = map_raw_to_percent(raw);
|
||||
out_data->raw = raw;
|
||||
out_data->voltage_mv = mv;
|
||||
out_data->moisture_percent = moisture;
|
||||
out_data->level = map_percent_to_level(moisture);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t cap_soil_sensor_set_calibration(int air_raw, int water_raw)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(air_raw > water_raw, ESP_ERR_INVALID_ARG, TAG, "need air_raw > water_raw");
|
||||
s_cfg.air_raw = air_raw;
|
||||
s_cfg.water_raw = water_raw;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t cap_soil_sensor_deinit(void)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (s_cali_enabled && s_cali_handle != NULL) {
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_delete_scheme_curve_fitting(s_cali_handle);
|
||||
#endif
|
||||
s_cali_handle = NULL;
|
||||
s_cali_enabled = false;
|
||||
}
|
||||
|
||||
if (s_adc_handle != NULL) {
|
||||
ESP_RETURN_ON_ERROR(adc_oneshot_del_unit(s_adc_handle), TAG, "adc unit delete failed");
|
||||
s_adc_handle = NULL;
|
||||
}
|
||||
|
||||
s_inited = false;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// 你当前接在 GPIO0(ESP32-C3 对应 ADC1_CH0),这里作为默认值。
|
||||
#define CAP_SOIL_SENSOR_DEFAULT_UNIT ADC_UNIT_1
|
||||
#define CAP_SOIL_SENSOR_DEFAULT_CHANNEL ADC_CHANNEL_0
|
||||
|
||||
typedef enum {
|
||||
CAP_SOIL_LEVEL_DRY = 0,
|
||||
CAP_SOIL_LEVEL_MOIST,
|
||||
CAP_SOIL_LEVEL_WET,
|
||||
} cap_soil_level_t;
|
||||
|
||||
typedef struct {
|
||||
adc_unit_t unit;
|
||||
adc_channel_t channel;
|
||||
adc_atten_t atten;
|
||||
adc_bitwidth_t bitwidth;
|
||||
|
||||
// 标定值:空气中读数(干)通常更大,水中读数(湿)通常更小。
|
||||
int air_raw;
|
||||
int water_raw;
|
||||
} cap_soil_sensor_config_t;
|
||||
|
||||
typedef struct {
|
||||
int raw;
|
||||
int voltage_mv;
|
||||
float moisture_percent;
|
||||
cap_soil_level_t level;
|
||||
} cap_soil_sensor_data_t;
|
||||
|
||||
esp_err_t cap_soil_sensor_init(const cap_soil_sensor_config_t *config);
|
||||
esp_err_t cap_soil_sensor_read(cap_soil_sensor_data_t *out_data);
|
||||
esp_err_t cap_soil_sensor_set_calibration(int air_raw, int water_raw);
|
||||
esp_err_t cap_soil_sensor_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +1,3 @@
|
||||
idf_component_register(SRCS "console_user_cmds.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES console_simple_init console i2c_master_messager io_device_control wifi-connect)
|
||||
REQUIRES console_simple_init console i2c_master_messager io_device_control wifi-connect capactive_soil_moisture_sensor_V2.0)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "console_simple_init.h"
|
||||
#include "console_user_cmds.h"
|
||||
#include "capactive_soil_moisture_sensor_V2.0.h"
|
||||
#include "i2c_master_messager.h"
|
||||
#include "io_device_control.h"
|
||||
#include "wifi-connect.h"
|
||||
@@ -28,6 +30,20 @@ static const char *wifi_status_to_str(wifi_connect_status_t status)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *soil_level_to_str(cap_soil_level_t level)
|
||||
{
|
||||
switch (level) {
|
||||
case CAP_SOIL_LEVEL_DRY:
|
||||
return "dry";
|
||||
case CAP_SOIL_LEVEL_MOIST:
|
||||
return "moist";
|
||||
case CAP_SOIL_LEVEL_WET:
|
||||
return "wet";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// hello: 最小可用命令,用于验证 console 链路是否正常。
|
||||
static int cmd_hello(int argc, char **argv)
|
||||
{
|
||||
@@ -182,6 +198,48 @@ static int cmd_wifi(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// soil: 查询土壤湿度,或动态更新空气/水中标定值。
|
||||
static int cmd_soil(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2 || strcmp(argv[1], "read") == 0) {
|
||||
cap_soil_sensor_data_t data = {0};
|
||||
esp_err_t ret = cap_soil_sensor_read(&data);
|
||||
if (ret != ESP_OK) {
|
||||
printf("soil read failed: %s\n", esp_err_to_name(ret));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("soil raw=%d, mv=%d, moisture=%.1f%%, level=%s\n",
|
||||
data.raw,
|
||||
data.voltage_mv,
|
||||
data.moisture_percent,
|
||||
soil_level_to_str(data.level));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "cal") == 0) {
|
||||
if (argc < 4) {
|
||||
printf("usage: soil cal <air_raw> <water_raw>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int air_raw = (int)strtol(argv[2], NULL, 10);
|
||||
int water_raw = (int)strtol(argv[3], NULL, 10);
|
||||
esp_err_t ret = cap_soil_sensor_set_calibration(air_raw, water_raw);
|
||||
if (ret != ESP_OK) {
|
||||
printf("soil cal failed: %s\n", esp_err_to_name(ret));
|
||||
printf("hint: air_raw should be greater than water_raw\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("soil calibration updated: air=%d, water=%d\n", air_raw, water_raw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("usage: soil <read|cal <air_raw> <water_raw>>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
esp_err_t console_user_cmds_register(void)
|
||||
{
|
||||
const esp_console_cmd_t hello_cmd = {
|
||||
@@ -222,5 +280,13 @@ esp_err_t console_user_cmds_register(void)
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_console_cmd_register(&wifi_cmd), "console_user_cmds", "register wifi failed");
|
||||
|
||||
const esp_console_cmd_t soil_cmd = {
|
||||
.command = "soil",
|
||||
.help = "土壤湿度读取与标定。用法: soil <read|cal <air_raw> <water_raw>>",
|
||||
.hint = "<read|cal>",
|
||||
.func = cmd_soil,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_console_cmd_register(&soil_cmd), "console_user_cmds", "register soil failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user