prettyprint

2023年6月23日 星期五

[Raspberry Pi Pico W] BLE Ep 3. BTstack Security manager, ATT read, write and notification

本文章介紹在Raspbery Pi Pico W下使用BTstack BLE功能。

實作peripheral 和central 使用LE secure connection。

central device使用ATT write來控制peripheral上的LED燈條的亮度,  peripheral device使用ATT notification告知central device現在設定led的亮度, central device將資訊顯示在TFT display上。

peripheral 和central device實作如下圖所示。


一、security manager:
每個device通知配對連線對象的IO capability如下:
  1. DisplayOnly: Device can display numbers or text but cannot accept input
  2. KeyboardOnly: Device can accept text or numeric input from the user
  3. DisplayYesNo: Device allows the user to respond with YES or NO
  4. NoInputNoOutput: Device has no input or output capabilities a user can utilize
  5. KeyboardDisplay: Device has both a keyboard and a display

1. Central device SM設定:
sm_init();
// enable LE Secure Connections Only mode - disables Legacy pairing
sm_set_secure_connections_only_mode(true);
// LE Secure Connections, Numeric Comparison
sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION |SM_AUTHREQ_BONDING);
2.Peripheral device SM設定:
sm_init();
// enable LE Secure Connections Only mode - disables Legacy pairing
sm_set_secure_connections_only_mode(true);
// LE Secure Connections, Numeric Comparison
sm_set_io_capabilities(IO_CAPABILITY_KEYBOARD_ONLY);
sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION |SM_AUTHREQ_BONDING);


二、ATT read, write, notification:
本實驗使用custom 128bits UUID, ATT attributes如下所示:
  • peripheral device處理HCI, ATT and SM event相對應的handler:
  • central device端處理HCI, ATT, SM相對應的handler:
ATT handler:

  • ATT database:
Pico SDK 1.5.1後在CMake增加了pico_btstack_make_gatt_header函數將.gatt轉成.h ATT database。
  • btstack_config.h 啟用LE功能。
詳細程式碼附於文末。

三、成果展示:


四、程式碼:
  • le_peripheral.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "le_peripheral.h"
#include "pico/cyw43_arch.h"
#include "btstack.h"

#include "inttypes.h"
#include "keypad.h"
#include "ws2812.h"

static btstack_packet_callback_registration_t hci_event_callback_registration;
static btstack_packet_callback_registration_t sm_event_callback_registration;
static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void att_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);

static uint16_t led_brightness=0;
static uint8_t led_brightness_scale=10;

hci_con_handle_t con_handle;
bool le_notification_enabled=false;


const uint8_t adv_data[] = {
    // Flags general discoverable, BR/EDR not supported
    0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, 
    // Name
    0x07, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'S','M',' ','L','E','D', 

    0x11, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
    0x40, 0x88,0x12,0x7D,0x32,0x69,0x92,0x95, 0x5D,0x4E, 0xE4,0x37,0x60, 0x06,0x00, 0x00,
};
const uint8_t adv_data_len = sizeof(adv_data);

void set_led_birghness(uint16_t brightness) {

    ws2812_play(brightness, led_brightness_scale);
    if (le_notification_enabled) {
        att_server_request_can_send_now_event(con_handle);

    }

}
uint32_t get_passkey() {
    uint8_t passkey[6];
    char *pt;
    uint8_t one_digit;
    uint8_t pos=0;
    absolute_time_t timeout = get_absolute_time();
    while (1) {
        one_digit=get_new_keypad_value();
        if(one_digit) {
            passkey[pos++] = one_digit;
            printf("%c", one_digit);
            if (pos >=6) break;
        }
        busy_wait_ms(1);
        if (absolute_time_diff_us(timeout, get_absolute_time())> 15000000U) break;
    }
    //passkey[6]='0';
    //printf("passkey:%u\n", atol(passkey));
    return strtoul(passkey, &pt, 10);
}

static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
    if (att_handle == ATT_CHARACTERISTIC_00000661_37E4_4E5D_9592_69327D128840_01_VALUE_HANDLE){          
            uint16_t ret = att_read_callback_handle_little_endian_16(led_brightness, offset, buffer, buffer_size);
            return ret;
    }
    
    return 0;
}


static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
    UNUSED(transaction_mode);
    UNUSED(offset);
    UNUSED(buffer_size);

    if (att_handle == ATT_CHARACTERISTIC_00000662_37E4_4E5D_9592_69327D128840_01_VALUE_HANDLE) {
        led_brightness = little_endian_read_16(buffer, 0);
        set_led_birghness(led_brightness);
        return sizeof(uint16_t);
    }
    if (att_handle == ATT_CHARACTERISTIC_00000663_37E4_4E5D_9592_69327D128840_01_VALUE_HANDLE) {
        led_brightness_scale = little_endian_read_16(buffer, 0);
        //printf("led_brightness_scale:%d\n", led_brightness_scale);
        return sizeof(uint16_t);
    }

    if (att_handle == ATT_CHARACTERISTIC_00000661_37E4_4E5D_9592_69327D128840_01_CLIENT_CONFIGURATION_HANDLE) {
            le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
            con_handle = connection_handle;
            att_server_request_can_send_now_event(con_handle);
    }
    return 0;
}
static void att_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
    UNUSED(channel);
    UNUSED(size);

    if (packet_type != HCI_EVENT_PACKET) return;

    switch (hci_event_packet_get_type(packet)) {
        
        case ATT_EVENT_CAN_SEND_NOW:   

            if (att_server_notify(con_handle, ATT_CHARACTERISTIC_00000661_37E4_4E5D_9592_69327D128840_01_VALUE_HANDLE, 
                        (uint8_t*)&led_brightness, 2)) {
  
            }   
            break;
        default:
            break;
    }
}
static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
    UNUSED(channel);
    UNUSED(size);

    if (packet_type != HCI_EVENT_PACKET) return;
    hci_con_handle_t con_handle;
    uint8_t status;

    switch (hci_event_packet_get_type(packet)) {
        case BTSTACK_EVENT_STATE:
            // BTstack activated, get started
            if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){
                
            }
            break;
        
        case HCI_EVENT_LE_META:
            // wait for connection complete
            if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
            con_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
            //sm_bonding_decline(con_handle);
            sm_request_pairing(con_handle);
            //gap_advertisements_enable(false);
            break;
        
        case HCI_EVENT_DISCONNECTION_COMPLETE:
            le_notification_enabled=false;
            printf("disconnect...\n");
            //gap_advertisements_enable(true);
            break;
        default:
            break;
    }
}


static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
    UNUSED(channel);
    UNUSED(size);

    if (packet_type != HCI_EVENT_PACKET) return;

    bd_addr_t addr;
    bd_addr_type_t addr_type;

    switch (hci_event_packet_get_type(packet)) {
        case SM_EVENT_JUST_WORKS_REQUEST:
            printf("Just works requested\n");
            sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
            break;
        case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
            printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
            sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
            break;
        case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
            printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
            break;
        case SM_EVENT_PASSKEY_INPUT_NUMBER:
            printf("Passkey Input requested\n");
            uint32_t passkey;
            passkey = get_passkey();
            printf("Sending fixed passkey %"PRIu32"\n", passkey);
            sm_passkey_input(sm_event_passkey_input_number_get_handle(packet), passkey);
            break;
        case SM_EVENT_PAIRING_STARTED:
            printf("Pairing started\n");
            break;
        case SM_EVENT_PAIRING_COMPLETE:
            switch (sm_event_pairing_complete_get_status(packet)){
                case ERROR_CODE_SUCCESS:
                    printf("Pairing complete, success\n");
                    break;
                case ERROR_CODE_CONNECTION_TIMEOUT:
                    printf("Pairing failed, timeout\n");
                    break;
                case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
                    printf("Pairing failed, disconnected\n");
                    break;
                case ERROR_CODE_AUTHENTICATION_FAILURE:
                    printf("Pairing failed, authentication failure with reason = %u\n", sm_event_pairing_complete_get_reason(packet));
                    break;
                default:
                    break;
            }
            break;
        case SM_EVENT_REENCRYPTION_STARTED:
            sm_event_reencryption_complete_get_address(packet, addr);
            printf("Bonding information exists for addr type %u, identity addr %s -> start re-encryption\n",
                   sm_event_reencryption_started_get_addr_type(packet), bd_addr_to_str(addr));
            break;
        case SM_EVENT_REENCRYPTION_COMPLETE:
            switch (sm_event_reencryption_complete_get_status(packet)){
                case ERROR_CODE_SUCCESS:
                    printf("Re-encryption complete, success\n");
                    break;
                case ERROR_CODE_CONNECTION_TIMEOUT:
                    printf("Re-encryption failed, timeout\n");
                    break;
                case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
                    printf("Re-encryption failed, disconnected\n");
                    sm_event_reencryption_complete_get_address(packet, addr);
                    addr_type = sm_event_reencryption_started_get_addr_type(packet);
                    gap_delete_bonding(addr_type, addr);
                    sm_request_pairing(sm_event_reencryption_complete_get_handle(packet));
                    break;
                case ERROR_CODE_PIN_OR_KEY_MISSING:
                    printf("Re-encryption failed, bonding information missing\n\n");
                    printf("Assuming remote lost bonding information\n");
                    printf("Deleting local bonding information and start new pairing...\n");
                    sm_event_reencryption_complete_get_address(packet, addr);
                    addr_type = sm_event_reencryption_started_get_addr_type(packet);
                    gap_delete_bonding(addr_type, addr);
                    sm_request_pairing(sm_event_reencryption_complete_get_handle(packet));
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }
}

int main()
{
    stdio_init_all();

    //sleep_ms(5000);

    if(cyw43_arch_init()) {
        puts("cyw43_init error");
        return 0;
    }
    ws2812_init(pio0, 2, 17);
    led_brightness=0;
    

    keypad_init();
    

    l2cap_init();

    // setup SM: Display only
    sm_init();

    // setup ATT server
    att_server_init(profile_data, att_read_callback, att_write_callback);

    // setup GATT Client
    gatt_client_init();

    // register handler
    hci_event_callback_registration.callback = &hci_packet_handler;
    hci_add_event_handler(&hci_event_callback_registration);

    att_server_register_packet_handler(att_packet_handler);

    sm_event_callback_registration.callback = &sm_packet_handler;
    sm_add_event_handler(&sm_event_callback_registration);

    // enable LE Secure Connections Only mode - disables Legacy pairing
    sm_set_secure_connections_only_mode(true);

    // LE Secure Connections, Numeric Comparison
    
    sm_set_io_capabilities(IO_CAPABILITY_KEYBOARD_ONLY);
    //sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
    sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION |SM_AUTHREQ_BONDING);

    // setup advertisements
    uint16_t adv_int_min = 0x0030;
    uint16_t adv_int_max = 0x0030;
    uint8_t adv_type = 0;
    bd_addr_t null_addr;
    memset(null_addr, 0, 6);
    gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
    gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
    gap_advertisements_enable(1);

    hci_power_control(HCI_POWER_ON);
   
   while(1) {
    tight_loop_contents();
    sleep_ms(3000);
    //led_brightness++;
    //if (le_notification_enabled)
        //att_server_request_can_send_now_event(con_handle);
   }

    return 0;
}

  • le_peripheral.gatt

PRIMARY_SERVICE, GAP_SERVICE
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "SM LED Peripheral"

PRIMARY_SERVICE, GATT_SERVICE
CHARACTERISTIC, GATT_DATABASE_HASH, READ,

// led control Service
PRIMARY_SERVICE, 00000660-37E4-4E5D-9592-69327D128840
// lec control Service, Characteristic, led bright with read + notification + authentication
CHARACTERISTIC,  00000661-37E4-4E5D-9592-69327D128840, READ | NOTIFY | ENCRYPTION_KEY_SIZE_16 | DYNAMIC,
// lec control Service, Characteristic, led control bright with write + authentication
CHARACTERISTIC,  00000662-37E4-4E5D-9592-69327D128840, WRITE | ENCRYPTION_KEY_SIZE_16 | DYNAMIC,
// lec control Service, Characteristic, led control scale with write + authentication
CHARACTERISTIC,  00000663-37E4-4E5D-9592-69327D128840, WRITE | ENCRYPTION_KEY_SIZE_16 | DYNAMIC,
  • CMakeLists.txt(peripheral)

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")

set(PICO_BOARD pico_w CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
  message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()

project(le_peripheral C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(le_peripheral le_peripheral.c )

pico_set_program_name(le_peripheral "le_peripheral")
pico_set_program_version(le_peripheral "0.1")

pico_enable_stdio_uart(le_peripheral 0)
pico_enable_stdio_usb(le_peripheral 1)

# Add the standard library to the build
target_link_libraries(le_peripheral
        pico_stdlib
        pico_cyw43_arch_none
        pico_btstack_cyw43
        pico_btstack_ble)

# Add the standard include files to the build
target_include_directories(le_peripheral PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}
  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)

add_subdirectory(pico_keypad)
add_subdirectory(ws2812)
target_link_libraries(le_peripheral
        pico_keypad
        ws2812)
pico_btstack_make_gatt_header(le_peripheral PRIVATE "${CMAKE_CURRENT_LIST_DIR}/le_peripheral.gatt")
pico_add_extra_outputs(le_peripheral)


  • le_central.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "btstack.h"
#include "pico/cyw43_arch.h"
#include "pico_tft.h"
#include "fonts/font_fixedsys_mono_16.h"
#include "fonts/font_freemono_mono_bold_24.h"
#include "tft_string/tft_string.h"
#include "le_central.h"

#define BUTTON_PIN  16

#define SERVER_DEVICE_NAME "SM LED"
typedef enum {
    CLIENT_STOP=0,
    QUERY_SERVICE,
    QUERY_CHARACTERISTIC_LED_BRIGHTNESS,
    QUERY_CHARACTERISTIC_LED_SET_BRIGHTNESS,
    QUERY_CHARACTERISTIC_LED_BRIGHT_SCALE,
    ENABLE_NOTIFICATION,
    ENABLE_COMPLETE,
    CLIENT_CONNECTED,
    CLIENT_SET_LED_BRIGHTNESS
} client_event_query_t;

uint16_t led_brightness, led_brightness_scale=10;
uint8_t service_uuid128[]={
    0x00,0x00,0x06,0x60,0x37,0xE4,0x4E,0x5D,0x95,0x92,0x69,0x32,0x7D,0x12,0x88,0x40
};
uint8_t char_led_brightness_uuid128[]={
    0x00,0x00,0x06,0x61,0x37,0xE4,0x4E,0x5D,0x95,0x92,0x69,0x32,0x7D,0x12,0x88,0x40
};
uint8_t char_set_led_brightness_uuid128[]={
    0x00,0x00,0x06,0x62,0x37,0xE4,0x4E,0x5D,0x95,0x92,0x69,0x32,0x7D,0x12,0x88,0x40
};
uint8_t char_led_bright_scale_uuid128[]={
    0x00,0x00,0x06,0x63,0x37,0xE4,0x4E,0x5D,0x95,0x92,0x69,0x32,0x7D,0x12,0x88,0x40
};


client_event_query_t client_event_query;

gatt_client_notification_t notification_listener_led_birghtness;
static hci_con_handle_t connection_handler;
gatt_client_service_t led_control_service;
gatt_client_characteristic_t char_set_led_brightness, char_led_brightness, char_led_bright_scale;

static btstack_packet_callback_registration_t hci_event_callback_registration;
static btstack_packet_callback_registration_t sm_event_callback_registration;
static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);

static void tft_show_message(uint8_t *msg);
static void tft_show_passkey(uint32_t passkey);
static void tft_print_data();

uint32_t get_passkey() {
    return 123456;
}

static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
    UNUSED(channel);
    UNUSED(size);

    if (packet_type != HCI_EVENT_PACKET) return;

    bd_addr_t addr;
    bd_addr_type_t addr_type;

    switch (hci_event_packet_get_type(packet)) {
        case SM_EVENT_JUST_WORKS_REQUEST:
            printf("Just works requested\n");
            sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
            break;
        case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
            printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
            sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
            break;
        case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
            printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
            tft_show_passkey(sm_event_passkey_display_number_get_passkey(packet));
            break;
        case SM_EVENT_PASSKEY_INPUT_NUMBER:
            printf("Passkey Input requested\n");
            uint32_t passkey;
            passkey = get_passkey();
            printf("Sending fixed passkey %"PRIu32"\n", passkey);
            sm_passkey_input(sm_event_passkey_input_number_get_handle(packet), passkey);
            break;
        case SM_EVENT_PAIRING_STARTED:
            printf("Pairing started\n");
            break;
        case SM_EVENT_PAIRING_COMPLETE:
            switch (sm_event_pairing_complete_get_status(packet)){
                case ERROR_CODE_SUCCESS:
                    printf("Pairing complete, success\n");
                    tft_print_data();
                    break;
                case ERROR_CODE_CONNECTION_TIMEOUT:
                    printf("Pairing failed, timeout\n");
                    tft_show_message("Pairing failed, timeout");
                    busy_wait_ms(1000);
                    gap_disconnect(connection_handler);
                    break;
                case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
                    printf("Pairing failed, disconnected\n");
                    tft_show_message("Pairing failed, disconnected");
                    busy_wait_ms(1000);
                    gap_disconnect(connection_handler);
                    break;
                case ERROR_CODE_AUTHENTICATION_FAILURE:
                    printf("Pairing failed, authentication failure with reason = %u\n", sm_event_pairing_complete_get_reason(packet));
                    tft_show_message("authentication failure");
                    busy_wait_ms(1000);
                    gap_disconnect(connection_handler);
                    break;
                default:
                    break;
            }
            break;
        case SM_EVENT_REENCRYPTION_STARTED:
            sm_event_reencryption_complete_get_address(packet, addr);
            printf("Bonding information exists for addr type %u, identity addr %s -> start re-encryption\n",
                   sm_event_reencryption_started_get_addr_type(packet), bd_addr_to_str(addr));
            break;
        case SM_EVENT_REENCRYPTION_COMPLETE:
            switch (sm_event_reencryption_complete_get_status(packet)){
                case ERROR_CODE_SUCCESS:
                    printf("Re-encryption complete, success\n");
                    tft_print_data();
                    break;
                case ERROR_CODE_CONNECTION_TIMEOUT:
                    printf("Re-encryption failed, timeout\n");
                    tft_show_message("Re-encryption failed");
                    busy_wait_ms(1000);

                    break;
                case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
                    printf("Re-encryption failed, disconnected\n");
                    tft_show_message("Re-encryption failed");
                    busy_wait_ms(1000);

                    break;
                case ERROR_CODE_PIN_OR_KEY_MISSING:
                    printf("Re-encryption failed, bonding information missing\n\n");
                    printf("Assuming remote lost bonding information\n");
                    printf("Deleting local bonding information and start new pairing...\n");
                    tft_show_message("Re-encryption failed");
                    busy_wait_ms(1000);

                    sm_event_reencryption_complete_get_address(packet, addr);
                    addr_type = sm_event_reencryption_started_get_addr_type(packet);
                    gap_delete_bonding(addr_type, addr);
                    sm_request_pairing(sm_event_reencryption_complete_get_handle(packet));
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }
}

static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
    UNUSED(channel);
    UNUSED(size);
    bd_addr_t addr;
    bd_addr_type_t addr_type; 
    if (packet_type != HCI_EVENT_PACKET) return;
    
    uint8_t event = hci_event_packet_get_type(packet);
    switch (event) {
        case BTSTACK_EVENT_STATE:
            // BTstack activated, get started
            if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break;
            gap_set_scan_parameters(0,0x0030, 0x0030);
            gap_start_scan();
            break;
        case GAP_EVENT_ADVERTISING_REPORT:
            tft_show_message("Scan Device...");
            const uint8_t * adv_data = gap_event_advertising_report_get_data(packet);
            uint8_t         adv_len  = gap_event_advertising_report_get_data_length(packet);
            if (ad_data_contains_uuid128(adv_len, adv_data, service_uuid128)) {
                gap_event_advertising_report_get_address(packet, addr);
                addr_type = gap_event_advertising_report_get_address_type(packet);
                gap_stop_scan();
                gap_connect(addr, addr_type);
            }
            break;
        case HCI_EVENT_LE_META:
            // wait for connection complete
            if (hci_event_le_meta_get_subevent_code(packet) !=  HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
            connection_handler = hci_subevent_le_connection_complete_get_connection_handle(packet);
            // query primary services
            //sm_bonding_decline(connection_handler);
            //sm_request_pairing(connection_handler);
            client_event_query=QUERY_SERVICE;
            gatt_client_discover_primary_services_by_uuid128(handle_gatt_client_event, connection_handler,
                        service_uuid128);
            break;
        case HCI_EVENT_DISCONNECTION_COMPLETE:
            tft_show_message("Device Disconn");
            client_event_query = CLIENT_STOP;
            gap_start_scan();
            break;
        default:
            break;
    }
}
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
    UNUSED(packet_type);
    UNUSED(channel);
    UNUSED(size);
    uint16_t temp;

    switch(hci_event_packet_get_type(packet)){
        case GATT_EVENT_SERVICE_QUERY_RESULT:
            gatt_event_service_query_result_get_service(packet, &led_control_service);
        break;
        case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:

            if (client_event_query == QUERY_CHARACTERISTIC_LED_SET_BRIGHTNESS) { 
                gatt_event_characteristic_query_result_get_characteristic(packet, &char_set_led_brightness);
            }
            if (client_event_query == QUERY_CHARACTERISTIC_LED_BRIGHTNESS) { 
                gatt_event_characteristic_query_result_get_characteristic(packet, &char_led_brightness);
            }
            if (client_event_query == QUERY_CHARACTERISTIC_LED_BRIGHT_SCALE) { 
                gatt_event_characteristic_query_result_get_characteristic(packet, &char_led_bright_scale);
            }
        break;
        case GATT_EVENT_QUERY_COMPLETE:
            switch(client_event_query) {
                case QUERY_SERVICE:
                    client_event_query = QUERY_CHARACTERISTIC_LED_BRIGHTNESS;
                    gatt_client_discover_characteristics_for_service_by_uuid128(handle_gatt_client_event, connection_handler, 
                        &led_control_service, char_led_brightness_uuid128);                
                break;
                case QUERY_CHARACTERISTIC_LED_BRIGHTNESS:
                    client_event_query = QUERY_CHARACTERISTIC_LED_SET_BRIGHTNESS;
                    gatt_client_discover_characteristics_for_service_by_uuid128(handle_gatt_client_event, connection_handler, 
                        &led_control_service, char_set_led_brightness_uuid128);
                break;
                case QUERY_CHARACTERISTIC_LED_SET_BRIGHTNESS:
                    client_event_query = QUERY_CHARACTERISTIC_LED_BRIGHT_SCALE;
                    gatt_client_discover_characteristics_for_service_by_uuid128(handle_gatt_client_event, connection_handler, 
                        &led_control_service,char_led_bright_scale_uuid128);
                break;
                case QUERY_CHARACTERISTIC_LED_BRIGHT_SCALE:
                    client_event_query = ENABLE_NOTIFICATION;
                    gatt_client_listen_for_characteristic_value_updates(&notification_listener_led_birghtness, handle_gatt_client_event, 
                        connection_handler, &char_led_brightness);
                    // enable notifications
                    gatt_client_write_client_characteristic_configuration(handle_gatt_client_event, connection_handler,
                        &char_led_brightness, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);


                break;
                
                case ENABLE_NOTIFICATION:
                client_event_query = CLIENT_CONNECTED;
         
                break;
                case CLIENT_SET_LED_BRIGHTNESS:

                break;
            }
            break;
            case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
                if (char_led_brightness.value_handle == gatt_event_characteristic_value_query_result_get_value_handle(packet)) { 
                    led_brightness = little_endian_read_16(gatt_event_characteristic_value_query_result_get_value(packet), 0);

                    tft_print_data();
                    //printf("led_brightness read:%d\n", led_brightness);
                    
                }
                 
            break;
            case GATT_EVENT_NOTIFICATION:

                if (char_led_brightness.value_handle == gatt_event_notification_get_value_handle(packet)) { 
                  
                    led_brightness = little_endian_read_16(gatt_event_notification_get_value(packet), 0);
                    tft_print_data();

                    client_event_query = CLIENT_CONNECTED;
                }

            break;
            case ATT_EVENT_CAN_SEND_NOW:
            break;
        default:
            break;
    }
}

void tft_show_message(uint8_t* msg) {
    tft_fill_rect(0,0,TFT_WIDTH, TFT_HEIGHT, 0xffff);
    tft_draw_string(20,5, msg, 0xf800, &font_fixedsys_mono_16);
}

void tft_show_passkey(uint32_t passkey) {
    char ts[40];
    tft_fill_rect(0,0,TFT_WIDTH, TFT_HEIGHT, 0xffff);
    tft_draw_string(20,5, "Pairing Code", 0xf800, &font_fixedsys_mono_16);
    sprintf(ts, "%u", passkey);
    tft_draw_string(30,40, ts, 0x001f, &font_freemono_mono_bold_24);
}
void tft_print_data() {
    char ts[50];
    tft_fill_rect(0,0,TFT_WIDTH, TFT_HEIGHT, 0xffff);
    tft_draw_string(10,5, "LED Birghtness", 0xf800, &font_fixedsys_mono_16);
    tft_draw_line(4,34,155,34, 0x07e0);
    tft_draw_line(155,34,155,65, 0x07e0);
    tft_draw_line(4,65,155,65, 0x07e0);
    tft_draw_line(4,34,4,65, 0x07e0);
    tft_fill_rect(5,35,150/led_brightness_scale*led_brightness, 30, 0x001f);
    
    sprintf(ts, "%d%c ", (int)((float)led_brightness/(float)led_brightness_scale*100), '%');
    tft_draw_string(65,75, ts, 0xf800, &font_fixedsys_mono_16);
    
}
void button_pin_press_callback(uint gpio, uint32_t events) {
    if (gpio == BUTTON_PIN && client_event_query == CLIENT_CONNECTED) {
        led_brightness++;
        if (led_brightness > 10) led_brightness=0;
        client_event_query=CLIENT_SET_LED_BRIGHTNESS;
        gatt_client_write_value_of_characteristic(handle_gatt_client_event, connection_handler, char_set_led_brightness.value_handle, 2, (uint8_t*)&led_brightness);
    }
}
int main()
{
    stdio_init_all();

    //sleep_ms(5000);

    gpio_init(BUTTON_PIN);
    gpio_pull_up(BUTTON_PIN);
    gpio_set_irq_enabled_with_callback(BUTTON_PIN, GPIO_IRQ_LEVEL_LOW , true, &button_pin_press_callback);


    tft_init();
    
    if (cyw43_arch_init()) {
        printf("cyw43_init error\n");
        return 0;
    }
    client_event_query = CLIENT_STOP;
    
    l2cap_init();
    sm_init();

      // setup GATT client
    l2cap_init();

    // Initialize GATT client 
    gatt_client_init();

    // Optinoally, Setup security manager
    sm_init();

    // register for HCI events
    hci_event_callback_registration.callback = &handle_hci_event;
    hci_add_event_handler(&hci_event_callback_registration);

    sm_event_callback_registration.callback = &sm_packet_handler;
    sm_add_event_handler(&sm_event_callback_registration);
    
    // enable LE Secure Connections Only mode - disables Legacy pairing
    sm_set_secure_connections_only_mode(true);

    // LE Secure Connections, Numeric Comparison
    sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
    sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION |SM_AUTHREQ_BONDING);

    
    hci_power_control(HCI_POWER_ON);
    while(client_event_query != CLIENT_CONNECTED) {
        tight_loop_contents();
    }
    gatt_client_write_value_of_characteristic(handle_gatt_client_event, connection_handler, char_led_bright_scale.value_handle, 2, (uint8_t*)&led_brightness_scale);

    while(1) {
      

        tight_loop_contents();
    }
    

    return 0;
}

  • le_central.gatt

PRIMARY_SERVICE, GAP_SERVICE
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "SM LED Central"

PRIMARY_SERVICE, GATT_SERVICE
CHARACTERISTIC, GATT_DATABASE_HASH, READ,
  • CMakeLists.txt(central)

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")

set(PICO_BOARD pico_w CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
  message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()

project(le_central C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(le_central le_central.c )

pico_set_program_name(le_central "le_central")
pico_set_program_version(le_central "0.1")

pico_enable_stdio_uart(le_central 1)
pico_enable_stdio_usb(le_central 0)

# Add the standard library to the build
target_link_libraries(le_central
        pico_stdlib
        pico_cyw43_arch_none
        pico_btstack_cyw43
        pico_btstack_ble)

# Add the standard include files to the build
target_include_directories(le_central PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}
  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)
add_subdirectory(pico_tft)
target_link_libraries(le_central 
        pico_tft)   
pico_btstack_make_gatt_header(le_central PRIVATE "${CMAKE_CURRENT_LIST_DIR}/le_central.gatt")
pico_add_extra_outputs(le_central)
  • btstack_config.h

#ifndef _PICO_BTSTACK_BTSTACK_CONFIG_H
#define _PICO_BTSTACK_BTSTACK_CONFIG_H

// BTstack features that can be enabled
#define ENABLE_LE_PERIPHERAL
#define ENABLE_LE_CENTRAL
#define ENABLE_LE_SECURE_CONNECTIONS   ////
#define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE
#define ENABLE_LOG_INFO
#define ENABLE_LOG_ERROR
#define ENABLE_PRINTF_HEXDUMP
#define ENABLE_SCO_OVER_HCI

// BTstack configuration. buffers, sizes, ...
#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
#define MAX_NR_AVDTP_CONNECTIONS 1
#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
#define MAX_NR_AVRCP_CONNECTIONS 2
#define MAX_NR_BNEP_CHANNELS 1
#define MAX_NR_BNEP_SERVICES 1
#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES  2
#define MAX_NR_GATT_CLIENTS 1
#define MAX_NR_HCI_CONNECTIONS 2
#define MAX_NR_HID_HOST_CONNECTIONS 1
#define MAX_NR_HIDS_CLIENTS 1
#define MAX_NR_HFP_CONNECTIONS 1
#define MAX_NR_L2CAP_CHANNELS  4
#define MAX_NR_L2CAP_SERVICES  3
#define MAX_NR_RFCOMM_CHANNELS 1
#define MAX_NR_RFCOMM_MULTIPLEXERS 1
#define MAX_NR_RFCOMM_SERVICES 1
#define MAX_NR_SERVICE_RECORD_ITEMS 4
#define MAX_NR_SM_LOOKUP_ENTRIES 3
#define MAX_NR_WHITELIST_ENTRIES 16
#define MAX_NR_LE_DEVICE_DB_ENTRIES 16

// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
#define MAX_NR_CONTROLLER_ACL_BUFFERS 3
#define MAX_NR_CONTROLLER_SCO_PACKETS 3

// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
#define HCI_HOST_ACL_PACKET_LEN 1124 //1024
#define HCI_HOST_ACL_PACKET_NUM 3
#define HCI_HOST_SCO_PACKET_LEN 120
#define HCI_HOST_SCO_PACKET_NUM 3

// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
#define NVM_NUM_DEVICE_DB_ENTRIES 16
#define NVM_NUM_LINK_KEYS 16

// We don't give btstack a malloc, so use a fixed-size ATT DB.
#define MAX_ATT_DB_SIZE 512

// BTstack HAL configuration
#define HAVE_EMBEDDED_TIME_MS

// map btstack_assert onto Pico SDK assert()
#define HAVE_ASSERT

// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
#define HCI_RESET_RESEND_TIMEOUT_MS 1000

#define ENABLE_SOFTWARE_AES128
#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS

//#define HAVE_BTSTACK_STDIN

// To get the audio demos working even with HCI dump at 115200, this truncates long ACL packetws
//#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100

#ifdef ENABLE_CLASSIC
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
#endif

#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H

  

沒有留言:

張貼留言