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:
- 2016-04-21
- Revision:
- 1:f68a5e55a60e
- Parent:
- 0:0ef6455cbb4d
- Child:
- 2:6fb0b87b041d
File content as of revision 1:f68a5e55a60e:
/* * ------- 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 21st, 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 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" // 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}; const Gap::Address_t mac_board_3 = {0x0f, 0x72, 0xbf, 0x43, 0xbc, 0xd0}; const Gap::Address_t mac_board_4 = {0x98, 0x1a, 0x92, 0x10, 0x7f, 0xd5}; #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 connected2server = 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 Server(Peripheral)\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); // Server(Peripheral) 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 Server(Peripheral) 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 Server(central to peripheral) 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); } // Server to Client(peripheral 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; } // Server(Peripheral) 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 (connected2server){ 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;} 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("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); connected2server = true; connectionHandle = params->handle; ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback); ble.gattClient().launchServiceDiscovery( params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback/*, 0xa000, 0xa001*/ ); } else { DEBUG("connected as Server (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); connected2server = false; if (params->handle == SOFT_DEVICE_FATHER_HANDLE) { ble.startAdvertising(); // restart advertising } else { ble.gap().startScan(advertisementCallback); // restart scan } }