WH-BLE103藍芽晶片組網測試
阿新 • • 發佈:2020-12-12
LIST
開發環境
MCU:STM8L151K4T6
藍芽模組:WH-BLE103(文末附官方資料連結)
編譯:IAR_STM8
燒錄:STVP
開發平臺:vscode
主要擴充套件:Embedded IDE、C\C++
測試過程
前言
最近想鼓搗鼓搗藍芽,之前沒有接觸過藍芽協議棧,感覺從現成的模組入手會更快一點。
選用的是WH-BLE103,支援透傳以及AT指令組網。這裡簡單科普一下,透傳是啥呢?其實就是把藍芽模組當串列埠來用,你發過來的資料是什麼,我接收到的就是什麼。而AT指令,可以快速的設定藍芽模組的各項引數。
目標
- 實現十臺裝置的組網和資訊通訊
- 實現手機通過中繼控制/查詢網內節點裝置資訊(實驗中使用LED亮暗表示資訊)
工程與程式碼
- 工程配置
因為我希望組網是一個動態的過程(可以任意新增或刪除裝置),因此需要申請動態陣列,看了下memory map,我在工程中申請了一個256位元組的heap。
"linker": {
"linker-config": "lnkstm8l151k4.icf",
"auto-search-runtime-lib" : true,
"use-C_SPY-debug-lib": true,
"config-defines": [
"_CSTACK_SIZE=0x0400",
"_HEAP_SIZE=0x0100"
]
}
- MCU串列埠初始化
AT指令可以設定藍芽的串列埠通訊引數,本實驗沿用了WH-BLE103的出廠引數,具體可看註釋。
void bsp_uart_init(void)
{
USART_DeInit(USART1) ;
CLK_PeripheralClockConfig(CLK_Peripheral_USART1, ENABLE);
//remappin
//SYSCFG_REMAPPinConfig(REMAP_Pin_USART1TxRxPortC, ENABLE);
/**
* @param USART1 ,
* @param BR: 57600,
* @param WL: 8bit,
* @param SB: 1 bit,
* @param PR: no parity,
* @param MODE: RX/TX mode
*/
USART_Init(USART1, (uint32_t)57600, USART_WordLength_8b, USART_StopBits_1, USART_Parity_No, (USART_Mode_Tx | USART_Mode_Rx));
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
ITC_SetSoftwarePriority(USART1_RX_IRQn, ITC_PriorityLevel_2);//priority 2
USART_Cmd(USART1, ENABLE);
USART1_RX_STA = 0;
bsp_tim3_init(1250);//Enter interrupt after 10ms
}
- MCU串列埠資料處理程式
用於處理藍芽回傳資料流和外部(如手機)資料流
USART1_RX_STA的宣告入下:
Bit [15] | Bit [14:0] |
---|---|
0: waiting for receive, 1: received a set of data | record the length of data |
採用了10ms規則,即兩次資料流的接收間隔必須大於10ms
void USART1_IRQHandler(void)
{
uint8_t receive_data;
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET) //Check whether the specified UART1 interrupt occurs.
{
receive_data = USART_ReceiveData8(USART1);
if((USART1_RX_STA & (uint16_t)(1<<15)) == 0)
{
if(USART1_RX_STA < USART1_RX_MAX_LEN) //Determine whether the byte overflows
{
TIM3_SetCounter(0);//recount
if(0 == USART1_RX_STA)
TIM3_Cmd(ENABLE);
USART1_RX_buf[USART1_RX_STA++] = receive_data;
// if(RELAY_DEV == DEVICE_ID)
// USART1_RELAY_buf[USART1_RX_STA++] = receive_data;
}
else
USART1_RX_STA |= (uint16_t) 1<<15;//enforce finish receive
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //Clear UART1 pending flag
}
}
- AT指令傳送
基於藍芽模組手冊編寫,根據藍芽回傳規則,AT指令傳送成功時的返回資料尾為 “OK\r\n”,進入AT模式時返回的資料尾為 “ok\r\n”。
uint8_t AT_Send(uint8_t *atcmd)
{
uint16_t tag = 1;
uint8_t t;
// uint16_t k;
USART1_RX_STA = 0;
memset(USART1_RX_buf, 0, sizeof(USART1_RX_buf));
uint8_t retry = 10;//number of AT command sending attempts
while (retry--)
{
USART1_SendWord(atcmd);
delay_ms_1(10);
for (t = 0; t < 10; t++)
{
if(USART1_RX_STA & 0x8000)
break;
delay_ms_1(5);
// k = TIM3_GetCounter();
}
if ((USART1_RX_STA & 0x8000))//receive the data
{
tag = USART1_RX_STA & 0x7FFF;//get the length of data
USART1_RX_STA = 0;//clear the state flag
if ((('O' == USART1_RX_buf[tag-4]) &&
('K' == USART1_RX_buf[tag-3])) ||
(('o' == USART1_RX_buf[tag-4]) &&
('k' == USART1_RX_buf[tag-3])))
{
tag = 0;//enter succeed
break;
}
}
}
//clear the rx buffer
USART1_RX_STA = 0;
memset(USART1_RX_buf, 0, sizeof(USART1_RX_buf));
if(0 == retry)tag = 1;//enter failed
return tag;
}
- MESH組網命令
有了前面的鋪墊,寫一個函式來一鍵組網就輕而易舉啦!
這裡值得注意的是,進入組網後,藍芽模組會重啟!但至於重啟的時間,比較玄學,我也不清楚。這裡偷懶了,因為有10臺裝置,乾脆寫了10條AT指令,程式碼比較不優雅。:)
void MESH_cmd(FunctionalState NewState)
{
assert_param(IS_FUNCTIONAL_STATE(NewState));
uint8_t i = 0;
if (ENABLE == NewState)
{
if (0 == AT_Send("+++a"))//Enter AT Mode
{
// delay_ms_1(100);
#if (1 == DEVICE_ID)
AT_Send("AT+NAME=N1\r\n");//NODE 1 in MESH
// delay_ms_1(100);
#endif
#if (2 == DEVICE_ID)
AT_Send("AT+NAME=N2\r\n");//NODE 2 in MESH
// delay_ms_1(100);
#endif
#if (3 == DEVICE_ID)
AT_Send("AT+NAME=N3\r\n");//NODE 3 in MESH
// delay_ms_1(100);
#endif
#if (4 == DEVICE_ID)
AT_Send("AT+NAME=N4\r\n");//NODE 4 in MESH
// delay_ms_1(100);
#endif
#if (5 == DEVICE_ID)
AT_Send("AT+NAME=N5\r\n");//NODE 5 in MESH
// delay_ms_1(100);
#endif
#if (6 == DEVICE_ID)
AT_Send("AT+NAME=N6\r\n");//NODE 6 in MESH
// delay_ms_1(100);
#endif
#if (7 == DEVICE_ID)
AT_Send("AT+NAME=N7\r\n");//NODE 7 in MESH
// delay_ms_1(100);
#endif
#if (8 == DEVICE_ID)
AT_Send("AT+NAME=N8\r\n");//NODE 8 in MESH
// delay_ms_1(100);
#endif
#if (9 == DEVICE_ID)
AT_Send("AT+NAME=N9\r\n");//NODE 9 in MESH
// delay_ms_1(100);
#endif
#if (10 == DEVICE_ID)
AT_Send("AT+NAME=N10\r\n");//NODE 10 in MESH
// delay_ms_1(100);
#endif
AT_Send("AT+PASS=111111\r\n");//Set matching password: 111111
// delay_ms_1(100);
while(0 != AT_Send("AT+MODE=F\r\n"))//Enter AT Mode
{
if ((i++) >= 5)
{
i = 0;
USART1_SendWord("Enter MESH error...\r\n");
beep_play(E_BEEP_MODE_ERR);
memset(USART1_RX_buf, 0, sizeof(USART1_RX_buf));
return;
}
}
USART1_SendWord("Enter MESH successfully...\r\n");
beep_play(E_BEEP_MODE_SUCCESS);
memset(USART1_RX_buf, 0, sizeof(USART1_RX_buf));
}
else
{
USART1_SendWord("MESH ERROR...\r\n");
}
}
if (DISABLE == NewState)
{
if (0 == AT_Send("+++a"))//Enter AT Mode
{
#if (1 == DEVICE_ID)
AT_Send("AT+NAME=D1\r\n");//DEVICE 1 out MESH
#endif
#if (2 == DEVICE_ID)
AT_Send("AT+NAME=D2\r\n");//DEVICE 2 out MESH
#endif
#if (3 == DEVICE_ID)
AT_Send("AT+NAME=D3\r\n");//DEVICE 3 out MESH
#endif
#if (4 == DEVICE_ID)
AT_Send("AT+NAME=D4\r\n");//DEVICE 4 out MESH
#endif
#if (5 == DEVICE_ID)
AT_Send("AT+NAME=D5\r\n");//DEVICE 5 out MESH
#endif
#if (6 == DEVICE_ID)
AT_Send("AT+NAME=D6\r\n");//DEVICE 6 out MESH
#endif
#if (7 == DEVICE_ID)
AT_Send("AT+NAME=D7\r\n");//DEVICE 7 out MESH
#endif
#if (8 == DEVICE_ID)
AT_Send("AT+NAME=D8\r\n");//DEVICE 8 out MESH
#endif
#if (9 == DEVICE_ID)
AT_Send("AT+NAME=D9\r\n");//DEVICE 9 out MESH
#endif
#if (10 == DEVICE_ID)
AT_Send("AT+NAME=D10\r\n");//DEVICE 10 out MESH
#endif
AT_Send("AT+MODE=S\r\n");//mode:Slave
AT_Send("AT+UARTTM=2\r\n");//data packing time: 20ms
while(AT_Send("AT+ENTM\r\n"))//Exit AT Mode
{
if ((i++) >= 5)
{
i = 0;
USART1_SendWord("Exit MESH error...\r\n");
beep_play(E_BEEP_MODE_ERR);
return;
}
}
USART1_SendWord("Exit MESH successfully...\r\n");
beep_play(E_BEEP_MODE_SUCCESS);
}
else
USART1_SendWord("NON-MESH ERROR...\r\n");
}
}
- 手機通過中繼控制網路
實際上官方文件中並沒有提供藍芽組網後的中繼功能,所以本實驗採用的是模式切換的方法。(在組網模式和從裝置模式之間反覆橫跳)
volatile uint8_t ctrl_string[] = "::000000000";//used to control LED group
/**
Non-MESH mode
NAME: Dx >>>Device x (x=1, 2, 3,...)
LED MODE: Flashing LEDG in freq 1000ms
KEY MODE: Enter MESH Mode through short press(<3s)
LINK PROC: Flashing LEDR in freq 100ms(NU)
LINK OK: The LEDR(G) flash alternately for 1.5s with beep play
LINK ERR: The buzzer beeps for 2s and the LEDR is on
MESH mode
NAME: Nx >>>Node x (x=1, 2, 3,...)
LED MODE: Flashing LEDR in freq 1000ms
KEY MODE: Exit MESH Mode through long press(>3s)
DISLINK PROC: Flashing LEDG in freq 100ms(NU)
DISLINK OK: The LEDR(G) flash alternately for 1.5s with beep play
DISLINK ERR: The buzzer beeps for 2s and the LEDG is on
**/
#if (RELAY_DEV == DEVICE_ID)
if(0 == BLE_STA_flag)//process msg when enter mesh mode
{
delay_ms_1(100);
//init
memset(USART1_RX_buf, 0, sizeof(USART1_RX_buf));
USART1_RX_STA = 0;
//process
uint8_t *temp_string;//intermediate variables
temp_string = (uint8_t *)ctrl_string;
USART1_SendWord(temp_string);
delay_ms_1(500);
MESH_cmd(DISABLE);
BLE_status_it();
}
if((USART1_RX_STA & (uint16_t)(1<<15)) == 0)//No message
return;
if(('0' != USART1_RX_buf[1]) && ('1' != USART1_RX_buf[1]))//non-phone message
{
memset(USART1_RX_buf, 0, sizeof(USART1_RX_buf));
USART1_RX_STA = 0;
return;
}
if((USART1_RX_STA & (uint16_t)(1<<15)) == 0x8000)//Get phone message
{
if (0 != (USART1_RX_STA & 0X0001))//invaild cmd
{
USART1_SendWord("Invaild message...");
memset(USART1_RX_buf, 0, sizeof(USART1_RX_buf));
USART1_RX_STA = 0;
return;
}
uint8_t id;//get the id
uint8_t sta;//get the status
uint16_t i = 0;
while (i < (USART1_RX_STA & 0x7fff))
{
id = USART1_RX_buf[i];
sta = USART1_RX_buf[i+1];
id -= 48;//ascii code offset compasation
ctrl_string[id+1] = sta;
i += 2;
}
memset(USART1_RX_buf, 0, sizeof(USART1_RX_buf));
USART1_RX_STA = 0;
AT_Send("+++a");
AT_Send("AT+DISCONN\r\n");
AT_Send("AT+ENTM\r\n");
delay_ms_1(100);
MESH_cmd(ENABLE);
BLE_status_it();
}
#endif
- 手機查詢節點資訊
void node_info_query(void)
{
uint8_t *temp_string;//intermediate variables
// uint8_t *node_info_string = "N2: 0\r\nN3: 0\r\nN4: 0\r\nN5: 0\r\nN6: 0\r\nN7: 0\r\nN8: 0\r\nN9: 0\r\n";//record the status of node
uint8_t k = 4, j = 0;
temp_string = (uint8_t *)ctrl_string;
temp_string += 3;
uint8_t i =strlen(temp_string);
i *= 7;
uint8_t *node_info_string = (uint8_t *)malloc(i+1);//record the status of node
memset(node_info_string, 0, sizeof(node_info_string));
for (i = 0; i < (strlen(temp_string) * 7); i+=7)
{
node_info_string[i] = 'N';
node_info_string[i+1] = '2' + j;
node_info_string[i+2] = ':';
node_info_string[i+3] = ' ';
node_info_string[i+4] = '0';
node_info_string[i+5] = '\r';
node_info_string[i+6] = '\n';
j++;
}
node_info_string+=4;
*node_info_string = *temp_string;
temp_string++;
while (*temp_string)
{
node_info_string+=7;
*node_info_string = *temp_string;
k+=7;
temp_string++;
}
node_info_string-=k;
data_packet_process(node_info_string);
free(node_info_string);//release the rom
node_info_string = NULL;
}
結語
模組簡單易上手,但是也存在許多坑。組網模式下的藍芽機制資料過少,在處理大位元組包時重傳機制可能會造成丟包,追尾。藍芽模式的轉換需要預留時間,否則無法正常發包。