本文章介紹在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如下:
- DisplayOnly: Device can display numbers or text but cannot accept input
- KeyboardOnly: Device can accept text or numeric input from the user
- DisplayYesNo: Device allows the user to respond with YES or NO
- NoInputNoOutput: Device has no input or output capabilities a user can utilize
- 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);
本實驗使用custom 128bits UUID, ATT attributes如下所示:
- peripheral device處理HCI, ATT and SM event相對應的handler:
- 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(¬ification_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
沒有留言:
張貼留言