HTML5标准支持WEBSOCKET Client,因此对于基于HTML5标准的桌面型应用(如Electron APP)以及移动APP(如HBUILDERX APP),可以通过WIFI模块,实现与嵌入式端WEBSOCKET Server的通讯。WIFI模块与嵌入式端的接口有多种包括SPI和TTL串口等,WIFI转TTL串口在物联网领域用得比较多。这里设计基于WIFI转TTL模块HC-25的STM32 WEBSOCKET Server。STM32与HC-25之间为透传通讯。HC-25为3.3V供电模块。相同类型的透传模块还有HLK-M20。
HC-25支持网页配置模式,上电HC-25模块后,PC或手机搜索并连接HC-25热点如:
打开浏览器,在地址栏输入 http://192.168.4.1,回车,出现模块网页界面:
输入密码后,点击登录,可以进入网页设置。初始密码为空。可以点击“设置密码”框,进行密码设置,密码不小于 8 位。
然后可以修改串口的波特率,以及重设网络名称,网络密码以及网络地址:
设置模块工作为Server模式:
无需设置MQTT参数:
保存后就可以将HC-25模块连接到STM32开发板。
然后重新从PC或手机连接HC-25热点后,就可以进行TCP连接,连接完成后,就可以与STM32的UART串口进行透明数据传输。也就完成了实现Websocket通讯的基础设置。
如果采用HLK-M20模块,则没有网页配置方式,需要先将模块接到串口工具上,上电后切换到AT模式,通过AT指令进行配置和保存,再重启后就进入透明传输模式。
这里基于HAL库采用STM32L476RGT6开发板和STM32CUBEIDE开发环境实现范例。
首先建立工程并配置时钟:
配置USART3作为连接HC-25模块的TTL串口:
保存并生成初始工程文件:
在工程目录的包含文件目录(\Core\Inc)建立websocket.h文件内容如下:
/*
Note: currently only for single websocket frame communication, can be expanded if necessary.
*/
#include
#include
#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主代码实现接受WEBSOCKET连接,范例功能设计为在接收到两个字节时,返回这两个字节, 在接收到其他数据时,返回"F" (fault)。
代码里用到延时函数原理参考 STM32 HAL us delay(微秒延时)的指令延时实现方式及优化 。
完整的main.c文件代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* © Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "websocket.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define WRXMAX 1460
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++;
}
uint32_t 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 usNum = (uint32_t)(Delay*usDelayBase);
delayReg = 0;
while(delayReg!=usNum) delayReg++;
}
void PY_Delay_us_enh(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 PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
uint8_t Uart3_RxBuff[WRXMAX+1]={0}; //for max length of one package of Ethernet
uint8_t uart3_rx_status = 0;
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart3;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART3_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* 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_USART3_UART_Init();
/* USER CODE BEGIN 2 */
PY_usDelayTest();
PY_usDelayOptimize();
PY_Delay_us(2000000);
uart3_rx_status=0;
HAL_UART_Receive_IT(&huart3, Uart3_RxBuff, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//start of websocket connection
if(uart3_rx_status==1)
{
HAL_UART_Receive(&huart3, (uint8_t *)&Uart3_RxBuff[1], WS_MIN_LEN_READ-1, 2000);
memcpy(g_ws_read_buf, Uart3_RxBuff, WS_MIN_LEN_READ); //Receving data ends with "0x0A" and "0x0D" for recognition
if (compute_accept_key()==0)
{
uart3_rx_status = 0;
}
else
{
uart3_rx_status = 2;
shake_hand() ;
HAL_UART_Transmit(&huart3, g_ws_read_buf, strlen(g_ws_read_buf), 2000);
}
memset(&Uart3_RxBuff[1], '\0', WRXMAX); //Set "end character" for recognition
HAL_UART_Receive_IT(&huart3, (uint8_t *)&Uart3_RxBuff[0], 1);
}
//end of websocket connection
//start of data receiving and processing
if(uart3_rx_status==3)
{
HAL_UART_Receive(&huart3, (uint8_t *)&Uart3_RxBuff[1], WS_MIN_LEN_READ-1, 2000);
memcpy(g_ws_read_buf, Uart3_RxBuff, WS_MIN_LEN_READ);
analy_data() ;
if(payloadLen==2)
{
pack_data(g_ws_write_buf_t, 2) ;
HAL_UART_Transmit(&huart3, g_ws_write_buf, pack_data_length, 2000);
}
else{
g_ws_write_buf_t[0]='F'; //Failure for data receiving length
pack_data(g_ws_write_buf_t, 1) ;
HAL_UART_Transmit(&huart3, g_ws_write_buf, pack_data_length, 2000);
}
uart3_rx_status=2;
memset(&Uart3_RxBuff[0], '\0', WRXMAX); //Set "end character" for recognition
HAL_UART_Receive_IT(&huart3, (uint8_t *)&Uart3_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
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** 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 = 1;
RCC_OscInitStruct.PLL.PLLN = 10;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
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_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART3 Initialization Function
* @param None
* @retval None
*/
static void MX_USART3_UART_Init(void)
{
/* USER CODE BEGIN USART3_Init 0 */
/* USER CODE END USART3_Init 0 */
/* USER CODE BEGIN USART3_Init 1 */
/* USER CODE END USART3_Init 1 */
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART3_Init 2 */
/* USER CODE END USART3_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
}
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
if(UartHandle==&huart3)
{
if(uart3_rx_status==0)
{
uart3_rx_status=1;
}
if(uart3_rx_status==2)
{
uart3_rx_status=3;
}
}
}
/* 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 */
Websocket Demo
这里的WEBSOCKET协议应用特征如下:
–End–