在GPRS模块(SIM800C)和STM32芯片上实现MQTT协议(HAL版)
自己又勤快起来,把使用SIM800C芯片连接MQTT服务器用HAL库实现一下。现在只是把路子感觉走通了,但还没有调试(手头没有硬件)。先把自己现在做到的东西记录一下。
费话不多说,立刻开始:
一、根据自己的开发板选定两个串口。
本例中,开发板使用的是STM32F103C8的开发板,使用了UART1和UART2两个串口,其中UART1用于与SIM800通信,UART2用于调试信息的输出调试信息。
具体的生成过程可以参考以下博客,我就不做重复工作了:
STM32CubeMX软件工程描述_USART配置过程 – strongerHuang – 博客园
二、移植MQTT
为了增强其适用性,所以选择从0开始移植MQTT。这是一个相对比较复杂的过程,下面就一一进行讲解。
2.1移植什么版本的MQTT
我选择的是Eclipse Paho – MQTT and MQTT-SN software,这里有一个问题非常关键,那就是尽量选择那种可靠性高与功能比较完整的版本进行移植。之前就遇到过移植了一个不是非常完善的版本,在一个MQTT服务器上能正常运行,到另外一个服务器上没有办法正常运行了,自己花了几天几夜来查找问题,好不容易找到问题又花了好几天修改了这个问题,所以自己移植的是现在最流行,使用最广泛的MQTT库。
2.2下载并将库文件导入项目
首先,将Eclipse Paho的嵌入式库下载下来,地址为:GitHub – eclipse/paho.mqtt.embedded-c
里面包括了非常多的内容。本文只实现最基本的功能,只使用MQTTPacket这个文件夹即可。这个文件夹中是MQTT协议中最基本的部分,MQTTPacket文件夹下面主要有三个文件夹,我们使用的文件主要集中在src文件夹和samples文件夹中。
- MQTTPacket – simple de/serialization of MQTT packets, plus helper functions
- The MQTTPacket directory contains the lowest level C library with the smallest requirements. This supplies simple serialization and deserialization routines. They serve as a base for the higher level libraries, but can also be used on their own It is mainly up to you to write and read to and from the network.
上面是一个说明,就是说这个文件夹是最基本的,我们将src文件夹中的内容全部拷到项目目录中,然后用一个文本编辑器打开samples文件夹中的pub0sub1.c文件,参考pub0sub1.c文件来改写之前项目的main.c文件。
2.3 在main函数中实现MQTT功能
我先帮大家分析一下pub0sub1.c中的main函数,然后我们仿照这个main函数改写CubeMx生成的项目的main函数就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
/******************************************************************************* * Copyright (c) 2014 IBM Corp. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Ian Craggs - initial API and implementation and/or initial documentation * Sergio R. Caprile - clarifications and/or documentation extension * Modified by Riley Ge(grlsr@163.com) * 增加了中文的注释与解释 *******************************************************************************/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include "MQTTPacket.h" #include "transport.h" /* This is in order to get an asynchronous signal to stop the sample, as the code loops waiting for msgs on the subscribed topic. Your actual code will depend on your hw and approach*/ //现在关于signal的没有用到,所以在自己使用时全部删除了 #include <signal.h> int toStop = 0; void cfinish(int sig) { signal(SIGINT, NULL); toStop = 1; } void stop_init(void) { signal(SIGINT, cfinish); signal(SIGTERM, cfinish); } /* */ int main(int argc, char *argv[]) { //先进行一些变量的注册,每一个变量都在后面有用到,具体使用时再解释 MQTTPacket_connectData data = MQTTPacket_connectData_initializer; int rc = 0; int mysock = 0; unsigned char buf[200]; int buflen = sizeof(buf); int msgid = 1; MQTTString topicString = MQTTString_initializer; int req_qos = 0; char* payload = "mypayload"; int payloadlen = strlen(payload); int len = 0; char *host = "m2m.eclipse.org"; int port = 1883; stop_init(); if (argc > 1) host = argv[1]; if (argc > 2) port = atoi(argv[2]); //如果使用SIM800,这个函数没什么用,可以把SIM800初始化的东西放到里面,但我没有这么做 mysock = transport_open(host, port); if(mysock < 0) return mysock; printf("Sending to hostname %s port %d\n", host, port); data.clientID.cstring = "me"; data.keepAliveInterval = 20; data.cleansession = 1; data.username.cstring = "testuser"; data.password.cstring = "testpassword"; //现在将连接字符串格式化一下,现在还没有发送 len = MQTTSerialize_connect(buf, buflen, &data); //现在是数据的发送 rc = transport_sendPacketBuffer(mysock, buf, len); /* wait for connack */ //发送后接收服务器返回的数据,这里使用了一个函数的指针,要定义这个指针 if (MQTTPacket_read(buf, buflen, transport_getdata) == CONNACK) { unsigned char sessionPresent, connack_rc; if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) != 1 || connack_rc != 0) { printf("Unable to connect, return code %d\n", connack_rc); goto exit; } } else goto exit; /* subscribe */ //订阅主题 topicString.cstring = "substopic"; len = MQTTSerialize_subscribe(buf, buflen, 0, msgid, 1, &topicString, &req_qos); rc = transport_sendPacketBuffer(mysock, buf, len); //等待服务器答复 if (MQTTPacket_read(buf, buflen, transport_getdata) == SUBACK) /* wait for suback */ { unsigned short submsgid; int subcount; int granted_qos; rc = MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, buf, buflen); if (granted_qos != 0) { printf("granted qos != 0, %d\n", granted_qos); goto exit; } } else goto exit; /* loop getting msgs on subscribed topic */ topicString.cstring = "pubtopic"; while (!toStop) { /* transport_getdata() has a built-in 1 second timeout, your mileage will vary */ //接收并处理消息 if (MQTTPacket_read(buf, buflen, transport_getdata) == PUBLISH) { unsigned char dup; int qos; unsigned char retained; unsigned short msgid; int payloadlen_in; unsigned char* payload_in; int rc; MQTTString receivedTopic; rc = MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic, &payload_in, &payloadlen_in, buf, buflen); printf("message arrived %.*s\n", payloadlen_in, payload_in); } printf("publishing reading\n"); len = MQTTSerialize_publish(buf, buflen, 0, 0, 0, 0, topicString, (unsigned char*)payload, payloadlen); rc = transport_sendPacketBuffer(mysock, buf, len); } printf("disconnecting\n"); len = MQTTSerialize_disconnect(buf, buflen); rc = transport_sendPacketBuffer(mysock, buf, len); exit: transport_close(mysock); return 0; } |
其实上面这段代码里主要有两个问题:
一是:我们解决之后就可以正常使用MQTT了,第一个是数据发送问题,上面的代码中使用的是 transport_sendPacketBuffer(mysock, buf, len);,我们完全可以使用HAL的库函数HAL_UART_Transmit来代替。
二是:transport_getdata函数,这个函数并不复杂,要自己重新写一个。
三、总结
这样MQTT就移植完了。如果有什么问题,大家可以问我,我也尽量帮大家解答。
原创文章,文章首发于:Riley Ge (@rileyge) — Steemit
原创文章,转载请注明: 转载自TsonTec:测量解决方案提供者
楼主有工程代码有吗
transport_getdata这个函数怎么重写能指点下么?
我有定制需求,请联系我 QQ: 22769835