BLE Client UART function

Dependencies:   RingBuffer

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

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 }