STM32串口WEBSOCKET Server设计(基于ESP8266 WIFI模块AT模式)
HTML5标准支持WEBSOCKET Client,因此对于基于HTML5标准的桌面型应用(如Electron APP)以及移动APP(如HBUILDERX APP),可以通过WIFI模块,实现与嵌入式端WEBSOCKET Server的通讯。WIFI模块与嵌入式端的接口有多种包括SPI和TTL串口等,WIFI转TTL串口在物联网领域用得比较多。这里设计基于WIFI转TTL模块ESP8266的STM32 WEBSOCKET Server。STM32与ESP8266之间通过AT指令通讯。
ESP8266是WIFI转串口的模块,有多种型号,有5V供电的模块(如ESP-12F),也有3.3V供电的模块(如ESP-01),STM32与这些模块之间的串口连接,采用3.3V电平的UART即可。
ESP8266基本配置
这里采用标准AT版本固件库的ESP8266。首先通过将ESP8266模块连接到USB转TTL串口模块,进行一些初始配置。这里需要注意,有些USB转TTL串口模块的输出电流不足,通过TTL串口访问时会出现异常,这种情况下就需要通过外部电源供电。连接到PC后,通过PC串口工具(默认115200波特率)发送AT,如果得到AT则通讯正常。
- 查询和配置ESP8266启动后的模式
通过发送如下指令进行查询:
AT+CWMODE_DEF?
如果得到回复:
+CWMODE_DEF:2
则为正确模式,否则发送如下指令设置正确模式。指令里的_DEF表示存储到ESP8266内部FLASH里,(下次)启动后会读取生效。
AT+CWMODE_DEF=2
- 设置IP和子网掩码
发送如下类似指令设置IP和子网掩码:
AT+CIPAP_DEF="192.168.4.1","192.168.4.1","255.255.255.0"
以上部分为将ESP8266模块连接到STM32之前所做的配置。这部分配置相对比较固定,且可断电不失,因此先做配置,从而减少STM32里的处理内容。
STM32CUBEIDE配置
这里基于HAL库采用STM32F401CCU6开发板和STM32CUBEIDE开发环境实现范例。 首先建立工程并配置时钟: 配置两个USART串口,USART1连接ESP8266模块,USART6用作调试输出。
保存并生成初始工程文件: 在工程目录的包含文件目录(\Core\Inc)建立websocket.h文件内容如下:
/*
Note: currently only for single websocket frame communication, can be expanded if necessary.
*/
#include <string.h>
#include <stdio.h>
#define WS_MIN_LEN_READ 1088
#define WS_MAX_LEN_WRITE 512
char g_ws_read_buf[WS_MIN_LEN_READ] = {0};
char g_ws_write_buf[WS_MAX_LEN_WRITE] = {0};
char g_ws_write_buf_t[WS_MAX_LEN_WRITE] = {0};
unsigned short int ws_handshake_done = 0;
unsigned long long payloadLen = 0;
unsigned long long pack_data_length;
#ifdef __cplusplus
extern "C"{
#endif
#define SHA1CircularShift(bits,word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32-(bits))))
const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
typedef struct SHA1Context
{
unsigned Message_Digest[5];
unsigned Length_Low;
unsigned Length_High;
unsigned char Message_Block[64];
int Message_Block_Index;
int Computed;
int Corrupted;
}SHA1Context;
int tolower(int c);
int htoi(const char s[],int start,int len);
char fetch_sec_key(void);
char compute_accept_key(void) ;
char shake_hand(void);
void base64_encode(void);
char sha1_hash(void);
void SHA1Reset(SHA1Context * context);
int SHA1Result(SHA1Context * context);
void SHA1Input(SHA1Context * context,const char * message_array,unsigned length);
void SHA1ProcessMessageBlock(SHA1Context *);
void SHA1PadMessage(SHA1Context * context);
char analy_data(void);
void pack_data(char * message, unsigned long long dn);
void pack_data_bin(char * message, unsigned long long dn) ;
//if error, return 0, else return 1
char fetch_sec_key(void)
{
const char * flag = "Sec-WebSocket-Key: ";
char * keyBegin = NULL;
int i = 0;
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
keyBegin = strstr(g_ws_read_buf, flag);
if(! keyBegin) //w/o effective request head
{
return 0;
}
//w/ effective request head
keyBegin += strlen(flag);
for(i = 0; i < strlen(g_ws_read_buf); i++)
{
if((keyBegin[i] == 0x0A) || (keyBegin[i] == 0x0D)) //0x0A: new line; 0x0D: return
{
break;
}
g_ws_write_buf[i] = keyBegin[i];
}
return 1;
}
//if error, return 0, else return 1
char compute_accept_key(void)
{
const char * GUID="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
int i = 0, n = 0;
//websocket key
if(! fetch_sec_key())
return 0;
strcat(g_ws_write_buf, GUID);
if(! sha1_hash())
return 0;
n = strlen(g_ws_write_buf);
memset(g_ws_read_buf, '\0', WS_MIN_LEN_READ);
for(i = 0; i < n; i += 2)
{
g_ws_read_buf[i / 2] = htoi(g_ws_write_buf, i, 2);
}
base64_encode();
return 1;
}
//if error, return 0, else return 1
char shake_hand(void)
{
memset(g_ws_read_buf, '\0', WS_MIN_LEN_READ);
sprintf(g_ws_read_buf, "HTTP/1.1 101 Switching Protocols\r\n");
sprintf(g_ws_read_buf, "%sUpgrade: websocket\r\n", g_ws_read_buf);
sprintf(g_ws_read_buf, "%sConnection: Upgrade\r\n", g_ws_read_buf);
sprintf(g_ws_read_buf, "%sSec-WebSocket-Accept: %s\r\n\r\n", g_ws_read_buf, g_ws_write_buf);
return 1;
}
/*base64 function*/
void base64_encode(void)
{
int tmp = 0, i = 0;
int prepare = 0;
int temp = strlen(g_ws_read_buf) % 3;
char * f = NULL;
char changed[4];
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
f = g_ws_write_buf;
while (tmp < strlen(g_ws_read_buf))
{
temp = 0;
prepare = 0;
memset(changed, '\0', 4);
while (temp < 3)
{
if (tmp >= strlen(g_ws_read_buf))
{
break;
}
prepare = ((prepare << 8) | (g_ws_read_buf[tmp] & 0xFF));
tmp++;
temp++;
}
prepare = (prepare << ((3 - temp) * 8));
for (i = 0; i < 4 ;i++ )
{
if (temp < i)
{
changed[i] = 0x40;
}
else
{
changed[i] = (prepare >> ((3 - i) * 6)) & 0x3F;
}
*f = base[changed[i]];
f++;
}
}
*f = '\0';
}
/*SHA1 function*/
void SHA1Reset(SHA1Context * context)
{
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Message_Digest[0] = 0x67452301;
context->Message_Digest[1] = 0xEFCDAB89;
context->Message_Digest[2] = 0x98BADCFE;
context->Message_Digest[3] = 0x10325476;
context->Message_Digest[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
}
int SHA1Result(SHA1Context * context)
{
if (context->Corrupted)
{
return 0;
}
if (!context->Computed)
{
SHA1PadMessage(context);
context->Computed = 1;
}
return 1;
}
void SHA1Input(SHA1Context * context,const char * message_array,unsigned length)
{
if (!length)
return;
if (context->Computed || context->Corrupted)
{
context->Corrupted = 1;
return;
}
while(length-- && !context->Corrupted)
{
context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF);
context->Length_Low += 8;
context->Length_Low &= 0xFFFFFFFF;
if (context->Length_Low == 0)
{
context->Length_High++;
context->Length_High &= 0xFFFFFFFF;
if (context->Length_High == 0) context->Corrupted = 1;
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
}
void SHA1ProcessMessageBlock(SHA1Context * context)
{
const unsigned K[] = {0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
int t;
unsigned temp;
unsigned W[80];
unsigned A, B, C, D, E;
for(t = 0; t < 16; t++)
{
W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
}
for(t = 16; t < 80; t++)
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
A = context->Message_Digest[0];
B = context->Message_Digest[1];
C = context->Message_Digest[2];
D = context->Message_Digest[3];
E = context->Message_Digest[4];
for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->Message_Digest[0] = (context->Message_Digest[0] + A) & 0xFFFFFFFF;
context->Message_Digest[1] = (context->Message_Digest[1] + B) & 0xFFFFFFFF;
context->Message_Digest[2] = (context->Message_Digest[2] + C) & 0xFFFFFFFF;
context->Message_Digest[3] = (context->Message_Digest[3] + D) & 0xFFFFFFFF;
context->Message_Digest[4] = (context->Message_Digest[4] + E) & 0xFFFFFFFF;
context->Message_Block_Index = 0;
}
void SHA1PadMessage(SHA1Context *context)
{
if (context->Message_Block_Index > 55)
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 64)
context->Message_Block[context->Message_Block_Index++] = 0;
SHA1ProcessMessageBlock(context);
while(context->Message_Block_Index < 56)
context->Message_Block[context->Message_Block_Index++] = 0;
}
else
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 56)
context->Message_Block[context->Message_Block_Index++] = 0;
}
context->Message_Block[56] = (context->Length_High >> 24 ) & 0xFF;
context->Message_Block[57] = (context->Length_High >> 16 ) & 0xFF;
context->Message_Block[58] = (context->Length_High >> 8 ) & 0xFF;
context->Message_Block[59] = (context->Length_High) & 0xFF;
context->Message_Block[60] = (context->Length_Low >> 24 ) & 0xFF;
context->Message_Block[61] = (context->Length_Low >> 16 ) & 0xFF;
context->Message_Block[62] = (context->Length_Low >> 8 ) & 0xFF;
context->Message_Block[63] = (context->Length_Low) & 0xFF;
SHA1ProcessMessageBlock(context);
}
//if error, return 0, else return 1
char sha1_hash(void)
{
SHA1Context sha = {0};
SHA1Reset(&sha);
SHA1Input(&sha, g_ws_write_buf, strlen(g_ws_write_buf));
if (! SHA1Result(&sha))
{
printf("%s-%d:Could not compute message digest.\n", __func__, __LINE__);
return 0;
}
else
{
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
sprintf(g_ws_write_buf, "%08X%08X%08X%08X%08X", sha.Message_Digest[0],sha.Message_Digest[1],
sha.Message_Digest[2],sha.Message_Digest[3],sha.Message_Digest[4]);
return 1;
}
}
/*convertion function*/
int tolower(int c)
{
if (c >= 'A' && c <= 'Z')
{
return c + 'a' - 'A';
}
else
{
return c;
}
}
int htoi(const char s[],int start,int len)
{
int i,j;
int n = 0;
if (s[0] == '0' && (s[1]=='x' || s[1]=='X'))
{
i = 2;
}
else
{
i = 0;
}
i+=start;
j=0;
for (; (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'f') || (s[i] >='A' && s[i] <= 'F');++i)
{
if(j>=len)
{
break;
}
if (tolower(s[i]) > '9')
{
n = 16 * n + (10 + tolower(s[i]) - 'a');
}
else
{
n = 16 * n + (tolower(s[i]) - '0');
}
j++;
}
return n;
}
/*
Frame format:
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 ......
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
*/
char analy_data(void)
{
char fin = 0;
char maskFlag = 0;
char masks[4] = {0};
char temp[8];
unsigned long long i = 0;
payloadLen = 0;
if (strlen(g_ws_read_buf) < 2)
{
//data len error.
return 0;
}
fin = (g_ws_read_buf[0] & 0x80) == 0x80;
if (!fin)
{
//fin error.
return 0;
}
maskFlag = (g_ws_read_buf[1] & 0x80) == 0x80;
if (!maskFlag)
{
//mask flag error.
return 0;
}
payloadLen = g_ws_read_buf[1] & 0x7F;
if (payloadLen == 126)
{
memcpy(masks, g_ws_read_buf + 4, 4);
payloadLen = (((unsigned short)g_ws_read_buf[2] ) << 8) | ((unsigned short)g_ws_read_buf[3]);
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
memcpy(g_ws_write_buf, g_ws_read_buf + 8, payloadLen);
}
else if (payloadLen == 127)
{
memcpy(masks, g_ws_read_buf + 10, 4);
for ( i = 0; i < 8; i++)
{
temp[i] = g_ws_read_buf[2 + i];
}
memcpy(&payloadLen,temp,8);
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
memcpy(g_ws_write_buf, g_ws_read_buf + 14, payloadLen);//toggle error(core dumped) if data is too long.
}
else
{
memcpy(masks, g_ws_read_buf + 2, 4);
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
memcpy(g_ws_write_buf, g_ws_read_buf + 6, payloadLen);
}
memset(g_ws_write_buf_t, '\0', WS_MAX_LEN_WRITE);
for (i = 0; i < payloadLen; i++)
{
g_ws_write_buf_t[i] = (char)(g_ws_write_buf[i] ^ masks[i % 4]);
}
//get data: g_ws_write_buf_t w/ data length: payloadLen
return 1;
}
void pack_data(char * message, unsigned long long dn) // message: input; dn: input; len: output
{
if (dn<=0) pack_data_length = 0;
else if ((dn > 0)&&(dn < 126))
{
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
g_ws_write_buf[0] = 0x81;
g_ws_write_buf[1] = dn;
memcpy(g_ws_write_buf + 2, message, dn);
pack_data_length = dn + 2;
}
else if ((dn >= 126)&&(dn <= 0xFFFF))
{
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
g_ws_write_buf[0] = 0x81;
g_ws_write_buf[1] = 126;
g_ws_write_buf[2] = ((dn >> 8) & 0xFF);
g_ws_write_buf[3] = (dn & 0xFF);
memcpy(g_ws_write_buf + 4, message, dn);
pack_data_length = dn + 4;
}
else
{
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
g_ws_write_buf[0] = 0x81;
g_ws_write_buf[1] = 127;
g_ws_write_buf[2] = ((dn >> 56) & 0xFF);
g_ws_write_buf[3] = ((dn >> 48) & 0xFF);
g_ws_write_buf[4] = ((dn >> 40) & 0xFF);
g_ws_write_buf[5] = ((dn >> 32) & 0xFF);
g_ws_write_buf[6] = ((dn >> 24) & 0xFF);
g_ws_write_buf[7] = ((dn >> 16) & 0xFF);
g_ws_write_buf[8] = ((dn >> 8) & 0xFF);
g_ws_write_buf[9] = ((dn >> 0) & 0xFF);
memcpy(g_ws_write_buf + 10, message, dn);
pack_data_length = dn + 10;
}
//get data: g_ws_write_buf w/ length: *len
//*len is essential for byte communication beyond character communication
}
void pack_data_bin(char * message, unsigned long long dn) // message: input; dn: input; len: output
{
if (dn<=0) pack_data_length = 0;
else if ((dn > 0)&&(dn < 126))
{
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
g_ws_write_buf[0] = 0x82;
g_ws_write_buf[1] = dn;
memcpy(g_ws_write_buf + 2, message, dn);
pack_data_length = dn + 2;
}
else if ((dn >= 126)&&(dn <= 0xFFFF))
{
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
g_ws_write_buf[0] = 0x82;
g_ws_write_buf[1] = 126;
g_ws_write_buf[2] = ((dn >> 8) & 0xFF);
g_ws_write_buf[3] = (dn & 0xFF);
memcpy(g_ws_write_buf + 4, message, dn);
pack_data_length = dn + 4;
}
else
{
memset(g_ws_write_buf, '\0', WS_MAX_LEN_WRITE);
g_ws_write_buf[0] = 0x82;
g_ws_write_buf[1] = 127;
g_ws_write_buf[2] = ((dn >> 56) & 0xFF);
g_ws_write_buf[3] = ((dn >> 48) & 0xFF);
g_ws_write_buf[4] = ((dn >> 40) & 0xFF);
g_ws_write_buf[5] = ((dn >> 32) & 0xFF);
g_ws_write_buf[6] = ((dn >> 24) & 0xFF);
g_ws_write_buf[7] = ((dn >> 16) & 0xFF);
g_ws_write_buf[8] = ((dn >> 8) & 0xFF);
g_ws_write_buf[9] = ((dn >> 0) & 0xFF);
memcpy(g_ws_write_buf + 10, message, dn);
pack_data_length = dn + 10;
}
//get data: g_ws_write_buf w/ length: *len
//*len is essential for byte communication beyond character communication
}
#ifdef __cplusplus
}
#endif
STM32主代码
因为ESP8266上电时向串口输出上电信息,因此STM32在打开USART1串口接收中断前,应该延时一段时间,以规避处理这部分上电信息。然后STM32控制ESP8266进入无回显状态(ATE0指令),开启多连接(AT+CIPMUX=1指令),设置端口号(AT+CIPSERVER=1,8080指令,这里8080为端口号),以及准备发送(AT+CIPSENDEX=0,1460指令,实际发送过程中,会通过特定的结束符指示进行发送)。 STM32主代码实现接受WEBSOCKET连接,范例功能设计为在接收到两个字节时,返回这两个字节, 在接收到其他数据时,返回"F" (fault)。 为简化例程设计,一些向ESP8266发送的指令的回复,会接收但不做识别处理。 代码里用到延时函数原理参考 STM32 HAL us delay(微秒延时)的指令延时实现方式及优化 。
完整的main.c文件代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2022 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdlib.h>
#include <stdio.h>
#include "websocket.h"
/*
To config ESP8266 module in advance for the following command:
AT+CWSAP_DEF="ESP_Pegasus","12345678",1,4 //Set AP&PSW
AT+CIPAP_DEF="192.168.4.1","192.168.4.1","255.255.255.0" //Set IP
*/
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define WRXMAX 1460
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart6;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART6_UART_Init(void);
/* USER CODE BEGIN PFP */
float usDelayBase;
void PY_usDelayTest(void)
{
uint32_t firstms, secondms;
uint32_t counter = 0;
firstms = HAL_GetTick()+1;
secondms = firstms+1;
while(uwTick!=firstms) ;
while(uwTick!=secondms) counter++;
usDelayBase = ((float)counter)/1000;
}
void PY_Delay_us_t(uint32_t Delay)
{
uint32_t delayReg;
uint32_t usNum = (uint32_t)(Delay*usDelayBase);
delayReg = 0;
while(delayReg!=usNum) delayReg++;
}
void PY_usDelayOptimize(void)
{
uint32_t firstms, secondms;
float coe = 1.0;
firstms = HAL_GetTick();
PY_Delay_us_t(1000000) ;
secondms = HAL_GetTick();
coe = ((float)1000)/(secondms-firstms);
usDelayBase = coe*usDelayBase;
}
void PY_Delay_us(uint32_t Delay)
{
uint32_t delayReg;
uint32_t msNum = Delay/1000;
uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);
if(msNum>0) HAL_Delay(msNum);
delayReg = 0;
while(delayReg!=usNum) delayReg++;
}
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t Uart1_RxBuff[WRXMAX+1]={0}; //for max length of one package of Ethernet
uint8_t uart1_rx_status = 0x80;
uint8_t uart1_rx_rxloc = 0;
char * uart1_tx_cmd = "AT+CIPSENDEX=0,1460\r\n";
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART6_UART_Init();
/* USER CODE BEGIN 2 */
uart1_rx_status=0x80;
PY_usDelayTest();
PY_usDelayOptimize();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//start of ESP8266 config
if(uart1_rx_status==0x80)
{
PY_Delay_us_t(2000000); //to escape ESP8266 power up information
memset(Uart1_RxBuff, '\0', WRXMAX);
uart1_rx_status=0x81;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
uart1_tx_cmd = "ATE0\r\n";
HAL_UART_Transmit(&huart1, uart1_tx_cmd,8,2000);
}
if(uart1_rx_status==0x82)
{
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
HAL_UART_Transmit(&huart6, Uart1_RxBuff, strlen(Uart1_RxBuff), 2720);
memset(Uart1_RxBuff, '\0', WRXMAX);
uart1_rx_status=0x83;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
uart1_tx_cmd = "AT+CIPMUX=1\r\n";
HAL_UART_Transmit(&huart1, uart1_tx_cmd,15,2000);
}
if(uart1_rx_status==0x84)
{
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
HAL_UART_Transmit(&huart6, Uart1_RxBuff, strlen(Uart1_RxBuff), 2720);
memset(Uart1_RxBuff, '\0', WRXMAX);
uart1_rx_status=0x85;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
uart1_tx_cmd = "AT+CIPSERVER=1,8080\r\n";
HAL_UART_Transmit(&huart1, uart1_tx_cmd,23,2000);
}
if(uart1_rx_status==0x86)
{
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
HAL_UART_Transmit(&huart6, Uart1_RxBuff, strlen(Uart1_RxBuff), 2720);
memset(Uart1_RxBuff, '\0', WRXMAX);
uart1_rx_status=0x00;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
uart1_tx_cmd = "AT+CIPSENDEX=0,1460\r\n";
}
//end of ESP8266 config
//start of websocket connection
if(uart1_rx_status==1)
{
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
uart1_rx_rxloc=0;
for(uint8_t j=0;j<30;j++)
{
if(Uart1_RxBuff[j]==0x3A) //searching ":"
{
uart1_rx_rxloc=j+1;
break;
}
}
memcpy(g_ws_read_buf, &Uart1_RxBuff[uart1_rx_rxloc], WS_MIN_LEN_READ); //Receving data ends with "0x0A" and "0x0D" for recognition
if (compute_accept_key()==0)
{
uart1_rx_status = 0;
}
else
{
HAL_UART_Transmit(&huart1, uart1_tx_cmd,23, 2000);
uart1_rx_status=0xf1;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
while(uart1_rx_status==0xf1) PY_Delay_us(10);
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
shake_hand() ;
HAL_UART_Transmit(&huart1, g_ws_read_buf, strlen(g_ws_read_buf), 2000);
Uart1_RxBuff[0]=0x5c;
Uart1_RxBuff[1]=0x30;
HAL_UART_Transmit(&huart1, Uart1_RxBuff,2, 2000);
uart1_rx_status=0xf1;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
while(uart1_rx_status==0xf1) PY_Delay_us(10);
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
uart1_rx_status = 2;
HAL_UART_Transmit(&huart6, "Websocket connected!", 23, 2720);
}
memset(Uart1_RxBuff, '\0', WRXMAX); //Set "end character" for recognition
HAL_UART_Receive_IT(&huart1, (uint8_t *)&Uart1_RxBuff[0], 1);
}
//end of websocket connection
//start of data receiving and processing
if(uart1_rx_status==3)
{
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 2000);
uart1_rx_rxloc=0;
for(uint8_t j=0;j<30;j++)
{
if(Uart1_RxBuff[j]==0x3A) //searching ":"
{
uart1_rx_rxloc=j+1;
break;
}
}
memcpy(g_ws_read_buf, &Uart1_RxBuff[uart1_rx_rxloc], WS_MIN_LEN_READ);
analy_data() ;
if(payloadLen!=2)
{
HAL_UART_Transmit(&huart1, uart1_tx_cmd,23, 2000);
uart1_rx_status=0xf1;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
while(uart1_rx_status==0xf1) PY_Delay_us(10);
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
g_ws_write_buf_t[0]='F'; //Failure for data receiving length
pack_data(g_ws_write_buf_t, 1) ;
HAL_UART_Transmit(&huart1, g_ws_write_buf, pack_data_length, 2000);
Uart1_RxBuff[0]=0x5c;
Uart1_RxBuff[1]=0x30;
HAL_UART_Transmit(&huart1, Uart1_RxBuff,2, 2000); //flag of start sending
uart1_rx_status=0xf1;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
while(uart1_rx_status==0xf1) PY_Delay_us(10);
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
memset(Uart1_RxBuff, '\0', WRXMAX); //Set "end character" for recognition
uart1_rx_status=2;
HAL_UART_Receive_IT(&huart1, (uint8_t *)&Uart1_RxBuff[0], 1);
}
else
{
HAL_UART_Transmit(&huart1, uart1_tx_cmd,23, 2000);
uart1_rx_status=0xf1;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
while(uart1_rx_status==0xf1) PY_Delay_us(10);
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
pack_data(g_ws_write_buf_t, 2) ;
HAL_UART_Transmit(&huart1, g_ws_write_buf, pack_data_length, 2000);
Uart1_RxBuff[0]=0x5c;
Uart1_RxBuff[1]=0x30;
HAL_UART_Transmit(&huart1, Uart1_RxBuff,2, 2000); //flag of start sending
uart1_rx_status=0xf1;
HAL_UART_Receive_IT(&huart1, Uart1_RxBuff, 1);
while(uart1_rx_status==0xf1) PY_Delay_us(10);
HAL_UART_Receive(&huart1, (uint8_t *)&Uart1_RxBuff[1], WS_MIN_LEN_READ-1, 100);
memset(Uart1_RxBuff, '\0', WRXMAX); //Set "end character" for recognition
uart1_rx_status=2;
HAL_UART_Receive_IT(&huart1, (uint8_t *)&Uart1_RxBuff[0], 1);
}
}
//end of data receiving and processing
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* @brief USART6 Initialization Function
* @param None
* @retval None
*/
static void MX_USART6_UART_Init(void)
{
/* USER CODE BEGIN USART6_Init 0 */
/* USER CODE END USART6_Init 0 */
/* USER CODE BEGIN USART6_Init 1 */
/* USER CODE END USART6_Init 1 */
huart6.Instance = USART6;
huart6.Init.BaudRate = 115200;
huart6.Init.WordLength = UART_WORDLENGTH_8B;
huart6.Init.StopBits = UART_STOPBITS_1;
huart6.Init.Parity = UART_PARITY_NONE;
huart6.Init.Mode = UART_MODE_TX_RX;
huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart6.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart6) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART6_Init 2 */
/* USER CODE END USART6_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
}
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
if(UartHandle==&huart1) //for WiFi
{
if(uart1_rx_status==0)
{
uart1_rx_status=1;
}
if(uart1_rx_status==2)
{
uart1_rx_status=3;
}
if(uart1_rx_status==0x81)
{
uart1_rx_status=0x82;
}
if(uart1_rx_status==0x83)
{
uart1_rx_status=0x84;
}
if(uart1_rx_status==0x85)
{
uart1_rx_status=0x86;
}
if(uart1_rx_status==0xf1)
{
uart1_rx_status=0xf0;
}
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
测试过程
- 嵌入式端上电后,打开PC或手机WIFI,查找对应的热点如ESP_PEGASUS,然后输入密码如12345678连接。如果有出现当前WIFI热点不能上网的提示时,选择保持此连接;
- 用Chrome浏览器打开下面代码的websocket_test.html文件;
- 点击”Run WebSocket“链接,触发websocket连接及发送两个字符"OK";
- 从开发者工具的控制台查看过程及接收信息。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Websocket Demo</title>
<script type="text/javascript">
var cmd = new Uint8ClampedArray(2);
function WebSocketTest()
{
if ("WebSocket" in window)
{
console.log("WebSocket supported!");
var ws = new WebSocket("ws://192.168.4.1:8080");
ws.onopen = function()
{
setTimeout(function(){
cmd[0]=79; cmd[1]=75;
ws.send(cmd);
console.log("data sent");
},1000);
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
console.log("data received: "+received_msg);
};
ws.onclose = function()
{
console.log("connection closed");
};
}
else
{
console.log("WebSocket unsupported!");
}
}
</script>
</head>
<body>
<div id="sse">
<a href="javascript:WebSocketTest()">Run WebSocket</a>
</div>
</body>
</html>
WEBSOCKET协议应用特征
这里的WEBSOCKET协议应用特征如下:
- 只接受客户端发来的单帧数据,即FIN为0时认为错误不做处理。因此如果要发来的数据很长时,要在客户端自行先做分段,再将分段按单帧进行发送;
- 只接受客户端发来的加密数据,未加密时认为错误不做处理;
- 客户端发来的opcode对服务器端没有价值,嵌入式服务器端当作字节数据处理即可;
- 服务器端向客户端发送数据时不做数据加密;
- 服务器端向客户端发送的opcode有意义,浏览器客户端的处理方式会不同。
–End–
|