1. 程式人生 > 其它 >WH-BLE103藍芽晶片組網測試

WH-BLE103藍芽晶片組網測試

技術標籤:藍芽微控制器藍芽

LIST


開發環境

MCU:STM8L151K4T6
藍芽模組:WH-BLE103(文末附官方資料連結)
編譯:IAR_STM8
燒錄:STVP
開發平臺:vscode
主要擴充套件:Embedded IDE、C\C++


測試過程

前言

最近想鼓搗鼓搗藍芽,之前沒有接觸過藍芽協議棧,感覺從現成的模組入手會更快一點。
選用的是WH-BLE103,支援透傳以及AT指令組網。這裡簡單科普一下,透傳是啥呢?其實就是把藍芽模組當串列埠來用,你發過來的資料是什麼,我接收到的就是什麼。而AT指令,可以快速的設定藍芽模組的各項引數。

對於WH-BLE103來說,只需要設定配對密碼和改變模式就能自行組網。

目標

  • 實現十臺裝置的組網和資訊通訊
  • 實現手機通過中繼控制/查詢網內節點裝置資訊(實驗中使用LED亮暗表示資訊)

工程與程式碼

  • 工程配置
    因為我希望組網是一個動態的過程(可以任意新增或刪除裝置),因此需要申請動態陣列,看了下memory map,我在工程中申請了一個256位元組的heap。
    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 datarecord 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;
}

結語

模組簡單易上手,但是也存在許多坑。組網模式下的藍芽機制資料過少,在處理大位元組包時重傳機制可能會造成丟包,追尾。藍芽模式的轉換需要預留時間,否則無法正常發包。


官方資料

WH-BLE103軟體手冊
WH-BLE103 MESH官方案例
STM8L151K4資料手冊
EIDE手冊