添加农业环境模块,集成 MQTT 客户端功能,支持配置参数和数据发布
This commit is contained in:
5
components/agri_env/CMakeLists.txt
Normal file
5
components/agri_env/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "agri_env.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES driver mqtt cjson esp_timer esp_event relay_ctrl
|
||||
)
|
||||
26
components/agri_env/Kconfig.projbuild
Normal file
26
components/agri_env/Kconfig.projbuild
Normal file
@@ -0,0 +1,26 @@
|
||||
menu "MQTT 配置参数"
|
||||
config AGRI_ENV_MQTT_BROKER_URI
|
||||
string "MQTT 服务器地址"
|
||||
default ""
|
||||
|
||||
config AGRI_ENV_MQTT_USERNAME
|
||||
string "MQTT 用户名"
|
||||
default ""
|
||||
|
||||
config AGRI_ENV_MQTT_PASSWORD
|
||||
string "MQTT 密码"
|
||||
default ""
|
||||
|
||||
config AGRI_ENV_MQTT_CLIENT_ID
|
||||
string "MQTT Client ID"
|
||||
default "agri-env-monitor"
|
||||
|
||||
config AGRI_ENV_MQTT_PUBLISH_TOPIC
|
||||
string "MQTT 发布主题"
|
||||
default "agri/env/data"
|
||||
|
||||
config AGRI_ENV_MQTT_SUBSCRIBE_TOPIC
|
||||
string "MQTT 订阅主题"
|
||||
default "agri/env/cmd"
|
||||
|
||||
endmenu
|
||||
261
components/agri_env/agri_env.c
Normal file
261
components/agri_env/agri_env.c
Normal file
@@ -0,0 +1,261 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "cJSON.h"
|
||||
#include "relay_ctrl.h"
|
||||
|
||||
#include "agri_env.h"
|
||||
|
||||
static const char *TAG = "agri_env";
|
||||
|
||||
/**
|
||||
* @brief 农业环境模块上下文结构体
|
||||
* 包含 MQTT 客户端句柄、连接状态以及用于线程安全的互斥锁
|
||||
*/
|
||||
typedef struct {
|
||||
SemaphoreHandle_t lock; /*!< 互斥锁,保护共享资源 */
|
||||
|
||||
esp_mqtt_client_handle_t mqtt_client; /*!< ESP MQTT 客户端句柄 */
|
||||
bool mqtt_connected; /*!< MQTT 连接状态标识 */
|
||||
} agri_env_ctx_t;
|
||||
|
||||
static agri_env_ctx_t s_ctx;
|
||||
|
||||
/**
|
||||
* @brief 规范化 MQTT 代理 URI
|
||||
*
|
||||
* 将 menuconfig 中的原始字符串处理为标准形式(例如加上 mqtt:// 前缀或默认端口 1883)
|
||||
*
|
||||
* @param buffer 存储结果的缓冲区
|
||||
* @param buffer_size 缓冲区大小
|
||||
* @param was_prefixed [out] 如果进行了修整或添加前缀,则设为 true
|
||||
* @return const char* 返回规范化后的字符串指针,失败返回 NULL
|
||||
*/
|
||||
static const char *agri_env_get_normalized_mqtt_uri(char *buffer, size_t buffer_size, bool *was_prefixed)
|
||||
{
|
||||
const char *uri = CONFIG_AGRI_ENV_MQTT_BROKER_URI;
|
||||
if (was_prefixed != NULL) {
|
||||
*was_prefixed = false;
|
||||
}
|
||||
|
||||
if (uri == NULL || uri[0] == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 去除前导空白字符
|
||||
while (*uri == ' ' || *uri == '\t' || *uri == '\r' || *uri == '\n') {
|
||||
++uri;
|
||||
}
|
||||
|
||||
size_t uri_len = strlen(uri);
|
||||
// 去除尾部空白字符
|
||||
while (uri_len > 0 && (uri[uri_len - 1] == ' ' || uri[uri_len - 1] == '\t' || uri[uri_len - 1] == '\r' || uri[uri_len - 1] == '\n')) {
|
||||
--uri_len;
|
||||
}
|
||||
|
||||
if (uri_len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 如果已经包含协议前缀 (如 mqtt://), 直接复制并返回
|
||||
if (strstr(uri, "://") != NULL) {
|
||||
if (uri_len + 1 >= buffer_size) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(buffer, uri, uri_len);
|
||||
buffer[uri_len] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// 自动补全协议前缀和默认端口
|
||||
const bool has_port = memchr(uri, ':', uri_len) != NULL;
|
||||
int written;
|
||||
if (has_port) {
|
||||
written = snprintf(buffer, buffer_size, "mqtt://%.*s", (int)uri_len, uri);
|
||||
} else {
|
||||
written = snprintf(buffer, buffer_size, "mqtt://%.*s:1883", (int)uri_len, uri);
|
||||
}
|
||||
|
||||
if (written < 0 || written >= (int)buffer_size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (was_prefixed != NULL) {
|
||||
*was_prefixed = true;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MQTT 事件处理回调函数
|
||||
*
|
||||
* 处理连接、断开、接收数据等事件
|
||||
*/
|
||||
static void agri_env_mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
(void)handler_args;
|
||||
(void)base;
|
||||
|
||||
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
|
||||
if (event == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_id == MQTT_EVENT_CONNECTED) {
|
||||
xSemaphoreTake(s_ctx.lock, portMAX_DELAY);
|
||||
s_ctx.mqtt_connected = true;
|
||||
xSemaphoreGive(s_ctx.lock);
|
||||
ESP_LOGI(TAG, "MQTT 已连接");
|
||||
|
||||
// 连接成功后订阅配置的主题
|
||||
if (strlen(CONFIG_AGRI_ENV_MQTT_SUBSCRIBE_TOPIC) > 0) {
|
||||
esp_mqtt_client_subscribe(s_ctx.mqtt_client, CONFIG_AGRI_ENV_MQTT_SUBSCRIBE_TOPIC, 0);
|
||||
ESP_LOGI(TAG, "已订阅主题: %s", CONFIG_AGRI_ENV_MQTT_SUBSCRIBE_TOPIC);
|
||||
}
|
||||
} else if (event_id == MQTT_EVENT_DISCONNECTED) {
|
||||
xSemaphoreTake(s_ctx.lock, portMAX_DELAY);
|
||||
s_ctx.mqtt_connected = false;
|
||||
xSemaphoreGive(s_ctx.lock);
|
||||
ESP_LOGW(TAG, "MQTT 已断开连接");
|
||||
} else if (event_id == MQTT_EVENT_DATA) {
|
||||
ESP_LOGI(TAG, "收到 MQTT 消息: topic=%.*s payload=%.*s",
|
||||
event->topic_len,
|
||||
event->topic,
|
||||
event->data_len,
|
||||
event->data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动 MQTT 客户端
|
||||
*/
|
||||
esp_err_t agri_env_mqtt_start(void)
|
||||
{
|
||||
if (s_ctx.lock == NULL) {
|
||||
s_ctx.lock = xSemaphoreCreateMutex();
|
||||
ESP_RETURN_ON_FALSE(s_ctx.lock != NULL, ESP_ERR_NO_MEM, TAG, "互斥锁创建失败");
|
||||
}
|
||||
|
||||
if (s_ctx.mqtt_client != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
char mqtt_uri[256];
|
||||
bool mqtt_uri_prefixed = false;
|
||||
const char *normalized_uri = agri_env_get_normalized_mqtt_uri(mqtt_uri, sizeof(mqtt_uri), &mqtt_uri_prefixed);
|
||||
|
||||
if (normalized_uri == NULL) {
|
||||
ESP_LOGW(TAG, "MQTT Broker URI 为空,请在 menuconfig 中填写");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (mqtt_uri_prefixed) {
|
||||
ESP_LOGW(TAG, "MQTT Broker URI 已规范化为: %s", normalized_uri);
|
||||
}
|
||||
|
||||
// MQTT 客户端配置
|
||||
const esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker.address.uri = normalized_uri,
|
||||
.credentials.client_id = CONFIG_AGRI_ENV_MQTT_CLIENT_ID,
|
||||
.credentials.username = CONFIG_AGRI_ENV_MQTT_USERNAME,
|
||||
.credentials.authentication.password = CONFIG_AGRI_ENV_MQTT_PASSWORD,
|
||||
.session.protocol_ver = MQTT_PROTOCOL_V_3_1, // 使用 MQTT v3.1 协议
|
||||
};
|
||||
|
||||
s_ctx.mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
ESP_RETURN_ON_FALSE(s_ctx.mqtt_client != NULL, ESP_FAIL, TAG, "MQTT 客户端初始化失败");
|
||||
|
||||
// 注册所有 MQTT 事件的回调
|
||||
ESP_RETURN_ON_ERROR(
|
||||
esp_mqtt_client_register_event(s_ctx.mqtt_client, MQTT_EVENT_ANY, agri_env_mqtt_event_handler, NULL),
|
||||
TAG,
|
||||
"MQTT 注册事件失败");
|
||||
|
||||
// 启动 MQTT 客户端任务
|
||||
ESP_RETURN_ON_ERROR(esp_mqtt_client_start(s_ctx.mqtt_client), TAG, "MQTT 启动失败");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 停止并销毁 MQTT 客户端
|
||||
*/
|
||||
esp_err_t agri_env_mqtt_stop(void)
|
||||
{
|
||||
if (s_ctx.mqtt_client == NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_mqtt_client_stop(s_ctx.mqtt_client);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_mqtt_client_destroy(s_ctx.mqtt_client);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
xSemaphoreTake(s_ctx.lock, portMAX_DELAY);
|
||||
s_ctx.mqtt_client = NULL;
|
||||
s_ctx.mqtt_connected = false;
|
||||
xSemaphoreGive(s_ctx.lock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查 MQTT 是否已成功连接
|
||||
*
|
||||
* @return true 已连接, false 未连接
|
||||
*/
|
||||
bool agri_env_mqtt_is_connected(void)
|
||||
{
|
||||
bool connected = false;
|
||||
if (s_ctx.lock == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
xSemaphoreTake(s_ctx.lock, portMAX_DELAY);
|
||||
connected = s_ctx.mqtt_connected;
|
||||
xSemaphoreGive(s_ctx.lock);
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发布数据到指定主题
|
||||
*
|
||||
* 当前处于“仅 MQTT”模式,发布内容为固定的 JSON 数据:{"mode":"mqtt_only"}
|
||||
*
|
||||
* @param topic 目标主题
|
||||
* @param qos 服务质量等级
|
||||
* @param retain 保留消息标识
|
||||
* @return esp_err_t 成功返回 ESP_OK,失败返回相应错误码
|
||||
*/
|
||||
esp_err_t agri_env_mqtt_publish(const char *topic, const char *payload, int qos, int retain)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(topic != NULL && topic[0] != '\0', ESP_ERR_INVALID_ARG, TAG, "主题为空");
|
||||
ESP_RETURN_ON_FALSE(payload != NULL, ESP_ERR_INVALID_ARG, TAG, "内容为空");
|
||||
ESP_RETURN_ON_FALSE(s_ctx.mqtt_client != NULL, ESP_ERR_INVALID_STATE, TAG, "MQTT 客户端未启动");
|
||||
ESP_RETURN_ON_FALSE(agri_env_mqtt_is_connected(), ESP_ERR_INVALID_STATE, TAG, "MQTT 未连接");
|
||||
|
||||
int msg_id = esp_mqtt_client_publish(s_ctx.mqtt_client, topic, payload, 0, qos, retain);
|
||||
|
||||
ESP_RETURN_ON_FALSE(msg_id >= 0, ESP_FAIL, TAG, "MQTT 发布失败");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t agri_env_mqtt_publish_latest(const char *topic, int qos, int retain)
|
||||
{
|
||||
static const char *payload = "{\"mode\":\"mqtt_only\"}";
|
||||
return agri_env_mqtt_publish(topic, payload, qos, retain);
|
||||
}
|
||||
25
components/agri_env/include/agri_env.h
Normal file
25
components/agri_env/include/agri_env.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 启动 MQTT 客户端连接。 */
|
||||
esp_err_t agri_env_mqtt_start(void);
|
||||
/* 停止 MQTT 客户端连接并释放资源。 */
|
||||
esp_err_t agri_env_mqtt_stop(void);
|
||||
/* 查询 MQTT 当前是否已连接。 */
|
||||
bool agri_env_mqtt_is_connected(void);
|
||||
/* 发布指定的 JSON 载荷到指定主题。 */
|
||||
esp_err_t agri_env_mqtt_publish(const char *topic, const char *payload, int qos, int retain);
|
||||
/* 发布固定 MQTT-only 心跳载荷到指定主题。 */
|
||||
esp_err_t agri_env_mqtt_publish_latest(const char *topic, int qos, int retain);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -13,6 +13,16 @@ dependencies:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.0.0
|
||||
espressif/cjson:
|
||||
component_hash: e788323270d90738662d66fffa910bfe1fba019bba087f01557e70c40485b469
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.0'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.7.19~2
|
||||
espressif/cmake_utilities:
|
||||
component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
|
||||
dependencies:
|
||||
@@ -51,6 +61,16 @@ dependencies:
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.5.1
|
||||
espressif/mqtt:
|
||||
component_hash: ffdad5659706b4dc14bc63f8eb73ef765efa015bf7e9adf71c813d52a2dc9342
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.3'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.0.0
|
||||
espressif/sensor_hub:
|
||||
component_hash: b3e72f3e2d10e165b972b17fff889f294b1a685769efacf73ec8f1e92152d1d5
|
||||
dependencies:
|
||||
@@ -92,9 +112,11 @@ dependencies:
|
||||
version: 1.1.3
|
||||
direct_dependencies:
|
||||
- espressif/aht30
|
||||
- espressif/cjson
|
||||
- espressif/esp_lvgl_port
|
||||
- espressif/mqtt
|
||||
- idf
|
||||
- supcik/hx711
|
||||
manifest_hash: 09736a2502ce314a6733743671e919311d43e602eb74410732a63f10ed3aa60a
|
||||
manifest_hash: f379725444325394085b05956aaab66b9a797466dc72d898cd7645dbe6cb359c
|
||||
target: esp32s3
|
||||
version: 2.0.0
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
idf_component_register(SRCS "main.cpp"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES nvs_flash esp_wifi sntp_time aht30 esp_event esp_system wifi-connect ui lvgl_st7789_use efuse relay_ctrl bh1750 MQ-2 JW01 human_door fire_sensor hx711 su-03t)
|
||||
REQUIRES nvs_flash agri_env esp_wifi sntp_time aht30 esp_event esp_system wifi-connect ui lvgl_st7789_use efuse relay_ctrl bh1750 MQ-2 JW01 human_door fire_sensor hx711 su-03t)
|
||||
|
||||
@@ -17,3 +17,5 @@ dependencies:
|
||||
espressif/esp_lvgl_port: ^2.7.2
|
||||
espressif/aht30: ^1.0.0
|
||||
supcik/hx711: ^1.1.3
|
||||
espressif/mqtt: ^1.0.0
|
||||
espressif/cjson: ^1.7.19~2
|
||||
|
||||
118
main/main.cpp
118
main/main.cpp
@@ -37,6 +37,7 @@
|
||||
#include "fire_sensor.h"
|
||||
#include "hx711.hpp"
|
||||
#include "su-03t.h"
|
||||
#include "agri_env.h"
|
||||
|
||||
#define TAG "MAIN"
|
||||
#define CO2_SPOILAGE_THRESHOLD_PPM 1500.0f
|
||||
@@ -97,7 +98,8 @@ static void reconfigure_twdt(uint32_t timeout_ms, uint32_t idle_core_mask)
|
||||
};
|
||||
|
||||
esp_err_t ret = esp_task_wdt_reconfigure(&twdt_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "TWDT reconfigure failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
@@ -227,33 +229,41 @@ static void relay_status_task(void *arg)
|
||||
{
|
||||
bool relay_on = false;
|
||||
|
||||
if (relay_ctrl_get(RELAY_CTRL_ID_1, &relay_on) == ESP_OK) {
|
||||
if (relay_ctrl_get(RELAY_CTRL_ID_1, &relay_on) == ESP_OK)
|
||||
{
|
||||
set_var_fan_status(relay_status_text(relay_on));
|
||||
if (s_env_data_lock) {
|
||||
if (s_env_data_lock)
|
||||
{
|
||||
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
|
||||
s_env_data.fan_on = relay_on;
|
||||
xSemaphoreGive(s_env_data_lock);
|
||||
}
|
||||
}
|
||||
if (relay_ctrl_get(RELAY_CTRL_ID_2, &relay_on) == ESP_OK) {
|
||||
if (relay_ctrl_get(RELAY_CTRL_ID_2, &relay_on) == ESP_OK)
|
||||
{
|
||||
set_var_light_status(relay_status_text(relay_on));
|
||||
if (s_env_data_lock) {
|
||||
if (s_env_data_lock)
|
||||
{
|
||||
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
|
||||
s_env_data.light_on = relay_on;
|
||||
xSemaphoreGive(s_env_data_lock);
|
||||
}
|
||||
}
|
||||
if (relay_ctrl_get(RELAY_CTRL_ID_3, &relay_on) == ESP_OK) {
|
||||
if (relay_ctrl_get(RELAY_CTRL_ID_3, &relay_on) == ESP_OK)
|
||||
{
|
||||
set_var_cool_status(relay_status_text(relay_on));
|
||||
if (s_env_data_lock) {
|
||||
if (s_env_data_lock)
|
||||
{
|
||||
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
|
||||
s_env_data.cool_on = relay_on;
|
||||
xSemaphoreGive(s_env_data_lock);
|
||||
}
|
||||
}
|
||||
if (relay_ctrl_get(RELAY_CTRL_ID_4, &relay_on) == ESP_OK) {
|
||||
if (relay_ctrl_get(RELAY_CTRL_ID_4, &relay_on) == ESP_OK)
|
||||
{
|
||||
set_var_hot_status(relay_status_text(relay_on));
|
||||
if (s_env_data_lock) {
|
||||
if (s_env_data_lock)
|
||||
{
|
||||
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
|
||||
s_env_data.hot_on = relay_on;
|
||||
xSemaphoreGive(s_env_data_lock);
|
||||
@@ -272,9 +282,11 @@ static void sntp_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
if (wait_for_wifi_connected(pdMS_TO_TICKS(15000))) {
|
||||
if (wait_for_wifi_connected(pdMS_TO_TICKS(15000)))
|
||||
{
|
||||
esp_err_t sntp_ret = sntp_timp_sync_time(10000);
|
||||
if (sntp_ret != ESP_OK) {
|
||||
if (sntp_ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "SNTP sync failed: %s", esp_err_to_name(sntp_ret));
|
||||
}
|
||||
}
|
||||
@@ -289,20 +301,24 @@ static void sntp_task(void *arg)
|
||||
static void su03t_rx_callback(const su03t_frame_t *frame, void *user_ctx)
|
||||
{
|
||||
(void)user_ctx;
|
||||
if (frame == NULL) {
|
||||
if (frame == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char hex_buf[256];
|
||||
size_t pos = 0;
|
||||
for (size_t i = 0; i < frame->params_len && pos + 4 < sizeof(hex_buf); ++i) {
|
||||
for (size_t i = 0; i < frame->params_len && pos + 4 < sizeof(hex_buf); ++i)
|
||||
{
|
||||
int n = snprintf(&hex_buf[pos], sizeof(hex_buf) - pos, "%02X ", frame->params[i]);
|
||||
if (n <= 0) {
|
||||
if (n <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pos += (size_t)n;
|
||||
}
|
||||
if (pos == 0) {
|
||||
if (pos == 0)
|
||||
{
|
||||
snprintf(hex_buf, sizeof(hex_buf), "(no params)");
|
||||
}
|
||||
|
||||
@@ -311,7 +327,8 @@ static void su03t_rx_callback(const su03t_frame_t *frame, void *user_ctx)
|
||||
(unsigned)frame->params_len,
|
||||
hex_buf);
|
||||
|
||||
if (s_env_data_lock) {
|
||||
if (s_env_data_lock)
|
||||
{
|
||||
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
|
||||
s_env_data.su03t_last_msgno = frame->msgno;
|
||||
s_env_data.su03t_rx_count++;
|
||||
@@ -332,9 +349,11 @@ static void hx711_task(void *arg)
|
||||
int tare_ok_count = 0;
|
||||
|
||||
// 上电空载自动去皮:当前重量作为 0g 基准
|
||||
for (int i = 0; i < HX711_TARE_SAMPLES; ++i) {
|
||||
for (int i = 0; i < HX711_TARE_SAMPLES; ++i)
|
||||
{
|
||||
int32_t raw = hx711.Read(pdMS_TO_TICKS(HX711_READ_TIMEOUT_MS));
|
||||
if (raw != HX711::kUndefined) {
|
||||
if (raw != HX711::kUndefined)
|
||||
{
|
||||
tare_sum += raw;
|
||||
tare_ok_count++;
|
||||
}
|
||||
@@ -342,10 +361,13 @@ static void hx711_task(void *arg)
|
||||
}
|
||||
|
||||
int32_t tare_offset = 0;
|
||||
if (tare_ok_count > 0) {
|
||||
if (tare_ok_count > 0)
|
||||
{
|
||||
tare_offset = (int32_t)(tare_sum / tare_ok_count);
|
||||
ESP_LOGI(TAG, "HX711 tare done: raw0=%ld, samples=%d", (long)tare_offset, tare_ok_count);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "HX711 tare failed, use 0 as offset");
|
||||
}
|
||||
|
||||
@@ -355,14 +377,18 @@ static void hx711_task(void *arg)
|
||||
bool display_locked = false;
|
||||
uint32_t stable_count = 0;
|
||||
uint32_t err_cnt = 0;
|
||||
for (;;) {
|
||||
for (;;)
|
||||
{
|
||||
int32_t value = hx711.Read(pdMS_TO_TICKS(HX711_READ_TIMEOUT_MS));
|
||||
if (value != HX711::kUndefined) {
|
||||
if (value != HX711::kUndefined)
|
||||
{
|
||||
float weight_g = ((float)(value - tare_offset)) / HX711_COUNTS_PER_GRAM;
|
||||
if (fabsf(weight_g) < HX711_ZERO_DEADBAND_G) {
|
||||
if (fabsf(weight_g) < HX711_ZERO_DEADBAND_G)
|
||||
{
|
||||
weight_g = 0.0f;
|
||||
}
|
||||
if (weight_g < 0.0f) {
|
||||
if (weight_g < 0.0f)
|
||||
{
|
||||
weight_g = 0.0f;
|
||||
}
|
||||
|
||||
@@ -370,7 +396,8 @@ static void hx711_task(void *arg)
|
||||
filtered_weight_g = filtered_weight_g * (1.0f - HX711_FILTER_ALPHA) + weight_g * HX711_FILTER_ALPHA;
|
||||
float rounded_weight_g = roundf(filtered_weight_g * 100.0f) / 100.0f;
|
||||
|
||||
if (!display_initialized) {
|
||||
if (!display_initialized)
|
||||
{
|
||||
display_weight_g = rounded_weight_g;
|
||||
display_initialized = true;
|
||||
}
|
||||
@@ -378,40 +405,53 @@ static void hx711_task(void *arg)
|
||||
float diff_from_display = fabsf(rounded_weight_g - display_weight_g);
|
||||
|
||||
// 稳定后锁定显示,重量明显变化时再解锁并继续更新
|
||||
if (display_locked) {
|
||||
if (diff_from_display >= HX711_UNLOCK_DELTA_G) {
|
||||
if (display_locked)
|
||||
{
|
||||
if (diff_from_display >= HX711_UNLOCK_DELTA_G)
|
||||
{
|
||||
display_locked = false;
|
||||
stable_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!display_locked) {
|
||||
if (diff_from_display <= HX711_STABLE_BAND_G) {
|
||||
if (stable_count < HX711_STABLE_SAMPLES) {
|
||||
if (!display_locked)
|
||||
{
|
||||
if (diff_from_display <= HX711_STABLE_BAND_G)
|
||||
{
|
||||
if (stable_count < HX711_STABLE_SAMPLES)
|
||||
{
|
||||
stable_count++;
|
||||
}
|
||||
if (stable_count >= HX711_STABLE_SAMPLES) {
|
||||
if (stable_count >= HX711_STABLE_SAMPLES)
|
||||
{
|
||||
display_locked = true;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
stable_count = 0;
|
||||
}
|
||||
|
||||
if (diff_from_display >= HX711_UPDATE_MIN_STEP_G) {
|
||||
if (diff_from_display >= HX711_UPDATE_MIN_STEP_G)
|
||||
{
|
||||
display_weight_g = rounded_weight_g;
|
||||
}
|
||||
}
|
||||
|
||||
set_var_ice_weight(display_weight_g);
|
||||
|
||||
if (s_env_data_lock) {
|
||||
if (s_env_data_lock)
|
||||
{
|
||||
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
|
||||
s_env_data.ice_weight = display_weight_g;
|
||||
xSemaphoreGive(s_env_data_lock);
|
||||
}
|
||||
err_cnt = 0;
|
||||
} else {
|
||||
if ((++err_cnt % 20) == 0) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((++err_cnt % 20) == 0)
|
||||
{
|
||||
ESP_LOGW(TAG, "HX711 read timeout, check DOUT/SCK wiring and power");
|
||||
}
|
||||
}
|
||||
@@ -449,6 +489,12 @@ extern "C" void app_main(void)
|
||||
if (wait_for_wifi_connected(pdMS_TO_TICKS(15000)))
|
||||
{
|
||||
set_var_system_ip(wifi_connect_get_ip());
|
||||
|
||||
esp_err_t err = agri_env_mqtt_start();
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "MQTT 启动失败: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 独立 SNTP 对时任务
|
||||
|
||||
Reference in New Issue
Block a user