设备终端设置

在前文中我们提到, 设备终端跟腾讯云之间的通信采用的是MQTT协议, 而MQTT协议需要消息代理端和客户端相互配合, 因此, 我们在终端设备上, 需要实现一个MQTT Client. 腾讯云上支持的MQTT协议和标准协议区别不大, 可以在MQTT协议说明查看具体区别. 因此, 技术上来说, 设备终端只要实现了标准的MQTT客户端, 均可以跟腾讯云正常通信.

在本节中, 我们介绍如何使用腾讯IoT OS与腾讯云通过MQTT进行通信.

腾讯IoT OS集成了对MQTT和腾讯云的支持, 开发者只需要通过简单的设置即可实现与腾讯云的通信, 整体步骤主要包含:

  • 项目工程中添加MQTT组件相关文件
  • 适配AT HAL层串口及网络接口函数
  • 执行脚本生成MQTT配置头文件
  • 调用OS提供的接口完成和腾讯云的对接项目工程中添加MQTT组件相关文件

在工程中新建halatmqtt组, 并添加下列源文件,可以参考TencentOS_tiny\board\TencentOS_tiny_EVB_MX\KEIL\tencent_os_mqtt工程:

设备终端设置 - 图1

上述文件分别位于TencentOS_tiny\net\at, TencentOS_tiny\components\connectivity\Eclipse-Paho-MQTTTencentOS_tiny\platform\hal\st\stm32l4xx\src目录下.

相应的, 也需要引入对应的头文件

设备终端设置 - 图2

适配AT HAL层串口及网络接口函数

上层MQTT的操作最终都会转为为对底层通信设备的socket操作, OS集成了sal_module_wrapper层, 但是由于各个开发板硬件不同, 因此需要对sal_module_wrapper进行适配. 适配过程的核心数据结构为sal_module_t, 在TencentOS_tiny\net\sal_module_wrapper\sal_module_wrapper.h文件中定义:

  1. typedef struct sal_module_st{
  2. int (*init)(void);
  3. int (*get_local_mac)(char *mac);
  4. int (*get_local_ip)(char *ip, char *gw, char *mask);
  5. int (*parse_domain)(const char *host_name, char *host_ip, size_t host_ip_len);
  6. int (*connect)(const char *ip, const char *port, sal_proto_t proto);
  7. int (*send)(int sock, const void *buf, size_t len);
  8. int (*recv_timeout)(int sock, void *buf, size_t len, uint32_t timeout);
  9. int (*recv)(int sock, void *buf, size_t len);
  10. int (*sendto)(int sock, char *ip, char *port, const void *buf, size_t len);
  11. int (*recvfrom)(int sock, char *ip, char *port, void *buf, size_t len);
  12. int (*recvfrom_timeout)(int sock, char *ip, char *port, void *buf, size_t len, uint32_t timeout);
  13. int (*close)(int sock);
  14. }sal_module_t;

在使用的时候, 并需要全部实现, 只需实现下列几个核心的接口即可:

  1. sal_module_t sal_module_esp8266 = {
  2. .init = esp8266_init,
  3. .connect = esp8266_connect,
  4. .send = esp8266_send,
  5. .recv_timeout = esp8266_recv_timeout,
  6. .recv = esp8266_recv,
  7. .sendto = esp8266_sendto,
  8. .recvfrom = esp8266_recvfrom,
  9. .recvfrom_timeout = esp8266_recvfrom_timeout,
  10. .close = esp8266_close,
  11. .parse_domain = esp8266_parse_domain,
  12. };

这里接口对应的esp8266_xxx函数需要开发者根据硬件上具有的通信模组进行相应的适配.

该结构填充完后, 需要在使用MQTT之前, 调用一下初始化接口注册串口和网络API函数:

  1. esp8266_sal_init(hal_uart_port_t uart_port);

执行脚本生成MQTT配置头文件

从之前介绍腾讯云端配置的章节中可以知道, 在MQTT客户端, 所需要的核心信息只包含:

  • Product ID
  • Device Name
  • Password开发者在终端节点开发过程中, 可以使用TencentOS_tiny\tools下的mqtt_config_gen.py脚本来动态生成MQTT配置文件mqtt_config.h
  1. $ python mqtt_config_gen.py
  2. product id:QOW7EO9S31
  3. device name:dev1
  4. password:k05qMb3EXM5CE5ocNcsDvA==
  5. subscribe topic:[default:control]
  6. publish topic:[default:event]
  7. ===============Generate tos_mqtt_config.h==================
  8. #ifndef TOS_MQTT_CONFIG_H
  9. #define TOS_MQTT_CONFIG_H
  10.  
  11. #define MQTT_SERVER_IP "111.230.189.156"
  12. #define MQTT_SERVER_PORT "1883"
  13. #define MQTT_PRODUCT_ID "QOW7EO9S31"
  14. #define MQTT_DEV_NAME "dev1"
  15. #define MQTT_CLIENT_ID "QOW7EO9S31dev1"
  16. #define MQTT_USR_NAME "QOW7EO9S31dev1;21010406;12365;2147483648"
  17. #define MQTT_PASSWORD "49344dd251a98d07e33ba54d4d2a0640303c6e8c;hmacsha1"
  18. #define MQTT_SUBSCRIBE_TOPIC "QOW7EO9S31/dev1/control"
  19. #define MQTT_PUBLISH_TOPIC "QOW7EO9S31/dev1/event"
  20.  
  21. #endif

如上所示, 只需要按提示依次输入腾讯云端提供的Product ID, Device NamePassword即可, 至于订阅/发布的topic, 如果采用默认的control和event直接回车, 也可自行设置. 脚本会在脚本所在目录下生成一个tos_mqtt_config.h的头文件, 开发者将其放置到自己的工程目录下任意位置并在工程中添加头文件引用即可.

调用TencentOS tiny提供的接口完成和腾讯云的对接

以下是与腾讯云对接并收发信息的例程:

  1. void mqtt_demo(void)
  2. {
  3. int count = 1;
  4. int sock_id = 0;
  5. uint8_t read_data[40];
  6. int read_len;
  7. char topic[30];
  8. char buffer[32];
  9.  
  10. mqtt_con_param_t con_param;
  11. con_param.keep_alive_interval = 2000;
  12. con_param.cleansession = 1;
  13. con_param.username = MQTT_USR_NAME;
  14. con_param.password = MQTT_PASSWORD;
  15. con_param.client_id = MQTT_CLIENT_ID;
  16.  
  17. mqtt_pub_param_t pub_param;
  18. pub_param.dup = 0;
  19. pub_param.qos = 0;
  20. pub_param.retained = 0;
  21. pub_param.id = 0;
  22. pub_param.topic = MQTT_PUBLISH_TOPIC;
  23.  
  24. mqtt_sub_param_t sub_param;
  25. sub_param.count = 1;
  26. sub_param.dup = 0;
  27. sub_param.id = 1;
  28. sub_param.req_qos = 0;
  29. sub_param.topic = MQTT_SUBSCRIBE_TOPIC;
  30.  
  31. #ifdef USE_ESP8266
  32. esp8266_sal_init(HAL_UART_PORT_0);
  33. esp8266_join_ap("SheldonDai", "srnr6x9xbhmb0");
  34. #endif
  35.  
  36. #ifdef USE_NB_BC35
  37. int bc35_28_95_sal_init(hal_uart_port_t uart_port);
  38. bc35_28_95_sal_init(HAL_UART_PORT_0);
  39. #endif
  40.  
  41. sock_id = tos_mqtt_connect(MQTT_SERVER_IP, MQTT_SERVER_PORT, &con_param);
  42.  
  43. printf("socket id: %d\r\n",sock_id);
  44.  
  45. if (tos_mqtt_subscribe(sock_id, &sub_param) != 0) {
  46. printf("subscribe failed!!!\n");
  47. }
  48.  
  49. while (1) {
  50. memset(buffer, 0, sizeof(buffer));
  51.  
  52. snprintf(buffer, sizeof(buffer), "@%3.1f@%3.1f@%.1f", 25.8,80.6, 90.1);
  53.  
  54. printf("publish topic\r\n");
  55. pub_param.topic = MQTT_PUBLISH_TOPIC;
  56. pub_param.payload = (unsigned char *)buffer;
  57. pub_param.payload_len = sizeof(buffer);
  58. if (tos_mqtt_publish(sock_id, &pub_param) != 0) {
  59. printf("publish failed!!!\n");
  60. }
  61.  
  62. printf("MQTT count: %d\r\n",count);
  63. printf("read buf\r\n");
  64. read_len = tos_mqtt_receive(topic, sizeof(topic), read_data, sizeof(read_data));
  65. if (read_len >= 0) {
  66. printf("---------->topic: %s, payload: %s, payload_len: %d\n", topic, read_data, read_len);
  67. }
  68.  
  69. count++;
  70. osDelay(1000);
  71. }
  72. }

可以看到, 其中核心的接口为:

tos_mqtt_connect: 连接腾讯云

tos_mqtt_subscribe: 订阅Topic

tos_mqtt_receive: 读取订阅消息

tos_mqtt_publish: 发布消息

开发者可以根据自己的业务需求来调用这些接口完成特定的业务逻辑.