type = MQTT_UnPacketRecv(cmd); switch (type) { case MQTT_PKT_CMD: // 命令下发 result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len); // 解出topic和消息体 if (result == 0) { UsartPrintf(USART_DEBUG, "cmdid: %s, req: %s, req_len: %d\r\n", cmdid_topic, req_payload, req_len); if (MQTT_PacketCmdResp(cmdid_topic, req_payload, &mqttPacket) == 0) // 命令回复组包 { UsartPrintf(USART_DEBUG, "Tips: Send CmdResp\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); // 回复命令 MQTT_DeleteBuffer(&mqttPacket); // 删包 } } break; case MQTT_PKT_PUBLISH: // 接收的Publish消息 result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id); if (result == 0) { UsartPrintf(USART_DEBUG, "topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n", cmdid_topic, topic_len, req_payload, req_len); switch (qos) { case 1: // 收到publish的qos为1,设备需要回复Ack if (MQTT_PacketPublishAck(pkt_id, &mqttPacket) == 0) { UsartPrintf(USART_DEBUG, "Tips: Send PublishAck\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); } break; case 2: // 收到publish的qos为2,设备先回复Rec // 平台回复Rel,设备再回复Comp if (MQTT_PacketPublishRec(pkt_id, &mqttPacket) == 0) { UsartPrintf(USART_DEBUG, "Tips: Send PublishRec\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); } break; default: break; } } break; case MQTT_PKT_PUBACK: // 发送Publish消息,平台回复的Ack if (MQTT_UnPacketPublishAck(cmd) == 0) UsartPrintf(USART_DEBUG, "Tips: MQTT Publish Send OK\r\n"); break; case MQTT_PKT_PUBREC: // 发送Publish消息,平台回复的Rec,设备需回复Rel消息 if (MQTT_UnPacketPublishRec(cmd) == 0) { UsartPrintf(USART_DEBUG, "Tips: Rev PublishRec\r\n"); if (MQTT_PacketPublishRel(MQTT_PUBLISH_ID, &mqttPacket) == 0) { UsartPrintf(USART_DEBUG, "Tips: Send PublishRel\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); } } break; case MQTT_PKT_PUBREL: // 收到Publish消息,设备回复Rec后,平台回复的Rel,设备需再回复Comp if (MQTT_UnPacketPublishRel(cmd, pkt_id) == 0) { UsartPrintf(USART_DEBUG, "Tips: Rev PublishRel\r\n"); if (MQTT_PacketPublishComp(MQTT_PUBLISH_ID, &mqttPacket) == 0) { UsartPrintf(USART_DEBUG, "Tips: Send PublishComp\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); } } break; case MQTT_PKT_PUBCOMP: // 发送Publish消息,平台返回Rec,设备回复Rel,平台再返回的Comp if (MQTT_UnPacketPublishComp(cmd) == 0) { UsartPrintf(USART_DEBUG, "Tips: Rev PublishComp\r\n"); } break; case MQTT_PKT_SUBACK: // 发送Subscribe消息的Ack if (MQTT_UnPacketSubscribe(cmd) == 0) UsartPrintf(USART_DEBUG, "Tips: MQTT Subscribe OK\r\n"); else UsartPrintf(USART_DEBUG, "Tips: MQTT Subscribe Err\r\n"); break; case MQTT_PKT_UNSUBACK: // 发送UnSubscribe消息的Ack if (MQTT_UnPacketUnSubscribe(cmd) == 0) UsartPrintf(USART_DEBUG, "Tips: MQTT UnSubscribe OK\r\n"); else UsartPrintf(USART_DEBUG, "Tips: MQTT UnSubscribe Err\r\n"); break; default: result = -1; break; } ESP8266_Clear(); // 清空缓存 if (result == -1) return; dataPtr = strchr(req_payload, '}'); // 搜索'}' if (dataPtr != NULL && result != -1) // 如果找到了 { dataPtr++; while (*dataPtr >= '0' && *dataPtr <= '9') // 判断是否是下发的命令控制数据 { numBuf[num++] = *dataPtr++; } num = atoi((const char *)numBuf); // 转为数值形式 } if (type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH) { MQTT_FreeBuffer(cmdid_topic); MQTT_FreeBuffer(req_payload); } } 解析这个代码

时间: 2025-06-19 07:17:32 浏览: 11
<think>嗯,我现在要分析用户提供的这段MQTT处理代码。首先,我需要理解代码的整体结构和功能。看起来这是一个基于C语言的嵌入式系统代码,处理MQTT协议的不同类型数据包。用户可能想了解这段代码的工作流程,或者可能遇到问题需要解决。 首先,代码里有一个switch语句,根据type的不同值执行不同的case。type是通过MQTT_UnPacketRecv(cmd)函数获取的,可能代表接收到的MQTT数据包类型。接下来各个case处理不同的MQTT报文类型,比如命令下发(MQTT_PKT_CMD)、发布消息(MQTT_PKT_PUBLISH)以及各种确认包(PUBACK、PUBREC等)。 我需要逐个分析每个case的功能。比如,在MQTT_PKT_CMD的情况下,代码调用MQTT_UnPacketCmd解包命令,获取主题、负载和长度,然后发送响应。对于MQTT_PKT_PUBLISH,处理不同QoS级别的响应,比如QoS1需要回复PUBACK,QoS2则需要先回复PUBREC,再处理后续的PUBREL和PUBCOMP。 接下来,代码在处理完主要逻辑后,清空缓存(ESP8266_Clear()),然后检查结果,并解析req_payload中的数字数据。最后,释放相关缓冲区。 需要注意的地方包括:各个解包和组包函数的返回值处理是否正确,资源释放是否有遗漏,以及QoS2的完整流程是否被正确处理。例如,在QoS2的情况下,设备需要先回复PUBREC,然后等待平台的PUBREL,再回复PUBCOMP。代码中的逻辑是否符合这个流程? 另外,代码末尾对req_payload的处理,搜索'}'字符并提取后面的数字,可能需要确保数据格式正确,避免缓冲区溢出或解析错误。例如,numBuf数组的大小是否足够,是否有越界风险? 还有错误处理部分,比如result == -1时的处理,是否覆盖了所有可能的错误情况?是否存在内存泄漏的可能,比如MQTT_FreeBuffer是否在所有路径都被调用? 用户可能的问题可能涉及代码中的某个部分无法正常工作,比如命令响应未发送,或者QoS2的处理流程中断。需要检查每个步骤的条件判断和函数返回值是否正确处理。此外,调试输出(UsartPrintf)是否能够帮助定位问题,比如打印的topic、payload等信息是否正确。 总结来说,这段代码实现了MQTT协议中不同报文类型的接收与响应处理,特别是针对命令下发和发布消息的不同QoS级别进行了相应的确认回复。需要确保各个步骤的顺序和条件正确,资源管理得当,以及错误处理完善。</think>以下是对代码的逐步解析: 1. **报文类型识别** ```c type = MQTT_UnPacketRecv(cmd); ``` 通过`MQTT_UnPacketRecv`解析接收到的原始数据`cmd`,识别MQTT报文类型,返回结果存储在`type`中。 2. **多分支处理** ```c switch (type) { case MQTT_PKT_CMD: //命令报文 case MQTT_PKT_PUBLISH: //发布报文 case MQTT_PKT_PUBACK: //QoS1确认 ...} ``` 根据报文类型进入不同处理分支,主要处理命令下发、消息发布及其服务质量(QoS)相关的交互流程。 3. **命令报文处理流程** ```c result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len); if (result == 0) { MQTT_PacketCmdResp(...); //组响应包 ESP8266_SendData(...); //发送响应 MQTT_DeleteBuffer(...); //释放资源 } ``` - 解析命令主题和负载 - 生成响应报文并通过WiFi模块发送 - 清理报文缓冲区 4. **发布消息处理(QoS分级)** ```c switch (qos) { case 1: //QoS1需回复PUBACK MQTT_PacketPublishAck(...); case 2: //QoS2需完成PUBREC→PUBREL→PUBCOMP握手 MQTT_PacketPublishRec(...); ...} ``` - QoS0:无确认 - QoS1:单次确认(PUBACK) - QoS2:四次握手(PUBREC→PUBREL→PUBCOMP) 5. **消息确认处理** ```c case MQTT_PKT_PUBREC: //收到PUBREC后回复PUBREL MQTT_PacketPublishRel(...); case MQTT_PKT_PUBREL: //收到PUBREL后回复PUBCOMP MQTT_PacketPublishComp(...); ``` 实现QoS2的完整确认流程,确保消息可靠传输。 6. **订阅管理** ```c case MQTT_PKT_SUBACK: //订阅确认 case MQTT_PKT_UNSUBACK: //取消订阅确认 ``` 处理订阅操作的成功/失败状态反馈。 7. **数据解析与清理** ```c dataPtr = strchr(req_payload, '}'); //定位数据段结尾 atoi((const char *)numBuf); //转换数字参数 MQTT_FreeBuffer(...); //释放动态内存 ``` - 从JSON格式负载中提取数字参数 - 确保内存资源释放,防止泄漏 8. **调试输出** ```c UsartPrintf(USART_DEBUG, ...) //输出调试信息 ``` 通过串口打印关键处理步骤,便于监控运行状态。 **流程图示意** ``` 开始 │ ├─▶ 接收原始数据 → 解析报文类型 │ ├─▶ 命令报文 → 解析命令 → 发送响应 → 释放资源 │ ├─▶ 发布报文 → 根据QoS级别处理 → 发送对应确认包 │ ├─▶ 订阅确认 → 记录状态 │ └─▶ 数据解析 → 清理资源 → 结束 ``` **关键数学表达式** - QoS2传输可靠性公式(四次握手): $$ \text{成功概率} = p^4 \quad (p为单次传输成功率) $$ **注意事项** 1. 内存管理:所有`MQTT_DeleteBuffer`和`MQTT_FreeBuffer`必须成对出现 2. QoS2流程必须完整执行,否则会导致连接阻塞 3. 数字解析部分需增加缓冲区溢出检查 4. 建议在`default`分支添加错误日志记录未识别报文类型
阅读全文

相关推荐

#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; }解释代码

uint8 MQTT_PacketPublish(uint16 pkt_id, const int8 *topic, const int8 *payload, uint32 payload_len, enum MqttQosLevel qos, int32 retain, int32 own, MQTT_PACKET_STRUCTURE *mqttPacket) { uint32 total_len = 0, topic_len = 0; uint32 data_len = 0; int32 len = 0; uint8 flags = 0; //pkt_id检查---------------------------------------------------------------------------- if(pkt_id == 0) return 1; //$dp为系统上传数据点的指令-------------------------------------------------------------- for(topic_len = 0; topic[topic_len] != '\0'; ++topic_len) { if((topic[topic_len] == '#') || (topic[topic_len] == '+')) return 2; } //Publish消息--------------------------------------------------------------------------- flags |= MQTT_PKT_PUBLISH << 4; //retain标志---------------------------------------------------------------------------- if(retain) flags |= 0x01; //总长度-------------------------------------------------------------------------------- total_len = topic_len + payload_len + 2; //qos级别--主要用于PUBLISH(发布态)消息的,保证消息传递的次数----------------------------- switch(qos) { case MQTT_QOS_LEVEL0: flags |= MQTT_CONNECT_WILL_QOS0; //最多一次 break; case MQTT_QOS_LEVEL1: flags |= 0x02; //最少一次 total_len += 2; break; case MQTT_QOS_LEVEL2: flags |= 0x04; //只有一次 total_len += 2; break; default: return 3; } //分配内存------------------------------------------------------------------------------ if(payload != NULL) { if(payload[0] == 2) { uint32 data_len_t = 0; while(payload[data_len_t++] != '}'); data_len_t -= 3; data_len = data_len_t + 7; data_len_t = payload_len - data_len; MQTT_NewBuffer(mqttPacket, total_len + 3 - data_len_t); if(mqttPacket->_data == NULL) return 4; memset(mqttPacket->_data, 0, total_len + 3 - data_len_t); } else { MQTT_NewBuffer(mqttPacket, total_len + 5); if(mqttPacket->_data == NULL) return 4; memset(mqttPacket->_data, 0, to

uint8 MQTT_PacketPublish(uint16 pkt_id, const int8 *topic, const int8 *payload, uint32 payload_len, enum MqttQosLevel qos, int32 retain, int32 own, MQTT_PACKET_STRUCTURE *mqttPacket) { uint32 total_len = 0, topic_len = 0; uint32 data_len = 0; int32 len = 0; uint8 flags = 0; // pkt_id检查---------------------------------------------------------------------------- if (pkt_id == 0) return 1; //$dp为系统上传数据点的指令-------------------------------------------------------------- for (topic_len = 0; topic[topic_len] != '\0'; ++topic_len) { if ((topic[topic_len] == '#') || (topic[topic_len] == '+')) return 2; } // Publish消息--------------------------------------------------------------------------- flags |= MQTT_PKT_PUBLISH << 4; // retain标志---------------------------------------------------------------------------- if (retain) flags |= 0x01; // 总长度-------------------------------------------------------------------------------- total_len = topic_len + payload_len + 2; // qos级别--主要用于PUBLISH(发布态)消息的,保证消息传递的次数----------------------------- switch (qos) { case MQTT_QOS_LEVEL0: flags |= MQTT_CONNECT_WILL_QOS0; // 最多一次 break; case MQTT_QOS_LEVEL1: flags |= 0x02; // 最少一次 total_len += 2; break; case MQTT_QOS_LEVEL2: flags |= 0x04; // 只有一次 total_len += 2; break; default: return 3; } // 分配内存------------------------------------------------------------------------------ if (payload[0] == 2) { uint32 data_len_t = 0; while (payload[data_len_t++] != '}') ; data_len_t -= 3; data_len = data_len_t + 7; data_len_t = payload_len - data_len; MQTT_NewBuffer(mqttPacket, total_len + 3 - data_len_t); if (mqttPacket->_data == NULL) return 4; memset(mqttPacket->_data, 0, total_len + 3 - data_len_t); } else { MQTT_NewBuffer(mqttPacket, total_len + 3); if (mqttPacket->_data == NULL) return 4; memset(mqttPacket->_data, 0, total_len + 3); }解释代码

/* 3、PUBLISH 发布消息 C <=> S */ char MQTT_Publish(int qos, int retain, char *post_topic, unsigned int id, char *message) { const int BUFF_SIZE = 512; char data[BUFF_SIZE] = {0}; int len = 0; int remaining_length; int encoded_remaining_len_bytes = 0; int temp_remaining_length; char remaining_encoded[4]; int encoded_len; /* ------------------------------- 报文检查、配置 ------------------------------------ */ int topic_length = post_topic ? strlen(post_topic) : 0; int message_length = message ? strlen(message) : 0; if (topic_length == 0 || topic_length > 65535) // 主题长度小于65535 return 1; if (!message) // 消息内容不可为空 return 2; if (qos > 0 && id == 0) // QoS>0时必须提供有效id return 3; if (qos < 0 || qos > 2) return 4; /* 剩余长度计算 */ remaining_length = (2 + topic_length) + ((qos > 0) ? 2 : 0) + message_length; /* 缓冲区容量检查 */ temp_remaining_length = remaining_length; do { encoded_remaining_len_bytes++; temp_remaining_length /= 128; } while (temp_remaining_length > 0); /* 确保数据长度不超过缓存大小 */ if ((1 + encoded_remaining_len_bytes + remaining_length) > BUFF_SIZE) { return 5; } /* ------------------------------- 报文检查、配置结束 ------------------------------------ */ /* ------------------------------- 开始构建报文 ------------------------------------ */ /* 固定报头构造 */ data[len++] = 0x30 | (qos << 1) | retain; /* 剩余长度编码 */ encoded_len = encode_remaining_length(remaining_length, remaining_encoded); memcpy(&data[len], remaining_encoded, encoded_len); len += encoded_len; /* 可变报头与有效载荷 */ // 主题名 data[len++] = (topic_length >> 8) & 0xFF; data[len++] = topic_length & 0xFF; memcpy(&data[len], post_topic, topic_length); len += topic_length; /* 报文标识符(QoS > 0) */ if (qos > 0) { data[len++] = (id >> 8) & 0xFF; data[len++] = id & 0xFF; } /* 有效载荷 */ memcpy(&data[len], message, message_length); len += message_length; /* ------------------------------- 构建报文完成 ------------------------------------ */ /* 数据发送 */ MQTT_SendData(data, 4); // 直接发送数据 mqtt_debug(data, len, "\r\n* 3、PUBLISH – 发布消息 *\r\n"); if(qos > 0) { data[0] |= 0x08; // 置位dup重发标志 Write_MQTT_TxDataBuff(data, len); // 将数据保存到缓冲区 mqtt_debug(data, len, "\r\n* 将重发标志dup置1 ,然后将报文保存到缓冲区 *\r\n"); } WIFI_Clean(); return 0; } 对比这2个代码 // 发送消息入口 void mqtt_publish(uint8_t *data, uint16_t len, MQTT_QoS qos) { static uint16_t pid = 0; uint16_t packet_id = pid++; // QoS需要重传机制 if (qos > QoS0) { RetransPacket pkt = { .packet_id = packet_id, .send_time = sys_tick, .retry_count = 0, .payload = malloc(len), .payload_len = len, .qos = qos }; memcpy(pkt.payload, data, len); retrans_list_insert(pkt); } // 实际发送函数 send_mqtt_packet(data, len, qos, packet_id); } // 收到ACK时的处理 void mqtt_handle_puback(uint16_t packet_id) { retrans_list_remove(packet_id); }

/* 3、PUBLISH 发布消息 C <=> S */ char MQTT_Publish(int qos, int retain, char *post_topic, char *message) { const int BUFF_SIZE = 512; char data[BUFF_SIZE] = {0}; int len = 0; int remaining_length; static unsigned int id; int encoded_remaining_len_bytes = 0; int temp_remaining_length; char remaining_encoded[4]; int encoded_len; /* ------------------------------- 报文检查、配置 ------------------------------------ */ int topic_length = post_topic ? strlen(post_topic) : 0; int message_length = message ? strlen(message) : 0; if (topic_length == 0 || topic_length > 65535) // 主题长度小于65535 return 1; if (!message) // 消息内容不可为空 return 2; if (qos < 0 || qos > 2) return 3; /* 剩余长度计算 */ remaining_length = (2 + topic_length) + ((qos > 0) ? 2 : 0) + message_length; /* 缓冲区容量检查 */ temp_remaining_length = remaining_length; do { encoded_remaining_len_bytes++; temp_remaining_length /= 128; } while (temp_remaining_length > 0); /* 确保数据长度不超过缓存大小 */ if ((1 + encoded_remaining_len_bytes + remaining_length) > BUFF_SIZE) { return 4; } /* ------------------------------- 报文检查、配置结束 ------------------------------------ */ /* ------------------------------- 开始构建报文 ------------------------------------ */ /* 固定报头构造 */ data[len++] = 0x30 | (qos << 1) | retain; /* 剩余长度编码 */ encoded_len = encode_remaining_length(remaining_length, remaining_encoded); memcpy(&data[len], remaining_encoded, encoded_len); len += encoded_len; /* 可变报头与有效载荷 */ // 主题名 data[len++] = (topic_length >> 8) & 0xFF; data[len++] = topic_length & 0xFF; memcpy(&data[len], post_topic, topic_length); len += topic_length; /* 报文标识符(QoS > 0) */ if (qos > 0) { if(id == 0) id = 1; data[len++] = (id >> 8) & 0xFF; data[len++] = id & 0xFF; id++; } /* 有效载荷 */ memcpy(&data[len], message, message_length); len += message_length; /* ------------------------------- 构建报文完成 ------------------------------------ */ /* 数据发送 */ MQTT_SendData(data, 4); // 直接发送数据 mqtt_debug(data, len, "\r\n* 3、PUBLISH – 发布消息 *\r\n"); if(qos > 0) { data[0] |= 0x08; // 置位dup重发标志 Write_MQTT_TxDataBuff(data, len); // 将数据保存到缓冲区 mqtt_debug(data, len, "\r\n* 将重发标志dup置1 ,然后将报文保存到缓冲区 *\r\n"); } WIFI_Clean(); return 0; } 使下面的代码能够配合代码1 // 发送消息入口 void mqtt_publish(uint8_t *data, uint16_t len, MQTT_QoS qos) { static uint16_t pid = 0; uint16_t packet_id = pid++; // QoS需要重传机制 if (qos > QoS0) { RetransPacket pkt = { .packet_id = packet_id, .send_time = sys_tick, .retry_count = 0, .payload = malloc(len), .payload_len = len, .qos = qos }; memcpy(pkt.payload, data, len); retrans_list_insert(pkt); } // 实际发送函数 send_mqtt_packet(data, len, qos, packet_id); } // 收到ACK时的处理 void mqtt_handle_puback(uint16_t packet_id) { retrans_list_remove(packet_id); }

void mqtt_publish(uint8_t *data, uint16_t len, MQTT_QoS qos) { static uint16_t pid = 0; uint16_t packet_id = 0; // 默认QoS0的Packet ID为0 if (qos > QoS0) { packet_id = pid++; // 处理pid溢出:跳过正在重传的ID(需辅助函数检查) while (is_packet_id_in_use(pid)) { pid++; } RetransPacket pkt = { .packet_id = packet_id, .send_time = sys_tick, .retry_count = 0, .payload = malloc(len), .payload_len = len, .qos = qos }; memcpy(pkt.payload, data, len); retrans_list_insert(pkt); } // 发送时明确处理QoS0的Packet ID send_mqtt_packet(data, len, qos, (qos > QoS0) ? packet_id : 0); } /* 3、PUBLISH 发布消息 C <=> S */ char MQTT_Publish(int qos, int retain, char *post_topic, unsigned int id, char *message) { const int BUFF_SIZE = 512; char data[BUFF_SIZE] = {0}; int len = 0; int remaining_length; int encoded_remaining_len_bytes = 0; int temp_remaining_length; char remaining_encoded[4]; int encoded_len; /* ------------------------------- 报文检查、配置 ------------------------------------ */ int topic_length = post_topic ? strlen(post_topic) : 0; int message_length = message ? strlen(message) : 0; if (topic_length == 0 || topic_length > 65535) // 主题长度小于65535 return 1; if (!message) // 消息内容不可为空 return 2; if (qos > 0 && id == 0) // QoS>0时必须提供有效id return 3; if (qos < 0 || qos > 2) return 4; /* 剩余长度计算 */ remaining_length = (2 + topic_length) + ((qos > 0) ? 2 : 0) + message_length; /* 缓冲区容量检查 */ temp_remaining_length = remaining_length; do { encoded_remaining_len_bytes++; temp_remaining_length /= 128; } while (temp_remaining_length > 0); /* 确保数据长度不超过缓存大小 */ if ((1 + encoded_remaining_len_bytes + remaining_length) > BUFF_SIZE) { return 5; } /* ------------------------------- 报文检查、配置结束 ------------------------------------ */ /* ------------------------------- 开始构建报文 ------------------------------------ */ /* 固定报头构造 */ data[len++] = 0x30 | (qos << 1) | retain; /* 剩余长度编码 */ encoded_len = encode_remaining_length(remaining_length, remaining_encoded); memcpy(&data[len], remaining_encoded, encoded_len); len += encoded_len; /* 可变报头与有效载荷 */ // 主题名 data[len++] = (topic_length >> 8) & 0xFF; data[len++] = topic_length & 0xFF; memcpy(&data[len], post_topic, topic_length); len += topic_length; /* 报文标识符(QoS > 0) */ if (qos > 0) { data[len++] = (id >> 8) & 0xFF; data[len++] = id & 0xFF; } /* 有效载荷 */ memcpy(&data[len], message, message_length); len += message_length; /* ------------------------------- 构建报文完成 ------------------------------------ */ /* 数据发送 */ MQTT_SendData(data, 4); // 直接发送数据 mqtt_debug(data, len, "\r\n* 3、PUBLISH – 发布消息 *\r\n"); if(qos > 0) { data[0] |= 0x08; // 置位dup重发标志 Write_MQTT_TxDataBuff(data, len); // 将数据保存到缓冲区 mqtt_debug(data, len, "\r\n* 将重发标志dup置1 ,然后将报文保存到缓冲区 *\r\n"); } WIFI_Clean(); return 0; }对比这2个代码,第一个函数能构建publish报文?

char MQTT_Publish(int dup, int qos, int retain, char *post_topic, unsigned int id, char *message) { const int BUFF_SIZE = 512; char data[BUFF_SIZE] = {0}; int len = 0; int remaining_length; int encoded_remaining_len_bytes = 0; int temp_remaining_length; char remaining_encoded[4]; int encoded_len; int topic_length = post_topic ? strlen(post_topic) : 0; int message_length = message ? strlen(message) : 0; if (topic_length == 0 || topic_length > 65535) // 主题长度小于65535 return 1; if (!message) // 消息内容不可为空 return 2; if (qos > 0 && id == 0) // QoS>0时必须提供有效id return 3; if (qos == 0 && dup != 0) // QoS为0,不支持重发 return 4; if (qos < 0 || qos > 2) return 5; /* 剩余长度计算 */ remaining_length = (2 + topic_length) + ((qos > 0) ? 2 : 0) + message_length; /* 缓冲区容量检查 */ temp_remaining_length = remaining_length; do { encoded_remaining_len_bytes++; temp_remaining_length /= 128; } while (temp_remaining_length > 0); /* 确保数据长度不超过缓存大小 */ if ((1 + encoded_remaining_len_bytes + remaining_length) > BUFF_SIZE) { return 6; } /* 固定报头构造 */ data[len++] = 0x30 | (dup << 3) | (qos << 1) | retain; /* 剩余长度编码 */ encoded_len = encode_remaining_length(remaining_length, remaining_encoded); memcpy(&data[len], remaining_encoded, encoded_len); len += encoded_len; /* 可变报头与有效载荷 */ // 主题名 data[len++] = (topic_length >> 8) & 0xFF; data[len++] = topic_length & 0xFF; memcpy(&data[len], post_topic, topic_length); len += topic_length; /* 报文标识符(QoS > 0) */ if (qos > 0) { data[len++] = (id >> 8) & 0xFF; data[len++] = id & 0xFF; } /* 有效载荷 */ memcpy(&data[len], message, message_length); len += message_length; /* 数据发送 */ MQTT_SendData(data, 4); // 直接发送数据 if(qos > 0) { MQTT_Publish(1, qos, retain, post_topic, id, message);

uint8 MQTT_PacketSubscribe(uint16 pkt_id, enum MqttQosLevel qos, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket) { uint32 topic_len = 0, remain_len = 0; int16 len = 0; uint8 i = 0; if(pkt_id == 0) return 1; //计算topic长度------------------------------------------------------------------------- for(; i < topics_cnt; i++) { if(topics[i] == NULL) return 2; topic_len += strlen(topics[i]); } //2 bytes packet id + topic filter(2 bytes topic + topic length + 1 byte reserve)------ remain_len = 2 + 3 * topics_cnt + topic_len; //分配内存------------------------------------------------------------------------------ MQTT_NewBuffer(mqttPacket, remain_len + 5); if(mqttPacket->_data == NULL) return 3; /*************************************固定头部***********************************************/ //固定头部----------------------头部消息------------------------------------------------- mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_SUBSCRIBE << 4 | 0x02; //固定头部----------------------剩余长度值----------------------------------------------- len = MQTT_DumpLength(remain_len, mqttPacket->_data + mqttPacket->_len); if(len < 0) { MQTT_DeleteBuffer(mqttPacket); return 4; } else mqttPacket->_len += len; /*************************************payload***********************************************/ //payload----------------------pkt_id--------------------------------------------------- mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id); mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id); //payload----------------------topic_name----------------------------------------------- for(i = 0; i < topics_cnt; i++) { topic_len = strlen(topics[i]); mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len); mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len); strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topics[i], topic_len); mqttPacket->_len += topic_len; mqttPacket->_data[mqttPacket->_len++] = qos & 0xFF; } return 0; }

/************************************************************************************************** Filename: SampleApp.h Revised: $Date: 2007-10-27 17:22:23 -0700 (Sat, 27 Oct 2007) $ Revision: $Revision: 15795 $ Description: This file contains the Sample Application definitions. Copyright 2007 Texas Instruments Incorporated. All rights reserved. IMPORTANT: Your use of this Software is limited to those specific rights granted under the terms of a software license agreement between the user who downloaded the software, his/her employer (which must be your employer) and Texas Instruments Incorporated (the "License"). You may not use this Software unless you agree to abide by the terms of the License. The License limits your use, and you acknowledge, that the Software may not be modified, copied or distributed unless embedded on a Texas Instruments microcontroller or used solely and exclusively in conjunction with a Texas Instruments radio frequency transceiver, which is integrated into your product. Other than for the foregoing purpose, you may not use, reproduce, copy, prepare derivative works of, modify, distribute, perform, display or sell this Software and/or its documentation for any purpose. YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED 揂S IS?WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. Should you have any questions regarding your right to use this Software, contact Texas Instruments Incorporated at www.TI.com. **************************************************************************************************/ #ifndef SAMPLEAPP_H #define SAMPLEAPP_H #ifdef __cplusplus extern "C" { #endif /********************************************************************* * INCLUDES */ #include "ZComDef.h" /********************************************************************* * CONSTANTS */ // These constants are only for example and should be changed to the // device's needs #define SAMPLEAPP_ENDPOINT 20 #define SAMPLEAPP_PROFID 0x0F08 #define SAMPLEAPP_DEVICEID 0x0001 #define SAMPLEAPP_DEVICE_VERSION 0 #define SAMPLEAPP_FLAGS 0 #define SAMPLEAPP_MAX_CLUSTERS 2 #define SAMPLEAPP_PERIODIC_CLUSTERID 1 #define SAMPLEAPP_FLASH_CLUSTERID 2 #define SAMPLEAPP_LEDCTL_CLUSTERID 3 // Send Message Timeout #define SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT 5000 // Every 5 seconds // Application Events (OSAL) - These are bit weighted definitions. #define SAMPLEAPP_SEND_PERIODIC_MSG_EVT 0x0001 // Group ID for Flash Command #define SAMPLEAPP_FLASH_GROUP 0x0001 // Flash Command Duration - in milliseconds #define SAMPLEAPP_FLASH_DURATION 1000 /********************************************************************* * MACROS */ /********************************************************************* * FUNCTIONS */ /* * Task Initialization for the Generic Application */ extern void SampleApp_Init( uint8 task_id ); /* * Task Event Processor for the Generic Application */ extern UINT16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ); /********************************************************************* *********************************************************************/ #ifdef __cplusplus } #endif #endif /* SAMPLEAPP_H */ /************************************************************************************************** Filename: SampleApp.c Revised: $Date: 2009-03-18 15:56:27 -0700 (Wed, 18 Mar 2009) $ Revision: $Revision: 19453 $ Description: Sample Application (no Profile). Copyright 2007 Texas Instruments Incorporated. All rights reserved. IMPORTANT: Your use of this Software is limited to those specific rights granted under the terms of a software license agreement between the user who downloaded the software, his/her employer (which must be your employer) and Texas Instruments Incorporated (the "License"). You may not use this Software unless you agree to abide by the terms of the License. The License limits your use, and you acknowledge, that the Software may not be modified, copied or distributed unless embedded on a Texas Instruments microcontroller or used solely and exclusively in conjunction with a Texas Instruments radio frequency transceiver, which is integrated into your product. Other than for the foregoing purpose, you may not use, reproduce, copy, prepare derivative works of, modify, distribute, perform, display or sell this Software and/or its documentation for any purpose. YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED 揂S IS?WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. Should you have any questions regarding your right to use this Software, contact Texas Instruments Incorporated at www.TI.com. **************************************************************************************************/ /********************************************************************* This application isn't intended to do anything useful, it is intended to be a simple example of an application's structure. This application sends it's messages either as broadcast or broadcast filtered group messages. The other (more normal) message addressing is unicast. Most of the other sample applications are written to support the unicast message model. Key control: SW1: Sends a flash command to all devices in Group 1. SW2: Adds/Removes (toggles) this device in and out of Group 1. This will enable and disable the reception of the flash command. *********************************************************************/ /********************************************************************* * INCLUDES */ #include <stdio.h> #include <string.h> #include "OSAL.h" #include "ZGlobals.h" #include "AF.h" #include "aps_groups.h" #include "ZDApp.h" #include "MT_UART.h" //add by 1305106 #include "SampleApp.h" #include "SampleAppHw.h" #include "OnBoard.h" /* HAL */ #include "hal_lcd.h" #include "hal_led.h" #include "hal_key.h" #include"sht11.h" /********************************************************************* * MACROS */ /********************************************************************* * CONSTANTS */ /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // This list should be filled with Application specific Cluster IDs. const cId_t SampleApp_ClusterList[SAMPLEAPP_MAX_CLUSTERS] = { SAMPLEAPP_PERIODIC_CLUSTERID, SAMPLEAPP_FLASH_CLUSTERID, }; const SimpleDescriptionFormat_t SampleApp_SimpleDesc = { SAMPLEAPP_ENDPOINT, // int Endpoint; SAMPLEAPP_PROFID, // uint16 AppProfId[2]; SAMPLEAPP_DEVICEID, // uint16 AppDeviceId[2]; SAMPLEAPP_DEVICE_VERSION, // int AppDevVer:4; SAMPLEAPP_FLAGS, // int AppFlags:4; SAMPLEAPP_MAX_CLUSTERS, // uint8 AppNumInClusters; (cId_t *)SampleApp_ClusterList, // uint8 *pAppInClusterList; SAMPLEAPP_MAX_CLUSTERS, // uint8 AppNumInClusters; (cId_t *)SampleApp_ClusterList // uint8 *pAppInClusterList; }; // This is the Endpoint/Interface description. It is defined here, but // filled-in in SampleApp_Init(). Another way to go would be to fill // in the structure here and make it a "const" (in code space). The // way it's defined in this sample app it is define in RAM. endPointDesc_t SampleApp_epDesc; /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ /********************************************************************* * LOCAL VARIABLES */ uint8 SampleApp_TaskID; // Task ID for internal task/event processing // This variable will be received when // SampleApp_Init() is called. devStates_t SampleApp_NwkState; uint8 SampleApp_TransID; // This is the unique message ID (counter) afAddrType_t SampleApp_Periodic_DstAddr; afAddrType_t SampleApp_Flash_DstAddr; aps_Group_t SampleApp_Group; uint8 SampleAppPeriodicCounter = 0; uint8 SampleAppFlashCounter = 0; /********************************************************************* * LOCAL FUNCTIONS */ void SampleApp_HandleKeys( uint8 shift, uint8 keys ); void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pckt ); void SampleApp_SendPeriodicMessage( void ); void SampleApp_SendFlashMessage( uint16 flashTime ); /********************************************************************* * NETWORK LAYER CALLBACKS */ /********************************************************************* * PUBLIC FUNCTIONS */ /********************************************************************* * @fn SampleApp_Init * * @brief Initialization function for the Generic App Task. * This is called during initialization and should contain * any application specific initialization (ie. hardware * initialization/setup, table initialization, power up * notificaiton ... ). * * @param task_id - the ID assigned by OSAL. This ID should be * used to send messages and set timers. * * @return none */ void SampleApp_Init( uint8 task_id ) { SampleApp_TaskID = task_id; SampleApp_NwkState = DEV_INIT; SampleApp_TransID = 0; // Device hardware initialization can be added here or in main() (Zmain.c). // If the hardware is application specific - add it here. // If the hardware is other parts of the device add it in main(). #if defined ( BUILD_ALL_DEVICES ) // The "Demo" target is setup to have BUILD_ALL_DEVICES and HOLD_AUTO_START // We are looking at a jumper (defined in SampleAppHw.c) to be jumpered // together - if they are - we will start up a coordinator. Otherwise, // the device will start as a router. if ( readCoordinatorJumper() ) zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR; else zgDeviceLogicalType = ZG_DEVICETYPE_ROUTER; #endif // BUILD_ALL_DEVICES #if defined ( HOLD_AUTO_START ) // HOLD_AUTO_START is a compile option that will surpress ZDApp // from starting the device and wait for the application to // start the device. ZDOInitDevice(0); #endif // Setup for the periodic message's destination address // Broadcast to everyone SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast; SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF; // Setup for the flash command's destination address - Group 1 SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP; // Fill out the endpoint description. SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; SampleApp_epDesc.task_id = &SampleApp_TaskID; SampleApp_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc; SampleApp_epDesc.latencyReq = noLatencyReqs; // Register the endpoint description with the AF afRegister( &SampleApp_epDesc ); // Register for all key events - This app will handle all key events RegisterForKeys( SampleApp_TaskID ); MT_UartRegisterTaskID( SampleApp_TaskID ); //add by 1305106 // By default, all devices start out in Group 1 SampleApp_Group.ID = 0x0001; osal_memcpy( SampleApp_Group.name, "Group 1", 7 ); aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group ); #if defined ( LCD_SUPPORTED ) HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 ); #endif } /********************************************************************* * @fn SampleApp_ProcessEvent * * @brief Generic Application Task event processor. This function * is called to process all events for the task. Events * include timers, messages and any other user defined events. * * @param task_id - The OSAL assigned task ID. * @param events - events to process. This is a bit map and can * contain more than one event. * * @return none */ uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) { MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { // Received when a key is pressed case KEY_CHANGE: SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; // Received when a messages is received (OTA) for this endpoint case AF_INCOMING_MSG_CMD: SampleApp_MessageMSGCB( MSGpkt ); break;; // Received whenever the device changes state in the network case ZDO_STATE_CHANGE: SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status); if ( (SampleApp_NwkState == DEV_ZB_COORD) || (SampleApp_NwkState == DEV_ROUTER) || (SampleApp_NwkState == DEV_END_DEVICE) ) { // Start sending the periodic message in a regular interval. HalLedSet(HAL_LED_1, HAL_LED_MODE_ON); osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT ); } else { // Device is no longer in the network } break; default: break; } osal_msg_deallocate( (uint8 *)MSGpkt ); // Release the memory MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); // Next - if one is available } return (events ^ SYS_EVENT_MSG); // return unprocessed events } // Send a message out - This event is generated by a timer // (setup in SampleApp_Init()). if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) { SampleApp_SendPeriodicMessage(); // Send the periodic message // Setup to send message again in normal period (+ a little jitter) osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) ); return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT); // return unprocessed events } return 0; // Discard unknown events } /********************************************************************* * Event Generation Functions */ /********************************************************************* * @fn SampleApp_HandleKeys * * @brief Handles all key events for this device. * * @param shift - true if in shift/alt. * @param keys - bit field for key events. Valid entries: * HAL_KEY_SW_2 * HAL_KEY_SW_1 * * @return none */ void SampleApp_HandleKeys( uint8 shift, uint8 keys ) { (void)shift; // Intentionally unreferenced parameter if ( keys & HAL_KEY_SW_6 ) { /* This key sends the Flash Command is sent to Group 1. * This device will not receive the Flash Command from this * device (even if it belongs to group 1). */ SampleApp_SendFlashMessage( SAMPLEAPP_FLASH_DURATION ); } if ( keys & HAL_KEY_SW_2 ) { /* The Flashr Command is sent to Group 1. * This key toggles this device in and out of group 1. * If this device doesn't belong to group 1, this application * will not receive the Flash command sent to group 1. */ aps_Group_t *grp; grp = aps_FindGroup( SAMPLEAPP_ENDPOINT, SAMPLEAPP_FLASH_GROUP ); if ( grp ) { // Remove from the group aps_RemoveGroup( SAMPLEAPP_ENDPOINT, SAMPLEAPP_FLASH_GROUP ); } else { // Add to the flash group aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group ); } } } /********************************************************************* * LOCAL FUNCTIONS */ /********************************************************************* * @fn SampleApp_MessageMSGCB * * @brief Data message processor callback. This function processes * any incoming data - probably from other devices. So, based * on cluster ID, perform the intended action. * * @param none * * @return none */ void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) { uint16 flashTime; unsigned char *buf; switch ( pkt->clusterId ) { case SAMPLEAPP_PERIODIC_CLUSTERID: buf = pkt->cmd.Data; HalUARTWrite(0,"\r\nTemp:", 7); HalUARTWrite(0, buf, 7); HalUARTWrite(0," Humi:", 6); HalUARTWrite(0, buf+7, 7); break; case SAMPLEAPP_FLASH_CLUSTERID: flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] ); HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) ); break; } } /********************************************************************* * @fn SampleApp_SendPeriodicMessage * * @brief Send the periodic message. * * @param none * * @return none */ void SampleApp_SendPeriodicMessage( void ) { char temp_buf[7]; char humi_buf[7]; char i; char buf[14]; float humi,temp; if(GetHumiAndTemp(&humi,&temp) == 0) { sprintf(humi_buf, (char *)"%f", humi); sprintf(temp_buf, (char *)"%f", temp); for(i=0; i<7; i++) { buf[i] = temp_buf[i]; buf[i+7] = humi_buf[i]; } AF_DataRequest( &SampleApp_Periodic_DstAddr, &SampleApp_epDesc, SAMPLEAPP_PERIODIC_CLUSTERID, 14, (unsigned char*)buf, &SampleApp_TransID, AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ); } } /********************************************************************* * @fn SampleApp_SendFlashMessage * * @brief Send the flash message to group 1. * * @param flashTime - in milliseconds * * @return none */ void SampleApp_SendFlashMessage( uint16 flashTime ) { uint8 buffer[3]; buffer[0] = (uint8)(SampleAppFlashCounter++); buffer[1] = LO_UINT16( flashTime ); buffer[2] = HI_UINT16( flashTime ); if ( AF_DataRequest( &SampleApp_Flash_DstAddr, &SampleApp_epDesc, SAMPLEAPP_FLASH_CLUSTERID, 3, buffer, &SampleApp_TransID, AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) { } else { // Error occurred in request to send. } } /********************************************************************* *********************************************************************/ 一、系统分层功能 感知层:用实验箱的两个传感器模块 传感器 1:温湿度传感器,采集温度、湿度数据。 传感器 2:红外对射 ,采集触发状态。 网络层: Zigbee 通信:传感器 1、2 的数据,经 Zigbee 节点传给协调器,协调器通过串口发 PC 端。 数据处理:解析串口数据,分主题发 MQTT 网络,或存数据库 。 应用层:用 Python 做终端,实现界面、串口、数据库交互(可加 MQTT),完成这些功能: 二、具体功能 数据上传: 远程设备实时收传感器 1 数据,格式:学号姓名缩写+温度+湿度 。 远程设备实时收传感器 2 数据,格式:学号姓名缩写+传感器名+interrupt 。 Mysql 数据库:建不同数据表存两个传感器数据,表含 学号姓名缩写、传感器名、数据值、传感器状态 等字段 。 命令下发: 发 学号姓名缩写+Num1Led+on ,传感器 1 连的 Zigbee 模块 LED2 亮;发 …+off 则灭 。 发 学号姓名缩写+Num2Led+on ,传感器 2 连的 Zigbee 模块 LED2 亮;发 …+off 则灭 。 数据联动: 传感器 1:温度>25 且湿度>60,其连的 Zigbee 模块 LED2 闪烁,远程设备、数据库表显示 “温湿度异常”;恢复后显示 “异常解除” 。 传感器 2:触发超 5 次,其连的 Zigbee 模块 LED2 闪烁,远程设备、数据库表显示 “传感状态异常”;恢复后显示 “传感异常解除” 。 强制解除:发 学号姓名缩写+Num1Led+relie ,传感器 1 连的 LED2 停闪,设备和表显示 “强制异常解除温湿度” ;发 …+Num2Led+relie 同理。 数据展示:Python 界面里,串口控制,实时显示传感器 1 的 学号姓名缩写、温度、湿度 数据,呈现底层传感器联动状态 。 根据所给代码和要求,完成功能,给出完整的代码

最新推荐

recommend-type

软件论文设计方案(1).docx

软件论文设计方案(1).docx
recommend-type

飞思OA数据库文件下载指南

根据给定的文件信息,我们可以推断出以下知识点: 首先,从标题“飞思OA源代码[数据库文件]”可以看出,这里涉及的是一个名为“飞思OA”的办公自动化(Office Automation,简称OA)系统的源代码,并且特别提到了数据库文件。OA系统是用于企事业单位内部办公流程自动化的软件系统,它旨在提高工作效率、减少不必要的工作重复,以及增强信息交流与共享。 对于“飞思OA源代码”,这部分信息指出我们正在讨论的是OA系统的源代码部分,这通常意味着软件开发者或维护者拥有访问和修改软件底层代码的权限。源代码对于开发人员来说非常重要,因为它是软件功能实现的直接体现,而数据库文件则是其中的一个关键组成部分,用来存储和管理用户数据、业务数据等信息。 从描述“飞思OA源代码[数据库文件],以上代码没有数据库文件,请从这里下”可以分析出以下信息:虽然文件列表中提到了“DB”,但实际在当前上下文中,并没有提供包含完整数据库文件的下载链接或直接说明,这意味着如果用户需要获取完整的飞思OA系统的数据库文件,可能需要通过其他途径或者联系提供者获取。 文件的标签为“飞思OA源代码[数据库文件]”,这与标题保持一致,表明这是一个与飞思OA系统源代码相关的标签,而附加的“[数据库文件]”特别强调了数据库内容的重要性。在软件开发中,标签常用于帮助分类和检索信息,所以这个标签在这里是为了解释文件内容的属性和类型。 文件名称列表中的“DB”很可能指向的是数据库文件。在一般情况下,数据库文件的扩展名可能包括“.db”、“.sql”、“.mdb”、“.dbf”等,具体要看数据库的类型和使用的数据库管理系统(如MySQL、SQLite、Access等)。如果“DB”是指数据库文件,那么它很可能是以某种形式的压缩文件或包存在,这从“压缩包子文件的文件名称列表”可以推测。 针对这些知识点,以下是一些详细的解释和补充: 1. 办公自动化(OA)系统的构成: - OA系统由多个模块组成,比如工作流管理、文档管理、会议管理、邮件系统、报表系统等。 - 系统内部的流程自动化能够实现任务的自动分配、状态跟踪、结果反馈等。 - 通常,OA系统会提供用户界面来与用户交互,如网页形式的管理界面。 2. 数据库文件的作用: - 数据库文件用于存储数据,是实现业务逻辑和数据管理的基础设施。 - 数据库通常具有数据的CRUD(创建、读取、更新、删除)功能,是信息检索和管理的核心组件。 - 数据库文件的结构和设计直接关系到系统的性能和可扩展性。 3. 数据库文件类型: - 根据数据库管理系统不同,数据库文件可以有不同格式。 - 例如,MySQL数据库的文件通常是“.frm”文件存储表结构,“.MYD”存储数据,“.MYI”存储索引。 - 对于SQLite,数据库就是一个单独的“.sqlite”文件。 4. 数据库设计和管理: - 数据库设计需要遵循一定的规范和最佳实践,如范式化以减少数据冗余。 - 管理数据库包括数据备份、恢复、性能调优、安全性管理等。 5. OA系统开发及源代码维护: - 开发一个OA系统涉及需求分析、系统设计、编码实现、测试、部署和维护等多个阶段。 - OA系统源代码的维护工作包括代码审查、重构、版本控制和更新等。 综上所述,我们可以知道“飞思OA源代码[数据库文件]”所提供的信息包括了对飞思OA系统及其数据库文件的基础介绍和可能的获取途径。对于开发者而言,需要了解如何管理和操作这些数据库文件以确保系统的正常运行和数据的完整性。此外,对于那些希望进行系统定制或二次开发的用户,源代码的可访问性是非常重要的,这可以允许用户根据自己的需求来修改和增强系统功能。
recommend-type

Qt信号与槽优化:提升系统性能与响应速度的实战技巧

# 摘要 本文系统地探讨了Qt框架中信号与槽机制的原理、高级特性、性能优化以及在大型项目中的应用。首先,概述了信号与槽的基本概念和工作原理,包括信号的发射、槽函数的绑定与调用过程。随后,本文深入分析了信号与槽的内部机制,如数据类型兼容性和连接类型,并探讨了优化信
recommend-type

D8流向算法

<think>我们被要求解释或实现D8流向算法。D8流向算法是一种用于水文分析的数字高程模型(DEM)处理方法,用于确定地表水流的方向。每个像元的水流方向被指定为指向周围8个相邻像元中坡度最陡的那个方向。 ### 算法原理 在D8算法中,每个像元的水流方向被定义为指向其8个相邻像元(包括对角线方向)中坡度最大的方向。坡度由高程差除以距离计算,其中相邻像元的距离为1(水平和垂直方向)或√2(对角线方向)。具体步骤如下: 1. 对于中心像元,计算其与8个相邻像元的高程差(中心像元高程减去相邻像元高程,得到正值表示下坡)。 2. 计算每个相邻方向的坡度:坡度 = 高程差 / 距离(水平/垂直方向
recommend-type

精选36个精美ICO图标免费打包下载

在当今的软件开发和应用程序设计中,图标作为图形用户界面(GUI)的一个重要组成部分,承担着向用户传达信息、增加美观性和提高用户体验的重要角色。图标不仅仅是一个应用程序或文件的象征,它还是品牌形象在数字世界中的延伸。因此,开发人员和设计师往往会对默认生成的图标感到不满意,从而寻找更加精美和个性化的图标资源。 【标题】中提到的“精美ICO图标打包下载”,指向用户提供的是一组精选的图标文件,这些文件格式为ICO。ICO文件是一种图标文件格式,主要被用于Windows操作系统中的各种文件和应用程序的图标。由于Windows系统的普及,ICO格式的图标在软件开发中有着广泛的应用。 【描述】中提到的“VB、VC编写应用的自带图标很难看,换这些试试”,提示我们这个ICO图标包是专门为使用Visual Basic(VB)和Visual C++(VC)编写的应用程序准备的。VB和VC是Microsoft公司推出的两款编程语言,其中VB是一种主要面向初学者的面向对象编程语言,而VC则是更加专业化的C++开发环境。在这些开发环境中,用户可以选择自定义应用程序的图标,以提升应用的视觉效果和用户体验。 【标签】中的“.ico 图标”直接告诉我们,这些打包的图标是ICO格式的。在设计ICO图标时,需要注意其独特的尺寸要求,因为ICO格式支持多种尺寸的图标,例如16x16、32x32、48x48、64x64、128x128等像素尺寸,甚至可以包含高DPI版本以适应不同显示需求。此外,ICO文件通常包含多种颜色深度的图标,以便在不同的背景下提供最佳的显示效果。 【压缩包子文件的文件名称列表】显示了这些精美ICO图标的数量,即“精美ICO图标36个打包”。这意味着该压缩包内包含36个不同的ICO图标资源。对于软件开发者和设计师来说,这意味着他们可以从这36个图标中挑选适合其应用程序或项目的图标,以替代默认的、可能看起来不太吸引人的图标。 在实际应用中,将这些图标应用到VB或VC编写的程序中,通常需要编辑程序的资源文件或使用相应的开发环境提供的工具进行图标更换。例如,在VB中,可以通过资源编辑器选择并替换程序的图标;而在VC中,则可能需要通过设置项目属性来更改图标。由于Windows系统支持在编译应用程序时将图标嵌入到可执行文件(EXE)中,因此一旦图标更换完成并重新编译程序,新图标就会在程序运行时显示出来。 此外,当谈及图标资源时,还应当了解图标制作的基本原则和技巧,例如:图标设计应简洁明了,以传达清晰的信息;色彩运用需考虑色彩搭配的美观性和辨识度;图标风格要与应用程序的整体设计风格保持一致,等等。这些原则和技巧在选择和设计图标时都非常重要。 总结来说,【标题】、【描述】、【标签】和【压缩包子文件的文件名称列表】共同勾勒出了一个为VB和VC编程语言用户准备的ICO图标资源包。开发者通过下载和使用这些图标,能够有效地提升应用程序的外观和用户体验。在这一过程中,了解和应用图标设计与应用的基本知识至关重要。
recommend-type

【Qt数据库融合指南】:MySQL与Qt无缝集成的技巧

# 摘要 本文全面探讨了Qt数据库集成的基础知识与进阶应用,从Qt与MySQL的基础操作讲起,深入到Qt数据库编程接口的配置与使用,并详细介绍了数据模型和视图的实现。随着章节的深入,内容逐渐从基础的数据操作界面构建过渡到高级数据库操作实践,涵盖了性能优化、安全性策略和事务管理。本文还特别针对移动设备上的数据库集成进行了讨
recommend-type

Looking in links: https://shi-labs.com/natten/wheels/ WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ ERROR: Ignored the following yanked versions: 0.14.1 ERROR: Could not find a version that satisfies the requirement natten==0.17.4+torch250cu121 (from versions: 0.14.2.post4, 0.14.4, 0.14.5, 0.14.6, 0.15.0, 0.15.1, 0.17.0, 0.17.1, 0.17.3, 0.17.4, 0.17.5, 0.20.0, 0.20.1) ERROR: No matching distribution found for natten==0.17.4+torch250cu121

<think>我们正在解决用户安装特定版本的natten包(0.17.4+torch250cu121)时遇到的ReadTimeoutError和版本未找到错误。 根据经验,这两个错误通常与网络问题和版本匹配问题有关。 步骤1: 分析问题 - ReadTimeoutError: 通常是由于网络连接不稳定或PyPI服务器响应慢导致下载超时。 - Version not found: 可能的原因包括: a) 指定的版本号在PyPI上不存在。 b) 指定的版本号与当前环境的Python版本或CUDA版本不兼容。 步骤2: 验证版本是否存在 我们可以通过访问PyP
recommend-type

精选教程分享:数据库系统基础学习资料

《世界著名计算机教材精选 数据库系统基础教程》这一标题揭示了该教材主要讨论的是数据库系统的基础知识。教材作为教学的重要工具,其内容往往涵盖某一领域的基本概念、原理、设计方法以及实现技术等。而该书被冠以“世界著名计算机教材精选”的标签,表明其可能源自世界范围内公认的、具有权威性的数据库系统教材,经过筛选汇编而成。 首先,从数据库系统的基础知识讲起,数据库系统的概念是在20世纪60年代随着计算机技术的发展而诞生的。数据库系统是一个集成化的数据集合,这些数据是由用户共享,且被组织成特定的数据模型以便进行高效的数据检索和管理。在数据库系统中,核心的概念包括数据模型、数据库设计、数据库查询语言、事务管理、并发控制和数据库系统的安全性等。 1. 数据模型:这是描述数据、数据关系、数据语义以及数据约束的概念工具,主要分为层次模型、网状模型、关系模型和面向对象模型等。其中,关系模型因其实现简单、易于理解和使用,已成为当前主流的数据模型。 2. 数据库设计:这是构建高效且能够满足用户需求的数据库系统的关键步骤,它包含需求分析、概念设计、逻辑设计和物理设计等阶段。设计过程中需考虑数据的完整性、一致性、冗余控制等问题,常用的工具有ER模型(实体-关系模型)和UML(统一建模语言)。 3. 数据库查询语言:SQL(Structured Query Language)作为标准的关系型数据库查询语言,在数据库系统中扮演着至关重要的角色。它允许用户对数据库进行查询、更新、插入和删除操作。SQL语言的熟练掌握是数据库系统学习者必须具备的能力。 4. 事务管理:在数据库系统中,事务是一系列的操作序列,必须作为一个整体执行,要么全部完成,要么全部不执行。事务管理涉及到数据库的可靠性、并发控制和恢复等关键功能,保证了数据的原子性、一致性、隔离性和持久性(ACID属性)。 5. 并发控制:由于多个用户可能同时对数据库进行操作,因此必须采取一定的并发控制机制以防止数据的不一致性,常用的技术包括封锁、时间戳、乐观控制等。 6. 数据库系统的安全性:安全性是保护数据库免受未授权访问和恶意攻击的措施,它包括身份验证、授权和审计等。 “数据库”这一标签说明了该教材专注于数据库领域,这个领域不仅限于理论知识,还包括了数据库的实际应用和解决方案的实现。教材内容可能涵盖数据库管理系统的使用和配置、数据库应用开发、数据库的维护和优化等。 教材的中文版形式表明它是为了方便中文读者而翻译或编写的,这使得中文世界的读者能够更加方便地学习和研究数据库系统的基础知识。同时,分享这一教材的行为,体现了知识传播的重要性以及人们对于知识共享的积极态度。 从给出的压缩包子文件的文件名称列表来看,“_世界著名计算机教材精选 数据库系统基础教程”显示了该压缩包中包含的文件内容。对于学习者来说,能够通过这样的压缩包文件获取到权威的数据库系统学习材料,无疑是一种宝贵的学习资源。
recommend-type

Qt架构揭秘:模块化设计与系统扩展性的最佳实践

# 摘要 本文全面探讨了Qt框架的应用开发,涵盖了其架构基础、模块化设计理论与实践、系统扩展性理论与实践、以及高级应用开发技巧。通过对Qt模块化设计和系统扩展机制的深入解析,本文展示了如何构建模块化和高扩展性的Qt应用,并通过案例分析的方式,呈现了这些理论在实际项目中的应用。此外,还讨论了Qt在跨平台开发中的应用、性能优化和高级GUI设计。最后,文章展望了Qt架构优化的未来趋势和新技术的融入,为Qt框架的开发者提供了理论支持和实践经验。 # 关键字
recommend-type

docker镜像加使

<think>嗯,用户询问如何修改或扩展Docker镜像的功能。根据系统指令,需要确保所有数学公式正确使用$...$或$$...$$格式,但这个问题主要涉及Docker技术,可能不太需要数学表达式。不过还是保持警惕,万一有版本号之类的数字处理呢。 用户提到"参考站内引用"部分有关于docker commit的信息,这提示修改镜像的核心方法之一就是通过容器提交。但用户深层需求应该是想了解系统化的镜像定制方法,不仅仅是单条命令。 考虑到Docker镜像修改的常见场景,需要涵盖几种主流方法:基于容器提交的方式确实是最直接的,适合快速修改;但更规范的应该是使用Dockerfile构建,这样能确保可