记录两种是stm32 mqtt实现方法
1.使用keil自带库MQTTPacket实现
安装install
安装完成后点击运行时环境按钮
选中MQTTpacket
代码实现添加和使用
首先包含头文件 #include "MQTTPacket.h"
static MQTTTransport mytransport; //定义MQTT全局传输结构体
static uint8_t mqtt_parse_buf[1024] = {0}; 临时解析数据数组大小自己看数据包大小
实现两个函数:
//4g发送数据 阻塞发送
void transport_sendPacketBuffer(unsigned char* buf, int buflen)
{
HAL_UART_Transmit(&huart_4g, buf, buflen, 5000);
HAL_UART_Receive_IT(&huart_4g, &temp_value_4g, 1);
}
//4g获取数据 没有数据返回0
int transport_getdatanb(void *sck, unsigned char* buf, int count)
{
if(mqtt_4f_buf_index >= count && HAL_GetTick() - mqtt_4g_tick >= 3) {
memcpy(buf, mqtt_4g_buf, count);
memcpy(mqtt_4g_buf, mqtt_4g_buf + count, mqtt_4f_buf_index - count); //复制到前面
mqtt_4f_buf_index = mqtt_4f_buf_index - count;
return count;
}
return 0;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == HUART_4G){
mqtt_4g_buf[mqtt_4f_buf_index++] = temp_value_4g;
mqtt_4f_buf_index = mqtt_4f_buf_index >= sizeof(mqtt_4g_buf) ? 0 : mqtt_4f_buf_index; //超出缓冲区
HAL_UART_Receive_IT(&huart_4g, &temp_value_4g, 1);
mqtt_4g_tick = HAL_GetTick();
}
}
发送采用阻塞,接收采用中断,发送完成后必须重新开始中断接收不然会出现不能接收的现象。
数据通信采用4G串口透传,其他方式以此类推都可以使用
先初始化4G模块或者其他模块或者socket
初始化连接到服务器
//MQTT init
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
int buflen = sizeof(mqtt_parse_buf);
mytransport.getfn = transport_getdatanb;
mytransport.state = 0;
//
data.keepAliveInterval = 180;
data.cleansession = 1;
data.clientID.cstring = CCID; //clientId唯一id
data.username.cstring = "admin";
data.password.cstring = "123456";
//连接
int len = MQTTSerialize_connect(mqtt_parse_buf, buflen, &data);
transport_sendPacketBuffer(mqtt_parse_buf, len);
//等待确认
while(1) {
int frc = MQTTPacket_readnb(mqtt_parse_buf, buflen, &mytransport);
if (frc == CONNACK){
unsigned char sessionPresent, connack_rc;
if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, mqtt_parse_buf, buflen) != 1 || connack_rc != 0){
printf("Unable to connect, return code %d\n", connack_rc);
Soft_Reset();
}
break;
}
else if (frc == -1) {
printf("mqtt login error\n");
}
}
printf("MQTT OK");
初始换完成后执行MQTT循环
void MQTT_4G_POLL(void)
{
static uint8_t no_reply_count = 0;
static uint32_t mqtt_poll_tick = 0;
static int len = 0, buflen = sizeof(mqtt_parse_buf);
int msgtype = MQTTPacket_readnb(mqtt_parse_buf, buflen, &mytransport);
switch(msgtype) {
case -1:
mqtt_4f_buf_index = 0;
mytransport.state = 0;
break;
case PUBLISH:
{
unsigned char dup;
int qos, len = 0;
unsigned char retained;
unsigned short msgid;
int payloadlen_in = 0;
unsigned char* payload_in;
MQTTString receivedTopic;
MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic, &payload_in, &payloadlen_in, mqtt_parse_buf, sizeof(mqtt_parse_buf));
mqtt_recive_publish(receivedTopic.lenstring.data, receivedTopic.lenstring.len, (char *)payload_in, payloadlen_in);
if(qos == 1) {
len = MQTTSerialize_ack(mqtt_parse_buf, sizeof(mqtt_parse_buf), PUBACK, dup, msgid);
transport_sendPacketBuffer(mqtt_parse_buf, len);
}
if(qos == 2) {
len = MQTTSerialize_ack(mqtt_parse_buf, sizeof(mqtt_parse_buf), PUBREC, dup, msgid);
transport_sendPacketBuffer(mqtt_parse_buf, len);
}
break;
}
case SUBACK:
{
unsigned short packet_id = 0;
int count = 0, req_qos = 0;
if(MQTTDeserialize_suback(&packet_id, 1, &count, &req_qos, mqtt_parse_buf, sizeof(mqtt_parse_buf)) == 1 && packet_id > 0) {
//printf("subsucess:%d\n", subscribe_devices[254]);
}
break;
}
case PINGRESP:
no_reply_count = 0; //收到心跳回复
break;
default:
break;
}
//50s 发送心跳
if(HAL_GetTick() - mqtt_poll_tick > 50000) {
if(no_reply_count > 3) {
Soft_Reset();
}
len = MQTTSerialize_pingreq(mqtt_parse_buf, buflen);
transport_sendPacketBuffer(mqtt_parse_buf, len);
no_reply_count++;
mqtt_poll_tick = HAL_GetTick(); //更新检测时间
}
}
发布和订阅函数实现
//发布消息
static void publish(char * topic, unsigned char * payload)
{
MQTTString topicString = MQTTString_initializer;
topicString.cstring = topic;
int len = MQTTSerialize_publish(mqtt_parse_buf, sizeof(mqtt_parse_buf), 0, 0, 0, 0, topicString, payload, strlen((char *)payload));
transport_sendPacketBuffer(mqtt_parse_buf, len);
}
//订阅
static void subscribe(char * subtopic_buf)
{
static uint16_t picket_id = 1;
MQTTString topicString = MQTTString_initializer;
int req_qos = 1, len = 0;
int buflen = sizeof(mqtt_parse_buf);
//订阅
topicString.cstring = subtopic_buf;
len = MQTTSerialize_subscribe(mqtt_parse_buf, buflen, 0, ++picket_id, 1, &topicString, &req_qos);
transport_sendPacketBuffer(mqtt_parse_buf, len);
}
2 LWIP MQTT
#define MEMP_NUM_SYS_TIMEOUT 8
MQTT连接失败,报错:CONNECTION_REFUSED_NOT_AUTHORIZED;
用户名和密码没有通过验证,新版本mqttconnect()中没有用户名和密码的代码,添加后就可以正常连接了。
err_t
mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg,
const struct mqtt_connect_client_info_t *client_info)
{
err_t err;
size_t len;
u16_t client_id_length;
/* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */
u16_t remaining_length = 2 + 4 + 1 + 1 + 2;
u8_t flags = 0, will_topic_len = 0, will_msg_len = 0;
LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL);
LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL);
if (client->conn_state != TCP_DISCONNECTED) {
LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Already connected\n"));
return ERR_ISCONN;
}
/* Wipe clean */
memset(client, 0, sizeof(mqtt_client_t));
client->connect_arg = arg;
client->connect_cb = cb;
client->keep_alive = client_info->keep_alive;
mqtt_init_requests(client->req_list);
/* Build connect message */
if (client_info->will_topic != NULL && client_info->will_msg != NULL) {
flags |= MQTT_CONNECT_FLAG_WILL;
flags |= (client_info->will_qos & 3) << 3;
if (client_info->will_retain) {
flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
}
len = strlen(client_info->will_topic);
LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL);
LWIP_ERROR("mqtt_client_connect: client_info->will_topic length must be > 0", len > 0, return ERR_VAL);
will_topic_len = (u8_t)len;
len = strlen(client_info->will_msg);
LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL);
will_msg_len = (u8_t)len;
len = remaining_length + 2 + will_topic_len + 2 + will_msg_len;
LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
remaining_length = (u16_t)len;
}
/* Don't complicate things, always connect using clean session */
flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
len = strlen(client_info->client_id);
LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL);
client_id_length = (u16_t)len;
len = remaining_length + 2 + client_id_length;
LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
remaining_length = (u16_t)len;
//添加 用户验证
if(client_info->client_user != NULL) {
flags |= MQTT_CONNECT_FLAG_USERNAME;
len = remaining_length + 2 + strlen(client_info->client_user);
}
remaining_length = (u16_t)len;
if(client_info->client_pass != NULL) {
flags |= MQTT_CONNECT_FLAG_PASSWORD;
len = remaining_length + 2 + strlen(client_info->client_pass);
}
remaining_length = (u16_t)len;
//
if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
return ERR_MEM;
}
client->conn = tcp_new();
if (client->conn == NULL) {
return ERR_MEM;
}
/* Set arg pointer for callbacks */
tcp_arg(client->conn, client);
/* Any local address, pick random local port number */
err = tcp_bind(client->conn, IP_ADDR_ANY, 0);
if (err != ERR_OK) {
LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Error binding to local ip/port, %d\n", err));
goto tcp_fail;
}
LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port));
/* Connect to server */
err = tcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
if (err != ERR_OK) {
LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err));
goto tcp_fail;
}
/* Set error callback */
tcp_err(client->conn, mqtt_tcp_err_cb);
client->conn_state = TCP_CONNECTING;
/* Append fixed header */
mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length);
/* Append Protocol string */
mqtt_output_append_string(&client->output, "MQTT", 4);
/* Append Protocol level */
mqtt_output_append_u8(&client->output, 4);
/* Append connect flags */
mqtt_output_append_u8(&client->output, flags);
/* Append keep-alive */
mqtt_output_append_u16(&client->output, client_info->keep_alive);
/* Append client id */
mqtt_output_append_string(&client->output, client_info->client_id, client_id_length);
/* Append will message if used */
if ((flags & MQTT_CONNECT_FLAG_WILL) != 0) {
mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len);
mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len);
}
//添加用户验证
if((flags & MQTT_CONNECT_FLAG_USERNAME) != 0) {
mqtt_output_append_string(&client->output, client_info->client_user, strlen(client_info->client_user));
}
if((flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
mqtt_output_append_string(&client->output, client_info->client_pass, strlen(client_info->client_pass));
}
return ERR_OK;
tcp_fail:
tcp_abort(client->conn);
client->conn = NULL;
return err;
}
连接发布接收函数
void socket_subscribe(char *topic)
{
static char subscribe_topic[255][32] = {0};
if(strlen(topic) <= 32) {
if(mqtt_client_is_connected(&client)) {
mqtt_subscribe(&client, topic, 1, NULL, NULL);
}
}
}
void socket_publish(char * topic, unsigned char * payload)
{
if(mqtt_client_is_connected(&client)) {
mqtt_publish(&client, topic, (char *)payload, strlen((char *)payload), 1, 0, NULL, NULL);
}
}
static const char *g_topic = NULL;
static u32_t g_topic_len = 0;
static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
{
if(topic != NULL) {
g_topic = topic;
g_topic_len = strlen(g_topic);
}
}
static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
{
if(flags & MQTT_DATA_FLAG_LAST) {
mqtt_recive_publish(g_topic, g_topic_len, (char *)data, len);
}
}
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
if(status == MQTT_CONNECT_ACCEPTED) {
mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);
printf("sucessed\n");
} else {
printf("dis sucessed\n");
mqtt_socket_connect(client);
}
}
static void mqtt_socket_connect(mqtt_client_t *client)
{
static struct mqtt_connect_client_info_t ci;
static char client_id[32];
memset(&ci, 0, sizeof(ci));
sprintf(client_id, "Network%04X%04X%04X", (*((uint32_t *)0x1FFF7A10 + 0)), (*((uint32_t *)0x1FFF7A10 + 1)), (*((uint32_t *)0x1FFF7A10 + 2)));
printf("%s\n", client_id);
ci.client_id = client_id;
ci.client_user = "admin";
ci.client_pass = "123456";
ci.keep_alive = 60;
ci.will_topic = NULL;
mqtt_client_connect(client, &mqtt_ip, 1883, mqtt_connection_cb, NULL, &ci);
}
SIM7600CE TCP流程
//硬件上一定要把给模块断电的功能加上!
//如果是一个单独电源芯片给模块供电,可以把电源芯片的使能脚引到MCU上,控制模块断电,如果这样不行,建议用MOS管做开关,由MCU控制MOS管,给模块断电。MOS管压降只有0.1V,而且MOS管能通过的电流大。需要重启时,正常情况下,使用POWERKEY关机开机来重启模块,但我们不排除在极端情况下,POWERKEY无效,这时RESET也可能无效,就需要给模块断电来解决。而反复断电可能导致模块文件系统损坏
//即:需要重启模块时,正常情况用POWERKEY,POWERKEY 无效再断电
//初始化流程!
//1.开机之后循环发送"AT",每500毫秒发一次,一般发两三个之后就能收到OK了,表示串口通了
//2.收到正确应答后,发AT+CPIN?,如果没收到正确应答,就循环发送,每500毫秒发一次,最多发20次
//3.当AT+CPIN?收到正确应答后,发AT+CSQ,如果没收到正确应答,就循环发送,每500毫秒发一次,最多发100次
//4.当AT+CSQ收到正确应答后(CSQ的值只要是非零就是正确应答),发AT+CGREG?,如果没收到正确应答,就循环发送,每500毫秒发一次,最多发100次
//5.当AT+CGREG?收到正确应答后(0,1和0,5都是正确应答),表示模块初始化及注册网络已成功,这时才可进行数据连接!
//注意!以上初始化流程必须执行,当模块处于网络不好的条件下,注册时间会延长,如果不执行,可能会在不知道模块是否已注册上网络的情况下开始数据连接,
//导致数据连接不成功,不成功后重启模块,又重连,导致反复重启死循环
//以上命令超时时间为500毫秒,如果发送AT命令500毫秒后仍得不到响应,视为超时。
//所有AT命令均可在AT手册中搜到,以查看具体用法
//以上初始化流程在所有对7100的应用中都必须加上
//以下命令回复的超时时间除了特别说明的,剩下全为500毫秒
//发送 AT,测试串口或USB口是否通
AT
OK
//查询SIM卡是否已准备就绪
AT+CPIN?
+CPIN: READY//必须判断READY与否
OK//不能判断OK
//查询信号强度
AT+CSQ
+CSQ: 17,99//必须判断前一个数字,来确定信号强度,比如这里是17(范围0-31,31表示信号最强),后一个数字不用判断
OK//不能判断OK
//查询网络是否附着上
AT+CGREG?
+CGREG: 0,1//必须判断0,1或0,5才是正确的
OK//不能判断OK,0,1/0,2/0,3/0,5等等都会回OK
//到此初始化检测通过,说明模块可以正常工作了
//查询版本号
AT+SIMCOMATI
Manufacturer: SIMCOM INCORPORATED
Model: SIMCOM_SIM7100C
Revision: 4534B03SIM7100C
SIM7100C_4534_150326_V1.00
QCN:
IMEI: 866154020007660
+GCAP: +CGSM
DeviceInfo: 200,170
OK
//查询网络设置
AT+CNMP?
+CNMP: 2
OK
//查询当前网络
AT+CPSI?
+CPSI: LTE,Online,460-00,0x1816,27551521,245,EUTRAN-BAND40,38950,5,5,-68,-1046,-778,19
OK
//CMNET是中国移动,3GNET是联通,电信是CTNET
AT+CGDCONT=1,"IP","CMNET"
OK
AT+CSOCKSETPN=1
OK
AT+CIPMODE=0
OK
AT+NETOPEN
OK//不能判断OK
+NETOPEN: 0//默认超时时间120秒,必须出现0,才说明NETOPEN成功,如果该数字不是0,说明打开失败
AT+IPADDR
+IPADDR: 10.21.110.228//只要有IP,就说明NETOPEN成功
OK
AT+CIPOPEN=0,"TCP","116.247.119.165",9336//非阻塞式
OK//不能判断OK
+CIPOPEN: 0,0//默认超时时间120秒,这条表示在通道0上,链接服务器成功。前一个数字表示通道号,后一个数字必须为0,才说明CIPOPEN成功,如果该数字不是0,说明CIPOPEN失败
AT+CIPSEND=0,5
>ABCDE
OK//不能判断OK
+CIPSEND: 0,5,5//默认超时时间120秒,这条表示在通道0上,要发5个,实际也发送了5个,需要判断收到这条,才表示发送成功
AT+CIPCLOSE=0//关闭通道0的SOCKET链接
OK
+CIPCLOSE: 0,0//默认超时时间120秒,表示模块和服务器的SOCKET链接关闭了
AT+NETCLOSE//释放模块IP地址
OK
+NETCLOSE: 0//默认超时时间120秒,表示模块将IP地址释放了