Compare commits

...

2 Commits

Author SHA1 Message Date
Wang Beihong
0eb428aaa5 添加 HX711 电子秤支持,更新依赖项并集成到主程序中 2026-04-21 22:46:53 +08:00
Wang Beihong
4e829bc56f 添加独立的 SNTP 对时任务,确保在连接 Wi-Fi 后同步时间 2026-04-21 18:59:00 +08:00
4 changed files with 173 additions and 5 deletions

View File

@@ -80,10 +80,21 @@ dependencies:
registry_url: https://components.espressif.com
type: service
version: 9.5.0
supcik/hx711:
component_hash: 38de09d1a896b3eda3cbb8c259979deed77fa0c517516c2b344dc55ae04f4fb6
dependencies:
- name: idf
require: private
version: '>=5.3'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.1.3
direct_dependencies:
- espressif/aht30
- espressif/esp_lvgl_port
- idf
manifest_hash: 15dd4fd3d660b11406a1dde8c0ec0b63f12e3cd4064ff9026e70648e3f7bc8a3
- supcik/hx711
manifest_hash: 09736a2502ce314a6733743671e919311d43e602eb74410732a63f10ed3aa60a
target: esp32s3
version: 2.0.0

View File

@@ -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)
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)

View File

@@ -16,3 +16,4 @@ dependencies:
# public: true
espressif/esp_lvgl_port: ^2.7.2
espressif/aht30: ^1.0.0
supcik/hx711: ^1.1.3

View File

@@ -1,6 +1,7 @@
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include "wifi-connect.h"
#include "esp_lvgl_port.h"
#include "lvgl_st7789_use.h"
@@ -12,21 +13,39 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_task_wdt.h"
#include "sntp_time.h"
#include "esp_mac.h"
#include "esp_system.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "sntp_time.h"
#include "bh1750_use.h"
#include "aht30.h"
#include "MQ-2.h"
#include "JW01.h"
#include "human_door.h"
#include "fire_sensor.h"
#include "hx711.hpp"
#define TAG "MAIN"
#define CO2_SPOILAGE_THRESHOLD_PPM 1500.0f
#define FIRE_DANGER_THRESHOLD_PERCENT 35.0f
static const gpio_num_t kClockPin = GPIO_NUM_13;
static const gpio_num_t kDataPin = GPIO_NUM_14;
#define HX711_TARE_SAMPLES 24
#define HX711_READ_TIMEOUT_MS 350
#define HX711_REFRESH_MS 120
#define HX711_COUNTS_PER_GRAM 430.0f
#define HX711_ZERO_DEADBAND_G 2.0f
#define HX711_FILTER_ALPHA 0.15f
#define HX711_STABLE_BAND_G 0.30f
#define HX711_UNLOCK_DELTA_G 2.00f
#define HX711_UPDATE_MIN_STEP_G 0.05f
#define HX711_STABLE_SAMPLES 15
#define TWDT_NORMAL_TIMEOUT_MS 5000
#define TWDT_UI_INIT_TIMEOUT_MS 120000
typedef struct
{
@@ -38,6 +57,7 @@ typedef struct
float tvoc;
float hcho;
float co2;
float ice_weight;
float fire_percent;
bool fire_danger;
bool human_present;
@@ -48,6 +68,20 @@ static env_data_t s_env_data;
static SemaphoreHandle_t s_env_data_lock = NULL;
static volatile bool s_ui_ready = false;
static void reconfigure_twdt(uint32_t timeout_ms, uint32_t idle_core_mask)
{
const esp_task_wdt_config_t twdt_cfg = {
.timeout_ms = timeout_ms,
.idle_core_mask = idle_core_mask,
.trigger_panic = true,
};
esp_err_t ret = esp_task_wdt_reconfigure(&twdt_cfg);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "TWDT reconfigure failed: %s", esp_err_to_name(ret));
}
}
static bool wait_for_wifi_connected(TickType_t timeout_ticks)
{
const TickType_t start_ticks = xTaskGetTickCount();
@@ -98,11 +132,14 @@ static void ui_init_task(void *arg)
{
(void)arg;
ESP_LOGI(TAG, "UI init start");
lvgl_port_lock(100 / portTICK_PERIOD_MS);
ui_init();
lvgl_port_unlock();
ESP_LOGI(TAG, "UI init done");
s_ui_ready = true;
reconfigure_twdt(TWDT_NORMAL_TIMEOUT_MS, (1U << portNUM_PROCESSORS) - 1U);
vTaskDelete(NULL);
}
@@ -129,6 +166,116 @@ static void status_task(void *arg)
}
}
static void sntp_task(void *arg)
{
(void)arg;
if (wait_for_wifi_connected(pdMS_TO_TICKS(15000))) {
esp_err_t sntp_ret = sntp_timp_sync_time(10000);
if (sntp_ret != ESP_OK) {
ESP_LOGW(TAG, "SNTP sync failed: %s", esp_err_to_name(sntp_ret));
}
}
vTaskDelete(NULL);
}
static void hx711_task(void *arg)
{
(void)arg;
HX711 hx711(kClockPin, kDataPin, HX711::Mode::kChannelA128);
int64_t tare_sum = 0;
int tare_ok_count = 0;
// 上电空载自动去皮:当前重量作为 0g 基准
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) {
tare_sum += raw;
tare_ok_count++;
}
vTaskDelay(pdMS_TO_TICKS(40));
}
int32_t tare_offset = 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 {
ESP_LOGW(TAG, "HX711 tare failed, use 0 as offset");
}
float filtered_weight_g = 0.0f;
float display_weight_g = 0.0f;
bool display_initialized = false;
bool display_locked = false;
uint32_t stable_count = 0;
uint32_t err_cnt = 0;
for (;;) {
int32_t value = hx711.Read(pdMS_TO_TICKS(HX711_READ_TIMEOUT_MS));
if (value != HX711::kUndefined) {
float weight_g = ((float)(value - tare_offset)) / HX711_COUNTS_PER_GRAM;
if (fabsf(weight_g) < HX711_ZERO_DEADBAND_G) {
weight_g = 0.0f;
}
if (weight_g < 0.0f) {
weight_g = 0.0f;
}
// 一阶低通,减小抖动
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) {
display_weight_g = rounded_weight_g;
display_initialized = true;
}
float diff_from_display = fabsf(rounded_weight_g - display_weight_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) {
stable_count++;
}
if (stable_count >= HX711_STABLE_SAMPLES) {
display_locked = true;
}
} else {
stable_count = 0;
}
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) {
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) {
ESP_LOGW(TAG, "HX711 read timeout, check DOUT/SCK wiring and power");
}
}
vTaskDelay(pdMS_TO_TICKS(HX711_REFRESH_MS));
}
}
extern "C" void app_main(void)
{
vTaskDelay(pdMS_TO_TICKS(100));
@@ -141,6 +288,9 @@ extern "C" void app_main(void)
// 2. 初始化显示屏和 LVGL
start_lvgl_demo();
// UI 初始化期间,临时放宽 TWDT仅监控 CPU0 空闲任务)
reconfigure_twdt(TWDT_UI_INIT_TIMEOUT_MS, 1U << 0);
// 3. 在 CPU1 上执行 UI 初始化,避免 app_main 长时间占用 CPU0
xTaskCreatePinnedToCore(ui_init_task, "ui_init_task", 8192, NULL, 8, NULL, 1);
set_var_food_status("良好");
@@ -154,12 +304,18 @@ extern "C" void app_main(void)
set_var_system_ip(wifi_connect_get_ip());
}
// 4. 独立 SNTP 对时任务
xTaskCreate(sntp_task, "sntp_task", 4096, NULL, 4, NULL);
// HX711 电子秤GPIO13(SCK) / GPIO14(DOUT)
xTaskCreate(hx711_task, "hx711_task", 4096, NULL, 6, NULL);
// 初始化继电器 (独立配置每个通道)
const relay_config_t relay_cfg[RELAY_CTRL_ID_MAX] = {
{.pin = GPIO_NUM_9, .active_high = false},
{.pin = GPIO_NUM_10, .active_high = false},
{.pin = GPIO_NUM_11, .active_high = true},
{.pin = GPIO_NUM_12, .active_high = true},
{.pin = GPIO_NUM_11, .active_high = true},
{.pin = GPIO_NUM_10, .active_high = true},
{.pin = GPIO_NUM_9, .active_high = true},
};
ESP_ERROR_CHECK(relay_ctrl_init(relay_cfg));