How to turn a WaveShare nRF51-BLE400 into a discoverable beacon using mbed
Dependencies: mbed BLE_API nRF51822
Port for WaveShare BLE400 (chip nRF51 Aliexpress devboard)
Android Evothings good example for Nordic nRF51822-DK
As target for mbed-online used <Nordic nRF51822>
- (Large green board Nordic nRF51822-mKIT, actually deprecated on 01/03/2019)
Briefly: Handle via Evothings BLE Application 4 LEDs and 2 Buttons(via notify messages).
Figure 1: Evothings Android application screenshot
Android Evothings application sources
Happy coding!
maxxir 02/03/19
Diff: main.cpp
- Revision:
- 16:a84185b72bae
- Parent:
- 15:7e06fce6e4f8
- Child:
- 17:9071edee0b13
--- a/main.cpp Tue Jan 12 12:00:16 2016 +0000 +++ b/main.cpp Fri Mar 01 19:41:55 2019 +0000 @@ -1,98 +1,277 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2015 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* +Port for WaveShare BLE400 Evothings good example for Nordic nRF51822-DK +As target for mbed-online used <Nordic nRF51822> (Large green board Nordic nRF51822-mKIT, actually deprecated on ~01/03/2019) +Briefly: Handle via Evothings BLE Application 4 LEDs and 2 Buttons(via notify messages). + +This example original: +https://os.mbed.com/users/jensstruemper/code/Evothings-Updated/ +Android application: +http://evothings.com/2.2/doc/examples/nordic-nRF51-ble.html +This application explanation: +How to turn a Nordic Semiconductor nRF51-DK into a discoverable beacon using mbed +https://evothings.com/how-to-turn-a-nordic-semiconductor-nrf51-dk-into-a-discoverable-beacon-using-mbed/ + +Author porting: +Ibragimov Maksim aka maxxir +Russia Togliatty +01/03/2019 +*/ + +/* +PS. +My Win7 CMD script string example to flash BLE400 via STLINK-V2 && OpenOCD +content from flash_ble400.bat: +E:\stm32_eclipse_neon\OpenOCD\bin\openocd.exe -d2 -f interface/stlink-v2.cfg ; -f target/nrf51_stlink.tcl -c "program %1 verify reset exit"; shutdown; + +Flash example via console: +>flash_ble400.bat BLE_Evothings_NRF51822-BLE400.NRF51822.hex +*/ + +/* + * nRF51-DK BLEDevice service/characteristic (read/write) using mbed.org */ - + +// uncomment if not interested in a console log +#define CONSOLE_LOG + #include "mbed.h" #include "ble/BLE.h" -/* Optional: Device Name, add for human read-ability */ -const static char DEVICE_NAME[] = "ChangeMe!!"; +//------------------------------------------------------------------------- + +#ifdef CONSOLE_LOG +#define INFO(x, ...) printf(x, ##__VA_ARGS__); +#define INFO_NL(x, ...) printf(x "\r\n", ##__VA_ARGS__); +#else +#define INFO(x, ...) +#define INFO_NL(x, ...) +#endif + +// a little routine to print a 128-bit UUID nicely +void INFO_UUID(const char *prefix, UUID uuid) +{ + uint8_t *p = (uint8_t *)uuid.getBaseUUID(); + INFO("%s: ", prefix); + for (int i=0; i<16; i++) + { + INFO("%02x", p[i]); + if ((i == 3) || (i == 5) || (i == 7) || (i == 9)) INFO("-"); + } + INFO_NL(""); +} + +//------------------------------------------------------------------------- + +// name of the device +const static char DEVICE_NAME[] = "nRF51-BLE400"; + +// GATT service and characteristic UUIDs +const UUID nRF51_GATT_SERVICE = UUID((uint8_t *)"nRF51-DK "); +const UUID nRF51_GATT_CHAR_BUTTON = UUID((uint8_t *)"nRF51-DK button "); +const UUID nRF51_GATT_CHAR_LED = UUID((uint8_t *)"nRF51-DK led "); + +#define CHARACTERISTIC_BUTTON 0 +#define CHARACTERISTIC_LED 1 +#define CHARACTERISTIC_COUNT 2 + +// our bluetooth smart objects +BLE ble; +GattService *gatt_service; +GattCharacteristic *gatt_characteristics[CHARACTERISTIC_COUNT]; +uint8_t gatt_char_value[CHARACTERISTIC_COUNT]; + +#ifdef CONSOLE_LOG +Serial pc(USBTX,USBRX); +#endif + +//------------------------------------------------------------------------- +// button handling +//------------------------------------------------------------------------- + +// define our digital in values we will be using for the characteristic +//WaveShare BLE400 edited +DigitalIn button1(P0_16); +DigitalIn button2(P0_17); +//DigitalIn button3(P0_14); //Just substitute for check, actually not present on WaveShare BLE400 +//DigitalIn button4(P0_15); //Just substitute for check, actually not present on WaveShare BLE400 + +uint8_t button_new_value = 0; +uint8_t button_old_value = button_new_value; -/* You have up to 26 bytes of advertising data to use. */ -const static uint8_t AdvData[] = {0x01,0x02,0x03,0x04,0x05}; /* Example of hex data */ -//const static uint8_t AdvData[] = {"ChangeThisData"}; /* Example of character data */ +void monitorButtons() +{ + // read in the buttons, mapped into nibble (0000 = all off, 1111 = all on) + button_new_value = 0; + button_new_value |= (button1.read() != 1); button_new_value <<= 1; + button_new_value |= (button2.read() != 1); button_new_value <<= 1; + //Should not used for WaveShare BLE400 + /* + button_new_value |= (button3.read() != 1); button_new_value <<= 1; + button_new_value |= (button4.read() != 1); + */ + // set the updated value of the characteristic if data has changed + if (button_new_value != button_old_value) + { + ble.updateCharacteristicValue( + gatt_characteristics[CHARACTERISTIC_BUTTON] -> getValueHandle(), + &button_new_value, sizeof(button_new_value)); + button_old_value = button_new_value; + + INFO_NL(" button state: [0x%02x]", button_new_value); + } +} + +//------------------------------------------------------------------------- +// LED handling +//------------------------------------------------------------------------- +//WaveShare BLE400 edited +DigitalOut led1(P0_18); +DigitalOut led2(P0_19); +DigitalOut led3(P0_20); +DigitalOut led4(P0_21); +DigitalOut led5(P0_22); //Actually not used here + +uint8_t led_value = 0; + +/* +void onLedDataWritten(const uint8_t* value, uint8_t length) +{ + // we only care about a single byte + led_value = value[0]; -/* Optional: Restart advertising when peer disconnects */ + // depending on the value coming through; set/unset LED's + if ((led_value & 0x01) != 0) led1.write(0); else led1.write(1); + if ((led_value & 0x02) != 0) led2.write(0); else led2.write(1); + if ((led_value & 0x04) != 0) led3.write(0); else led3.write(1); + if ((led_value & 0x08) != 0) led4.write(0); else led4.write(1); + + INFO_NL(" led state: [0x%02x]", led_value); +} +*/ +//Adapted for WaveShare BLE400, LED_ON = HIGH (NRF51822-DK vice versa LED_ON = LOW) +void onLedDataWritten(const uint8_t* value, uint8_t length) +{ + // we only care about a single byte + led_value = value[0]; + + // depending on the value coming through; set/unset LED's + if ((led_value & 0x01) != 0) led1.write(1); else led1.write(0); + if ((led_value & 0x02) != 0) led2.write(1); else led2.write(0); + if ((led_value & 0x04) != 0) led3.write(1); else led3.write(0); + if ((led_value & 0x08) != 0) led4.write(1); else led4.write(0); + + INFO_NL(" led state: [0x%02x]", led_value); +} + +//------------------------------------------------------------------------- + +void onConnection(const Gap::ConnectionCallbackParams_t *params) +{ + INFO_NL(">> connected"); + + // set the initial values of the characteristics (for every session) + //led_value = 0; + onLedDataWritten(&led_value, 1); // force LED's to be in off state + //LED5=ON on connection (for BLE400) + led5.write(1); +} + +void onDataWritten(const GattWriteCallbackParams *context) +{ + // was the characteristic being written to nRF51_GATT_CHAR_LED? + if (context -> handle == + gatt_characteristics[CHARACTERISTIC_LED] -> getValueHandle()) + { + onLedDataWritten(context -> data, context -> len); + } +} + void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) { - BLE::Instance().gap().startAdvertising(); -} -/** - * This function is called when the ble initialization process has failed - */ -void onBleInitError(BLE &ble, ble_error_t error) -{ - /* Avoid compiler warnings */ - (void) ble; - (void) error; - - /* Initialization error handling should go here */ -} - -/** - * Callback triggered when the ble initialization process has finished - */ -void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) -{ - BLE& ble = params->ble; - ble_error_t error = params->error; - - if (error != BLE_ERROR_NONE) { - /* In case of error, forward the error handling to onBleInitError */ - onBleInitError(ble, error); - return; - } - - /* Ensure that it is the default instance of BLE */ - if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { - return; - } - - /* Set device name characteristic data */ - ble.gap().setDeviceName((const uint8_t *) DEVICE_NAME); - - /* Optional: add callback for disconnection */ - ble.gap().onDisconnection(disconnectionCallback); - - /* Sacrifice 3B of 31B to Advertising Flags */ - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE ); - ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); - - /* Sacrifice 2B of 31B to AdvType overhead, rest goes to AdvData array you define */ - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData)); - - /* Optional: Add name to device */ - //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); - - /* Set advertising interval. Longer interval == longer battery life */ - ble.gap().setAdvertisingInterval(100); /* 100ms */ - - /* Start advertising */ - ble.gap().startAdvertising(); + INFO_NL(">> disconnected"); + ble.gap().startAdvertising(); // restart advertising + INFO_NL(">> device advertising"); + //LED5=OFF on connection (for BLE400) + led5.write(0); } -int main(void) + +int main() { - BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE); - - /* Initialize BLE baselayer, always do this first! */ - ble.init(bleInitComplete); +#ifdef CONSOLE_LOG + // wait a second before trying to write something to console + wait(1); +#endif + INFO_NL(">> nRF51-BLE400 start"); + + // create our button characteristic (read, notify) + gatt_characteristics[CHARACTERISTIC_BUTTON] = + new GattCharacteristic( + nRF51_GATT_CHAR_BUTTON, + &gatt_char_value[CHARACTERISTIC_BUTTON], 1, 1, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + // create our LED characteristic (read, write) + gatt_characteristics[CHARACTERISTIC_LED] = + new GattCharacteristic( + nRF51_GATT_CHAR_LED, + &gatt_char_value[CHARACTERISTIC_LED], 1, 1, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); + + // create our service, with both characteristics + gatt_service = + new GattService(nRF51_GATT_SERVICE, + gatt_characteristics, CHARACTERISTIC_COUNT); + + // initialize our ble device + ble.init(); + ble.gap().setDeviceName((uint8_t *)DEVICE_NAME); + INFO_NL(">> initialized device '%s'", DEVICE_NAME); - /* Infinite loop waiting for BLE events */ - while (true) { - /* Save power while waiting for callback events */ - ble.waitForEvent(); + // configure our advertising type, payload and interval + ble.gap().accumulateAdvertisingPayload( + GapAdvertisingData::BREDR_NOT_SUPPORTED | + GapAdvertisingData::LE_GENERAL_DISCOVERABLE); + ble.gap().accumulateAdvertisingPayload( + GapAdvertisingData::COMPLETE_LOCAL_NAME, + (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); + ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + ble.gap().setAdvertisingInterval(160); // 100ms + INFO_NL(">> configured advertising type, payload and interval"); + + // configure our callbacks + ble.gap().onDisconnection(disconnectionCallback); + ble.gap().onConnection(onConnection); + ble.onDataWritten(onDataWritten); + INFO_NL(">> registered for callbacks"); + + // add our gatt service with two characteristics + ble.addService(*gatt_service); + INFO_NL(">> added GATT service with two characteristics"); + + // show some debugging information about service/characteristics + INFO_UUID(" ", nRF51_GATT_SERVICE); + INFO_UUID(" :", nRF51_GATT_CHAR_BUTTON); + INFO_UUID(" :", nRF51_GATT_CHAR_LED); + + // start advertising + ble.gap().startAdvertising(); + INFO_NL(">> device advertising"); + + // start monitoring the buttons and posting new values + Ticker ticker; + ticker.attach(monitorButtons, 0.1); // every 10th of a second + INFO_NL(">> monitoring button state"); + + // let the device do its thing + INFO_NL(">> waiting for events "); + for (;;) + { + ble.waitForEvent(); } } + +//------------------------------------------------------------------------- \ No newline at end of file