Kenji Arai
/
BLE_Uart_Client
BLE Client UART function
Embed:
(wiki syntax)
Show/hide line numbers
main.cpp
00001 /* 00002 * ------- BLE Central/Client UART function ----------------------------------- 00003 * communicate with BLE_UART_Server program 00004 * --- Tested on Switch Science mbed TY51822r3 --- 00005 * 00006 * Modified by Kenji Arai 00007 * http://www.page.sannet.ne.jp/kenjia/index.html 00008 * https://os.mbed.com/users/kenjiArai/ 00009 * 00010 * Started: April 8th, 2016 00011 * Revised: June 13th, 2016 00012 * Revised: Feburary 10th, 2018 Not set mac addr but use device name 00013 * Revised: Feburary 11th, 2018 use mbed-os5.7.4 with CircularBuffer 00014 * 00015 * Original program (see original.cpp file): 00016 * S130 potential unstability case [closed] by Fabien Comte 00017 * https://devzone.nordicsemi.com/question/49705/ 00018 * s130-potential-unstability-case/ 00019 * GitHub Q&A by Fabien COMTE 00020 * https://github.com/ARMmbed/ble/issues/69 00021 * Reference program: 00022 * BLE_Central_test by noboru koshinaka 00023 * https://os.mbed.com/users/noboruk/code/BLE_Central_test/ 00024 * Tested Server Device: 00025 * BLE_Uart_Server 00026 * https://os.mbed.com/users/kenjiArai/code/BLE_Uart_Server/ 00027 */ 00028 00029 // Include -------------------------------------------------------------------- 00030 #include "mbed.h" 00031 #include "BLE.h" 00032 #include "DiscoveredCharacteristic.h" 00033 #include "DiscoveredService.h" 00034 #include "UARTService.h" 00035 #include "CircularBuffer.h" 00036 00037 // Definition ----------------------------------------------------------------- 00038 //#define USE_MAC // if you use mac address, please define it 00039 00040 #define NUM_ONCE 20 00041 #define BFSIZE (NUM_ONCE+4) 00042 00043 //#define USE_DEBUG_MODE 00044 #ifdef USE_DEBUG_MODE 00045 #define DBG(...) { pc.printf(__VA_ARGS__); } 00046 #else 00047 #define DBG(...) 00048 #endif 00049 00050 #define SOFT_DEVICE_FATHER_HANDLE 3 00051 00052 // Object --------------------------------------------------------------------- 00053 BLE& ble_uart = BLE::Instance(); 00054 DigitalOut alivenessLED(LED1, 1); 00055 DigitalOut connectedLED(D10, 0); 00056 Serial pc(USBTX, USBRX, 115200); 00057 //Serial pc(P0_3, P0_1, 115200); // for another board 00058 Ticker ticker; 00059 CircularBuffer<char, 1536> ser_bf; 00060 Thread tsk; 00061 00062 // ROM / Constant data -------------------------------------------------------- 00063 #ifdef USE_MAC 00064 #warning "You need to modify below value based on your board." 00065 const Gap::Address_t mac_board_0 = {0x50, 0x2b, 0xea, 0x14, 0x95, 0xd2}; 00066 const Gap::Address_t mac_board_1 = {0x59, 0x2c, 0xa8, 0x0e, 0xe2, 0xef}; 00067 const Gap::Address_t mac_board_2 = {0x0f, 0x72, 0xbf, 0x43, 0xbc, 0xd0}; 00068 const Gap::Address_t mac_board_3 = {0x83, 0xc9, 0x1a, 0x90, 0xdf, 0xd6}; 00069 const Gap::Address_t mac_board_4 = {0x43, 0xa4, 0x36, 0x11, 0x5b, 0xeb}; 00070 #else 00071 const char PEER_NAME[] = "UART_PJL"; 00072 #endif 00073 00074 // RAM ------------------------------------------------------------------------ 00075 Gap::Handle_t connectionHandle = 0xFFFF; 00076 DiscoveredCharacteristic uartTXCharacteristic; 00077 DiscoveredCharacteristic uartRXCharacteristic; 00078 bool foundUartRXCharacteristic = false; 00079 bool connected2server = false; 00080 bool connection_tx = false; 00081 bool connection_rx = false; 00082 UARTService * uartServicePtr = NULL; 00083 Gap::Address_t my_mac; 00084 int my_board_index = -1; 00085 bool received_uart_dat = false; 00086 int8_t uart_buffer[BFSIZE]; 00087 uint8_t uart_bf_len; 00088 volatile bool rx_isr_busy = false; 00089 00090 // Function prototypes -------------------------------------------------------- 00091 // BLE 00092 void advertisementCallback(const Gap::AdvertisementCallbackParams_t *); 00093 void serviceDiscoveryCallback(const DiscoveredService *); 00094 void characteristicDiscoveryCallback(const DiscoveredCharacteristic *); 00095 void discoveryTerminationCallback(Gap::Handle_t ); 00096 void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *); 00097 void connectionCallback(const Gap::ConnectionCallbackParams_t *); 00098 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *); 00099 // Interrupt related 00100 void periodicCallback(void); 00101 void serialRxCallback(void); 00102 // serial receiving 00103 void pc_ser_rx(void); 00104 void preparation_sending_data(void); 00105 // Pre-check 00106 bool mac_equals(const Gap::Address_t, const Gap::Address_t); 00107 int get_board_index(const Gap::Address_t); 00108 void adjust_line(uint8_t *); 00109 00110 //------------------------------------------------------------------------------ 00111 // Control Program 00112 //------------------------------------------------------------------------------ 00113 int main(void) 00114 { 00115 alivenessLED = 0; 00116 pc.attach(&serialRxCallback, Serial::RxIrq); 00117 ticker.attach(periodicCallback, 1); 00118 tsk.start(pc_ser_rx); 00119 // clear terminal output 00120 for (int k = 0; k < 3; k++) { 00121 pc.printf("\r\n"); 00122 } 00123 // opening message 00124 pc.printf("UART Communication / Client(Central) side\r\n"); 00125 pc.printf(" need Server module (run BLE_Uart_Server program)\r\n"); 00126 // Mixed role ************************************************************** 00127 ble_uart.init(); 00128 ble_uart.gap().onConnection(connectionCallback); 00129 ble_uart.gap().onDisconnection(disconnectionCallback); 00130 // Client(Central) role **************************************************** 00131 ble_uart.gattClient().onHVX(onReceivedDataFromDeviceCallback); 00132 ble_uart.gap().setScanParams(500, 450); 00133 ble_uart.gap().startScan(advertisementCallback); 00134 while(true) { 00135 // allow notifications from Server(Peripheral) 00136 if (foundUartRXCharacteristic && 00137 !ble_uart.gattClient().isServiceDiscoveryActive()) { 00138 // need to do the following only once 00139 foundUartRXCharacteristic = false; 00140 uint16_t value = BLE_HVX_NOTIFICATION; 00141 ble_uart.gattClient().write( 00142 GattClient::GATT_OP_WRITE_REQ, 00143 connectionHandle, 00144 uartRXCharacteristic.getValueHandle() + 1, 00145 sizeof(uint16_t), 00146 reinterpret_cast<const uint8_t *>(&value) 00147 ); 00148 } 00149 if (received_uart_dat == true) { 00150 received_uart_dat = false; 00151 for(int i = 0; i < uart_bf_len; i++) { 00152 //pc.printf("%c", uart_buffer[i]); 00153 pc.putc(uart_buffer[i]); 00154 } 00155 } 00156 ble_uart.waitForEvent(); 00157 } 00158 } 00159 00160 void periodicCallback(void) 00161 { 00162 // Do blinky on alivenessLED to indicate system aliveness 00163 alivenessLED = !alivenessLED; 00164 if (connected2server) { 00165 connectedLED = 1; 00166 } else { 00167 connectedLED = 0; 00168 } 00169 if (rx_isr_busy == true) { 00170 rx_isr_busy = false; 00171 } else { 00172 tsk.signal_set(0x01); 00173 } 00174 } 00175 00176 void serialRxCallback() 00177 { 00178 ser_bf.push(pc.getc()); 00179 rx_isr_busy = true; 00180 tsk.signal_set(0x01); 00181 } 00182 00183 void pc_ser_rx() 00184 { 00185 static uint8_t linebf_irq[BFSIZE]; 00186 static volatile uint8_t linebf_irq_len = 0; 00187 00188 while(true) { 00189 Thread::signal_wait(0x01); 00190 if (ser_bf.empty()) { 00191 if (linebf_irq_len != 0) { 00192 linebf_irq[linebf_irq_len] = 0; 00193 adjust_line(linebf_irq); 00194 linebf_irq_len = 0; 00195 uartTXCharacteristic.write(NUM_ONCE, linebf_irq); 00196 } 00197 } 00198 while(!ser_bf.empty()) { 00199 char c; 00200 ser_bf.pop(c); 00201 if (c == '\b') { 00202 linebf_irq_len--; 00203 pc.putc(c); 00204 pc.putc(' '); 00205 pc.putc(c); 00206 } else if ((c >= ' ') || (c == '\r') || (c == '\n')) { 00207 bool overflow = false; 00208 if ((c == '\r') || (c == '\n')) { 00209 if (linebf_irq_len == NUM_ONCE - 1) { // remain only 1 buffer 00210 overflow = true; 00211 linebf_irq[linebf_irq_len++] = '\r'; 00212 pc.putc('\r'); 00213 } else { 00214 overflow = false; 00215 linebf_irq[linebf_irq_len++] = '\r'; 00216 linebf_irq[linebf_irq_len++] = '\n'; 00217 pc.printf("\r\n"); 00218 } 00219 } else { 00220 linebf_irq[linebf_irq_len++] = c; 00221 pc.putc(c); 00222 } 00223 if (linebf_irq_len >= NUM_ONCE ) { 00224 linebf_irq[linebf_irq_len] = 0; 00225 uartTXCharacteristic.write(linebf_irq_len, linebf_irq); 00226 linebf_irq_len = 0; 00227 if (overflow == true) { 00228 overflow = false; 00229 linebf_irq[linebf_irq_len++] = '\n'; 00230 pc.putc('\n'); 00231 } 00232 } 00233 } 00234 } 00235 } 00236 } 00237 00238 void adjust_line(uint8_t *bf) 00239 { 00240 uint8_t i, c; 00241 00242 for (i = 0; i <NUM_ONCE; bf++, i++) { 00243 c = *bf; 00244 if (c == 0) { 00245 break; 00246 } 00247 } 00248 for (; i < NUM_ONCE; bf++, i++) { 00249 *bf = 0x11; 00250 } 00251 *(bf + 1) = 0; 00252 } 00253 00254 void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params) 00255 { 00256 DBG( 00257 "received HVX callback for handle %u; type %s\r\r\n", 00258 params->handle, 00259 (params->type == BLE_HVX_NOTIFICATION) ? "notification" : "indication" 00260 ); 00261 if (params->type == BLE_HVX_NOTIFICATION) { 00262 if ((params->handle 00263 == uartRXCharacteristic.getValueHandle()) && (params->len > 0)) { 00264 uart_bf_len = params->len; 00265 strcpy((char *)uart_buffer, (char *)params->data); 00266 received_uart_dat = true; 00267 } 00268 } 00269 } 00270 00271 #ifdef USE_MAC 00272 00273 bool mac_equals(const Gap::Address_t mac_1, const Gap::Address_t mac_2) 00274 { 00275 DBG("Address: "); 00276 for (int i = 0; i < 6; i++) { 00277 DBG("0x%02x ", mac_1[i]); 00278 } 00279 DBG("\r\n"); 00280 for (int i = 0; i < 6; i++) { 00281 if (mac_1[i] != mac_2[i]) { 00282 DBG("0x%02x != 0x%02x at %d\r\n", mac_1[i], mac_2[i], i); 00283 return false; 00284 } else { 00285 DBG("0x%02x == 0x%02x at %d\r\n", mac_1[i], mac_2[i], i); 00286 } 00287 } 00288 return true; 00289 } 00290 00291 int get_board_index(const Gap::Address_t mac) 00292 { 00293 if (mac_equals(mac, mac_board_0)) { 00294 return 0; 00295 } 00296 if (mac_equals(mac, mac_board_1)) { 00297 return 1; 00298 } 00299 if (mac_equals(mac, mac_board_2)) { 00300 return 2; 00301 } 00302 if (mac_equals(mac, mac_board_3)) { 00303 return 3; 00304 } 00305 if (mac_equals(mac, mac_board_4)) { 00306 return 4; 00307 } 00308 return -1; 00309 } 00310 00311 // Client(Central) role ******************************************************** 00312 void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) 00313 { 00314 // connections 00315 int peer_board_index = get_board_index(params->peerAddr); 00316 if (peer_board_index != -1) { 00317 pc.printf(""); 00318 pc.printf( 00319 "adv peerAddr [%02x %02x %02x %02x %02x %02x]\r\n", 00320 params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], 00321 params->peerAddr[2], params->peerAddr[1], params->peerAddr[0] 00322 ); 00323 pc.printf( 00324 "rssi=%+4d, isScanResponse %u, AdvertisementType %u\r\n", 00325 params->rssi, params->isScanResponse, params->type 00326 ); 00327 ble_uart.gap().connect( 00328 params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL); 00329 } 00330 } 00331 00332 #else 00333 00334 // Client(Central) role ******************************************************** 00335 void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) 00336 { 00337 bool name_match = false; 00338 00339 // parse the advertising payload, looking for data type COMPLETE_LOCAL_NAME 00340 // The advertising payload is a collection of key/value records where 00341 // byte 0: length of the record excluding this byte 00342 // byte 1: The key, it is the type of the data 00343 // byte [2..N] The value. N is equal to byte0 - 1 00344 00345 for( uint8_t i = 0; i < params->advertisingDataLen; ++i) { 00346 const uint8_t record_length = params->advertisingData[i]; 00347 if (record_length == 0) { 00348 continue; 00349 } 00350 const uint8_t type = params->advertisingData[i + 1]; 00351 const uint8_t* value = params->advertisingData + i + 2; 00352 const uint8_t value_length = record_length - 1; 00353 00354 if(type == GapAdvertisingData::COMPLETE_LOCAL_NAME) { 00355 if ((value_length == sizeof(PEER_NAME)) 00356 && (memcmp(value, PEER_NAME, value_length) == 0)) { 00357 pc.printf( 00358 "\r\nadv peerAddr[%02x %02x %02x %02x %02x %02x] rssi %d, ", 00359 params->peerAddr[5], params->peerAddr[4], 00360 params->peerAddr[3], params->peerAddr[2], 00361 params->peerAddr[1], params->peerAddr[0], 00362 params->rssi 00363 ); 00364 pc.printf( 00365 "isScanResponse %u, AdvertisementType %u\r\n", 00366 params->isScanResponse, params->type 00367 ); 00368 name_match = true; 00369 break; 00370 } 00371 } 00372 i += record_length; 00373 } 00374 if( name_match != true ) { 00375 return; 00376 } 00377 00378 pc.printf("Found device name : %s\r\n",PEER_NAME); 00379 // connections 00380 ble_uart.gap().connect(params->peerAddr, 00381 Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL); 00382 } 00383 00384 #endif 00385 00386 void serviceDiscoveryCallback(const DiscoveredService *service) 00387 { 00388 DBG("service found...\r\n"); 00389 if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) { 00390 DBG( 00391 "Service UUID-%x attrs[%u %u]\r\n", 00392 service->getUUID().getShortUUID(), 00393 service->getStartHandle(), 00394 service->getEndHandle() 00395 ); 00396 } else { 00397 DBG("Service UUID-"); 00398 const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID(); 00399 for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) { 00400 DBG("%02x", longUUIDBytes[i]); 00401 } 00402 DBG(" attrs[%u %u]\r\n", 00403 service->getStartHandle(), service->getEndHandle()); 00404 } 00405 } 00406 00407 void characteristicDiscoveryCallback( 00408 const DiscoveredCharacteristic *characteristicP) 00409 { 00410 DBG( 00411 " C UUID-%x valueAttr[%u] props[%x]\r\n", 00412 characteristicP->getUUID().getShortUUID(), 00413 characteristicP->getValueHandle(), 00414 (uint8_t)characteristicP->getProperties().broadcast() 00415 ); 00416 if (characteristicP->getUUID().getShortUUID() 00417 == UARTServiceTXCharacteristicShortUUID) { 00418 DBG("Sevice TX 0x%04x\r\n", UARTServiceTXCharacteristicShortUUID); 00419 uartTXCharacteristic = *characteristicP; 00420 connection_tx = true; 00421 } else if (characteristicP->getUUID().getShortUUID() 00422 == UARTServiceRXCharacteristicShortUUID) { 00423 DBG("Sevice RX 0x%04x\r\n", UARTServiceRXCharacteristicShortUUID); 00424 uartRXCharacteristic = *characteristicP; 00425 foundUartRXCharacteristic = true; 00426 connection_rx = true; 00427 } 00428 } 00429 00430 void discoveryTerminationCallback(Gap::Handle_t connectionHandle) 00431 { 00432 DBG("terminated SD for handle=%u\r\n", connectionHandle); 00433 } 00434 00435 // Mixed role ****************************************************************** 00436 void connectionCallback(const Gap::ConnectionCallbackParams_t *params) 00437 { 00438 if (params->role == Gap::CENTRAL) { 00439 pc.printf("connected as Client(Central) (handle = %d)\r\n\r", 00440 params->handle); 00441 connected2server = true; 00442 connectionHandle = params->handle; 00443 ble_uart.gattClient().onServiceDiscoveryTermination( 00444 discoveryTerminationCallback); 00445 ble_uart.gattClient().launchServiceDiscovery( 00446 params->handle, 00447 serviceDiscoveryCallback, 00448 characteristicDiscoveryCallback 00449 ); 00450 } 00451 pc.printf( 00452 "Client(Central/Myself) %02x:%02x:%02x:%02x:%02x:%02x\r\n", 00453 params->ownAddr[5], params->ownAddr[4], params->ownAddr[3], 00454 params->ownAddr[2], params->ownAddr[1], params->ownAddr[0] 00455 ); 00456 pc.printf( 00457 "Connected Server(Peripheral) %02x:%02x:%02x:%02x:%02x:%02x\r\n", 00458 params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], 00459 params->peerAddr[2], params->peerAddr[1], params->peerAddr[0] 00460 ); 00461 } 00462 00463 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) 00464 { 00465 DBG("handle = %d ", params->handle); 00466 pc.printf(" -> disconnected\r\n", params->handle); 00467 connected2server = false; 00468 // connection_1st = false; 00469 connection_tx = false; 00470 connection_rx = false; 00471 if (params->handle == SOFT_DEVICE_FATHER_HANDLE) { 00472 ble_uart.startAdvertising(); // restart advertising 00473 } else { 00474 ble_uart.gap().startScan(advertisementCallback);// restart scan 00475 } 00476 }
Generated on Fri Jul 15 2022 16:37:01 by 1.7.2