(参考)端云互通组件无线接入实例

无线接入介绍

无线的接入方式包括WIFI、GSM、NB-IoT、Zigbee、蓝牙等,本文主要讲解WIFI和GSM(GPRS)的接入方式。对物联网开发者来说,WIFI或者GSM一般都是一个单独的模块,运行在MCU上的LiteOS SDK端云互通组件需要使用WIFI或者GSM提供的网络服务时,需要通过串口AT指令就可以了,如下图所示,ESP8266是乐鑫的WIFI模组,SIM900A是SIMCom芯讯通推出的GSM/GPRS模组。

图 1 Huawei LiteOS SDK端云互通组件无线接入方案示意图
(参考)端云互通组件无线接入实例 - 图1

AT 即Attention,AT指令集是从终端设备 (Terminal Equipment,TE)或者数据终端设备 (Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter, TA)或数据电路终端设备 (Data Circuit Terminal Equipment,DCE)发送的。通过TA,TE发送AT指令来控制移动台(Mobile Station,MS)的功能,与GSM 网络业务进行交互。用户可以通过AT指令进行呼叫、短信、电话本、数据业务、传真等方面的控制。

AT框架介绍

不论使用ESP8266还是SIM900A,都可以使用AT+UART方式接入,主要的差别在于具体的AT指令,但很多情况下都是类似的,LiteOS SDK端云互通组件提供了一种AT框架,也可以称之为AT模板,方便用户移植不同串口通信模块(需要支持TCP/IP协议栈),AT框架的方案如下图所示。

图 1 AT框架方案结构图
(参考)端云互通组件无线接入实例 - 图2

结构图中AT Socket用于适配Atiny Socket接口,类似posix socket,AT Send用于调用at_cmd发送AT命令,AT Recv用于AT Analyse Task,通过LiteOS消息队列Post消息到用户接收任务。AT Analyse Task的主要功能是解析来自串口的消息,包括用户数据和命令的响应,串口USART主要是在中断或者DMA模式下接收数据,AT API Register是提供设备模块注册的API函数。

结构图中深蓝色的部分是AT框架公共部分代码,开发者不需要修改;浅蓝色的部分是设备相关代码,开发者需要编写相应的设备代码,根据at_api.h文件的定义,开发者只要实现以下函数接口即可:

  1. typedef struct {
  2. int32_t (*init)(void); /*初始化,初始化串口、IP网络等*/
  3. int8_t (*get_localmac)(int8_t *mac);/*获取本地MAC*/
  4. int8_t (*get_localip)(int8_t *ip, int8_t * gw, int8_t * mask);/*获取本地IP*/
  5. /*建立TCP或者UDP连接*/
  6. int32_t (*connect)(const int8_t * host, const int8_t *port, int32_t proto);
  7. /*发送,当命令发送后,如果超过一定的时间没收到应答,要返回错误*/
  8. int32_t (*send)(int32_t id , const uint8_t *buf, uint32_t len);
  9. int32_t (*recv_timeout)(int32_t id , int8_t *buf, uint32_t len, int32_t timeout);
  10. int32_t (*recv)(int32_t id , int8_t *buf, uint32_t len);
  11. int32_t (*close)(int32_t id);/*关闭连接*/
  12. int32_t (*recv_cb)(int32_t id);/*收到各种事件处理,暂不实现 */
  13. int32_t (*deinit)(void);
  14. }at_adaptor_api;

at_api.h位于 LiteOS/include/at_frame。

移植WIFI模块-ESP8266

上一小节中,本文对AT框架进行了简单的介绍。其中需要开发者实现at_api_interface.h中所定义的接口,之后通过AT API Register进行注册,供上层的Agent Socket调用。本节给出WIFI模块ESP8266的具体例子,帮助开发者进行移植。

  1. STM32F429开发板上连接ESP8266 串口wifi模块,如下图所示:

    (参考)端云互通组件无线接入实例 - 图3

  2. 首先在设备文件esp8266.c定义API结构体。

    1. at_adaptor_api at_interface = {
    2. .init = esp8266_init,
    3. .get_localmac = esp8266_get_localmac, /*get local MAC*/
    4. .get_localip = esp8266_get_localip,/*get local IP*/
    5. /*build TCP or UDP connection*/
    6. .connect = esp8266_connect,
    7. .send = esp8266_send,
    8. .recv_timeout = esp8266_recv_timeout,
    9. .recv = esp8266_recv,
    10. .close = esp8266_close,/*close connection*/
    11. .recv_cb = esp8266_recv_cb,/* operation for events, not implements yet */
    12. .deinit = esp8266_deinit,
    13. };

    esp8266.c位于 LiteOS/components/net/at_device/wifi_esp8266

  3. 在main.c文件中,代码如下:

    1. #elif defined(WITH_AT_FRAMEWORK) && (defined(USE_ESP8266) || defined(USE_SIM900A))
    2. extern at_adaptor_api at_interface;
    3. at_api_register(&at_interface); //注册开发者定义的接口
    4. agent_tiny_entry();
    5. #endif

    main.c位于 LiteOS/targets/Cloud_STM32F429IGTx_FIRE/Src。

  4. 确保打开了编译宏。

    图 1 全局宏包含WITH_AT_FRAMEWORK和USE_ESP8266
    (参考)端云互通组件无线接入实例 - 图4

  5. 在esp8266.c实现具体设备API接口。

    例如demo例程初始化如下:

  1. int32_t esp8266_init()
  2. {
  3. at.init();
  4. at.oob_register(AT_DATAF_PREFIX, strlen(AT_DATAF_PREFIX), esp8266_data_handler);
  5. #ifdef USE_USARTRX_DMA HAL_UART_Receive_DMA(&at_usart,&at.recv_buf[at_user_conf.user_buf_len*0],at_user_conf.user_buf_len);
  6. #endif
  7. esp8266_reset();
  8. esp8266_echo_off();
  9. esp8266_choose_net_mode(STA);
  10. while(AT_FAILED == esp8266_joinap(WIFI_SSID, WIFI_PASSWD))
  11. {
  12. AT_LOG("connect ap failed, repeat...");
  13. };
  14. esp8266_set_mux_mode(at.mux_mode);
  15. static int8_t ip[32];
  16. static int8_t gw[32];
  17. static int8_t mac[32];
  18. esp8266_get_localip(ip, gw, NULL);
  19. esp8266_get_localmac(mac);
  20. AT_LOG("get ip:%s, gw:%s mac:%s", ip, gw, mac);
  21. return AT_OK;
  22. }
  1. 其它几个接口参考esp8266.c即可,而ESP8266模块AT命令定义的宏在esp8266.h,具体含义可以查看ESP8266官方手册,另外用户需要在esp8266.h中修改自己连接的wifissid和密码。
  2. ```
  3. #define AT_CMD_RST "AT+RST"
  4. #define AT_CMD_ECHO_OFF "ATE0"
  5. #define AT_CMD_CWMODE "AT+CWMODE_CUR"
  6. #define AT_CMD_JOINAP "AT+CWJAP_CUR"
  7. #define AT_CMD_MUX "AT+CIPMUX"
  8. #define AT_CMD_CONN "AT+CIPSTART"
  9. #define AT_CMD_SEND "AT+CIPSEND"
  10. #define AT_CMD_CLOSE "AT+CIPCLOSE"
  11. #define AT_CMD_CHECK_IP "AT+CIPSTA_CUR?"
  12. #define AT_CMD_CHECK_MAC "AT+CIPSTAMAC_CUR?"
  13. ```
  14. esp8266.h位于 LiteOS/components/net/at\_device/wifi\_esp8266

移植GSM模块-SIM900A

与ESP8266非常类似,只不过具体AT命令有稍微差异。

  1. STM32F429开发板上连接SIM900A串口GSM模块,如下图所示

    (参考)端云互通组件无线接入实例 - 图5

  2. 在设备文件sim900a.c定义API结构体。

    1. at_adaptor_api at_interface = {
    2. .init = sim900a_ini,
    3. /*TCP or UDP connect*/
    4. .connect = sim900a_connect,
    5. /*send data, if no response, retrun error*/
    6. .send = sim900a_send,
    7. .recv_timeout = sim900a_recv_timeout,
    8. .recv = sim900a_recv,
    9. .close = sim900a_close,/*close connect*/
    10. .recv_cb = sim900a_recv_cb,/*receive event handle, no available by now */
    11. .deinit = sim900a_deinit,
    12. };

    sim900a.c位于 LiteOS/components/net/at_device/gprs_sim900a。

  3. 在main.c文件中,代码如下:

    1. #elif defined(WITH_AT_FRAMEWORK) && (defined(USE_ESP8266) || defined(USE_SIM900A))
    2. extern at_adaptor_api at_interface;
    3. at_api_register(&at_interface);
    4. agent_tiny_entry();
    5. #endif
  4. 确保打开了编译宏

    图 1 全局宏包含WITH_AT_FRAMEWORK和USE_SIM900A
    (参考)端云互通组件无线接入实例 - 图6

  5. 在sim900a.c实现具体设备API接口。

    例如demo例程发送和接收函数如下:

    1. int32_t sim900a_recv_timeout(int32_t id, int8_t * buf, uint32_t len, int32_t timeout)
    2. {
    3. uint32_t qlen = sizeof(QUEUE_BUFF);
    4. QUEUE_BUFF qbuf = {0, NULL};
    5. printf("****at.linkid[id].qid=%d***\n",at.linkid[id].qid);
    6. int ret = LOS_QueueReadCopy(at.linkid[id].qid, &qbuf, &qlen, timeout);
    7. AT_LOG("ret = %x, len = %d, id = %d", ret, qbuf.len, id);
    8. if (qbuf.len){
    9. memcpy(buf, qbuf.addr, qbuf.len);
    10. atiny_free(qbuf.addr);
    11. }
    12. return qbuf.len;
    13. }
    14. int32_t sim900a_send(int32_t id , const uint8_t *buf, uint32_t len)
    15. {
    16. int32_t ret = -1;
    17. char cmd[64] = {0};
    18. if (AT_MUXMODE_SINGLE == at.mux_mode)
    19. {
    20. snprintf(cmd, 64, "%s=%d", AT_CMD_SEND, len);
    21. }
    22. else
    23. {
    24. snprintf(cmd, 64, "%s=%d,%d", AT_CMD_SEND, id, len);
    25. }
    26. ret = at.write((int8_t *)cmd, "SEND OK", (int8_t*)buf, len);
    27. return ret;
    28. }

    而SIM900A模块AT命令定义的宏在sim900a.h定义如下,具体含义可以查看SIM900A官方手册。

    1. #define AT_CMD_AT "AT"
    2. #define AT_CMD_CPIN "AT+CPIN?"//check sim card
    3. #define AT_CMD_COPS "AT+COPS?"//check register network
    4. #define AT_CMD_CLOSE "AT+CIPCLOSE"
    5. #define AT_CMD_SHUT "AT+CIPSHUT"
    6. #define AT_CMD_ECHO_OFF "ATE0"
    7. #define AT_CMD_ECHO_ON "ATE1"
    8. #define AT_CMD_MUX "AT+CIPMUX"
    9. #define AT_CMD_CLASS "AT+CGCLASS"//set MS type
    10. #define AT_CMD_PDP_CONT "AT+CGDCONT"//configure pdp context
    11. #define AT_CMD_PDP_ATT "AT+CGATT"//pdp attach network
    12. #define AT_CMD_PDP_ACT "AT+CGACT"//active pdp context
    13. #define AT_CMD_CSTT "AT+CSTT"//start task
    14. #define AT_CMD_CIICR "AT+CIICR"//start gprs connect
    15. #define AT_CMD_CIFSR "AT+CIFSR"//get local ip
    16. #define AT_CMD_CIPHEAD "AT+CIPHEAD"
    17. #define AT_CMD_CONN "AT+CIPSTART"
    18. #define AT_CMD_SEND "AT+CIPSEND"
    19. #define AT_CMD_CLOSE "AT+CIPCLOSE"

    sim900a.h位于 LiteOS/components/net/at_device/gprs_sim900a。

注意事项

由于LiteOS SDK端云互通组件的发送和接收在同一个任务中,接收消息的接口不能一直是阻塞的,而必须使用带有超时机制的接收接口,即我们总是实现int32_t (\*recv_timeout)(int32_t id , int8_t \*buf, uint32_t len, int32_t timeout)这个接口,且接收超时时间目前是10秒(#define BIND_TIMEOUT (10))。

如果用户设计的应用发送消息和接收消息在不同的任务中,那么可以使用阻塞接口int32_t (\*recv)(int32_t id , int8_t \*buf, uint32_t len)。