活动介绍

uint8_t MQTT_PubRec(uint16_t id, uint8_t send) { uint8_t data[4] = {0x50, 0x02, 0x00, 0x00}; if (id == 0) // 校验Packet ID范围 return 0; // 可变头构造 (固定操作) data[2] = (id >> 8) & 0xFF; // 高字节 data[3] = id & 0xFF; // 低字节 if (send) // 发送模式 { MQTT_SendData(data, 4); // 发送原始字节流 Write_MQTT_TxDataBuff(data, 4); // 记录发送缓冲 mqtt_debug(MQTT_RxBUFF, MQTT_RxCounter, "\r\n* 发送 PubRec (50) 报文 *\r\n"); return 1; } else // 接收模式 { // 报文类型和长度校验 if (WIFI_RxBUFF[0] == 0x50 && WIFI_RxBUFF[1] == 0x02 && 0 == memcmp(WIFI_RxBUFF, data, 4)) { mqtt_debug(MQTT_RxBUFF, MQTT_RxCounter, "\r\n* 接收到PubRec (50) 报文 *\r\n"); return 1; } } return 0; } 给函数增加参数说明

时间: 2025-06-22 20:20:32 浏览: 17
<think>好的,我现在需要帮用户为这个MQTT_PubRec函数添加参数说明。首先,我要仔细阅读代码,理解每个参数的作用。函数定义是uint8_t MQTT_PubRec(uint16_t id, uint8_t send),有两个参数:id和send。 先看id参数。在代码里,id被用来构造数据包的可变头,高字节和低字节分别存入数组的第三和第四个位置。而且有一个检查,如果id是0的话就返回0,说明id的有效范围是非零的,所以参数说明里应该指出id必须是大于0的16位无符号整数。 然后是send参数,它是一个uint8_t类型。根据代码,当send非零时,函数进入发送模式,调用发送数据的函数;否则进入接收模式,校验接收缓冲区的内容。所以send的作用是控制模式,0表示接收模式,非0表示发送模式。参数说明需要明确这一点,可能用布尔值或0/1来表示。 接下来要考虑用户的需求。用户可能希望参数说明清晰明了,符合文档规范,可能需要包括参数的类型、作用、有效范围或取值。另外,用户可能是在编写文档或者注释,所以需要按照常见的格式来写,比如Doxygen风格。 还要注意代码中的返回值和函数功能。函数返回0或1表示成功或失败,但参数说明主要关注参数本身。不过可能需要补充函数整体的作用,比如这个函数用于处理MQTT的PUBREC报文,用于QoS 2级别的消息流。 在写参数说明的时候,要确保术语准确,比如“报文标识符”、“发送模式”等。同时,检查是否有其他需要注意的地方,比如id的取值范围是否还有其他限制,但代码里只检查了是否为0,所以只需说明非零即可。 最后,确保语言简洁,用中文表达,符合用户的要求。可能需要将每个参数用@param标签标出,并附上详细的说明,包括用途、取值范围、注意事项等。这样用户在查看文档时能够快速理解每个参数的意义和用法。</think>为`MQTT_PubRec`函数添加参数说明如下(采用Doxygen风格注释): ```c /** * @brief 处理MQTT PUBREC报文(QoS 2消息流第二阶段) * @param id 报文标识符(Packet Identifier),必须为大于0的16位无符号整数 * 用于匹配PUBREL报文,范围: 1-65535 * @param send 操作模式选择标志: * - 非0: 发送模式,构造并发送PUBREC报文 * - 0: 接收模式,校验接收到的PUBREC报文 * @return uint8_t 执行结果: * - 1: 成功发送/校验通过 * - 0: 参数错误(id=0)/报文校验失败 */ uint8_t MQTT_PubRec(uint16_t id, uint8_t send) { /* 原有函数体保持不变... */ } ``` 参数说明详解: 1. **id参数** - 类型:`uint16_t` - 作用:用于QoS 2消息流程的报文匹配,需要与PUBLISH/PUBREL报文中的Packet Identifier保持一致 - 取值范围:$1 \leq id \leq 65535$(0被系统保留用于错误标识) - 存储方式:大端格式(通过`data[2] = (id >> 8)`写入高字节) 2. **send参数** - 类型:`uint8_t` - 工作模式: - **发送模式**(非0值): 1. 构造固定格式报文:$$ \text{0x50} \quad \text{0x02} \quad \text{id\_high} \quad \text{id\_low} $$ 2. 通过`MQTT_SendData`发送原始字节流 3. 记录发送缓冲区(用于后续重传机制) - **接收模式**(0值): 1. 校验报文类型:首字节必须为0x50(PUBREC控制报文) 2. 校验报文长度:第二字节必须为0x02(剩余长度) 3. 校验报文标识符:需与本地记录的id完全匹配 函数设计特点: - 采用**双模式设计**,通过send参数区分发送/接收场景 - 严格遵循MQTT 3.1.1协议规范,报文格式符合:$$ \text{Control Packet} = \text{Fixed Header} + \text{Variable Header} $$ - 包含调试日志输出功能(通过mqtt_debug实现) - 提供传输持久化(Write_MQTT_TxDataBuff)支持消息重传
阅读全文

相关推荐

增加注释enum MQTTErrors mqtt_sync(struct mqtt_client *client) { /* Recover from any errors */ enum MQTTErrors err; int reconnecting = 0; MQTT_PAL_MUTEX_LOCK(&client->mutex); if (client->error != MQTT_ERROR_RECONNECTING && client->error != MQTT_OK && client->reconnect_callback != NULL) { client->reconnect_callback(client, &client->reconnect_state); if (client->error != MQTT_OK) { client->error = MQTT_ERROR_RECONNECT_FAILED; /* normally unlocked during CONNECT */ MQTT_PAL_MUTEX_UNLOCK(&client->mutex); } err = client->error; if (err != MQTT_OK) return err; } else { /* mqtt_reconnect will have queued the disconnect packet - that needs to be sent and then call reconnect */ if (client->error == MQTT_ERROR_RECONNECTING) { reconnecting = 1; client->error = MQTT_OK; } MQTT_PAL_MUTEX_UNLOCK(&client->mutex); } /* Call inspector callback if necessary */ if (client->inspector_callback != NULL) { MQTT_PAL_MUTEX_LOCK(&client->mutex); err = client->inspector_callback(client); MQTT_PAL_MUTEX_UNLOCK(&client->mutex); if (err != MQTT_OK) return err; } /* Call receive */ err = (enum MQTTErrors)__mqtt_recv(client); if (err != MQTT_OK) return err; /* Call send */ err = (enum MQTTErrors)__mqtt_send(client); /* mqtt_reconnect will essentially be a disconnect if there is no callback */ if (reconnecting && client->reconnect_callback != NULL) { MQTT_PAL_MUTEX_LOCK(&client->mutex); client->reconnect_callback(client, &client->reconnect_state); } return err; } uint16_t __mqtt_next_pid(struct mqtt_client *client) { int pid_exists = 0; if (client->pid_lfsr == 0) { client->pid_lfsr = 163u; } /* LFSR taps taken from: https://en.wikipedia.org/wiki/Linear-feedback_shift_register */ do { struct mqtt_que

分析代码#include “main.h” #include “tim.h” #include “usart.h” #include “gpio.h” #include “oled.h” #include “DHT11.H” #include < stdio.h> 无效 SystemClock_Config(void); int beez_flag=1; uint8_t num[7]={0}; uint8_t hum_data[2] = {'\0'}; uint8_t尘埃[2]= {'\0'}; //按键中断回调函数,用来控制按键的 无效 HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin); //通过这个函数发数据发送到串口,并连接云端 void send_AT(uint8_t* 数据) { while(数据) { HAL_UART_Transmit(&huart2,data,1,0xFFFF); 数据++; }} int main(void) (无效) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM3_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init(); OLED_Init(); OLED_Clear(); HAL_TIM_Base_Start_IT(&htim3); oled的英文 uint8_t tem[] = “tem: ”; uint8_t hum[] = “嗡嗡声: ”; uint8_t dust[] = “尘埃: ”; uint8_t aa[1] = “.”; //蜂鸣器默认为低电平 HAL_GPIO_WritePin(GPIOB,beez_Pin,GPIO_PIN_RESET); 测试esp8266是否正常 uint8_t at[] = “AT\r\n”; 设置路由器模式 1 station模式 2 AP路由器模式 3 station+AP混合模式 uint8_t模式[] = “AT+CWMODE=3\r\n”; 设置模块WiFi名称,WiFi密码(注意WiFi要调成这个,2.4GHZ) uint8_t cwjap[] = “AT+CWJAP=”2222“,”66666666“\r\n”; // 开启透明传输模式 uint8_t cipmode[] = “AT+CIPMODE=1\r\n”; //连接服务器和端口 uint8_t cipstart[] =“AT+CIPSTART=”TCP“,”bemfa.com“,8344\r\n”; //进入透传模式,下面发的都会无条件传输 uint8_t cipsend[] = “AT+CIPSEND\r\n”; //如果需要订阅主题,发送这个订阅指令,发送完订阅,控制台推送的话,8266就可以收到推送消息了 uint8_t rj[] = “cmd=1&uid=20c394fcc95e8ce996d87470e7fa5b08&topic=rj\r\n”; //如果需要推送指令,发布数据指令 uint8_t dataa[] = “cmd=2&uid=20c394fcc95e8ce996d87470e7fa5b08&topic=rj&msg=temp=123#hum=456#soil=789#sss=999\r\n”; uint8_t dataaa[] = “cmd=2&uid=20c394fcc95e8ce996d87470e7fa5b08&topic=rj&msg=”; uint8_t热度[] = “cmd=0&msg=ping\r\n”; uint8_t ssss[200] = {'\0'}; send_AT(在); HAL_Delay(3000); send_AT( 模式); HAL_Delay(3000); send_AT(cwjap); HAL_Delay(3000); send_AT(cipmode); HAL_Delay(3000); send_AT(Cipstart); HAL_Delay(3000); send_AT (cipsend); HAL_Delay(3000); send_AT(rj); HAL_Delay(3000); send_AT(dataa); HAL_Delay(3000); uint8_t rec_buf[9]={'\0'}; while (1) { HAL_UART_Receive(&huart1, rec_buf, 9, 1000); dht_init(&hum_data[0],&hum_data[1]); //拼接字符串 sprintf((char)ssss,“%stem=%d#hum=%d#dust=%d.%d\r\n”,dataaa,hum_data[0],hum_data[1],rec_buf[4],rec_buf[5]); //数据 send_AT(ssss); HAL_Delay(2000 年); //心跳 send_AT(热); HAL_Delay(2000 年); OLED_Clear(); OLED_ShowString(0,0,tem,16); OLED_ShowNum(28,0,hum_data[0],3,16);//温度 OLED_ShowString(65,0,哼,16); OLED_ShowNum(95,0,hum_data[1],3,16);//湿度 OLED_ShowString(0,2,灰尘,16); OLED_ShowNum(39,2,rec_buf[4],2,16);//粉尘整数 OLED_ShowString(59,2,aa,16); OLED_ShowNum(64,2,rec_buf[5],2,16);//粉尘小数 //蜂鸣器的响或不响 if((beez_flag%2==1) & & (hum_data[1]>70)) { HAL_GPIO_WritePin(GPIOB,beez_Pin,GPIO_PIN_SET); } else { HAL_GPIO_WritePin(GPIOB,beez_Pin,GPIO_PIN_RESET); } HAL_Delay(1000); } } void SystemClock_Config (无效) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.振荡器类型 = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; 如果 (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.时钟类型 = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); }} 无效 HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { beez_flag++; } 无效 Error_Handler(void) { __disable_irq(); while (1) {} }用到了什么嵌入式技术

#include <stdio.h> #include <string.h> #include <math.h> #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "freertos/semphr.h" #include "driver/rmt_tx.h" #include "led_ws2812.h" #include "driver/gpio.h" #include "driver/adc.h" #include "driver/i2c.h" #include "nvs_flash.h" #include "mqtt_client.h" #include "simple_wifi_sta.h" // 网络配置 #define MQTT_ADDRESS "114.55.64.216" // MQTT连接地址 #define MQTT_PORT 1863 // MQTT连接端口号 #define MQTT_CLIENT "soil_0001" // Client ID #define MQTT_USERNAME "devname" // MQTT用户名 #define MQTT_PASSWORD "devpwd" // MQTT密码 #define MQTT_PUBLIC_TOPIC "/gws/soil_0001" // 推送消息主题 #define MQTT_SUBSCRIBE_TOPIC "/gws/soil_0001" // 订阅主题 // 定义事件组,用于通知WIFI连接成功 #define WIFI_CONNECT_BIT BIT0 static EventGroupHandle_t s_wifi_ev = NULL; // MQTT客户端操作句柄 static esp_mqtt_client_handle_t s_mqtt_client = NULL; // MQTT连接标志 static bool s_is_mqtt_connected = false; // 灯带配置 #define WS2812_GPIO_NUM GPIO_NUM_45 #define WS2812_LED_NUM 6 TaskHandle_t RgbTaskHandle = NULL; // 添加RGB任务控制事件组 static EventGroupHandle_t rgb_event_group; #define RGB_RUN_BIT BIT0 // RGB任务运行标志位 // 雷达配置 #define LED_GPIO GPIO_NUM_16 #define LEIDA_GPIO GPIO_NUM_4 TaskHandle_t RadarTaskHandle = NULL; // ADC配置 #define ADC_GPIO GPIO_NUM_8 // IO8对应ADC1通道7 #define ADC_CHANNEL ADC1_CHANNEL_7 TaskHandle_t AdcTaskHandle = NULL; // I2C参数配置 #define I2C_MASTER_SCL_IO 2 // SCL引脚 #define I2C_MASTER_SDA_IO 1 // SDA引脚 #define I2C_MASTER_NUM I2C_NUM_0 // I2C控制器编号 #define I2C_MASTER_FREQ_HZ 100000 // I2C频率100kHz #define I2C_MASTER_TX_BUF_DISABLE 0 // 禁用发送缓冲区 #define I2C_MASTER_RX_BUF_DISABLE 0 // 禁用接收缓冲区 TaskHandle_t IicTaskHandle = NULL; // 传感器地址 #define BH1750_ADDR 0x23 // BH1750地址 #define SHT20_ADDR 0x40 // SHT20温湿度传感器地址 #define BMP280_ADDR 0x76 // BMP280 I2C地址 // BH1750寄存器定义 #define BH1750_POWER_DOWN 0x00 #define BH1750_POWER_ON 0x01 #define BH1750_RESET 0x07 #define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 // SHT20寄存器定义 #define SHT20_TRIGGER_TEMP_MEASUREMENT 0xF3 #define SHT20_TRIGGER_HUMIDITY_MEASUREMENT 0xF5 #define SHT20_SOFT_RESET 0xFE // BMP280相关定义 #define BMP280_REG_ID 0xD0 #define BMP280_REG_RESET 0xE0 #define BMP280_REG_STATUS 0xF3 #define BMP280_REG_CTRL_MEAS 0xF4 #define BMP280_REG_CONFIG 0xF5 #define BMP280_REG_PRESS_MSB 0xF7 #define BMP280_REG_TEMP_MSB 0xFA #define BMP280_REG_CALIB 0x88 // BMP280校准参数结构体 typedef struct { uint16_t dig_T1; int16_t dig_T2; int16_t dig_T3; uint16_t dig_P1; int16_t dig_P2; int16_t dig_P3; int16_t dig_P4; int16_t dig_P5; int16_t dig_P6; int16_t dig_P7; int16_t dig_P8; int16_t dig_P9; } bmp280_calib_t; // 传感器数据结构体(全局共享) typedef struct { float temp; // 温度(°C) float humi; // 湿度(%) float press; // 气压(Pa) float lux; // 光照(lx) float adc_voltage;// ADC电压(V) float adc_resist; // 土壤电阻(Ω) } sensor_data_t; // 全局传感器数据及互斥锁 static sensor_data_t g_sensor_data = { 0 }; static SemaphoreHandle_t g_sensor_mutex; static const char* TAG = "main"; static const char* TAG2 = "adc_radar"; static const char* TAG_ADC = "adc"; static const char* TAG_I2C = "I2C_Sensors"; /** * mqtt连接事件处理函数 */ static void aliot_mqtt_event_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: // 连接成功 ESP_LOGI(TAG, "mqtt connected"); s_is_mqtt_connected = true; // 连接成功后订阅主题 esp_mqtt_client_subscribe_single(s_mqtt_client, MQTT_SUBSCRIBE_TOPIC, 1); break; case MQTT_EVENT_DISCONNECTED: // 连接断开 ESP_LOGI(TAG, "mqtt disconnected"); s_is_mqtt_connected = false; break; case MQTT_EVENT_SUBSCRIBED: // 收到订阅ACK ESP_LOGI(TAG, "mqtt subscribed ack, msg_id=%d", event->msg_id); break; case MQTT_EVENT_DATA: // 收到订阅消息 printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); break; case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); break; default: break; } } /** 启动mqtt连接 */ void mqtt_start(void) { esp_mqtt_client_config_t mqtt_cfg = { 0 }; mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_TCP; mqtt_cfg.broker.address.hostname = MQTT_ADDRESS; mqtt_cfg.broker.address.port = MQTT_PORT; mqtt_cfg.credentials.client_id = MQTT_CLIENT; mqtt_cfg.credentials.username = MQTT_USERNAME; mqtt_cfg.credentials.authentication.password = MQTT_PASSWORD; ESP_LOGI(TAG, "mqtt connect->clientId:%s,username:%s", mqtt_cfg.credentials.client_id, mqtt_cfg.credentials.username); s_mqtt_client = esp_mqtt_client_init(&mqtt_cfg); esp_mqtt_client_register_event(s_mqtt_client, ESP_EVENT_ANY_ID, aliot_mqtt_event_handler, s_mqtt_client); esp_mqtt_client_start(s_mqtt_client); } /** wifi事件通知 */ void wifi_event_handler(WIFI_EV_e ev) { if (ev == WIFI_CONNECTED) { xEventGroupSetBits(s_wifi_ev, WIFI_CONNECT_BIT); } } /** I2C主控制器初始化 */ static esp_err_t i2c_master_init(void) { i2c_config_t conf = { .mode = I2C_MODE_MASTER, .sda_io_num = I2C_MASTER_SDA_IO, .scl_io_num = I2C_MASTER_SCL_IO, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = I2C_MASTER_FREQ_HZ, }; ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf)); return i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); } /** 初始化BH1750环境光传感器 */ bool bh1750_init(void) { ESP_LOGI(TAG_I2C, "Initializing BH1750..."); // 软复位 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BH1750_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, BH1750_RESET, true); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "BH1750 reset failed: 0x%x", ret); return false; } vTaskDelay(pdMS_TO_TICKS(10)); // 启动连续高分辨率模式 cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BH1750_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, BH1750_CONTINUOUS_HIGH_RES_MODE, true); i2c_master_stop(cmd); ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "BH1750 mode set failed: 0x%x", ret); return false; } ESP_LOGI(TAG_I2C, "BH1750 initialized successfully"); return true; } /** 读取BH1750环境光强度 */ float bh1750_read_light(void) { uint8_t data[2] = { 0 }; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BH1750_ADDR << 1) | I2C_MASTER_READ, true); i2c_master_read_byte(cmd, &data[0], I2C_MASTER_ACK); i2c_master_read_byte(cmd, &data[1], I2C_MASTER_NACK); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "BH1750 read failed: 0x%x", ret); return -1; } // 计算光照强度(lx) uint16_t light = (data[0] << 8) | data[1]; return light / 1.2; // 根据数据手册计算 } /** 初始化SHT20温湿度传感器 */ bool sht20_init(void) { ESP_LOGI(TAG_I2C, "Initializing SHT20..."); // 软复位 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (SHT20_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, SHT20_SOFT_RESET, true); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "SHT20 reset failed: 0x%x", ret); return false; } vTaskDelay(pdMS_TO_TICKS(15)); // 等待复位完成 ESP_LOGI(TAG_I2C, "SHT20 initialized successfully"); return true; } /** 读取SHT20测量值 */ static bool sht20_read_measurement(uint8_t command, float* value, bool is_temperature) { // 发送测量命令 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (SHT20_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, command, true); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "SHT20 measurement trigger failed: 0x%x", ret); return false; } // 等待测量完成(温度100ms,湿度40ms) vTaskDelay(pdMS_TO_TICKS(is_temperature ? 100 : 40)); // 读取测量结果(3字节:MSB, LSB, CRC) uint8_t data[3] = { 0 }; cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (SHT20_ADDR << 1) | I2C_MASTER_READ, true); i2c_master_read_byte(cmd, &data[0], I2C_MASTER_ACK); i2c_master_read_byte(cmd, &data[1], I2C_MASTER_ACK); i2c_master_read_byte(cmd, &data[2], I2C_MASTER_NACK); i2c_master_stop(cmd); ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "SHT20 read failed: 0x%x", ret); return false; } // 提取原始值(清除状态位) uint16_t raw_value = (data[0] << 8) | data[1]; raw_value &= 0xFFFC; // 清除最后两位状态位 // 计算实际值 if (is_temperature) { *value = -46.85 + (175.72 * raw_value) / 65536.0; } else { *value = -6.0 + (125.0 * raw_value) / 65536.0; } return true; } /** 读取SHT20温度 */ float sht20_read_temperature(void) { float temperature = 0.0; if (!sht20_read_measurement(SHT20_TRIGGER_TEMP_MEASUREMENT, &temperature, true)) { ESP_LOGE(TAG_I2C, "Failed to read SHT20 temperature"); return -1; } return temperature; } /** 读取SHT20湿度 */ float sht20_read_humidity(void) { float humidity = 0.0; if (!sht20_read_measurement(SHT20_TRIGGER_HUMIDITY_MEASUREMENT, &humidity, false)) { ESP_LOGE(TAG_I2C, "Failed to read SHT20 humidity"); return -1; } return humidity; } /** 初始化BMP280气压传感器 */ bool bmp280_init(bmp280_calib_t* calib) { ESP_LOGI(TAG_I2C, "Initializing BMP280..."); // 重置设备 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, BMP280_REG_RESET, true); i2c_master_write_byte(cmd, 0xB6, true); // 复位值 i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "BMP280 reset failed: 0x%x", ret); return false; } vTaskDelay(pdMS_TO_TICKS(10)); // 等待复位完成 // 检查设备ID uint8_t id = 0; cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, BMP280_REG_ID, true); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_READ, true); i2c_master_read_byte(cmd, &id, I2C_MASTER_NACK); i2c_master_stop(cmd); ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK || id != 0x58) { // BMP280 ID为0x58 ESP_LOGE(TAG_I2C, "BMP280 ID check failed: 0x%02X", id); return false; } // 读取校准参数 uint8_t calib_data[24] = { 0 }; cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, BMP280_REG_CALIB, true); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_READ, true); i2c_master_read(cmd, calib_data, sizeof(calib_data), I2C_MASTER_LAST_NACK); i2c_master_stop(cmd); ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "BMP280 calibration read failed: 0x%x", ret); return false; } // 解析校准参数 calib->dig_T1 = (calib_data[1] << 8) | calib_data[0]; calib->dig_T2 = (calib_data[3] << 8) | calib_data[2]; calib->dig_T3 = (calib_data[5] << 8) | calib_data[4]; calib->dig_P1 = (calib_data[7] << 8) | calib_data[6]; calib->dig_P2 = (calib_data[9] << 8) | calib_data[8]; calib->dig_P3 = (calib_data[11] << 8) | calib_data[10]; calib->dig_P4 = (calib_data[13] << 8) | calib_data[12]; calib->dig_P5 = (calib_data[15] << 8) | calib_data[14]; calib->dig_P6 = (calib_data[17] << 8) | calib_data[16]; calib->dig_P7 = (calib_data[19] << 8) | calib_data[18]; calib->dig_P8 = (calib_data[21] << 8) | calib_data[20]; calib->dig_P9 = (calib_data[23] << 8) | calib_data[22]; // 配置传感器 (温度x2 + 气压x16,正常模式) cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, BMP280_REG_CTRL_MEAS, true); i2c_master_write_byte(cmd, 0b10110111, true); // 温度x2,气压x16,正常模式 i2c_master_stop(cmd); ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "BMP280 config failed: 0x%x", ret); return false; } ESP_LOGI(TAG_I2C, "BMP280 initialized successfully"); return true; } /** 读取BMP280温度和气压原始值 */ static bool bmp280_read_raw(int32_t* temp_raw, int32_t* press_raw) { uint8_t data[6] = { 0 }; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, BMP280_REG_PRESS_MSB, true); i2c_master_start(cmd); i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_READ, true); i2c_master_read(cmd, data, sizeof(data), I2C_MASTER_LAST_NACK); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(100)); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG_I2C, "BMP280 read raw failed: 0x%x", ret); return false; } *press_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); *temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); return true; } /** 计算实际温度和气压值 */ static void bmp280_compensate(const bmp280_calib_t* calib, int32_t adc_T, int32_t adc_P, float* temperature, float* pressure) { // 补偿温度 int32_t var1, var2, T; var1 = (((double)adc_T) / 16384.0 - ((double)calib->dig_T1) / 1024.0) * ((double)calib->dig_T2); var2 = (((double)adc_T) / 131072.0 - ((double)calib->dig_T1) / 8192.0) * (((double)adc_T) / 131072.0 - ((double)calib->dig_T1) / 8192.0) * ((double)calib->dig_T3); int32_t t_fine = (int32_t)(var1 + var2); T = (var1 + var2) / 5120.0; // 温度补偿值 *temperature = (float)T * 100; // 单位:°C // 补偿气压 int64_t p, var3, var4; var1 = ((double)t_fine / 2.0) - 64000.0; var2 = var1 * var1 * (double)calib->dig_P6 / 32768.0; var2 = var2 + ((var1 * (double)calib->dig_P5) * 2.0); var2 = (var2 / 4.0) + (((double)calib->dig_P4) * 65536.0); var1 = (((double)calib->dig_P3) * var1 * var1 / 524288.0 + ((double)calib->dig_P2) * var1) / 524288.0; var1 = (1.0 + var1 / 32768.0) * ((double)calib->dig_P1); p = 1048576 - (double)adc_P; p = (p - (var2 / 4096.0)) * 6250.0 / var1; var1 = ((double)calib->dig_P9) * p * p / 2147483648.0; var2 = p * ((double)calib->dig_P8) / 32768.0; p = p + (var1 + var2 + ((double)calib->dig_P7)) / 16.0; *pressure = (float)p; // 单位:Pa } /** 读取BMP280温度和气压 */ bool bmp280_read(const bmp280_calib_t* calib, float* temperature, float* pressure) { int32_t temp_raw, press_raw; if (!bmp280_read_raw(&temp_raw, &press_raw)) { return false; } bmp280_compensate(calib, temp_raw, press_raw, temperature, pressure); *temperature /= 100.0; // 转换为°C return true; } /** I2C传感器任务(处理BH1750、SHT20和BMP280) */ void task_i2c_sensors(void* pvParameters) { // 初始化I2C ESP_ERROR_CHECK(i2c_master_init()); // 初始化传感器 bool bh1750_initialized = bh1750_init(); bool sht20_initialized = sht20_init(); bmp280_calib_t bmp280_calib; bool bmp280_initialized = bmp280_init(&bmp280_calib); vTaskDelay(pdMS_TO_TICKS(150)); // 等待传感器初始化完成 while (1) { // 读取传感器数据 float temp = -1, humi = -1, press = -1, lux = -1; float bmp_temp = -1; if (sht20_initialized) { temp = sht20_read_temperature(); humi = sht20_read_humidity(); } if (bmp280_initialized) { if (bmp280_read(&bmp280_calib, &bmp_temp, &press) && temp < 0) { temp = bmp_temp; // 若SHT20温度读取失败,使用BMP280温度 } } if (bh1750_initialized) { lux = bh1750_read_light(); } // 打印传感器数据 ESP_LOGI(TAG_I2C, "Temp: %.2f°C, Humi: %.2f%%, Press: %.2fPa, Lux: %.2flx, bmp280_temp: %.2f°C", temp, humi, press, lux, bmp_temp); // 更新全局传感器数据(加锁保护) if (xSemaphoreTake(g_sensor_mutex, pdMS_TO_TICKS(100)) == pdTRUE) { g_sensor_data.temp = temp; g_sensor_data.humi = humi; g_sensor_data.press = press; g_sensor_data.lux = lux; xSemaphoreGive(g_sensor_mutex); } // 读取间隔 vTaskDelay(pdMS_TO_TICKS(1500)); } } /** 全局亮度控制变量 */ float global_brightness = 0.2f; // 默认亮度为50% /** * 设置全局亮度 * @param brightness 亮度值 (0.0-1.0) */ void set_global_brightness(float brightness) { // 限制亮度范围 if (brightness < 0.0f) brightness = 0.0f; if (brightness > 1.0f) brightness = 1.0f; global_brightness = brightness; ESP_LOGI(TAG, "Brightness set to: %.2f", global_brightness); } /** * HSV转RGB颜色空间并调整亮度 * @param h 色相 (0.0-360.0) * @param s 饱和度 (0.0-1.0) * @param v 明度 (0.0-1.0) * @param brightness 亮度调整因子 (0.0-1.0) * @param r 输出红色值 (0-255) * @param g 输出绿色值 (0-255) * @param b 输出蓝色值 (0-255) */ void hsv_to_rgb(float h, float s, float v, float brightness, uint8_t *r, uint8_t *g, uint8_t *b) { int i; float f, p, q, t; // 应用亮度调整(使用非线性曲线增强暗部细节) v *= brightness * brightness; // 平方曲线使暗部更细腻 if (s == 0) { // 无饱和度(灰色) *r = *g = *b = (uint8_t)(v * 255); return; } h /= 60.0f; // 转换为0-6范围 i = (int)floor(h) % 6; f = h - floor(h); // 小数部分 p = v * (1 - s); q = v * (1 - s * f); t = v * (1 - s * (1 - f)); // 使用更精确的浮点数计算和舍入 switch (i) { case 0: *r = (uint8_t)(v*255.0f + 0.5f); *g = (uint8_t)(t*255.0f + 0.5f); *b = (uint8_t)(p*255.0f + 0.5f); break; case 1: *r = (uint8_t)(q*255.0f + 0.5f); *g = (uint8_t)(v*255.0f + 0.5f); *b = (uint8_t)(p*255.0f + 0.5f); break; case 2: *r = (uint8_t)(p*255.0f + 0.5f); *g = (uint8_t)(v*255.0f + 0.5f); *b = (uint8_t)(t*255.0f + 0.5f); break; case 3: *r = (uint8_t)(p*255.0f + 0.5f); *g = (uint8_t)(q*255.0f + 0.5f); *b = (uint8_t)(v*255.0f + 0.5f); break; case 4: *r = (uint8_t)(t*255.0f + 0.5f); *g = (uint8_t)(p*255.0f + 0.5f); *b = (uint8_t)(v*255.0f + 0.5f); break; default: *r = (uint8_t)(v*255.0f + 0.5f); *g = (uint8_t)(p*255.0f + 0.5f); *b = (uint8_t)(q*255.0f + 0.5f); break; } } /** RGB任务(平滑波浪彩虹效果) */ void task_rgb(void* pvParameters) { gpio_reset_pin(WS2812_GPIO_NUM); ws2812_strip_handle_t ws2812_handle = NULL; ws2812_init(WS2812_GPIO_NUM, WS2812_LED_NUM, &ws2812_handle); // 初始化熄灭所有LED for (int index = 0; index < WS2812_LED_NUM; index++) { ws2812_write(ws2812_handle, index, 0, 0, 0); } // 色相偏移量(控制彩虹波浪移动) - 使用浮点数提高精度 float hue_offset = 0.0f; // 波浪速度控制(数值越小速度越快) const float wave_speed = 0.5f; // 每个LED的色相间隔(减小步长使颜色过渡更平滑) const float hue_step = 8.0f; // 帧率控制(减少延迟提高帧率) const TickType_t frame_delay = pdMS_TO_TICKS(30); ESP_LOGI(TAG, "RGB task initialized with smooth rainbow wave effect"); while (1) { // 等待RGB运行标志 xEventGroupWaitBits(rgb_event_group, RGB_RUN_BIT, pdFALSE, // 不自动清除位 pdTRUE, // 等待位被设置 portMAX_DELAY); ESP_LOGI(TAG, "Smooth rainbow wave effect started"); // 持续运行彩虹效果,直到标志位被清除 while ((xEventGroupGetBits(rgb_event_group) & RGB_RUN_BIT) != 0) { // 为每个LED设置颜色 for (int index = 0; index < WS2812_LED_NUM; index++) { // 使用浮点数计算色相,避免整数截断 float hue = fmodf(hue_offset + index * hue_step, 360.0f); uint8_t r, g, b; // 转换HSV到RGB,使用全局亮度控制 hsv_to_rgb(hue, 1.0f, 0.9f, global_brightness, &r, &g, &b); // 设置LED颜色 ws2812_write(ws2812_handle, index, r, g, b); } // 更新色相偏移(使用浮点数提高精度) hue_offset = fmodf(hue_offset + wave_speed, 360.0f); // 减少延迟,提高帧率,使动画更流畅 vTaskDelay(frame_delay); } // 标志位被清除,熄灭所有LED for (int index = 0; index < WS2812_LED_NUM; index++) { ws2812_write(ws2812_handle, index, 0, 0, 0); } ESP_LOGI(TAG, "Smooth rainbow wave effect stopped"); } } /** 雷达任务 */ void task_radar(void* pvParameters) { gpio_reset_pin(LED_GPIO); gpio_reset_pin(LEIDA_GPIO); gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT); gpio_set_level(LED_GPIO, 1); // 初始熄灭 gpio_config_t radar_cfg = { .pin_bit_mask = (1ULL << LEIDA_GPIO), .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE }; gpio_config(&radar_cfg); ESP_LOGI(TAG2, "Radar task started"); static TickType_t rgb_start_tick = 0; const TickType_t rgb_duration = pdMS_TO_TICKS(50000); // 50秒 while (1) { bool detected = gpio_get_level(LEIDA_GPIO); gpio_set_level(LED_GPIO, !detected); // 检测到物体时点亮LED if (detected) { // 设置RGB运行标志 if ((xEventGroupGetBits(rgb_event_group) & RGB_RUN_BIT) == 0) { xEventGroupSetBits(rgb_event_group, RGB_RUN_BIT); ESP_LOGI(TAG2, "RGB activated by radar"); } rgb_start_tick = xTaskGetTickCount(); // 更新启动时间 } // 超时检查:50秒无检测则关闭RGB if ((xEventGroupGetBits(rgb_event_group) & RGB_RUN_BIT) && (xTaskGetTickCount() - rgb_start_tick > rgb_duration)) { xEventGroupClearBits(rgb_event_group, RGB_RUN_BIT); ESP_LOGI(TAG2, "RGB deactivated by timeout"); } vTaskDelay(pdMS_TO_TICKS(10)); } } /** ADC任务(土壤传感器) */ void task_adc(void* pvParameters) { // 配置ADC adc1_config_width(ADC_WIDTH_BIT_12); adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_DB_11); const float vref = 3.3f; // 参考电压 const float adc_max = 4095.0f; // 12位ADC最大值 while (1) { int adc_value = adc1_get_raw(ADC_CHANNEL); float voltage = adc_value * vref / adc_max; float I_value = voltage / 1000.0f; // 电流计算 float V_value = 3.3f - voltage; // 土壤两侧电压 float R_value = (I_value > 0) ? (V_value / I_value) : -1; // 电阻计算 ESP_LOGI(TAG_ADC, "ADC: %d, Voltage: %.2fV, Resist: %.2fΩ", adc_value, voltage, R_value); // 更新全局ADC数据 if (xSemaphoreTake(g_sensor_mutex, pdMS_TO_TICKS(100)) == pdTRUE) { g_sensor_data.adc_voltage = voltage; g_sensor_data.adc_resist = R_value; xSemaphoreGive(g_sensor_mutex); } vTaskDelay(pdMS_TO_TICKS(1500)); } } /** MQTT发送任务 */ static void task_wifi_mqtt(void* pvParameters) { // 初始化NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ESP_ERROR_CHECK(nvs_flash_init()); } s_wifi_ev = xEventGroupCreate(); EventBits_t ev = 0; // 初始化WIFI wifi_sta_init(wifi_event_handler); // 等待WIFI连接成功 ev = xEventGroupWaitBits(s_wifi_ev, WIFI_CONNECT_BIT, pdTRUE, pdFALSE, portMAX_DELAY); if (ev & WIFI_CONNECT_BIT) { mqtt_start(); } static char mqtt_json[512]; sensor_data_t temp_data; // 临时存储数据 while (1) { // 每5秒发送一次数据 if (s_is_mqtt_connected) { // 读取全局传感器数据(加锁保护) if (xSemaphoreTake(g_sensor_mutex, pdMS_TO_TICKS(100)) == pdTRUE) { temp_data = g_sensor_data; xSemaphoreGive(g_sensor_mutex); } // 构建JSON格式数据 snprintf(mqtt_json, sizeof(mqtt_json), "{\"opt\":\"dev_sta\",\"val\":{" "\"temp\":%.2f," "\"humi\":%.2f," "\"press\":%.2f," "\"light\":%.2f," "\"adc_voltage\":%.2f," "\"adc_resist\":%.2f" "}}", temp_data.temp, temp_data.humi, temp_data.press, temp_data.lux, temp_data.adc_voltage, temp_data.adc_resist); // 发送MQTT消息 int msg_id = esp_mqtt_client_publish(s_mqtt_client, MQTT_PUBLIC_TOPIC, mqtt_json, strlen(mqtt_json), 1, // QoS=1 0); // 不保留 ESP_LOGI(TAG, "MQTT published: %s (msg_id=%d)", mqtt_json, msg_id); } vTaskDelay(pdMS_TO_TICKS(3000)); // 2.5秒发送一次 } } void app_main(void) { // 初始化互斥锁保护传感器数据 g_sensor_mutex = xSemaphoreCreateMutex(); configASSERT(g_sensor_mutex); // 初始化RGB控制事件组 rgb_event_group = xEventGroupCreate(); // 启动各任务 xTaskCreate(task_adc, "adc_task", 4096, NULL, 8, &AdcTaskHandle); xTaskCreate(task_i2c_sensors, "i2c_sensors_task", 8192, NULL, 10, &IicTaskHandle); xTaskCreate(task_rgb, "rgb_task", 4096, NULL, 9, &RgbTaskHandle); xTaskCreate(task_radar, "radar_task", 4096, NULL, 9, &RadarTaskHandle); // 启动WIFI和MQTT任务 xTaskCreate(task_wifi_mqtt, "wifi_mqtt_task", 8192, NULL, 10, NULL); }在此基础上增加蓝牙配网功能

#include "MqttKit.h" #include <string.h> #include <stdio.h> #define CMD_TOPIC_PREFIX "$creq" void MQTT_NewBuffer(MQTT_PACKET_STRUCTURE *mqttPacket, uint32 size) { uint32 i = 0; if (mqttPacket->_data == NULL) { mqttPacket->_memFlag = MEM_FLAG_ALLOC; mqttPacket->_data = (uint8 *)MQTT_MallocBuffer(size); if (mqttPacket->_data != NULL) { mqttPacket->_len = 0; mqttPacket->_size = size; for (; i < mqttPacket->_size; i++) mqttPacket->_data[i] = 0; } } else { mqttPacket->_memFlag = MEM_FLAG_STATIC; for (; i < mqttPacket->_size; i++) mqttPacket->_data[i] = 0; mqttPacket->_len = 0; if (mqttPacket->_size < size) mqttPacket->_data = NULL; } } void MQTT_DeleteBuffer(MQTT_PACKET_STRUCTURE *mqttPacket) { if (mqttPacket->_memFlag == MEM_FLAG_ALLOC) MQTT_FreeBuffer(mqttPacket->_data); mqttPacket->_data = NULL; mqttPacket->_len = 0; mqttPacket->_size = 0; mqttPacket->_memFlag = MEM_FLAG_NULL; } int32 MQTT_DumpLength(size_t len, uint8 *buf) { int32 i = 0; for (i = 1; i <= 4; ++i) { *buf = len % 128; len >>= 7; if (len > 0) { *buf |= 128; ++buf; } else { return i; } } return -1; } int32 MQTT_ReadLength(const uint8 *stream, int32 size, uint32 *len) { int32 i; const uint8 *in = stream; uint32 multiplier = 1; *len = 0; for (i = 0; i < size; ++i) { *len += (in[i] & 0x7f) * multiplier; if (!(in[i] & 0x80)) { return i + 1; } multiplier <<= 7; if (multiplier >= 2097152) // 128 * *128 * *128 { return -2; // error, out of range } } return -1; // not complete }uint8 MQTT_UnPacketRecv(uint8 *dataPtr) { uint8 status = 255; uint8 type = dataPtr[0] >> 4; // 类型检查 if (type < 1 || type > 14) return status; if (type == MQTT_PKT_PUBLISH) { uint8 *msgPtr; uint32 remain_len = 0; msgPtr = dataPtr + MQTT_ReadLength(dataPtr + 1, 4, &remain_len) + 1; if (remain_len < 2 || dataPtr[0] & 0x01) // retain return 255; if (remain_len < ((uint16)msgPtr[0] << 8 | msgPtr[1]) + 2) return 255; if (strstr((int8 *)msgPtr + 2, CMD_TOPIC_PREFIX) != NULL) // 如果是命令下发 status = MQTT_PKT_CMD; else status = MQTT_PKT_PUBLISH; } else status = type; return status; }解释代码

#ifndef _ESP8266_H_ #define _ESP8266_H_ #include "main.h" //C¿â #include <stdarg.h> #include <stdlib.h> #define SSID "WIFI" #define PASS "123456789" #define ProductKey "a1wDiNYFwS5" #define DeviceName "PillsCar" #define ClientId "123|securemode=3\\,signmethod=hmacsha1|" #define Password "6940E27041D06C047F31951986F328A11267240C" #define mqttHostUrl "a1wDiNYFwS5.iot-as-mqtt.cn-shanghai.aliyuncs.com" #define port "1883" #define Huart_wifi huart2 #define REV_OK 0 //½ÓÊÕÍê³É±êÖ¾ #define REV_WAIT 1 //½ÓÊÕδÍê³É±êÖ¾ #define DelayXms(x) HAL_Delay(x) extern unsigned char ESP8266_buf[1024]; extern unsigned short ESP8266_cnt; extern uint8_t uartwifi_value; //´®¿Ú2½ÓÊÕ»º´æ±äÁ¿ typedef struct{ //ʱ¼ä½á¹¹Ìå uint16_t year; uint8_t month; uint8_t day; uint8_t week; uint8_t hour; uint8_t minute; uint8_t second; }Time_Get; void ESP8266_init(void); //Á¬ÉÏÍøÂçÔò²»¼ÌÐøÁ¬½ÓÁË void Ali_MQTT_Publish(void); //Éϱ¨ÏûÏ¢ ½¨Òé1sÉÏ´«Ò»´ÎÊý¾Ý void Ali_MQTT_Publish_1(void); void Ali_MQTT_Publish_3(void); void Ali_MQTT_Publish_4(void); void Ali_MQTT_Publish_mode(void); void Ali_MQTT_Recevie(void); //½ÓÊÕÏûÏ¢ _Bool ESP8266_Status(void); //1-Á¬½Ó״̬ 0-¶Ï¿ª×´Ì¬ Time_Get ESP8266_Get_Time(void); //´®¿Ú»Øµ÷º¯ÊýʹÓ÷½·¨ //void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //{ // if(huart->Instance == Huart_wifi.Instance)//´®¿Ú´¥·¢ÖÐ¶Ï // { // if(huart->Instance == Huart_wifi.Instance)//´®¿Ú´¥·¢ // { // HAL_UART_Receive_IT(&Huart_wifi, &uartwifi_value, 1); // if(ESP8266_cnt >= sizeof(ESP8266_buf)) ESP8266_cnt = 0; //·ÀÖ¹´®¿Ú±»Ë¢±¬ // ESP8266_buf[ESP8266_cnt++] = uartwifi_value; // } // } //} #endif

/** * @brief MQTT协议解析状态机 * @param q 循环队列指针,存储待解析的原始字节流 * @param ctx 解析上下文,保存解析过程中的中间状态 * @return 解析结果状态码: * NEED_MORE(需更多数据) | PARSE_ERROR(协议错误) | PARSE_COMPLETE(解析完成) * @note 基于MQTT 3.1.1协议规范实现[^1] */ char mqtt_parser(CircularQueue *q, uint8_t *packet_buff) { static uint8_t State = 0; // 解析状态 static uint32_t total_length = 0; // 报文长度 static uint8_t remaining_length_bytes = 0; // 剩余长度字节数 static uint8_t timeup_count; // 超时计数器 uint8_t packet_type; uint8_t byte; uint8_t multiplier; uint8_t remaining_length; uint32_t i; printf("timeup_count状态 %d\n", timeup_count); while (!Queue_Empty(q)) { printf("\n队列状态 %d\n", State); switch (State) { case 0: // 解析固定报头和剩余长度 if (Queue_Length(q) < 2) return 0; // 获取第一个字节(偏移量0) Queue_PeekAt(q, 0, &packet_type); packet_type >>= 4; /* 报文类型验证 */ if (packet_type < 1 || packet_type > 14) { Queue_MoveReadIndex(q, 1); // 仅丢弃错误字节 return 0; } /* 剩余长度解析 */ multiplier = 1; total_length = 0; remaining_length_bytes = 0; remaining_length = 0; do { /* 协议合规性检查 */ if (remaining_length_bytes >= 4)// 协议规定最多4字节 { Queue_MoveReadIndex(q, 1); // 丢弃错误报文 State = 0; return 0;// 立即退出避免死循环 } // 计算当前字节的物理偏移量 Queue_PeekAt(q, 1 + remaining_length_bytes, &byte); remaining_length += (byte & 0x7F) * multiplier; multiplier *= 128; remaining_length_bytes++; } while (byte & 0x80); total_length = 1 + remaining_length_bytes + remaining_length; // 计算报文长度 State = 1; break; case 1: if (Queue_Length(q) < total_length) { if (++timeup_count >= 5) { // 连续5次检测失败 Queue_MoveReadIndex(q, 1); // 仅丢弃当前报文已解析部分 State = 0; timeup_count = 0; BUZZER_Test(); /* 蜂鸣器播放音乐 */ } return 0; } else { timeup_count = 0; // 成功获取数据时清零 } // 批量拷贝数据 for (i = 0; i < total_length; i++) { Queue_PeekAt(q, i, &packet_buff[i]); } //打印报文 for (i = 0; i < total_length; i++) { printf("0x%02X ", packet_buff[i]); } // Aliyun_Iot((char *)packet_buff); // 调用报文处理函数 Queue_MoveReadIndex(q, total_length); // 一次性移除已读取数据 State = 0; total_length = 0; remaining_length_bytes = 0; timeup_count = 0; break; } } return 1; }

大家在看

recommend-type

华为逆变器SUN2000-(33KTL, 40KTL) MODBUS接口定义描述

ModBus-RTU 协议是工业领域广泛使用的通讯协议,是应用于电气通信终端上的一种通用语言。通过此协议,逆变器相互之间、逆变器经由网络(例如 RS485 总线)和其它设备之间可以通信。它已经成为一通用工业标准。有了它,不同厂商生产的逆变器设备可以连成工业网络,进行集中监控。协议中描述了主从节点定义方式,主节点使用各种请求方式访问其它设备的过程,从节点如何响应来自其它设备的请求,以及双方如何侦测错误并记录。它制定了消息域格局和数据内容的详细定义。 随着华为逆变器业务的不断拓展,越来越多的通用或定制逆变器采用 ModBus 协议进行通讯,本文对华为逆变器的 ModBus 协议进行了描述和说明,用于规范和约束后续的第三方集成开发和定制。
recommend-type

BCM 56XX SDK 编程手册

Broadcom SDK 5.6 平台指南,关于SDK编译方法、步骤的编程手册,是学习了解Broadcom SDK的很有用的参考手册
recommend-type

Gurobi 生产计划调度学习案例(含代码实现)

Gurobi 生产计划调度学习案例(含代码实现)
recommend-type

FPGA数字信号处理设计教程--system generator 入门与提高随书光盘源码

FPGA数字信号处理设计教程--system generator 入门与提高随书光盘源码
recommend-type

SPP Workshop.pdf

SPP Overall introduction SPP介绍 服务备件计划介绍 含某知名车企的实际案例

最新推荐

recommend-type

新能源车电机控制器:基于TI芯片的FOC算法源代码与实际应用

内容概要:本文详细介绍了基于TI芯片的FOC(场向量控制)算法在新能源车电机控制器中的应用。文章首先阐述了新能源车电机控制器的重要性及其对车辆性能的影响,接着深入探讨了FOC算法的工作原理,强调其在提高电机控制精度和能效方面的优势。随后,文章展示了完整的源代码资料,涵盖采样模块、CAN通信模块等多个关键部分,并指出这些代码不仅限于理论演示,而是来自实际量产的应用程序。此外,文中还特别提到代码遵循严格的规范,有助于读者理解和学习电机控制软件的最佳实践。 适合人群:从事新能源车研发的技术人员、电机控制工程师、嵌入式系统开发者以及对电机控制感兴趣的电子工程学生。 使用场景及目标:① 学习并掌握基于TI芯片的FOC算法的具体实现;② 理解电机控制器各模块的功能和交互方式;③ 提升实际项目开发能力,减少开发过程中遇到的问题。 其他说明:本文提供的源代码资料来源于早期已量产的新能源车控制器,因此具有较高的实用价值和参考意义。
recommend-type

中证500指数成分股历年调整名单2007至2023年 调入调出

中证500指数是中证指数有限公司开发的指数,样本空间内股票由全部A股中剔除沪深300指数成分股及总市值排名前300名的股票后,选取总市值排名靠前的500只股票组成,综合反映中国A股市场中一批中小市值公司的股票价格表现。包含字段:公告日期、变更日期、成份证券代码、成份证券简称、变动方式。各次调整日期:2006-12-26、2007-01-15、2007-06-01、2007-07-02、2007-12-10、2008-01-02、2008-06-04、2008-07-01、2008-12-15、2009-01-05、2009-05-05、2009-05-06、2009-06-15、2009-07-01、2009-08-10、2009-08-10。资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
recommend-type

基于28335的高精度旋变软解码技术及其应用 - 电机控制

内容概要:本文详细介绍了基于28335芯片实现的旋变软解码技术。该技术在0-360°范围内与TI方案相比,偏差极小(平均偏差最大为0.0009弧度),并且响应速度优于AD2S1205(解算器建立时间不超过5ms)。文中还讨论了信号解调方法,利用三角函数积化和差公式将旋变输出信号分解为高低频两部分,并通过锁相环和特殊设计的滤波器提高信号处理的精度和稳定性。最终,该技术在12位AD下能保证10-11位的精度。 适合人群:从事电机控制、自动化系统设计及相关领域的工程师和技术人员。 使用场景及目标:适用于需要高精度、快速响应的旋转变压器解码应用场景,如工业自动化、机器人技术和电动汽车等领域。目标是提供一种替代传统硬件解码方案的技术选择,提升系统的可靠性和性能。 阅读建议:读者可以通过本文深入了解旋变软解码的工作原理和技术细节,掌握其相对于现有解决方案的优势,从而更好地应用于实际项目中。
recommend-type

langchain4j-embeddings-bge-small-en-1.0.0-beta5.jar中文文档.zip

1、压缩文件中包含: 中文文档、jar包下载地址、Maven依赖、Gradle依赖、源代码下载地址。 2、使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 3、特殊说明: (1)本文档为人性化翻译,精心制作,请放心使用; (2)只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; (3)不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 4、温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件。 5、本文件关键字: jar中文文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册。
recommend-type

苹果内购(IAP)人民币价格明细表

资源下载链接为: https://pan.quark.cn/s/9e7ef05254f8 在苹果的生态系统中,IAP(应用内购买)是苹果应用商店(App Store)中应用开发者常采用的一种盈利模式,允许用户在应用内直接购买虚拟商品或服务。苹果为开发者提供了一份详细的人民币(CNY)IAP定价表,这份定价表具有以下特点: 价格分级:定价表由多个价格等级组成,开发者可根据虚拟商品的价值选择相应等级,等级越高,价格越高。例如,低等级可能对应基础功能解锁,高等级则对应高级服务或大量虚拟道具。 税收与分成:苹果会从应用内购买金额中抽取30%作为服务费或佣金,这是苹果生态的固定规则。不过,开发者实际到手的收入会因不同国家和地区的税收政策而有所变化,但定价表中的价格等级本身是固定的,便于开发者统一管理。 多级定价策略:通过设置不同价格等级,开发者可以根据商品或服务的类型与价值进行合理定价,以满足不同消费能力的用户需求,从而最大化应用的总收入。例如,一款游戏可以通过设置不同等级的虚拟货币包,吸引不同付费意愿的玩家。 特殊等级:除了标准等级外,定价表还包含备用等级和特殊等级(如备用等级A、备用等级B等),这些等级可能是为应对特殊情况或促销活动而设置的额外价格点,为开发者提供了更灵活的定价选择。 苹果IAP定价表是开发者设计应用内购机制的重要参考。它不仅为开发者提供了标准的收入分成模型,还允许开发者根据产品特性设定价格等级,以适应市场和满足不同用户需求。同时,开发者在使用定价表时,还需严格遵守苹果的《App Store审查指南》,包括30%的分成政策、使用苹果支付接口、提供清晰的产品描述和定价信息等。苹果对应用内交易有严格规定,以确保交易的透明性和安全性。总之,苹果IAP定价表是开发者在应用内购设计中不可或缺的工具,但开发者也需密切关注苹果政策变化,以确保应用的合规运营和收益最大化。
recommend-type

掌握XFireSpring整合技术:HELLOworld原代码使用教程

标题:“xfirespring整合使用原代码”中提到的“xfirespring”是指将XFire和Spring框架进行整合使用。XFire是一个基于SOAP的Web服务框架,而Spring是一个轻量级的Java/Java EE全功能栈的应用程序框架。在Web服务开发中,将XFire与Spring整合能够发挥两者的优势,例如Spring的依赖注入、事务管理等特性,与XFire的简洁的Web服务开发模型相结合。 描述:“xfirespring整合使用HELLOworld原代码”说明了在这个整合过程中实现了一个非常基本的Web服务示例,即“HELLOworld”。这通常意味着创建了一个能够返回"HELLO world"字符串作为响应的Web服务方法。这个简单的例子用来展示如何设置环境、编写服务类、定义Web服务接口以及部署和测试整合后的应用程序。 标签:“xfirespring”表明文档、代码示例或者讨论集中于XFire和Spring的整合技术。 文件列表中的“index.jsp”通常是一个Web应用程序的入口点,它可能用于提供一个用户界面,通过这个界面调用Web服务或者展示Web服务的调用结果。“WEB-INF”是Java Web应用中的一个特殊目录,它存放了应用服务器加载的Servlet类文件和相关的配置文件,例如web.xml。web.xml文件中定义了Web应用程序的配置信息,如Servlet映射、初始化参数、安全约束等。“META-INF”目录包含了元数据信息,这些信息通常由部署工具使用,用于描述应用的元数据,如manifest文件,它记录了归档文件中的包信息以及相关的依赖关系。 整合XFire和Spring框架,具体知识点可以分为以下几个部分: 1. XFire框架概述 XFire是一个开源的Web服务框架,它是基于SOAP协议的,提供了一种简化的方式来创建、部署和调用Web服务。XFire支持多种数据绑定,包括XML、JSON和Java数据对象等。开发人员可以使用注解或者基于XML的配置来定义服务接口和服务实现。 2. Spring框架概述 Spring是一个全面的企业应用开发框架,它提供了丰富的功能,包括但不限于依赖注入、面向切面编程(AOP)、数据访问/集成、消息传递、事务管理等。Spring的核心特性是依赖注入,通过依赖注入能够将应用程序的组件解耦合,从而提高应用程序的灵活性和可测试性。 3. XFire和Spring整合的目的 整合这两个框架的目的是为了利用各自的优势。XFire可以用来创建Web服务,而Spring可以管理这些Web服务的生命周期,提供企业级服务,如事务管理、安全性、数据访问等。整合后,开发者可以享受Spring的依赖注入、事务管理等企业级功能,同时利用XFire的简洁的Web服务开发模型。 4. XFire与Spring整合的基本步骤 整合的基本步骤可能包括添加必要的依赖到项目中,配置Spring的applicationContext.xml,以包括XFire特定的bean配置。比如,需要配置XFire的ServiceExporter和ServicePublisher beans,使得Spring可以管理XFire的Web服务。同时,需要定义服务接口以及服务实现类,并通过注解或者XML配置将其关联起来。 5. Web服务实现示例:“HELLOworld” 实现一个Web服务通常涉及到定义服务接口和服务实现类。服务接口定义了服务的方法,而服务实现类则提供了这些方法的具体实现。在XFire和Spring整合的上下文中,“HELLOworld”示例可能包含一个接口定义,比如`HelloWorldService`,和一个实现类`HelloWorldServiceImpl`,该类有一个`sayHello`方法返回"HELLO world"字符串。 6. 部署和测试 部署Web服务时,需要将应用程序打包成WAR文件,并部署到支持Servlet 2.3及以上版本的Web应用服务器上。部署后,可以通过客户端或浏览器测试Web服务的功能,例如通过访问XFire提供的服务描述页面(WSDL)来了解如何调用服务。 7. JSP与Web服务交互 如果在应用程序中使用了JSP页面,那么JSP可以用来作为用户与Web服务交互的界面。例如,JSP可以包含JavaScript代码来发送异步的AJAX请求到Web服务,并展示返回的结果给用户。在这个过程中,JSP页面可能使用XMLHttpRequest对象或者现代的Fetch API与Web服务进行通信。 8. 项目配置文件说明 项目配置文件如web.xml和applicationContext.xml分别在Web应用和服务配置中扮演关键角色。web.xml负责定义Web组件,比如Servlet、过滤器和监听器,而applicationContext.xml则负责定义Spring容器中的bean,包括数据源、事务管理器、业务逻辑组件和服务访问器等。 总之,通过上述整合使用原代码的知识点,可以深入理解XFire与Spring框架的结合使用,以及如何开发和部署基本的Web服务。这些技术知识有助于进行更高层次的Web服务开发,以及在复杂的IT环境中灵活运用各种框架和工具。
recommend-type

【Unity2018汉化大揭秘】:一步到位优化中文用户体验

# 摘要 本论文详细介绍了Unity2018汉化项目的实施过程,从理论准备到实践步骤,再到后期的测试与优化,最终分享了汉化经验和心得。首先,强调了汉化项目对于用户体验提升和产品国际化的重要性,并分析了汉化资源的分类、识别与管理方法。接着,详细阐述了汉化工具的选择、脚本编写、资源手动调整等实践步骤,以及汉化质量测试、体验优化和项目维护更新的重要性。论文最后通过案例研究,分析了汉化中的挑战和应对策略,并预测了汉化技术未来的发展方向。整个汉化过程不仅涉及技术层面的操作,还包含了深入的理论研究和实践心得分享,对提升汉化项目的效率和质量具有指导意义。 # 关键字 Unity汉化;用户体验;国际化;资源
recommend-type

iPhone

<think>我们注意到用户意图不明确,但提到了“照片提取”和“其他功能帮助”。因此,我们需要通过搜索来获取关于iPhone照片提取的常见方法以及其他可能的功能帮助。由于用户问题比较宽泛,我们将重点放在照片提取上,因为这是明确提到的关键词。同时,我们也会考虑一些其他常用功能的帮助。首先,针对照片提取,可能涉及从iPhone导出照片、从备份中提取照片、或者从损坏的设备中恢复照片等。我们将搜索这些方面的信息。其次,关于其他功能帮助,我们可以提供一些常见问题的快速指南,如电池优化、屏幕时间管理等。根据要求,我们需要将答案组织为多个方法或步骤,并在每个步骤间换行。同时,避免使用第一人称和步骤词汇。由于
recommend-type

驾校一点通软件:提升驾驶证考试通过率

标题“驾校一点通”指向的是一款专门为学员考取驾驶证提供帮助的软件,该软件强调其辅助性质,旨在为学员提供便捷的学习方式和复习资料。从描述中可以推断出,“驾校一点通”是一个与驾驶考试相关的应用软件,这类软件一般包含驾驶理论学习、模拟考试、交通法规解释等内容。 文件标题中的“2007”这个年份标签很可能意味着软件的最初发布时间或版本更新年份,这说明了软件具有一定的历史背景和可能经过了多次更新,以适应不断变化的驾驶考试要求。 压缩包子文件的文件名称列表中,有以下几个文件类型值得关注: 1. images.dat:这个文件名表明,这是一个包含图像数据的文件,很可能包含了用于软件界面展示的图片,如各种标志、道路场景等图形。在驾照学习软件中,这类图片通常用于帮助用户认识和记忆不同交通标志、信号灯以及驾驶过程中需要注意的各种道路情况。 2. library.dat:这个文件名暗示它是一个包含了大量信息的库文件,可能包含了法规、驾驶知识、考试题库等数据。这类文件是提供给用户学习驾驶理论知识和准备科目一理论考试的重要资源。 3. 驾校一点通小型汽车专用.exe:这是一个可执行文件,是软件的主要安装程序。根据标题推测,这款软件主要是针对小型汽车驾照考试的学员设计的。通常,小型汽车(C1类驾照)需要学习包括车辆构造、基础驾驶技能、安全行车常识、交通法规等内容。 4. 使用说明.html:这个文件是软件使用说明的文档,通常以网页格式存在,用户可以通过浏览器阅读。使用说明应该会详细介绍软件的安装流程、功能介绍、如何使用软件的各种模块以及如何通过软件来帮助自己更好地准备考试。 综合以上信息,我们可以挖掘出以下几个相关知识点: - 软件类型:辅助学习软件,专门针对驾驶考试设计。 - 应用领域:主要用于帮助驾考学员准备理论和实践考试。 - 文件类型:包括图片文件(images.dat)、库文件(library.dat)、可执行文件(.exe)和网页格式的说明文件(.html)。 - 功能内容:可能包含交通法规知识学习、交通标志识别、驾驶理论学习、模拟考试、考试题库练习等功能。 - 版本信息:软件很可能最早发布于2007年,后续可能有多个版本更新。 - 用户群体:主要面向小型汽车驾照考生,即C1类驾照学员。 - 使用方式:用户需要将.exe安装文件进行安装,然后根据.html格式的使用说明来熟悉软件操作,从而利用images.dat和library.dat中的资源来辅助学习。 以上知识点为从给定文件信息中提炼出来的重点,这些内容对于了解“驾校一点通”这款软件的功能、作用、使用方法以及它的发展历史都有重要的指导意义。
recommend-type

【DFLauncher自动化教程】:简化游戏启动流程,让游戏体验更流畅

# 摘要 DFLauncher是一个功能丰富的游戏启动和管理平台,本论文将介绍其安装、基础使用、高级设置、社区互动以及插件开发等方面。通过对配置文件的解析、界面定制、自动化功能的实现、高级配置选项、安全性和性能监控的详细讨论,本文阐述了DFLauncher如何帮助用户更高效地管理和优化游戏环境。此外,本文还探讨了DFLauncher社区的资源分享、教育教程和插件开发等内容,