
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/#
Revision 0:0ef6455cbb4d, committed 2016-04-16
- Comitter:
- kenjiArai
- Date:
- Sat Apr 16 07:32:21 2016 +0000
- Child:
- 1:f68a5e55a60e
- Commit message:
- BLE Client UART function
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_API.lib Sat Apr 16 07:32:21 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#66159681aa21
--- /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 + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Sat Apr 16 07:32:21 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/082adc85693f \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nRF51822.lib Sat Apr 16 07:32:21 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#f7faad332abc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/original.cpp Sat Apr 16 07:32:21 2016 +0000 @@ -0,0 +1,613 @@ +#if 0 +//------------------------------------------------------------------------------------------------- +// ORIGINAL PROGRAM +// S130 potential unstability case [closed] by Fabien Comte +// https://devzone.nordicsemi.com/question/49705/s130-potential-unstability-case/ +// +#include "mbed.h" +#include "BLE.h" +#include "UARTService.h" +#include "ble/DiscoveredCharacteristic.h" +#include "ble/DiscoveredService.h" +#include "UARTService.h" + +#define SOFT_DEVICE_FATHER_HANDLE 3 + +#define BOARDS_COUNT 3 + +const Gap::Address_t mac_board_0 = {0xb8, 0xac, 0x4e, 0x8d, 0x8b, 0xeb}; +const Gap::Address_t mac_board_1 = {0x9c, 0x43, 0x62, 0x30, 0xaf, 0xd2}; +const Gap::Address_t mac_board_2 = {0x5f, 0x1a, 0x9e, 0x6a, 0x63, 0xdd}; + + +// tiny ble board +#define LED_GREEN p21 +#define LED_RED p22 +#define LED_BLUE p23 +#define BUTTON_PIN p17 +#define BATTERY_PIN p1 + +#define MPU6050_SDA p12 +#define MPU6050_SCL p13 + +#define UART_TX p9 +#define UART_RX p11 +#define UART_CTS p8 +#define UART_RTS p10 + +DigitalOut led(LED_RED); +DigitalOut alivenessLED(LED_GREEN); +InterruptIn button(BUTTON_PIN); +AnalogIn battery(BATTERY_PIN); +Serial pc(UART_TX, UART_RX); + +bool mac_equals(const Gap::Address_t mac_1, const Gap::Address_t mac_2) +{ + #if 0 + if (mac_1[0] != mac_2[0]) + { + return false; + } + if (mac_1[1] != mac_2[1]) + { + return false; + } + if (mac_1[2] != mac_2[2]) + { + return false; + } + if (mac_1[3] != mac_2[3]) + { + return false; + } + if (mac_1[4] != mac_2[4]) + { + return false; + } + if (mac_1[5] != mac_2[5]) + { + return false; + } + #else + for (int i = 0; i < 6; i++) + { + if (mac_1[i] != mac_2[i]) + { + //pc.printf("0x%02x != 0x%02x at %d\r\n", mac_1[i], mac_2[i], i); + return false; + } + else + { + //pc.printf("0x%02x == 0x%02x at %d\r\n", mac_1[i], mac_2[i], i); + } + } + #endif + 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; +} + +void periodicCallback(void) +{ + alivenessLED = !alivenessLED; /* do blinky on alivenessLED while we're waiting for BLE events */ +} + + +// Mixed role **************************************************** +BLE ble; +Gap::Address_t my_mac; +int my_board_index = -1; + +// Device role **************************************************** +UARTService * uartServicePtr = NULL; +const static char DEVICE_NAME[] = "ChangeMe!!"; // change this +static const uint16_t uuid16_list[] = {UARTServiceShortUUID}; +volatile int central_handle = -1; + + +// Central role **************************************************** +Gap::Handle_t connectionHandle = 0xFFFF; +DiscoveredCharacteristic uartTXCharacteristic; +DiscoveredCharacteristic uartRXCharacteristic; +bool foundUartRXCharacteristic = false; +volatile int device_handle = -1; + + +// Device role **************************************************** +void onReceivedDataFromCentralCallback(const GattWriteCallbackParams *params) +{ + if (uartServicePtr != NULL) + { + if ((params->handle == uartServicePtr->getTXCharacteristicHandle()) && (params->len >= 1)) + { + if (params->data[0] != '0') + { + led = 1; + } + else + { + led = 0; + } + + for(int i = 0; i < params->len; i++) + { + pc.printf("%c", params->data[i]); + } + + pc.printf(" (%d, %d)\r\n", params->handle, params->connHandle); + } + } +} + +// Central role **************************************************** +void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) +{ + // do connections like a triangle + int peer_board_index = get_board_index(params->peerAddr); + + int next_board_index = my_board_index + 1; + if (next_board_index >= BOARDS_COUNT) + { + next_board_index = 0; + } + + //pc.printf("adv %d, %d, %d\r\n", peer_board_index, my_board_index, next_board_index); + + // force order + if ((central_handle != -1) || (peer_board_index == 0)) + { + if (peer_board_index == next_board_index) + { + //pc.printf("adv peerAddr[%02x %02x %02x %02x %02x %02x] rssi %d, isScanResponse %u, AdvertisementType %u\r\n", + // params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], + // params->rssi, params->isScanResponse, params->type); + + ble.gap().connect(params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL); + } + } +} + +void serviceDiscoveryCallback(const DiscoveredService *service) +{ + if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) + { + pc.printf("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle()); + } + else + { + //pc.printf("S UUID-"); + const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID(); + for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) + { + pc.printf("%02x", longUUIDBytes[i]); + } + pc.printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle()); + } +} + +void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP) +{ + //pc.printf(" 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("fit TX 0x%04x\r\n", UARTServiceTXCharacteristicShortUUID); + /* !ALERT! Alter this filter to suit your device. */ + uartTXCharacteristic = *characteristicP; + } + else if (characteristicP->getUUID().getShortUUID() == UARTServiceRXCharacteristicShortUUID) + { + pc.printf("fit RX 0x%04x\r\n", UARTServiceRXCharacteristicShortUUID); + /* !ALERT! Alter this filter to suit your device. */ + uartRXCharacteristic = *characteristicP; + foundUartRXCharacteristic = true; + } +} + +void discoveryTerminationCallback(Gap::Handle_t connectionHandle) +{ + pc.printf("terminated SD for handle %u\r\n", connectionHandle); +} + +void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params) +{ + //pc.printf("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)) + { + for (int i = 0; i < params->len; i++) + { + pc.printf("%c", params->data[i]); + } + + pc.printf(" (%d, %d)\r\n", params->handle, params->connHandle); + } + } + else + { + pc.printf("%d\r\n", params->type); + } +} + +// Mixed role **************************************************** +void connectionCallback(const Gap::ConnectionCallbackParams_t *params) +{ + if (params->role == Gap::CENTRAL) + { + if (central_handle == -1) + { + ble.stopAdvertising(); // stop advertising during discovery, incoming connection breaks discovery + } + + device_handle = params->handle; + pc.printf("connected as central (handle = %d)\r\n\r", params->handle); + connectionHandle = params->handle; + ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback); + int ret = ble.gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, UARTServiceShortUUID/*, 0xa001*/); + + if (ret != BLE_ERROR_NONE) + { + pc.printf("launchServiceDiscovery failed error = %d\r\n\r", ret); + } + } + else + { + central_handle = params->handle; + pc.printf("connected as device (handle = %d)\r\n\r", params->handle); + + //pc.printf("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 = 40; + connectionParams.connectionSupervisionTimeout = 500; + + int ret = ble.updateConnectionParams(params->handle, &connectionParams); + if (ret != BLE_ERROR_NONE) + { + pc.printf("failed to update connection parameter\r\n"); + } + */ + } + pc.printf("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(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) +{ + char * ascii_reason = "?"; + switch (reason) + { + case Gap::CONNECTION_TIMEOUT: + ascii_reason = "connection timeout"; + break; + case Gap::REMOTE_USER_TERMINATED_CONNECTION: + ascii_reason = "user terminated connection"; + break; + case Gap::REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES: + ascii_reason = "low resources"; + break; + case Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF: + ascii_reason = "power off"; + break; + case Gap::LOCAL_HOST_TERMINATED_CONNECTION: + ascii_reason = "host terminated connection"; + break; + case Gap::CONN_INTERVAL_UNACCEPTABLE: + ascii_reason = "interval unacceptable"; + break; + default: + ascii_reason = "unknown"; + break; + } + + pc.printf("disconnected (reason = %s, handle = %d)\r\n", ascii_reason, handle); + + + if (handle == SOFT_DEVICE_FATHER_HANDLE) + { + central_handle = -1; + // restart advertising + ble.startAdvertising(); + } + else + { + device_handle = -1; + // restart scan + ble.gap().startScan(advertisementCallback); + } +} + + + +void serialTxCallback() +{ + +} + +int rx_char = -1; +void serialRxCallback() +{ + if (rx_char != -1) + { + pc.printf("overflow\r\n"); + } + + //computer.putc(computer.getc()); + rx_char = pc.getc(); +} + +/* +void gattServerOnDataSent(unsigned count) +{ + +} +*/ + + +int main(void) +{ + alivenessLED = 0; + + pc.baud(115200); + //pc.attach(&serialTxCallback, Serial::TxIrq); + pc.attach(&serialRxCallback, Serial::RxIrq); + + // clear terminal output + for (int k = 0; k < 32; k++) + { + pc.printf("\r\n"); + } + + pc.printf("Central and device\r\n"); + + Ticker ticker; + ticker.attach(periodicCallback, 1); + + + // 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); + pc.printf("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"); + + + // try to speed up but looks like if it was ignored + Gap::ConnectionParams_t fast; + if (ble.getPreferredConnectionParams(&fast) != BLE_ERROR_NONE) + { + pc.printf("getPreferredConnectionParams failed\r\n"); + } + else + { + fast.minConnectionInterval = 16; // 20 ms + fast.maxConnectionInterval = 32; // 40 ms + fast.slaveLatency = 0; + if (ble.gap().setPreferredConnectionParams(&fast) != BLE_ERROR_NONE) + { + pc.printf("setPreferredConnectionParams failed\r\n"); + } + } + ble.gap().onConnection(connectionCallback); + ble.gap().onDisconnection(disconnectionCallback); + + // Device role **************************************************** + ble.gattServer().onDataWritten(onReceivedDataFromCentralCallback); + //ble.gattServer().onDataSent(gattServerOnDataSent); + + UARTService uartService(ble); + uartServicePtr = &uartService; + + // setup advertising + ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); // BLE only, no classic BT + ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); // add name + ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // UUID's broadcast in advertising packet + ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); // advertising type + ble.setAdvertisingInterval(100); + + // Central role **************************************************** + ble.gattClient().onHVX(onReceivedDataFromDeviceCallback); + ble.gap().setScanParams(500, 450); + + + // start advertising and scan + ble.startAdvertising(); + ble.gap().startScan(advertisementCallback); + + while (true) + { + // allow notifications from device + if (foundUartRXCharacteristic && !ble.gattClient().isServiceDiscoveryActive()) + { + foundUartRXCharacteristic = false; /* need to do the following only once */ + + uint16_t value = BLE_HVX_NOTIFICATION; + int ret = ble.gattClient().write(GattClient::GATT_OP_WRITE_REQ, + connectionHandle, + uartRXCharacteristic.getValueHandle() + 1, /* HACK Alert. We're assuming that CCCD descriptor immediately follows the value attribute. */ + sizeof(uint16_t), /* HACK Alert! size should be made into a BLE_API constant. */ + reinterpret_cast<const uint8_t *>(&value)); + + if (ret == BLE_ERROR_NONE) + { + pc.printf("\r\ndevice notifications enabled\r\n"); + } + else + { + switch (ret) + { + case BLE_STACK_BUSY: + foundUartRXCharacteristic = true; // retry later + break; + case BLE_ERROR_NO_MEM: + foundUartRXCharacteristic = true; // retry later + break; + case BLE_ERROR_INVALID_STATE: + pc.printf("\r\ndevice notifications enable failed\r\n"); + break; + default: + break; + } + } + + if (!foundUartRXCharacteristic) + { + if (central_handle == -1) + { + ble.startAdvertising(); + } + } + } + + // while a new char from computer is available + while (rx_char != -1) + { + uint8_t temp[20]; + int length = 1; + + uint8_t command = rx_char; + rx_char = -1; + + // if special char to test a 20 bytes frame + /* + if (command == '*') + { + pc.printf("20 chars\r\n"); + + int c = 0; + for (c = 0; c < 20; c++) + { + temp[c] = 'a' + c; + } + length = 20; + } + else + { + temp[0] = command; + } + */ + temp[0] = command; + + // to central + //if (command == '1') + { + if (central_handle != -1) + { + // device to central + while (1) + { + if (central_handle == -1) + { + pc.printf("\r\ndisconnected 1 (to central)\r\n"); + break; + } + if (!ble.gap().getState().connected) + { + pc.printf("\r\ndisconnected 2 (to central)\r\n"); + break; + } + int ret = ble.gattServer().write(uartServicePtr->getRXCharacteristicHandle(), temp, length); + + if (ret == BLE_ERROR_NONE) + { + //pc.printf("\r\nok (to central)\r\n"); + break; + } + else if (ret == BLE_STACK_BUSY) + { + //pc.printf("\r\nbusy (to central)\r\n"); + //break; + } + else if (ret == BLE_ERROR_OPERATION_NOT_PERMITTED) + { + pc.printf("\r\nnot permitted (to central)\r\n"); + break; + } + else if (ret == BLE_ERROR_INVALID_STATE) + { + pc.printf("\r\ninvalid state (to central)\r\n"); + break; + } + else + { + pc.printf("\r\ncode %d (to central)\r\n", ret); + break; + } + } + } + else + { + pc.printf("\r\nnot connected with central\r\n"); + } + } + + // to device + //if (command == '2') + { + if (device_handle != -1) + { + // central to device + while (1) + { + if (device_handle == -1) + { + pc.printf("\r\ndisconnected (to device)\r\n"); + break; + } + int ret = uartTXCharacteristic.write(length, temp); + if (ret == BLE_ERROR_NONE) + { + //pc.printf("\r\nok (to device)\r\n"); + break; + } + else if (ret == BLE_STACK_BUSY) + { + //pc.printf("\r\nbusy (to device)\r\n"); + //break; + } + else if (ret == BLE_ERROR_OPERATION_NOT_PERMITTED) + { + pc.printf("\r\nnot permitted (to device)\r\n"); + break; + } + else if (ret == BLE_ERROR_INVALID_STATE) + { + pc.printf("\r\ninvalid state (to device)\r\n"); + break; + } + else + { + pc.printf("\r\ncode %d (to device)\r\n", ret); + break; + } + } + } + else + { + pc.printf("\r\nnot connected with device\r\n"); + } + } + } + + ble.waitForEvent(); // save power + } +} +#endif