Add Bluetooth examples
Co-authored-by: Peter Harper <77111776+peterharperuk@users.noreply.github.com>
This commit is contained in:
68
pico_w/bt/standalone/CMakeLists.txt
Normal file
68
pico_w/bt/standalone/CMakeLists.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
# 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 server_common.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} # For 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)
|
||||
|
||||
# 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} # For 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)
|
||||
|
||||
if (WIFI_SSID AND WIFI_PASSWORD)
|
||||
# Another version of the sensor example, but this time also runs iperf over wifi
|
||||
add_executable(picow_ble_temp_sensor_with_wifi
|
||||
server_with_wifi.c server_common.c
|
||||
)
|
||||
target_link_libraries(picow_ble_temp_sensor_with_wifi
|
||||
pico_stdlib
|
||||
pico_btstack_ble
|
||||
pico_btstack_cyw43
|
||||
pico_cyw43_arch_lwip_threadsafe_background
|
||||
pico_lwip_iperf
|
||||
hardware_adc
|
||||
)
|
||||
target_include_directories(picow_ble_temp_sensor_with_wifi PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR} # For btstack config
|
||||
)
|
||||
target_compile_definitions(picow_ble_temp_sensor_with_wifi PRIVATE
|
||||
WIFI_SSID=\"${WIFI_SSID}\"
|
||||
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
|
||||
)
|
||||
pico_btstack_make_gatt_header(picow_ble_temp_sensor_with_wifi PRIVATE "${CMAKE_CURRENT_LIST_DIR}/temp_sensor.gatt")
|
||||
|
||||
pico_add_extra_outputs(picow_ble_temp_sensor_with_wifi)
|
||||
example_auto_set_url(picow_ble_temp_sensor_with_wifi)
|
||||
endif()
|
||||
58
pico_w/bt/standalone/btstack_config.h
Normal file
58
pico_w/bt/standalone/btstack_config.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef _PICO_BTSTACK_BTSTACK_CONFIG_H
|
||||
#define _PICO_BTSTACK_BTSTACK_CONFIG_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
|
||||
273
pico_w/bt/standalone/client.c
Normal file
273
pico_w/bt/standalone/client.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "btstack.h"
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#if 0
|
||||
#define DEBUG_LOG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_LOG(...)
|
||||
#endif
|
||||
|
||||
#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 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 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){
|
||||
printf("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){
|
||||
printf("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);
|
||||
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);
|
||||
} else {
|
||||
printf("Unexpected length %d\n", value_length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("Unknown packet type 0x%02x\n", hci_event_packet_get_type(packet));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("error\n");
|
||||
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);
|
||||
printf("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();
|
||||
printf("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_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);
|
||||
}
|
||||
printf("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 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();
|
||||
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
|
||||
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();
|
||||
return 0;
|
||||
}
|
||||
76
pico_w/bt/standalone/lwipopts.h
Normal file
76
pico_w/bt/standalone/lwipopts.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef _LWIPOPTS_H
|
||||
#define _LWIPOPTS_H
|
||||
|
||||
// see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details
|
||||
|
||||
#define NO_SYS 1
|
||||
#define LWIP_SOCKET 0
|
||||
#define MEM_LIBC_MALLOC 0
|
||||
#define MEM_ALIGNMENT 4
|
||||
#define MEM_SIZE 4000
|
||||
#define MEMP_NUM_TCP_SEG 32
|
||||
#define MEMP_NUM_ARP_QUEUE 10
|
||||
#define PBUF_POOL_SIZE 24
|
||||
#define LWIP_ARP 1
|
||||
#define LWIP_ETHERNET 1
|
||||
#define LWIP_ICMP 1
|
||||
#define LWIP_RAW 1
|
||||
#define TCP_WND (8 * TCP_MSS)
|
||||
#define TCP_MSS 1460
|
||||
#define TCP_SND_BUF (8 * TCP_MSS)
|
||||
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
|
||||
#define LWIP_NETIF_STATUS_CALLBACK 1
|
||||
#define LWIP_NETIF_LINK_CALLBACK 1
|
||||
#define LWIP_NETIF_HOSTNAME 1
|
||||
#define LWIP_NETCONN 0
|
||||
#define MEM_STATS 0
|
||||
#define SYS_STATS 0
|
||||
#define MEMP_STATS 0
|
||||
#define LINK_STATS 0
|
||||
#define LWIP_CHKSUM_ALGORITHM 3
|
||||
#define LWIP_DHCP 1
|
||||
#define LWIP_IPV4 1
|
||||
#define LWIP_TCP 1
|
||||
#define LWIP_UDP 1
|
||||
#define LWIP_DNS 1
|
||||
#define LWIP_TCP_KEEPALIVE 1
|
||||
#define LWIP_NETIF_TX_SINGLE_PBUF 1
|
||||
#define DHCP_DOES_ARP_CHECK 0
|
||||
#define LWIP_DHCP_DOES_ACD_CHECK 0
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define LWIP_DEBUG 1
|
||||
#define LWIP_STATS 1
|
||||
#define LWIP_STATS_DISPLAY 1
|
||||
#endif
|
||||
|
||||
#define ETHARP_DEBUG LWIP_DBG_OFF
|
||||
#define NETIF_DEBUG LWIP_DBG_OFF
|
||||
#define PBUF_DEBUG LWIP_DBG_OFF
|
||||
#define API_LIB_DEBUG LWIP_DBG_OFF
|
||||
#define API_MSG_DEBUG LWIP_DBG_OFF
|
||||
#define SOCKETS_DEBUG LWIP_DBG_OFF
|
||||
#define ICMP_DEBUG LWIP_DBG_OFF
|
||||
#define INET_DEBUG LWIP_DBG_OFF
|
||||
#define IP_DEBUG LWIP_DBG_OFF
|
||||
#define IP_REASS_DEBUG LWIP_DBG_OFF
|
||||
#define RAW_DEBUG LWIP_DBG_OFF
|
||||
#define MEM_DEBUG LWIP_DBG_OFF
|
||||
#define MEMP_DEBUG LWIP_DBG_OFF
|
||||
#define SYS_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_INPUT_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_RTO_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_CWND_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_WND_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_FR_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_RST_DEBUG LWIP_DBG_OFF
|
||||
#define UDP_DEBUG LWIP_DBG_OFF
|
||||
#define TCPIP_DEBUG LWIP_DBG_OFF
|
||||
#define PPP_DEBUG LWIP_DBG_OFF
|
||||
#define SLIP_DEBUG LWIP_DBG_OFF
|
||||
#define DHCP_DEBUG LWIP_DBG_OFF
|
||||
|
||||
#endif /* __LWIPOPTS_H__ */
|
||||
79
pico_w/bt/standalone/server.c
Normal file
79
pico_w/bt/standalone/server.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "btstack.h"
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/btstack_cyw43.h"
|
||||
#include "hardware/adc.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "server_common.h"
|
||||
|
||||
#define HEARTBEAT_PERIOD_MS 1000
|
||||
|
||||
static btstack_timer_source_t heartbeat;
|
||||
static btstack_packet_callback_registration_t hci_event_callback_registration;
|
||||
|
||||
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()) {
|
||||
printf("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);
|
||||
|
||||
// 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();
|
||||
return 0;
|
||||
}
|
||||
106
pico_w/bt/standalone/server_common.c
Normal file
106
pico_w/bt/standalone/server_common.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "btstack.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
#include "temp_sensor.h"
|
||||
#include "server_common.h"
|
||||
|
||||
#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;
|
||||
hci_con_handle_t con_handle;
|
||||
uint16_t current_temp;
|
||||
|
||||
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);
|
||||
printf("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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
printf("Write temp %.2f degc\n", deg_c);
|
||||
}
|
||||
22
pico_w/bt/standalone/server_common.h
Normal file
22
pico_w/bt/standalone/server_common.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef SERVER_COMMON_H_
|
||||
#define SERVER_COMMON_H_
|
||||
|
||||
#define ADC_CHANNEL_TEMPSENSOR 4
|
||||
|
||||
extern int le_notification_enabled;
|
||||
extern hci_con_handle_t con_handle;
|
||||
extern uint16_t current_temp;
|
||||
extern uint8_t const profile_data[];
|
||||
|
||||
void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
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);
|
||||
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);
|
||||
void poll_temp(void);
|
||||
|
||||
#endif
|
||||
117
pico_w/bt/standalone/server_with_wifi.c
Normal file
117
pico_w/bt/standalone/server_with_wifi.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "btstack.h"
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/ip4_addr.h"
|
||||
#include "lwip/apps/lwiperf.h"
|
||||
|
||||
#include "server_common.h"
|
||||
|
||||
#define HEARTBEAT_PERIOD_MS 1000
|
||||
|
||||
static void heartbeat_handler(async_context_t *context, async_at_time_worker_t *worker);
|
||||
|
||||
static async_at_time_worker_t heartbeat_worker = { .do_work = heartbeat_handler };
|
||||
static btstack_packet_callback_registration_t hci_event_callback_registration;
|
||||
|
||||
static void heartbeat_handler(async_context_t *context, async_at_time_worker_t *worker) {
|
||||
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
|
||||
async_context_add_at_time_worker_in_ms(context, &heartbeat_worker, HEARTBEAT_PERIOD_MS);
|
||||
}
|
||||
|
||||
// Report IP results and exit
|
||||
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);
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// initialize CYW43 architecture
|
||||
// - will enable BT if CYW43_ENABLE_BLUETOOTH == 1
|
||||
// - will enable lwIP if CYW43_LWIP == 1
|
||||
if (cyw43_arch_init()) {
|
||||
printf("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);
|
||||
|
||||
// register for ATT event
|
||||
att_server_register_packet_handler(packet_handler);
|
||||
|
||||
// set one-shot btstack timer
|
||||
async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &heartbeat_worker, HEARTBEAT_PERIOD_MS);
|
||||
|
||||
// Connect to Wi-Fi
|
||||
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)) {
|
||||
printf("failed to connect.\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("Connected.\n");
|
||||
}
|
||||
|
||||
// setup iperf
|
||||
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();
|
||||
|
||||
// turn on bluetooth!
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
|
||||
// For threadsafe background we can just enter a loop
|
||||
while(true) {
|
||||
sleep_ms(1000);
|
||||
}
|
||||
|
||||
cyw43_arch_deinit();
|
||||
return 0;
|
||||
}
|
||||
8
pico_w/bt/standalone/temp_sensor.gatt
Normal file
8
pico_w/bt/standalone/temp_sensor.gatt
Normal file
@@ -0,0 +1,8 @@
|
||||
PRIMARY_SERVICE, GAP_SERVICE
|
||||
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "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,
|
||||
Reference in New Issue
Block a user