1.交互流程簡介
(1)設備上電,先控制8266的復位引腳為低電平,讓模塊復位
(2)發送指令:ATE0,取消回顯
(3)發送指令:AT+CWMODE=2,設置ESP01S為AP模式
(4)發送指令:AT+CIPMUX=1,設置多路連接,AP模式最多支持5個設備連接
(5)發送指令:AT+CWSAP=“ESP01S_test”,“12345678”,1,3,啟動一個WIFI熱點
(6)發送指令:AT+CIPSERVER=1,8089,啟動TCP Server
(7)發送指令:AT+CIPSERVER=1,8089,啟動TCP Server
(8)大循環中檢測是否收到ESP01S數據,收到數據后立刻返回。
2.程序框架簡介
程序主要包括如下4個功能模塊:ESP01S初始化、串口處理、Event回調函數、事件處理;串口處理模塊包括串口接收和定時器判斷一幀數據是否接收完成功能,Event回調函數主要用來通知應用層系統的狀態,方便應用層做出相應,比如設備檢測到其他TCP Client客戶端接入模塊,可以控制LED狀態,事件處理模塊主要包含應用程序大循環,大循環中檢測系統事件狀態,根據事件狀態再大循環中做出響應。
二、系統功能模塊詳述
1.Event回調函數
本程序使用了函數指針,應用層將事件處理函數傳到hal_common.c中int hal_sys_contex_init(sys_status_fun fun, void *user_data)函數
void system_status_callback(int sock, int event)
{
system_context-》sock_id = sock;
system_context-》event = event;
switch (event)
{
case STA_CONNECTED:
rt_kprintf(“Sock %d connected!\r\n”, sock);
break;
case STA_CLOSED:
rt_kprintf(“Sock %d closed!\r\n”, sock);
break;
case STA_DATA_ARRIVED:
rt_kprintf(“Sock %d data arrived!\r\n”, sock);
break;
default:
break;
}
}
typedef enum {
STA_CONNECTED,
STA_CLOSED,
STA_DATA_ARRIVED, // clients send data to wifi
STA_EVENT_MAX,
}sys_event_e;
typedef void (*sys_status_fun)(int sock, int event);
typedef struct sys_ctx{
int sock_id;
sys_event_e event;
char data_buf[SYS_CTX_UART_RECV_SIZE];
sys_status_fun sys_status_cb;
void *user_data;
}sys_ctx_t;
int hal_sys_contex_init(sys_status_fun fun, void *user_data)
{
sys_contex.sys_status_cb = fun;
sys_contex.user_data = user_data;
return 0;
}
int main(void)
{
hal_sys_contex_init(system_status_callback, RT_NULL);
while(1)
{
}
}
2.串口處理
串口處理模塊包括串口接收和定時器判斷一幀數據是否接收完成功能,串口接收函數代碼如下:
#define RX_BUF_MAX_LEN 1024 //最大接收緩存字節數
struct STRUCT_USART_Fram_S //串口數據幀的處理結構體
{
char Data_RX_BUF [ RX_BUF_MAX_LEN ];
uint16_t FramLength;
struct {
uint8_t FramFinishFlag;
} InfBit;
} ;
struct STRUCT_USART_Fram_S Esp8266_Frame_Record;
void USART2_IRQHandler()
{
uint8_t ch = -1;
if(RESET != usart_interrupt_flag_get(EVAL_COM2, USART_INT_FLAG_RBNE))
{
ch = usart_data_receive(EVAL_COM2);
// if ( Esp8266_Frame_Record.FramLength 《 ( RX_BUF_MAX_LEN - 1 ) ) //預留1個字節寫結束符
// {
Esp8266_Frame_Record .Data_RX_BUF [ Esp8266_Frame_Record.FramLength ] = ch;
// }
Esp8266_Frame_Record.FramLength ++;
if (Esp8266_Frame_Record.FramLength 》= 1024)
{
Esp8266_Frame_Record.FramLength = 0;
}
cnt = Esp8266_Frame_Record.FramLength;
// rt_kprintf(“。..。...uart recv : %c, count is %d\r\n”, ch, cnt);
Esp8266_Frame_Record.InfBit.FramStartFlag = 1;
}
}
中斷處理函數中,將接收的數據放到Esp8266_Frame_Record .Data_RX_BUF中,然后將
Esp8266_Frame_Record.InfBit.FramStartFlag置1,這個標志位再定時器中會用到,可以用來判斷接收一幀數據是否完成。
一幀數據接收是否完成的判斷邏輯是:定時器會定期檢測,如果FramStartFlag為1,說明串口正在接收數據,沒接收一個數據,FramLength加1,因此,當進入定時器中斷函數,判斷FramStartFlag為1情況下FrameLength如果不再增加,說明一幀數據接收完成。
static void timeout1(void *parameter)
{
int sock_id = -1;
char buff[128] = { 0x00 };
int len = 0;
sys_event_e event = STA_EVENT_MAX;
// rt_kprintf(“timer‘s cnt is %d, FrameLength is %d\r\n”, cnt, Esp8266_Frame_Record.FramLength);
if (1 == Esp8266_Frame_Record.InfBit.FramStartFlag)
{
if (cnt == Esp8266_Frame_Record.FramLength && cnt != 0)
{
cnt = 0;
Esp8266_Frame_Record .Data_RX_BUF [ Esp8266_Frame_Record.FramLength ] = 0x00;
rt_kprintf(“timer --------》 data %s\r\n”, Esp8266_Frame_Record.Data_RX_BUF);
if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, “CONNECT”))
{
sscanf(Esp8266_Frame_Record.Data_RX_BUF, “%d,%s”, &sock_id, buff);
event = STA_CONNECTED;
}else if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, “CLOSED”))
{
sscanf(Esp8266_Frame_Record.Data_RX_BUF, “%d,%s”, &sock_id, buff);
event = STA_CLOSED;
}else if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, “+IPD”))
{
rt_memset(hal_sys_contex_get()-》data_buf, 0x00, SYS_CTX_UART_RECV_SIZE);
sscanf(Esp8266_Frame_Record.Data_RX_BUF, “%*[^+]+IPD,%d,%d:%[^\r]”, &sock_id, &len, hal_sys_contex_get()-》data_buf);
event = STA_DATA_ARRIVED;
rt_kprintf(“parsed +IPD :%s\r\n”, hal_sys_contex_get()-》data_buf);
}
// call sys_status_cb
if (hal_sys_contex_get()-》sys_status_cb)
{
hal_sys_contex_get()-》sys_status_cb(sock_id, event);
}
Esp8266_Frame_Record.InfBit.FramFinishFlag = 1;
Esp8266_Frame_Record.InfBit.FramStartFlag = 0;
}else
{
cnt = Esp8266_Frame_Record.FramLength;
}
}else
{
cnt = 0;
Esp8266_Frame_Record.FramLength = 0;
}
}
注意:事件處理本質上是在此調用hal_sys_contex_get()-》sys_status_cb(sock_id, event)映射到應用層的void system_status_callback(int sock, int event)函數。
3.事件處理
事件處理的核心再while(1)中,根據系統當前事件狀態做出響應,本節是檢測到事件為數據類型時候,將數據原路返回。
int main(void)
{
/* enable the LED clock */
rcu_periph_clock_enable(RCU_GPIOA);
/* configure LED GPIO port */
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
gpio_bit_reset(GPIOA, GPIO_PIN_1);
// create iwdt_thread
dynamic_thread = rt_thread_create(“led_thread”, led_process_thread_entry,
RT_NULL, 512, 2, 10);
rt_thread_startup(dynamic_thread);
// init sys_ctx
hal_sys_contex_init(system_status_callback, RT_NULL);
system_context = hal_sys_contex_get();
hal_timer_init();
ESP8266_Init();
rt_thread_mdelay(1000);
ESP8266_Ate0();
tcp_server_init();
tcp_server_start();
while(1)
{
if (STA_DATA_ARRIVED == system_context-》event)
{
// send back
ESP8266_SendString ( DISABLE, system_context-》data_buf, rt_strlen(system_context-》data_buf), system_context-》sock_id );
}
rt_thread_mdelay(10);
}
return 0;
}
三、運行
下載程序完畢后,重啟設備,ESP01S啟動一個WIFI熱點,并啟動TCP Server,log如下:
電腦連接熱點,使用網絡助手連接192.168.4.1:8089
網絡助手發送數據給ESP01S
關閉網絡助手,應用程序也可以檢測到,如下Log所示
評論