BLE Client UART function
This is a BLE Client (Central) program for nRF51.
You can communicate with mbed BLE using "BLE_Uart_Server" program as follows.
/users/kenjiArai/code/BLE_Uart_Server/
Please refer following my notebook.
/users/kenjiArai/notebook/ble-client-and-peripheral-using-switch-sience-ty51/#
main.cpp
- Committer:
- kenjiArai
- Date:
- 2017-10-22
- Revision:
- 3:9236f8e65c80
- Parent:
- 2:6fb0b87b041d
- Child:
- 4:342b665fb826
File content as of revision 3:9236f8e65c80:
/* * ------- BLE Central/Client UART function ----------------------------------- * communicate with BLE_UART_Server program * --- Tested on Switch Science mbed TY51822r3 --- * * Modified by Kenji Arai * http://www.page.sannet.ne.jp/kenjia/index.html * http://mbed.org/users/kenjiArai/ * * Started: April 8th, 2016 * Revised: June 13th, 2016 * Revised: October 22nd, 2017 Run on mbed-OS-5.6.2 * * Original program (see original.cpp file): * S130 potential unstability case [closed] by Fabien Comte * https://devzone.nordicsemi.com/question/49705/ * s130-potential-unstability-case/ * GitHub Q&A by Fabien COMTE * https://github.com/ARMmbed/ble/issues/69 * Tested Server Device: * BLE_Uart_Server * https://developer.mbed.org/users/kenjiArai/code/BLE_Uart_Server/ */ // Include -------------------------------------------------------------------- #include "mbed.h" #include "BLE.h" #include "UARTService.h" #include "ble/DiscoveredCharacteristic.h" #include "ble/DiscoveredService.h" #include "RingBuffer.h" // Definition ----------------------------------------------------------------- #define NUM_ONCE 20 #define BFSIZE (NUM_ONCE+4) //#define USE_DEBUG_MODE #ifdef USE_DEBUG_MODE #define DBG(...) { pc.printf(__VA_ARGS__); } #else #define DBG(...) #endif #define SOFT_DEVICE_FATHER_HANDLE 3 // Object --------------------------------------------------------------------- BLE ble; DigitalOut alivenessLED(LED1, 1); DigitalOut connectedLED(D10, 0); Serial pc(USBTX, USBRX, 115200); //Serial pc(P0_3, P0_1, 115200); // for another board Ticker ticker; RingBuffer ser_bf(1536); Thread tsk; // ROM / Constant data -------------------------------------------------------- #warning "You need to modify below value based on your board." const Gap::Address_t mac_board_0 = {0x50, 0x2b, 0xea, 0x14, 0x95, 0xd2}; const Gap::Address_t mac_board_1 = {0x59, 0x2c, 0xa8, 0x0e, 0xe2, 0xef}; const Gap::Address_t mac_board_2 = {0x0f, 0x72, 0xbf, 0x43, 0xbc, 0xd0}; const Gap::Address_t mac_board_3 = {0x83, 0xc9, 0x1a, 0x90, 0xdf, 0xd6}; const Gap::Address_t mac_board_4 = {0x43, 0xa4, 0x36, 0x11, 0x5b, 0xeb}; // RAM ------------------------------------------------------------------------ Gap::Handle_t connectionHandle = 0xFFFF; DiscoveredCharacteristic uartTXCharacteristic; DiscoveredCharacteristic uartRXCharacteristic; bool foundUartRXCharacteristic = false; bool connected2server = false; bool connection_tx = false; bool connection_rx = false; UARTService * uartServicePtr = NULL; Gap::Address_t my_mac; int my_board_index = -1; bool received_uart_dat = false; int8_t uart_buffer[BFSIZE]; uint8_t uart_bf_len; volatile bool rx_isr_busy = false; // Function prototypes -------------------------------------------------------- // BLE void advertisementCallback(const Gap::AdvertisementCallbackParams_t *); void serviceDiscoveryCallback(const DiscoveredService *); void characteristicDiscoveryCallback(const DiscoveredCharacteristic *); void discoveryTerminationCallback(Gap::Handle_t ); void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *); void connectionCallback(const Gap::ConnectionCallbackParams_t *); void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *); // Interrupt related void periodicCallback(void); void serialRxCallback(void); // serial receiving void pc_ser_rx(void); void preparation_sending_data(void); // Pre-check bool mac_equals(const Gap::Address_t, const Gap::Address_t); int get_board_index(const Gap::Address_t); void adjust_line(uint8_t *); //------------------------------------------------------------------------------ // Control Program //------------------------------------------------------------------------------ int main(void) { alivenessLED = 0; pc.attach(&serialRxCallback, Serial::RxIrq); ticker.attach(periodicCallback, 1); tsk.start(pc_ser_rx); // clear terminal output for (int k = 0; k < 5; k++) { pc.printf("\r\n");} // opening message pc.printf("UART Communication / Client(Central) side\r\n"); pc.printf(" need Sever module (run BLE_Uart_Sever program)\r\n"); // Mixed role ************************************************************** ble.init(); ble.gap().onConnection(connectionCallback); ble.gap().onDisconnection(disconnectionCallback); // Client(Central) role **************************************************** ble.gattClient().onHVX(onReceivedDataFromDeviceCallback); ble.gap().setScanParams(500, 450); ble.gap().startScan(advertisementCallback); while(true) { // allow notifications from Server(Peripheral) if (foundUartRXCharacteristic && !ble.gattClient().isServiceDiscoveryActive()) { // need to do the following only once foundUartRXCharacteristic = false; uint16_t value = BLE_HVX_NOTIFICATION; ble.gattClient().write( GattClient::GATT_OP_WRITE_REQ, connectionHandle, uartRXCharacteristic.getValueHandle() + 1, sizeof(uint16_t), reinterpret_cast<const uint8_t *>(&value) ); } if (received_uart_dat == true){ received_uart_dat = false; for(int i = 0; i < uart_bf_len; i++){ //pc.printf("%c", uart_buffer[i]); pc.putc(uart_buffer[i]); } } ble.waitForEvent(); } } void periodicCallback(void) { // Do blinky on alivenessLED to indicate system aliveness alivenessLED = !alivenessLED; if (connected2server){ connectedLED = 1; } else { connectedLED = 0; } if (rx_isr_busy == true){ rx_isr_busy = false; } else { tsk.signal_set(0x01); } } void serialRxCallback() { ser_bf.save(pc.getc()); rx_isr_busy = true; tsk.signal_set(0x01); } void pc_ser_rx() { static uint8_t linebf_irq[BFSIZE]; static volatile uint8_t linebf_irq_len = 0; while(true){ Thread::signal_wait(0x01); if (ser_bf.check() == 0){ if (linebf_irq_len != 0){ linebf_irq[linebf_irq_len] = 0; adjust_line(linebf_irq); linebf_irq_len = 0; uartTXCharacteristic.write(NUM_ONCE, linebf_irq); } } while(ser_bf.check() != 0){ char c = ser_bf.read(); if (c == '\b'){ linebf_irq_len--; pc.putc(c); pc.putc(' '); pc.putc(c); } else if ((c >= ' ') || (c == '\r') || (c == '\n')){ bool overflow = false; if ((c == '\r') || (c == '\n')) { if (linebf_irq_len == NUM_ONCE - 1){// remain only 1 buffer overflow = true; linebf_irq[linebf_irq_len++] = '\r'; pc.putc('\r'); } else { overflow = false; linebf_irq[linebf_irq_len++] = '\r'; linebf_irq[linebf_irq_len++] = '\n'; pc.printf("\r\n"); } } else { linebf_irq[linebf_irq_len++] = c; pc.putc(c); } if (linebf_irq_len >= NUM_ONCE ){ linebf_irq[linebf_irq_len] = 0; uartTXCharacteristic.write(linebf_irq_len, linebf_irq); linebf_irq_len = 0; if (overflow == true){ overflow = false; linebf_irq[linebf_irq_len++] = '\n'; pc.putc('\n'); } } } } } } void adjust_line(uint8_t *bf) { uint8_t i, c; for (i = 0; i <NUM_ONCE; bf++, i++){ c = *bf; if (c == 0){ break;} } for (; i < NUM_ONCE; bf++, i++){ *bf = 0x11;} *(bf + 1) = 0; } void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params) { DBG( "received HVX callback for handle %u; type %s\r\r\n", params->handle, (params->type == BLE_HVX_NOTIFICATION) ? "notification" : "indication" ); if (params->type == BLE_HVX_NOTIFICATION){ if ((params->handle == uartRXCharacteristic.getValueHandle()) && (params->len > 0)) { uart_bf_len = params->len; strcpy((char *)uart_buffer, (char *)params->data); received_uart_dat = true; } } } bool mac_equals(const Gap::Address_t mac_1, const Gap::Address_t mac_2) { DBG("Address: "); for (int i = 0; i < 6; i++){ DBG("0x%02x ", mac_1[i]); } DBG("\r\n"); for (int i = 0; i < 6; i++){ if (mac_1[i] != mac_2[i]){ DBG("0x%02x != 0x%02x at %d\r\n", mac_1[i], mac_2[i], i); return false; } else { DBG("0x%02x == 0x%02x at %d\r\n", mac_1[i], mac_2[i], i); } } return true; } int get_board_index(const Gap::Address_t mac) { if (mac_equals(mac, mac_board_0)) { return 0;} if (mac_equals(mac, mac_board_1)) { return 1;} if (mac_equals(mac, mac_board_2)) { return 2;} if (mac_equals(mac, mac_board_3)) { return 3;} if (mac_equals(mac, mac_board_4)) { return 4;} return -1; } // Client(Central) role ******************************************************** void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) { // connections int peer_board_index = get_board_index(params->peerAddr); if (peer_board_index != -1){ pc.printf(""); pc.printf( "adv peerAddr [%02x %02x %02x %02x %02x %02x]\r\n", params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0] ); pc.printf( "rssi=%+4d, isScanResponse %u, AdvertisementType %u\r\n", params->rssi, params->isScanResponse, params->type ); ble.gap().connect( params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL); } } void serviceDiscoveryCallback(const DiscoveredService *service) { DBG("service found...\r\n"); if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT){ DBG( "Service UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle() ); } else { DBG("Service UUID-"); const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID(); for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) { DBG("%02x", longUUIDBytes[i]); } DBG(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle()); } } void characteristicDiscoveryCallback( const DiscoveredCharacteristic *characteristicP) { DBG( " C UUID-%x valueAttr[%u] props[%x]\r\n", characteristicP->getUUID().getShortUUID(), characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast() ); if (characteristicP->getUUID().getShortUUID() == UARTServiceTXCharacteristicShortUUID) { DBG("Sevice TX 0x%04x\r\n", UARTServiceTXCharacteristicShortUUID); uartTXCharacteristic = *characteristicP; connection_tx = true; } else if (characteristicP->getUUID().getShortUUID() == UARTServiceRXCharacteristicShortUUID) { DBG("Sevice RX 0x%04x\r\n", UARTServiceRXCharacteristicShortUUID); uartRXCharacteristic = *characteristicP; foundUartRXCharacteristic = true; connection_rx = true; } } void discoveryTerminationCallback(Gap::Handle_t connectionHandle) { DBG("terminated SD for handle=%u\r\n", connectionHandle); } // Mixed role ****************************************************************** void connectionCallback(const Gap::ConnectionCallbackParams_t *params) { if (params->role == Gap::CENTRAL) { pc.printf("connected as Client(Central) (handle = %d)\r\n\r", params->handle); connected2server = true; connectionHandle = params->handle; ble.gattClient().onServiceDiscoveryTermination( discoveryTerminationCallback); ble.gattClient().launchServiceDiscovery( params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback ); } pc.printf( "Client(Central/Myself) %02x:%02x:%02x:%02x:%02x:%02x\r\n", params->ownAddr[5], params->ownAddr[4], params->ownAddr[3], params->ownAddr[2], params->ownAddr[1], params->ownAddr[0] ); pc.printf( "Connected Sever(peripheral) %02x:%02x:%02x:%02x:%02x:%02x\r\n", params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0] ); } void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) { DBG("handle = %d ", params->handle); pc.printf(" -> disconnected\r\n", params->handle); connected2server = false; // connection_1st = false; connection_tx = false; connection_rx = false; if (params->handle == SOFT_DEVICE_FATHER_HANDLE) { ble.startAdvertising(); // restart advertising } else { ble.gap().startScan(advertisementCallback); // restart scan } }