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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
|
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch demonstrates the central API() that allows you to connect
* to multiple peripherals boards (Bluefruit nRF52 in peripheral mode, or
* any Bluefruit nRF51 boards).
*
* One or more Bluefruit boards, configured as a peripheral with the
* bleuart service running are required for this demo.
*
* This sketch will:
* - Read data from the HW serial port (normally USB serial, accessible
* via the Serial Monitor for example), and send any incoming data to
* all other peripherals connected to the central device.
* - Forward any incoming bleuart messages from a peripheral to all of
* the other connected devices.
*
* It is recommended to give each peripheral board a distinct name in order
* to more easily distinguish the individual devices.
*
* Connection Handle Explanation
* -----------------------------
* The total number of connections is BLE_MAX_CONN = BLE_PRPH_MAX_CONN + BLE_CENTRAL_MAX_CONN.
*
* The 'connection handle' is an integer number assigned by the SoftDevice
* (Nordic's proprietary BLE stack). Each connection will receive it's own
* numeric 'handle' starting from 0 to BLE_MAX_CONN-1, depending on the order
* of connection(s).
*
* - E.g If our Central board connects to a mobile phone first (running as a peripheral),
* then afterwards connects to another Bluefruit board running in peripheral mode, then
* the connection handle of mobile phone is 0, and the handle for the Bluefruit
* board is 1, and so on.
*/
/* LED PATTERNS
* ------------
* LED_RED - Blinks pattern changes based on the number of connections.
* LED_BLUE - Blinks constantly when scanning
*/
#include <bluefruit.h>
// Struct containing peripheral info
typedef struct
{
char name[32];
uint16_t conn_handle;
// Each prph need its own bleuart client service
BLEClientUart bleuart;
} prph_info_t;
/* Peripheral info array (one per peripheral device)
*
* There are 'BLE_CENTRAL_MAX_CONN' central connections, but the
* the connection handle can be numerically larger (for example if
* the peripheral role is also used, such as connecting to a mobile
* device). As such, we need to convert connection handles <-> the array
* index where appropriate to prevent out of array accesses.
*
* Note: One can simply declares the array with BLE_MAX_CONN and use connection
* handle as index directly with the expense of SRAM.
*/
prph_info_t prphs[BLE_CENTRAL_MAX_CONN];
// Software Timer for blinking the RED LED
SoftwareTimer blinkTimer;
uint8_t connection_num = 0; // for blink pattern
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
// Initialize blinkTimer for 100 ms and start it
blinkTimer.begin(100, blink_timer_callback);
blinkTimer.start();
Serial.println("Bluefruit52 Central Multi BLEUART Example");
Serial.println("-----------------------------------------\n");
// Initialize Bluefruit with max concurrent connections as Peripheral = 1, Central = 1
// SRAM usage required by SoftDevice will increase with number of connections
Bluefruit.begin(0, 4);
// Set Name
Bluefruit.setName("Bluefruit52 Central");
// Init peripheral pool
for (uint8_t idx=0; idx<BLE_CENTRAL_MAX_CONN; idx++)
{
// Invalid all connection handle
prphs[idx].conn_handle = BLE_CONN_HANDLE_INVALID;
// All of BLE Central Uart Serivce
prphs[idx].bleuart.begin();
prphs[idx].bleuart.setRxCallback(bleuart_rx_callback);
}
// Callbacks for Central
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Filter only accept bleuart service in advertising
* - Don't use active scan (used to retrieve the optional scan response adv packet)
* - Start(0) = will scan forever since no timeout is given
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in units of 0.625 ms
Bluefruit.Scanner.filterUuid(BLEUART_UUID_SERVICE);
Bluefruit.Scanner.useActiveScan(false); // Don't request scan response data
Bluefruit.Scanner.start(0); // 0 = Don't stop scanning after n seconds
}
/**
* Callback invoked when scanner picks up an advertising packet
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Since we configure the scanner with filterUuid()
// Scan callback only invoked for device with bleuart service advertised
// Connect to the device with bleuart service in advertising packet
Bluefruit.Central.connect(report);
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
// Find an available ID to use
int id = findConnHandle(BLE_CONN_HANDLE_INVALID);
// Eeek: Exceeded the number of connections !!!
if ( id < 0 ) return;
prph_info_t* peer = &prphs[id];
peer->conn_handle = conn_handle;
Bluefruit.Gap.getPeerName(conn_handle, peer->name, 32);
Serial.print("Connected to ");
Serial.println(peer->name);
Serial.print("Discovering BLE UART service ... ");
if ( peer->bleuart.discover(conn_handle) )
{
Serial.println("Found it");
Serial.println("Enabling TXD characteristic's CCCD notify bit");
peer->bleuart.enableTXD();
Serial.println("Continue scanning for more peripherals");
Bluefruit.Scanner.start(0);
Serial.println("Enter some text in the Serial Monitor to send it to all connected peripherals:");
} else
{
Serial.println("Found ... NOTHING!");
// disconect since we couldn't find bleuart service
Bluefruit.Central.disconnect(conn_handle);
}
connection_num++;
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
* https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/cores/nRF5/nordic/softdevice/s140_nrf52_6.1.1_API/include/ble_hci.h
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
connection_num--;
// Mark the ID as invalid
int id = findConnHandle(conn_handle);
// Non-existant connection, something went wrong, DBG !!!
if ( id < 0 ) return;
// Mark conn handle as invalid
prphs[id].conn_handle = BLE_CONN_HANDLE_INVALID;
Serial.print(prphs[id].name);
Serial.println(" disconnected!");
}
/**
* Callback invoked when BLE UART data is received
* @param uart_svc Reference object to the service where the data
* arrived.
*/
void bleuart_rx_callback(BLEClientUart& uart_svc)
{
// uart_svc is prphs[conn_handle].bleuart
uint16_t conn_handle = uart_svc.connHandle();
int id = findConnHandle(conn_handle);
prph_info_t* peer = &prphs[id];
// Print sender's name
Serial.printf("[From %s]: ", peer->name);
// Read then forward to all peripherals
while ( uart_svc.available() )
{
// default MTU with an extra byte for string terminator
char buf[20+1] = { 0 };
if ( uart_svc.read(buf,sizeof(buf)-1) )
{
Serial.println(buf);
sendAll(buf);
}
}
}
/**
* Helper function to send a string to all connected peripherals
*/
void sendAll(const char* str)
{
Serial.print("[Send to All]: ");
Serial.println(str);
for(uint8_t id=0; id < BLE_CENTRAL_MAX_CONN; id++)
{
prph_info_t* peer = &prphs[id];
if ( peer->bleuart.discovered() )
{
peer->bleuart.print(str);
}
}
}
void loop()
{
// First check if we are connected to any peripherals
if ( Bluefruit.Central.connected() )
{
// default MTU with an extra byte for string terminator
char buf[20+1] = { 0 };
// Read from HW Serial (normally USB Serial) and send to all peripherals
if ( Serial.readBytes(buf, sizeof(buf)-1) )
{
sendAll(buf);
}
}
}
/**
* Find the connection handle in the peripheral array
* @param conn_handle Connection handle
* @return array index if found, otherwise -1
*/
int findConnHandle(uint16_t conn_handle)
{
for(int id=0; id<BLE_CENTRAL_MAX_CONN; id++)
{
if (conn_handle == prphs[id].conn_handle)
{
return id;
}
}
return -1;
}
/**
* Software Timer callback is invoked via a built-in FreeRTOS thread with
* minimal stack size. Therefore it should be as simple as possible. If
* a periodically heavy task is needed, please use Scheduler.startLoop() to
* create a dedicated task for it.
*
* More information http://www.freertos.org/RTOS-software-timer.html
*/
void blink_timer_callback(TimerHandle_t xTimerID)
{
(void) xTimerID;
// Period of sequence is 10 times (1 second).
// RED LED will toggle first 2*n times (on/off) and remain off for the rest of period
// Where n = number of connection
static uint8_t count = 0;
if ( count < 2*connection_num ) digitalToggle(LED_RED);
if ( count % 2 && digitalRead(LED_RED)) digitalWrite(LED_RED, LOW); // issue #98
count++;
if (count >= 10) count = 0;
}
|