feat: 集成 MQTT 高频上报与远程控制功能,并标准化协议文档
1. 核心功能增强: - 实现 1s/次的传感器数据主动上报,温湿度精度提升至小数点后两位。 - 新增基于 cJSON 的 MQTT 控制指令解析逻辑,支持对双路继电器的远程开关控制。 - 引入即时回执 (ACK) 机制:设备在执行控制指令后立即通过 `agri/env/ack` 主题反馈执行状态。 2. 系统架构优化: - 引入 `sntp_time` 组件实现自动对时,确保上报数据携带准确的 `YYYY-MM-DD HH:MM:SS` 时间戳。 - 增加基于 eFuse 的芯片 UID 获取逻辑,结合 MAC 地址实现设备唯一标识。 - 为传感器采集任务引入 Mutex 互斥锁,确保多任务环境下 `env_sample_data_t` 的线程安全。 - 将所有传感器任务堆栈提升至 3072 字节,解决 cJSON 操作导致的 Stack Overflow 风险。 3. 文档与规范: - 新增 `README_MQTT.md` (V1.2),定义了环境报文、控制指令及 ACK 回执的完整 JSON Schema。 - 同步更新主工程 `README.md`,明确硬件接线说明及系统功能列表。
This commit is contained in:
3
components/sntp_time/CMakeLists.txt
Normal file
3
components/sntp_time/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "sntp_time.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_timer esp_event esp_netif lwip)
|
||||
20
components/sntp_time/include/sntp_time.h
Normal file
20
components/sntp_time/include/sntp_time.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 初始化 SNTP 并等待首次对时完成
|
||||
*
|
||||
* @param timeout_ms 等待首次同步的超时时间(毫秒)
|
||||
* @return esp_err_t ESP_OK 表示已完成同步
|
||||
*/
|
||||
esp_err_t sntp_timp_sync_time(uint32_t timeout_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
157
components/sntp_time/sntp_time.c
Normal file
157
components/sntp_time/sntp_time.c
Normal file
@@ -0,0 +1,157 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "sntp_time.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_sntp.h"
|
||||
#include "esp_netif_sntp.h"
|
||||
#include "sys/time.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
static const char *TAG = "sntp_timp";
|
||||
|
||||
#define SNTP_TIME_VALID_UNIX_TS 1700000000
|
||||
#define SNTP_WAIT_POLL_MS 200
|
||||
#define SNTP_REFRESH_PERIOD_MS 1000
|
||||
|
||||
extern void set_var_sntp_time(const char *value) __attribute__((weak));
|
||||
|
||||
static TaskHandle_t s_time_refresh_task = NULL;
|
||||
|
||||
static time_t get_current_time(void);
|
||||
|
||||
static void publish_sntp_time_var(const char *value)
|
||||
{
|
||||
if (set_var_sntp_time != NULL) {
|
||||
set_var_sntp_time(value);
|
||||
}
|
||||
}
|
||||
|
||||
static void format_current_time(char *buffer, size_t buffer_size)
|
||||
{
|
||||
time_t now = get_current_time();
|
||||
struct tm timeinfo;
|
||||
|
||||
localtime_r(&now, &timeinfo);
|
||||
strftime(buffer, buffer_size, "%Y-%m-%d %H:%M:%S", &timeinfo);
|
||||
}
|
||||
|
||||
static void sntp_time_refresh_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
char time_text[32];
|
||||
for (;;) {
|
||||
format_current_time(time_text, sizeof(time_text));
|
||||
publish_sntp_time_var(time_text);
|
||||
vTaskDelay(pdMS_TO_TICKS(SNTP_REFRESH_PERIOD_MS));
|
||||
}
|
||||
}
|
||||
|
||||
// =========================== 时间相关函数 ===========================
|
||||
static void set_timezone(void)
|
||||
{
|
||||
// 设置中国标准时间(北京时间)
|
||||
setenv("TZ", "CST-8", 1);
|
||||
tzset();
|
||||
ESP_LOGI(TAG, "时区设置为北京时间 (CST-8)");
|
||||
}
|
||||
|
||||
static time_t get_current_time(void)
|
||||
{
|
||||
// 使用POSIX函数获取时间
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
static void print_current_time(void)
|
||||
{
|
||||
char buffer[64];
|
||||
format_current_time(buffer, sizeof(buffer));
|
||||
|
||||
ESP_LOGI(TAG, "当前时间: %s", buffer);
|
||||
}
|
||||
|
||||
static esp_err_t start_time_refresh_task_if_needed(void)
|
||||
{
|
||||
if (s_time_refresh_task != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
BaseType_t ok = xTaskCreate(sntp_time_refresh_task,
|
||||
"sntp_time",
|
||||
3072,
|
||||
NULL,
|
||||
3,
|
||||
&s_time_refresh_task);
|
||||
return (ok == pdPASS) ? ESP_OK : ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
static void configure_sntp_servers(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "初始化SNTP服务");
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
esp_sntp_setservername(0, "cn.pool.ntp.org"); // 中国 NTP 服务器
|
||||
esp_sntp_setservername(1, "ntp1.aliyun.com"); // 阿里云 NTP 服务器
|
||||
#else
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, "cn.pool.ntp.org");
|
||||
sntp_setservername(1, "cn.pool.ntp.org");
|
||||
sntp_setservername(2, "ntp1.aliyun.com");
|
||||
#endif
|
||||
}
|
||||
|
||||
static esp_err_t wait_for_time_sync(uint32_t timeout_ms)
|
||||
{
|
||||
int64_t start_ms = esp_timer_get_time() / 1000;
|
||||
for (;;) {
|
||||
time_t now = get_current_time();
|
||||
if (now >= SNTP_TIME_VALID_UNIX_TS) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int64_t elapsed_ms = (esp_timer_get_time() / 1000) - start_ms;
|
||||
if (elapsed_ms >= (int64_t)timeout_ms) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(SNTP_WAIT_POLL_MS));
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t sntp_timp_sync_time(uint32_t timeout_ms)
|
||||
{
|
||||
if (timeout_ms == 0) {
|
||||
timeout_ms = 10000;
|
||||
}
|
||||
|
||||
set_timezone();
|
||||
|
||||
if (esp_sntp_enabled()) {
|
||||
esp_sntp_stop();
|
||||
}
|
||||
|
||||
configure_sntp_servers();
|
||||
esp_sntp_init();
|
||||
|
||||
esp_err_t ret = wait_for_time_sync(timeout_ms);
|
||||
if (ret == ESP_OK) {
|
||||
print_current_time();
|
||||
char time_text[32];
|
||||
format_current_time(time_text, sizeof(time_text));
|
||||
publish_sntp_time_var(time_text);
|
||||
|
||||
esp_err_t task_ret = start_time_refresh_task_if_needed();
|
||||
if (task_ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "创建时间刷新任务失败: %s", esp_err_to_name(task_ret));
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "SNTP 对时超时(%lu ms)", (unsigned long)timeout_ms);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user