08_Z-Stack中使用ESP8266
前言
本章讲解Zigbee协调器使用ESP8266模块,与上位机实现TCP/IP通信。将介绍Mcu串口配置、AT指令集,并提供一份基于AT指令集的ESP驱动代码。ESP相关代码在AT.c
和AT.h
文件中,将其添加到App
组下。
Mcu串口配置
在之前的章节中,讲解了CC2530的UART0配置,借助于Z-Stack提供的HAL接口,能很方便的将其移植到Z-Stack的框架上。但由于Z-Stack的一些缺陷,其默认是不支持同时使用两个串口的,因此对于与ESP通信的UART1,我们采用操作寄存器的方式进行配置和使用
- CC2530 UART1配置
/**
* @brief 初始化与ESP通信所用的MCU串口 UART1 Rx: P0_5 Tx: P0_4
*/
void AT_McuUart_Init(void)
{// 端口配置PERCFG = 0x00; // 外设采用默认位置P2DIR |= 0x40; // 当端口0引脚冲突时,优先使用UART1P2DIR &= 0x7F;P0SEL |= 0x30; // P0端口 4、5引脚设置为外设功能// UART 配置U1CSR |= 0x80; // 选择UART模式U1UCR |= 0x02; // 8bit 无校验 1bit停止位// 波特率设置 Baud = (256 + BAUD_M)*2^(BAUD_E) / 2^28 * F F为系统时钟频率 32MHz// 若波特兰要设置成115200 则BAUD_M = 194 BAUD_E = 11U1BAUD |= 0xC2; // 194U1GCR |= 0x0B; // 11// 配置接收中断URX1IF = 0; // 清除接收中断标志URX1IE = 1; // 使能接受中断EA = 1; // 开启全局中断U1CSR |= 0x40; // 接收器使能 需要先配置完USART所有参数
}
- 定义接收缓存区和接收状态
unsigned char AT_RxBuf; // 接收缓存
unsigned char AT_RxData[AT_UART_BUF_LEN]; // 数据保存
unsigned char AT_RxState = 0; // 接收状态 0
- 中断函数接收消息
/**
* @brief AT UART1 接收中断函数
*/
#pragma vector = URX1_VECTOR
__interrupt void USAT1_ISR(void)
{static unsigned int idx = 0;if(U1CSR&0x04 != 0) // 接收到数据{if(AT_RxState == 1){memset(&AT_RxData, 0, sizeof(AT_RxData)); // 清空缓存AT_RxState = 0;}AT_RxBuf = U1DBUF; // 读取数据if(idx < AT_UART_BUF_LEN && AT_RxState==0) // 接收数据{AT_RxData[idx++] = AT_RxBuf;if(AT_RxBuf == '\n'){AT_RxState = 1; // 接收完成idx = 0;}}else // 数据超过限制{AT_RxState = 1; // 接收完成idx = 0;} }URX1IF = 0; // 清除中断标志
}
- 重写
putchar()
函数
/**
* @brief 重写 putchar函数 UART0
*/
__near_func int putchar(int c)
{UTX1IF = 0; // 清除发送标志位U1DBUF = (char)c;while(UTX1IF == 0); // 等待发送成功 发送成功时 发送标志位复位return(c);
}
AT指令集
AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的指令集。
其指令结构为: 每条AT指令都以AT
作为开头,并以特定的字符或字符串结尾。指令的响应数据包中包含执行结果,如果指令执行成功,通常会返回OK
字符串;如果执行失败,则返回ERROR
字符串或其他错误代码。
ESP8266就能通过AT指令集进行配置与操作,而实际上官方提供了一套SDK接口,但由于我们的需求比较简单,因此直接使用AT指令集,从而避免繁多的文档查找和学习
下面以TCP单连接为例:
- 配置WIFI模式:
AT+CWMODE=3
- 连接路由器
AT+CWJAP="SSID","password"
- 查询ESP8266设备的IP地址
AT+CIFSR
- 用网络调试助手创建一个TCP服务器,假设创建服务器地址为"192.168.1.101": 8080
AT+CIPSTART="TCP","192.168.1.101",8080
- 发送数据
AT+CIPSEND=4 // 设置数据长度
test // 发送的数据
ESP驱动代码
根据AT指令集,实现ESP的TCP单连接的驱动代码
AT.h
AT.h
中,可配置需要连接的路由器和主机IP/端口。也可配置ESP在SoftAP模式下的名称和密码
#ifndef __AT_H
#define __AT_H
#include "hal_types.h"
/*==============MCU Parameters============*/
#define AT_UART_BUF_LEN 128
#define AT_UART_BR HAL_UART_BR_115200
#define AT_UART_PORT HAL_UART_PORT_1
/*==============ESP Parameters===============*/
#define SSID "\"Nanxl\"" // 路由器WIFI名称
#define PASSWORD "\"1805110506@qq.com\"" // 路由器WIFI密码
#define DSTADDR "\"192.168.1.101\"" // 目标主机IP
#define DSTPORT "8080" // 目标主机端口
#define DEVICE_ID "\"ESP-NANXL\"" // ESP WIFI名称
#define DEVICE_PASS "\"ESP-2025\"" // ESP WIFI密码
#define AT_UART_BR_NUM "115200"
#define AT_UART_DATA_BIT "8"
#define AT_UART_STOP_BIT "1"
#define AT_UART_PARITY "0"
#define AT_UART_FLOW_CTR "1"
#define AT_MAX_WAIT_TIME 32000
/*==============AT Command===============*/
/*==============Basic Command=========*/
#define AT "AT" // 测试指令
#define AT_RESET "AT+RST" // 重启
#define AT_CLOSE_ECHO "ATE0" // 关闭回显
#define AT_OPEN_ECHO "ATE1" // 打开回显
#define AT_RESTORE "AT+RESTORE" // 恢复出厂设置
#define AT_VERSION_INFO "AT+GMR" // 查询版本信息
#define AT_GET_MODE "AT+CWMODE?" // 查询ESP8266工作模式
#define AT_SET_DVEAP_INFO "AT+CWSAP=" DEVICE_ID "," DEVICE_PASS ",5,3" // 配置ESP SOFT AP参数
#define AT_SET_DEV_UART "AT+UART=" AT_UART_BR_NUM ","\AT_UART_DATA_BIT "," \AT_UART_STOP_BIT "," \AT_UART_PARITY "," \AT_UART_FLOW_CTR/*==============TCP/IP================*/
#define AT_SET_APS_MODE "AT+CWMODE=3" // 配置WIFI模式(softAP+station mode) 并保存到FLASH
#define AT_CONNECT_ROUTER "AT+CWJAP=" SSID "," PASSWORD // 连接路由器
#define AT_ACK_IP "AT+CIFSR" // 获取ESP8266 设备的IP地址
#define AT_CONNECT_SERVER "AT+CIPSTART=" "\"TCP\"" "," DSTADDR "," DSTPORT // 连接服务器
#define AT_GET_AP_INFO "AT+CWJAP?" // 查询已接入的AP
#define AT_SEND_DATA "AT+CIPSEND="
/*===============functions=============*/
extern void AT_ESP_Init(void);
extern bool AT_Test(void);
extern bool AT_Send_Data(char * data);
extern void AT_McuUart_Init(void);
static void AT_UartCB(uint8 port, uint8 event);
static void AT_Dev_Uart_Config(void);
static void AT_SendCmd(char * cmd);
static void AT_Connect_TCP_Server(void);
static void AT_Basic_Config(void);
static bool AT_Wait_Cmd(uint16 time);
static bool AT_Wait_SendData(void);
static void AT_Delay_Ms(uint16 time);
/*===============variable==============*/
extern unsigned char AT_RxBuf; // 接收缓存
extern unsigned char AT_RxData[AT_UART_BUF_LEN]; // 数据保存
extern unsigned char AT_RxState; // 接收状态 0
#endif
AT.c
#include "ioCC2530.h"
#include "AT.h"
#include "hal_uart.h"
#include "OnBoard.h"
#include <stdio.h>
#include <string.h>
unsigned char AT_RxBuf; // 接收缓存
unsigned char AT_RxData[AT_UART_BUF_LEN]; // 数据保存
unsigned char AT_RxState = 0; // 接收状态 0
/**
* @brief 重写 putchar函数 UART0
*/
__near_func int putchar(int c)
{UTX1IF = 0; // 清除发送标志位U1DBUF = (char)c;while(UTX1IF == 0); // 等待发送成功 发送成功时 发送标志位复位return(c);
}/**
* @brief 初始化ESP8266
*/
void AT_ESP_Init(void)
{// ESP Basic ConfigAT_Basic_Config();// ESP Uart ConfigAT_Dev_Uart_Config();// Connect to TCP ServerAT_Connect_TCP_Server();
}/**
* @brief 测试ESP能否正常工作
*/
bool AT_Test(void)
{AT_SendCmd(AT_CLOSE_ECHO); // 关闭回显AT_Wait_Cmd(2000);AT_SendCmd(AT); // 测试指令return AT_Wait_Cmd(2000);
}/**
* @brief 发送数据
*/
bool AT_Send_Data(char * data)
{char cmd[AT_UART_BUF_LEN];sprintf(cmd, "%s%c", AT_SEND_DATA, strlen(data)+'0');AT_SendCmd(cmd);AT_Wait_Cmd(5);AT_SendCmd(data);AT_Wait_Cmd(5);return AT_Wait_SendData(); // 等待发送数据完成
}
/**
* @brief 初始化与ESP通信所用的MCU串口 UART1 Rx: P0_5 Tx: P0_4
*/
void AT_McuUart_Init(void)
{// 端口配置PERCFG = 0x00; // 外设采用默认位置P2DIR |= 0x40; // 当端口0引脚冲突时,优先使用UART1P2DIR &= 0x7F;P0SEL |= 0x30; // P0端口 4、5引脚设置为外设功能// UART 配置U1CSR |= 0x80; // 选择UART模式U1UCR |= 0x02; // 8bit 无校验 1bit停止位// 波特率设置 Baud = (256 + BAUD_M)*2^(BAUD_E) / 2^28 * F F为系统时钟频率 32MHz// 若波特兰要设置成115200 则BAUD_M = 194 BAUD_E = 11U1BAUD |= 0xC2; // 194U1GCR |= 0x0B; // 11// 配置接收中断URX1IF = 0; // 清除接收中断标志URX1IE = 1; // 使能接受中断EA = 1; // 开启全局中断U1CSR |= 0x40; // 接收器使能 需要先配置完USART所有参数
}/**
* @brief AT UART1 接收中断函数
*/
#pragma vector = URX1_VECTOR
__interrupt void USAT1_ISR(void)
{static unsigned int idx = 0;if(U1CSR&0x04 != 0) // 接收到数据{if(AT_RxState == 1){memset(&AT_RxData, 0, sizeof(AT_RxData)); // 清空缓存AT_RxState = 0;}AT_RxBuf = U1DBUF; // 读取数据if(idx < AT_UART_BUF_LEN && AT_RxState==0) // 接收数据{AT_RxData[idx++] = AT_RxBuf;if(AT_RxBuf == '\n'){AT_RxState = 1; // 接收完成idx = 0;}}else // 数据超过限制{AT_RxState = 1; // 接收完成idx = 0;} }URX1IF = 0; // 清除中断标志
}/**
* @brief 向ESP发送命令
*/
static void AT_SendCmd(char * cmd)
{printf("%s\r\n", cmd);
}/**
* @brief
*/
static void AT_Basic_Config(void)
{AT_SendCmd(AT_CLOSE_ECHO); // 关闭回显AT_Wait_Cmd(2000);AT_SendCmd(AT_SET_DVEAP_INFO); // 设置SoftAP参数AT_Wait_Cmd(2000);
}/**
* @brief ESP 串口配置
*/
static void AT_Dev_Uart_Config(void)
{AT_SendCmd(AT_SET_DEV_UART);AT_Wait_Cmd(2000);
}/**
* @brief 设置ESP为TCP模式 并连接到TCP服务器
*/
static void AT_Connect_TCP_Server(void)
{AT_SendCmd(AT_SET_APS_MODE); // 设置ESP为 SoftAP+Station 模式AT_Wait_Cmd(2000); // 等待指令完成AT_SendCmd(AT_CONNECT_ROUTER); // 连接路由器WIFIAT_Wait_Cmd(2000);AT_SendCmd(AT_CONNECT_SERVER); // 连接服务器AT_Wait_Cmd(2000);AT_Send_Data("hello TCP"); // 发送初始数据AT_Send_Data("hello TCP"); // 发送初始数据AT_Send_Data("hello TCP"); // 发送初始数据AT_Send_Data("hello TCP"); // 发送初始数据AT_Send_Data("hello TCP"); // 发送初始数据AT_Send_Data("hello TCP"); // 发送初始数据AT_Send_Data("hello TCP"); // 发送初始数据AT_Send_Data("hello TCP"); // 发送初始数据AT_Send_Data("hello TCP"); // 发送初始数据
}/**
* @brief 等待指令完成
*/
static bool AT_Wait_Cmd(uint16 time)
{AT_Delay_Ms(time);if(strncmp(AT_RxData, "OK", 2)==0)return TRUE;return FALSE;
}/**
* @brief AT 延时函数
*/
void AT_Delay_Ms(uint16 time)
{while(time--){Onboard_wait(1000); // OnBorad_wait 提供us级延时,但并不完全准确}
}/**
* @brief 等待发送数据完成
*/
static bool AT_Wait_SendData(void)
{AT_Wait_Cmd(10); // 等待接收消息 延时时间应根据发送数据的长度进行调整if(strncmp((char *)AT_RxData, "Send OK", 7) == 0)return TRUE;return FALSE;
}
- Note: 发送数据指令后的延时时间,应根据发送数据的长度进行调整。
- AT_RxData在接受数据时存在丢包情况暂未解决
应用层
在zcl_samplesw.c
中编写以下代码
/*==================ESP8266=================*/
#ifdef ZDO_COORDINATOR/*** @brief ESP8266初始化*/static void zclSampleSw_ESP_Init(void){AT_McuUart_Init(); // 初始化与ESP8266通讯的串口AT_ESP_Init(); // ESP8266初始化}/*** @brief 通过ESP8266发送数据*/static bool zclSampleSw_Send_Data(char * data){return AT_Send_Data(data);}
#endif
在zclSampleSw_Init
中调用zclSampleSw_ESP_Init
#ifdef ZDO_COORDINATOR// Init UartzclSampleSw_InitUart();// Init ESPzclSampleSw_ESP_Init();ZDO_RegisterForZDOMsg ( zclSampleSw_TaskID, Device_annce ); // 注册 Device_annce 簇ID...
实验现象
-
查看PC端的IP地址
-
在
AT.h
中,修改对应参数
- 打开网络调试助手,创建一个TCP服务器
- 将程序下载进开发板中,运行(这九条消息是连接到服务器后自动发送的, 代码在
AT_Connect_TCP_Server
中)
参考资料
ESP8266 Non-OS SDK API 参考.pdf
ESP8266 Non-OS AT 指令使用示例_V1.3.pdf