(参考)端云互通组件无线接入实例
无线接入介绍
无线的接入方式包括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端云互通组件无线接入方案示意图
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框架方案结构图
结构图中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文件的定义,开发者只要实现以下函数接口即可:
typedef struct {
int32_t (*init)(void); /*初始化,初始化串口、IP网络等*/
int8_t (*get_localmac)(int8_t *mac);/*获取本地MAC*/
int8_t (*get_localip)(int8_t *ip, int8_t * gw, int8_t * mask);/*获取本地IP*/
/*建立TCP或者UDP连接*/
int32_t (*connect)(const int8_t * host, const int8_t *port, int32_t proto);
/*发送,当命令发送后,如果超过一定的时间没收到应答,要返回错误*/
int32_t (*send)(int32_t id , const uint8_t *buf, uint32_t len);
int32_t (*recv_timeout)(int32_t id , int8_t *buf, uint32_t len, int32_t timeout);
int32_t (*recv)(int32_t id , int8_t *buf, uint32_t len);
int32_t (*close)(int32_t id);/*关闭连接*/
int32_t (*recv_cb)(int32_t id);/*收到各种事件处理,暂不实现 */
int32_t (*deinit)(void);
}at_adaptor_api;
at_api.h位于 LiteOS/include/at_frame。
移植WIFI模块-ESP8266
上一小节中,本文对AT框架进行了简单的介绍。其中需要开发者实现at_api_interface.h中所定义的接口,之后通过AT API Register进行注册,供上层的Agent Socket调用。本节给出WIFI模块ESP8266的具体例子,帮助开发者进行移植。
STM32F429开发板上连接ESP8266 串口wifi模块,如下图所示:
首先在设备文件esp8266.c定义API结构体。
at_adaptor_api at_interface = {
.init = esp8266_init,
.get_localmac = esp8266_get_localmac, /*get local MAC*/
.get_localip = esp8266_get_localip,/*get local IP*/
/*build TCP or UDP connection*/
.connect = esp8266_connect,
.send = esp8266_send,
.recv_timeout = esp8266_recv_timeout,
.recv = esp8266_recv,
.close = esp8266_close,/*close connection*/
.recv_cb = esp8266_recv_cb,/* operation for events, not implements yet */
.deinit = esp8266_deinit,
};
esp8266.c位于 LiteOS/components/net/at_device/wifi_esp8266
在main.c文件中,代码如下:
#elif defined(WITH_AT_FRAMEWORK) && (defined(USE_ESP8266) || defined(USE_SIM900A))
extern at_adaptor_api at_interface;
at_api_register(&at_interface); //注册开发者定义的接口
agent_tiny_entry();
#endif
main.c位于 LiteOS/targets/Cloud_STM32F429IGTx_FIRE/Src。
确保打开了编译宏。
图 1 全局宏包含WITH_AT_FRAMEWORK和USE_ESP8266
在esp8266.c实现具体设备API接口。
例如demo例程初始化如下:
int32_t esp8266_init()
{
at.init();
at.oob_register(AT_DATAF_PREFIX, strlen(AT_DATAF_PREFIX), esp8266_data_handler);
#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);
#endif
esp8266_reset();
esp8266_echo_off();
esp8266_choose_net_mode(STA);
while(AT_FAILED == esp8266_joinap(WIFI_SSID, WIFI_PASSWD))
{
AT_LOG("connect ap failed, repeat...");
};
esp8266_set_mux_mode(at.mux_mode);
static int8_t ip[32];
static int8_t gw[32];
static int8_t mac[32];
esp8266_get_localip(ip, gw, NULL);
esp8266_get_localmac(mac);
AT_LOG("get ip:%s, gw:%s mac:%s", ip, gw, mac);
return AT_OK;
}
其它几个接口参考esp8266.c即可,而ESP8266模块AT命令定义的宏在esp8266.h,具体含义可以查看ESP8266官方手册,另外用户需要在esp8266.h中修改自己连接的wifi的ssid和密码。
```
#define AT_CMD_RST "AT+RST"
#define AT_CMD_ECHO_OFF "ATE0"
#define AT_CMD_CWMODE "AT+CWMODE_CUR"
#define AT_CMD_JOINAP "AT+CWJAP_CUR"
#define AT_CMD_MUX "AT+CIPMUX"
#define AT_CMD_CONN "AT+CIPSTART"
#define AT_CMD_SEND "AT+CIPSEND"
#define AT_CMD_CLOSE "AT+CIPCLOSE"
#define AT_CMD_CHECK_IP "AT+CIPSTA_CUR?"
#define AT_CMD_CHECK_MAC "AT+CIPSTAMAC_CUR?"
```
esp8266.h位于 LiteOS/components/net/at\_device/wifi\_esp8266。
移植GSM模块-SIM900A
与ESP8266非常类似,只不过具体AT命令有稍微差异。
STM32F429开发板上连接SIM900A串口GSM模块,如下图所示
在设备文件sim900a.c定义API结构体。
at_adaptor_api at_interface = {
.init = sim900a_ini,
/*TCP or UDP connect*/
.connect = sim900a_connect,
/*send data, if no response, retrun error*/
.send = sim900a_send,
.recv_timeout = sim900a_recv_timeout,
.recv = sim900a_recv,
.close = sim900a_close,/*close connect*/
.recv_cb = sim900a_recv_cb,/*receive event handle, no available by now */
.deinit = sim900a_deinit,
};
sim900a.c位于 LiteOS/components/net/at_device/gprs_sim900a。
在main.c文件中,代码如下:
#elif defined(WITH_AT_FRAMEWORK) && (defined(USE_ESP8266) || defined(USE_SIM900A))
extern at_adaptor_api at_interface;
at_api_register(&at_interface);
agent_tiny_entry();
#endif
确保打开了编译宏
图 1 全局宏包含WITH_AT_FRAMEWORK和USE_SIM900A
在sim900a.c实现具体设备API接口。
例如demo例程发送和接收函数如下:
int32_t sim900a_recv_timeout(int32_t id, int8_t * buf, uint32_t len, int32_t timeout)
{
uint32_t qlen = sizeof(QUEUE_BUFF);
QUEUE_BUFF qbuf = {0, NULL};
printf("****at.linkid[id].qid=%d***\n",at.linkid[id].qid);
int ret = LOS_QueueReadCopy(at.linkid[id].qid, &qbuf, &qlen, timeout);
AT_LOG("ret = %x, len = %d, id = %d", ret, qbuf.len, id);
if (qbuf.len){
memcpy(buf, qbuf.addr, qbuf.len);
atiny_free(qbuf.addr);
}
return qbuf.len;
}
int32_t sim900a_send(int32_t id , const uint8_t *buf, uint32_t len)
{
int32_t ret = -1;
char cmd[64] = {0};
if (AT_MUXMODE_SINGLE == at.mux_mode)
{
snprintf(cmd, 64, "%s=%d", AT_CMD_SEND, len);
}
else
{
snprintf(cmd, 64, "%s=%d,%d", AT_CMD_SEND, id, len);
}
ret = at.write((int8_t *)cmd, "SEND OK", (int8_t*)buf, len);
return ret;
}
而SIM900A模块AT命令定义的宏在sim900a.h定义如下,具体含义可以查看SIM900A官方手册。
#define AT_CMD_AT "AT"
#define AT_CMD_CPIN "AT+CPIN?"//check sim card
#define AT_CMD_COPS "AT+COPS?"//check register network
#define AT_CMD_CLOSE "AT+CIPCLOSE"
#define AT_CMD_SHUT "AT+CIPSHUT"
#define AT_CMD_ECHO_OFF "ATE0"
#define AT_CMD_ECHO_ON "ATE1"
#define AT_CMD_MUX "AT+CIPMUX"
#define AT_CMD_CLASS "AT+CGCLASS"//set MS type
#define AT_CMD_PDP_CONT "AT+CGDCONT"//configure pdp context
#define AT_CMD_PDP_ATT "AT+CGATT"//pdp attach network
#define AT_CMD_PDP_ACT "AT+CGACT"//active pdp context
#define AT_CMD_CSTT "AT+CSTT"//start task
#define AT_CMD_CIICR "AT+CIICR"//start gprs connect
#define AT_CMD_CIFSR "AT+CIFSR"//get local ip
#define AT_CMD_CIPHEAD "AT+CIPHEAD"
#define AT_CMD_CONN "AT+CIPSTART"
#define AT_CMD_SEND "AT+CIPSEND"
#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)。