A simple program that uses the UARTService to transfer string data over GATT. Can be used with the nRF Toolbox app for rapid prototyping.
Dependencies: BLE_API mbed nRF51822
ABOUT
This program is intended to make it easy to prototype a sensor that sends data to a smart device using Bluetooth Low Energy (AKA Bluetooth Smart). Nordic's UARTService is used because it allows string data to be exchanged over GATT using Nordic's nRF Toolbox app (available for download in both Apple and Google app stores).
HARDWARE
By default the program assumes that the hardware being used is Nordic's nRF51822-mKIT (PCA10024) along with an analog sensor.
SETUP
A simple light sensor is a great example of an analog sensor and requires only:
Example breadboard circuit (with the photoresistor acting as part of a voltage divider):
+---------------+ | XXXXXXXXXX | | X | | PHOTORESISTOR | VCC | X | ^ | XXXXXXXXXX | | +---------------+ | | | | +-----+ | | +-----+ 10k +-----+ +------+ +-----+ | | | | | | P0.01+-------------+ | -+- | | GND | | NOTE: It doesn't matter which lead of the sensor is connected to VCC and which is connected to GND.
The value that will be read on the analog pin will decrease if the sensor is exposed to brighter light.
VCC is about 2.9V when the board is powered via USB and 2.2V when using the 2032 coin cell battery.
SOFTWARE
The only file in the program that is not part of the normal libraries is main.cpp.
The easiest way to get started is to use the precompiled nRF Toolbox app on your smart device (iPhone4s or newer, Android 4.3 or newer).
SETUP
It is recommended to change the SHORT_NAME variable to differentiate the device from other devices in the area (default is 'HACKDEMO'). Note that the SHORT_NAME string will probably be used as the description when the device is discovered by scanners.
The nRF51822-mKIT has a UART-to-USB adapter that can be used for debugging. The default configuration is 19200 baud, 8N1, no flow control. On Linux, the serial port enumerates as /dev/ttyACM0. On Windows, the port is displayed in the Device Manager as 'mbed Serial Port (COMX)'. Note that the serial port will be temporarily disabled when new programs are loaded.
USAGE
After the program is compiled and downloaded it will advertise continuously.
To connect:
- Open the nRF Toolbox app
- Select the 'UART' mode
- Click the 'CONNECT' button
- Select the device with the correct SHORT_NAME from the 'AVAILABLE DEVICES' list
- Wait for the primary service to be found
All of the commands that are sent to the mKIT will be acknowledged with a string in the form 'OK' or a string in the form 'ERROR: ...'. The following commands are defined:
- Write a '1' character to turn LED_1 on
- Write a '0' character to turn LED_1 off
- Write an 'a' character to read P0.01 as a 16-bit integer
If an error occurs then LED_2 will be turned on and a line number and non-zero error code will be printed to the UART.
In this screenshot the up-arrow button sends a '1', down-arrow sends a '0', and the info button sends an 'a':
Revision 0:442c7a6f1978, committed 2014-12-15
- Comitter:
- Daniel Veilleux
- Date:
- Mon Dec 15 21:05:18 2014 -0800
- Commit message:
- Initial commit.
Changed in this revision
diff -r 000000000000 -r 442c7a6f1978 BLE_API.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_API.lib Mon Dec 15 21:05:18 2014 -0800 @@ -0,0 +1,1 @@ +http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#fb2a891a0d98
diff -r 000000000000 -r 442c7a6f1978 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Dec 15 21:05:18 2014 -0800 @@ -0,0 +1,193 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 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. + */ + +#include "mbed.h" +#include "BLEDevice.h" +#include "UARTService.h" +#include "nrf_temp.h" + +#define MAX_REPLY_LEN (UARTService::BLE_UART_SERVICE_MAX_DATA_LEN) +#define SENSOR_READ_INTERVAL_S (0.5F) +#define ADV_INTERVAL_MS (1000UL) +#define UART_BAUD_RATE (19200UL) +#define DEVICE_NAME ("DEMO SENSOR") // This can be read AFTER connecting to the device. +#define SHORT_NAME ("HACKDEMO") // Keep this short: max 8 chars if a 128bit UUID is also advertised. + +#define DEBUG(...) { m_serial_port.printf(__VA_ARGS__); } + + +BLEDevice m_ble; +Serial m_serial_port(p9, p11); // TX pin, RX pin +DigitalOut m_cmd_led(LED1); +DigitalOut m_error_led(LED2); +AnalogIn m_analog_in(p1); +uint16_t m_analog_in_value; +UARTService *m_uart_service_ptr; + + +/** + * This callback is used whenever a disconnection occurs. + */ +void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) +{ + switch (reason) { + case Gap::REMOTE_USER_TERMINATED_CONNECTION: + DEBUG("Disconnected (REMOTE_USER_TERMINATED_CONNECTION)\n\r"); + break; + case Gap::LOCAL_HOST_TERMINATED_CONNECTION: + DEBUG("Disconnected (LOCAL_HOST_TERMINATED_CONNECTION)\n\r"); + break; + case Gap::CONN_INTERVAL_UNACCEPTABLE: + DEBUG("Disconnected (CONN_INTERVAL_UNACCEPTABLE)\n\r"); + break; + } + + DEBUG("Restarting the advertising process\n\r"); + m_ble.startAdvertising(); +} + + +/** + * This callback is used whenever the host writes data to one of our GATT characteristics. + */ +void dataWrittenCallback(const GattCharacteristicWriteCBParams *params) +{ + // Ensure that initialization is finished and the host has written to the TX characteristic. + if ((m_uart_service_ptr != NULL) && (params->charHandle == m_uart_service_ptr->getTXCharacteristicHandle())) { + uint8_t buf[MAX_REPLY_LEN]; + uint32_t len = 0; + + if (1 == params->len) { + switch (params->data[0]) { + case '0': + m_cmd_led = 0; + len = snprintf((char*) buf, MAX_REPLY_LEN, "OK"); + break; + case '1': + m_cmd_led = 1; + len = snprintf((char*) buf, MAX_REPLY_LEN, "OK"); + break; + case 'a': + len = snprintf((char*) buf, MAX_REPLY_LEN, "%d", m_analog_in_value); + break; + default: + len = snprintf((char*) buf, MAX_REPLY_LEN, "ERROR: Unknown char"); + break; + } + } + else + { + len = snprintf((char*) buf, MAX_REPLY_LEN, "ERROR: Invalid len"); + } + + m_ble.updateCharacteristicValue(m_uart_service_ptr->getRXCharacteristicHandle(), buf, len); + + DEBUG("%d bytes received from host\n\r", params->len); + } +} + + +/** + * This callback is used whenever a write to a GATT characteristic causes data to be sent to the host. + */ +void dataSentCallback(unsigned count) +{ + // NOTE: The count always seems to be 1 regardless of data. + DEBUG("%d bytes sent to host\n\r", count); +} + + +/** + * This callback is scheduled to be called periodically via a low-priority interrupt. + */ +void periodicCallback(void) +{ + m_analog_in_value = m_analog_in.read_u16(); +} + + +void error(ble_error_t err, uint32_t line) +{ + m_error_led = 1; + DEBUG("Error %d on line number %d\n\r", err, line); +} + + +int main(void) +{ + ble_error_t err; + Ticker ticker; + + m_serial_port.baud(UART_BAUD_RATE); + + DEBUG("Initialising\n\r"); + + m_cmd_led = 0; + m_error_led = 0; + m_analog_in_value = 0; + + ticker.attach(periodicCallback, SENSOR_READ_INTERVAL_S); + + m_ble.init(); + m_ble.onDisconnection(disconnectionCallback); + m_ble.onDataWritten(dataWrittenCallback); + m_ble.onDataSent(dataSentCallback); + + // Set the TX power in dBm units. + // Possible values (in decreasing order): 4, 0, -4, -8, -12, -16, -20. + err = m_ble.setTxPower(4); + if (BLE_ERROR_NONE != err) { + error(err, __LINE__); + } + + // Setup advertising (GAP stuff). + err = m_ble.setDeviceName(DEVICE_NAME); + if (BLE_ERROR_NONE != err) { + error(err, __LINE__); + } + + err = m_ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED); + if (BLE_ERROR_NONE != err) { + error(err, __LINE__); + } + + m_ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + + err = m_ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, + (const uint8_t *)SHORT_NAME, + (sizeof(SHORT_NAME) - 1)); + if (BLE_ERROR_NONE != err) { + error(err, __LINE__); + } + + err = m_ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, + (const uint8_t *)UARTServiceUUID_reversed, + sizeof(UARTServiceUUID_reversed)); + if (BLE_ERROR_NONE != err) { + error(err, __LINE__); + } + + m_ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADV_INTERVAL_MS)); + m_ble.startAdvertising(); + + // Create a UARTService object (GATT stuff). + UARTService uartService(m_ble); + m_uart_service_ptr = &uartService; + + while (true) { + m_ble.waitForEvent(); + } +}
diff -r 000000000000 -r 442c7a6f1978 mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon Dec 15 21:05:18 2014 -0800 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/4fc01daae5a5 \ No newline at end of file
diff -r 000000000000 -r 442c7a6f1978 nRF51822.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nRF51822.lib Mon Dec 15 21:05:18 2014 -0800 @@ -0,0 +1,1 @@ +http://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#17fe69405098