本文章介紹在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
沒有留言:
張貼留言