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:

  1. Open the nRF Toolbox app
  2. Select the 'UART' mode
  3. Click the 'CONNECT' button
  4. Select the device with the correct SHORT_NAME from the 'AVAILABLE DEVICES' list
  5. 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': /media/uploads/foolsday/screenshot_2014-12-15-20-32-23.png

Committer:
Daniel Veilleux
Date:
Mon Dec 15 21:05:18 2014 -0800
Revision:
0:442c7a6f1978
Initial commit.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Daniel Veilleux 0:442c7a6f1978 1 /* mbed Microcontroller Library
Daniel Veilleux 0:442c7a6f1978 2 * Copyright (c) 2006-2013 ARM Limited
Daniel Veilleux 0:442c7a6f1978 3 *
Daniel Veilleux 0:442c7a6f1978 4 * Licensed under the Apache License, Version 2.0 (the "License");
Daniel Veilleux 0:442c7a6f1978 5 * you may not use this file except in compliance with the License.
Daniel Veilleux 0:442c7a6f1978 6 * You may obtain a copy of the License at
Daniel Veilleux 0:442c7a6f1978 7 *
Daniel Veilleux 0:442c7a6f1978 8 * http://www.apache.org/licenses/LICENSE-2.0
Daniel Veilleux 0:442c7a6f1978 9 *
Daniel Veilleux 0:442c7a6f1978 10 * Unless required by applicable law or agreed to in writing, software
Daniel Veilleux 0:442c7a6f1978 11 * distributed under the License is distributed on an "AS IS" BASIS,
Daniel Veilleux 0:442c7a6f1978 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Daniel Veilleux 0:442c7a6f1978 13 * See the License for the specific language governing permissions and
Daniel Veilleux 0:442c7a6f1978 14 * limitations under the License.
Daniel Veilleux 0:442c7a6f1978 15 */
Daniel Veilleux 0:442c7a6f1978 16
Daniel Veilleux 0:442c7a6f1978 17 #include "mbed.h"
Daniel Veilleux 0:442c7a6f1978 18 #include "BLEDevice.h"
Daniel Veilleux 0:442c7a6f1978 19 #include "UARTService.h"
Daniel Veilleux 0:442c7a6f1978 20 #include "nrf_temp.h"
Daniel Veilleux 0:442c7a6f1978 21
Daniel Veilleux 0:442c7a6f1978 22 #define MAX_REPLY_LEN (UARTService::BLE_UART_SERVICE_MAX_DATA_LEN)
Daniel Veilleux 0:442c7a6f1978 23 #define SENSOR_READ_INTERVAL_S (0.5F)
Daniel Veilleux 0:442c7a6f1978 24 #define ADV_INTERVAL_MS (1000UL)
Daniel Veilleux 0:442c7a6f1978 25 #define UART_BAUD_RATE (19200UL)
Daniel Veilleux 0:442c7a6f1978 26 #define DEVICE_NAME ("DEMO SENSOR") // This can be read AFTER connecting to the device.
Daniel Veilleux 0:442c7a6f1978 27 #define SHORT_NAME ("HACKDEMO") // Keep this short: max 8 chars if a 128bit UUID is also advertised.
Daniel Veilleux 0:442c7a6f1978 28
Daniel Veilleux 0:442c7a6f1978 29 #define DEBUG(...) { m_serial_port.printf(__VA_ARGS__); }
Daniel Veilleux 0:442c7a6f1978 30
Daniel Veilleux 0:442c7a6f1978 31
Daniel Veilleux 0:442c7a6f1978 32 BLEDevice m_ble;
Daniel Veilleux 0:442c7a6f1978 33 Serial m_serial_port(p9, p11); // TX pin, RX pin
Daniel Veilleux 0:442c7a6f1978 34 DigitalOut m_cmd_led(LED1);
Daniel Veilleux 0:442c7a6f1978 35 DigitalOut m_error_led(LED2);
Daniel Veilleux 0:442c7a6f1978 36 AnalogIn m_analog_in(p1);
Daniel Veilleux 0:442c7a6f1978 37 uint16_t m_analog_in_value;
Daniel Veilleux 0:442c7a6f1978 38 UARTService *m_uart_service_ptr;
Daniel Veilleux 0:442c7a6f1978 39
Daniel Veilleux 0:442c7a6f1978 40
Daniel Veilleux 0:442c7a6f1978 41 /**
Daniel Veilleux 0:442c7a6f1978 42 * This callback is used whenever a disconnection occurs.
Daniel Veilleux 0:442c7a6f1978 43 */
Daniel Veilleux 0:442c7a6f1978 44 void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
Daniel Veilleux 0:442c7a6f1978 45 {
Daniel Veilleux 0:442c7a6f1978 46 switch (reason) {
Daniel Veilleux 0:442c7a6f1978 47 case Gap::REMOTE_USER_TERMINATED_CONNECTION:
Daniel Veilleux 0:442c7a6f1978 48 DEBUG("Disconnected (REMOTE_USER_TERMINATED_CONNECTION)\n\r");
Daniel Veilleux 0:442c7a6f1978 49 break;
Daniel Veilleux 0:442c7a6f1978 50 case Gap::LOCAL_HOST_TERMINATED_CONNECTION:
Daniel Veilleux 0:442c7a6f1978 51 DEBUG("Disconnected (LOCAL_HOST_TERMINATED_CONNECTION)\n\r");
Daniel Veilleux 0:442c7a6f1978 52 break;
Daniel Veilleux 0:442c7a6f1978 53 case Gap::CONN_INTERVAL_UNACCEPTABLE:
Daniel Veilleux 0:442c7a6f1978 54 DEBUG("Disconnected (CONN_INTERVAL_UNACCEPTABLE)\n\r");
Daniel Veilleux 0:442c7a6f1978 55 break;
Daniel Veilleux 0:442c7a6f1978 56 }
Daniel Veilleux 0:442c7a6f1978 57
Daniel Veilleux 0:442c7a6f1978 58 DEBUG("Restarting the advertising process\n\r");
Daniel Veilleux 0:442c7a6f1978 59 m_ble.startAdvertising();
Daniel Veilleux 0:442c7a6f1978 60 }
Daniel Veilleux 0:442c7a6f1978 61
Daniel Veilleux 0:442c7a6f1978 62
Daniel Veilleux 0:442c7a6f1978 63 /**
Daniel Veilleux 0:442c7a6f1978 64 * This callback is used whenever the host writes data to one of our GATT characteristics.
Daniel Veilleux 0:442c7a6f1978 65 */
Daniel Veilleux 0:442c7a6f1978 66 void dataWrittenCallback(const GattCharacteristicWriteCBParams *params)
Daniel Veilleux 0:442c7a6f1978 67 {
Daniel Veilleux 0:442c7a6f1978 68 // Ensure that initialization is finished and the host has written to the TX characteristic.
Daniel Veilleux 0:442c7a6f1978 69 if ((m_uart_service_ptr != NULL) && (params->charHandle == m_uart_service_ptr->getTXCharacteristicHandle())) {
Daniel Veilleux 0:442c7a6f1978 70 uint8_t buf[MAX_REPLY_LEN];
Daniel Veilleux 0:442c7a6f1978 71 uint32_t len = 0;
Daniel Veilleux 0:442c7a6f1978 72
Daniel Veilleux 0:442c7a6f1978 73 if (1 == params->len) {
Daniel Veilleux 0:442c7a6f1978 74 switch (params->data[0]) {
Daniel Veilleux 0:442c7a6f1978 75 case '0':
Daniel Veilleux 0:442c7a6f1978 76 m_cmd_led = 0;
Daniel Veilleux 0:442c7a6f1978 77 len = snprintf((char*) buf, MAX_REPLY_LEN, "OK");
Daniel Veilleux 0:442c7a6f1978 78 break;
Daniel Veilleux 0:442c7a6f1978 79 case '1':
Daniel Veilleux 0:442c7a6f1978 80 m_cmd_led = 1;
Daniel Veilleux 0:442c7a6f1978 81 len = snprintf((char*) buf, MAX_REPLY_LEN, "OK");
Daniel Veilleux 0:442c7a6f1978 82 break;
Daniel Veilleux 0:442c7a6f1978 83 case 'a':
Daniel Veilleux 0:442c7a6f1978 84 len = snprintf((char*) buf, MAX_REPLY_LEN, "%d", m_analog_in_value);
Daniel Veilleux 0:442c7a6f1978 85 break;
Daniel Veilleux 0:442c7a6f1978 86 default:
Daniel Veilleux 0:442c7a6f1978 87 len = snprintf((char*) buf, MAX_REPLY_LEN, "ERROR: Unknown char");
Daniel Veilleux 0:442c7a6f1978 88 break;
Daniel Veilleux 0:442c7a6f1978 89 }
Daniel Veilleux 0:442c7a6f1978 90 }
Daniel Veilleux 0:442c7a6f1978 91 else
Daniel Veilleux 0:442c7a6f1978 92 {
Daniel Veilleux 0:442c7a6f1978 93 len = snprintf((char*) buf, MAX_REPLY_LEN, "ERROR: Invalid len");
Daniel Veilleux 0:442c7a6f1978 94 }
Daniel Veilleux 0:442c7a6f1978 95
Daniel Veilleux 0:442c7a6f1978 96 m_ble.updateCharacteristicValue(m_uart_service_ptr->getRXCharacteristicHandle(), buf, len);
Daniel Veilleux 0:442c7a6f1978 97
Daniel Veilleux 0:442c7a6f1978 98 DEBUG("%d bytes received from host\n\r", params->len);
Daniel Veilleux 0:442c7a6f1978 99 }
Daniel Veilleux 0:442c7a6f1978 100 }
Daniel Veilleux 0:442c7a6f1978 101
Daniel Veilleux 0:442c7a6f1978 102
Daniel Veilleux 0:442c7a6f1978 103 /**
Daniel Veilleux 0:442c7a6f1978 104 * This callback is used whenever a write to a GATT characteristic causes data to be sent to the host.
Daniel Veilleux 0:442c7a6f1978 105 */
Daniel Veilleux 0:442c7a6f1978 106 void dataSentCallback(unsigned count)
Daniel Veilleux 0:442c7a6f1978 107 {
Daniel Veilleux 0:442c7a6f1978 108 // NOTE: The count always seems to be 1 regardless of data.
Daniel Veilleux 0:442c7a6f1978 109 DEBUG("%d bytes sent to host\n\r", count);
Daniel Veilleux 0:442c7a6f1978 110 }
Daniel Veilleux 0:442c7a6f1978 111
Daniel Veilleux 0:442c7a6f1978 112
Daniel Veilleux 0:442c7a6f1978 113 /**
Daniel Veilleux 0:442c7a6f1978 114 * This callback is scheduled to be called periodically via a low-priority interrupt.
Daniel Veilleux 0:442c7a6f1978 115 */
Daniel Veilleux 0:442c7a6f1978 116 void periodicCallback(void)
Daniel Veilleux 0:442c7a6f1978 117 {
Daniel Veilleux 0:442c7a6f1978 118 m_analog_in_value = m_analog_in.read_u16();
Daniel Veilleux 0:442c7a6f1978 119 }
Daniel Veilleux 0:442c7a6f1978 120
Daniel Veilleux 0:442c7a6f1978 121
Daniel Veilleux 0:442c7a6f1978 122 void error(ble_error_t err, uint32_t line)
Daniel Veilleux 0:442c7a6f1978 123 {
Daniel Veilleux 0:442c7a6f1978 124 m_error_led = 1;
Daniel Veilleux 0:442c7a6f1978 125 DEBUG("Error %d on line number %d\n\r", err, line);
Daniel Veilleux 0:442c7a6f1978 126 }
Daniel Veilleux 0:442c7a6f1978 127
Daniel Veilleux 0:442c7a6f1978 128
Daniel Veilleux 0:442c7a6f1978 129 int main(void)
Daniel Veilleux 0:442c7a6f1978 130 {
Daniel Veilleux 0:442c7a6f1978 131 ble_error_t err;
Daniel Veilleux 0:442c7a6f1978 132 Ticker ticker;
Daniel Veilleux 0:442c7a6f1978 133
Daniel Veilleux 0:442c7a6f1978 134 m_serial_port.baud(UART_BAUD_RATE);
Daniel Veilleux 0:442c7a6f1978 135
Daniel Veilleux 0:442c7a6f1978 136 DEBUG("Initialising\n\r");
Daniel Veilleux 0:442c7a6f1978 137
Daniel Veilleux 0:442c7a6f1978 138 m_cmd_led = 0;
Daniel Veilleux 0:442c7a6f1978 139 m_error_led = 0;
Daniel Veilleux 0:442c7a6f1978 140 m_analog_in_value = 0;
Daniel Veilleux 0:442c7a6f1978 141
Daniel Veilleux 0:442c7a6f1978 142 ticker.attach(periodicCallback, SENSOR_READ_INTERVAL_S);
Daniel Veilleux 0:442c7a6f1978 143
Daniel Veilleux 0:442c7a6f1978 144 m_ble.init();
Daniel Veilleux 0:442c7a6f1978 145 m_ble.onDisconnection(disconnectionCallback);
Daniel Veilleux 0:442c7a6f1978 146 m_ble.onDataWritten(dataWrittenCallback);
Daniel Veilleux 0:442c7a6f1978 147 m_ble.onDataSent(dataSentCallback);
Daniel Veilleux 0:442c7a6f1978 148
Daniel Veilleux 0:442c7a6f1978 149 // Set the TX power in dBm units.
Daniel Veilleux 0:442c7a6f1978 150 // Possible values (in decreasing order): 4, 0, -4, -8, -12, -16, -20.
Daniel Veilleux 0:442c7a6f1978 151 err = m_ble.setTxPower(4);
Daniel Veilleux 0:442c7a6f1978 152 if (BLE_ERROR_NONE != err) {
Daniel Veilleux 0:442c7a6f1978 153 error(err, __LINE__);
Daniel Veilleux 0:442c7a6f1978 154 }
Daniel Veilleux 0:442c7a6f1978 155
Daniel Veilleux 0:442c7a6f1978 156 // Setup advertising (GAP stuff).
Daniel Veilleux 0:442c7a6f1978 157 err = m_ble.setDeviceName(DEVICE_NAME);
Daniel Veilleux 0:442c7a6f1978 158 if (BLE_ERROR_NONE != err) {
Daniel Veilleux 0:442c7a6f1978 159 error(err, __LINE__);
Daniel Veilleux 0:442c7a6f1978 160 }
Daniel Veilleux 0:442c7a6f1978 161
Daniel Veilleux 0:442c7a6f1978 162 err = m_ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
Daniel Veilleux 0:442c7a6f1978 163 if (BLE_ERROR_NONE != err) {
Daniel Veilleux 0:442c7a6f1978 164 error(err, __LINE__);
Daniel Veilleux 0:442c7a6f1978 165 }
Daniel Veilleux 0:442c7a6f1978 166
Daniel Veilleux 0:442c7a6f1978 167 m_ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
Daniel Veilleux 0:442c7a6f1978 168
Daniel Veilleux 0:442c7a6f1978 169 err = m_ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
Daniel Veilleux 0:442c7a6f1978 170 (const uint8_t *)SHORT_NAME,
Daniel Veilleux 0:442c7a6f1978 171 (sizeof(SHORT_NAME) - 1));
Daniel Veilleux 0:442c7a6f1978 172 if (BLE_ERROR_NONE != err) {
Daniel Veilleux 0:442c7a6f1978 173 error(err, __LINE__);
Daniel Veilleux 0:442c7a6f1978 174 }
Daniel Veilleux 0:442c7a6f1978 175
Daniel Veilleux 0:442c7a6f1978 176 err = m_ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
Daniel Veilleux 0:442c7a6f1978 177 (const uint8_t *)UARTServiceUUID_reversed,
Daniel Veilleux 0:442c7a6f1978 178 sizeof(UARTServiceUUID_reversed));
Daniel Veilleux 0:442c7a6f1978 179 if (BLE_ERROR_NONE != err) {
Daniel Veilleux 0:442c7a6f1978 180 error(err, __LINE__);
Daniel Veilleux 0:442c7a6f1978 181 }
Daniel Veilleux 0:442c7a6f1978 182
Daniel Veilleux 0:442c7a6f1978 183 m_ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADV_INTERVAL_MS));
Daniel Veilleux 0:442c7a6f1978 184 m_ble.startAdvertising();
Daniel Veilleux 0:442c7a6f1978 185
Daniel Veilleux 0:442c7a6f1978 186 // Create a UARTService object (GATT stuff).
Daniel Veilleux 0:442c7a6f1978 187 UARTService uartService(m_ble);
Daniel Veilleux 0:442c7a6f1978 188 m_uart_service_ptr = &uartService;
Daniel Veilleux 0:442c7a6f1978 189
Daniel Veilleux 0:442c7a6f1978 190 while (true) {
Daniel Veilleux 0:442c7a6f1978 191 m_ble.waitForEvent();
Daniel Veilleux 0:442c7a6f1978 192 }
Daniel Veilleux 0:442c7a6f1978 193 }