-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathserver.c
More file actions
192 lines (158 loc) · 7.08 KB
/
Copy pathserver.c
File metadata and controls
192 lines (158 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
* 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 "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;
}