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/#
Diff: main.cpp
- Revision:
- 0:0ef6455cbb4d
- Child:
- 1:f68a5e55a60e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Sat Apr 16 07:32:21 2016 +0000 @@ -0,0 +1,422 @@ +/* + * ------- BLE Client UART function -------------------------------- + * --- 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: April 16th, 2016 + * + * 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 Peripheral Device: + * BLE_Uart_Peripheral + * https://developer.mbed.org/users/kenjiArai/code/BLE_Uart_Peripheral/ + * + */ + +// Include --------------------------------------------------------------------------------------- +#include "mbed.h" +#include "BLE.h" +#include "UARTService.h" +#include "ble/DiscoveredCharacteristic.h" +#include "ble/DiscoveredService.h" + +// Definition ------------------------------------------------------------------------------------ +#define NEED_CONSOLE_OUTPUT 0 + +#if NEED_CONSOLE_OUTPUT +#define DEBUG(...) { printf(__VA_ARGS__); } +#else +#define DEBUG(...) +#endif + +#define SOFT_DEVICE_FATHER_HANDLE 3 +#define BOARDS_COUNT 3 + +// Object ---------------------------------------------------------------------------------------- +BLE ble; +DigitalOut alivenessLED(LED1, 1); +DigitalOut connectedLED(D10, 0); +DigitalIn sw(D12, PullUp); +InterruptIn signal_sw(D12); // duplicated!! +Serial pc(USBTX, USBRX); +Ticker ticker; + +// ROM / Constant data --------------------------------------------------------------------------- +#warning "You need to modify below value based on your board." +const Gap::Address_t mac_board_0 = {0x59, 0x2c, 0xa8, 0x0e, 0xe2, 0xef}; +const Gap::Address_t mac_board_1 = {0x50, 0x2b, 0xea, 0x14, 0x95, 0xd2}; +const Gap::Address_t mac_board_2 = {0x30, 0x74, 0x6d, 0xbd, 0x83, 0xf4}; +#warning "You need to confirm your device name." +const static char DEVICE_NAME[] = "UART_C"; +static const uint16_t uuid16_list[] = {UARTServiceShortUUID}; + +// RAM ------------------------------------------------------------------------------------------- +Gap::Handle_t connectionHandle = 0xFFFF; +DiscoveredCharacteristic uartTXCharacteristic; +DiscoveredCharacteristic uartRXCharacteristic; +bool foundUartRXCharacteristic = false; +bool connected2peripheral = false; +UARTService * uartServicePtr = NULL; +Gap::Address_t my_mac; +int my_board_index = -1; +bool recieved_uart_dat0 = false; +int8_t uart_buffer0[24]; +uint8_t uart_bf0_len; +bool recieved_uart_dat1 = false; +int8_t uart_buffer1[24]; +uint8_t uart_bf1_len; +bool line_input = false; +uint8_t linebuf_irq[24]; +int linebf_irq_len = 0; +uint8_t linebuf[24]; +int linebf_len = 0; + +// Function prototypes --------------------------------------------------------------------------- +// BLE +void onReceivedDataFromCentralCallback(const GattWriteCallbackParams *params); +void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params); +void serviceDiscoveryCallback(const DiscoveredService *service); +void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP); +void discoveryTerminationCallback(Gap::Handle_t connectionHandle); +void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params); +void connectionCallback(const Gap::ConnectionCallbackParams_t *params); +void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params); +// Interrupt related +void periodicCallback(void); +void serialRxCallback(void); +// Pre-check +bool mac_equals(const Gap::Address_t mac_1, const Gap::Address_t mac_2); +int get_board_index(const Gap::Address_t mac); +void adjust_line(uint8_t *bf); + +//------------------------------------------------------------------------------------------------- +// Control Program +//------------------------------------------------------------------------------------------------- +int main(void){ + alivenessLED = 0; + pc.attach(&serialRxCallback, Serial::RxIrq); + ticker.attach(periodicCallback, 1); + for (int k = 0; k < 20; k++) { pc.printf("\r\n");} // clear terminal output + pc.printf("UART Communication / Client side\r\n"); // opening message + pc.printf(" Client(Central) and Peripheral(device)\r\n"); // opening message + // Mixed role ************************************************************* + ble.init(); + Gap::AddressType_t my_mac_type; + ble.gap().getAddress(&my_mac_type, my_mac); + my_board_index = get_board_index(my_mac); + DEBUG( + "me %02x:%02x:%02x:%02x:%02x:%02x (%s)\r\n", + my_mac[5], my_mac[4], my_mac[3], my_mac[2], my_mac[1], my_mac[0], + (my_mac_type == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random" + ); + ble.gap().onConnection(connectionCallback); + ble.gap().onDisconnection(disconnectionCallback); + // Peripheral(Device) role ************************************************ + ble.gattServer().onDataWritten(onReceivedDataFromCentralCallback); + UARTService uartService(ble); + uartServicePtr = &uartService; + // setup advertising + ble.accumulateAdvertisingPayload( + GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE + ); + ble.accumulateAdvertisingPayload( + GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME) + ); + ble.accumulateAdvertisingPayload( + GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, + (uint8_t *)uuid16_list, + sizeof(uuid16_list) + ); // UUID's broadcast in advertising packet (set advertising type) + ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + ble.setAdvertisingInterval(100); + // Client(Central) role *************************************************** + ble.gattClient().onHVX(onReceivedDataFromDeviceCallback); + ble.gap().setScanParams(500, 450); + ble.startAdvertising(); // start advertising and scan + ble.gap().startScan(advertisementCallback); + while(true) { + // allow notifications from Peripheral(device) + if (foundUartRXCharacteristic && !ble.gattClient().isServiceDiscoveryActive()){ + foundUartRXCharacteristic = false; // need to do the following only once + 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 (recieved_uart_dat0 == true){ + recieved_uart_dat0 = false; + for(int i = 0; i < uart_bf0_len; i++){ + pc.printf("%c", uart_buffer0[i]); + } + pc.printf("\r\n"); + } + if (recieved_uart_dat1 == true){ + recieved_uart_dat1 = false; + for(int i = 0; i < uart_bf1_len; i++){ + pc.printf("%c", uart_buffer1[i]); + } + pc.printf("\r\n"); + } + if (line_input == true){ + line_input = false; + // Client to Peripheral(central to device) + adjust_line(linebuf); + int ret = uartTXCharacteristic.write(20, linebuf); + if (ret == BLE_ERROR_NONE){ + DEBUG("\r\ndone (c2p)\r\n"); + } else if (ret == BLE_STACK_BUSY){ + DEBUG("\r\nbusy (c2p)\r\n"); + } else if (ret == BLE_ERROR_OPERATION_NOT_PERMITTED) { + DEBUG("\r\nnot connected (c2d)\r\n"); + } else { + DEBUG("\r\ncode %d (c2d)\r\n", ret); + } + // Peripheral to Client(device to central) + if (ble.gap().getState().connected){ + int ret = ble.gattServer().write( + uartServicePtr->getRXCharacteristicHandle(), + linebuf, + linebf_len + ); + if (ret == BLE_ERROR_NONE){ + DEBUG("\r\ndone (p2c)\r\n"); + } else if (ret == BLE_STACK_BUSY){ + DEBUG("\r\nbusy (p2c)\r\n"); + } else if (ret == BLE_ERROR_OPERATION_NOT_PERMITTED){ + DEBUG("\r\nnot connected (p2c)\r\n"); + } else { + DEBUG("\r\ncode %d (p2c)\r\n", ret); + } + } + } + ble.waitForEvent(); // save power + } +} + +void serialRxCallback(){ + char c = pc.getc(); + if (c == '\r') { + linebuf_irq[linebf_irq_len++] = c; + pc.printf("\r\n"); + linebf_len = linebf_irq_len; + strcpy((char *)linebuf, (char *)linebuf_irq); + linebf_irq_len = 0; + line_input = true; + } else if ((c == '\b') && linebf_irq_len) { + linebf_irq_len--; + pc.putc(c); + pc.putc(' '); + pc.putc(c); + } else if (((uint8_t)c >= ' ') && (linebf_irq_len < 20)) { + linebuf_irq[linebf_irq_len++] = c; + pc.putc(c); + } else if ( c == 0x1f ){ // Control+? + SCB->AIRCR = 0x05fa0004; // System RESET!! + } + linebuf_irq[linebf_irq_len] = 0; +} + +void adjust_line(uint8_t *bf){ +uint8_t i, c; + + for (i = 0; i <20; bf++, i++){ + c = *bf; + if ( (c == '\r') || (c == '\n') || (c == 0)){ + break; + } + } + for (; i < 20; bf++, i++){ + *bf = ' '; + } + *(bf + 1) = 0; +} + +// Peripheral(Device) role **************************************************** +void onReceivedDataFromCentralCallback(const GattWriteCallbackParams *params){ + if (uartServicePtr != NULL){ + if ((params->handle == + uartServicePtr->getTXCharacteristicHandle()) && (params->len >= 1)) + { + uart_bf0_len = params->len; + strcpy((char *)uart_buffer0, (char *)params->data); + recieved_uart_dat0 = true; + } + } +} + +void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params){ + DEBUG( + "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_bf1_len = params->len; + strcpy((char *)uart_buffer1, (char *)params->data); + recieved_uart_dat1 = true; + } + } +} + +void periodicCallback(void){ + alivenessLED = !alivenessLED; // Do blinky on alivenessLED to indicate system aliveness + if (connected2peripheral){ + connectedLED = 1; + } else { + connectedLED = 0; + } +} + +bool mac_equals(const Gap::Address_t mac_1, const Gap::Address_t mac_2){ + DEBUG("Address: "); + for (int i = 0; i < 6; i++){ + DEBUG("0x%02x ", mac_1[i]); + } + DEBUG("\r\n"); + for (int i = 0; i < 6; i++){ + if (mac_1[i] != mac_2[i]){ + DEBUG("0x%02x != 0x%02x at %d\r\n", mac_1[i], mac_2[i], i); + return false; + } else { + DEBUG("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;} + 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("adv peerAddr"); + pc.printf( + "[%02x %02x %02x %02x %02x %02x] rssi %+4d, isScanResponse %u, AdvertisementType %u", + params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], + params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], + params->rssi, + params->isScanResponse, + params->type + ); + pc.printf("\r\n"); + ble.gap().connect(params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL); + } +} + +void serviceDiscoveryCallback(const DiscoveredService *service) { + DEBUG("service found...\r\n"); + if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) { + DEBUG( + "Service UUID-%x attrs[%u %u]\r\n", + service->getUUID().getShortUUID(), + service->getStartHandle(), + service->getEndHandle() + ); + } else { + DEBUG("Service UUID-"); + const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID(); + for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) { + DEBUG("%02x", longUUIDBytes[i]); + } + DEBUG(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle()); + } +} + +void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP){ + DEBUG( + " C UUID-%x valueAttr[%u] props[%x]\r\n", + characteristicP->getUUID().getShortUUID(), + characteristicP->getValueHandle(), + (uint8_t)characteristicP->getProperties().broadcast() + ); + if (characteristicP->getUUID().getShortUUID() + == UARTServiceTXCharacteristicShortUUID) + { + pc.printf("Sevice TX 0x%04x\r\n", UARTServiceTXCharacteristicShortUUID); + uartTXCharacteristic = *characteristicP; + } else if (characteristicP->getUUID().getShortUUID() + == UARTServiceRXCharacteristicShortUUID) + { + pc.printf("Sevice RX 0x%04x\r\n", UARTServiceRXCharacteristicShortUUID); + uartRXCharacteristic = *characteristicP; + foundUartRXCharacteristic = true; + } +} + +void discoveryTerminationCallback(Gap::Handle_t connectionHandle){ + DEBUG("terminated SD for handle=%u\r\n", connectionHandle); +} + +// Mixed role ***************************************************************** +void connectionCallback(const Gap::ConnectionCallbackParams_t *params){ + if (params->role == Gap::CENTRAL) { + DEBUG("connected as Client(central) (handle = %d)\r\n\r", params->handle); + connected2peripheral = true; + connectionHandle = params->handle; + ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback); + ble.gattClient().launchServiceDiscovery( + params->handle, + serviceDiscoveryCallback, + characteristicDiscoveryCallback/*, + 0xa000, 0xa001*/ + ); + } else { + DEBUG("connected as device (handle = %d)\r\n\r", params->handle); + DEBUG( + "Conn. params => min=%d, max=%d, slave=%d, supervision=%d\r\n", + params->connectionParams->minConnectionInterval, + params->connectionParams->maxConnectionInterval, + params->connectionParams->slaveLatency, + params->connectionParams->connectionSupervisionTimeout + ); + Gap::ConnectionParams_t connectionParams; + connectionParams.minConnectionInterval = 6; + connectionParams.maxConnectionInterval = 12; + connectionParams.slaveLatency = 0; + connectionParams.connectionSupervisionTimeout = 500; + if (ble.updateConnectionParams(params->handle, &connectionParams) != BLE_ERROR_NONE){ + DEBUG("failed to update connection parameter\r\n"); + } + } + DEBUG( + "own %02x:%02x:%02x:%02x:%02x:%02x (%s), peer %02x:%02x:%02x:%02x:%02x:%02x (%s)\r\n", + params->ownAddr[5], params->ownAddr[4], params->ownAddr[3], + params->ownAddr[2], params->ownAddr[1], params->ownAddr[0], + (params->ownAddrType == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random", + params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], + params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], + (params->peerAddrType == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random" + ); +} + +void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params){ + DEBUG("handle = %d ", params->handle); + pc.printf(" -> disconnected\r\n", params->handle); + connected2peripheral = false; + if (params->handle == SOFT_DEVICE_FATHER_HANDLE) { + ble.startAdvertising(); // restart advertising + } else { + ble.gap().startScan(advertisementCallback); // restart scan + } +}