Add Bluetooth examples

Co-authored-by: Peter Harper <77111776+peterharperuk@users.noreply.github.com>
This commit is contained in:
graham sanderson
2023-02-10 17:59:58 -06:00
parent 03f97a8999
commit 1c5d9aa567
127 changed files with 2296 additions and 63 deletions

View 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()

View 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

View 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(&notification_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(&notification_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;
}

View 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__ */

View 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;
}

View 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*)&current_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 *)&current_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);
}

View 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

View 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;
}

View 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,