diff --git a/CMakeLists.txt b/CMakeLists.txt index a76866c0d..37cda9ff1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,3 +95,4 @@ add_subdirectory(usb) add_subdirectory(watchdog) add_subdirectory(sha) add_subdirectory(freertos) +add_subdirectory(bluetooth) diff --git a/README.md b/README.md index f43a7339a..049f59cc9 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,7 @@ These networking examples are only available if Wi-Fi is supported by the board. App|Description ---|--- [picow_access_point](pico_w/wifi/access_point) | Starts a WiFi access point, and fields DHCP requests. +[picow_access_point_wifi_provisioning](pico_w/wifi/access_point_wifi_provisioning) | Starts a WiFi access point, and allows WiFi credentials to be provisioned through a web page. [picow_blink](pico_w/wifi/blink) | Blinks the on-board LED (which is connected via the WiFi chip). [picow_blink_slow_clock](pico_w/wifi/blink) | Blinks the on-board LED (which is connected via the WiFi chip) with a slower system clock to show how to reconfigure communication with the WiFi chip at run time under those circumstances. [picow_blink_fast_clock](pico_w/wifi/blink) | Blinks the on-board LED (which is connected via the WiFi chip) with a faster system clock to show how to reconfigure communication with the WiFi chip at build time under those circumstances. @@ -295,9 +296,13 @@ Some standalone Bluetooth examples (without all the common example build infrast App|Description ---|--- -[picow_ble_temp_sensor](pico_w/bt/standalone) | Reads from the on board temperature sensor and sends notifications via BLE. -[picow_ble_temp_sensor_with_wifi](pico_w/bt/standalone) | Same as above but also connects to Wi-Fi and starts an "iperf" server. -[picow_ble_temp_reader](pico_w/bt/standalone) | Connects to one of the above "sensors" and reads the temperature. +[ble_pointer](bluetooth/ble_pointer) | Bluetooth HID mouse using mpu6050 to detect angle and move cursor. +[doorbell](bluetooth/doorbell) | Detects button press on transmitter Pico and illuminates LED on reciever Pico. +[temp_server](bluetooth/temp_sensor) | Reads from the on board temperature sensor and sends notifications via BLE. +[temp_client](bluetooth/temp_sensor) | Connects to the above temp_server and reads the temperature. +[secure_temp_server](bluetooth/secure_temp_sensor) | Variant of temp_server which allows exploration of LE_secure configurations. +[secure_temp_client](bluetooth/secure_temp_sensor) | Connects to the above secure_temp_server and reads the temperature. +[wifi_provisioner](bluetooth/wifi_provisioner) | Allows WiFi credentials to be provisioned over BLE, either using a mobile app or with the included python script. ### PIO diff --git a/bluetooth/CMakeLists.txt b/bluetooth/CMakeLists.txt new file mode 100644 index 000000000..6a7db8253 --- /dev/null +++ b/bluetooth/CMakeLists.txt @@ -0,0 +1,27 @@ +if (NOT PICO_CYW43_SUPPORTED) + return() +endif() +if (DEFINED ENV{PICO_BTSTACK_PATH} AND (NOT PICO_BTSTACK_PATH)) + set(PICO_BTSTACK_PATH $ENV{PICO_BTSTACK_PATH}) + message("Using PICO_BTSTACK_PATH from environment ('${PICO_BTSTACK_PATH}')") +endif () +set(BTSTACK_TEST_PATH "src/bluetooth.h") +if (NOT PICO_BTSTACK_PATH) + set(PICO_BTSTACK_PATH ${PICO_SDK_PATH}/lib/btstack) + if (PICO_CYW43_SUPPORTED AND NOT EXISTS ${PICO_BTSTACK_PATH}/${BTSTACK_TEST_PATH}) + message(WARNING "btstack submodule has not been initialized; Pico Bluetooth support will be unavailable. + hint: try 'git submodule update --init' from your SDK directory (${PICO_SDK_PATH}).") + endif() +elseif (NOT EXISTS ${PICO_BTSTACK_PATH}/${BTSTACK_TEST_PATH}) + message(WARNING "PICO_BTSTACK_PATH specified but content not present.") +endif() +if (NOT TARGET pico_btstack_base) + message("Skipping Pico Bluetooth examples as support is not available") +else() + add_subdirectory(btstack_examples) + add_subdirectory(secure_temp_sensor) + add_subdirectory(doorbell) + add_subdirectory(wifi_provisioner) + add_subdirectory(ble_pointer) + add_subdirectory(temp_sensor) +endif() diff --git a/pico_w/bt/README.md b/bluetooth/README.md similarity index 82% rename from pico_w/bt/README.md rename to bluetooth/README.md index 5e39b09d9..87229fe36 100644 --- a/pico_w/bt/README.md +++ b/bluetooth/README.md @@ -6,11 +6,7 @@ There's a standalone example in `pico-examples/pico_w/bt/standalone`. ## Debugging To debug Bluetooth issues you can enable [btstack](https://github.com/bluekitchen/btstack) debug output which also enables packet logging. -Define `WANT_HCI_DUMP=1` in your CMakeLists.txt file. Uncomment this line to enable debug in the btstack examples. - - target_compile_definitions(picow_bt_example_common INTERFACE - #WANT_HCI_DUMP=1 # This enables btstack debug - ) +Define `WANT_HCI_DUMP=1` in your CMakeLists.txt file. ## Packet logging diff --git a/bluetooth/ble_pointer/CMakeLists.txt b/bluetooth/ble_pointer/CMakeLists.txt new file mode 100644 index 000000000..4eb655bc6 --- /dev/null +++ b/bluetooth/ble_pointer/CMakeLists.txt @@ -0,0 +1,33 @@ +# Add mpu6050 libary +add_library(mpu6050_i2c_lib INTERFACE) +target_sources(mpu6050_i2c_lib INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/mpu6050_i2c_lib.c +) +target_include_directories(mpu6050_i2c_lib INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) +target_link_libraries(mpu6050_i2c_lib INTERFACE + pico_stdlib + hardware_i2c +) + +add_executable(ble_pointer + ble_pointer.c +) +target_link_libraries(ble_pointer + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + mpu6050_i2c_lib +) +target_include_directories(ble_pointer PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. +) +target_compile_definitions(ble_pointer PRIVATE + #WANT_HCI_DUMP=1 +) +pico_btstack_make_gatt_header(ble_pointer PRIVATE "${CMAKE_CURRENT_LIST_DIR}/ble_pointer.gatt") +pico_add_extra_outputs(ble_pointer) + diff --git a/bluetooth/ble_pointer/README.md b/bluetooth/ble_pointer/README.md new file mode 100644 index 000000000..68a504c87 --- /dev/null +++ b/bluetooth/ble_pointer/README.md @@ -0,0 +1,7 @@ +### BLE pointer + +This example is based on BTstack's 'hog_mouse_demo' and demonstrates a Bluetooth HID mouse. Cursor position is controlled by mpu6050 angle measurements, allowing you to point at the screen and move the mouse cursor. + +To use this example connect a mpu6050 (which can be found at https://thepihut.com/products/6-dof-sensor-mpu6050) to the pico, with SDA connected to pin 4 and SCL connected to pin 5. Also, connect 2 buttons to pins 15 and 16 and ground for left and right click. + +Once powered, you should be able to pair your computer with 'HID Mouse' and use the pointer. diff --git a/bluetooth/ble_pointer/ble_pointer.c b/bluetooth/ble_pointer/ble_pointer.c new file mode 100644 index 000000000..775f02f51 --- /dev/null +++ b/bluetooth/ble_pointer/ble_pointer.c @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2017 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#include +#include +#include +#include +#include + +#include "ble_pointer.h" + +#include "btstack.h" + +#include "ble/gatt-service/battery_service_server.h" +#include "ble/gatt-service/device_information_service_server.h" +#include "ble/gatt-service/hids_device.h" + +#include "pico/cyw43_arch.h" +#include "pico/btstack_cyw43.h" +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +#include "pico/binary_info.h" + +// for mpu6050 +#include +#include + +#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) +#error Example requires a board with I2C pins +#endif + +#ifndef NDEBUG +#define DEBUG_LOG(...) printf(__VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif +#define INFO_LOG printf +#define ERROR_LOG printf + +#define ALPHA 0.05 // for complimentary filter, big alpha gives faster reponse but more noise + +// FS values are 0, 1, 2, or 3 +// For gyro, correspond to += 250, 500, 1000, 2000 deg/s +// For accel, correspond to += 2, 4, 8, 16g +#define GYRO_FS 0 +#define ACCEL_FS 0 + +// how many readings taken to find gyro offsets +#define OFFSET_NUM 10000 + +// pins for buttons +#ifndef LEFT_BUTTON_GPIO_NUM +#define LEFT_BUTTON_GPIO_NUM 15 +#endif + +#ifndef RIGHT_BUTTON_GPIO_NUM +#define RIGHT_BUTTON_GPIO_NUM 16 +#endif + +#ifndef MOUSE_PERIOD_MS +#define MOUSE_PERIOD_MS 15 +#endif + +#ifndef SCREEN_WIDTH +#define SCREEN_WIDTH 1920 +#endif + +#ifndef SCREEN_HEIGHT +#define SCREEN_HEIGHT 1080 +#endif + +static float roll; +static float pitch; +static float yaw; + +static float roll_offset; +static float pitch_offset; +static float yaw_offset; + +// start in top left corner +static int abs_x = 0; +static int abs_y = 0; + +static int dx; +static int dy; +static uint8_t buttons; +static btstack_timer_source_t mousing_timer; +static int mouse_active = 0; + +// from USB HID Specification 1.1, Appendix B.2 +const uint8_t hid_descriptor_mouse_boot_mode[] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + + 0x85, 0x01, // Report ID 1 + + 0x09, 0x01, // USAGE (Pointer) + + 0xa1, 0x00, // COLLECTION (Physical) + +#if 1 + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x03, // USAGE_MAXIMUM (Button 3) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x95, 0x03, // REPORT_COUNT (3) + 0x75, 0x01, // REPORT_SIZE (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x05, // REPORT_SIZE (5) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) +#endif + +#if 1 + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x02, // REPORT_COUNT (2) + 0x81, 0x06, // INPUT (Data,Var,Rel) +#endif + + 0xc0, // END_COLLECTION + 0xc0 // END_COLLECTION +}; + +static btstack_packet_callback_registration_t hci_event_callback_registration; +static btstack_packet_callback_registration_t l2cap_event_callback_registration; +static btstack_packet_callback_registration_t sm_event_callback_registration; +static uint8_t battery = 100; +static hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID; +static uint8_t protocol_mode = 1; + +static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +const uint8_t adv_data[] = { + // Flags general discoverable, BR/EDR not supported + 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, + // Name + 0x0a, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'H', 'I', 'D', ' ', 'M', 'o', 'u', 's', 'e', + // 16-bit Service UUIDs + 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE & 0xff, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE >> 8, + // Appearance HID - Mouse (Category 15, Sub-Category 2) + 0x03, BLUETOOTH_DATA_TYPE_APPEARANCE, 0xC2, 0x03, +}; +const uint8_t adv_data_len = sizeof(adv_data); + +static void hog_mouse_setup(void){ + + // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) + if (cyw43_arch_init()) { + ERROR_LOG("failed to initialise cyw43_arch\n"); + } + + // setup l2cap + l2cap_init(); + + // setup SM: Display only + sm_init(); + sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION | SM_AUTHREQ_BONDING); + + // setup ATT server + att_server_init(profile_data, NULL, NULL); + + // setup battery service + battery_service_server_init(battery); + + // setup device information service + device_information_service_server_init(); + + // setup HID Device service + hids_device_init(0, hid_descriptor_mouse_boot_mode, sizeof(hid_descriptor_mouse_boot_mode)); + + // 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); + + // register for events + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + // register for connection parameter updates + l2cap_event_callback_registration.callback = &packet_handler; + l2cap_add_event_handler(&l2cap_event_callback_registration); + + sm_event_callback_registration.callback = &packet_handler; + sm_add_event_handler(&sm_event_callback_registration); + + hids_device_register_packet_handler(packet_handler); +} + +// HID Report sending +static void send_report(uint8_t buttons, int8_t dx, int8_t dy){ + uint8_t report[] = { buttons, (uint8_t) dx, (uint8_t) dy }; + switch (protocol_mode){ + case 0: + hids_device_send_boot_mouse_input_report(con_handle, report, sizeof(report)); + break; + case 1: + hids_device_send_input_report(con_handle, report, sizeof(report)); + break; + default: + break; + } +} + +static void mousing_timer_handler(btstack_timer_source_t * ts){ + + if (con_handle == HCI_CON_HANDLE_INVALID) { + INFO_LOG("Disabling mouse\n"); + mouse_active = 0; + return; + } + + mpu6050_fusion_output(roll_offset, pitch_offset, yaw_offset, ALPHA, MOUSE_PERIOD_MS, &roll, &pitch, &yaw, GYRO_FS); + + // move proportional to roll/yaw and pitch (if within boundary, otherwise dont move) + // int desired_x = SCREEN_WIDTH/2 + (SCREEN_WIDTH/2) * roll/90; // fully right at 90 degrees, fully left at -90 degrees + int desired_x = SCREEN_WIDTH/2 + (SCREEN_WIDTH/2) * - yaw/45; // alternative x based on yaw rather than roll (prone to drift) + int desired_y = SCREEN_HEIGHT/2 + (SCREEN_HEIGHT/2) * pitch/45; // fully top at 45 degrees, fully bottom at -45 degrees + + dx = desired_x - abs_x; + dy = desired_y - abs_y; + + if (abs_x + dx < 0 || abs_x + dx > SCREEN_WIDTH) { // about to move off side of screen + dx = 0; + } + + if (abs_y + dy < 0 || abs_y + dy > SCREEN_HEIGHT) { // about to move off top/bottom of screen + dy = 0; + } + + abs_x += dx; + abs_y += dy; + + // trigger send. We can call this from here because we are in a btstack callback + hids_device_request_can_send_now_event(con_handle); + + // set next timer + btstack_run_loop_set_timer(ts, MOUSE_PERIOD_MS); + btstack_run_loop_add_timer(ts); +} + +static void button_notification_fn(__unused void * arg) { + hids_device_request_can_send_now_event(con_handle); +} +static btstack_context_callback_registration_t button_notification = { .callback = button_notification_fn }; + +// IRQ handler for mouse buttons +void button_irq_handler(uint gpio, uint32_t events) { + if (gpio == LEFT_BUTTON_GPIO_NUM) { + INFO_LOG("Left click!\n"); + if (!gpio_get(gpio)) { + buttons = 1; + } else { + buttons = 0; + } + } else if (gpio == RIGHT_BUTTON_GPIO_NUM) { + INFO_LOG("Right click!\n"); + if (!gpio_get(gpio)) { + buttons = 2; + } else { + buttons = 0; + } + } + // We want to call hids_device_request_can_send_now_event, + // but we are in an IRQ and so it's dangerous to call BTStack directly from here. + // So we ask BTstack to callback a function from the "main thread" + // We could also use an async (when pending) worker using async_when_pending_worker_t, + // async_context_add_when_pending_worker and async_context_set_work_pending + btstack_run_loop_execute_on_main_thread(&button_notification); +} + +static void hid_embedded_start_mousing(void){ + if (mouse_active) return; + mouse_active = 1; + + INFO_LOG("Enabling mouse\n"); + + // set one-shot timer + mousing_timer.process = &mousing_timer_handler; + btstack_run_loop_set_timer(&mousing_timer, MOUSE_PERIOD_MS); + btstack_run_loop_add_timer(&mousing_timer); +} + +static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + uint16_t conn_interval; + + if (packet_type != HCI_EVENT_PACKET) return; + + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_DISCONNECTION_COMPLETE: + con_handle = HCI_CON_HANDLE_INVALID; + INFO_LOG("Disconnected\n"); + break; + case SM_EVENT_JUST_WORKS_REQUEST: + INFO_LOG("Just Works requested\n"); + sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); + break; + case SM_EVENT_NUMERIC_COMPARISON_REQUEST: + INFO_LOG("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: + INFO_LOG("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); + break; + case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE: + DEBUG_LOG("L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet)); + break; + case HCI_EVENT_META_GAP: + switch (hci_event_gap_meta_get_subevent_code(packet)) { + case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: + // print connection parameters (without using float operations) + conn_interval = gap_subevent_le_connection_complete_get_conn_interval(packet); + DEBUG_LOG("LE Connection Complete:\n"); + DEBUG_LOG("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); + DEBUG_LOG("- Connection Latency: %u\n", gap_subevent_le_connection_complete_get_conn_latency(packet)); + break; + default: + break; + } + break; + case HCI_EVENT_LE_META: + switch (hci_event_le_meta_get_subevent_code(packet)) { + case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: + // print connection parameters (without using float operations) + conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); + DEBUG_LOG("LE Connection Update:\n"); + DEBUG_LOG("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); + DEBUG_LOG("- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet)); + break; + default: + break; + } + break; + case HCI_EVENT_HIDS_META: + switch (hci_event_hids_meta_get_subevent_code(packet)){ + case HIDS_SUBEVENT_INPUT_REPORT_ENABLE: + con_handle = hids_subevent_input_report_enable_get_con_handle(packet); + DEBUG_LOG("Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet)); + hid_embedded_start_mousing(); + break; + case HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE: + con_handle = hids_subevent_boot_keyboard_input_report_enable_get_con_handle(packet); + DEBUG_LOG("Boot Keyboard Characteristic Subscribed %u\n", hids_subevent_boot_keyboard_input_report_enable_get_enable(packet)); + break; + case HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE: + con_handle = hids_subevent_boot_mouse_input_report_enable_get_con_handle(packet); + DEBUG_LOG("Boot Mouse Characteristic Subscribed %u\n", hids_subevent_boot_mouse_input_report_enable_get_enable(packet)); + break; + case HIDS_SUBEVENT_PROTOCOL_MODE: + protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet); + DEBUG_LOG("Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot"); + break; + case HIDS_SUBEVENT_CAN_SEND_NOW: + send_report(buttons, dx, dy); + break; + default: + break; + } + break; + + default: + break; + } +} + +int main(void) { + stdio_init_all(); + + // Make the I2C pins available to picotool + bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); + + // mouse buttons + gpio_init(LEFT_BUTTON_GPIO_NUM); + gpio_init(RIGHT_BUTTON_GPIO_NUM); + + gpio_set_dir(LEFT_BUTTON_GPIO_NUM, GPIO_IN); + gpio_set_dir(RIGHT_BUTTON_GPIO_NUM, GPIO_IN); + + gpio_pull_up(LEFT_BUTTON_GPIO_NUM); + gpio_pull_up(RIGHT_BUTTON_GPIO_NUM); + + gpio_set_irq_enabled_with_callback(LEFT_BUTTON_GPIO_NUM, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &button_irq_handler); + gpio_set_irq_enabled(RIGHT_BUTTON_GPIO_NUM, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); + + // First initialise mpu6050 + // initialise to min full scale(+=250deg/s and +=2g) + mpu6050_initialise(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GYRO_FS, ACCEL_FS); + mpu6050_reset(); + + // get gyro offsets + mpu6050_get_gyro_offset(OFFSET_NUM, &roll_offset, &pitch_offset, &yaw_offset, GYRO_FS); + + // get initial roll and pitch values based on accelerometer + int16_t acceleration[3], gyro[3], temp; + mpu6050_read_raw(acceleration, gyro, &temp); + roll = atan2(acceleration[1] , acceleration[2]) * 57.3; + pitch = atan2((- acceleration[0]) , sqrt(acceleration[1] * acceleration[1] + acceleration[2] * acceleration[2])) * 57.3; + + // assume yaw starts at 0 degrees (no absolute reference) + yaw = 0; + + // now setup BLE + hog_mouse_setup(); + + // turn on! + hci_power_control(HCI_POWER_ON); + + // btstack_run_loop_execute is only required when using the 'polling' method (e.g. using pico_cyw43_arch_poll library). + // This example uses the 'threadsafe background` method, where BT work is handled in a low priority IRQ, so it + // is fine to call bt_stack_run_loop_execute() but equally you can continue executing user code. + +#if 1 // this is only necessary when using polling (which we aren't, but we're showing it is still safe to call in this case) + btstack_run_loop_execute(); +#else + // this core is free to do it's own stuff except when using 'polling' method (in which case you should use + // btstacK_run_loop_ methods to add work to the run loop. + + // this is a forever loop in place of where user code would go. + while(true) { + sleep_ms(1000); + } +#endif + return 0; +} diff --git a/bluetooth/ble_pointer/ble_pointer.fzz b/bluetooth/ble_pointer/ble_pointer.fzz new file mode 100644 index 000000000..347d8a82d Binary files /dev/null and b/bluetooth/ble_pointer/ble_pointer.fzz differ diff --git a/bluetooth/ble_pointer/ble_pointer.gatt b/bluetooth/ble_pointer/ble_pointer.gatt new file mode 100644 index 000000000..b2459f553 --- /dev/null +++ b/bluetooth/ble_pointer/ble_pointer.gatt @@ -0,0 +1,48 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "HID Mouse" + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_DATABASE_HASH, READ, + +// add Battery Service +// Battery Service 180F +PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL, DYNAMIC | READ | NOTIFY, + +// add Device ID Service +// Device Information 180A +PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID, DYNAMIC | READ, + +// add HID Service +// Human Interface Device 1812 +PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE, DYNAMIC | READ | WRITE_WITHOUT_RESPONSE, + +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_REPORT, DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16, +// fixed report id = 1, type = Input (1) +REPORT_REFERENCE, READ, 1, 1 + +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_REPORT, DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE | ENCRYPTION_KEY_SIZE_16, +// fixed report id = 2, type = Output (2) +REPORT_REFERENCE, READ, 2, 2 + +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_REPORT, DYNAMIC | READ | WRITE | ENCRYPTION_KEY_SIZE_16, +// fixed report id = 3, type = Feature (3) +REPORT_REFERENCE, READ, 3, 3 + +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT, DYNAMIC | READ | WRITE | NOTIFY, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT, DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT, DYNAMIC | READ | WRITE | NOTIFY, +// bcdHID = 0x101 (v1.0.1), bCountryCode 0, remote wakeable = 0 | normally connectable 2 +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION, READ, 01 01 00 02 +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT, DYNAMIC | WRITE_WITHOUT_RESPONSE, diff --git a/bluetooth/ble_pointer/ble_pointer_bb.png b/bluetooth/ble_pointer/ble_pointer_bb.png new file mode 100644 index 000000000..14acd872e Binary files /dev/null and b/bluetooth/ble_pointer/ble_pointer_bb.png differ diff --git a/pico_w/bt/standalone/client/btstack_config.h b/bluetooth/ble_pointer/btstack_config.h similarity index 61% rename from pico_w/bt/standalone/client/btstack_config.h rename to bluetooth/ble_pointer/btstack_config.h index ce1919916..0dd0d82cb 100644 --- a/pico_w/bt/standalone/client/btstack_config.h +++ b/bluetooth/ble_pointer/btstack_config.h @@ -1,6 +1,6 @@ #ifndef _PICO_BTSTACK_CONFIG_H #define _PICO_BTSTACK_CONFIG_H -#include "btstack_config_common.h" +#include "../config/btstack_config_common.h" #endif diff --git a/bluetooth/ble_pointer/mpu6050_i2c_lib.c b/bluetooth/ble_pointer/mpu6050_i2c_lib.c new file mode 100644 index 000000000..cd767454d --- /dev/null +++ b/bluetooth/ble_pointer/mpu6050_i2c_lib.c @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "pico/stdlib.h" +#include "pico/binary_info.h" +#include "hardware/i2c.h" +#include "mpu6050_i2c_lib.h" +#include + +#define RAD_TO_DEG(r) (r) * 57.3 + +// Defualt address +static int addr = 0x68; + +void mpu6050_reset(void) { + // Two byte reset. First byte register, second byte data + // There are a load more options to set up the device in different ways that could be added here + uint8_t buf[] = {0x6B, 0x80}; + i2c_write_blocking(i2c_default, addr, buf, 2, false); + sleep_ms(100); // Allow device to reset and stabilize + + // Clear sleep mode (0x6B register, 0x00 value) + buf[1] = 0x00; // Clear sleep mode by writing 0x00 to the 0x6B register + i2c_write_blocking(i2c_default, addr, buf, 2, false); + sleep_ms(10); // Allow stabilization after waking up +} + +void mpu6050_read_raw(int16_t accel[3], int16_t gyro[3], int16_t *temp) { + // For this particular device, we send the device the register we want to read + // first, then subsequently read from the device. The register is auto incrementing + // so we don't need to keep sending the register we want, just the first. + + uint8_t buffer[6]; + + // Start reading acceleration registers from register 0x3B for 6 bytes + // accel measurments are from 0x3B to 0x40 + uint8_t val = 0x3B; + i2c_write_blocking(i2c_default, addr, &val, 1, true); // true to keep master control of bus + i2c_read_blocking(i2c_default, addr, buffer, 6, false); + + for (int i = 0; i < 3; i++) { + // register 3B contains accel_xout[15:8] + // next register contains accel_xout[7:0] + // so we bit shift 3B contents by 8 and concatinate with contents of resister 3C + // repeat for each pair of registers + accel[i] = (buffer[i * 2] << 8 | buffer[(i * 2) + 1]); + } + + // Now gyro data from reg 0x43 for 6 bytes + // The register is auto incrementing on each read + val = 0x43; + i2c_write_blocking(i2c_default, addr, &val, 1, true); + i2c_read_blocking(i2c_default, addr, buffer, 6, false); // False - finished with bus + + for (int i = 0; i < 3; i++) { + gyro[i] = (buffer[i * 2] << 8 | buffer[(i * 2) + 1]);; + } + + // Now temperature from reg 0x41 for 2 bytes + // The register is auto incrementing on each read + val = 0x41; + i2c_write_blocking(i2c_default, addr, &val, 1, true); + i2c_read_blocking(i2c_default, addr, buffer, 2, false); // False - finished with bus + + *temp = buffer[0] << 8 | buffer[1]; +} + +// FS values are 0, 1, 2, or 3 +// For gyro, correspond to += 250, 500, 1000, 2000 deg/s +// For accel, correspond to += 2, 4, 8, 16g +int mpu6050_initialise(int sda_pin, int scl_pin, int gyro_fs, int accel_fs) { + // Set the i2c pins + i2c_init(i2c_default, 400 * 1000); + gpio_set_function(sda_pin, GPIO_FUNC_I2C); + gpio_set_function(scl_pin, GPIO_FUNC_I2C); + gpio_pull_up(sda_pin); + gpio_pull_up(scl_pin); + + // set full scale ranges for gyro and accel + // register 1B is gyro config - bit 4 and bit 3 are used for full scale select + uint8_t buffer[1]; + const uint8_t gyro_config = 0x1B; + + // first read + i2c_write_blocking(i2c_default, addr, &gyro_config, 1, true); + i2c_read_blocking(i2c_default, addr, buffer, 1, false); + + uint8_t to_write = buffer[0] | ((gyro_fs & 3) << 3); // Set gyro bits + + uint8_t write_buf[2] = {gyro_config, to_write}; + i2c_write_blocking(i2c_default, addr, write_buf, 1, false); + + // register 1C is for accel config + const uint8_t accel_config = 0x1C; + + // first read + i2c_write_blocking(i2c_default, addr, &accel_config, 1, true); + i2c_read_blocking(i2c_default, addr, buffer, 1, false); + + to_write = buffer[0] | ((accel_fs & 3) << 3); // Set accel bits + + write_buf[0] = accel_config; + write_buf[1] = to_write; + i2c_write_blocking(i2c_default, addr, write_buf, 1, false); + + return 0; +} + +// function takes num sensor readings to get gyro offsets +// sets roll_offset, pitch_offset and yaw_offset +void mpu6050_get_gyro_offset(int num, float *roll_offset, float *pitch_offset, float *yaw_offset, int gyro_fs) { + float r_offset = 0; + float p_offset = 0; + float y_offset = 0; + int16_t acceleration[3], gyro[3], temp; + + // can calculate gyro full scale range from the setting using 250 * 2^setting + int range = 250 * pow(2, gyro_fs); + + const float scale_factor = 1 / 32768.0 * range; + for (int i = 0; i < num; i++) { + mpu6050_read_raw(acceleration, gyro, &temp); + r_offset += gyro[0] * scale_factor; + p_offset += gyro[1] * scale_factor; + y_offset += gyro[2] * scale_factor; + } + r_offset /= num; + p_offset /= num; + y_offset /= num; + + printf("roll offset %f\n", r_offset); + printf("pitch offset %f\n", p_offset); + printf("yaw offset is %f\n", y_offset); + + *roll_offset = r_offset; + *pitch_offset = p_offset; + *yaw_offset = y_offset; +} + +// function outputs roll, pitch using sensor fusion +void mpu6050_fusion_output(float roll_offset, float pitch_offset, float yaw_offset, float alpha, int delta_ms, float *roll, float *pitch, float *yaw, int gyro_fs) { + int16_t acceleration[3], gyro[3], temp; + mpu6050_read_raw(acceleration, gyro, &temp); + // calculate pitch and roll from accelerometer output + // this calculation is independent of full scale + float accel_roll = RAD_TO_DEG(atan2(acceleration[1] , acceleration[2])); + float accel_pitch = RAD_TO_DEG(atan2((- acceleration[0]) , hypot(acceleration[1], acceleration[2]))); + + // can calculate gyro full scale range from the setting using 250 * 2^setting + int range = 250 * pow(2, gyro_fs); + float roll_rate = gyro[0] / 32768.0 * range - roll_offset; + float pitch_rate = gyro[1] / 32768.0 * range - pitch_offset; + float yaw_rate = gyro[2] / 32768.0 * range - yaw_offset; + + // calculate pitch and roll using sensor fusion (complimentary filter) + *roll = (1 - alpha) * (*roll + roll_rate * delta_ms/1000) + alpha * accel_roll; + *pitch = (1 - alpha) * (*pitch + pitch_rate * delta_ms/1000) + alpha * accel_pitch; + *yaw = *yaw + yaw_rate * delta_ms/1000; +} diff --git a/bluetooth/ble_pointer/mpu6050_i2c_lib.h b/bluetooth/ble_pointer/mpu6050_i2c_lib.h new file mode 100644 index 000000000..e46ccb716 --- /dev/null +++ b/bluetooth/ble_pointer/mpu6050_i2c_lib.h @@ -0,0 +1,12 @@ +#include + +#ifndef MPU6050_I2C_LIB +#define MPU6050_I2C_LIB + +void mpu6050_reset(void); +void mpu6050_read_raw(int16_t accel[3], int16_t gyro[3], int16_t *temp); +int mpu6050_initialise(int SDA_pin, int SCL_pin, int GYRO_FS, int ACCEL_FS); +void mpu6050_get_gyro_offset(int num, float *roll_offset, float *pitch_offset, float *yaw_offset, int gyro_fs); +void mpu6050_fusion_output(float roll_offset, float pitch_offset, float yaw_offset, float alpha, int delta_ms, float *roll, float *pitch, float *yaw, int gyro_fs); + +#endif \ No newline at end of file diff --git a/bluetooth/btstack_examples/CMakeLists.txt b/bluetooth/btstack_examples/CMakeLists.txt new file mode 100644 index 000000000..4f97a67b1 --- /dev/null +++ b/bluetooth/btstack_examples/CMakeLists.txt @@ -0,0 +1,85 @@ +if (NOT TARGET pico_btstack_hxcmod_player) + # mod player used by a2dp_source_demo and mod_player and demo song + add_library(pico_btstack_hxcmod_player INTERFACE) + target_sources(pico_btstack_hxcmod_player INTERFACE + ${PICO_SDK_PATH}/lib/btstack/3rd-party/hxcmod-player/hxcmod.c + ${PICO_SDK_PATH}/lib/btstack/3rd-party/hxcmod-player/mods/mod.c + ) + target_include_directories(pico_btstack_hxcmod_player INTERFACE + ${PICO_SDK_PATH}/lib/btstack/3rd-party/hxcmod-player + ${PICO_SDK_PATH}/lib/btstack/3rd-party/hxcmod-player/mods + ) + target_compile_definitions(pico_btstack_hxcmod_player INTERFACE + ENABLE_MODPLAYER=1 + ) +endif() + +add_subdirectory(a2dp_source_demo) +add_subdirectory(gap_inquiry) +add_subdirectory(gap_le_advertisements) +add_subdirectory(gatt_counter) +add_subdirectory(gatt_streamer_server) +add_subdirectory(gatt_heart_rate_client) +add_subdirectory(hid_host_demo) +add_subdirectory(hid_keyboard_demo) +add_subdirectory(hid_mouse_demo) +add_subdirectory(hog_host_demo) +add_subdirectory(hog_keyboard_demo) +add_subdirectory(hog_mouse_demo) +add_subdirectory(le_streamer_client) +add_subdirectory(pbap_client_demo) +add_subdirectory(sm_pairing_central) +add_subdirectory(sm_pairing_peripheral) +add_subdirectory(spp_counter) +add_subdirectory(spp_streamer) +add_subdirectory(spp_streamer_client) +add_subdirectory(spp_streamer_with_wifi) + +# These examples connect to WiFi +# Define WIFI_SSID and WIFI_PASSWORD to enable them +if (WIFI_SSID AND WIFI_PASSWORD) + add_subdirectory(gatt_counter_with_wifi) + add_subdirectory(gatt_streamer_server_with_wifi) +endif() + +# These examples need https://github.com/raspberrypi/pico-extras +# PICO_EXTRAS_PATH should specify its location +if (PICO_EXTRAS_PATH) + add_subdirectory(a2dp_sink_demo) + if (BTSTACK_EXAMPLES_ALL) + add_subdirectory(hfp_hf_demo) + add_subdirectory(hfp_ag_demo) + add_subdirectory(hsp_ag_demo) + add_subdirectory(hsp_hs_demo) + add_subdirectory(mod_player) + add_subdirectory(sine_player) + endif() +endif() + +# Define BTSTACK_EXAMPLES_ALL=1 to enable these examples +if (BTSTACK_EXAMPLES_ALL) + add_subdirectory(ancs_client_demo) + add_subdirectory(att_delayed_response) + add_subdirectory(avrcp_browsing_client) + add_subdirectory(dut_mode_classic) + add_subdirectory(gap_dedicated_bonding) + add_subdirectory(gap_link_keys) + add_subdirectory(gatt_battery_query) + add_subdirectory(gatt_browser) + add_subdirectory(gatt_device_information_query) + add_subdirectory(hog_boot_host_demo) + add_subdirectory(le_credit_based_flow_control_mode_client) + add_subdirectory(le_credit_based_flow_control_mode_server) + add_subdirectory(le_mitm) + add_subdirectory(led_counter) + add_subdirectory(nordic_spp_le_counter) + add_subdirectory(nordic_spp_le_streamer) + add_subdirectory(pan_lwip_http_server) + add_subdirectory(sdp_bnep_query) + add_subdirectory(sdp_general_query) + add_subdirectory(sdp_rfcomm_query) + add_subdirectory(spp_and_gatt_counter) + add_subdirectory(spp_and_gatt_streamer) + add_subdirectory(spp_flowcontrol) + add_subdirectory(ublox_spp_le_counter) +endif() diff --git a/bluetooth/btstack_examples/a2dp_sink_demo/CMakeLists.txt b/bluetooth/btstack_examples/a2dp_sink_demo/CMakeLists.txt new file mode 100644 index 000000000..10d98b4e0 --- /dev/null +++ b/bluetooth/btstack_examples/a2dp_sink_demo/CMakeLists.txt @@ -0,0 +1,26 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:a2dpsinkdemoExample +add_executable(a2dp_sink_demo + ../main.c + ../btstack_audio_pico.c + ${PICO_BTSTACK_PATH}/example/a2dp_sink_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(a2dp_sink_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support + pico_btstack_sbc_decoder + pico_audio_i2s +) +target_include_directories(a2dp_sink_demo PRIVATE + . # for btstack config +) +target_compile_definitions(a2dp_sink_demo PRIVATE + USING_I2C=1 + PICO_AUDIO_I2S_DATA_PIN=9 + PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(a2dp_sink_demo) +example_auto_set_url(a2dp_sink_demo) diff --git a/bluetooth/btstack_examples/a2dp_sink_demo/btstack_config.h b/bluetooth/btstack_examples/a2dp_sink_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/a2dp_sink_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/a2dp_source_demo/CMakeLists.txt b/bluetooth/btstack_examples/a2dp_source_demo/CMakeLists.txt new file mode 100644 index 000000000..b3f3382b3 --- /dev/null +++ b/bluetooth/btstack_examples/a2dp_source_demo/CMakeLists.txt @@ -0,0 +1,23 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:a2dpsourcedemoExample +add_executable(a2dp_source_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/a2dp_source_demo.c # note: this source is in pico-sdk/lib/btstack + ${PICO_BTSTACK_PATH}/src/btstack_audio_generator.c +) +target_link_libraries(a2dp_source_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support + pico_btstack_hxcmod_player + pico_btstack_sbc_encoder +) +target_include_directories(a2dp_source_demo PRIVATE + . # for btstack config +) +target_compile_definitions(a2dp_source_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(a2dp_source_demo) +example_auto_set_url(a2dp_source_demo) diff --git a/bluetooth/btstack_examples/a2dp_source_demo/btstack_config.h b/bluetooth/btstack_examples/a2dp_source_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/a2dp_source_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/ancs_client_demo/CMakeLists.txt b/bluetooth/btstack_examples/ancs_client_demo/CMakeLists.txt new file mode 100644 index 000000000..48f9ba1c1 --- /dev/null +++ b/bluetooth/btstack_examples/ancs_client_demo/CMakeLists.txt @@ -0,0 +1,23 @@ +add_executable(ancs_client_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/ancs_client_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(ancs_client_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(ancs_client_demo PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(ancs_client_demo PRIVATE + ${PICO_BTSTACK_PATH}/example/ancs_client_demo.gatt +) +target_compile_definitions(ancs_client_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(ancs_client_demo) +example_auto_set_url(ancs_client_demo) diff --git a/bluetooth/btstack_examples/ancs_client_demo/btstack_config.h b/bluetooth/btstack_examples/ancs_client_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/ancs_client_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/att_delayed_response/CMakeLists.txt b/bluetooth/btstack_examples/att_delayed_response/CMakeLists.txt new file mode 100644 index 000000000..6b784da2d --- /dev/null +++ b/bluetooth/btstack_examples/att_delayed_response/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:attdelayedresponseExample +add_executable(att_delayed_response + ../main.c + ${PICO_BTSTACK_PATH}/example/att_delayed_response.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(att_delayed_response PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(att_delayed_response PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(att_delayed_response PRIVATE + ${PICO_BTSTACK_PATH}/example/att_delayed_response.gatt +) +target_compile_definitions(att_delayed_response PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(att_delayed_response) +example_auto_set_url(att_delayed_response) diff --git a/bluetooth/btstack_examples/att_delayed_response/btstack_config.h b/bluetooth/btstack_examples/att_delayed_response/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/att_delayed_response/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/avrcp_browsing_client/CMakeLists.txt b/bluetooth/btstack_examples/avrcp_browsing_client/CMakeLists.txt new file mode 100644 index 000000000..528cdf771 --- /dev/null +++ b/bluetooth/btstack_examples/avrcp_browsing_client/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:avrcpbrowsingclientExample +add_executable(avrcp_browsing_client + ../main.c + ${PICO_BTSTACK_PATH}/example/avrcp_browsing_client.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(avrcp_browsing_client PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(avrcp_browsing_client PRIVATE + . # for btstack config +) +target_compile_definitions(avrcp_browsing_client PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(avrcp_browsing_client) +example_auto_set_url(avrcp_browsing_client) diff --git a/bluetooth/btstack_examples/avrcp_browsing_client/btstack_config.h b/bluetooth/btstack_examples/avrcp_browsing_client/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/avrcp_browsing_client/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/pico_w/bt/btstack_audio_pico.c b/bluetooth/btstack_examples/btstack_audio_pico.c similarity index 100% rename from pico_w/bt/btstack_audio_pico.c rename to bluetooth/btstack_examples/btstack_audio_pico.c diff --git a/bluetooth/btstack_examples/dut_mode_classic/CMakeLists.txt b/bluetooth/btstack_examples/dut_mode_classic/CMakeLists.txt new file mode 100644 index 000000000..76356c7d1 --- /dev/null +++ b/bluetooth/btstack_examples/dut_mode_classic/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:dutmodeclassicExample +add_executable(dut_mode_classic + ../main.c + ${PICO_BTSTACK_PATH}/example/dut_mode_classic.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(dut_mode_classic PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(dut_mode_classic PRIVATE + . # for btstack config +) +target_compile_definitions(dut_mode_classic PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(dut_mode_classic) +example_auto_set_url(dut_mode_classic) diff --git a/bluetooth/btstack_examples/dut_mode_classic/btstack_config.h b/bluetooth/btstack_examples/dut_mode_classic/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/dut_mode_classic/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gap_dedicated_bonding/CMakeLists.txt b/bluetooth/btstack_examples/gap_dedicated_bonding/CMakeLists.txt new file mode 100644 index 000000000..940e4bd5a --- /dev/null +++ b/bluetooth/btstack_examples/gap_dedicated_bonding/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(gap_dedicated_bonding + ../main.c + ${PICO_BTSTACK_PATH}/example/gap_dedicated_bonding.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gap_dedicated_bonding PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(gap_dedicated_bonding PRIVATE + . # for btstack config +) +target_compile_definitions(gap_dedicated_bonding PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gap_dedicated_bonding) +example_auto_set_url(gap_dedicated_bonding) diff --git a/bluetooth/btstack_examples/gap_dedicated_bonding/btstack_config.h b/bluetooth/btstack_examples/gap_dedicated_bonding/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gap_dedicated_bonding/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gap_inquiry/CMakeLists.txt b/bluetooth/btstack_examples/gap_inquiry/CMakeLists.txt new file mode 100644 index 000000000..bdc0add90 --- /dev/null +++ b/bluetooth/btstack_examples/gap_inquiry/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gapinquiryExample +add_executable(gap_inquiry + ../main.c + ${PICO_BTSTACK_PATH}/example/gap_inquiry.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gap_inquiry PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(gap_inquiry PRIVATE + . # for btstack config +) +target_compile_definitions(gap_inquiry PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gap_inquiry) +example_auto_set_url(gap_inquiry) diff --git a/bluetooth/btstack_examples/gap_inquiry/btstack_config.h b/bluetooth/btstack_examples/gap_inquiry/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gap_inquiry/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gap_le_advertisements/CMakeLists.txt b/bluetooth/btstack_examples/gap_le_advertisements/CMakeLists.txt new file mode 100644 index 000000000..e524afa77 --- /dev/null +++ b/bluetooth/btstack_examples/gap_le_advertisements/CMakeLists.txt @@ -0,0 +1,19 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gapleadvertisementsExample +add_executable(gap_le_advertisements + ../main.c + ${PICO_BTSTACK_PATH}/example/gap_le_advertisements.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gap_le_advertisements PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(gap_le_advertisements PRIVATE + . # for btstack config +) +target_compile_definitions(gap_le_advertisements PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gap_le_advertisements) +example_auto_set_url(gap_le_advertisements) diff --git a/bluetooth/btstack_examples/gap_le_advertisements/btstack_config.h b/bluetooth/btstack_examples/gap_le_advertisements/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gap_le_advertisements/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gap_link_keys/CMakeLists.txt b/bluetooth/btstack_examples/gap_link_keys/CMakeLists.txt new file mode 100644 index 000000000..f7f57322c --- /dev/null +++ b/bluetooth/btstack_examples/gap_link_keys/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gaplinkkeysExample +add_executable(gap_link_keys + ../main.c + ${PICO_BTSTACK_PATH}/example/gap_link_keys.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gap_link_keys PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(gap_link_keys PRIVATE + . # for btstack config +) +target_compile_definitions(gap_link_keys PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gap_link_keys) +example_auto_set_url(gap_link_keys) diff --git a/bluetooth/btstack_examples/gap_link_keys/btstack_config.h b/bluetooth/btstack_examples/gap_link_keys/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gap_link_keys/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_battery_query/CMakeLists.txt b/bluetooth/btstack_examples/gatt_battery_query/CMakeLists.txt new file mode 100644 index 000000000..3569f01ed --- /dev/null +++ b/bluetooth/btstack_examples/gatt_battery_query/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gattbatteryqueryExample +add_executable(gatt_battery_query + ../main.c + ${PICO_BTSTACK_PATH}/example/gatt_battery_query.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gatt_battery_query PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(gatt_battery_query PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(gatt_battery_query PRIVATE + ${PICO_BTSTACK_PATH}/example/gatt_battery_query.gatt +) +target_compile_definitions(gatt_battery_query PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gatt_battery_query) +example_auto_set_url(gatt_battery_query) diff --git a/bluetooth/btstack_examples/gatt_battery_query/btstack_config.h b/bluetooth/btstack_examples/gatt_battery_query/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_battery_query/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_browser/CMakeLists.txt b/bluetooth/btstack_examples/gatt_browser/CMakeLists.txt new file mode 100644 index 000000000..f1ed6fd1f --- /dev/null +++ b/bluetooth/btstack_examples/gatt_browser/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gattbrowserExample +add_executable(gatt_browser + ../main.c + ${PICO_BTSTACK_PATH}/example/gatt_browser.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gatt_browser PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(gatt_browser PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(gatt_browser PRIVATE + ${PICO_BTSTACK_PATH}/example/gatt_browser.gatt +) +target_compile_definitions(gatt_browser PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gatt_browser) +example_auto_set_url(gatt_browser) diff --git a/bluetooth/btstack_examples/gatt_browser/btstack_config.h b/bluetooth/btstack_examples/gatt_browser/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_browser/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_counter/CMakeLists.txt b/bluetooth/btstack_examples/gatt_counter/CMakeLists.txt new file mode 100644 index 000000000..d2b150cc6 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_counter/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gattcounterExample +add_executable(gatt_counter + ../main.c + ${PICO_BTSTACK_PATH}/example/gatt_counter.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gatt_counter PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(gatt_counter PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(gatt_counter PRIVATE + ${PICO_BTSTACK_PATH}/example/gatt_counter.gatt +) +target_compile_definitions(gatt_counter PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gatt_counter) +example_auto_set_url(gatt_counter) diff --git a/bluetooth/btstack_examples/gatt_counter/btstack_config.h b/bluetooth/btstack_examples/gatt_counter/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_counter/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_counter_with_wifi/CMakeLists.txt b/bluetooth/btstack_examples/gatt_counter_with_wifi/CMakeLists.txt new file mode 100644 index 000000000..177ee16a5 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_counter_with_wifi/CMakeLists.txt @@ -0,0 +1,28 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gattcounterExample +add_executable(gatt_counter_with_wifi + ../main.c + ${PICO_BTSTACK_PATH}/example/gatt_counter.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gatt_counter_with_wifi PRIVATE + pico_stdlib + pico_cyw43_arch_lwip_threadsafe_background # we want lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_lwip_iperf +) +target_include_directories(gatt_counter_with_wifi PRIVATE + . # for btstack config and lwipopts.h +) +target_compile_definitions(gatt_counter_with_wifi PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + USING_IPERF=1 + #WANT_HCI_DUMP=1 +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(gatt_counter_with_wifi PRIVATE + ${PICO_BTSTACK_PATH}/example/gatt_counter.gatt +) +pico_add_extra_outputs(gatt_counter_with_wifi) +example_auto_set_url(gatt_counter_with_wifi) diff --git a/bluetooth/btstack_examples/gatt_counter_with_wifi/btstack_config.h b/bluetooth/btstack_examples/gatt_counter_with_wifi/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_counter_with_wifi/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_counter_with_wifi/lwipopts.h b/bluetooth/btstack_examples/gatt_counter_with_wifi/lwipopts.h new file mode 100644 index 000000000..8bf98aed8 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_counter_with_wifi/lwipopts.h @@ -0,0 +1 @@ +#include "../../../pico_w/wifi/lwipopts_examples_common.h" diff --git a/bluetooth/btstack_examples/gatt_device_information_query/CMakeLists.txt b/bluetooth/btstack_examples/gatt_device_information_query/CMakeLists.txt new file mode 100644 index 000000000..cbac743d1 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_device_information_query/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gattdeviceinformationqueryExample +add_executable(gatt_device_information_query + ../main.c + ${PICO_BTSTACK_PATH}/example/gatt_device_information_query.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gatt_device_information_query PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(gatt_device_information_query PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(gatt_device_information_query PRIVATE + ${PICO_BTSTACK_PATH}/example/gatt_device_information_query.gatt +) +target_compile_definitions(gatt_device_information_query PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gatt_device_information_query) +example_auto_set_url(gatt_device_information_query) diff --git a/bluetooth/btstack_examples/gatt_device_information_query/btstack_config.h b/bluetooth/btstack_examples/gatt_device_information_query/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_device_information_query/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_heart_rate_client/CMakeLists.txt b/bluetooth/btstack_examples/gatt_heart_rate_client/CMakeLists.txt new file mode 100644 index 000000000..3c21fda32 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_heart_rate_client/CMakeLists.txt @@ -0,0 +1,19 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gattheartrateclientExample +add_executable(gatt_heart_rate_client + ../main.c + ${PICO_BTSTACK_PATH}/example/gatt_heart_rate_client.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gatt_heart_rate_client PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(gatt_heart_rate_client PRIVATE + . # for btstack config +) +target_compile_definitions(gatt_heart_rate_client PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gatt_heart_rate_client) +example_auto_set_url(gatt_heart_rate_client) diff --git a/bluetooth/btstack_examples/gatt_heart_rate_client/btstack_config.h b/bluetooth/btstack_examples/gatt_heart_rate_client/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_heart_rate_client/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_streamer_server/CMakeLists.txt b/bluetooth/btstack_examples/gatt_streamer_server/CMakeLists.txt new file mode 100644 index 000000000..60f761768 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_streamer_server/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gattstreamerserverExample +add_executable(gatt_streamer_server + ../main.c + ${PICO_BTSTACK_PATH}/example/gatt_streamer_server.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gatt_streamer_server PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(gatt_streamer_server PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(gatt_streamer_server PRIVATE + ${PICO_BTSTACK_PATH}/example/gatt_streamer_server.gatt +) +target_compile_definitions(gatt_streamer_server PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(gatt_streamer_server) +example_auto_set_url(gatt_streamer_server) diff --git a/bluetooth/btstack_examples/gatt_streamer_server/btstack_config.h b/bluetooth/btstack_examples/gatt_streamer_server/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_streamer_server/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/CMakeLists.txt b/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/CMakeLists.txt new file mode 100644 index 000000000..04616845b --- /dev/null +++ b/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/CMakeLists.txt @@ -0,0 +1,28 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:gattstreamerserverExample +add_executable(gatt_streamer_server_with_wifi + ../main.c + ${PICO_BTSTACK_PATH}/example/gatt_streamer_server.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(gatt_streamer_server_with_wifi PRIVATE + pico_stdlib + pico_cyw43_arch_lwip_threadsafe_background # we want lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_lwip_iperf +) +target_include_directories(gatt_streamer_server_with_wifi PRIVATE + . # for btstack config and lwipopts.h +) +target_compile_definitions(gatt_streamer_server_with_wifi PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + USING_IPERF=1 + #WANT_HCI_DUMP=1 +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(gatt_streamer_server_with_wifi PRIVATE + ${PICO_BTSTACK_PATH}/example/gatt_streamer_server.gatt +) +pico_add_extra_outputs(gatt_streamer_server_with_wifi) +example_auto_set_url(gatt_streamer_server_with_wifi) diff --git a/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/btstack_config.h b/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/lwipopts.h b/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/lwipopts.h new file mode 100644 index 000000000..8bf98aed8 --- /dev/null +++ b/bluetooth/btstack_examples/gatt_streamer_server_with_wifi/lwipopts.h @@ -0,0 +1 @@ +#include "../../../pico_w/wifi/lwipopts_examples_common.h" diff --git a/bluetooth/btstack_examples/hfp_ag_demo/CMakeLists.txt b/bluetooth/btstack_examples/hfp_ag_demo/CMakeLists.txt new file mode 100644 index 000000000..04194ca5f --- /dev/null +++ b/bluetooth/btstack_examples/hfp_ag_demo/CMakeLists.txt @@ -0,0 +1,28 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hfpagdemoExample +add_executable(hfp_ag_demo + ../main.c + ../btstack_audio_pico.c + ${PICO_BTSTACK_PATH}/example/hfp_ag_demo.c # note: this source is in pico-sdk/lib/btstack + ${PICO_BTSTACK_PATH}/src/btstack_audio_generator.c + ${PICO_BTSTACK_PATH}/example/sco_demo_util.c +) +target_link_libraries(hfp_ag_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support + pico_btstack_sbc_decoder + pico_audio_i2s +) +target_include_directories(hfp_ag_demo PRIVATE + . # for btstack config +) +target_compile_definitions(hfp_ag_demo PRIVATE + USING_I2C=1 + PICO_AUDIO_I2S_DATA_PIN=9 + PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hfp_ag_demo) +example_auto_set_url(hfp_ag_demo) diff --git a/bluetooth/btstack_examples/hfp_ag_demo/btstack_config.h b/bluetooth/btstack_examples/hfp_ag_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hfp_ag_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hfp_hf_demo/CMakeLists.txt b/bluetooth/btstack_examples/hfp_hf_demo/CMakeLists.txt new file mode 100644 index 000000000..51e9c0e03 --- /dev/null +++ b/bluetooth/btstack_examples/hfp_hf_demo/CMakeLists.txt @@ -0,0 +1,27 @@ +add_executable(hfp_hf_demo + ../main.c + ../btstack_audio_pico.c + ${PICO_BTSTACK_PATH}/example/hfp_hf_demo.c # note: this source is in pico-sdk/lib/btstack + ${PICO_BTSTACK_PATH}/src/btstack_audio_generator.c + ${PICO_BTSTACK_PATH}/example/sco_demo_util.c +) +target_link_libraries(hfp_hf_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support + pico_btstack_sbc_decoder + pico_audio_i2s +) +target_include_directories(hfp_hf_demo PRIVATE + . # for btstack config +) +target_compile_definitions(hfp_hf_demo PRIVATE + USING_I2C=1 + PICO_AUDIO_I2S_DATA_PIN=9 + PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hfp_hf_demo) +example_auto_set_url(hfp_hf_demo) diff --git a/bluetooth/btstack_examples/hfp_hf_demo/btstack_config.h b/bluetooth/btstack_examples/hfp_hf_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hfp_hf_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hid_host_demo/CMakeLists.txt b/bluetooth/btstack_examples/hid_host_demo/CMakeLists.txt new file mode 100644 index 000000000..c86ad562b --- /dev/null +++ b/bluetooth/btstack_examples/hid_host_demo/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hidhostdemoExample +add_executable(hid_host_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/hid_host_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(hid_host_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(hid_host_demo PRIVATE + . # for btstack config +) +target_compile_definitions(hid_host_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hid_host_demo) +example_auto_set_url(hid_host_demo) diff --git a/bluetooth/btstack_examples/hid_host_demo/btstack_config.h b/bluetooth/btstack_examples/hid_host_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hid_host_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hid_keyboard_demo/CMakeLists.txt b/bluetooth/btstack_examples/hid_keyboard_demo/CMakeLists.txt new file mode 100644 index 000000000..6df11ec39 --- /dev/null +++ b/bluetooth/btstack_examples/hid_keyboard_demo/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hidkeyboarddemoExample +add_executable(hid_keyboard_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/hid_keyboard_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(hid_keyboard_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(hid_keyboard_demo PRIVATE + . # for btstack config +) +target_compile_definitions(hid_keyboard_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hid_keyboard_demo) +example_auto_set_url(hid_keyboard_demo) diff --git a/bluetooth/btstack_examples/hid_keyboard_demo/btstack_config.h b/bluetooth/btstack_examples/hid_keyboard_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hid_keyboard_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hid_mouse_demo/CMakeLists.txt b/bluetooth/btstack_examples/hid_mouse_demo/CMakeLists.txt new file mode 100644 index 000000000..a8da575ba --- /dev/null +++ b/bluetooth/btstack_examples/hid_mouse_demo/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hogmousedemoExample +add_executable(hid_mouse_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/hid_mouse_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(hid_mouse_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(hid_mouse_demo PRIVATE + . # for btstack config +) +target_compile_definitions(hid_mouse_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hid_mouse_demo) +example_auto_set_url(hid_mouse_demo) diff --git a/bluetooth/btstack_examples/hid_mouse_demo/btstack_config.h b/bluetooth/btstack_examples/hid_mouse_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hid_mouse_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hog_boot_host_demo/CMakeLists.txt b/bluetooth/btstack_examples/hog_boot_host_demo/CMakeLists.txt new file mode 100644 index 000000000..7acdf9218 --- /dev/null +++ b/bluetooth/btstack_examples/hog_boot_host_demo/CMakeLists.txt @@ -0,0 +1,19 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hogboothostdemoExample +add_executable(hog_boot_host_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/hog_boot_host_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(hog_boot_host_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(hog_boot_host_demo PRIVATE + . # for btstack config +) +target_compile_definitions(hog_boot_host_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hog_boot_host_demo) +example_auto_set_url(hog_boot_host_demo) diff --git a/bluetooth/btstack_examples/hog_boot_host_demo/btstack_config.h b/bluetooth/btstack_examples/hog_boot_host_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hog_boot_host_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hog_host_demo/CMakeLists.txt b/bluetooth/btstack_examples/hog_host_demo/CMakeLists.txt new file mode 100644 index 000000000..b935fb6f9 --- /dev/null +++ b/bluetooth/btstack_examples/hog_host_demo/CMakeLists.txt @@ -0,0 +1,23 @@ +add_executable(hog_host_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/hog_host_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(hog_host_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(hog_host_demo PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(hog_host_demo PRIVATE + ${PICO_BTSTACK_PATH}/example/hog_host_demo.gatt +) +target_compile_definitions(hog_host_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hog_host_demo) +example_auto_set_url(hog_host_demo) diff --git a/bluetooth/btstack_examples/hog_host_demo/btstack_config.h b/bluetooth/btstack_examples/hog_host_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hog_host_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hog_keyboard_demo/CMakeLists.txt b/bluetooth/btstack_examples/hog_keyboard_demo/CMakeLists.txt new file mode 100644 index 000000000..e00e14beb --- /dev/null +++ b/bluetooth/btstack_examples/hog_keyboard_demo/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hogkeyboarddemoExample +add_executable(hog_keyboard_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/hog_keyboard_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(hog_keyboard_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(hog_keyboard_demo PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(hog_keyboard_demo PRIVATE + ${PICO_BTSTACK_PATH}/example/hog_keyboard_demo.gatt +) +target_compile_definitions(hog_keyboard_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hog_keyboard_demo) +example_auto_set_url(hog_keyboard_demo) diff --git a/bluetooth/btstack_examples/hog_keyboard_demo/btstack_config.h b/bluetooth/btstack_examples/hog_keyboard_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hog_keyboard_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hog_mouse_demo/CMakeLists.txt b/bluetooth/btstack_examples/hog_mouse_demo/CMakeLists.txt new file mode 100644 index 000000000..e07aa92ad --- /dev/null +++ b/bluetooth/btstack_examples/hog_mouse_demo/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hogmousedemoExample +add_executable(hog_mouse_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/hog_mouse_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(hog_mouse_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(hog_mouse_demo PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(hog_mouse_demo PRIVATE + ${PICO_BTSTACK_PATH}/example/hog_mouse_demo.gatt +) +target_compile_definitions(hog_mouse_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hog_mouse_demo) +example_auto_set_url(hog_mouse_demo) diff --git a/bluetooth/btstack_examples/hog_mouse_demo/btstack_config.h b/bluetooth/btstack_examples/hog_mouse_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hog_mouse_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hsp_ag_demo/CMakeLists.txt b/bluetooth/btstack_examples/hsp_ag_demo/CMakeLists.txt new file mode 100644 index 000000000..d02702e0c --- /dev/null +++ b/bluetooth/btstack_examples/hsp_ag_demo/CMakeLists.txt @@ -0,0 +1,28 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hspagdemoExample +add_executable(hsp_ag_demo + ../main.c + ../btstack_audio_pico.c + ${PICO_BTSTACK_PATH}/example/hsp_ag_demo.c # note: this source is in pico-sdk/lib/btstack + ${PICO_BTSTACK_PATH}/src/btstack_audio_generator.c + ${PICO_BTSTACK_PATH}/example/sco_demo_util.c +) +target_link_libraries(hsp_ag_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support + pico_btstack_sbc_decoder + pico_audio_i2s +) +target_include_directories(hsp_ag_demo PRIVATE + . # for btstack config +) +target_compile_definitions(hsp_ag_demo PRIVATE + USING_I2C=1 + PICO_AUDIO_I2S_DATA_PIN=9 + PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hsp_ag_demo) +example_auto_set_url(hsp_ag_demo) diff --git a/bluetooth/btstack_examples/hsp_ag_demo/btstack_config.h b/bluetooth/btstack_examples/hsp_ag_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hsp_ag_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/hsp_hs_demo/CMakeLists.txt b/bluetooth/btstack_examples/hsp_hs_demo/CMakeLists.txt new file mode 100644 index 000000000..0ce15de8f --- /dev/null +++ b/bluetooth/btstack_examples/hsp_hs_demo/CMakeLists.txt @@ -0,0 +1,28 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:hsphsdemoExample +add_executable(hsp_hs_demo + ../main.c + ../btstack_audio_pico.c + ${PICO_BTSTACK_PATH}/example/hsp_hs_demo.c # note: this source is in pico-sdk/lib/btstack + ${PICO_BTSTACK_PATH}/src/btstack_audio_generator.c + ${PICO_BTSTACK_PATH}/example/sco_demo_util.c +) +target_link_libraries(hsp_hs_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support + pico_btstack_sbc_decoder + pico_audio_i2s +) +target_include_directories(hsp_hs_demo PRIVATE + . # for btstack config +) +target_compile_definitions(hsp_hs_demo PRIVATE + USING_I2C=1 + PICO_AUDIO_I2S_DATA_PIN=9 + PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(hsp_hs_demo) +example_auto_set_url(hsp_hs_demo) diff --git a/bluetooth/btstack_examples/hsp_hs_demo/btstack_config.h b/bluetooth/btstack_examples/hsp_hs_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/hsp_hs_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/le_credit_based_flow_control_mode_client/CMakeLists.txt b/bluetooth/btstack_examples/le_credit_based_flow_control_mode_client/CMakeLists.txt new file mode 100644 index 000000000..3067e444c --- /dev/null +++ b/bluetooth/btstack_examples/le_credit_based_flow_control_mode_client/CMakeLists.txt @@ -0,0 +1,19 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:lecreditbasedflowcontrolmodeclientExample +add_executable(le_credit_based_flow_control_mode_client + ../main.c + ${PICO_BTSTACK_PATH}/example/le_credit_based_flow_control_mode_client.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(le_credit_based_flow_control_mode_client PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(le_credit_based_flow_control_mode_client PRIVATE + . # for btstack config +) +target_compile_definitions(le_credit_based_flow_control_mode_client PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(le_credit_based_flow_control_mode_client) +example_auto_set_url(le_credit_based_flow_control_mode_client) diff --git a/bluetooth/btstack_examples/le_credit_based_flow_control_mode_client/btstack_config.h b/bluetooth/btstack_examples/le_credit_based_flow_control_mode_client/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/le_credit_based_flow_control_mode_client/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/le_credit_based_flow_control_mode_server/CMakeLists.txt b/bluetooth/btstack_examples/le_credit_based_flow_control_mode_server/CMakeLists.txt new file mode 100644 index 000000000..43c5e46c8 --- /dev/null +++ b/bluetooth/btstack_examples/le_credit_based_flow_control_mode_server/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:lecreditbasedflowcontrolmodeserverExample +add_executable(le_credit_based_flow_control_mode_server + ../main.c + ${PICO_BTSTACK_PATH}/example/le_credit_based_flow_control_mode_server.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(le_credit_based_flow_control_mode_server PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(le_credit_based_flow_control_mode_server PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(le_credit_based_flow_control_mode_server PRIVATE + ${PICO_BTSTACK_PATH}/example/le_credit_based_flow_control_mode_server.gatt +) +target_compile_definitions(le_credit_based_flow_control_mode_server PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(le_credit_based_flow_control_mode_server) +example_auto_set_url(le_credit_based_flow_control_mode_server) diff --git a/bluetooth/btstack_examples/le_credit_based_flow_control_mode_server/btstack_config.h b/bluetooth/btstack_examples/le_credit_based_flow_control_mode_server/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/le_credit_based_flow_control_mode_server/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/le_mitm/CMakeLists.txt b/bluetooth/btstack_examples/le_mitm/CMakeLists.txt new file mode 100644 index 000000000..8f31e36ce --- /dev/null +++ b/bluetooth/btstack_examples/le_mitm/CMakeLists.txt @@ -0,0 +1,19 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:lemitmExample +add_executable(le_mitm + ../main.c + ${PICO_BTSTACK_PATH}/example/le_mitm.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(le_mitm PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(le_mitm PRIVATE + . # for btstack config +) +target_compile_definitions(le_mitm PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(le_mitm) +example_auto_set_url(le_mitm) diff --git a/bluetooth/btstack_examples/le_mitm/btstack_config.h b/bluetooth/btstack_examples/le_mitm/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/le_mitm/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/le_streamer_client/CMakeLists.txt b/bluetooth/btstack_examples/le_streamer_client/CMakeLists.txt new file mode 100644 index 000000000..08758d0bd --- /dev/null +++ b/bluetooth/btstack_examples/le_streamer_client/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:lestreamerclientExample +add_executable(le_streamer_client + ../main.c + ${PICO_BTSTACK_PATH}/example/le_streamer_client.c # note: this source is in pico-sdk/lib/btstack + ${PICO_BTSTACK_PATH}/src/ble/gatt_service_client.c +) +target_link_libraries(le_streamer_client PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(le_streamer_client PRIVATE + . # for btstack config +) +target_compile_definitions(le_streamer_client PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(le_streamer_client) +example_auto_set_url(le_streamer_client) diff --git a/bluetooth/btstack_examples/le_streamer_client/btstack_config.h b/bluetooth/btstack_examples/le_streamer_client/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/le_streamer_client/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/led_counter/CMakeLists.txt b/bluetooth/btstack_examples/led_counter/CMakeLists.txt new file mode 100644 index 000000000..7d575af37 --- /dev/null +++ b/bluetooth/btstack_examples/led_counter/CMakeLists.txt @@ -0,0 +1,19 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:ledcounterExample +add_executable(led_counter + ../main.c + ${PICO_BTSTACK_PATH}/example/led_counter.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(led_counter PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(led_counter PRIVATE + . # for btstack config +) +target_compile_definitions(led_counter PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(led_counter) +example_auto_set_url(led_counter) diff --git a/bluetooth/btstack_examples/led_counter/btstack_config.h b/bluetooth/btstack_examples/led_counter/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/led_counter/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/main.c b/bluetooth/btstack_examples/main.c new file mode 100644 index 000000000..39297dc99 --- /dev/null +++ b/bluetooth/btstack_examples/main.c @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" +#include "btstack.h" + +#if USING_IPERF +#include "lwip/apps/lwiperf.h" +#endif + +// This is implemented isn pico-sdk/lib/btstack/example/.c +int btstack_main(int argc, const char * argv[]); + +#if USING_I2C +// This sets up the audio sink +const btstack_audio_sink_t * btstack_audio_pico_sink_get_instance(void); +#endif + +#if USING_IPERF +// Report IP results +static void iperf_report(void *arg, enum lwiperf_report_type report_type, + const ip_addr_t *local_addr, u16_t local_port, const ip_addr_t *remote_addr, u16_t remote_port, + u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec) { + static uint32_t total_iperf_megabytes = 0; + uint32_t mbytes = bytes_transferred / 1024 / 1024; + float mbits = bandwidth_kbitpsec / 1000.0; + total_iperf_megabytes += mbytes; + + printf("Completed iperf transfer of %d MBytes @ %.1f Mbits/sec\n", mbytes, mbits); + printf("Total iperf megabytes since start %d Mbytes\n", total_iperf_megabytes); +} +#endif + +// This is used in some examples to turn the led on and off +void hal_led_toggle(void){ + static int led_state; + led_state = 1 - led_state; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_state); +} + +int main() { + stdio_init_all(); + + if (cyw43_arch_init() != PICO_OK) { + panic("failed to cyw43"); + } + +#if USING_I2C + btstack_audio_sink_set_instance(btstack_audio_pico_sink_get_instance()); +#endif + + // Setup the example + btstack_main(0, NULL); + +#if defined(WIFI_SSID) && defined(WIFI_PASSWORD) + // Connect to WiFi + cyw43_arch_enable_sta_mode(); + printf("Connecting to Wi-Fi...\n"); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + panic("failed to connect"); + } else { + printf("Connected.\n"); + } +#endif + +#if USING_IPERF + // Run iperf server + cyw43_arch_lwip_begin(); + printf("\nReady, running iperf server at %s\n", ip4addr_ntoa(netif_ip4_addr(netif_list))); + lwiperf_start_tcp_server_default(&iperf_report, NULL); + cyw43_arch_lwip_end(); +#endif + + btstack_run_loop_execute(); // run until btstack_run_loop_trigger_exit is called + + cyw43_arch_deinit(); + return 0; +} diff --git a/bluetooth/btstack_examples/mod_player/CMakeLists.txt b/bluetooth/btstack_examples/mod_player/CMakeLists.txt new file mode 100644 index 000000000..b0bd0abd4 --- /dev/null +++ b/bluetooth/btstack_examples/mod_player/CMakeLists.txt @@ -0,0 +1,25 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:modplayerExample +add_executable(mod_player + ../main.c + ../btstack_audio_pico.c + ${PICO_BTSTACK_PATH}/example/mod_player.c # note: this source is in pico-sdk/lib/btstack + ) +target_link_libraries(mod_player PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_hxcmod_player + pico_audio_i2s +) +target_include_directories(mod_player PRIVATE + . # for btstack config +) +target_compile_definitions(mod_player PRIVATE + USING_I2C=1 + PICO_AUDIO_I2S_DATA_PIN=9 + PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(mod_player) +example_auto_set_url(mod_player) diff --git a/bluetooth/btstack_examples/mod_player/btstack_config.h b/bluetooth/btstack_examples/mod_player/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/mod_player/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/nordic_spp_le_counter/CMakeLists.txt b/bluetooth/btstack_examples/nordic_spp_le_counter/CMakeLists.txt new file mode 100644 index 000000000..4b1b9673f --- /dev/null +++ b/bluetooth/btstack_examples/nordic_spp_le_counter/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:nordicspplecounterExample +add_executable(nordic_spp_le_counter + ../main.c + ${PICO_BTSTACK_PATH}/example/nordic_spp_le_counter.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(nordic_spp_le_counter PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(nordic_spp_le_counter PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(nordic_spp_le_counter PRIVATE + ${PICO_BTSTACK_PATH}/example/nordic_spp_le_counter.gatt +) +target_compile_definitions(nordic_spp_le_counter PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(nordic_spp_le_counter) +example_auto_set_url(nordic_spp_le_counter) diff --git a/bluetooth/btstack_examples/nordic_spp_le_counter/btstack_config.h b/bluetooth/btstack_examples/nordic_spp_le_counter/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/nordic_spp_le_counter/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/nordic_spp_le_streamer/CMakeLists.txt b/bluetooth/btstack_examples/nordic_spp_le_streamer/CMakeLists.txt new file mode 100644 index 000000000..718565c66 --- /dev/null +++ b/bluetooth/btstack_examples/nordic_spp_le_streamer/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:nordicspplestreamerExample +add_executable(nordic_spp_le_streamer + ../main.c + ${PICO_BTSTACK_PATH}/example/nordic_spp_le_streamer.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(nordic_spp_le_streamer PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(nordic_spp_le_streamer PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(nordic_spp_le_streamer PRIVATE + ${PICO_BTSTACK_PATH}/example/nordic_spp_le_streamer.gatt +) +target_compile_definitions(nordic_spp_le_streamer PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(nordic_spp_le_streamer) +example_auto_set_url(nordic_spp_le_streamer) diff --git a/bluetooth/btstack_examples/nordic_spp_le_streamer/btstack_config.h b/bluetooth/btstack_examples/nordic_spp_le_streamer/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/nordic_spp_le_streamer/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/pan_lwip_http_server/CMakeLists.txt b/bluetooth/btstack_examples/pan_lwip_http_server/CMakeLists.txt new file mode 100644 index 000000000..6eb06d339 --- /dev/null +++ b/bluetooth/btstack_examples/pan_lwip_http_server/CMakeLists.txt @@ -0,0 +1,26 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:panlwiphttpserverExample +add_executable(pan_lwip_http_server + ../main.c + ${PICO_BTSTACK_PATH}/example/pan_lwip_http_server.c # note: this source is in pico-sdk/lib/btstack + ${PICO_BTSTACK_PATH}/3rd-party/lwip/dhcp-server/dhserver.c +) +target_link_libraries(pan_lwip_http_server PRIVATE + pico_stdlib + pico_cyw43_arch_threadsafe_background + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support + pico_btstack_bnep_lwip # btstack lwip + pico_lwip_http + pico_lwip_nosys + ) +target_include_directories(pan_lwip_http_server PRIVATE + . # for btstack config and lwipopts.h + ${PICO_BTSTACK_PATH}/3rd-party/lwip/dhcp-server +) +target_compile_definitions(pan_lwip_http_server PRIVATE + CYW43_LWIP=0 # Stop cyw43 doing lwip stuff + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(pan_lwip_http_server) +example_auto_set_url(pan_lwip_http_server) diff --git a/bluetooth/btstack_examples/pan_lwip_http_server/btstack_config.h b/bluetooth/btstack_examples/pan_lwip_http_server/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/pan_lwip_http_server/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/pan_lwip_http_server/lwipopts.h b/bluetooth/btstack_examples/pan_lwip_http_server/lwipopts.h new file mode 100644 index 000000000..8bf98aed8 --- /dev/null +++ b/bluetooth/btstack_examples/pan_lwip_http_server/lwipopts.h @@ -0,0 +1 @@ +#include "../../../pico_w/wifi/lwipopts_examples_common.h" diff --git a/bluetooth/btstack_examples/pbap_client_demo/CMakeLists.txt b/bluetooth/btstack_examples/pbap_client_demo/CMakeLists.txt new file mode 100644 index 000000000..8e73c97cd --- /dev/null +++ b/bluetooth/btstack_examples/pbap_client_demo/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:pbapclientdemoExample +add_executable(pbap_client_demo + ../main.c + ${PICO_BTSTACK_PATH}/example/pbap_client_demo.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(pbap_client_demo PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(pbap_client_demo PRIVATE + . # for btstack config +) +target_compile_definitions(pbap_client_demo PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(pbap_client_demo) +example_auto_set_url(pbap_client_demo) diff --git a/bluetooth/btstack_examples/pbap_client_demo/btstack_config.h b/bluetooth/btstack_examples/pbap_client_demo/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/pbap_client_demo/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/sdp_bnep_query/CMakeLists.txt b/bluetooth/btstack_examples/sdp_bnep_query/CMakeLists.txt new file mode 100644 index 000000000..b3c6a8ef9 --- /dev/null +++ b/bluetooth/btstack_examples/sdp_bnep_query/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sdpbnepqueryExample +add_executable(sdp_bnep_query + ../main.c + ${PICO_BTSTACK_PATH}/example/sdp_bnep_query.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(sdp_bnep_query PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(sdp_bnep_query PRIVATE + . # for btstack config +) +target_compile_definitions(sdp_bnep_query PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(sdp_bnep_query) +example_auto_set_url(sdp_bnep_query) diff --git a/bluetooth/btstack_examples/sdp_bnep_query/btstack_config.h b/bluetooth/btstack_examples/sdp_bnep_query/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/sdp_bnep_query/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/sdp_general_query/CMakeLists.txt b/bluetooth/btstack_examples/sdp_general_query/CMakeLists.txt new file mode 100644 index 000000000..2263f0067 --- /dev/null +++ b/bluetooth/btstack_examples/sdp_general_query/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sdpgeneralqueryExample +add_executable(sdp_general_query + ../main.c + ${PICO_BTSTACK_PATH}/example/sdp_general_query.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(sdp_general_query PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(sdp_general_query PRIVATE + . # for btstack config +) +target_compile_definitions(sdp_general_query PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(sdp_general_query) +example_auto_set_url(sdp_general_query) diff --git a/bluetooth/btstack_examples/sdp_general_query/btstack_config.h b/bluetooth/btstack_examples/sdp_general_query/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/sdp_general_query/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/sdp_rfcomm_query/CMakeLists.txt b/bluetooth/btstack_examples/sdp_rfcomm_query/CMakeLists.txt new file mode 100644 index 000000000..2a40f138e --- /dev/null +++ b/bluetooth/btstack_examples/sdp_rfcomm_query/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sdprfcommqueryExample +add_executable(sdp_rfcomm_query + ../main.c + ${PICO_BTSTACK_PATH}/example/sdp_rfcomm_query.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(sdp_rfcomm_query PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(sdp_rfcomm_query PRIVATE + . # for btstack config +) +target_compile_definitions(sdp_rfcomm_query PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(sdp_rfcomm_query) +example_auto_set_url(sdp_rfcomm_query) diff --git a/bluetooth/btstack_examples/sdp_rfcomm_query/btstack_config.h b/bluetooth/btstack_examples/sdp_rfcomm_query/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/sdp_rfcomm_query/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/sine_player/CMakeLists.txt b/bluetooth/btstack_examples/sine_player/CMakeLists.txt new file mode 100644 index 000000000..79828de8a --- /dev/null +++ b/bluetooth/btstack_examples/sine_player/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sineplayerExample +add_executable(sine_player + ../main.c + ../btstack_audio_pico.c + ${PICO_BTSTACK_PATH}/example/sine_player.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(sine_player PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_audio_i2s +) +target_include_directories(sine_player PRIVATE + . # for btstack config +) +target_compile_definitions(sine_player PRIVATE + USING_I2C=1 + PICO_AUDIO_I2S_DATA_PIN=9 + PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(sine_player) +example_auto_set_url(sine_player) diff --git a/bluetooth/btstack_examples/sine_player/btstack_config.h b/bluetooth/btstack_examples/sine_player/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/sine_player/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/sm_pairing_central/CMakeLists.txt b/bluetooth/btstack_examples/sm_pairing_central/CMakeLists.txt new file mode 100644 index 000000000..1067f9341 --- /dev/null +++ b/bluetooth/btstack_examples/sm_pairing_central/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:smpairingcentralExample +add_executable(sm_pairing_central + ../main.c + ${PICO_BTSTACK_PATH}/example/sm_pairing_central.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(sm_pairing_central PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(sm_pairing_central PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(sm_pairing_central PRIVATE + ${PICO_BTSTACK_PATH}/example/sm_pairing_central.gatt +) +target_compile_definitions(sm_pairing_central PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(sm_pairing_central) +example_auto_set_url(sm_pairing_central) diff --git a/bluetooth/btstack_examples/sm_pairing_central/btstack_config.h b/bluetooth/btstack_examples/sm_pairing_central/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/sm_pairing_central/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/sm_pairing_peripheral/CMakeLists.txt b/bluetooth/btstack_examples/sm_pairing_peripheral/CMakeLists.txt new file mode 100644 index 000000000..0313406e1 --- /dev/null +++ b/bluetooth/btstack_examples/sm_pairing_peripheral/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:smpairingperipheralExample +add_executable(sm_pairing_peripheral + ../main.c + ${PICO_BTSTACK_PATH}/example/sm_pairing_peripheral.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(sm_pairing_peripheral PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support +) +target_include_directories(sm_pairing_peripheral PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(sm_pairing_peripheral PRIVATE + ${PICO_BTSTACK_PATH}/example/sm_pairing_peripheral.gatt +) +target_compile_definitions(sm_pairing_peripheral PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(sm_pairing_peripheral) +example_auto_set_url(sm_pairing_peripheral) diff --git a/bluetooth/btstack_examples/sm_pairing_peripheral/btstack_config.h b/bluetooth/btstack_examples/sm_pairing_peripheral/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/sm_pairing_peripheral/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/spp_and_gatt_counter/CMakeLists.txt b/bluetooth/btstack_examples/spp_and_gatt_counter/CMakeLists.txt new file mode 100644 index 000000000..e2c896d10 --- /dev/null +++ b/bluetooth/btstack_examples/spp_and_gatt_counter/CMakeLists.txt @@ -0,0 +1,25 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sppandgattcounterExample +add_executable(spp_and_gatt_counter + ../main.c + ${PICO_BTSTACK_PATH}/example/spp_and_gatt_counter.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(spp_and_gatt_counter PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(spp_and_gatt_counter PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(spp_and_gatt_counter PRIVATE + ${PICO_BTSTACK_PATH}/example/spp_and_gatt_counter.gatt +) +target_compile_definitions(spp_and_gatt_counter PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(spp_and_gatt_counter) +example_auto_set_url(spp_and_gatt_counter) diff --git a/bluetooth/btstack_examples/spp_and_gatt_counter/btstack_config.h b/bluetooth/btstack_examples/spp_and_gatt_counter/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/spp_and_gatt_counter/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/spp_and_gatt_streamer/CMakeLists.txt b/bluetooth/btstack_examples/spp_and_gatt_streamer/CMakeLists.txt new file mode 100644 index 000000000..9722e7939 --- /dev/null +++ b/bluetooth/btstack_examples/spp_and_gatt_streamer/CMakeLists.txt @@ -0,0 +1,24 @@ +add_executable(spp_and_gatt_streamer + ../main.c + ${PICO_BTSTACK_PATH}/example/spp_and_gatt_streamer.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(spp_and_gatt_streamer PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(spp_and_gatt_streamer PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(spp_and_gatt_streamer PRIVATE + ${PICO_BTSTACK_PATH}/example/spp_and_gatt_streamer.gatt +) +target_compile_definitions(spp_and_gatt_streamer PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(spp_and_gatt_streamer) +example_auto_set_url(spp_and_gatt_streamer) diff --git a/bluetooth/btstack_examples/spp_and_gatt_streamer/btstack_config.h b/bluetooth/btstack_examples/spp_and_gatt_streamer/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/spp_and_gatt_streamer/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/spp_counter/CMakeLists.txt b/bluetooth/btstack_examples/spp_counter/CMakeLists.txt new file mode 100644 index 000000000..31e5f2924 --- /dev/null +++ b/bluetooth/btstack_examples/spp_counter/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sppcounterExample +add_executable(spp_counter + ../main.c + ${PICO_BTSTACK_PATH}/example/spp_counter.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(spp_counter PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(spp_counter PRIVATE + . # for btstack config +) +target_compile_definitions(spp_counter PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(spp_counter) +example_auto_set_url(spp_counter) diff --git a/bluetooth/btstack_examples/spp_counter/btstack_config.h b/bluetooth/btstack_examples/spp_counter/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/spp_counter/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/spp_flowcontrol/CMakeLists.txt b/bluetooth/btstack_examples/spp_flowcontrol/CMakeLists.txt new file mode 100644 index 000000000..654d95d9b --- /dev/null +++ b/bluetooth/btstack_examples/spp_flowcontrol/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sppflowcontrolExample +add_executable(spp_flowcontrol + ../main.c + ${PICO_BTSTACK_PATH}/example/spp_flowcontrol.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(spp_flowcontrol PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(spp_flowcontrol PRIVATE + . # for btstack config +) +target_compile_definitions(spp_flowcontrol PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(spp_flowcontrol) +example_auto_set_url(spp_flowcontrol) diff --git a/bluetooth/btstack_examples/spp_flowcontrol/btstack_config.h b/bluetooth/btstack_examples/spp_flowcontrol/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/spp_flowcontrol/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/spp_streamer/CMakeLists.txt b/bluetooth/btstack_examples/spp_streamer/CMakeLists.txt new file mode 100644 index 000000000..424a281b3 --- /dev/null +++ b/bluetooth/btstack_examples/spp_streamer/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sppstreamerExample +add_executable(spp_streamer + ../main.c + ${PICO_BTSTACK_PATH}/example/spp_streamer.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(spp_streamer PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(spp_streamer PRIVATE + . # for btstack config +) +target_compile_definitions(spp_streamer PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(spp_streamer) +example_auto_set_url(spp_streamer) diff --git a/bluetooth/btstack_examples/spp_streamer/btstack_config.h b/bluetooth/btstack_examples/spp_streamer/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/spp_streamer/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/spp_streamer_client/CMakeLists.txt b/bluetooth/btstack_examples/spp_streamer_client/CMakeLists.txt new file mode 100644 index 000000000..9b8153329 --- /dev/null +++ b/bluetooth/btstack_examples/spp_streamer_client/CMakeLists.txt @@ -0,0 +1,20 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sppstreamerclientExample +add_executable(spp_streamer_client + ../main.c + ${PICO_BTSTACK_PATH}/example/spp_streamer_client.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(spp_streamer_client PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(spp_streamer_client PRIVATE + . # for btstack config +) +target_compile_definitions(spp_streamer_client PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(spp_streamer_client) +example_auto_set_url(spp_streamer_client) diff --git a/bluetooth/btstack_examples/spp_streamer_client/btstack_config.h b/bluetooth/btstack_examples/spp_streamer_client/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/spp_streamer_client/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/spp_streamer_with_wifi/CMakeLists.txt b/bluetooth/btstack_examples/spp_streamer_with_wifi/CMakeLists.txt new file mode 100644 index 000000000..73a48d9c3 --- /dev/null +++ b/bluetooth/btstack_examples/spp_streamer_with_wifi/CMakeLists.txt @@ -0,0 +1,24 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:sppstreamerExample +add_executable(spp_streamer_with_wifi + ../main.c + ${PICO_BTSTACK_PATH}/example/spp_streamer.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(spp_streamer_with_wifi PRIVATE + pico_stdlib + pico_cyw43_arch_lwip_threadsafe_background # we want lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support + pico_lwip_iperf +) +target_include_directories(spp_streamer_with_wifi PRIVATE + . # for btstack config +) +target_compile_definitions(spp_streamer_with_wifi PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + USING_IPERF=1 + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(spp_streamer_with_wifi) +example_auto_set_url(spp_streamer_with_wifi) diff --git a/bluetooth/btstack_examples/spp_streamer_with_wifi/btstack_config.h b/bluetooth/btstack_examples/spp_streamer_with_wifi/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/spp_streamer_with_wifi/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/bluetooth/btstack_examples/spp_streamer_with_wifi/lwipopts.h b/bluetooth/btstack_examples/spp_streamer_with_wifi/lwipopts.h new file mode 100644 index 000000000..8bf98aed8 --- /dev/null +++ b/bluetooth/btstack_examples/spp_streamer_with_wifi/lwipopts.h @@ -0,0 +1 @@ +#include "../../../pico_w/wifi/lwipopts_examples_common.h" diff --git a/bluetooth/btstack_examples/ublox_spp_le_counter/CMakeLists.txt b/bluetooth/btstack_examples/ublox_spp_le_counter/CMakeLists.txt new file mode 100644 index 000000000..5b3cb85ed --- /dev/null +++ b/bluetooth/btstack_examples/ublox_spp_le_counter/CMakeLists.txt @@ -0,0 +1,25 @@ +# See https://bluekitchen-gmbh.com/btstack/#examples/examples/#sec:ubloxspplecounterExample +add_executable(ublox_spp_le_counter + ../main.c + ${PICO_BTSTACK_PATH}/example/ublox_spp_le_counter.c # note: this source is in pico-sdk/lib/btstack +) +target_link_libraries(ublox_spp_le_counter PRIVATE + pico_stdlib + pico_cyw43_arch_none # we don't need lwip support + pico_btstack_cyw43 # enable bt + pico_btstack_ble # ble support + pico_btstack_classic # classic support +) +target_include_directories(ublox_spp_le_counter PRIVATE + . # for btstack config +) +# This builds a header file with details of the gatt service described in a gatt file +# note: this source for this is in pico-sdk/lib/btstack +pico_btstack_make_gatt_header(ublox_spp_le_counter PRIVATE + ${PICO_BTSTACK_PATH}/example/ublox_spp_le_counter.gatt +) +target_compile_definitions(ublox_spp_le_counter PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(ublox_spp_le_counter) +example_auto_set_url(ublox_spp_le_counter) diff --git a/bluetooth/btstack_examples/ublox_spp_le_counter/btstack_config.h b/bluetooth/btstack_examples/ublox_spp_le_counter/btstack_config.h new file mode 100644 index 000000000..6eafef0a0 --- /dev/null +++ b/bluetooth/btstack_examples/ublox_spp_le_counter/btstack_config.h @@ -0,0 +1 @@ +#include "../../config/btstack_config_common.h" diff --git a/pico_w/bt/config/btstack_config.h b/bluetooth/config/btstack_config_common.h similarity index 100% rename from pico_w/bt/config/btstack_config.h rename to bluetooth/config/btstack_config_common.h diff --git a/bluetooth/doorbell/CMakeLists.txt b/bluetooth/doorbell/CMakeLists.txt new file mode 100644 index 000000000..6fbc8aa21 --- /dev/null +++ b/bluetooth/doorbell/CMakeLists.txt @@ -0,0 +1,44 @@ +# Standalone example that connects to the server Pico and flashes an external LED when the button is pressed +# Flashes the on-board LED once quickly each second when it's running but not connected to another device +# Flashes the on-board LED twice quickly each second when connected to another device +add_executable(doorbell_client + client.c + ) +target_link_libraries(doorbell_client + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + ) +target_include_directories(doorbell_client PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # For our common btstack config + ) +target_compile_definitions(doorbell_client PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(doorbell_client) + + +# Standalone example that connects to the client Pico and detects button inputs +# Flashes slowly each second to show it's running +add_executable(doorbell_server + server.c + ) +target_link_libraries(doorbell_server + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + ) +target_include_directories(doorbell_server PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # For our common btstack config + ) +target_compile_definitions(doorbell_server PRIVATE + #WANT_HCI_DUMP=1 +) +pico_btstack_make_gatt_header(doorbell_server PRIVATE "${CMAKE_CURRENT_LIST_DIR}/doorbell.gatt") + +pico_add_extra_outputs(doorbell_server) + diff --git a/bluetooth/doorbell/README.md b/bluetooth/doorbell/README.md new file mode 100644 index 000000000..72f5c2ec9 --- /dev/null +++ b/bluetooth/doorbell/README.md @@ -0,0 +1,5 @@ +### Doorbell + +This example uses BLE to communicate between 2 Pico Ws. When the button on one Pico is pressed, the LED connected to the second Pico will illuminate. + +To use this example, connect a LED in series with a 330 ohm resistor between pin 15 and ground on the client Pico. On the server Pico, connect a button between pin 15 and ground. \ No newline at end of file diff --git a/pico_w/bt/standalone/server/btstack_config.h b/bluetooth/doorbell/btstack_config.h similarity index 61% rename from pico_w/bt/standalone/server/btstack_config.h rename to bluetooth/doorbell/btstack_config.h index ce1919916..0dd0d82cb 100644 --- a/pico_w/bt/standalone/server/btstack_config.h +++ b/bluetooth/doorbell/btstack_config.h @@ -1,6 +1,6 @@ #ifndef _PICO_BTSTACK_CONFIG_H #define _PICO_BTSTACK_CONFIG_H -#include "btstack_config_common.h" +#include "../config/btstack_config_common.h" #endif diff --git a/bluetooth/doorbell/client.c b/bluetooth/doorbell/client.c new file mode 100644 index 000000000..9be502c78 --- /dev/null +++ b/bluetooth/doorbell/client.c @@ -0,0 +1,312 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "btstack.h" +#include "pico/cyw43_arch.h" +#include "pico/stdlib.h" + +#ifndef NDEBUG +#define DEBUG_LOG(...) printf(__VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif +#define INFO_LOG printf +#define ERROR_LOG printf + +#define LED_QUICK_FLASH_DELAY_MS 100 +#define LED_SLOW_FLASH_DELAY_MS 1000 + +#define EXT_LED_GPIO_NUM 15 + +typedef enum { + TC_OFF, + TC_IDLE, + TC_W4_SCAN_RESULT, + TC_W4_CONNECT, + TC_W4_SERVICE_RESULT, + TC_W4_CHARACTERISTIC_RESULT, + TC_W4_ENABLE_NOTIFICATIONS_COMPLETE, + TC_W4_READY +} gc_state_t; + +static btstack_packet_callback_registration_t hci_event_callback_registration; +static gc_state_t state = TC_OFF; +static bd_addr_t server_addr; +static bd_addr_type_t server_addr_type; +static hci_con_handle_t connection_handle; +static gatt_client_service_t server_service; +static gatt_client_characteristic_t server_characteristic; +static bool listener_registered; +static gatt_client_notification_t notification_listener; +static btstack_timer_source_t heartbeat; + +static void doorbell_led_worker_fn(async_context_t *context, async_at_time_worker_t *worker) { + int val = (int)worker->user_data; + gpio_put(EXT_LED_GPIO_NUM, (val % 2) == 0); + if (val < 11) { + worker->user_data = (void*)(val + 1); + async_context_add_at_time_worker_in_ms(context, worker, 100); + } +} +static async_at_time_worker_t doorbell_led_worker = { .do_work = doorbell_led_worker_fn }; + +static void client_start(void){ + INFO_LOG("Start scanning!\n"); + state = TC_W4_SCAN_RESULT; + gap_set_scan_parameters(0,0x0030, 0x0030); + gap_start_scan(); +} + +static bool advertisement_report_contains_service(uint16_t service, uint8_t *advertisement_report){ + // get advertisement from report event + const uint8_t * adv_data = gap_event_advertising_report_get_data(advertisement_report); + uint8_t adv_len = gap_event_advertising_report_get_data_length(advertisement_report); + + // iterate over advertisement data + ad_context_t context; + for (ad_iterator_init(&context, adv_len, adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){ + uint8_t data_type = ad_iterator_get_data_type(&context); + uint8_t data_size = ad_iterator_get_data_len(&context); + const uint8_t * data = ad_iterator_get_data(&context); + switch (data_type){ + case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: + for (int i = 0; i < data_size; i += 2) { + uint16_t type = little_endian_read_16(data, i); + if (type == service) return true; + } + default: + break; + } + } + return false; +} + +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); + + uint8_t att_status; + switch(state){ + case TC_W4_SERVICE_RESULT: + switch(hci_event_packet_get_type(packet)) { + case GATT_EVENT_SERVICE_QUERY_RESULT: + // store service (we expect only one) + gatt_event_service_query_result_get_service(packet, &server_service); + break; + case GATT_EVENT_QUERY_COMPLETE: + att_status = gatt_event_query_complete_get_att_status(packet); + if (att_status != ATT_ERROR_SUCCESS){ + ERROR_LOG("SERVICE_QUERY_RESULT, ATT Error 0x%02x.\n", att_status); + gap_disconnect(connection_handle); + break; + } + // service query complete, look for characteristic + state = TC_W4_CHARACTERISTIC_RESULT; + INFO_LOG("Search for binary sensing characteristic.\n"); + gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &server_service, ORG_BLUETOOTH_CHARACTERISTIC_DIGITAL_OUTPUT); + break; + default: + break; + } + break; + case TC_W4_CHARACTERISTIC_RESULT: + switch(hci_event_packet_get_type(packet)) { + case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: + INFO_LOG("Storing characteristic\n"); + gatt_event_characteristic_query_result_get_characteristic(packet, &server_characteristic); + break; + case GATT_EVENT_QUERY_COMPLETE: + att_status = gatt_event_query_complete_get_att_status(packet); + if (att_status != ATT_ERROR_SUCCESS){ + ERROR_LOG("CHARACTERISTIC_QUERY_RESULT, ATT Error 0x%02x.\n", att_status); + gap_disconnect(connection_handle); + break; + } + // register handler for notifications + listener_registered = true; + gatt_client_listen_for_characteristic_value_updates(¬ification_listener, handle_gatt_client_event, connection_handle, &server_characteristic); + // enable notifications + INFO_LOG("Enable notify on characteristic.\n"); + state = TC_W4_ENABLE_NOTIFICATIONS_COMPLETE; + gatt_client_write_client_characteristic_configuration(handle_gatt_client_event, connection_handle, + &server_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); + break; + default: + break; + } + break; + case TC_W4_ENABLE_NOTIFICATIONS_COMPLETE: + switch(hci_event_packet_get_type(packet)) { + case GATT_EVENT_QUERY_COMPLETE: + INFO_LOG("Notifications enabled, ATT status 0x%02x\n", gatt_event_query_complete_get_att_status(packet)); + if (gatt_event_query_complete_get_att_status(packet) != ATT_ERROR_SUCCESS) break; + state = TC_W4_READY; + break; + default: + break; + } + break; + case TC_W4_READY: + switch(hci_event_packet_get_type(packet)) { + case GATT_EVENT_NOTIFICATION: { + uint16_t value_length = gatt_event_notification_get_value_length(packet); + const uint8_t *value = gatt_event_notification_get_value(packet); + if (value_length == 2) { + uint state = little_endian_read_16(value, 0); + INFO_LOG("Doorbell state %d \n", state); + if (state) { + // flash LED + doorbell_led_worker.user_data = 0; + async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &doorbell_led_worker, 0); + } + } else { + ERROR_LOG("Unexpected length %d\n", value_length); + } + break; + } + default: + ERROR_LOG("Unknown packet type 0x%02x\n", hci_event_packet_get_type(packet)); + break; + } + break; + default: + ERROR_LOG("Unknown state %d\n", state); + break; + } +} + +static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + UNUSED(size); + UNUSED(channel); + bd_addr_t local_addr; + if (packet_type != HCI_EVENT_PACKET) return; + + uint8_t event_type = hci_event_packet_get_type(packet); + switch(event_type){ + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) { + gap_local_bd_addr(local_addr); + DEBUG_LOG("BTstack up and running on %s.\n", bd_addr_to_str(local_addr)); + client_start(); + } else { + state = TC_OFF; + } + break; + case GAP_EVENT_ADVERTISING_REPORT: + if (state != TC_W4_SCAN_RESULT) return; + // check name in advertisement + if (!advertisement_report_contains_service(ORG_BLUETOOTH_SERVICE_BINARY_SENSOR, packet)) return; + // store address and type + gap_event_advertising_report_get_address(packet, server_addr); + server_addr_type = gap_event_advertising_report_get_address_type(packet); + // stop scanning, and connect to the device + state = TC_W4_CONNECT; + gap_stop_scan(); + DEBUG_LOG("Connecting to device with addr %s.\n", bd_addr_to_str(server_addr)); + gap_connect(server_addr, server_addr_type); + break; + case HCI_EVENT_LE_META: + // wait for connection complete + switch (hci_event_le_meta_get_subevent_code(packet)) { + case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: + if (state != TC_W4_CONNECT) return; + connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); + // initialize gatt client context with handle, and add it to the list of active clients + // query primary services + DEBUG_LOG("Search for binary sensing service.\n"); + state = TC_W4_SERVICE_RESULT; + gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, connection_handle, ORG_BLUETOOTH_SERVICE_BINARY_SENSOR); + break; + default: + break; + } + break; + case HCI_EVENT_DISCONNECTION_COMPLETE: + // unregister listener + connection_handle = HCI_CON_HANDLE_INVALID; + if (listener_registered){ + listener_registered = false; + gatt_client_stop_listening_for_characteristic_value_updates(¬ification_listener); + } + DEBUG_LOG("Disconnected %s\n", bd_addr_to_str(server_addr)); + if (state == TC_OFF) break; + client_start(); + break; + default: + break; + } +} + +static void heartbeat_handler(struct btstack_timer_source *ts) { + // Invert the led + static bool quick_flash; + static bool led_on = true; + + led_on = !led_on; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + if (listener_registered && led_on) { + quick_flash = !quick_flash; + } else if (!listener_registered) { + quick_flash = false; + } + + // Restart timer + btstack_run_loop_set_timer(ts, (led_on || quick_flash) ? LED_QUICK_FLASH_DELAY_MS : LED_SLOW_FLASH_DELAY_MS); + btstack_run_loop_add_timer(ts); +} + +int main() { + stdio_init_all(); + + // initialize external LED + gpio_init(EXT_LED_GPIO_NUM); + gpio_set_dir(EXT_LED_GPIO_NUM, GPIO_OUT); + + // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) + if (cyw43_arch_init()) { + ERROR_LOG("failed to initialise cyw43_arch\n"); + return -1; + } + + l2cap_init(); + sm_init(); + sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); + + // setup empty ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android and iOS + att_server_init(NULL, NULL, NULL); + + gatt_client_init(); + + hci_event_callback_registration.callback = &hci_event_handler; + hci_add_event_handler(&hci_event_callback_registration); + + // set one-shot btstack timer + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, LED_SLOW_FLASH_DELAY_MS); + btstack_run_loop_add_timer(&heartbeat); + + // turn on! + hci_power_control(HCI_POWER_ON); + + // btstack_run_loop_execute is only required when using the 'polling' method (e.g. using pico_cyw43_arch_poll library). + // This example uses the 'threadsafe background` method, where BT work is handled in a low priority IRQ, so it + // is fine to call bt_stack_run_loop_execute() but equally you can continue executing user code. + +#if 1 // this is only necessary when using polling (which we aren't, but we're showing it is still safe to call in this case) + btstack_run_loop_execute(); +#else + // this core is free to do it's own stuff except when using 'polling' method (in which case you should use + // btstacK_run_loop_ methods to add work to the run loop. + + // this is a forever loop in place of where user code would go. + while(true) { + sleep_ms(1000); + } +#endif + return 0; +} diff --git a/bluetooth/doorbell/doorbell.fzz b/bluetooth/doorbell/doorbell.fzz new file mode 100644 index 000000000..378241ec8 Binary files /dev/null and b/bluetooth/doorbell/doorbell.fzz differ diff --git a/bluetooth/doorbell/doorbell.gatt b/bluetooth/doorbell/doorbell.gatt new file mode 100644 index 000000000..be0c10968 --- /dev/null +++ b/bluetooth/doorbell/doorbell.gatt @@ -0,0 +1,11 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "picow_doorbell" + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_DATABASE_HASH, READ, + +// physical activity service +PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_BINARY_SENSOR + +// +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_DIGITAL_OUTPUT, READ | NOTIFY | INDICATE | DYNAMIC, diff --git a/bluetooth/doorbell/doorbell_bb.png b/bluetooth/doorbell/doorbell_bb.png new file mode 100644 index 000000000..868e7e8b2 Binary files /dev/null and b/bluetooth/doorbell/doorbell_bb.png differ diff --git a/bluetooth/doorbell/server.c b/bluetooth/doorbell/server.c new file mode 100644 index 000000000..b762296c1 --- /dev/null +++ b/bluetooth/doorbell/server.c @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "btstack.h" +#include "pico/cyw43_arch.h" +#include "pico/btstack_cyw43.h" +#include "pico/stdlib.h" +#include "doorbell.h" // generated by pico_btstack_make_gatt_header in CMakeLists.txt +#include "hardware/gpio.h" + + +#define HEARTBEAT_PERIOD_MS 1000 +#define APP_AD_FLAGS 0x06 +#define BUTTON_GPIO_NUM 15 + +#ifndef NDEBUG +#define DEBUG_LOG printf +#else +#define DEBUG_LOG(...) +#endif + +static uint8_t adv_data[] = { + // Flags general discoverable + 0x02, BLUETOOTH_DATA_TYPE_FLAGS, APP_AD_FLAGS, + // Name + 0x17, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'P', 'i', 'c', 'o', ' ', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', + 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, 0x3b, 0x18, +}; + +static const uint8_t adv_data_len = sizeof(adv_data); + +static bool le_notification_enabled; +static uint16_t current_state; +static hci_con_handle_t con_handle; +extern uint8_t const profile_data[]; + +static btstack_timer_source_t heartbeat; +static btstack_packet_callback_registration_t hci_event_callback_registration; + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + UNUSED(size); + UNUSED(channel); + bd_addr_t local_addr; + if (packet_type != HCI_EVENT_PACKET) return; + + uint8_t event_type = hci_event_packet_get_type(packet); + switch(event_type){ + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; + gap_local_bd_addr(local_addr); + DEBUG_LOG("BTstack up and running on %s.\n", bd_addr_to_str(local_addr)); + + // setup advertisements + uint16_t adv_int_min = 800; + uint16_t adv_int_max = 800; + 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); + assert(adv_data_len <= 31); // ble limitation + gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); + gap_advertisements_enable(1); + + + break; + case HCI_EVENT_DISCONNECTION_COMPLETE: + le_notification_enabled = 0; + break; + case ATT_EVENT_CAN_SEND_NOW: + att_server_notify(con_handle, ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_DIGITAL_OUTPUT_01_VALUE_HANDLE, (uint8_t*)¤t_state, sizeof(current_state)); + break; + default: + break; + } +} + +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) { + UNUSED(connection_handle); + + if (att_handle == ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_DIGITAL_OUTPUT_01_VALUE_HANDLE){ + return att_read_callback_handle_blob((const uint8_t *)¤t_state, sizeof(current_state), offset, buffer, buffer_size); + } + 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_ORG_BLUETOOTH_CHARACTERISTIC_DIGITAL_OUTPUT_01_CLIENT_CONFIGURATION_HANDLE) return 0; + le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; + con_handle = connection_handle; + DEBUG_LOG("att_write_callback notifications enabled %d\n", le_notification_enabled); + if (le_notification_enabled) { + att_server_request_can_send_now_event(con_handle); + } + return 0; +} + +static void doorbell_notification_fn(__unused void * arg) { + att_server_request_can_send_now_event(con_handle); +} +static btstack_context_callback_registration_t doorbell_notification = { .callback = doorbell_notification_fn }; + +static void button_irq_handler(uint gpio, uint32_t events) { + uint16_t new_state = 0; + if (events & GPIO_IRQ_EDGE_FALL) new_state = 1; + if (events & GPIO_IRQ_EDGE_RISE) new_state = 0; + if (current_state != new_state) { + current_state = new_state; + DEBUG_LOG("button_irq_handler state %u notifications enabled %d\n", current_state, le_notification_enabled); + if (le_notification_enabled) { + // We want to call att_server_request_can_send_now_event, + // but we are in an IRQ and so it's dangerous to call BTStack directly from here. + // So we ask BTstack to callback a function from the "main thread" + // We could also use an async (when pending) worker using async_when_pending_worker_t, + // async_context_add_when_pending_worker and async_context_set_work_pending + btstack_run_loop_execute_on_main_thread(&doorbell_notification); + } + } +} + +static void heartbeat_handler(struct btstack_timer_source *ts) { + static uint32_t counter = 0; + counter++; + + // Invert the led + static int led_on = true; + led_on = !led_on; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + + // Restart timer + btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(ts); +} + +int main() { + stdio_init_all(); + + gpio_init(BUTTON_GPIO_NUM); + gpio_set_dir(BUTTON_GPIO_NUM, GPIO_IN); + gpio_pull_up(BUTTON_GPIO_NUM); + gpio_set_irq_enabled_with_callback(BUTTON_GPIO_NUM, GPIO_IRQ_EDGE_FALL|GPIO_IRQ_EDGE_RISE, true, &button_irq_handler); + + // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) + if (cyw43_arch_init()) { + printf("failed to initialise cyw43_arch\n"); + return -1; + } + + l2cap_init(); + sm_init(); + + att_server_init(profile_data, att_read_callback, att_write_callback); + + // inform about BTstack state + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + // register for ATT event + att_server_register_packet_handler(packet_handler); + + // set one-shot btstack timer + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(&heartbeat); + + // turn on bluetooth! + hci_power_control(HCI_POWER_ON); + + // btstack_run_loop_execute is only required when using the 'polling' method (e.g. using pico_cyw43_arch_poll library). + // This example uses the 'threadsafe background` method, where BT work is handled in a low priority IRQ, so it + // is fine to call bt_stack_run_loop_execute() but equally you can continue executing user code. + +#if 0 // btstack_run_loop_execute() is not required, so lets not use it + btstack_run_loop_execute(); +#else + // this core is free to do it's own stuff except when using 'polling' method (in which case you should use + // btstacK_run_loop_ methods to add work to the run loop. + + // this is a forever loop in place of where user code would go. + while(true) { + sleep_ms(1000); + } +#endif + return 0; +} diff --git a/bluetooth/secure_temp_sensor/CMakeLists.txt b/bluetooth/secure_temp_sensor/CMakeLists.txt new file mode 100644 index 000000000..82aa02c53 --- /dev/null +++ b/bluetooth/secure_temp_sensor/CMakeLists.txt @@ -0,0 +1,74 @@ +# Select a security setting to explore the BLE security +# +# security setting 0: Just works (pairing), no MITM (Man In The Middle) protection +# client and server have no input or output support +# +# security setting 1: Numeric comparison with MITM protection +# client can query yes or no from the user, server has a display only +# server displays passkey +# client displays passkey and user can select Yes or No if they agree the passkey is from the server +# +# security setting 2: +# client has a keyboard and display, server has a display only +# server displays passkey +# client user enters the passkey displayed by the server +# +# security setting 3: +# client has a display only, server has a display and keyboard +# Client displays passkey +# server user enters the passkey displayed by the server +if (NOT DEFINED SECURITY_SETTING) + set(SECURITY_SETTING 1) +endif() + +# Standalone example that reads from the on board temperature sensor and sends notifications via BLE +# Flashes slowly each second to show it's running +add_executable(secure_temp_server + server.c +) +target_link_libraries(secure_temp_server + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + hardware_adc +) +target_include_directories(secure_temp_server PRIVATE + ${CMAKE_CURRENT_LIST_DIR} # For our btstack config +) +target_compile_definitions(secure_temp_server PRIVATE + # Wait up to 3s for stdio USB to initialise, to avoid losing any debug on startup + PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS=3000 + SECURITY_SETTING=${SECURITY_SETTING} + #WANT_HCI_DUMP=1 +) +pico_btstack_make_gatt_header(secure_temp_server PRIVATE "${CMAKE_CURRENT_LIST_DIR}/temp_sensor.gatt") +pico_add_extra_outputs(secure_temp_server) +pico_enable_stdio_usb(secure_temp_server 1) +pico_enable_stdio_uart(secure_temp_server 1) + +# Standalone example that connects to secure_temp_server and reads the temperature +# Flahes once quickly each second when it's running but not connected to another device +# Flashes twice quickly each second when connected to another device and reading it's temperature +add_executable(secure_temp_client + client.c + ) +target_link_libraries(secure_temp_client + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + hardware_adc + ) +target_include_directories(secure_temp_client PRIVATE + ${CMAKE_CURRENT_LIST_DIR} # For our btstack config + ) +target_compile_definitions(secure_temp_client PRIVATE + # Wait up to 3s for stdio USB to initialise, to avoid losing any debug on startup + PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS=3000 + SECURITY_SETTING=${SECURITY_SETTING} + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(secure_temp_client) +pico_enable_stdio_usb(secure_temp_client 1) +pico_enable_stdio_uart(secure_temp_client 1) diff --git a/bluetooth/secure_temp_sensor/README.md b/bluetooth/secure_temp_sensor/README.md new file mode 100644 index 000000000..9fa185202 --- /dev/null +++ b/bluetooth/secure_temp_sensor/README.md @@ -0,0 +1,28 @@ +### Secure temp sensor + +This example uses BLE to communicate temperature between a pair of pico Ws. This example is a variant of temp sensor, using LE secure to provide a secure connection. + +secure_temp_server is a peripheral or server that transmits its temperature to another device +secure_temp_client is a client that reads a temperature from another device + +In server.c and client.c there is a define SECURITY_SETTING which you can change to explore different security options: + +security setting 0: Just works (pairing), no MITM (Man In The Middle) protection + client and server have no input or output support + +security setting 1: Numeric comparison with MITM protection + client can query yes or no from the user, server has a display only + server displays passkey + client displays passkey and user can select Yes or No if they agree the passkey is from the server + +security setting 2: + client has a keyboard and display, server has a display only + server displays passkey + client user enters the passkey displayed by the server + +security setting 3: + client has a display only, server has a display and keyboard + Client displays passkey + server user enters the passkey displayed by the server + +You will need to use the console with both devices to see the passkeys and answer security prompts. Both stdio over UART and USB are enabled so you can use either. diff --git a/bluetooth/secure_temp_sensor/btstack_config.h b/bluetooth/secure_temp_sensor/btstack_config.h new file mode 100644 index 000000000..0dd0d82cb --- /dev/null +++ b/bluetooth/secure_temp_sensor/btstack_config.h @@ -0,0 +1,6 @@ +#ifndef _PICO_BTSTACK_CONFIG_H +#define _PICO_BTSTACK_CONFIG_H + +#include "../config/btstack_config_common.h" + +#endif diff --git a/bluetooth/secure_temp_sensor/client.c b/bluetooth/secure_temp_sensor/client.c new file mode 100644 index 000000000..36b331139 --- /dev/null +++ b/bluetooth/secure_temp_sensor/client.c @@ -0,0 +1,543 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "btstack.h" +#include "pico/cyw43_arch.h" +#include "pico/stdlib.h" +#include "inttypes.h" +#include "string.h" + +#ifndef NDEBUG +#define DEBUG_LOG(...) printf(__VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif +#define INFO_LOG printf +#define ERROR_LOG printf + +#define LED_QUICK_FLASH_DELAY_MS 100 +#define LED_SLOW_FLASH_DELAY_MS 1000 + +typedef enum { + TC_OFF, + TC_IDLE, + TC_W4_SCAN_RESULT, + TC_W4_CONNECT, + TC_W4_SERVICE_RESULT, + TC_W4_CHARACTERISTIC_RESULT, + TC_W4_ENABLE_NOTIFICATIONS_COMPLETE, + TC_W4_READY +} gc_state_t; + +static btstack_packet_callback_registration_t hci_event_callback_registration; +static btstack_packet_callback_registration_t sm_event_callback_registration; +static gc_state_t state = TC_OFF; +static bd_addr_t server_addr; +static bd_addr_type_t server_addr_type; +static hci_con_handle_t connection_handle; +static gatt_client_service_t server_service; +static gatt_client_characteristic_t server_characteristic; +static bool listener_registered; +static gatt_client_notification_t notification_listener; +static btstack_timer_source_t heartbeat; + +// Select a security setting to explore the BLE security +// +// security setting 0: Just works (pairing), no MITM (Man In The Middle) protection +// client and server have no input or output support +// +// security setting 1: Numeric comparison with MITM protection +// client can query yes or no from the user, server has a display only +// server generates and displays passkey +// client displays passkey and user can select Yes or No if they agree the passkey is from the server +// +// security setting 2: +// client has a keyboard and display, server has a display only +// server generates and displays passkey +// client user enters the passkey displayed by the server +// +// security setting 3: +// client has a display only, server has a display and keyboard +// Client generates and displays passkey +// server user enters the passkey displayed by the server +#ifndef SECURITY_SETTING +#define SECURITY_SETTING 1 +#endif + +static void configure_security(int security_setting) { + DEBUG_LOG("Security setting %u selected.\n", security_setting); + sm_set_secure_connections_only_mode(true); + switch (security_setting) { + case 0: + sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION); + break; + case 1: + sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_YES_NO); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION); + break; + case 2: + sm_set_io_capabilities(IO_CAPABILITY_KEYBOARD_DISPLAY); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION); + break; + case 3: + sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION); + break; + default: + assert(false); + ERROR_LOG("invalid security setting %u", security_setting); + break; + } +} + +static void client_start(void){ + DEBUG_LOG("Start scanning!\n"); + state = TC_W4_SCAN_RESULT; + gap_set_scan_parameters(0,0x0030, 0x0030); + gap_start_scan(); +} + +static bool advertisement_report_contains_service(uint16_t service, uint8_t *advertisement_report){ + // get advertisement from report event + const uint8_t * adv_data = gap_event_advertising_report_get_data(advertisement_report); + uint8_t adv_len = gap_event_advertising_report_get_data_length(advertisement_report); + + // iterate over advertisement data + ad_context_t context; + for (ad_iterator_init(&context, adv_len, adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){ + uint8_t data_type = ad_iterator_get_data_type(&context); + uint8_t data_size = ad_iterator_get_data_len(&context); + const uint8_t * data = ad_iterator_get_data(&context); + switch (data_type){ + case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: + for (int i = 0; i < data_size; i += 2) { + uint16_t type = little_endian_read_16(data, i); + if (type == service) return true; + } + default: + break; + } + } + return false; +} + +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); + + uint8_t att_status; + switch(state){ + case TC_W4_SERVICE_RESULT: + switch(hci_event_packet_get_type(packet)) { + case GATT_EVENT_SERVICE_QUERY_RESULT: + // store service (we expect only one) + DEBUG_LOG("Storing service\n"); + gatt_event_service_query_result_get_service(packet, &server_service); + break; + case GATT_EVENT_QUERY_COMPLETE: + att_status = gatt_event_query_complete_get_att_status(packet); + if (att_status != ATT_ERROR_SUCCESS){ + ERROR_LOG("SERVICE_QUERY_RESULT, ATT Error 0x%02x.\n", att_status); + gap_disconnect(connection_handle); + break; + } + // service query complete, look for characteristic + state = TC_W4_CHARACTERISTIC_RESULT; + DEBUG_LOG("Search for env sensing characteristic.\n"); + gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &server_service, ORG_BLUETOOTH_CHARACTERISTIC_TEMPERATURE); + break; + default: + break; + } + break; + case TC_W4_CHARACTERISTIC_RESULT: + switch(hci_event_packet_get_type(packet)) { + case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: + DEBUG_LOG("Storing characteristic\n"); + gatt_event_characteristic_query_result_get_characteristic(packet, &server_characteristic); + break; + case GATT_EVENT_QUERY_COMPLETE: + att_status = gatt_event_query_complete_get_att_status(packet); + if (att_status != ATT_ERROR_SUCCESS){ + ERROR_LOG("CHARACTERISTIC_QUERY_RESULT, ATT Error 0x%02x.\n", att_status); + gap_disconnect(connection_handle); + break; + } + // register handler for notifications + listener_registered = true; + gatt_client_listen_for_characteristic_value_updates(¬ification_listener, handle_gatt_client_event, connection_handle, &server_characteristic); + // enable notifications + DEBUG_LOG("Enable notify on characteristic.\n"); + state = TC_W4_ENABLE_NOTIFICATIONS_COMPLETE; + gatt_client_write_client_characteristic_configuration(handle_gatt_client_event, connection_handle, + &server_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); + break; + default: + break; + } + break; + case TC_W4_ENABLE_NOTIFICATIONS_COMPLETE: + switch(hci_event_packet_get_type(packet)) { + case GATT_EVENT_QUERY_COMPLETE: + DEBUG_LOG("Notifications enabled, ATT status 0x%02x\n", gatt_event_query_complete_get_att_status(packet)); + if (gatt_event_query_complete_get_att_status(packet) != ATT_ERROR_SUCCESS) break; + state = TC_W4_READY; + break; + default: + break; + } + break; + case TC_W4_READY: + switch(hci_event_packet_get_type(packet)) { + case GATT_EVENT_NOTIFICATION: { + uint16_t value_length = gatt_event_notification_get_value_length(packet); + const uint8_t *value = gatt_event_notification_get_value(packet); + if (value_length == 2) { + float temp = little_endian_read_16(value, 0); + INFO_LOG("read temp %.2f degc\n", temp / 100); + } else { + ERROR_LOG("Unexpected length %d\n", value_length); + } + break; + } + default: + ERROR_LOG("Unknown packet type 0x%02x\n", hci_event_packet_get_type(packet)); + break; + } + break; + default: + assert(false); + ERROR_LOG("error bad state %u\n", state); + break; + } +} + +static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + UNUSED(size); + UNUSED(channel); + bd_addr_t local_addr; + if (packet_type != HCI_EVENT_PACKET) return; + hci_con_handle_t con_handle; + uint8_t status; + + uint8_t event_type = hci_event_packet_get_type(packet); + switch(event_type){ + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) { + gap_local_bd_addr(local_addr); + DEBUG_LOG("BTstack up and running on %s.\n", bd_addr_to_str(local_addr)); + client_start(); + } else { + state = TC_OFF; + } + break; + case GAP_EVENT_ADVERTISING_REPORT: + if (state != TC_W4_SCAN_RESULT) return; + // check name in advertisement + if (!advertisement_report_contains_service(ORG_BLUETOOTH_SERVICE_ENVIRONMENTAL_SENSING, packet)) return; + // store address and type + gap_event_advertising_report_get_address(packet, server_addr); + server_addr_type = gap_event_advertising_report_get_address_type(packet); + // stop scanning, and connect to the device + state = TC_W4_CONNECT; + gap_stop_scan(); + DEBUG_LOG("Connecting to device with addr %s.\n", bd_addr_to_str(server_addr)); + gap_connect(server_addr, server_addr_type); + break; + case HCI_EVENT_LE_META: + // wait for connection complete + switch (hci_event_le_meta_get_subevent_code(packet)) { + case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: + if (state != TC_W4_CONNECT) return; + connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); + // initialize gatt client context with handle, and add it to the list of active clients + // query primary services + DEBUG_LOG("Search for env sensing service.\n"); + state = TC_W4_SERVICE_RESULT; + gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, connection_handle, ORG_BLUETOOTH_SERVICE_ENVIRONMENTAL_SENSING); + break; + default: + break; + } + break; + case HCI_EVENT_META_GAP: + // wait for connection complete + if (hci_event_gap_meta_get_subevent_code(packet) != GAP_SUBEVENT_LE_CONNECTION_COMPLETE) break; + con_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); + DEBUG_LOG("Connection complete\n"); + sm_request_pairing(con_handle); + break; + case GATT_EVENT_QUERY_COMPLETE: + status = gatt_event_query_complete_get_att_status(packet); + switch (status){ + case ATT_ERROR_INSUFFICIENT_ENCRYPTION: + ERROR_LOG("GATT Query result: Insufficient Encryption\n"); + break; + case ATT_ERROR_INSUFFICIENT_AUTHENTICATION: + ERROR_LOG("GATT Query result: Insufficient Authentication\n"); + break; + case ATT_ERROR_BONDING_INFORMATION_MISSING: + ERROR_LOG("GATT Query result: Bonding Information Missing\n"); + break; + case ATT_ERROR_SUCCESS: + DEBUG_LOG("GATT Query result: OK\n"); + break; + default: + assert(false); + ERROR_LOG("Unexpected GATT Query result: 0x%02x\n", gatt_event_query_complete_get_att_status(packet)); + break; + } + break; + case HCI_EVENT_DISCONNECTION_COMPLETE: + // unregister listener + connection_handle = HCI_CON_HANDLE_INVALID; + if (listener_registered){ + listener_registered = false; + gatt_client_stop_listening_for_characteristic_value_updates(¬ification_listener); + } + DEBUG_LOG("Disconnected %s\n", bd_addr_to_str(server_addr)); + if (state == TC_OFF) break; + client_start(); + break; + default: + break; + } +} + +static uint32_t passkey_result; +static uint8_t passkey_type; +static hci_con_handle_t passkey_handle; + +// Send passkey result to BTStack +static void passkey_done_fn(__unused void * arg) { + switch (passkey_type) { + case SM_EVENT_NUMERIC_COMPARISON_REQUEST: { + if (passkey_result) { + DEBUG_LOG("SM_EVENT_NUMERIC_COMPARISON_REQUEST confirm\n"); + sm_numeric_comparison_confirm(passkey_handle); + } else { + DEBUG_LOG("SM_EVENT_NUMERIC_COMPARISON_REQUEST decline\n"); + sm_bonding_decline(passkey_handle); + } + break; + } + case SM_EVENT_PASSKEY_INPUT_NUMBER: { + DEBUG_LOG("SM_EVENT_PASSKEY_INPUT_NUMBER %u\n", passkey_result); + sm_passkey_input(passkey_handle, passkey_result); + break; + } + default: { + assert(false); // should not happen! + ERROR_LOG("passkey_done_fn: Unexpected passkey type %u\n", passkey_type); + break; + } + } +} +static btstack_context_callback_registration_t passkey_done = { .callback = passkey_done_fn }; + +// Handle a key press +static void passkey_press_callback(__unused void *user_data) { + int key = getchar_timeout_us(0); // get any pending key press but don't wait + switch (passkey_type) { + case SM_EVENT_NUMERIC_COMPARISON_REQUEST: { + if (key == 'y' || key == 'Y') { + passkey_result = 1; + } else { + passkey_result = 0; + } + // We want to call a BTStack function now, + // but we might be in an IRQ and so it's dangerous to call BTStack directly from here. + // So we ask BTstack to callback a function from the "main thread" + // We could also use an async (when pending) worker using async_when_pending_worker_t, + // async_context_add_when_pending_worker and async_context_set_work_pending + btstack_run_loop_execute_on_main_thread(&passkey_done); + break; + } + case SM_EVENT_PASSKEY_INPUT_NUMBER: { + if (key == 13) { + // See above comment + INFO_LOG("\n"); + btstack_run_loop_execute_on_main_thread(&passkey_done); + } else if (key >= '0' && key <= '9') { + INFO_LOG("%c", key); + passkey_result = (passkey_result * 10) + (key - '0'); + } + break; + } + default: { + assert(false); // should not happen! + ERROR_LOG("passkey_callback: Unexpected passkey type %u\n", passkey_type); + 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: + INFO_LOG("Just works requested\n"); + sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); + break; + case SM_EVENT_NUMERIC_COMPARISON_REQUEST: + // Ask the user if the passkey matches the number shown by the server + INFO_LOG("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet)); + INFO_LOG("Is the server showing this number? (y/n)\n"); + // We want to "wait" for user input but we must not block, + // so use a stdio callback to tell us when characters are available + passkey_type = SM_EVENT_NUMERIC_COMPARISON_REQUEST; + passkey_handle = sm_event_passkey_display_number_get_handle(packet); + stdio_set_chars_available_callback(passkey_press_callback, NULL); + break; + case SM_EVENT_PASSKEY_DISPLAY_NUMBER: + INFO_LOG("Displaying passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); + break; + case SM_EVENT_PASSKEY_INPUT_NUMBER: + INFO_LOG("Enter the passkey shown by the server:\n"); + // We want to "wait" for user input but we must not block, + // so use a stdio callback to tell us when characters are available + passkey_result = 0; + passkey_type = SM_EVENT_PASSKEY_INPUT_NUMBER; + passkey_handle = sm_event_passkey_input_number_get_handle(packet); + stdio_set_chars_available_callback(passkey_press_callback, NULL); + break; + case SM_EVENT_PAIRING_STARTED: + DEBUG_LOG("Pairing started\n"); + break; + case SM_EVENT_PAIRING_COMPLETE: + stdio_set_chars_available_callback(NULL, NULL); // stop key press notifications + switch (sm_event_pairing_complete_get_status(packet)){ + case ERROR_CODE_SUCCESS: + DEBUG_LOG("Pairing complete, success\n"); + break; + case ERROR_CODE_CONNECTION_TIMEOUT: + ERROR_LOG("Pairing failed, timeout\n"); + break; + case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: + ERROR_LOG("Pairing failed, disconnected\n"); + break; + case ERROR_CODE_AUTHENTICATION_FAILURE: + ERROR_LOG("Pairing failed, authentication failure with reason = %u\n", sm_event_pairing_complete_get_reason(packet)); + break; + default: + assert(false); + ERROR_LOG("Unexpected pairing status %d\n", sm_event_pairing_complete_get_status(packet)); + break; + } + break; + case SM_EVENT_REENCRYPTION_STARTED: + sm_event_reencryption_complete_get_address(packet, addr); + DEBUG_LOG("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: + DEBUG_LOG("Re-encryption complete, success\n"); + break; + case ERROR_CODE_CONNECTION_TIMEOUT: + ERROR_LOG("Re-encryption failed, timeout\n"); + break; + case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: + ERROR_LOG("Re-encryption failed, disconnected\n"); + break; + case ERROR_CODE_PIN_OR_KEY_MISSING: + ERROR_LOG("Re-encryption failed, bonding information missing\n\n"); + ERROR_LOG("Assuming remote lost bonding information\n"); + ERROR_LOG("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; + } +} + +static void heartbeat_handler(struct btstack_timer_source *ts) { + // Invert the led + static bool quick_flash; + static bool led_on = true; + + led_on = !led_on; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + if (listener_registered && led_on) { + quick_flash = !quick_flash; + } else if (!listener_registered) { + quick_flash = false; + } + + // Restart timer + btstack_run_loop_set_timer(ts, (led_on || quick_flash) ? LED_QUICK_FLASH_DELAY_MS : LED_SLOW_FLASH_DELAY_MS); + btstack_run_loop_add_timer(ts); +} + +int main() { + stdio_init_all(); + + // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) + if (cyw43_arch_init()) { + ERROR_LOG("failed to initialise cyw43_arch\n"); + return -1; + } + + l2cap_init(); + sm_init(); + + // setup empty ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android and iOS + att_server_init(NULL, NULL, NULL); + + gatt_client_init(); + + hci_event_callback_registration.callback = &hci_packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + sm_event_callback_registration.callback = &sm_packet_handler; + sm_add_event_handler(&sm_event_callback_registration); + + // apply security configuration settings + configure_security(SECURITY_SETTING); + + // set one-shot btstack timer + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, LED_SLOW_FLASH_DELAY_MS); + btstack_run_loop_add_timer(&heartbeat); + + // turn on! + hci_power_control(HCI_POWER_ON); + + // btstack_run_loop_execute is only required when using the 'polling' method (e.g. using pico_cyw43_arch_poll library). + // This example uses the 'threadsafe background` method, where BT work is handled in a low priority IRQ, so it + // is fine to call bt_stack_run_loop_execute() but equally you can continue executing user code. + +#if 1 // this is only necessary when using polling (which we aren't, but we're showing it is still safe to call in this case) + btstack_run_loop_execute(); +#else + // this core is free to do it's own stuff except when using 'polling' method (in which case you should use + // btstacK_run_loop_ methods to add work to the run loop. + + // this is a forever loop in place of where user code would go. + while(true) { + sleep_ms(1000); + } +#endif + return 0; +} \ No newline at end of file diff --git a/bluetooth/secure_temp_sensor/server.c b/bluetooth/secure_temp_sensor/server.c new file mode 100644 index 000000000..42e9ac1eb --- /dev/null +++ b/bluetooth/secure_temp_sensor/server.c @@ -0,0 +1,435 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "btstack.h" +#include "pico/cyw43_arch.h" +#include "pico/btstack_cyw43.h" +#include "hardware/adc.h" +#include "pico/stdlib.h" +#include "temp_sensor.h" +#include +#include + +#ifndef NDEBUG +#define DEBUG_LOG(...) printf(__VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif +#define INFO_LOG printf +#define ERROR_LOG printf + +#define HEARTBEAT_PERIOD_MS 1000 +#define ADC_CHANNEL_TEMPSENSOR 4 + +#define APP_AD_FLAGS 0x06 +static uint8_t adv_data[] = { + // Flags general discoverable + 0x02, BLUETOOTH_DATA_TYPE_FLAGS, APP_AD_FLAGS, + // Name + 0x17, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'P', 'i', 'c', 'o', ' ', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', + 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, 0x1a, 0x18, +}; +static const uint8_t adv_data_len = sizeof(adv_data); + +int le_notification_enabled; +static hci_con_handle_t con_handle; +static uint16_t current_temp; + +extern uint8_t const profile_data[]; +static void poll_temp(void); + +static btstack_timer_source_t heartbeat; +static btstack_packet_callback_registration_t hci_event_callback_registration; +static btstack_packet_callback_registration_t sm_event_callback_registration; + +// Select a security setting to explore the BLE security +// +// security setting 0: Just works (pairing), no MITM (Man In The Middle) protection +// client and server have no input or output support +// +// security setting 1: Numeric comparison with MITM protection +// client can query yes or no from the user, server has a display only +// server generates and displays passkey +// client displays passkey and user can select Yes or No if they agree the passkey is from the server +// +// security setting 2: +// client has a keyboard and display, server has a display only +// server generates and displays passkey +// client user enters the passkey displayed by the server +// +// security setting 3: +// client has a display only, server has a display and keyboard +// Client generates and displays passkey +// server user enters the passkey displayed by the server +#ifndef SECURITY_SETTING +#define SECURITY_SETTING 1 +#endif + +static void configure_security(int security_setting) { + DEBUG_LOG("Security setting %u selected.\n", security_setting); + sm_set_secure_connections_only_mode(true); + switch (security_setting) { + case 0: + sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION); + break; + case 1: + sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_YES_NO); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION); + break; + case 2: + sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION); + break; + case 3: + sm_set_io_capabilities(IO_CAPABILITY_KEYBOARD_ONLY); + sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION); + break; + default: + assert(false); + ERROR_LOG("invalid security setting %u", security_setting); + break; + } +} + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + UNUSED(size); + UNUSED(channel); + bd_addr_t local_addr; + if (packet_type != HCI_EVENT_PACKET) return; + + uint8_t event_type = hci_event_packet_get_type(packet); + switch(event_type){ + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; + gap_local_bd_addr(local_addr); + DEBUG_LOG("BTstack up and running on %s.\n", bd_addr_to_str(local_addr)); + + // setup advertisements + uint16_t adv_int_min = 800; + uint16_t adv_int_max = 800; + 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); + assert(adv_data_len <= 31); // ble limitation + gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); + gap_advertisements_enable(1); + + poll_temp(); + + break; + case HCI_EVENT_DISCONNECTION_COMPLETE: + le_notification_enabled = 0; + break; + case ATT_EVENT_CAN_SEND_NOW: + att_server_notify(con_handle, ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_TEMPERATURE_01_VALUE_HANDLE, (uint8_t*)¤t_temp, sizeof(current_temp)); + break; + default: + break; + } +} + +static uint32_t passkey_result; +static uint8_t passkey_type; +static hci_con_handle_t passkey_handle; + +// Send passkey result to BTStack +static void passkey_done_fn(__unused void * arg) { + switch (passkey_type) { + case SM_EVENT_PASSKEY_INPUT_NUMBER: { + DEBUG_LOG("SM_EVENT_PASSKEY_INPUT_NUMBER %u\n", passkey_result); + sm_passkey_input(passkey_handle, passkey_result); + break; + } + default: { + assert(false); // should not happen! + ERROR_LOG("passkey_done_fn: Unexpected passkey type %u\n", passkey_type); + break; + } + } +} +static btstack_context_callback_registration_t passkey_done = { .callback = passkey_done_fn }; + +// Handle a key press +static void passkey_press_callback(__unused void *user_data) { + int key = getchar_timeout_us(0); // get any pending key press but don't wait + switch (passkey_type) { + case SM_EVENT_PASSKEY_INPUT_NUMBER: { + if (key == 13) { + // See above comment + INFO_LOG("\n"); + btstack_run_loop_execute_on_main_thread(&passkey_done); + } else if (key >= '0' && key <= '9') { + INFO_LOG("%c", key); + passkey_result = (passkey_result * 10) + (key - '0'); + } + break; + } + default: { + assert(false); // should not happen! + ERROR_LOG("passkey_callback: Unexpected passkey type %u\n", passkey_type); + 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; + + hci_con_handle_t con_handle; + bd_addr_t addr; + bd_addr_type_t addr_type; + uint8_t status; + + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_META_GAP: + switch (hci_event_gap_meta_get_subevent_code(packet)) { + case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: + DEBUG_LOG("Connection complete\n"); + con_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); + sm_request_pairing(con_handle); + break; + default: + break; + } + break; + case SM_EVENT_JUST_WORKS_REQUEST: + INFO_LOG("Just Works requested\n"); + sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); + break; + case SM_EVENT_NUMERIC_COMPARISON_REQUEST: + // Server always confirms the comparison + INFO_LOG("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: + INFO_LOG("Displaying passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); + break; + case SM_EVENT_PASSKEY_INPUT_NUMBER: + INFO_LOG("Enter the passkey shown by the client:\n"); + // We want to "wait" for user input but we must not block, + // so use a stdio callback to tell us when characters are available + passkey_result = 0; + passkey_type = SM_EVENT_PASSKEY_INPUT_NUMBER; + passkey_handle = sm_event_passkey_input_number_get_handle(packet); + stdio_set_chars_available_callback(passkey_press_callback, NULL); + break; + case SM_EVENT_IDENTITY_CREATED: + sm_event_identity_created_get_identity_address(packet, addr); + DEBUG_LOG("Identity created: type %u address %s\n", sm_event_identity_created_get_identity_addr_type(packet), bd_addr_to_str(addr)); + break; + case SM_EVENT_IDENTITY_RESOLVING_SUCCEEDED: + sm_event_identity_resolving_succeeded_get_identity_address(packet, addr); + DEBUG_LOG("Identity resolved: type %u address %s\n", sm_event_identity_resolving_succeeded_get_identity_addr_type(packet), bd_addr_to_str(addr)); + break; + case SM_EVENT_IDENTITY_RESOLVING_FAILED: + sm_event_identity_created_get_address(packet, addr); + DEBUG_LOG("Identity resolving failed\n"); + break; + case SM_EVENT_PAIRING_STARTED: + DEBUG_LOG("Pairing started\n"); + break; + case SM_EVENT_PAIRING_COMPLETE: + switch (sm_event_pairing_complete_get_status(packet)){ + case ERROR_CODE_SUCCESS: + DEBUG_LOG("Pairing complete, success\n"); + break; + case ERROR_CODE_CONNECTION_TIMEOUT: + ERROR_LOG("Pairing failed, timeout\n"); + break; + case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: + ERROR_LOG("Pairing failed, disconnected\n"); + break; + case ERROR_CODE_AUTHENTICATION_FAILURE: + ERROR_LOG("Pairing failed, authentication failure with reason = %u\n", sm_event_pairing_complete_get_reason(packet)); + break; + default: + assert(false); + ERROR_LOG("Unexpected pairing status %d\n", sm_event_pairing_complete_get_status(packet)); + break; + } + break; + case SM_EVENT_REENCRYPTION_STARTED: + sm_event_reencryption_complete_get_address(packet, addr); + DEBUG_LOG("Bonding information exists for addr type %u, identity addr %s -> re-encryption started\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: + DEBUG_LOG("Re-encryption complete, success\n"); + break; + case ERROR_CODE_CONNECTION_TIMEOUT: + ERROR_LOG("Re-encryption failed, timeout\n"); + break; + case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: + ERROR_LOG("Re-encryption failed, disconnected\n"); + break; + case ERROR_CODE_PIN_OR_KEY_MISSING: + ERROR_LOG("Re-encryption failed, bonding information missing\n\n"); + ERROR_LOG("Assuming remote lost bonding information\n"); + ERROR_LOG("Deleting local bonding information to allow for 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); + break; + default: + break; + } + break; + case GATT_EVENT_QUERY_COMPLETE: + status = gatt_event_query_complete_get_att_status(packet); + switch (status){ + case ATT_ERROR_INSUFFICIENT_ENCRYPTION: + ERROR_LOG("GATT Query failed, Insufficient Encryption\n"); + break; + case ATT_ERROR_INSUFFICIENT_AUTHENTICATION: + ERROR_LOG("GATT Query failed, Insufficient Authentication\n"); + break; + case ATT_ERROR_BONDING_INFORMATION_MISSING: + ERROR_LOG("GATT Query failed, Bonding Information Missing\n"); + break; + case ATT_ERROR_SUCCESS: + DEBUG_LOG("GATT Query successful\n"); + break; + default: + assert(false); + ERROR_LOG("Unexpected GATT Query failed, status 0x%02x\n", gatt_event_query_complete_get_att_status(packet)); + break; + } + break; + default: + break; + } +} + +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) { + UNUSED(connection_handle); + + if (att_handle == ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_TEMPERATURE_01_VALUE_HANDLE){ + return att_read_callback_handle_blob((const uint8_t *)¤t_temp, sizeof(current_temp), offset, buffer, buffer_size); + } + 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_ORG_BLUETOOTH_CHARACTERISTIC_TEMPERATURE_01_CLIENT_CONFIGURATION_HANDLE) return 0; + le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; + con_handle = connection_handle; + if (le_notification_enabled) { + att_server_request_can_send_now_event(con_handle); + } + return 0; +} + +static void poll_temp(void) { + adc_select_input(ADC_CHANNEL_TEMPSENSOR); + uint32_t raw32 = adc_read(); + const uint32_t bits = 12; + + // Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16) + uint16_t raw16 = raw32 << (16 - bits) | raw32 >> (2 * bits - 16); + + // ref https://github.com/raspberrypi/pico-micropython-examples/blob/master/adc/temperature.py + const float conversion_factor = 3.3 / (65535); + float reading = raw16 * conversion_factor; + + // The temperature sensor measures the Vbe voltage of a biased bipolar diode, connected to the fifth ADC channel + // Typically, Vbe = 0.706V at 27 degrees C, with a slope of -1.721mV (0.001721) per degree. + float deg_c = 27 - (reading - 0.706) / 0.001721; + current_temp = deg_c * 100; + INFO_LOG("Write temp %.2f degc\n", deg_c); + } + +static void heartbeat_handler(struct btstack_timer_source *ts) { + static uint32_t counter = 0; + counter++; + + // Update the temp every 10s + if (counter % 10 == 0) { + poll_temp(); + if (le_notification_enabled) { + att_server_request_can_send_now_event(con_handle); + } + } + + // Invert the led + static int led_on = true; + led_on = !led_on; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + + // Restart timer + btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(ts); +} + +int main() { + stdio_init_all(); + + // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) + if (cyw43_arch_init()) { + ERROR_LOG("failed to initialise cyw43_arch\n"); + return -1; + } + + // Initialise adc for the temp sensor + adc_init(); + adc_select_input(ADC_CHANNEL_TEMPSENSOR); + adc_set_temp_sensor_enabled(true); + + l2cap_init(); + sm_init(); + + att_server_init(profile_data, att_read_callback, att_write_callback); + + // inform about BTstack state + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + // sm packet handler + sm_event_callback_registration.callback = &sm_packet_handler; + sm_add_event_handler(&sm_event_callback_registration); + + // apply security configuration settings + configure_security(SECURITY_SETTING); + + // register for ATT event + att_server_register_packet_handler(packet_handler); + + // set one-shot btstack timer + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(&heartbeat); + + // turn on bluetooth! + hci_power_control(HCI_POWER_ON); + + // btstack_run_loop_execute is only required when using the 'polling' method (e.g. using pico_cyw43_arch_poll library). + // This example uses the 'threadsafe background` method, where BT work is handled in a low priority IRQ, so it + // is fine to call bt_stack_run_loop_execute() but equally you can continue executing user code. + +#if 0 // btstack_run_loop_execute() is not required, so lets not use it + btstack_run_loop_execute(); +#else + // this core is free to do it's own stuff except when using 'polling' method (in which case you should use + // btstacK_run_loop_ methods to add work to the run loop. + + // this is a forever loop in place of where user code would go. + while(true) { + sleep_ms(1000); + } +#endif + return 0; +} diff --git a/bluetooth/secure_temp_sensor/temp_sensor.gatt b/bluetooth/secure_temp_sensor/temp_sensor.gatt new file mode 100644 index 000000000..e0219483e --- /dev/null +++ b/bluetooth/secure_temp_sensor/temp_sensor.gatt @@ -0,0 +1,8 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "secure_picow_temp" + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_DATABASE_HASH, READ, + +PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_ENVIRONMENTAL_SENSING +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_TEMPERATURE, READ | NOTIFY | INDICATE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, diff --git a/bluetooth/temp_sensor/CMakeLists.txt b/bluetooth/temp_sensor/CMakeLists.txt new file mode 100644 index 000000000..93b5ebc96 --- /dev/null +++ b/bluetooth/temp_sensor/CMakeLists.txt @@ -0,0 +1,44 @@ +# Example that reads from the on board temperature sensor and sends notifications via BLE +# Flashes slowly each second to show it's running +add_executable(temp_server + server.c + ) +target_link_libraries(temp_server + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + hardware_adc + ) +target_include_directories(temp_server PRIVATE + ${CMAKE_CURRENT_LIST_DIR} # For our btstack config + ) +target_compile_definitions(temp_server PRIVATE + #WANT_HCI_DUMP=1 +) +pico_btstack_make_gatt_header(temp_server PRIVATE "${CMAKE_CURRENT_LIST_DIR}/temp_sensor.gatt") + +pico_add_extra_outputs(temp_server) +example_auto_set_url(temp_server) + +# Example that connects to temp_server and reads the temperature +# Flashes once quickly each second when it's running but not connected to another device +# Flashes twice quickly each second when connected to another device and reading it's temperature +add_executable(temp_reader + client.c + ) +target_link_libraries(temp_reader + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + hardware_adc + ) +target_include_directories(temp_reader PRIVATE + ${CMAKE_CURRENT_LIST_DIR} # For our btstack config + ) +target_compile_definitions(temp_reader PRIVATE + #WANT_HCI_DUMP=1 +) +pico_add_extra_outputs(temp_reader) +example_auto_set_url(temp_reader) diff --git a/bluetooth/temp_sensor/README.md b/bluetooth/temp_sensor/README.md new file mode 100644 index 000000000..f0d321120 --- /dev/null +++ b/bluetooth/temp_sensor/README.md @@ -0,0 +1,6 @@ +### Temp sensor + +This example uses BLE to communicate temperature between a pair of pico Ws. It reads temperature by measuring some onboard voltage and applying a conversion factor. + +temp_server is a peripheral or server that transmits its temperature to another device +temp_reader is a client that reads a temperature from another device \ No newline at end of file diff --git a/bluetooth/temp_sensor/btstack_config.h b/bluetooth/temp_sensor/btstack_config.h new file mode 100644 index 000000000..0dd0d82cb --- /dev/null +++ b/bluetooth/temp_sensor/btstack_config.h @@ -0,0 +1,6 @@ +#ifndef _PICO_BTSTACK_CONFIG_H +#define _PICO_BTSTACK_CONFIG_H + +#include "../config/btstack_config_common.h" + +#endif diff --git a/pico_w/bt/standalone/client/client.c b/bluetooth/temp_sensor/client.c similarity index 92% rename from pico_w/bt/standalone/client/client.c rename to bluetooth/temp_sensor/client.c index ca515ae3a..bbd9f7816 100644 --- a/pico_w/bt/standalone/client/client.c +++ b/bluetooth/temp_sensor/client.c @@ -9,11 +9,13 @@ #include "pico/cyw43_arch.h" #include "pico/stdlib.h" -#if 0 +#ifndef NDEBUG #define DEBUG_LOG(...) printf(__VA_ARGS__) #else #define DEBUG_LOG(...) #endif +#define INFO_LOG printf +#define ERROR_LOG printf #define LED_QUICK_FLASH_DELAY_MS 100 #define LED_SLOW_FLASH_DELAY_MS 1000 @@ -88,7 +90,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint case GATT_EVENT_QUERY_COMPLETE: att_status = gatt_event_query_complete_get_att_status(packet); if (att_status != ATT_ERROR_SUCCESS){ - printf("SERVICE_QUERY_RESULT, ATT Error 0x%02x.\n", att_status); + ERROR_LOG("SERVICE_QUERY_RESULT, ATT Error 0x%02x.\n", att_status); gap_disconnect(connection_handle); break; } @@ -110,7 +112,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint case GATT_EVENT_QUERY_COMPLETE: att_status = gatt_event_query_complete_get_att_status(packet); if (att_status != ATT_ERROR_SUCCESS){ - printf("CHARACTERISTIC_QUERY_RESULT, ATT Error 0x%02x.\n", att_status); + ERROR_LOG("CHARACTERISTIC_QUERY_RESULT, ATT Error 0x%02x.\n", att_status); gap_disconnect(connection_handle); break; } @@ -146,19 +148,20 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint DEBUG_LOG("Indication value len %d\n", value_length); if (value_length == 2) { float temp = little_endian_read_16(value, 0); - printf("read temp %.2f degc\n", temp / 100); + INFO_LOG("Read temp %.2f degc\n", temp / 100); } else { - printf("Unexpected length %d\n", value_length); + ERROR_LOG("Unexpected length %d\n", value_length); } break; } default: - printf("Unknown packet type 0x%02x\n", hci_event_packet_get_type(packet)); + ERROR_LOG("Unknown packet type 0x%02x\n", hci_event_packet_get_type(packet)); break; } break; default: - printf("error\n"); + assert(false); + ERROR_LOG("Unexpected state %d\n", state); break; } } @@ -174,7 +177,7 @@ static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *pa case BTSTACK_EVENT_STATE: if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) { gap_local_bd_addr(local_addr); - printf("BTstack up and running on %s.\n", bd_addr_to_str(local_addr)); + DEBUG_LOG("BTstack up and running on %s.\n", bd_addr_to_str(local_addr)); client_start(); } else { state = TC_OFF; @@ -190,7 +193,7 @@ static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *pa // stop scanning, and connect to the device state = TC_W4_CONNECT; gap_stop_scan(); - printf("Connecting to device with addr %s.\n", bd_addr_to_str(server_addr)); + INFO_LOG("Connecting to device with addr %s.\n", bd_addr_to_str(server_addr)); gap_connect(server_addr, server_addr_type); break; case HCI_EVENT_LE_META: @@ -216,7 +219,7 @@ static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *pa listener_registered = false; gatt_client_stop_listening_for_characteristic_value_updates(¬ification_listener); } - printf("Disconnected %s\n", bd_addr_to_str(server_addr)); + INFO_LOG("Disconnected %s\n", bd_addr_to_str(server_addr)); if (state == TC_OFF) break; client_start(); break; @@ -248,7 +251,7 @@ int main() { // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) if (cyw43_arch_init()) { - printf("failed to initialise cyw43_arch\n"); + ERROR_LOG("failed to initialise cyw43_arch\n"); return -1; } diff --git a/pico_w/bt/standalone/server/server.c b/bluetooth/temp_sensor/server.c similarity index 100% rename from pico_w/bt/standalone/server/server.c rename to bluetooth/temp_sensor/server.c diff --git a/pico_w/bt/standalone/server/temp_sensor.gatt b/bluetooth/temp_sensor/temp_sensor.gatt similarity index 100% rename from pico_w/bt/standalone/server/temp_sensor.gatt rename to bluetooth/temp_sensor/temp_sensor.gatt diff --git a/bluetooth/wifi_provisioner/CMakeLists.txt b/bluetooth/wifi_provisioner/CMakeLists.txt new file mode 100644 index 000000000..520460494 --- /dev/null +++ b/bluetooth/wifi_provisioner/CMakeLists.txt @@ -0,0 +1,30 @@ +# Add provisioning libary +add_library(wifi_prov_lib INTERFACE) +target_sources(wifi_prov_lib INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/wifi_prov_lib.c +) +target_include_directories(wifi_prov_lib INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) +target_link_libraries(wifi_prov_lib INTERFACE + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + hardware_flash +) +pico_btstack_make_gatt_header(wifi_prov_lib INTERFACE "${CMAKE_CURRENT_LIST_DIR}/provisioning.gatt") + +# Example which uses the provisioning libary to allow credentials to be set over BLE +add_executable(wifi_provisioner + example.c +) +target_link_libraries(wifi_provisioner + pico_stdlib + wifi_prov_lib + pico_lwip_iperf + pico_cyw43_arch_lwip_threadsafe_background +) +target_include_directories(wifi_provisioner PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) +pico_add_extra_outputs(wifi_provisioner) diff --git a/bluetooth/wifi_provisioner/README.md b/bluetooth/wifi_provisioner/README.md new file mode 100644 index 000000000..6aad4f57d --- /dev/null +++ b/bluetooth/wifi_provisioner/README.md @@ -0,0 +1,78 @@ +### BLE wifi provisioning + +This example demonstrates provisioning wifi credentials using bluetooth low energy. +The pico saves the most recent set of succesful credentials in flash for future use. +Upon powering, the pico attemps to connect using the saved credentials. +If this fails, the pico sets up a GATT server which you can connect to using a mobile BLE scanner app or the attached python script. +The GATT server has 2 custom characteritics, one for ssid and one for password. +To write to these characteristics you can run 'python3 set_credentials.py ssid password address'. +To run set_credentials.py you have to install the "bleak" python library, e.g... + +``` +python3 -m venv venv +. venv/bin/activate +pip install bleak +``` + +From the on you just need to activate the python virtual environment + +``` +. venv/bin/activate +``` + +It takes 3 parameters, the ssid name, the password and the Bluetooth address of the device running this example, e.g. + +``` +python set_credentials.py "my ssid" "my password" 2C:CF:67:BE:08:05 +submitted ssid: my ssid +submitted password: my password +submitted address: 2C:CF:67:BE:08:05 +Connected: True +Writing SSID... +Writing password... +``` + +The example waits 3s for you to press `W` when it starts to make it wipe any stored ssid and password to help testing. + +On the pico you should something like this... + +``` +Waiting to receive ssid and password via BLE +Identity resolving failed +Connection complete +Pairing started +Just Works requested +Pairing complete, success +Setting SSID +Current saved SSID: "my ssid" +Current saved password length: 0 +Setting password +Current saved SSID: "my ssid" +Current saved password length: 7 +connect status: joining +connect status: no ip +connect status: link up +Succesfully provisioned credentials using wifi_prov_lib! +finished provisioning result=0 + +Ready, running iperf server at 10.3.194.230 +``` + +When connected to the internet the example runs iperf. +Press "D" to disconnect and the pico will reboot. +The next time it connects it should retrieve the ssid and password details from flash. + +``` +Read credentials +Current saved SSID: "my ssid" +BTstack up and running on 2C:CF:67:BE:08:05. +Current saved password length: 7 + +connect status: joining +connect status: no ip +connect status: link up +Connected. +finished provisioning result=0 + +Ready, running iperf server at 10.3.194.230 +``` diff --git a/bluetooth/wifi_provisioner/btstack_config.h b/bluetooth/wifi_provisioner/btstack_config.h new file mode 100644 index 000000000..0dd0d82cb --- /dev/null +++ b/bluetooth/wifi_provisioner/btstack_config.h @@ -0,0 +1,6 @@ +#ifndef _PICO_BTSTACK_CONFIG_H +#define _PICO_BTSTACK_CONFIG_H + +#include "../config/btstack_config_common.h" + +#endif diff --git a/bluetooth/wifi_provisioner/example.c b/bluetooth/wifi_provisioner/example.c new file mode 100644 index 000000000..6c08a5de6 --- /dev/null +++ b/bluetooth/wifi_provisioner/example.c @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" +#include "hardware/watchdog.h" +#include "lwip/apps/lwiperf.h" +#include "wifi_prov_lib.h" + +#ifndef PROV_TIMEOUT_MS +#define PROV_TIMEOUT_MS 120000 +#endif + +// Report IP results +static void iperf_report(void *arg, enum lwiperf_report_type report_type, + const ip_addr_t *local_addr, u16_t local_port, const ip_addr_t *remote_addr, u16_t remote_port, + u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec) { + static uint32_t total_iperf_megabytes = 0; + uint32_t mbytes = bytes_transferred / 1024 / 1024; + float mbits = bandwidth_kbitpsec / 1000.0; + total_iperf_megabytes += mbytes; + + printf("Completed iperf transfer of %d MBytes @ %.1f Mbits/sec\n", mbytes, mbits); + printf("Total iperf megabytes since start %d Mbytes\n", total_iperf_megabytes); +} + +// Note: This is called from an interrupt handler +void key_pressed_func(void *param) { + int key = getchar_timeout_us(0); // get any pending key press but don't wait + if (key == 'd' || key == 'D') { + bool *exit = (bool*)param; + *exit = true; + } +} + +int main(void) { + stdio_init_all(); + + // This is for testing + printf("Press 'w' in the next 3s to wipe stored ssid and password\n"); + int c = getchar_timeout_us(3000000); + if (c == 'w' || c == 'W') { + printf("Wiping stored ssid and password\n"); + erase_credentials(); + } + + // initialize CYW43 driver architecture (will enable BT because CYW43_ENABLE_BLUETOOTH == 1) + if (cyw43_arch_init()) { + printf("failed to initialise cyw43_arch\n"); + return PICO_ERROR_GENERIC; + } + + // if unable to connect with saved ssid and password, waits 120 seconds + // for new credentials to be provisioned over BLE + int rc = start_ble_wifi_provisioning(PROV_TIMEOUT_MS); + printf("finished provisioning result=%d\n", rc); + if (rc != PICO_OK) { + panic("Wifi provisioning failed"); + } + + // Run iperf server + cyw43_arch_lwip_begin(); + printf("\nReady, running iperf server at %s\n", ip4addr_ntoa(netif_ip4_addr(netif_list))); + lwiperf_start_tcp_server_default(&iperf_report, NULL); + cyw43_arch_lwip_end(); + + bool exit = false; + stdio_set_chars_available_callback(key_pressed_func, &exit); + + // Run forever + printf("Press 'd' to disconnect and reboot\n"); + while(!exit) { + cyw43_arch_poll(); + cyw43_arch_wait_for_work_until(at_the_end_of_time); + } + + printf("Rebooting example...\n"); + watchdog_enable(500, true); + sleep_ms(1000); + return 0; +} diff --git a/bluetooth/wifi_provisioner/lwipopts.h b/bluetooth/wifi_provisioner/lwipopts.h new file mode 100644 index 000000000..aa8ae03a7 --- /dev/null +++ b/bluetooth/wifi_provisioner/lwipopts.h @@ -0,0 +1 @@ +#include "../../pico_w/wifi/lwipopts_examples_common.h" diff --git a/bluetooth/wifi_provisioner/provisioning.gatt b/bluetooth/wifi_provisioner/provisioning.gatt new file mode 100644 index 000000000..d298585ad --- /dev/null +++ b/bluetooth/wifi_provisioner/provisioning.gatt @@ -0,0 +1,16 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "pico_wifi_provisioning" + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_DATABASE_HASH, READ, + +PRIMARY_SERVICE, 6bfacb8f-6b40-49f8-906e-53bd113c5cfb + +// SSID characteristic +CHARACTERISTIC, b1829813-e8ec-4621-b9b5-6c1be43fe223, READ | WRITE | NOTIFY | DYNAMIC, +CHARACTERISTIC_USER_DESCRIPTION, READ, + + +// Password characteristic +CHARACTERISTIC, 410f5077-9e81-4f3b-b888-bf435174fa58, READ | WRITE | NOTIFY | DYNAMIC, +CHARACTERISTIC_USER_DESCRIPTION, READ, \ No newline at end of file diff --git a/bluetooth/wifi_provisioner/set_credentials.py b/bluetooth/wifi_provisioner/set_credentials.py new file mode 100644 index 000000000..d42a8610c --- /dev/null +++ b/bluetooth/wifi_provisioner/set_credentials.py @@ -0,0 +1,40 @@ +import asyncio +import sys +import argparse + +from bleak import BleakClient + +SSID_CHARACTERISTIC = "b1829813-e8ec-4621-b9b5-6c1be43fe223" +PASSWORD_CHARACTERISTIC = "410f5077-9e81-4f3b-b888-bf435174fa58" + +#Add arguments from terminal with python3 set_credentials.py ssid password address +parser=argparse.ArgumentParser(description="ssid, password and address parser") +parser.add_argument("ssid") +parser.add_argument("password") +parser.add_argument("address") +args = parser.parse_args() + +ssid = args.ssid +password = args.password +address = args.address + +print("submitted ssid: ", ssid) +print("submitted password: ", password) +print("submitted address: ", address) + +async def main(ssid, password): + async with BleakClient(address) as client: + print(f"Connected: {client.is_connected}") + + await client.pair() + + print("Writing SSID...") + await client.write_gatt_char(SSID_CHARACTERISTIC, ssid.encode("utf-8"), response=True) + await asyncio.sleep(1.0) + + print("Writing password...") + await client.write_gatt_char(PASSWORD_CHARACTERISTIC, password.encode("utf-8"), response=True) + await asyncio.sleep(1.0) + +if __name__ == "__main__": + asyncio.run(main(ssid, password)) \ No newline at end of file diff --git a/bluetooth/wifi_provisioner/wifi_prov_lib.c b/bluetooth/wifi_provisioner/wifi_prov_lib.c new file mode 100644 index 000000000..a043d29a1 --- /dev/null +++ b/bluetooth/wifi_provisioner/wifi_prov_lib.c @@ -0,0 +1,478 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "btstack.h" +#include "pico/cyw43_arch.h" +#include "pico/btstack_cyw43.h" +#include "pico/stdlib.h" +#include "provisioning.h" +#include "wifi_prov_lib.h" +#include "hardware/gpio.h" +#include "pico/flash.h" +#include "hardware/flash.h" + +#ifndef NDEBUG +#define DEBUG_LOG(...) printf(__VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif +#define INFO_LOG printf +#define ERROR_LOG printf + +#define HEARTBEAT_PERIOD_MS 1000 +#define APP_AD_FLAGS 0x06 + +// max lengths of credentials + 1 to ensure null termination +static char ssid[33] = ""; +static char password[64] = ""; +static bool connection_status = false; +static int le_notification_enabled; +static hci_con_handle_t con_handle; +static btstack_timer_source_t heartbeat; +static btstack_packet_callback_registration_t hci_event_callback_registration; +static btstack_packet_callback_registration_t sm_event_callback_registration; + +static uint8_t adv_data[] = { + // Flags general discoverable + 0x02, BLUETOOTH_DATA_TYPE_FLAGS, APP_AD_FLAGS, + // Name + 0x17, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'P', 'i', 'c', 'o', ' ', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', + 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, 0x10, 0xFF, +}; +static const uint8_t adv_data_len = sizeof(adv_data); + +// Define flash offset towards end of flash +#ifndef PICO_FLASH_BANK_TOTAL_SIZE +#define PICO_FLASH_BANK_TOTAL_SIZE (FLASH_SECTOR_SIZE * 2u) +#endif + +#ifndef PICO_FLASH_BANK_STORAGE_OFFSET +#if PICO_RP2350 && PICO_RP2350_A2_SUPPORTED +// picotool stores a "marker" in the last block of the last sector +#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - (2 * FLASH_SECTOR_SIZE) - PICO_FLASH_BANK_TOTAL_SIZE) +#else +#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE - PICO_FLASH_BANK_TOTAL_SIZE) +#endif +#endif + +static const uint8_t *flash_target_contents = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET); + +// This function will be called when it's safe to call flash_range_erase +static void call_flash_range_erase(void *param) { + uint32_t offset = (uint32_t)param; + flash_range_erase(offset, FLASH_SECTOR_SIZE); +} + +// This function will be called when it's safe to call flash_range_program +static void call_flash_range_program(void *param) { + uint32_t offset = ((uintptr_t*)param)[0]; + const uint8_t *data = (const uint8_t *)((uintptr_t*)param)[1]; + flash_range_program(offset, data, FLASH_PAGE_SIZE); +} + +// Security Manager Packet Handler +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; + + hci_con_handle_t con_handle; + bd_addr_t addr; + bd_addr_type_t addr_type; + uint8_t status; + + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_META_GAP: + switch (hci_event_gap_meta_get_subevent_code(packet)) { + case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: + DEBUG_LOG("Connection complete\n"); + con_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); + UNUSED(con_handle); + sm_request_pairing(con_handle); + break; + default: + break; + } + break; + case SM_EVENT_JUST_WORKS_REQUEST: + DEBUG_LOG("Just Works requested\n"); + sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); + break; + case SM_EVENT_IDENTITY_CREATED: + sm_event_identity_created_get_identity_address(packet, addr); + DEBUG_LOG("Identity created: type %u address %s\n", sm_event_identity_created_get_identity_addr_type(packet), bd_addr_to_str(addr)); + break; + case SM_EVENT_IDENTITY_RESOLVING_SUCCEEDED: + sm_event_identity_resolving_succeeded_get_identity_address(packet, addr); + DEBUG_LOG("Identity resolved: type %u address %s\n", sm_event_identity_resolving_succeeded_get_identity_addr_type(packet), bd_addr_to_str(addr)); + break; + case SM_EVENT_IDENTITY_RESOLVING_FAILED: + sm_event_identity_created_get_address(packet, addr); + ERROR_LOG("Identity resolving failed\n"); + break; + case SM_EVENT_PAIRING_STARTED: + DEBUG_LOG("Pairing started\n"); + break; + case SM_EVENT_PAIRING_COMPLETE: + switch (sm_event_pairing_complete_get_status(packet)){ + case ERROR_CODE_SUCCESS: + DEBUG_LOG("Pairing complete, success\n"); + break; + case ERROR_CODE_CONNECTION_TIMEOUT: + ERROR_LOG("Pairing failed, timeout\n"); + break; + case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: + ERROR_LOG("Pairing failed, disconnected\n"); + break; + case ERROR_CODE_AUTHENTICATION_FAILURE: + ERROR_LOG("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); + DEBUG_LOG("Bonding information exists for addr type %u, identity addr %s -> re-encryption started\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: + DEBUG_LOG("Re-encryption complete, success\n"); + break; + case ERROR_CODE_CONNECTION_TIMEOUT: + ERROR_LOG("Re-encryption failed, timeout\n"); + break; + case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: + ERROR_LOG("Re-encryption failed, disconnected\n"); + break; + case ERROR_CODE_PIN_OR_KEY_MISSING: + ERROR_LOG("Re-encryption failed, bonding information missing\n\n"); + ERROR_LOG("Assuming remote lost bonding information\n"); + ERROR_LOG("Deleting local bonding information to allow for 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); + break; + default: + break; + } + break; + case GATT_EVENT_QUERY_COMPLETE: + status = gatt_event_query_complete_get_att_status(packet); + switch (status){ + case ATT_ERROR_INSUFFICIENT_ENCRYPTION: + ERROR_LOG("GATT Query failed, Insufficient Encryption\n"); + break; + case ATT_ERROR_INSUFFICIENT_AUTHENTICATION: + ERROR_LOG("GATT Query failed, Insufficient Authentication\n"); + break; + case ATT_ERROR_BONDING_INFORMATION_MISSING: + ERROR_LOG("GATT Query failed, Bonding Information Missing\n"); + break; + case ATT_ERROR_SUCCESS: + DEBUG_LOG("GATT Query successful\n"); + break; + default: + ERROR_LOG("GATT Query failed, status 0x%02x\n", gatt_event_query_complete_get_att_status(packet)); + break; + } + break; + default: + break; + } +} + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + UNUSED(size); + UNUSED(channel); + bd_addr_t local_addr; + if (packet_type != HCI_EVENT_PACKET) return; + + uint8_t event_type = hci_event_packet_get_type(packet); + switch(event_type){ + case HCI_EVENT_META_GAP: + switch (hci_event_gap_meta_get_subevent_code(packet)) { + case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: + DEBUG_LOG("Connection complete\n"); + con_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); + UNUSED(con_handle); + sm_request_pairing(con_handle); + break; + default: + break; + } + break; + + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; + gap_local_bd_addr(local_addr); + INFO_LOG("BTstack up and running on %s.\n", bd_addr_to_str(local_addr)); + + // setup advertisements + uint16_t adv_int_min = 800; + uint16_t adv_int_max = 800; + 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); + assert(adv_data_len <= 31); // ble limitation + gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); + gap_advertisements_enable(1); + + break; + case HCI_EVENT_DISCONNECTION_COMPLETE: + le_notification_enabled = 0; + break; + case ATT_EVENT_CAN_SEND_NOW: + att_server_notify(con_handle, ATT_CHARACTERISTIC_b1829813_e8ec_4621_b9b5_6c1be43fe223_01_VALUE_HANDLE, (uint8_t*)ssid, sizeof(ssid)); + att_server_notify(con_handle, ATT_CHARACTERISTIC_410f5077_9e81_4f3b_b888_bf435174fa58_01_VALUE_HANDLE, (uint8_t*)password, sizeof(password)); + break; + + default: + break; + } +} + +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) { + UNUSED(connection_handle); + + // SSID read callbaclk + if (att_handle == ATT_CHARACTERISTIC_b1829813_e8ec_4621_b9b5_6c1be43fe223_01_VALUE_HANDLE){ + return att_read_callback_handle_blob((const uint8_t *)&ssid, sizeof(ssid), offset, buffer, buffer_size); + } + + // Password read callback + if (att_handle == ATT_CHARACTERISTIC_410f5077_9e81_4f3b_b888_bf435174fa58_01_VALUE_HANDLE){ + return att_read_callback_handle_blob((const uint8_t *)&password, sizeof(password), offset, buffer, buffer_size); + } + + 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); + + le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; + con_handle = connection_handle; + if (le_notification_enabled) { + att_server_request_can_send_now_event(con_handle); + //This occurs when the client enables notification (the download button on nrf scanner) + } + + // First characteristic (SSID) + if (att_handle == ATT_CHARACTERISTIC_b1829813_e8ec_4621_b9b5_6c1be43fe223_01_VALUE_HANDLE){ + DEBUG_LOG("Setting SSID\n"); + att_server_request_can_send_now_event(con_handle); + memset(ssid, 0, sizeof(ssid)); + memcpy(ssid, buffer, buffer_size); + //This occurs when the client sends a write request to the ssid characteristic (up arrow on nrf scanner) + DEBUG_LOG("Current saved SSID: \"%s\"\n", ssid); + DEBUG_LOG("Current saved password length: %u\n", strlen(password)); + } + + // Second characteristic (Password) + if (att_handle == ATT_CHARACTERISTIC_410f5077_9e81_4f3b_b888_bf435174fa58_01_VALUE_HANDLE){ + DEBUG_LOG("Setting password\n"); + att_server_request_can_send_now_event(con_handle); + memset(password, 0, sizeof(password)); + memcpy(password, buffer, buffer_size); + //This occurs when the client sends a write request to the password characteristic (up arrow on nrf scanner) + DEBUG_LOG("Current saved SSID: \"%s\"\n", ssid); + DEBUG_LOG("Current saved password length: %u\n", strlen(password)); + } + + return 0; +} + +static void heartbeat_handler(struct btstack_timer_source *ts) { + static uint32_t counter = 0; + counter++; + + // Invert the led + static int led_on = true; + led_on = !led_on; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + + // Restart timer + btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(ts); +} + +int erase_credentials(void) { + return flash_safe_execute(call_flash_range_erase, (void*)FLASH_TARGET_OFFSET, UINT32_MAX); +} + +static int save_credentials(char ssid[], char password[]) { + // create empty 256 byte list + uint8_t flash_data[FLASH_PAGE_SIZE] = {0}; + + uint ssid_len = strlen(ssid); + uint password_len = strlen(password); + + // no character has ascii value 0, so we can seperate our ssid and password with a single 0 + // first add ssid + for (uint i = 0; i < ssid_len; i++) { + int ascii = (int) ssid[i]; + flash_data[i] = ascii; + } + + //next add password + for (uint i = 0; i < password_len; i++) { + int ascii = (int) password[i]; + flash_data[i + ssid_len + 1] = ascii; + } + + //now erase and then write flash + int rc = erase_credentials(); + if (rc != PICO_OK) { + return rc; + } + + uintptr_t params[] = { FLASH_TARGET_OFFSET, (uintptr_t)flash_data}; + rc = flash_safe_execute(call_flash_range_program, params, UINT32_MAX); + return rc; +} + +static void read_credentials(void) { + uint counter = 0; + uint ssid_len = 0; + + // first check if the flash page begins with FF - this indicates the flash has not yet been written to + // so must initialise with empty write (otherwise crashes) + if (flash_target_contents[0] == 255) { + int rc = save_credentials("", ""); + hard_assert(rc == PICO_OK); + } + + //initialise temporary ssid and password as 1 bigger than max to ensure null termination + char t_ssid[33] = {0}; + char t_password[64] = {0}; + + // itterate through the flash and seperate ssid and password + for (uint i = 0; i < FLASH_PAGE_SIZE; i++) { + // when detect first zero, increment counter and continue. update ssid_len so we can index password + if (flash_target_contents[i] == 0 && counter == 0) { + counter++; + ssid_len = i; + continue; + } + // when detect second zero, have extracted both ssid and password so stop + else if (flash_target_contents[i] == 0 && counter == 1) + { + break; + } + // otherwise just write ssid and password + else if (counter == 0) { + t_ssid[i] = (char) flash_target_contents[i]; + } + else if (counter == 1) { + t_password[i - ssid_len - 1] = (char) flash_target_contents[i]; + } + } + // update global ssid and password + memset(ssid, 0, sizeof(ssid)); + memcpy(ssid, t_ssid, sizeof(t_ssid)); + + memset(password, 0, sizeof(password)); + memcpy(password, t_password, sizeof(t_password)); +} + +// this function carries out the BLE credential provisioning and also wifi connection +int start_ble_wifi_provisioning(int ble_timeout_ms) { + absolute_time_t timeout_time = make_timeout_time_ms(ble_timeout_ms); + l2cap_init(); + sm_init(); + + att_server_init(profile_data, att_read_callback, att_write_callback); + + // inform about BTstack state + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + // secure manager register handler + sm_event_callback_registration.callback = &sm_packet_handler; + sm_add_event_handler(&sm_event_callback_registration); + + // configure secure BLE (Just works) (legacy pairing) + gatt_client_set_required_security_level(LEVEL_2); + sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); + sm_set_authentication_requirements(0); + + // register for ATT event + att_server_register_packet_handler(packet_handler); + + // set one-shot btstack timer + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(&heartbeat); + + // turn on bluetooth! + hci_power_control(HCI_POWER_ON); + + read_credentials(); + DEBUG_LOG("Read credentials\n"); + DEBUG_LOG("Current saved SSID: \"%s\"\n", ssid); + DEBUG_LOG("Current saved password length: %u\n", strlen(password)); + + // first attempt to connect using saved credentials + cyw43_arch_enable_sta_mode(); + connection_status = false; + if (ssid[0] && password[0]) { + if (cyw43_arch_wifi_connect_timeout_ms(ssid, password, CYW43_AUTH_WPA2_AES_PSK, us_to_ms(absolute_time_diff_us(get_absolute_time(), timeout_time)))) { + ERROR_LOG("failed to connect with saved credentials\n"); + } else { + DEBUG_LOG("Connected.\n"); + connection_status = true; + } + } else { + INFO_LOG("Waiting to receive ssid and password via BLE\n"); + } + + // If this fails, wait for user to provision credentials over BLE until timeout + // cyw43_arch_wifi_connect_timeout_ms returns -2 for timeout and -7 for incorrect password + // wish to keep trying if password incorrect + int result = PICO_OK; + if (connection_status == false) { + while (true) { + if (ssid[0] && password[0]) { + result = cyw43_arch_wifi_connect_timeout_ms(ssid, password, CYW43_AUTH_WPA2_AES_PSK, us_to_ms(absolute_time_diff_us(get_absolute_time(), timeout_time))); + if (result == PICO_ERROR_TIMEOUT) { + ERROR_LOG("Timed out - failed provisioning!\n"); + break; + } else if (result == PICO_OK) { + connection_status = true; + DEBUG_LOG("Succesfully provisioned credentials using wifi_prov_lib!\n"); + // since connected, save credentiald for future use + result = save_credentials(ssid, password); + break; + } else if (result == PICO_ERROR_BADAUTH) { + DEBUG_LOG("Incorrect password - retrying\n"); + } else { + DEBUG_LOG("Connection error - failed provisioning!\n"); + break; + } + } else { + if (absolute_time_diff_us(get_absolute_time(), timeout_time) < 0) { + ERROR_LOG("Timed out - no ssid or password received!\n"); + result = PICO_ERROR_TIMEOUT; + break; + } + cyw43_arch_poll(); + cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000)); + } + } + } + // once finished, turn off bluetooth + hci_power_control(HCI_POWER_OFF); + return result; +} diff --git a/bluetooth/wifi_provisioner/wifi_prov_lib.h b/bluetooth/wifi_provisioner/wifi_prov_lib.h new file mode 100644 index 000000000..025634527 --- /dev/null +++ b/bluetooth/wifi_provisioner/wifi_prov_lib.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + #ifndef WIFI_PROV_LIB +#define WIFI_PROV_LIB + +int start_ble_wifi_provisioning(int ble_timeout_ms); +int erase_credentials(void); + +#endif \ No newline at end of file diff --git a/pico_w/CMakeLists.txt b/pico_w/CMakeLists.txt index f66c02e95..69c01349a 100644 --- a/pico_w/CMakeLists.txt +++ b/pico_w/CMakeLists.txt @@ -19,10 +19,5 @@ if (PICO_CYW43_SUPPORTED) # set by PICO_BOARD=pico_w set(WIFI_PASSWORD "${WIFI_PASSWORD}" CACHE INTERNAL "WiFi password for examples") add_subdirectory(wifi) - if (NOT TARGET pico_btstack_base) - message("Skipping Pico W Bluetooth examples as support is not available") - else() - add_subdirectory(bt) - endif() endif() endif() diff --git a/pico_w/bt/CMakeLists.txt b/pico_w/bt/CMakeLists.txt deleted file mode 100644 index 7423f4898..000000000 --- a/pico_w/bt/CMakeLists.txt +++ /dev/null @@ -1,397 +0,0 @@ -add_subdirectory(standalone) - -set(BTSTACK_ROOT ${PICO_SDK_PATH}/lib/btstack) -set(BTSTACK_EXAMPLE_PATH ${BTSTACK_ROOT}/example) -set(BTSTACK_3RD_PARTY_PATH ${BTSTACK_ROOT}/3rd-party) -set(BT_EXAMPLE_COMMON_DIR "${CMAKE_CURRENT_LIST_DIR}") - -add_library(pico_btstack_audio_example INTERFACE) -target_sources(pico_btstack_audio_example INTERFACE - ${PICO_BTSTACK_PATH}/src/btstack_audio.c - ${PICO_BTSTACK_PATH}/src/btstack_audio_generator.c - ) -if (PICO_EXTRAS_PATH) - target_sources(pico_btstack_audio_example INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/btstack_audio_pico.c - ) - target_link_libraries(pico_btstack_audio_example INTERFACE - pico_audio_i2s - ) -endif() - -# mod player used by a2dp_source_demo and mod_player and demo song -add_library(pico_btstack_hxcmod_player INTERFACE) -target_sources(pico_btstack_hxcmod_player INTERFACE - ${BTSTACK_3RD_PARTY_PATH}/hxcmod-player/hxcmod.c - ${BTSTACK_3RD_PARTY_PATH}/hxcmod-player/mods/mod.c - ) -target_include_directories(pico_btstack_hxcmod_player INTERFACE - ${BTSTACK_3RD_PARTY_PATH}/hxcmod-player - ${BTSTACK_3RD_PARTY_PATH}/hxcmod-player/mods - ) -if (PICO_EXTRAS_PATH) - target_compile_definitions(pico_btstack_hxcmod_player INTERFACE - ENABLE_MODPLAYER=1 - ) -endif() -add_library(pico_btstack_sco_demo_util INTERFACE) -target_sources(pico_btstack_sco_demo_util INTERFACE - # sco demo utils - ${BTSTACK_EXAMPLE_PATH}/sco_demo_util.c - ) - -# Adds common stuff for all the examples -add_library(picow_bt_example_common INTERFACE) -target_sources(picow_bt_example_common INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/picow_bt_example_common.c - ) -target_link_libraries(picow_bt_example_common INTERFACE - pico_stdlib - pico_btstack_cyw43 - ) -target_include_directories(picow_bt_example_common INTERFACE - ${BT_EXAMPLE_COMMON_DIR}/config # Use our own config - ${BTSTACK_EXAMPLE_PATH}/ - ) -target_compile_definitions(picow_bt_example_common INTERFACE - PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS=3000 - #WANT_HCI_DUMP=1 # This enables btstack debug - #ENABLE_SEGGER_RTT=1 - ) -target_link_libraries(picow_bt_example_common INTERFACE - pico_btstack_audio_example - ) -if (PICO_EXTRAS_PATH) - target_compile_definitions(picow_bt_example_common INTERFACE - TEST_AUDIO=1 - ) -endif() - -# Adds stuff needed for cyw43 lwip -add_library(picow_bt_example_cyw43_lwip INTERFACE) -target_link_libraries(picow_bt_example_cyw43_lwip INTERFACE - picow_bt_example_common - ) -target_compile_definitions(picow_bt_example_cyw43_lwip INTERFACE - WIFI_SSID=\"${WIFI_SSID}\" - WIFI_PASSWORD=\"${WIFI_PASSWORD}\" - CYW43_LWIP=1 - ) -target_link_libraries(picow_bt_example_cyw43_lwip INTERFACE - pico_lwip_iperf - ) -target_include_directories(picow_bt_example_cyw43_lwip INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/../wifi # for our common lwipopts - ) - -# disables cyw43 lwip -add_library(picow_bt_example_no_cyw43_lwip INTERFACE) -target_link_libraries(picow_bt_example_no_cyw43_lwip INTERFACE - picow_bt_example_common - ) -target_compile_definitions(picow_bt_example_no_cyw43_lwip INTERFACE - CYW43_LWIP=0 - ) - -# variant: no cyw43 lwip | poll -add_library(picow_bt_example_no_cyw43_lwip_poll INTERFACE) -target_sources(picow_bt_example_no_cyw43_lwip_poll INTERFACE - ${BT_EXAMPLE_COMMON_DIR}/picow_bt_example_poll.c - ) -target_link_libraries(picow_bt_example_no_cyw43_lwip_poll INTERFACE - picow_bt_example_no_cyw43_lwip - pico_cyw43_arch_poll - ) - -# variant: cyw43 lwip | poll -add_library(picow_bt_example_cyw43_lwip_poll INTERFACE) -target_sources(picow_bt_example_cyw43_lwip_poll INTERFACE - ${BT_EXAMPLE_COMMON_DIR}/picow_bt_example_poll.c - ) -target_link_libraries(picow_bt_example_cyw43_lwip_poll INTERFACE - picow_bt_example_cyw43_lwip - pico_cyw43_arch_lwip_poll - ) - -# variant: btstack lwip | poll -add_library(picow_bt_example_btstack_lwip_poll INTERFACE) -target_sources(picow_bt_example_btstack_lwip_poll INTERFACE - ${BT_EXAMPLE_COMMON_DIR}/picow_bt_example_poll.c - ) -target_link_libraries(picow_bt_example_btstack_lwip_poll INTERFACE - picow_bt_example_no_cyw43_lwip - pico_cyw43_arch_poll - pico_lwip_nosys - pico_btstack_bnep_lwip - ) -target_include_directories(picow_bt_example_btstack_lwip_poll INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/../wifi # for our common lwipopts - ) - -# variant: no cyw43 lwip | background -add_library(picow_bt_example_no_cyw43_lwip_background INTERFACE) -target_sources(picow_bt_example_no_cyw43_lwip_background INTERFACE - ${BT_EXAMPLE_COMMON_DIR}/picow_bt_example_background.c - ) -target_link_libraries(picow_bt_example_no_cyw43_lwip_background INTERFACE - picow_bt_example_no_cyw43_lwip - pico_cyw43_arch_threadsafe_background - ) - -# variant: cyw43 lwip | background -add_library(picow_bt_example_cyw43_lwip_background INTERFACE) -target_sources(picow_bt_example_cyw43_lwip_background INTERFACE - ${BT_EXAMPLE_COMMON_DIR}/picow_bt_example_background.c - ) -target_link_libraries(picow_bt_example_cyw43_lwip_background INTERFACE - picow_bt_example_cyw43_lwip - pico_cyw43_arch_lwip_threadsafe_background - ) - -# variant: btstack lwip | background -add_library(picow_bt_example_btstack_lwip_background INTERFACE) -target_sources(picow_bt_example_btstack_lwip_background INTERFACE - ${BT_EXAMPLE_COMMON_DIR}/picow_bt_example_background.c - ) -target_link_libraries(picow_bt_example_btstack_lwip_background INTERFACE - picow_bt_example_no_cyw43_lwip - pico_cyw43_arch_threadsafe_background - pico_lwip_nosys - pico_btstack_bnep_lwip - ) -target_include_directories(picow_bt_example_btstack_lwip_background INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/../wifi # for our common lwipopts - ) - -if (FREERTOS_KERNEL_PATH OR DEFINED ENV{FREERTOS_KERNEL_PATH}) -include(FreeRTOS_Kernel_import.cmake) -endif() - -if (TARGET FreeRTOS-Kernel) - # common freertos stuff - add_library(picow_bt_example_common_freertos INTERFACE) - target_sources(picow_bt_example_common_freertos INTERFACE - ${BT_EXAMPLE_COMMON_DIR}/picow_bt_example_freertos.c - ) - target_link_libraries(picow_bt_example_common_freertos INTERFACE - picow_bt_example_common - FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap - ) - target_compile_definitions(picow_bt_example_common_freertos INTERFACE - CYW43_TASK_STACK_SIZE=1024 - NO_SYS=0 - ) - - # variant: no cyw43 lwip | freertos - add_library(picow_bt_example_no_cyw43_lwip_freertos INTERFACE) - target_link_libraries(picow_bt_example_no_cyw43_lwip_freertos INTERFACE - picow_bt_example_common_freertos - picow_bt_example_no_cyw43_lwip - pico_cyw43_arch_sys_freertos - ) - - # variant: cyw43 lwip | freertos - add_library(picow_bt_example_cyw43_lwip_freertos INTERFACE) - target_link_libraries(picow_bt_example_cyw43_lwip_freertos INTERFACE - picow_bt_example_common_freertos - picow_bt_example_cyw43_lwip - pico_cyw43_arch_lwip_sys_freertos - ) - target_compile_definitions(picow_bt_example_cyw43_lwip_freertos INTERFACE - HAVE_LWIP=1 # stops btstack calling lwip_init - ) - - # variant: btstack lwip | freertos - add_library(picow_bt_example_btstack_lwip_freertos INTERFACE) - target_link_libraries(picow_bt_example_btstack_lwip_freertos INTERFACE - picow_bt_example_common_freertos - picow_bt_example_no_cyw43_lwip - pico_cyw43_arch_sys_freertos - pico_btstack_bnep_lwip_sys_freertos - pico_lwip_freertos - ) - target_include_directories(picow_bt_example_btstack_lwip_freertos INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/../wifi # for our common lwipopts - ) - target_compile_definitions(picow_bt_example_btstack_lwip_freertos INTERFACE - HAVE_LWIP=1 # stops btstack calling lwip_init - ) -endif() - -# Common stuff for all examples. Pass the example name -function(picow_bt_example_common NAME) - if (NOT TARGET picow_bt_example_common_${NAME}) - add_library(picow_bt_example_common_${NAME} INTERFACE) - target_sources(picow_bt_example_common_${NAME} INTERFACE - # actual example - ${BTSTACK_EXAMPLE_PATH}/${NAME}.c - ) - if (EXISTS "${BTSTACK_EXAMPLE_PATH}/${NAME}.gatt") - pico_btstack_make_gatt_header(picow_bt_example_common_${NAME} INTERFACE ${BTSTACK_EXAMPLE_PATH}/${NAME}.gatt) - endif() - endif() -endfunction() - -# The type of example to build -if(NOT DEFINED BTSTACK_EXAMPLE_TYPE) - set(BTSTACK_EXAMPLE_TYPE "background") -endif() -if ((NOT BTSTACK_EXAMPLE_TYPE STREQUAL "poll") AND - (NOT BTSTACK_EXAMPLE_TYPE STREQUAL "background") AND - (NOT BTSTACK_EXAMPLE_TYPE STREQUAL "freertos") AND - (NOT BTSTACK_EXAMPLE_TYPE STREQUAL "all")) - message(FATAL_ERROR "Unknown BTSTACK_EXAMPLE_TYPE '${BTSTACK_EXAMPLE_TYPE}'; valid options are 'poll', 'background', 'freertos' or 'all'") -endif() -set(BTSTACK_EXAMPLE_TYPE "${BTSTACK_EXAMPLE_TYPE}" CACHE INTERNAL "BT stack example type (poll|background|freertos|all)") - -# Add an poll example, pass btstack example name, target name, variant lib and extra libs -function(picow_bt_example_poll NAME TARGET_NAME VARIANT_LIB) - if ("${BTSTACK_EXAMPLE_TYPE}" STREQUAL "poll" OR "${BTSTACK_EXAMPLE_TYPE}" STREQUAL "all") - picow_bt_example_common(${NAME}) - add_executable(${TARGET_NAME}_poll) - target_link_libraries(${TARGET_NAME}_poll PRIVATE - picow_bt_example_common_${NAME} - ${VARIANT_LIB} - ${ARGN} # extra libs - ) - pico_add_extra_outputs(${TARGET_NAME}_poll) - example_auto_set_url(${TARGET_NAME}_poll) - endif() -endfunction() - -# Add an background example, pass btstack example name, target name, variant lib and extra libs -function(picow_bt_example_background NAME TARGET_NAME VARIANT_LIB) - if ("${BTSTACK_EXAMPLE_TYPE}" STREQUAL "background" OR "${BTSTACK_EXAMPLE_TYPE}" STREQUAL "all") - picow_bt_example_common(${NAME}) - add_executable(${TARGET_NAME}_background) - target_link_libraries(${TARGET_NAME}_background PRIVATE - picow_bt_example_common_${NAME} - ${VARIANT_LIB} - ${ARGN} # extra libs - ) - pico_add_extra_outputs(${TARGET_NAME}_background) - example_auto_set_url(${TARGET_NAME}_background) - endif() -endfunction() - -# Add a freertos example, pass btstack example name, target name, variant lib and extra libs -function(picow_bt_example_freertos NAME TARGET_NAME VARIANT_LIB) - if ("${BTSTACK_EXAMPLE_TYPE}" STREQUAL "freertos" OR "${BTSTACK_EXAMPLE_TYPE}" STREQUAL "all") - if (TARGET FreeRTOS-Kernel) - picow_bt_example_common(${NAME}) - add_executable(${TARGET_NAME}_freertos) - target_link_libraries(${TARGET_NAME}_freertos PRIVATE - picow_bt_example_common_${NAME} - ${VARIANT_LIB} - ${ARGN} # extra libs - ) - pico_add_extra_outputs(${TARGET_NAME}_freertos) - example_auto_set_url(${TARGET_NAME}_freertos) - endif() - endif() -endfunction() - -# The default name of the bt example target -function(picow_bt_example_target_name NAME RET) - SET(${RET} "picow_bt_example_${NAME}" PARENT_SCOPE) -endfunction() - -# Make a btstack example, NAME should match source file in lib/btstack/example -# Extra parameters indicate extra libraries to link to -function(picow_bt_example NAME) - picow_bt_example_target_name(${NAME} TARGET_NAME) - picow_bt_example_poll(${NAME} ${TARGET_NAME} picow_bt_example_no_cyw43_lwip_poll pico_btstack_ble pico_btstack_classic ${ARGN}) - picow_bt_example_background(${NAME} ${TARGET_NAME} picow_bt_example_no_cyw43_lwip_background pico_btstack_ble pico_btstack_classic ${ARGN}) - picow_bt_example_freertos(${NAME} ${TARGET_NAME} picow_bt_example_no_cyw43_lwip_freertos pico_btstack_ble pico_btstack_classic ${ARGN}) -endfunction() - -# List of examples from btstack -include(${BTSTACK_ROOT}/example/CMakeLists.txt) - -# Full list of bluetooth examples -set(BTSTACK_EXAMPLES - ${EXAMPLES_GENERAL} - ${EXAMPLES_CLASSIC_ONLY} - ${EXAMPLES_LE_ONLY} - ${EXAMPLES_DUAL_MODE} - pan_lwip_http_server -) - -# These examples run wifi and bt at the same time -if (WIFI_SSID AND WIFI_PASSWORD) - list(APPEND BTSTACK_EXAMPLES - gatt_counter_with_wifi - gatt_streamer_server_with_wifi - #spp_streamer_with_wifi - ) -endif() - -# Extra examples that are not that interesting or a bit tricky to run -# They are not built unless BTSTACK_EXAMPLES_ALL=1 -set(BTSTACK_EXAMPLES_ADDITIONAL - ancs_client_demo - att_delayed_response - avrcp_browsing_client - dut_mode_classic - gap_dedicated_bonding - gap_link_keys - le_credit_based_flow_control_mode_client - le_credit_based_flow_control_mode_server - le_mitm - led_counter - sdp_bnep_query - sdp_general_query - spp_flowcontrol - sdp_rfcomm_query - spp_and_gatt_counter - spp_and_gatt_streamer - hfp_ag_demo - hsp_ag_demo - hfp_hf_demo - hsp_hs_demo - mod_player - sine_player - ublox_spp_le_counter - nordic_spp_le_streamer - nordic_spp_le_counter - hog_boot_host_demo - gatt_battery_query - gatt_browser - gatt_device_information_query - le_streamer_client -) - -# These examples will only be built if pico-extras exists -set(BTSTACK_EXAMPLES_NEED_EXTRAS - a2dp_sink_demo - hfp_hf_demo - hfp_ag_demo - hsp_ag_demo - hsp_hs_demo - mod_player - sine_player -) - -# Add examples -set(BTSTACK_EXAMPLE_COUNT 0) -list(REMOVE_DUPLICATES BTSTACK_EXAMPLES) -foreach(NAME ${BTSTACK_EXAMPLES}) - # Ignore if sub folder not found - if (NOT IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/${NAME}) - continue() - endif() - # Ignore example if it needs pico-extras and pico-extras could not be found - if (NOT PICO_EXTRAS_PATH AND ${NAME} IN_LIST BTSTACK_EXAMPLES_NEED_EXTRAS) - continue() - endif() - # Ignore less interesting examples if BTSTACK_EXAMPLES_ALL=1 is not set - if (NOT BTSTACK_EXAMPLES_ALL AND ${NAME} IN_LIST BTSTACK_EXAMPLES_ADDITIONAL) - continue() - endif() - # add example - add_subdirectory_exclude_platforms(${NAME}) - MATH(EXPR BTSTACK_EXAMPLE_COUNT "${BTSTACK_EXAMPLE_COUNT}+1") -endforeach() -message("Adding ${BTSTACK_EXAMPLE_COUNT} BTstack examples of type '${BTSTACK_EXAMPLE_TYPE}'") - -suppress_btstack_warnings() diff --git a/pico_w/bt/FreeRTOS_Kernel_import.cmake b/pico_w/bt/FreeRTOS_Kernel_import.cmake deleted file mode 100644 index 15d8e7724..000000000 --- a/pico_w/bt/FreeRTOS_Kernel_import.cmake +++ /dev/null @@ -1,91 +0,0 @@ -# This is a copy of /portable/ThirdParty/GCC/RP2040/FREERTOS_KERNEL_import.cmake - -# This can be dropped into an external project to help locate the FreeRTOS kernel -# It should be include()ed prior to project(). Alternatively this file may -# or the CMakeLists.txt in this directory may be included or added via add_subdirectory -# respectively. - -if (DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH)) - set(FREERTOS_KERNEL_PATH $ENV{FREERTOS_KERNEL_PATH}) - message("Using FREERTOS_KERNEL_PATH from environment ('${FREERTOS_KERNEL_PATH}')") -endif () - -# first pass we look in old tree; second pass we look in new tree -foreach(SEARCH_PASS RANGE 0 1) - if (SEARCH_PASS) - # ports may be moving to submodule in the future - set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/Community-Supported-Ports/GCC") - set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../../..") - else() - set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC") - set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..") - endif() - - if(PICO_PLATFORM STREQUAL "rp2040") - set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2040") - else() - if (PICO_PLATFORM STREQUAL "rp2350-riscv") - set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2350_RISC-V") - else() - set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2350_ARM_NTZ") - endif() - endif() - - if (NOT FREERTOS_KERNEL_PATH) - # check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly) - get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH) - get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH) - if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) - get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) - endif() - if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) - get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) - message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake") - break() - elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel") - set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel) - message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}") - break() - endif() - endif () - - if (NOT FREERTOS_KERNEL_PATH) - foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source) - # check if FreeRTOS-Kernel exists under directory that included us - set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) - get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH) - if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) - get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH) - message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project") - break() - endif() - endforeach() - if (FREERTOS_KERNEL_PATH) - break() - endif() - endif() - - # user must have specified - if (FREERTOS_KERNEL_PATH) - if (EXISTS "${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") - break() - endif() - endif() -endforeach () - -if (NOT FREERTOS_KERNEL_PATH) - message(FATAL_ERROR "FreeRTOS location was not specified. Please set FREERTOS_KERNEL_PATH.") -endif() - -set(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" CACHE PATH "Path to the FreeRTOS Kernel") - -get_filename_component(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") -if (NOT EXISTS ${FREERTOS_KERNEL_PATH}) - message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' not found") -endif() -if (NOT EXISTS ${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) - message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain a '${PICO_PLATFORM}' port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") -endif() -set(FREERTOS_KERNEL_PATH ${FREERTOS_KERNEL_PATH} CACHE PATH "Path to the FreeRTOS_KERNEL" FORCE) - -add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL) diff --git a/pico_w/bt/a2dp_sink_demo/CMakeLists.txt b/pico_w/bt/a2dp_sink_demo/CMakeLists.txt deleted file mode 100644 index d92d715a2..000000000 --- a/pico_w/bt/a2dp_sink_demo/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_library(a2dp_sink_demo_pins INTERFACE) -target_compile_definitions(a2dp_sink_demo_pins INTERFACE - PICO_AUDIO_I2S_DATA_PIN=9 - PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 - ) - -picow_bt_example(a2dp_sink_demo pico_btstack_sbc_decoder a2dp_sink_demo_pins) diff --git a/pico_w/bt/a2dp_source_demo/CMakeLists.txt b/pico_w/bt/a2dp_source_demo/CMakeLists.txt deleted file mode 100644 index f3ff454d6..000000000 --- a/pico_w/bt/a2dp_source_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(a2dp_source_demo pico_btstack_hxcmod_player pico_btstack_sbc_encoder) \ No newline at end of file diff --git a/pico_w/bt/ancs_client_demo/CMakeLists.txt b/pico_w/bt/ancs_client_demo/CMakeLists.txt deleted file mode 100644 index 3a0c52f01..000000000 --- a/pico_w/bt/ancs_client_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(ancs_client_demo) diff --git a/pico_w/bt/att_delayed_response/CMakeLists.txt b/pico_w/bt/att_delayed_response/CMakeLists.txt deleted file mode 100644 index 441475a9e..000000000 --- a/pico_w/bt/att_delayed_response/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(att_delayed_response) diff --git a/pico_w/bt/avrcp_browsing_client/CMakeLists.txt b/pico_w/bt/avrcp_browsing_client/CMakeLists.txt deleted file mode 100644 index f78762eb5..000000000 --- a/pico_w/bt/avrcp_browsing_client/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(avrcp_browsing_client) diff --git a/pico_w/bt/config/FreeRTOSConfig.h b/pico_w/bt/config/FreeRTOSConfig.h deleted file mode 100644 index eed9527c5..000000000 --- a/pico_w/bt/config/FreeRTOSConfig.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * FreeRTOS V202111.00 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- - * Application specific definitions. - * - * These definitions should be adjusted for your particular hardware and - * application requirements. - * - * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE - * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. - * - * See http://www.freertos.org/a00110.html - *----------------------------------------------------------*/ - -/* Scheduler Related */ -#define configUSE_PREEMPTION 1 -#define configUSE_TICKLESS_IDLE 0 -#define configUSE_IDLE_HOOK 0 -#define configUSE_TICK_HOOK 0 -#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) -#define configMAX_PRIORITIES 32 -#define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 1024 -#define configUSE_16_BIT_TICKS 0 - -#define configIDLE_SHOULD_YIELD 1 - -/* Synchronization Related */ -#define configUSE_MUTEXES 1 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configUSE_APPLICATION_TASK_TAG 0 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configQUEUE_REGISTRY_SIZE 8 -#define configUSE_QUEUE_SETS 1 -#define configUSE_TIME_SLICING 1 -#define configUSE_NEWLIB_REENTRANT 0 -// todo need this for lwip FreeRTOS sys_arch to compile -#define configENABLE_BACKWARD_COMPATIBILITY 1 -#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 - -/* System */ -#define configSTACK_DEPTH_TYPE uint32_t -#define configMESSAGE_BUFFER_LENGTH_TYPE size_t - -/* Memory allocation related definitions. */ -#define configSUPPORT_STATIC_ALLOCATION 0 -#define configSUPPORT_DYNAMIC_ALLOCATION 1 -#define configTOTAL_HEAP_SIZE (128*1024) -#define configAPPLICATION_ALLOCATED_HEAP 0 - -/* Hook function related definitions. */ -#define configCHECK_FOR_STACK_OVERFLOW 0 -#define configUSE_MALLOC_FAILED_HOOK 0 -#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 - -/* Run time and task stats gathering related definitions. */ -#define configGENERATE_RUN_TIME_STATS 0 -#define configUSE_TRACE_FACILITY 1 -#define configUSE_STATS_FORMATTING_FUNCTIONS 0 - -/* Co-routine related definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES 1 - -/* Software timer related definitions. */ -#define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) -#define configTIMER_QUEUE_LENGTH 10 -#define configTIMER_TASK_STACK_DEPTH 1024 - -/* Interrupt nesting behaviour configuration. */ -/* -#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] -#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] -#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] -*/ - -#define configNUMBER_OF_CORES 2 -/* SMP (configNUMBER_OF_CORES > 1) only */ -#define configTICK_CORE 0 -#define configRUN_MULTIPLE_PRIORITIES 1 -#if configNUMBER_OF_CORES > 1 -#define configUSE_CORE_AFFINITY 1 -#endif -#define configUSE_PASSIVE_IDLE_HOOK 0 - -/* Armv8-M */ - -/* Not currently supported */ -#define configENABLE_MPU 0 -//#define configSYSTEM_CALL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 512 -#define configENABLE_FPU 1 -/* Not currently supported */ -#define configENABLE_TRUSTZONE 0 -#define configRUN_FREERTOS_SECURE_ONLY 1 -// see https://www.freertos.org/RTOS-Cortex-M3-M4.html -#define configMAX_SYSCALL_INTERRUPT_PRIORITY 16 - -/* RP2xxx specific */ -#define configSUPPORT_PICO_SYNC_INTEROP 1 -#define configSUPPORT_PICO_TIME_INTEROP 1 - -#include -/* Define to trap errors during development. */ -#define configASSERT(x) assert(x) - -/* Set the following definitions to 1 to include the API function, or zero -to exclude the API function. */ -#define INCLUDE_vTaskPrioritySet 1 -#define INCLUDE_uxTaskPriorityGet 1 -#define INCLUDE_vTaskDelete 1 -#define INCLUDE_vTaskSuspend 1 -#define INCLUDE_vTaskDelayUntil 1 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_xTaskGetSchedulerState 1 -#define INCLUDE_xTaskGetCurrentTaskHandle 1 -#define INCLUDE_uxTaskGetStackHighWaterMark 1 -#define INCLUDE_xTaskGetIdleTaskHandle 1 -#define INCLUDE_eTaskGetState 1 -#define INCLUDE_xTimerPendFunctionCall 1 -#define INCLUDE_xTaskAbortDelay 1 -#define INCLUDE_xTaskGetHandle 1 -#define INCLUDE_xTaskResumeFromISR 1 -#define INCLUDE_xQueueGetMutexHolder 1 - -/* A header file that defines trace macro can be included here. */ - -#endif /* FREERTOS_CONFIG_H */ - diff --git a/pico_w/bt/config/lwipopts.h b/pico_w/bt/config/lwipopts.h deleted file mode 100644 index b8983d7d9..000000000 --- a/pico_w/bt/config/lwipopts.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _LWIPOPTS_H -#define _LWIPOPTS_H - -// Generally you would define your own explicit list of lwIP options -// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) -// -// This example uses a common include to avoid repetition -#include "lwipopts_examples_common.h" - -#if !NO_SYS -#define TCPIP_THREAD_STACKSIZE 1024 -#define DEFAULT_THREAD_STACKSIZE 1024 -#define DEFAULT_RAW_RECVMBOX_SIZE 8 -#define TCPIP_MBOX_SIZE 8 -#define LWIP_TIMEVAL_PRIVATE 0 - -// not necessary, can be done either way -#define LWIP_TCPIP_CORE_LOCKING_INPUT 1 -#endif - -#endif diff --git a/pico_w/bt/dut_mode_classic/CMakeLists.txt b/pico_w/bt/dut_mode_classic/CMakeLists.txt deleted file mode 100644 index 44bc17fba..000000000 --- a/pico_w/bt/dut_mode_classic/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(dut_mode_classic) diff --git a/pico_w/bt/gap_dedicated_bonding/CMakeLists.txt b/pico_w/bt/gap_dedicated_bonding/CMakeLists.txt deleted file mode 100644 index 72daea2c0..000000000 --- a/pico_w/bt/gap_dedicated_bonding/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gap_dedicated_bonding) diff --git a/pico_w/bt/gap_inquiry/CMakeLists.txt b/pico_w/bt/gap_inquiry/CMakeLists.txt deleted file mode 100644 index 383582e87..000000000 --- a/pico_w/bt/gap_inquiry/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gap_inquiry) diff --git a/pico_w/bt/gap_le_advertisements/CMakeLists.txt b/pico_w/bt/gap_le_advertisements/CMakeLists.txt deleted file mode 100644 index 07f609326..000000000 --- a/pico_w/bt/gap_le_advertisements/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gap_le_advertisements) diff --git a/pico_w/bt/gap_link_keys/CMakeLists.txt b/pico_w/bt/gap_link_keys/CMakeLists.txt deleted file mode 100644 index b20dfc6b3..000000000 --- a/pico_w/bt/gap_link_keys/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gap_link_keys) diff --git a/pico_w/bt/gatt_battery_query/CMakeLists.txt b/pico_w/bt/gatt_battery_query/CMakeLists.txt deleted file mode 100644 index f4669eb28..000000000 --- a/pico_w/bt/gatt_battery_query/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gatt_battery_query) diff --git a/pico_w/bt/gatt_browser/CMakeLists.txt b/pico_w/bt/gatt_browser/CMakeLists.txt deleted file mode 100644 index 42971bc6c..000000000 --- a/pico_w/bt/gatt_browser/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gatt_browser) diff --git a/pico_w/bt/gatt_counter/CMakeLists.txt b/pico_w/bt/gatt_counter/CMakeLists.txt deleted file mode 100644 index 287e542e5..000000000 --- a/pico_w/bt/gatt_counter/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gatt_counter) diff --git a/pico_w/bt/gatt_counter_with_wifi/CMakeLists.txt b/pico_w/bt/gatt_counter_with_wifi/CMakeLists.txt deleted file mode 100644 index f8fea0a07..000000000 --- a/pico_w/bt/gatt_counter_with_wifi/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(NAME gatt_counter) -picow_bt_example_target_name(${NAME}_with_wifi TARGET_NAME) - -picow_bt_example_poll(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_poll pico_btstack_ble) -picow_bt_example_background(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_background pico_btstack_ble) -picow_bt_example_freertos(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_freertos pico_btstack_ble) diff --git a/pico_w/bt/gatt_device_information_query/CMakeLists.txt b/pico_w/bt/gatt_device_information_query/CMakeLists.txt deleted file mode 100644 index 227acc3ee..000000000 --- a/pico_w/bt/gatt_device_information_query/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gatt_device_information_query) diff --git a/pico_w/bt/gatt_heart_rate_client/CMakeLists.txt b/pico_w/bt/gatt_heart_rate_client/CMakeLists.txt deleted file mode 100644 index 5766c548c..000000000 --- a/pico_w/bt/gatt_heart_rate_client/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gatt_heart_rate_client) diff --git a/pico_w/bt/gatt_streamer_server/CMakeLists.txt b/pico_w/bt/gatt_streamer_server/CMakeLists.txt deleted file mode 100644 index 1e5ea1d30..000000000 --- a/pico_w/bt/gatt_streamer_server/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(gatt_streamer_server) diff --git a/pico_w/bt/gatt_streamer_server_with_wifi/CMakeLists.txt b/pico_w/bt/gatt_streamer_server_with_wifi/CMakeLists.txt deleted file mode 100644 index 9599e6f72..000000000 --- a/pico_w/bt/gatt_streamer_server_with_wifi/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(NAME gatt_streamer_server) -picow_bt_example_target_name(${NAME}_with_wifi TARGET_NAME) - -picow_bt_example_poll(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_poll pico_btstack_ble) -picow_bt_example_background(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_background pico_btstack_ble) -picow_bt_example_freertos(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_freertos pico_btstack_ble) diff --git a/pico_w/bt/hfp_ag_demo/CMakeLists.txt b/pico_w/bt/hfp_ag_demo/CMakeLists.txt deleted file mode 100644 index 5a5b60458..000000000 --- a/pico_w/bt/hfp_ag_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hfp_ag_demo pico_btstack_sco_demo_util pico_btstack_sbc_encoder) diff --git a/pico_w/bt/hfp_hf_demo/CMakeLists.txt b/pico_w/bt/hfp_hf_demo/CMakeLists.txt deleted file mode 100644 index 77e52f0af..000000000 --- a/pico_w/bt/hfp_hf_demo/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_library(hfp_hf_demo_pins INTERFACE) -target_compile_definitions(hfp_hf_demo_pins INTERFACE - PICO_AUDIO_I2S_DATA_PIN=9 - PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 - ) -picow_bt_example(hfp_hf_demo pico_btstack_sco_demo_util pico_btstack_sbc_encoder hfp_hf_demo_pins) diff --git a/pico_w/bt/hid_host_demo/CMakeLists.txt b/pico_w/bt/hid_host_demo/CMakeLists.txt deleted file mode 100644 index 3febdba15..000000000 --- a/pico_w/bt/hid_host_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hid_host_demo) diff --git a/pico_w/bt/hid_keyboard_demo/CMakeLists.txt b/pico_w/bt/hid_keyboard_demo/CMakeLists.txt deleted file mode 100644 index 29a8ac781..000000000 --- a/pico_w/bt/hid_keyboard_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hid_keyboard_demo) diff --git a/pico_w/bt/hid_mouse_demo/CMakeLists.txt b/pico_w/bt/hid_mouse_demo/CMakeLists.txt deleted file mode 100644 index aebb18a9a..000000000 --- a/pico_w/bt/hid_mouse_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hid_mouse_demo) diff --git a/pico_w/bt/hog_boot_host_demo/CMakeLists.txt b/pico_w/bt/hog_boot_host_demo/CMakeLists.txt deleted file mode 100644 index 536649db6..000000000 --- a/pico_w/bt/hog_boot_host_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hog_boot_host_demo) diff --git a/pico_w/bt/hog_host_demo/CMakeLists.txt b/pico_w/bt/hog_host_demo/CMakeLists.txt deleted file mode 100644 index c7ce663ff..000000000 --- a/pico_w/bt/hog_host_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hog_host_demo) diff --git a/pico_w/bt/hog_keyboard_demo/CMakeLists.txt b/pico_w/bt/hog_keyboard_demo/CMakeLists.txt deleted file mode 100644 index 6cae1e28f..000000000 --- a/pico_w/bt/hog_keyboard_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hog_keyboard_demo) diff --git a/pico_w/bt/hog_mouse_demo/CMakeLists.txt b/pico_w/bt/hog_mouse_demo/CMakeLists.txt deleted file mode 100644 index 2e176ef5d..000000000 --- a/pico_w/bt/hog_mouse_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hog_mouse_demo) diff --git a/pico_w/bt/hsp_ag_demo/CMakeLists.txt b/pico_w/bt/hsp_ag_demo/CMakeLists.txt deleted file mode 100644 index 1cdcfe054..000000000 --- a/pico_w/bt/hsp_ag_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hsp_ag_demo pico_btstack_sco_demo_util pico_btstack_sbc_encoder) diff --git a/pico_w/bt/hsp_hs_demo/CMakeLists.txt b/pico_w/bt/hsp_hs_demo/CMakeLists.txt deleted file mode 100644 index 24221c637..000000000 --- a/pico_w/bt/hsp_hs_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(hsp_hs_demo pico_btstack_sco_demo_util pico_btstack_sbc_encoder) diff --git a/pico_w/bt/le_credit_based_flow_control_mode_client/CMakeLists.txt b/pico_w/bt/le_credit_based_flow_control_mode_client/CMakeLists.txt deleted file mode 100644 index addcbe082..000000000 --- a/pico_w/bt/le_credit_based_flow_control_mode_client/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(le_credit_based_flow_control_mode_client) diff --git a/pico_w/bt/le_credit_based_flow_control_mode_server/CMakeLists.txt b/pico_w/bt/le_credit_based_flow_control_mode_server/CMakeLists.txt deleted file mode 100644 index 60529c970..000000000 --- a/pico_w/bt/le_credit_based_flow_control_mode_server/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(le_credit_based_flow_control_mode_server) diff --git a/pico_w/bt/le_mitm/CMakeLists.txt b/pico_w/bt/le_mitm/CMakeLists.txt deleted file mode 100644 index 0fc965fab..000000000 --- a/pico_w/bt/le_mitm/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(le_mitm) diff --git a/pico_w/bt/le_streamer_client/CMakeLists.txt b/pico_w/bt/le_streamer_client/CMakeLists.txt deleted file mode 100644 index 256f1dff6..000000000 --- a/pico_w/bt/le_streamer_client/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(le_streamer_client) diff --git a/pico_w/bt/led_counter/CMakeLists.txt b/pico_w/bt/led_counter/CMakeLists.txt deleted file mode 100644 index 9351f102f..000000000 --- a/pico_w/bt/led_counter/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(led_counter) diff --git a/pico_w/bt/mod_player/CMakeLists.txt b/pico_w/bt/mod_player/CMakeLists.txt deleted file mode 100644 index b27b23bd4..000000000 --- a/pico_w/bt/mod_player/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_library(mod_player_pins INTERFACE) -target_compile_definitions(mod_player_pins INTERFACE - PICO_AUDIO_I2S_DATA_PIN=9 - PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 - ) -picow_bt_example(mod_player pico_btstack_hxcmod_player mod_player_pins) diff --git a/pico_w/bt/nordic_spp_le_counter/CMakeLists.txt b/pico_w/bt/nordic_spp_le_counter/CMakeLists.txt deleted file mode 100644 index 9bb804fa7..000000000 --- a/pico_w/bt/nordic_spp_le_counter/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(nordic_spp_le_counter) diff --git a/pico_w/bt/nordic_spp_le_streamer/CMakeLists.txt b/pico_w/bt/nordic_spp_le_streamer/CMakeLists.txt deleted file mode 100644 index 0d3b48c50..000000000 --- a/pico_w/bt/nordic_spp_le_streamer/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(nordic_spp_le_streamer) diff --git a/pico_w/bt/pan_lwip_http_server/CMakeLists.txt b/pico_w/bt/pan_lwip_http_server/CMakeLists.txt deleted file mode 100644 index 82379beb0..000000000 --- a/pico_w/bt/pan_lwip_http_server/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_library(pan_lwip_dhserver INTERFACE) -target_sources(pan_lwip_dhserver INTERFACE - ${BTSTACK_3RD_PARTY_PATH}/lwip/dhcp-server/dhserver.c -) -target_include_directories(pan_lwip_dhserver INTERFACE - ${BTSTACK_3RD_PARTY_PATH}/lwip/dhcp-server -) - -picow_bt_example_target_name(pan_lwip_http_server TARGET_NAME) - -picow_bt_example_poll(pan_lwip_http_server ${TARGET_NAME} picow_bt_example_btstack_lwip_poll pan_lwip_dhserver pico_lwip_http pico_btstack_classic) -picow_bt_example_background(pan_lwip_http_server ${TARGET_NAME} picow_bt_example_btstack_lwip_background pan_lwip_dhserver pico_lwip_http pico_btstack_classic) -picow_bt_example_freertos(pan_lwip_http_server ${TARGET_NAME} picow_bt_example_btstack_lwip_freertos pan_lwip_dhserver pico_lwip_http pico_btstack_classic) diff --git a/pico_w/bt/pbap_client_demo/CMakeLists.txt b/pico_w/bt/pbap_client_demo/CMakeLists.txt deleted file mode 100644 index 503c52bae..000000000 --- a/pico_w/bt/pbap_client_demo/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(pbap_client_demo) diff --git a/pico_w/bt/picow_bt_example_background.c b/pico_w/bt/picow_bt_example_background.c deleted file mode 100644 index 24794d2b1..000000000 --- a/pico_w/bt/picow_bt_example_background.c +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "btstack_run_loop.h" -#include "pico/stdlib.h" -#include "picow_bt_example_common.h" - -int main() { - stdio_init_all(); - - int res = picow_bt_example_init(); - if (res){ - return -1; - } - - picow_bt_example_main(); - btstack_run_loop_execute(); -} diff --git a/pico_w/bt/picow_bt_example_common.c b/pico_w/bt/picow_bt_example_common.c deleted file mode 100644 index 7d93520b1..000000000 --- a/pico_w/bt/picow_bt_example_common.c +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#if TEST_AUDIO -#include "btstack_audio.h" -#endif -#include "btstack_event.h" -#include "hal_led.h" -#include "pico/cyw43_arch.h" -#include "pico/stdlib.h" -#include "btstack.h" - -#if defined(WIFI_SSID) && defined(WIFI_PASSWORD) -#define TEST_BTWIFI 1 -#endif - -#if TEST_BTWIFI -#include "lwip/ip4_addr.h" -#include "lwip/apps/lwiperf.h" -#endif - -// Start the btstack example -int btstack_main(int argc, const char * argv[]); - -#if TEST_AUDIO -const btstack_audio_sink_t * btstack_audio_pico_sink_get_instance(void); -#endif - -static btstack_packet_callback_registration_t hci_event_callback_registration; -static int led_state = 0; - -void hal_led_toggle(void){ - led_state = 1 - led_state; - cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_state); -} - -static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - UNUSED(size); - UNUSED(channel); - bd_addr_t local_addr; - if (packet_type != HCI_EVENT_PACKET) return; - switch(hci_event_packet_get_type(packet)){ - case BTSTACK_EVENT_STATE: - if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; - gap_local_bd_addr(local_addr); - printf("BTstack up and running on %s.\n", bd_addr_to_str(local_addr)); - break; - default: - break; - } -} - -#if TEST_BTWIFI -static void iperf_report(void *arg, enum lwiperf_report_type report_type, - const ip_addr_t *local_addr, u16_t local_port, const ip_addr_t *remote_addr, u16_t remote_port, - u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec) { - static uint32_t total_iperf_megabytes = 0; - uint32_t mbytes = bytes_transferred / 1024 / 1024; - float mbits = bandwidth_kbitpsec / 1000.0; - total_iperf_megabytes += mbytes; - printf("Completed iperf transfer of %u MBytes @ %.1f Mbits/sec\n", mbytes, mbits); - printf("Total iperf megabytes since start %u Mbytes\n", total_iperf_megabytes); -} -#endif - -int picow_bt_example_init(void) { - // initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1) - if (cyw43_arch_init()) { - printf("failed to initialise cyw43_arch\n"); - return -1; - } - - // inform about BTstack state - hci_event_callback_registration.callback = &packet_handler; - hci_add_event_handler(&hci_event_callback_registration); - - // setup i2s audio for sink -#if TEST_AUDIO - btstack_audio_sink_set_instance(btstack_audio_pico_sink_get_instance()); -#endif - return 0; -} - -void picow_bt_example_main(void) { - - btstack_main(0, NULL); - -#if TEST_BTWIFI - uint32_t start_ms = to_ms_since_boot(get_absolute_time()); - cyw43_arch_enable_sta_mode(); - printf("Connecting to WiFi \"%s\"...\n", WIFI_SSID); - if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { - panic("failed to connect"); - } else { - printf("Connected in %lus.\n", (to_ms_since_boot(get_absolute_time()) - start_ms) / 1000); - } - printf("\nReady, running iperf server at %s\n", ip4addr_ntoa(netif_ip4_addr(netif_list))); - lwiperf_start_tcp_server_default(&iperf_report, NULL); -#endif -} diff --git a/pico_w/bt/picow_bt_example_common.h b/pico_w/bt/picow_bt_example_common.h deleted file mode 100644 index 725322706..000000000 --- a/pico_w/bt/picow_bt_example_common.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -/* - * \brief Initialise BTstack example with cyw43 - * - * \return 0 if ok - */ -int picow_bt_example_init(void); - -/* - * \brief Run the BTstack example - * - */ -void picow_bt_example_main(void); diff --git a/pico_w/bt/picow_bt_example_freertos.c b/pico_w/bt/picow_bt_example_freertos.c deleted file mode 100644 index b3c8b3b15..000000000 --- a/pico_w/bt/picow_bt_example_freertos.c +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include -#include "pico/stdlib.h" -#include "FreeRTOS.h" -#include "task.h" - -#include "picow_bt_example_common.h" - -#if HAVE_LWIP -#include "pico/lwip_freertos.h" -#include "pico/cyw43_arch.h" -#endif - -#ifndef RUN_FREERTOS_ON_CORE -#define RUN_FREERTOS_ON_CORE 0 -#endif - -#define TEST_TASK_PRIORITY ( tskIDLE_PRIORITY + 2UL ) -#define BLINK_TASK_PRIORITY ( tskIDLE_PRIORITY + 1UL ) - -#ifdef TEST_BLINK_TASK -void blink_task(__unused void *params) { - printf("blink_task starts\n"); - while (true) { -#if 0 && configNUMBER_OF_CORES > 1 - static int last_core_id; - if (portGET_CORE_ID() != last_core_id) { - last_core_id = portGET_CORE_ID(); - printf("blinking now from core %d\n", last_core_id); - } -#endif - hal_led_toggle(); - vTaskDelay(200); - } -} -#endif - -void main_task(__unused void *params) { - - int res = picow_bt_example_init(); - if (res){ - return; - } - -// If we're using lwip but not via cyw43 (e.g. pan) we have to call this -#if HAVE_LWIP && !CYW43_LWIP - lwip_freertos_init(cyw43_arch_async_context()); -#endif - - picow_bt_example_main(); - -#ifdef TEST_BLINK_TASK - xTaskCreate(blink_task, "BlinkThread", configMINIMAL_STACK_SIZE, NULL, BLINK_TASK_PRIORITY, NULL); -#endif - - while(true) { - vTaskDelay(1000); - } - -#if HAVE_LWIP && !CYW43_LWIP - lwip_freertos_deinit(cyw43_arch_async_context()); -#endif -} - -void vLaunch( void) { - TaskHandle_t task; - xTaskCreate(main_task, "TestMainThread", 1024, NULL, TEST_TASK_PRIORITY, &task); - -#if NO_SYS && configUSE_CORE_AFFINITY && configNUMBER_OF_CORES > 1 - // we must bind the main task to one core (well at least while the init is called) - // (note we only do this in NO_SYS mode, because cyw43_arch_freertos - // takes care of it otherwise) - vTaskCoreAffinitySet(task, 1); -#endif - - /* Start the tasks and timer running. */ - vTaskStartScheduler(); -} - -int main() -{ - stdio_init_all(); - - /* Configure the hardware ready to run the demo. */ - const char *rtos_name; -#if ( configNUMBER_OF_CORES > 1 ) - rtos_name = "FreeRTOS SMP"; -#else - rtos_name = "FreeRTOS"; -#endif - -#if ( configNUMBER_OF_CORES == 2 ) - printf("Starting %s on both cores:\n", rtos_name); - vLaunch(); -#elif ( RUN_FREE_RTOS_ON_CORE == 1 ) - printf("Starting %s on core 1:\n", rtos_name); - multicore_launch_core1(vLaunch); - while (true); -#else - printf("Starting %s on core 0:\n", rtos_name); - vLaunch(); -#endif - return 0; -} diff --git a/pico_w/bt/picow_bt_example_poll.c b/pico_w/bt/picow_bt_example_poll.c deleted file mode 100644 index 24794d2b1..000000000 --- a/pico_w/bt/picow_bt_example_poll.c +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "btstack_run_loop.h" -#include "pico/stdlib.h" -#include "picow_bt_example_common.h" - -int main() { - stdio_init_all(); - - int res = picow_bt_example_init(); - if (res){ - return -1; - } - - picow_bt_example_main(); - btstack_run_loop_execute(); -} diff --git a/pico_w/bt/sdp_bnep_query/CMakeLists.txt b/pico_w/bt/sdp_bnep_query/CMakeLists.txt deleted file mode 100644 index 17cb14f0c..000000000 --- a/pico_w/bt/sdp_bnep_query/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(sdp_bnep_query) diff --git a/pico_w/bt/sdp_general_query/CMakeLists.txt b/pico_w/bt/sdp_general_query/CMakeLists.txt deleted file mode 100644 index e095cdedf..000000000 --- a/pico_w/bt/sdp_general_query/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(sdp_general_query) diff --git a/pico_w/bt/sdp_rfcomm_query/CMakeLists.txt b/pico_w/bt/sdp_rfcomm_query/CMakeLists.txt deleted file mode 100644 index f558231de..000000000 --- a/pico_w/bt/sdp_rfcomm_query/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(sdp_rfcomm_query) diff --git a/pico_w/bt/sine_player/CMakeLists.txt b/pico_w/bt/sine_player/CMakeLists.txt deleted file mode 100644 index 0ccbddef8..000000000 --- a/pico_w/bt/sine_player/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_library(sine_player_pins INTERFACE) -target_compile_definitions(sine_player_pins INTERFACE - PICO_AUDIO_I2S_DATA_PIN=9 - PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 - ) -picow_bt_example(sine_player sine_player_pins) diff --git a/pico_w/bt/sm_pairing_central/CMakeLists.txt b/pico_w/bt/sm_pairing_central/CMakeLists.txt deleted file mode 100644 index 456096d03..000000000 --- a/pico_w/bt/sm_pairing_central/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(sm_pairing_central) diff --git a/pico_w/bt/sm_pairing_peripheral/CMakeLists.txt b/pico_w/bt/sm_pairing_peripheral/CMakeLists.txt deleted file mode 100644 index 66e5a27df..000000000 --- a/pico_w/bt/sm_pairing_peripheral/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(sm_pairing_peripheral) diff --git a/pico_w/bt/spp_and_gatt_counter/CMakeLists.txt b/pico_w/bt/spp_and_gatt_counter/CMakeLists.txt deleted file mode 100644 index 7ed8e4a9c..000000000 --- a/pico_w/bt/spp_and_gatt_counter/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(spp_and_gatt_counter) diff --git a/pico_w/bt/spp_and_gatt_streamer/CMakeLists.txt b/pico_w/bt/spp_and_gatt_streamer/CMakeLists.txt deleted file mode 100644 index 47afd6a41..000000000 --- a/pico_w/bt/spp_and_gatt_streamer/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(spp_and_gatt_streamer) diff --git a/pico_w/bt/spp_counter/CMakeLists.txt b/pico_w/bt/spp_counter/CMakeLists.txt deleted file mode 100644 index 9465d5c39..000000000 --- a/pico_w/bt/spp_counter/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(spp_counter) diff --git a/pico_w/bt/spp_flowcontrol/CMakeLists.txt b/pico_w/bt/spp_flowcontrol/CMakeLists.txt deleted file mode 100644 index 75c4b838d..000000000 --- a/pico_w/bt/spp_flowcontrol/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(spp_flowcontrol) diff --git a/pico_w/bt/spp_streamer/CMakeLists.txt b/pico_w/bt/spp_streamer/CMakeLists.txt deleted file mode 100644 index f35d40169..000000000 --- a/pico_w/bt/spp_streamer/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(spp_streamer) diff --git a/pico_w/bt/spp_streamer_client/CMakeLists.txt b/pico_w/bt/spp_streamer_client/CMakeLists.txt deleted file mode 100644 index b85838859..000000000 --- a/pico_w/bt/spp_streamer_client/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(spp_streamer_client) diff --git a/pico_w/bt/spp_streamer_with_wifi/CMakeLists.txt b/pico_w/bt/spp_streamer_with_wifi/CMakeLists.txt deleted file mode 100644 index f30521f5b..000000000 --- a/pico_w/bt/spp_streamer_with_wifi/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(NAME spp_streamer) -picow_bt_example_target_name(${NAME}_with_wifi TARGET_NAME) - -picow_bt_example_poll(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_poll pico_btstack_classic) -picow_bt_example_background(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_background pico_btstack_classic) -picow_bt_example_freertos(${NAME} ${TARGET_NAME} picow_bt_example_cyw43_lwip_freertos pico_btstack_classic) diff --git a/pico_w/bt/standalone/CMakeLists.txt b/pico_w/bt/standalone/CMakeLists.txt deleted file mode 100644 index ef03fe482..000000000 --- a/pico_w/bt/standalone/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_subdirectory(client) -add_subdirectory(server) diff --git a/pico_w/bt/standalone/btstack_config_common.h b/pico_w/bt/standalone/btstack_config_common.h deleted file mode 100644 index 336e547fe..000000000 --- a/pico_w/bt/standalone/btstack_config_common.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _PICO_BTSTACK_CONFIG_COMMON_H -#define _PICO_BTSTACK_CONFIG_COMMON_H - -#ifndef ENABLE_BLE -#error Please link to pico_btstack_ble -#endif - -// BTstack features that can be enabled -#define ENABLE_LE_PERIPHERAL -#define ENABLE_LOG_INFO -#define ENABLE_LOG_ERROR -#define ENABLE_PRINTF_HEXDUMP - -// for the client -#if RUNNING_AS_CLIENT -#define ENABLE_LE_CENTRAL -#define MAX_NR_GATT_CLIENTS 1 -#else -#define MAX_NR_GATT_CLIENTS 0 -#endif - -// BTstack configuration. buffers, sizes, ... -#define HCI_OUTGOING_PRE_BUFFER_SIZE 4 -#define HCI_ACL_PAYLOAD_SIZE (255 + 4) -#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4 -#define MAX_NR_HCI_CONNECTIONS 1 -#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 (255+4) -#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 - -#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H diff --git a/pico_w/bt/standalone/client/CMakeLists.txt b/pico_w/bt/standalone/client/CMakeLists.txt deleted file mode 100644 index 808d2fdf9..000000000 --- a/pico_w/bt/standalone/client/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Standalone example that connects to picow_ble_temp_sensor and reads the temperature -# Flahes once quickly each second when it's running but not connected to another device -# Flashes twice quickly each second when connected to another device and reading it's temperature -add_executable(picow_ble_temp_reader - client.c - ) -target_link_libraries(picow_ble_temp_reader - pico_stdlib - pico_btstack_ble - pico_btstack_cyw43 - pico_cyw43_arch_none - hardware_adc - ) -target_include_directories(picow_ble_temp_reader PRIVATE - ${CMAKE_CURRENT_LIST_DIR} - ${CMAKE_CURRENT_LIST_DIR}/.. # For our common btstack config - ) -target_compile_definitions(picow_ble_temp_reader PRIVATE - RUNNING_AS_CLIENT=1 -) - -pico_add_extra_outputs(picow_ble_temp_reader) -example_auto_set_url(picow_ble_temp_reader) diff --git a/pico_w/bt/standalone/server/CMakeLists.txt b/pico_w/bt/standalone/server/CMakeLists.txt deleted file mode 100644 index b18ac013a..000000000 --- a/pico_w/bt/standalone/server/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -# Standalone example that reads from the on board temperature sensor and sends notifications via BLE -# Flashes slowly each second to show it's running -add_executable(picow_ble_temp_sensor - server.c - ) -target_link_libraries(picow_ble_temp_sensor - pico_stdlib - pico_btstack_ble - pico_btstack_cyw43 - pico_cyw43_arch_none - hardware_adc - ) -target_include_directories(picow_ble_temp_sensor PRIVATE - ${CMAKE_CURRENT_LIST_DIR} - ${CMAKE_CURRENT_LIST_DIR}/.. # For our common btstack config - ) -pico_btstack_make_gatt_header(picow_ble_temp_sensor PRIVATE "${CMAKE_CURRENT_LIST_DIR}/temp_sensor.gatt") - -pico_add_extra_outputs(picow_ble_temp_sensor) -example_auto_set_url(picow_ble_temp_sensor) diff --git a/pico_w/bt/ublox_spp_le_counter/CMakeLists.txt b/pico_w/bt/ublox_spp_le_counter/CMakeLists.txt deleted file mode 100644 index 92ef01ca3..000000000 --- a/pico_w/bt/ublox_spp_le_counter/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -picow_bt_example(ublox_spp_le_counter) diff --git a/pico_w/wifi/CMakeLists.txt b/pico_w/wifi/CMakeLists.txt index b096d2583..36d73f43f 100644 --- a/pico_w/wifi/CMakeLists.txt +++ b/pico_w/wifi/CMakeLists.txt @@ -4,6 +4,7 @@ set(WIFI_PASSWORD "${WIFI_PASSWORD}" CACHE INTERNAL "WiFi password for examples" add_subdirectory_exclude_platforms(blink) add_subdirectory_exclude_platforms(wifi_scan) add_subdirectory_exclude_platforms(access_point) +add_subdirectory_exclude_platforms(access_point_wifi_provisioning) if ("${WIFI_SSID}" STREQUAL "") message("Skipping some Pico W examples as WIFI_SSID is not defined") diff --git a/pico_w/wifi/access_point_wifi_provisioning/AP_provisioning.c b/pico_w/wifi/access_point_wifi_provisioning/AP_provisioning.c new file mode 100644 index 000000000..ec89a383a --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/AP_provisioning.c @@ -0,0 +1,355 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/cyw43_arch.h" +#include "pico/stdlib.h" + +#include "lwip/ip4_addr.h" +#include "lwip/init.h" +#include "lwip/apps/httpd.h" + +#include "dhcpserver.h" +#include "dnsserver.h" + +#include "pico/flash.h" +#include "hardware/flash.h" // for saving succesful credentials + +static absolute_time_t wifi_connected_time; +static bool led_on = false; + +// max lengths + 1 +static char ssid[33]; +static char password[64]; + +static int num_credentials; + +static bool connection_status = false; + +// how many sectors would you like to reserve +// each sector is 4096 bytes, so can hold 40 pairs of max length credentials +#define DESIRED_FLASH_SECTORS 1 +static char ssid_list[40 * DESIRED_FLASH_SECTORS][33]; +static char password_list[40 * DESIRED_FLASH_SECTORS][64]; + +// Define flash offset towards end of flash +#ifndef PICO_FLASH_BANK_TOTAL_SIZE +#define PICO_FLASH_BANK_TOTAL_SIZE (FLASH_SECTOR_SIZE * 2u) +#endif + +#ifndef PICO_FLASH_BANK_STORAGE_OFFSET +#if PICO_RP2350 && PICO_RP2350_A2_SUPPORTED +#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE - PICO_FLASH_BANK_TOTAL_SIZE - FLASH_SECTOR_SIZE * DESIRED_FLASH_SECTORS) +#else +#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - PICO_FLASH_BANK_TOTAL_SIZE - FLASH_SECTOR_SIZE * DESIRED_FLASH_SECTORS) +#endif +#endif + +static const uint8_t *flash_target_contents = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET); + +// Function prototypes +static void call_flash_range_erase(void *param); +static void call_flash_range_program(void *param); + +static void save_credentials(char ssid[], char password[]); +static void read_credentials(void); + +static void attempt_wifi_connection(void); + +static const char *credential_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]); +static const char *connect_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]); +static const char *connect_from_saved_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]); +static const char *clear_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]); + +static u16_t ssi_handler(int iIndex, char *pcInsert, int iInsertLen +#if LWIP_HTTPD_SSI_MULTIPART + , uint16_t current_tag_part, uint16_t *next_tag_part +#endif +); + +static tCGI cgi_handlers[] = { + { "/credentials.cgi", credential_cgi_handler }, + { "/connect.cgi", connect_cgi_handler }, + { "/connect_from_saved.cgi", connect_from_saved_cgi_handler}, + {"/clear.cgi", clear_cgi_handler} +}; + +// Be aware of LWIP_HTTPD_MAX_TAG_NAME_LEN +static const char *ssi_tags[] = { + "wifilist", + "ssid", + "password" +}; + +int main() { + stdio_init_all(); + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + printf("intitialised\n"); + + // for testing, erase memory first + // might need to erase memory first time you use provisioning in case there are garbage values in flash + //int rc = flash_safe_execute(call_flash_range_erase, (void*)FLASH_TARGET_OFFSET, UINT32_MAX); + //hard_assert(rc == PICO_OK); + + // First, try to connect to network using saved credentials + read_credentials(); + + cyw43_arch_enable_sta_mode(); + for (int i = 0; i < num_credentials; i++) { + if (cyw43_arch_wifi_connect_timeout_ms(ssid_list[i], password_list[i], CYW43_AUTH_WPA2_AES_PSK, 5000)) { + printf("failed to connect with saved credentials %i \n", i); + } else { + printf("Connected.\n"); + connection_status = true; + break; + } + } + + // If this fails, enable access point + if (connection_status == false) { + cyw43_arch_disable_sta_mode(); + cyw43_arch_enable_ap_mode("picow_test", "12345678", CYW43_AUTH_WPA2_AES_PSK); + printf("\nReady, running server at %s\n", ip4addr_ntoa(netif_ip4_addr(netif_list))); + + #if LWIP_IPV6 + #define IP(x) ((x).u_addr.ip4) + #else + #define IP(x) (x) + #endif + + ip4_addr_t mask; + ip4_addr_t gw; + IP(gw).addr = PP_HTONL(CYW43_DEFAULT_IP_AP_ADDRESS); + IP(mask).addr = PP_HTONL(CYW43_DEFAULT_IP_MASK); + + #undef IP + dhcp_server_t dhcp_server; + dhcp_server_init(&dhcp_server, &gw, &mask); + + dns_server_t dns_server; + dns_server_init(&dns_server, &gw); + + char hostname[sizeof(CYW43_HOST_NAME) + 4]; + memcpy(&hostname[0], CYW43_HOST_NAME, sizeof(CYW43_HOST_NAME) - 1); + hostname[sizeof(hostname) - 1] = '\0'; + netif_set_hostname(&cyw43_state.netif[CYW43_ITF_STA], hostname); + + // start http server + wifi_connected_time = get_absolute_time(); + + // setup http server + cyw43_arch_lwip_begin(); + httpd_init(); + http_set_cgi_handlers(cgi_handlers, LWIP_ARRAYSIZE(cgi_handlers)); + http_set_ssi_handler(ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags)); + cyw43_arch_lwip_end(); + } + + //wait for connection + while(connection_status == false) { + cyw43_arch_poll(); + cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000)); + } + + printf("Finished provisioning credentials. \n"); + cyw43_arch_deinit(); + return 0; +} + +// This function will be called when it's safe to call flash_range_erase +static void call_flash_range_erase(void *param) { + uint32_t offset = (uint32_t)param; + flash_range_erase(offset, FLASH_SECTOR_SIZE * DESIRED_FLASH_SECTORS); +} + +// This function will be called when it's safe to call flash_range_program +static void call_flash_range_program(void *param) { + uint32_t offset = ((uintptr_t*)param)[0]; + const uint8_t *data = (const uint8_t *)((uintptr_t*)param)[1]; + flash_range_program(offset, data, FLASH_SECTOR_SIZE * DESIRED_FLASH_SECTORS); +} + +// Functions for saving and reading credentials from flash +static void save_credentials(char ssid[], char password[]) { + // create empty 256 byte list + uint8_t flash_data[FLASH_SECTOR_SIZE * DESIRED_FLASH_SECTORS] = {0}; + + uint ssid_len = strlen(ssid); + uint password_len = strlen(password); + uint credential_count; + + //first check how many credentials are already saved + if (flash_target_contents[1] != 255) { + credential_count = flash_target_contents[1]; + //incriment this count since we are about to add a credential + credential_count++; + flash_data[1] = credential_count; + } else { + // first (empty) save, so dont want to incriment + credential_count = 0; + } + + //now need to find how far through the flash to start writing, and also add previous stuff to flash data + uint write_start_location = 2; + if (credential_count != 0) { + uint count = 0; + while (count < 2 * credential_count - 2) { + flash_data[write_start_location] = flash_target_contents[write_start_location]; + if (flash_target_contents[write_start_location] == 0) { + count++; + } + write_start_location++; + } + } + + // no character has ascii value 0, so we can seperate our ssid and password with a single 0 + // first add ssid + for (uint i = 0; i < ssid_len; i++) { + int ascii = (int) ssid[i]; + //printf("%i\n", ascii); + flash_data[i + write_start_location] = ascii; + } + + //next add password + for (uint i = 0; i < password_len; i++) { + int ascii = (int) password[i]; + flash_data[i + ssid_len + write_start_location + 1] = ascii; + } + + // must always erase flash before write + int rc = flash_safe_execute(call_flash_range_erase, (void*)FLASH_TARGET_OFFSET, UINT32_MAX); + hard_assert(rc == PICO_OK); + + // write flash + uintptr_t params[] = { FLASH_TARGET_OFFSET, (uintptr_t)flash_data}; + rc = flash_safe_execute(call_flash_range_program, params, UINT32_MAX); + hard_assert(rc == PICO_OK); +} + +static void read_credentials(void) { + uint credential_count; + + // first check if the flash page begins with FF - this indicates the flash has not yet been written to + // so must initialise with empty write + if (flash_target_contents[0] == 255) { + save_credentials("", ""); + } + + //second byte saves credential count (allows 255 sets of credentials, should be enough) + credential_count = flash_target_contents[1]; + num_credentials = credential_count; + //initialise temporary ssid and password as 1 bigger than max to ensure null termination + char t_ssid_list[20][33] = {0}; + char t_password_list[20][64] = {0}; + + uint space_count = 0; + uint start_index = 1; + + for (uint i = 2; i < FLASH_SECTOR_SIZE * DESIRED_FLASH_SECTORS; i++) { + if (space_count >= 2*credential_count) { + break; + } else if (flash_target_contents[i] == 0) { + space_count++; + start_index = i; + printf("\n"); + //printf("space count %i\n", space_count); + } else if (flash_target_contents[i] != 0 && space_count % 2 == 0) { + // there is a char, and even space count. So we are reading a ssid + t_ssid_list[(int) space_count / 2][i - start_index - 1] = flash_target_contents[i]; + //printf("%c", flash_target_contents[i]); + } else if (flash_target_contents[i] != 0 && space_count % 2 == 1) { + // there is a char and odd space count, so reading password + t_password_list[(int) space_count / 2][i - start_index - 1] = flash_target_contents[i]; + //printf("%c", flash_target_contents[i]); + } + } + + // update global ssid and password lists + memset(ssid_list, 0, sizeof(ssid_list)); + memcpy(ssid_list, t_ssid_list, sizeof(t_ssid_list)); + + memset(password_list, 0, sizeof(password_list)); + memcpy(password_list, t_password_list, sizeof(t_password_list)); +} + +static void attempt_wifi_connection(void) { + cyw43_arch_disable_ap_mode(); + cyw43_arch_enable_sta_mode(); + + if (cyw43_arch_wifi_connect_timeout_ms(ssid, password, CYW43_AUTH_WPA2_AES_PSK, 15000)) { + panic("Failed to connect!"); + } else { + printf("Connected.\n"); + connection_status = true; + // success, so save credentials for future use + save_credentials(ssid, password); + } +} + +static const char *credential_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { + printf("credential_cgi_handler called\n"); + strncpy(ssid, pcValue[0], sizeof(ssid) - 1); + strncpy(password, pcValue[1], sizeof(password) - 1); + printf("SSID AND PASSWORD: %s %s \n", ssid, password); + return "/index.shtml"; +} + +static const char *connect_from_saved_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { + printf("load_from_saved_cgi_handler called\n"); + strncpy(ssid, ssid_list[atoi(pcValue[0])], sizeof(ssid) - 1); + strncpy(password, password_list[atoi(pcValue[0])], sizeof(password) - 1); + attempt_wifi_connection(); + return "/index.shtml"; +} + +static const char *connect_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { + printf("connect_cgi_handler called\n"); + attempt_wifi_connection(); + return "/index.shtml"; +} + +static const char *clear_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { + printf("clear_cgi_handler called\n"); + int rc = flash_safe_execute(call_flash_range_erase, (void*)FLASH_TARGET_OFFSET, UINT32_MAX); + hard_assert(rc == PICO_OK); + save_credentials("", ""); + read_credentials(); + return "/index.shtml"; +} + +// Note that the buffer size is limited by LWIP_HTTPD_MAX_TAG_INSERT_LEN, so use LWIP_HTTPD_SSI_MULTIPART to return larger amounts of data +static u16_t ssi_handler(int iIndex, char *pcInsert, int iInsertLen +#if LWIP_HTTPD_SSI_MULTIPART + , uint16_t current_tag_part, uint16_t *next_tag_part +#endif +) { + int printed = 0; + switch (iIndex) { + case 0: { // wifilist + for (int i = 0; i < num_credentials; i++) { + printed += snprintf(pcInsert + printed, iInsertLen - printed, + "
  • SSID: %s, Password: %s, Index: %i
  • ", + ssid_list[i], password_list[i], i); + } + break; + } + case 1: { // ssid + printed = snprintf(pcInsert, iInsertLen, ssid); + break; + } + case 2: { // password + printed = snprintf(pcInsert, iInsertLen, password); + break; + } + default: { // unknown tag + printed = 0; + break; + } + } + return printed; +} \ No newline at end of file diff --git a/pico_w/wifi/access_point_wifi_provisioning/CMakeLists.txt b/pico_w/wifi/access_point_wifi_provisioning/CMakeLists.txt new file mode 100644 index 000000000..c22c25a5d --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/CMakeLists.txt @@ -0,0 +1,25 @@ +add_executable(AP_provisioning + AP_provisioning.c + dhcpserver/dhcpserver.c + dnsserver/dnsserver.c + ) +target_include_directories(AP_provisioning PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ${PICO_LWIP_CONTRIB_PATH}/apps/httpd + ${CMAKE_CURRENT_LIST_DIR}/dhcpserver + ${CMAKE_CURRENT_LIST_DIR}/dnsserver + ) +target_link_libraries(AP_provisioning PRIVATE + pico_cyw43_arch_lwip_poll + pico_lwip_http + ap_provisioning_content + pico_stdlib + hardware_flash + ) +pico_add_extra_outputs(AP_provisioning) + +pico_add_library(ap_provisioning_content NOFLAG) +pico_set_lwip_httpd_content(ap_provisioning_content INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/content/index.shtml + ) \ No newline at end of file diff --git a/pico_w/wifi/access_point_wifi_provisioning/README.md b/pico_w/wifi/access_point_wifi_provisioning/README.md new file mode 100644 index 000000000..b264909ff --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/README.md @@ -0,0 +1,4 @@ +### Access point wifi provisioning + +This example demonstrates provisioning wifi credentials using an access point. The pico saves succesful credentials in flash, which it first uses when attempting to join wifi. If this fails, the pico starts an access point which you can connect to and input credentials. The DNS server means that any address you enter when connected to the access point will take you to the provisioning web page. + diff --git a/pico_w/wifi/access_point_wifi_provisioning/content/index.shtml b/pico_w/wifi/access_point_wifi_provisioning/content/index.shtml new file mode 100644 index 000000000..5f552d417 --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/content/index.shtml @@ -0,0 +1,57 @@ + + + + + + + +

    AP provisioning

    + +

    Enter new credentials, submit, then connect, or select from previously saved.

    + +
    + +
    +

    Current SSID:

    +

    Current password:

    + +
    +
    +
    +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +

    List of saved credentials:

    +
    +
    +
    + +
    +
    + + +
    + + + \ No newline at end of file diff --git a/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/LICENSE b/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/LICENSE new file mode 100644 index 000000000..8f9b52cbb --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2022 Damien P. George + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/dhcpserver.c b/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/dhcpserver.c new file mode 100644 index 000000000..2061d047e --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/dhcpserver.c @@ -0,0 +1,309 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// For DHCP specs see: +// https://www.ietf.org/rfc/rfc2131.txt +// https://tools.ietf.org/html/rfc2132 -- DHCP Options and BOOTP Vendor Extensions + +#include +#include +#include + +#include "cyw43_config.h" +#include "dhcpserver.h" +#include "lwip/udp.h" + +#define DHCPDISCOVER (1) +#define DHCPOFFER (2) +#define DHCPREQUEST (3) +#define DHCPDECLINE (4) +#define DHCPACK (5) +#define DHCPNACK (6) +#define DHCPRELEASE (7) +#define DHCPINFORM (8) + +#define DHCP_OPT_PAD (0) +#define DHCP_OPT_SUBNET_MASK (1) +#define DHCP_OPT_ROUTER (3) +#define DHCP_OPT_DNS (6) +#define DHCP_OPT_HOST_NAME (12) +#define DHCP_OPT_REQUESTED_IP (50) +#define DHCP_OPT_IP_LEASE_TIME (51) +#define DHCP_OPT_MSG_TYPE (53) +#define DHCP_OPT_SERVER_ID (54) +#define DHCP_OPT_PARAM_REQUEST_LIST (55) +#define DHCP_OPT_MAX_MSG_SIZE (57) +#define DHCP_OPT_VENDOR_CLASS_ID (60) +#define DHCP_OPT_CLIENT_ID (61) +#define DHCP_OPT_END (255) + +#define PORT_DHCP_SERVER (67) +#define PORT_DHCP_CLIENT (68) + +#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds + +#define MAC_LEN (6) +#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) + +typedef struct { + uint8_t op; // message opcode + uint8_t htype; // hardware address type + uint8_t hlen; // hardware address length + uint8_t hops; + uint32_t xid; // transaction id, chosen by client + uint16_t secs; // client seconds elapsed + uint16_t flags; + uint8_t ciaddr[4]; // client IP address + uint8_t yiaddr[4]; // your IP address + uint8_t siaddr[4]; // next server IP address + uint8_t giaddr[4]; // relay agent IP address + uint8_t chaddr[16]; // client hardware address + uint8_t sname[64]; // server host name + uint8_t file[128]; // boot file name + uint8_t options[312]; // optional parameters, variable, starts with magic +} dhcp_msg_t; + +static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) { + // family is AF_INET + // type is SOCK_DGRAM + + *udp = udp_new(); + if (*udp == NULL) { + return -ENOMEM; + } + + // Register callback + udp_recv(*udp, cb_udp_recv, (void *)cb_data); + + return 0; // success +} + +static void dhcp_socket_free(struct udp_pcb **udp) { + if (*udp != NULL) { + udp_remove(*udp); + *udp = NULL; + } +} + +static int dhcp_socket_bind(struct udp_pcb **udp, uint16_t port) { + // TODO convert lwIP errors to errno + return udp_bind(*udp, IP_ANY_TYPE, port); +} + +static int dhcp_socket_sendto(struct udp_pcb **udp, struct netif *nif, const void *buf, size_t len, uint32_t ip, uint16_t port) { + if (len > 0xffff) { + len = 0xffff; + } + + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (p == NULL) { + return -ENOMEM; + } + + memcpy(p->payload, buf, len); + + ip_addr_t dest; + IP4_ADDR(ip_2_ip4(&dest), ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); + err_t err; + if (nif != NULL) { + err = udp_sendto_if(*udp, p, &dest, port, nif); + } else { + err = udp_sendto(*udp, p, &dest, port); + } + + pbuf_free(p); + + if (err != ERR_OK) { + return err; + } + + return len; +} + +static uint8_t *opt_find(uint8_t *opt, uint8_t cmd) { + for (int i = 0; i < 308 && opt[i] != DHCP_OPT_END;) { + if (opt[i] == cmd) { + return &opt[i]; + } + i += 2 + opt[i + 1]; + } + return NULL; +} + +static void opt_write_n(uint8_t **opt, uint8_t cmd, size_t n, const void *data) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = n; + memcpy(o, data, n); + *opt = o + n; +} + +static void opt_write_u8(uint8_t **opt, uint8_t cmd, uint8_t val) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = 1; + *o++ = val; + *opt = o; +} + +static void opt_write_u32(uint8_t **opt, uint8_t cmd, uint32_t val) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = 4; + *o++ = val >> 24; + *o++ = val >> 16; + *o++ = val >> 8; + *o++ = val; + *opt = o; +} + +static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) { + dhcp_server_t *d = arg; + (void)upcb; + (void)src_addr; + (void)src_port; + + // This is around 548 bytes + dhcp_msg_t dhcp_msg; + + #define DHCP_MIN_SIZE (240 + 3) + if (p->tot_len < DHCP_MIN_SIZE) { + goto ignore_request; + } + + size_t len = pbuf_copy_partial(p, &dhcp_msg, sizeof(dhcp_msg), 0); + if (len < DHCP_MIN_SIZE) { + goto ignore_request; + } + + dhcp_msg.op = DHCPOFFER; + memcpy(&dhcp_msg.yiaddr, &ip4_addr_get_u32(ip_2_ip4(&d->ip)), 4); + + uint8_t *opt = (uint8_t *)&dhcp_msg.options; + opt += 4; // assume magic cookie: 99, 130, 83, 99 + + uint8_t *msgtype = opt_find(opt, DHCP_OPT_MSG_TYPE); + if (msgtype == NULL) { + // A DHCP package without MSG_TYPE? + goto ignore_request; + } + + switch (msgtype[2]) { + case DHCPDISCOVER: { + int yi = DHCPS_MAX_IP; + for (int i = 0; i < DHCPS_MAX_IP; ++i) { + if (memcmp(d->lease[i].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { + // MAC match, use this IP address + yi = i; + break; + } + if (yi == DHCPS_MAX_IP) { + // Look for a free IP address + if (memcmp(d->lease[i].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { + // IP available + yi = i; + } + uint32_t expiry = d->lease[i].expiry << 16 | 0xffff; + if ((int32_t)(expiry - cyw43_hal_ticks_ms()) < 0) { + // IP expired, reuse it + memset(d->lease[i].mac, 0, MAC_LEN); + yi = i; + } + } + } + if (yi == DHCPS_MAX_IP) { + // No more IP addresses left + goto ignore_request; + } + dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; + opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPOFFER); + break; + } + + case DHCPREQUEST: { + uint8_t *o = opt_find(opt, DHCP_OPT_REQUESTED_IP); + if (o == NULL) { + // Should be NACK + goto ignore_request; + } + if (memcmp(o + 2, &ip4_addr_get_u32(ip_2_ip4(&d->ip)), 3) != 0) { + // Should be NACK + goto ignore_request; + } + uint8_t yi = o[5] - DHCPS_BASE_IP; + if (yi >= DHCPS_MAX_IP) { + // Should be NACK + goto ignore_request; + } + if (memcmp(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { + // MAC match, ok to use this IP address + } else if (memcmp(d->lease[yi].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { + // IP unused, ok to use this IP address + memcpy(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN); + } else { + // IP already in use + // Should be NACK + goto ignore_request; + } + d->lease[yi].expiry = (cyw43_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16; + dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; + opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK); + printf("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n", + dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5], + dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3]); + break; + } + + default: + goto ignore_request; + } + + opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip))); + opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &ip4_addr_get_u32(ip_2_ip4(&d->nm))); + opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip))); // aka gateway; can have multiple addresses + opt_write_n(&opt, DHCP_OPT_DNS, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip))); // this server is the dns + opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S); + *opt++ = DHCP_OPT_END; + struct netif *nif = ip_current_input_netif(); + dhcp_socket_sendto(&d->udp, nif, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, 0xffffffff, PORT_DHCP_CLIENT); + +ignore_request: + pbuf_free(p); +} + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { + ip_addr_copy(d->ip, *ip); + ip_addr_copy(d->nm, *nm); + memset(d->lease, 0, sizeof(d->lease)); + if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) { + return; + } + dhcp_socket_bind(&d->udp, PORT_DHCP_SERVER); +} + +void dhcp_server_deinit(dhcp_server_t *d) { + dhcp_socket_free(&d->udp); +} diff --git a/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/dhcpserver.h b/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/dhcpserver.h new file mode 100644 index 000000000..2349d2ea4 --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/dhcpserver/dhcpserver.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H +#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H + +#include "lwip/ip_addr.h" + +#define DHCPS_BASE_IP (16) +#define DHCPS_MAX_IP (8) + +typedef struct _dhcp_server_lease_t { + uint8_t mac[6]; + uint16_t expiry; +} dhcp_server_lease_t; + +typedef struct _dhcp_server_t { + ip_addr_t ip; + ip_addr_t nm; + dhcp_server_lease_t lease[DHCPS_MAX_IP]; + struct udp_pcb *udp; +} dhcp_server_t; + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm); +void dhcp_server_deinit(dhcp_server_t *d); + +#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H diff --git a/pico_w/wifi/access_point_wifi_provisioning/dnsserver/dnsserver.c b/pico_w/wifi/access_point_wifi_provisioning/dnsserver/dnsserver.c new file mode 100644 index 000000000..029870b56 --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/dnsserver/dnsserver.c @@ -0,0 +1,235 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include "dnsserver.h" +#include "lwip/udp.h" + +#define PORT_DNS_SERVER 53 +#define DUMP_DATA 0 + +#define DEBUG_printf(...) +#define ERROR_printf printf + +typedef struct dns_header_t_ { + uint16_t id; + uint16_t flags; + uint16_t question_count; + uint16_t answer_record_count; + uint16_t authority_record_count; + uint16_t additional_record_count; +} dns_header_t; + +#define MAX_DNS_MSG_SIZE 300 + +static int dns_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) { + *udp = udp_new(); + if (*udp == NULL) { + return -ENOMEM; + } + udp_recv(*udp, cb_udp_recv, (void *)cb_data); + return ERR_OK; +} + +static void dns_socket_free(struct udp_pcb **udp) { + if (*udp != NULL) { + udp_remove(*udp); + *udp = NULL; + } +} + +static int dns_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) { + ip_addr_t addr; + IP4_ADDR(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); + err_t err = udp_bind(*udp, &addr, port); + if (err != ERR_OK) { + ERROR_printf("dns failed to bind to port %u: %d", port, err); + assert(false); + } + return err; +} + +#if DUMP_DATA +static void dump_bytes(const uint8_t *bptr, uint32_t len) { + unsigned int i = 0; + + for (i = 0; i < len;) { + if ((i & 0x0f) == 0) { + printf("\n"); + } else if ((i & 0x07) == 0) { + printf(" "); + } + printf("%02x ", bptr[i++]); + } + printf("\n"); +} +#endif + +static int dns_socket_sendto(struct udp_pcb **udp, const void *buf, size_t len, const ip_addr_t *dest, uint16_t port) { + if (len > 0xffff) { + len = 0xffff; + } + + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (p == NULL) { + ERROR_printf("DNS: Failed to send message out of memory\n"); + return -ENOMEM; + } + + memcpy(p->payload, buf, len); + err_t err = udp_sendto(*udp, p, dest, port); + + pbuf_free(p); + + if (err != ERR_OK) { + ERROR_printf("DNS: Failed to send message %d\n", err); + return err; + } + +#if DUMP_DATA + dump_bytes(buf, len); +#endif + return len; +} + +static void dns_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) { + dns_server_t *d = arg; + DEBUG_printf("dns_server_process %u\n", p->tot_len); + + uint8_t dns_msg[MAX_DNS_MSG_SIZE]; + dns_header_t *dns_hdr = (dns_header_t*)dns_msg; + + size_t msg_len = pbuf_copy_partial(p, dns_msg, sizeof(dns_msg), 0); + if (msg_len < sizeof(dns_header_t)) { + goto ignore_request; + } + +#if DUMP_DATA + dump_bytes(dns_msg, msg_len); +#endif + + uint16_t flags = lwip_ntohs(dns_hdr->flags); + uint16_t question_count = lwip_ntohs(dns_hdr->question_count); + + DEBUG_printf("len %d\n", msg_len); + DEBUG_printf("dns flags 0x%x\n", flags); + DEBUG_printf("dns question count 0x%x\n", question_count); + + // flags from rfc1035 + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + // Check QR indicates a query + if (((flags >> 15) & 0x1) != 0) { + DEBUG_printf("Ignoring non-query\n"); + goto ignore_request; + } + + // Check for standard query + if (((flags >> 11) & 0xf) != 0) { + DEBUG_printf("Ignoring non-standard query\n"); + goto ignore_request; + } + + // Check question count + if (question_count < 1) { + DEBUG_printf("Invalid question count\n"); + goto ignore_request; + } + + // Print the question + DEBUG_printf("question: "); + const uint8_t *question_ptr_start = dns_msg + sizeof(dns_header_t); + const uint8_t *question_ptr_end = dns_msg + msg_len; + const uint8_t *question_ptr = question_ptr_start; + while(question_ptr < question_ptr_end) { + if (*question_ptr == 0) { + question_ptr++; + break; + } else { + if (question_ptr > question_ptr_start) { + DEBUG_printf("."); + } + int label_len = *question_ptr++; + if (label_len > 63) { + DEBUG_printf("Invalid label\n"); + goto ignore_request; + } + DEBUG_printf("%.*s", label_len, question_ptr); + question_ptr += label_len; + } + } + DEBUG_printf("\n"); + + // Check question length + if (question_ptr - question_ptr_start > 255) { + DEBUG_printf("Invalid question length\n"); + goto ignore_request; + } + + // Skip QNAME and QTYPE + question_ptr += 4; + + // Generate answer + uint8_t *answer_ptr = dns_msg + (question_ptr - dns_msg); + *answer_ptr++ = 0xc0; // pointer + *answer_ptr++ = question_ptr_start - dns_msg; // pointer to question + + *answer_ptr++ = 0; + *answer_ptr++ = 1; // host address + + *answer_ptr++ = 0; + *answer_ptr++ = 1; // Internet class + + *answer_ptr++ = 0; + *answer_ptr++ = 0; + *answer_ptr++ = 0; + *answer_ptr++ = 60; // ttl 60s + + *answer_ptr++ = 0; + *answer_ptr++ = 4; // length + memcpy(answer_ptr, &d->ip.addr, 4); // use our address + answer_ptr += 4; + + dns_hdr->flags = lwip_htons( + 0x1 << 15 | // QR = response + 0x1 << 10 | // AA = authoritative + 0x1 << 7); // RA = authenticated + dns_hdr->question_count = lwip_htons(1); + dns_hdr->answer_record_count = lwip_htons(1); + dns_hdr->authority_record_count = 0; + dns_hdr->additional_record_count = 0; + + // Send the reply + DEBUG_printf("Sending %d byte reply to %s:%d\n", answer_ptr - dns_msg, ipaddr_ntoa(src_addr), src_port); + dns_socket_sendto(&d->udp, &dns_msg, answer_ptr - dns_msg, src_addr, src_port); + +ignore_request: + pbuf_free(p); +} + +void dns_server_init(dns_server_t *d, ip_addr_t *ip) { + if (dns_socket_new_dgram(&d->udp, d, dns_server_process) != ERR_OK) { + DEBUG_printf("dns server failed to start\n"); + return; + } + if (dns_socket_bind(&d->udp, 0, PORT_DNS_SERVER) != ERR_OK) { + DEBUG_printf("dns server failed to bind\n"); + return; + } + ip_addr_copy(d->ip, *ip); + DEBUG_printf("dns server listening on port %d\n", PORT_DNS_SERVER); +} + +void dns_server_deinit(dns_server_t *d) { + dns_socket_free(&d->udp); +} diff --git a/pico_w/wifi/access_point_wifi_provisioning/dnsserver/dnsserver.h b/pico_w/wifi/access_point_wifi_provisioning/dnsserver/dnsserver.h new file mode 100644 index 000000000..d23534c03 --- /dev/null +++ b/pico_w/wifi/access_point_wifi_provisioning/dnsserver/dnsserver.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _DNSSERVER_H_ +#define _DNSSERVER_H_ + +#include "lwip/ip_addr.h" + +typedef struct dns_server_t_ { + struct udp_pcb *udp; + ip_addr_t ip; +} dns_server_t; + +void dns_server_init(dns_server_t *d, ip_addr_t *ip); +void dns_server_deinit(dns_server_t *d); + +#endif diff --git a/pico_w/bt/standalone/lwipopts.h b/pico_w/wifi/access_point_wifi_provisioning/lwipopts.h similarity index 80% rename from pico_w/bt/standalone/lwipopts.h rename to pico_w/wifi/access_point_wifi_provisioning/lwipopts.h index 32bb24c51..98170b8de 100644 --- a/pico_w/bt/standalone/lwipopts.h +++ b/pico_w/wifi/access_point_wifi_provisioning/lwipopts.h @@ -1,13 +1,27 @@ #ifndef _LWIPOPTS_H #define _LWIPOPTS_H -// see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details +// Common settings used in most of the pico_w examples +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details) +// allow override in some examples +#ifndef NO_SYS #define NO_SYS 1 +#endif +// allow override in some examples +#ifndef LWIP_SOCKET #define LWIP_SOCKET 0 +#endif +#if PICO_CYW43_ARCH_POLL +#define MEM_LIBC_MALLOC 1 +#else +// MEM_LIBC_MALLOC is incompatible with non polling versions #define MEM_LIBC_MALLOC 0 +#endif #define MEM_ALIGNMENT 4 +#ifndef MEM_SIZE #define MEM_SIZE 4000 +#endif #define MEMP_NUM_TCP_SEG 32 #define MEMP_NUM_ARP_QUEUE 10 #define PBUF_POOL_SIZE 24 @@ -27,6 +41,7 @@ #define SYS_STATS 0 #define MEMP_STATS 0 #define LINK_STATS 0 +// #define ETH_PAD_SIZE 2 #define LWIP_CHKSUM_ALGORITHM 3 #define LWIP_DHCP 1 #define LWIP_IPV4 1 @@ -73,4 +88,14 @@ #define SLIP_DEBUG LWIP_DBG_OFF #define DHCP_DEBUG LWIP_DBG_OFF -#endif /* __LWIPOPTS_H__ */ +// Enable some httpd features +#define LWIP_HTTPD_CGI 1 +#define LWIP_HTTPD_SSI 1 +#define LWIP_HTTPD_SSI_MULTIPART 1 +#define LWIP_HTTPD_SUPPORT_POST 0 +#define LWIP_HTTPD_SSI_INCLUDE_TAG 0 + +// Generated file containing html data +#define HTTPD_FSDATA_FILE "pico_fsdata.inc" + +#endif \ No newline at end of file