prettyprint

2022年4月15日 星期五

ESP-IDF: Using ESP-IDF Classic BT SPP API to implement ESP32 Bluetooth SPP Server and Client devices

 本實驗使用ESP-IDF Classic BT API來製作含有5個藍芽設備形成的piconet,一個ESP32-WROOM-32作為Master,兩個ESP32(ESP32-S, ESP32-CAM)為Slave,一台Android phone 為Slave執行Bluetooth Serial Terminal當作控制台,即時接受piconet中元件連線狀態,另外一台筆電為Slave執行Bluetooth Serial Terminal。Slave可向Master發送控制命令,或藉由Master向其他Slave發送控制命令或文字訊息。

本實驗共訂出5個指令類型:

  1. AT+ST<device_name>:向Master註冊自已的名稱<device_name>,例如:AT+STP0
  2. AT+LS: 列出已連線的Slaves,,例如:AT+LS。
  3. AT+CMD<target_device_name:command>:向其他device送出命令,例如:AT+CMDC0:ON
  4. AT+TXT<target_device_name:TEXT>:向其他device送出Text,例如:AT+TXTT0:TEST
  5. AT+DIS: 向Master送出離線訊息,,例如:AT+DIS。
本實驗Master名稱固定為M0, 擔任控制台的Slave指定為P0。如下圖所示:


一、成果展示影片:



二、使用軟體環境:

  1. VSCode
  2. ESP-IDF extension
  3. Bluetooth serial terminal for Android and for Windows

三、ESP-IDF Classic BT API - GAP & SPP:

SPP Bluetooth Stack:
BT GAP API提供設備間配對連線等,BT SPP API提供Serial Port Service(Server or client)等。
ESP32 Master與Slave端程式碼大同小異,一端負責提供SPP Service Server,另一端為SPP Client。
有關ESP藍芽架構主要參考文件為下列官方網址:

GAP與SPP以register callback function 來處理相對應的Event,API相關文件說明如下連結。

詳細程式碼如下所示,完整程式說明請觀看成果展示影片。

四、程式碼:

1. Server端(Run at Master side)
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_bt_defs.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_spp_api.h"
#include "esp_err.h"
#include "esp_log.h"
#include "string.h"
#include "driver/gpio.h"


#define TAG "MY_SPP_MASTER"
#define BT_DEVICE_NAME  "MY_SPP_MASTER"
#define SPP_SERVER_1 "MY_SPP_SERVER"
#define CLIENT_NAME_MAX 10

QueueHandle_t spp_queue_handle;
char rmt_bd_name[ESP_BT_GAP_MAX_BDNAME_LEN+1];
// slave device link list
typedef struct _tbd{
    uint32_t handle;
    uint8_t bd_client_name[CLIENT_NAME_MAX+1];
    uint8_t len;
    struct _tbd *next;
} bd_client_t;
bd_client_t *bd_link_header=NULL;

//input data item
typedef struct {
    uint32_t handle;
    uint16_t len;
    uint8_t in_data[ESP_SPP_MAX_MTU];
} spp_queue_item_t;
spp_queue_item_t spp_queue_item;

bool pannel_connected=false;
uint32_t pannel_handle = 0;

void my_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) {
    switch(event) {
        case ESP_BT_GAP_DISC_RES_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_DISC_RES_EVT");
        break;
        case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_DISC_STATE_CHANGED_EVT");
        break;
        case ESP_BT_GAP_RMT_SRVCS_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_RMT_SRVCS_EVT");
        break; 
        case ESP_BT_GAP_RMT_SRVC_REC_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_RMT_SRVC_REC_EVT");
        break;
        case ESP_BT_GAP_AUTH_CMPL_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_AUTH_CMPL_EVT");
            if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
                ESP_LOGI(TAG, "bda:%x%x%x%x%x%x, device_name:%s--", 
                param->auth_cmpl.bda[0],param->auth_cmpl.bda[1],param->auth_cmpl.bda[2],param->auth_cmpl.bda[3],param->auth_cmpl.bda[4],param->auth_cmpl.bda[5],
                (char*)param->auth_cmpl.device_name);
            }
        break;
        case ESP_BT_GAP_PIN_REQ_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_PIN_REQ_EVT");
        break;
        case ESP_BT_GAP_CFM_REQ_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_CFM_REQ_EVT");
            esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
        break;
        case ESP_BT_GAP_KEY_NOTIF_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_KEY_NOTIF_EVT");
        break;
        case ESP_BT_GAP_KEY_REQ_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_KEY_REQ_EVT");
        break;
        case ESP_BT_GAP_READ_RSSI_DELTA_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_READ_RSSI_DELTA_EVT");
        break;
        case ESP_BT_GAP_CONFIG_EIR_DATA_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_CONFIG_EIR_DATA_EVT");
        break;
        case ESP_BT_GAP_SET_AFH_CHANNELS_EVT:
            ESP_LOGI(TAG, "");
        break;
        case ESP_BT_GAP_READ_REMOTE_NAME_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_READ_REMOTE_NAME_EVT");
        break;
        case ESP_BT_GAP_MODE_CHG_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_MODE_CHG_EVT");
        break;
        case ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT");
        break;
        case ESP_BT_GAP_QOS_CMPL_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_QOS_CMPL_EVT");
        break;
        default:
        break;
    }
}
char msg[100];
char temp_name[30];
void my_bt_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
    switch(event) {
        case ESP_SPP_INIT_EVT:
            ESP_LOGI(TAG, "ESP_SPP_INIT_EVT");
        break;
        case ESP_SPP_UNINIT_EVT:
            ESP_LOGI(TAG, "ESP_SPP_UNINIT_EVT");
        break;
        case ESP_SPP_DISCOVERY_COMP_EVT:
            ESP_LOGI(TAG, "ESP_SPP_DISCOVERY_COMP_EVT");
        break;
        case ESP_SPP_OPEN_EVT:
            ESP_LOGI(TAG,"ESP_SPP_OPEN_EVT");
        break;
        case ESP_SPP_CLOSE_EVT:
            ESP_LOGI(TAG, "close handle:%d, status:%d", param->close.handle, param->close.status);
            spp_queue_item.handle = param->close.handle;
            spp_queue_item.len = 6;
            memcpy(spp_queue_item.in_data, (uint8_t*)"AT+DIS", 6);
            xQueueSend(spp_queue_handle, &spp_queue_item, portMAX_DELAY);
        break;
        case ESP_SPP_START_EVT:
            ESP_LOGI(TAG, "SPP_START_EVENT:status:%d, scn:%d", param->start.status, param->start.scn);
        break;
        case ESP_SPP_CL_INIT_EVT:
            ESP_LOGI(TAG, "ESP_SPP_CL_INIT_EVT");
        break;
        case ESP_SPP_DATA_IND_EVT:
            spp_queue_item.handle = param->data_ind.handle;
            spp_queue_item.len = param->data_ind.len;
            memcpy(spp_queue_item.in_data, param->data_ind.data, param->data_ind.len);
            if (spp_queue_item.in_data[spp_queue_item.len-2] == (uint8_t)'\r' && spp_queue_item.in_data[spp_queue_item.len-1] == (uint8_t)'\n') {
                spp_queue_item.len -= 2;
            }
            xQueueSend(spp_queue_handle, &spp_queue_item, portMAX_DELAY);
        break;
        case ESP_SPP_CONG_EVT:
            ESP_LOGI(TAG, "cong handle:%d, cong:%d, status:%d", param->cong.handle, param->cong.cong, param->cong.status);
        break;
        case ESP_SPP_WRITE_EVT:
            ESP_LOGI(TAG, "ESP_SPP_WRITE_EVT");
        break;
        case ESP_SPP_SRV_OPEN_EVT:
            ESP_LOGI(TAG, "ESP_SPP_SRV_OPEN_EVT:rem_bda:%x:%x:%x:%x:%x:%x",
            param->srv_open.rem_bda[0],param->srv_open.rem_bda[1],param->srv_open.rem_bda[2],param->srv_open.rem_bda[3],param->srv_open.rem_bda[4],param->srv_open.rem_bda[5]);
            ESP_LOGI(TAG, "handle:%d, new handle:%d", param->srv_open.handle, param->srv_open.new_listen_handle);
        break;
        case ESP_SPP_SRV_STOP_EVT:
            ESP_LOGI(TAG, "ESP_SPP_SRV_STOP_EVT");
        break;
        default:
        break;
    }
}

void set_bd_client(spp_queue_item_t item) {
    bd_client_t *client;
    bd_client_t *end = bd_link_header;

    client = malloc(sizeof(bd_client_t));
    client->handle = item.handle;
    memcpy(client->bd_client_name, item.in_data+5, item.len-5);
    if (memcmp(client->bd_client_name, (uint8_t*)"P0", item.len-5)==0) {
        pannel_connected=true;
        pannel_handle = item.handle;
    }
    client->len = item.len-5;
    client->next=NULL;
    if(end == NULL) {
        bd_link_header = client;
    } else {
        while(end) {
            if (memcmp(end->bd_client_name, item.in_data+5, item.len-5) == 0) {
                end->handle=client->handle;
                free(client);
                break;
            } else if (end->next == NULL) {
                end->next = client;
                if (pannel_handle) {
                    memcpy((uint8_t*)msg, client->bd_client_name, client->len);
                    sprintf(msg+client->len, ":Connected");
                    esp_spp_write(pannel_handle, client->len+10, (uint8_t*)msg);
                }
                break;
            }
            end = end->next;
        }
    }
}

void show_bd_connected_client(spp_queue_item_t item) {
    bd_client_t *end = bd_link_header;
    while (end) {
        esp_spp_write(item.handle, end->len, end->bd_client_name);
        vTaskDelay(20/portTICK_PERIOD_MS);
        esp_spp_write(item.handle, 2, (uint8_t*)"\r\n");
        vTaskDelay(20/portTICK_PERIOD_MS);
        end = end->next;
    }
}

void get_bd_client_name(uint32_t handle, char *name) {
    bd_client_t *end = bd_link_header;
    
    while (end) {
        if ((end->handle) == handle) {
            memcpy((uint8_t*)name, end->bd_client_name, end->len);
            name[end->len] = '\0';
            break;
        }
        end=end->next;
    }
    name=NULL;
}
void bd_send_cmd(spp_queue_item_t item) {
    bd_client_t *end = bd_link_header;
    int colon=0;
    for (int i=0; i < item.len; i++) {
        if (item.in_data[i] == (uint8_t)':') {
            colon = i;
            break;
        }
    }
    if (colon == 0) return;
    if (memcmp(item.in_data+6, (uint8_t*)"M0",colon-6)==0) { // M0 is default master device name
        if (memcmp(item.in_data+colon+1, (uint8_t*)"ON", item.len-(colon+1)) == 0) {
            gpio_set_level(GPIO_NUM_4, 1);
        }
        if (memcmp(item.in_data+colon+1, (uint8_t*)"OFF", item.len-(colon+1)) == 0) {
            gpio_set_level(GPIO_NUM_4, 0);
        }
    } else {
        while(end) {
            if (memcmp(end->bd_client_name, item.in_data+6, end->len)==0) {
                esp_spp_write(end->handle, item.len-(colon+1), item.in_data+colon+1);
                break;
            }
            end=end->next;
        }
    }
}

void bd_send_txt(spp_queue_item_t item) {
    bd_client_t *end = bd_link_header;
    int colon=0;
    for (int i=0; i < item.len; i++) {
        if (item.in_data[i] == (uint8_t)':') {
            colon = i;
            break;
        }
    }
    if (colon == 0) return;
    char name[10];
    while(end) {
        memcpy((uint8_t*)name, end->bd_client_name, end->len);
        name[end->len]='\0';
        printf("%s:%d\n", name, end->handle);
        if (memcmp(end->bd_client_name, item.in_data+6, end->len)==0) {
            esp_spp_write(end->handle, item.len-(colon+1), item.in_data+colon+1);
            break;
        }
        end=end->next;
    }
    
}

void bd_disconnect(spp_queue_item_t item) {
    bd_client_t *pre;
    bd_client_t *end = bd_link_header;
    
    get_bd_client_name(item.handle, temp_name);

    if (strcmp(temp_name, "P0")==0) {
        pannel_connected = false;
        pannel_handle=0;
    }
    pre = bd_link_header;
    while (end) {
        if (item.handle == end->handle) {
            pre->next = end->next;
            if (pannel_connected && pannel_handle) {
                memcpy((uint8_t*)msg, end->bd_client_name, end->len);
                sprintf(msg+end->len, ":Disconnected");
                esp_spp_write(pannel_handle, end->len+13, (uint8_t*)msg);
            }
            if (end == bd_link_header) {
                bd_link_header = end->next;
            } else {
                pre->next = end->next;
            }
            free(end);
            break;
        }
        pre = end;
        end = end->next;
    }
}
void QueueReceiveTask(void* param) {
    spp_queue_item_t item;
    while(1) {
        if (xQueueReceive(spp_queue_handle, &item, portMAX_DELAY) == pdTRUE) {
            item.in_data[item.len]='\0';
            if (memcmp(item.in_data, (uint8_t*)"AT+ST", 5) == 0) {
                set_bd_client(item);
            } else if (memcmp(item.in_data, (uint8_t*)"AT+LS", 5) == 0) {
                show_bd_connected_client(item);
            } else if (memcmp(item.in_data, (uint8_t*)"AT+CMD", 6) == 0) {
                bd_send_cmd(item);
            } else if (memcmp(item.in_data, (uint8_t*)"AT+TXT", 6) == 0) {
                bd_send_txt(item);
            } else if (memcmp(item.in_data, (uint8_t*)"AT+DIS", 6) == 0) {
                bd_disconnect(item);
            }
        }   
        vTaskDelay(10);
    } 
}

void app_main(void)
{
    esp_err_t err;
    err = nvs_flash_init();
    if (err != ESP_OK) {
        ESP_LOGI(TAG, "nvs flash init error");
        return;
    }
 
    gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
    gpio_set_level(GPIO_NUM_4, 0);
    spp_queue_handle =  xQueueCreate(10, sizeof(spp_queue_item_t));
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    err = esp_bt_controller_init(&bt_cfg);
    err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);

    err = esp_bluedroid_init();
    err = esp_bluedroid_enable();
    
    err = esp_spp_register_callback(my_bt_spp_cb);
    err = esp_spp_init(ESP_SPP_MODE_CB);

    err = esp_spp_start_srv(ESP_SPP_SEC_AUTHENTICATE, ESP_SPP_ROLE_MASTER, 1, SPP_SERVER_1);

    err = esp_bt_gap_register_callback(my_bt_gap_cb);
    
    esp_bt_cod_t cod;
    cod.major = 0x1;
    cod.minor=0x0;
    err = esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_MAJOR_MINOR);

    esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_NONE;
    err = esp_bt_gap_set_security_param(ESP_BT_SP_IOCAP_MODE, &iocap, sizeof(esp_bt_io_cap_t));
    err = esp_bt_dev_set_device_name(BT_DEVICE_NAME);
    err = esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);

    esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
    esp_bt_pin_code_t pin_code;
    esp_bt_gap_set_pin(pin_type, 0, pin_code);

    xTaskCreatePinnedToCore(QueueReceiveTask, "TASK", 5120, 0, 10,0,1);
 }

2. Client端(Run at Slave side)
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_spp_api.h"
#include "esp_err.h"
#include "esp_log.h"
#include "string.h"
#include "driver/gpio.h"


#define TAG "MY_SPP_C1"
#define BT_DEVICE_NAME  "MY_SPP_C1"
uint8_t client_name[7]="AT+STC1";
#define SPP_SERVER_NAME "MY_SPP_MASTER"
uint8_t *spp_peer_name;

esp_bd_addr_t spp_server_addr = {0};
uint32_t hServer=0;


QueueHandle_t spp_queue_handle;

typedef struct {
    uint32_t handle;
    uint16_t len;
    uint8_t in_data[ESP_SPP_MAX_MTU];
} spp_queue_item_t;

spp_queue_item_t spp_queue_item;

uint8_t bd_name_len;

void my_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) {
    switch(event) {
        case ESP_BT_GAP_DISC_RES_EVT:
            for (int i = 0 ; i < param->disc_res.num_prop; i++) {
                if (param->disc_res.prop[i].type == ESP_BT_GAP_DEV_PROP_EIR) {
                    spp_peer_name = esp_bt_gap_resolve_eir_data(param->disc_res.prop[i].val, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &bd_name_len);
                    if (!spp_peer_name) {
                        spp_peer_name = esp_bt_gap_resolve_eir_data(param->disc_res.prop[i].val, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &bd_name_len);
                    }
                    if (spp_peer_name) {
                        if (bd_name_len > ESP_BT_GAP_MAX_BDNAME_LEN) bd_name_len = ESP_BT_GAP_MAX_BDNAME_LEN;
                        spp_peer_name[bd_name_len]='\0';
                        // connect to default MASTER: MY_SPP_MASTER
                        if (strncmp(SPP_SERVER_NAME, (char*)spp_peer_name, bd_name_len) == 0) {
                            esp_bt_gap_cancel_discovery();
                            memcpy(spp_server_addr, param->disc_res.bda, ESP_BD_ADDR_LEN);
                            esp_spp_start_discovery(spp_server_addr);
                        }
                    } else {
                        ESP_LOGI(TAG, "GAP_DISC_RES, no peer_name, len:%d", bd_name_len);
                    }
                }
            }
        break;
        case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
            ESP_LOGI(TAG, "GAP_DISC_STATE_CHANGE");
        break;
        case ESP_BT_GAP_RMT_SRVCS_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_RMT_SRVCS_EVT");
        break; 
        case ESP_BT_GAP_RMT_SRVC_REC_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_RMT_SRVC_REC_EVT");
        break;
        case ESP_BT_GAP_AUTH_CMPL_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_AUTH_CMPL_EVT");
        break;
        case ESP_BT_GAP_PIN_REQ_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_PIN_REQ_EVT");
        break;
        case ESP_BT_GAP_CFM_REQ_EVT:
            esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
        break;
        case ESP_BT_GAP_KEY_NOTIF_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_KEY_NOTIF_EVT");
        break;
        case ESP_BT_GAP_KEY_REQ_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_KEY_REQ_EVT");
        break;
        case ESP_BT_GAP_READ_RSSI_DELTA_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_READ_RSSI_DELTA_EVT");
        break;
        case ESP_BT_GAP_CONFIG_EIR_DATA_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_CONFIG_EIR_DATA_EVT");
        break;
        case ESP_BT_GAP_SET_AFH_CHANNELS_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_SET_AFH_CHANNELS_EVT");
        break;
        case ESP_BT_GAP_READ_REMOTE_NAME_EVT:
            ESP_LOGI(TAG, "remote Name:%s",(char*)param->read_rmt_name.rmt_name);
        break;
        case ESP_BT_GAP_MODE_CHG_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_MODE_CHG_EVT");
        break;
        case ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT");
        break;
        case ESP_BT_GAP_QOS_CMPL_EVT:
            ESP_LOGI(TAG, "ESP_BT_GAP_QOS_CMPL_EVT");
        break;
        default:
        break;
    }
}

void my_bt_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
    switch(event) {
        case ESP_SPP_INIT_EVT:
            if (param->init.status == ESP_SPP_SUCCESS) {
                esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
                esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 0x10, 0);
            } else {
                ESP_LOGI(TAG, "ESP_SPP_INIT_ERROR");
            }
        break;
        case ESP_SPP_UNINIT_EVT:
        break;
        case ESP_SPP_DISCOVERY_COMP_EVT:
            // connect to spp server if found
            if (param->disc_comp.status == ESP_SPP_SUCCESS) {
                if (esp_spp_connect(ESP_SPP_SEC_AUTHORIZE, ESP_SPP_ROLE_SLAVE, 1, spp_server_addr)!=ESP_OK) {
                    ESP_LOGI(TAG, "esp_spp_connect error");
                }
            } else {
                ESP_LOGI(TAG, "ESP_SPP_DISCOVERY_COMP_EVT: not found retry...");
                esp_spp_start_discovery(spp_server_addr);
            }
        break;
        case ESP_SPP_OPEN_EVT:
             if (param->open.status == ESP_SPP_SUCCESS) {
                hServer=param->open.handle;
                esp_spp_write(param->open.handle, 7, (uint8_t*)client_name);
             } else {
                 ESP_LOGI(TAG, "CLINET OPEN- failure");
             }
        break;
        case ESP_SPP_CLOSE_EVT:
            ESP_LOGI(TAG, "close handle:%d, status:%d", param->close.handle, param->close.status);
        break;
        case ESP_SPP_START_EVT:
            ESP_LOGI(TAG, "SPP_START_EVENT:status:%d, scn:%d", param->start.status, param->start.scn);
        break;
        case ESP_SPP_CL_INIT_EVT:
            ESP_LOGI(TAG, "Clinet init");
        break;
        case ESP_SPP_DATA_IND_EVT:
            spp_queue_item.handle = param->data_ind.handle;
            spp_queue_item.len = param->data_ind.len;
            memcpy(spp_queue_item.in_data, param->data_ind.data, param->data_ind.len);
            xQueueSend(spp_queue_handle, &spp_queue_item, portMAX_DELAY);
        break;
        case ESP_SPP_CONG_EVT:
            ESP_LOGI(TAG, "cong handle:%d, cong:%d, status:%d", param->cong.handle, param->cong.cong, param->cong.status);
        break;
        case ESP_SPP_WRITE_EVT:
            ESP_LOGI(TAG, "ESP_SPP_WRITE_EVT");
        break;
        case ESP_SPP_SRV_OPEN_EVT:
        ESP_LOGI(TAG, "ESP_SPP_SRV_OPEN_EVT");
        break;
        case ESP_SPP_SRV_STOP_EVT:
        ESP_LOGI(TAG, "ESP_SPP_SRV_STOP_EVT");
        break;
        default:
        break;
    }
}

void QueueReceiveTask(void* param) {
    spp_queue_item_t item;
    while(1) {
        if (xQueueReceive(spp_queue_handle, &item, portMAX_DELAY) == pdTRUE) {
            if (memcmp(item.in_data, (uint8_t*)"ON", 2) == 0) {
                gpio_set_level(GPIO_NUM_4, 1);
            }
            if (memcmp(item.in_data, (uint8_t*)"OFF", 3) == 0) {
                gpio_set_level(GPIO_NUM_4, 0);
            }
        }   
        vTaskDelay(10);
    } 
}

void app_main(void)
{
    esp_err_t err;
    err = nvs_flash_init();
    if (err != ESP_OK) {
        ESP_LOGI(TAG, "nvs flash init error");
        return;
    }
    gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
    spp_queue_handle =  xQueueCreate(10, sizeof(spp_queue_item_t));
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    err = esp_bt_controller_init(&bt_cfg);
    err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);

    err = esp_bluedroid_init();
    err = esp_bluedroid_enable();
    
    err = esp_bt_gap_register_callback(my_bt_gap_cb);
    esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_NONE;
    err = esp_bt_gap_set_security_param(ESP_BT_SP_IOCAP_MODE, &iocap, sizeof(esp_bt_io_cap_t));
    err = esp_bt_dev_set_device_name(BT_DEVICE_NAME);
   
    err = esp_spp_register_callback(my_bt_spp_cb);
    err = esp_spp_init(ESP_SPP_MODE_CB);

    xTaskCreatePinnedToCore(QueueReceiveTask, "TASK", 4096, 0, 1,0,1);
 }

沒有留言:

張貼留言