shuichan_old/V1.4/STM32F103_App1/HardWare/WIFI/mqtt_client.c

268 lines
7.7 KiB
C
Raw Normal View History

/* mqtt_client.c - 精简版 MQTT 3.1.1 客户端ESP-01S TCP 透传之上)
*
* CONNECT / SUBSCRIBE(QoS0) / PUBLISH(QoS0) / PINGREQ
* CONNACK / SUBACK / PINGRESP / PUBLISH
* PUBLISH payload(JSON) network.c net_dispatch_message()
*/
#include "mqtt_client.h"
#include "esp8266.h"
#include "air780e.h" /* U2_CopyBuff / bg_delay */
#include "network.h"
#include "log.h"
#include "main.h"
#include "string.h"
#define MQTT_TX_BUF_SIZE 512 /* 单发送缓冲CONNECT/SUB/PUBLISH 最大约 350B */
#define MQTT_RX_JSON_SIZE 384 /* 下行 PUBLISH payload 暂存 */
static uint8_t tx_buf[MQTT_TX_BUF_SIZE];
static char s_cid[24];
static char s_user[24];
static char s_pass[24];
static volatile uint8_t s_connack_ok = 0;
static volatile uint8_t s_suback_ok = 0;
volatile uint8_t g_mqttc_ping_outstanding = 0;
void mqttc_init(const char *client_id, const char *user, const char *pass)
{
strncpy(s_cid, client_id, sizeof(s_cid) - 1); s_cid[sizeof(s_cid)-1] = 0;
strncpy(s_user, user, sizeof(s_user) - 1); s_user[sizeof(s_user)-1] = 0;
strncpy(s_pass, pass, sizeof(s_pass) - 1); s_pass[sizeof(s_pass)-1] = 0;
s_connack_ok = 0;
s_suback_ok = 0;
g_mqttc_ping_outstanding = 0;
}
/*==== 剩余长度编码MQTT 变长编码,我们的报文 < 16KB最多 3 字节)====*/
static uint8_t encode_rl(uint8_t *out, uint32_t len)
{
uint8_t n = 0;
do {
uint8_t b = len % 128;
len /= 128;
if (len > 0) b |= 0x80;
out[n++] = b;
} while (len > 0);
return n;
}
/* 追加一个带 2 字节长度前缀的 UTF-8 字符串,返回新偏移 */
static uint16_t put_str(uint8_t *buf, uint16_t pos, const char *s)
{
uint16_t l = strlen(s);
buf[pos++] = (uint8_t)(l >> 8);
buf[pos++] = (uint8_t)(l & 0xFF);
memcpy(buf + pos, s, l);
return pos + l;
}
/*==== CONNECTclean session + username + password ====*/
int mqttc_connect(void)
{
uint16_t pos, var_pos, rl_pos;
uint8_t rl_len;
tx_buf[0] = 0x10;
var_pos = 1 + 3; /* 预留剩余长度(最多3字节),最后回填 */
/* 可变头: "MQTT" + 协议级别4 + 连接标志0xC2 + keepalive */
var_pos = put_str(tx_buf, var_pos, "MQTT");
tx_buf[var_pos++] = 0x04;
tx_buf[var_pos++] = 0xC2;
tx_buf[var_pos++] = (uint8_t)(MQTT_KEEPALIVE_S >> 8);
tx_buf[var_pos++] = (uint8_t)(MQTT_KEEPALIVE_S & 0xFF);
/* 载荷: ClientID / Username / Password */
var_pos = put_str(tx_buf, var_pos, s_cid);
var_pos = put_str(tx_buf, var_pos, s_user);
var_pos = put_str(tx_buf, var_pos, s_pass);
/* 回填剩余长度(把可变头整体后移对齐到编码长度) */
pos = var_pos - (1 + 3);
rl_len = encode_rl(tx_buf + 1, pos);
if (rl_len < 3) {
memmove(tx_buf + 1 + rl_len, tx_buf + 1 + 3, pos);
var_pos -= (3 - rl_len);
}
rl_pos = var_pos;
log_info("> WiFi MQTT: CONNECT cid=%s", s_cid);
esp_send(tx_buf, rl_pos);
/* 等 CONNACK最多 5s期间切片喂狗 */
s_connack_ok = 0;
for (int i = 0; i < 50; i++) {
bg_delay(100);
while (mqttc_process() == 1) { }
if (s_connack_ok) {
log_info("> WiFi MQTT: CONNACK ok");
return 0;
}
}
log_warn("> WiFi MQTT: CONNACK timeout");
return -1;
}
/*==== SUBSCRIBE QoS0包标识固定 1 ====*/
int mqttc_subscribe(const char *topic)
{
uint16_t pos = 0;
tx_buf[pos++] = 0x82;
/* 剩余长度 = 2(包标识) + 2 + topic + 1(QoS) */
pos += encode_rl(tx_buf + pos, (uint32_t)(2 + 2 + strlen(topic) + 1));
tx_buf[pos++] = 0x00;
tx_buf[pos++] = 0x01;
pos = put_str(tx_buf, pos, topic);
tx_buf[pos++] = 0x00;
log_info("> WiFi MQTT: SUB %s", topic);
esp_send(tx_buf, pos);
s_suback_ok = 0;
for (int i = 0; i < 50; i++) {
bg_delay(100);
while (mqttc_process() == 1) { }
if (s_suback_ok) {
log_info("> WiFi MQTT: SUBACK ok");
return 0;
}
}
log_warn("> WiFi MQTT: SUBACK timeout");
return -1;
}
/*==== PUBLISH QoS0 ====*/
int mqttc_publish(const char *topic, const char *payload)
{
uint16_t pos = 0;
uint32_t rl = 2 + strlen(topic) + strlen(payload);
if (rl + 5 > MQTT_TX_BUF_SIZE) {
log_warn("> WiFi MQTT: publish too long (%u)", (unsigned)rl);
return -1;
}
tx_buf[pos++] = 0x30;
pos += encode_rl(tx_buf + pos, rl);
pos = put_str(tx_buf, pos, topic);
memcpy(tx_buf + pos, payload, strlen(payload));
pos += strlen(payload);
esp_send(tx_buf, pos);
return 0;
}
void mqttc_ping(void)
{
tx_buf[0] = 0xC0;
tx_buf[1] = 0x00;
esp_send(tx_buf, 2);
g_mqttc_ping_outstanding = 1;
}
/*==== 接收解析 ====*/
/* 二进制安全的子串查找(透传数据里可能含 0x00如 PINGRESP 0xD0 0x00 */
static uint8_t *memfind(uint8_t *hay, uint16_t hay_len, const char *needle)
{
uint16_t nlen = strlen(needle);
if (hay_len < nlen) return NULL;
for (uint16_t i = 0; i <= hay_len - nlen; i++) {
if (memcmp(hay + i, needle, nlen) == 0) return hay + i;
}
return NULL;
}
/* 消费缓冲前 n 字节 */
static void consume(uint16_t n)
{
if (n >= U2_CopyIndex) {
U2_CopyIndex = 0;
} else {
memmove(U2_CopyBuff, U2_CopyBuff + n, U2_CopyIndex - n);
U2_CopyIndex -= n;
}
U2_CopyBuff[U2_CopyIndex] = '\0';
}
int mqttc_process(void)
{
uint8_t *buf = U2_CopyBuff;
uint16_t len = U2_CopyIndex;
if (len == 0) {
U2_CopyFlag = 0;
return 0;
}
/* 断链检测:透传模式下 TCP 断开 ESP 会输出 "CLOSED" */
if (memfind(buf, len, "CLOSED")) {
consume(U2_CopyIndex);
U2_CopyFlag = 0;
return -1;
}
/* 帧同步:首字节高 4 位必须是合法报文类型(1~14),否则逐字节丢弃 */
uint8_t type = buf[0] >> 4;
if (type < 1 || type > 14) {
consume(1);
return 1;
}
/* 解码剩余长度(最多 4 字节) */
uint32_t rl = 0, mult = 1;
uint8_t rl_bytes = 0;
for (uint8_t i = 1; i <= 4; i++) {
if (i >= len) return 0; /* 头还没收全,等数据 */
rl += (uint32_t)(buf[i] & 0x7F) * mult;
rl_bytes = i;
if (!(buf[i] & 0x80)) break;
mult *= 128;
}
if (rl_bytes == 0) return 0;
uint32_t total = 1 + rl_bytes + rl;
if (total > U2_COPY_SIZE - 1) {
/* 异常巨型报文,丢弃首字节重新同步 */
consume(1);
return 1;
}
if (len < total) return 0; /* 报文没收全,等数据 */
uint8_t *pkt = buf + 1 + rl_bytes;
switch (type) {
case 2: /* CONNACK: 0x20 0x02 0x00 rc */
if (rl >= 2 && pkt[1] == 0) s_connack_ok = 1;
else log_warn("> WiFi MQTT: CONNACK refused rc=%u", rl >= 2 ? pkt[1] : 0xFF);
break;
case 9: /* SUBACK: 0x90 0x03 pktIdHi pktIdLo rcrc=0x80 表示订阅被拒绝 */
if (rl >= 3 && pkt[2] != 0x80) s_suback_ok = 1;
else log_warn("> WiFi MQTT: SUBACK rejected");
break;
case 13: /* PINGRESP */
g_mqttc_ping_outstanding = 0;
break;
case 3: { /* PUBLISH QoS0取 payload(JSON) 交网络层分发 */
static char json[MQTT_RX_JSON_SIZE];
uint16_t tlen = ((uint16_t)pkt[0] << 8) | pkt[1];
if (2 + tlen <= rl) {
uint32_t plen = rl - 2 - tlen;
if (plen >= sizeof(json)) plen = sizeof(json) - 1;
memcpy(json, pkt + 2 + tlen, plen);
json[plen] = '\0';
log_info("> WiFi MQTT rx: %.200s", json);
net_dispatch_message(json);
}
break;
}
default: /* PUBACK 等其他报文,忽略 */
break;
}
consume((uint16_t)total);
return 1;
}