Experimental BLE project showing how IO can be made with an App over BLE. Pointer to matching App will be added when ready, initially this works with: - Android App [nRF-Master Control Panel], supports Write,Read,Notify - Android Project [BluetoothLeGatt]

Dependencies:   BLE_API mbed nRF51822

This is an experimental project for BLE (Bluetooth LE == Bluetooth Low Energy == Bluetooth Smart).

  • It supports general IO over BLE with Read/Notify/Write support.
  • It is compatible with FOTA using Android App "nRF Master Control Panel" (20150126)
  • IO supported by:
    • Custom Android App is in the WIKI under: Android-App, developed from Android Sample "BluetoothLeGatt"
    • Android App: nRF-MCP (Master Control Panel)
    • iOS App LightBlue.
    • General HRM, HTM, Battery and similar apps should be able to access the matching services.
  • It includes combinations of code from other projects, alternative code included can be tried by moving comments (, //)
  • 20150126 bleIO r25: It compiles for both "Nordic nRF51822" and "Nordic nRF51822 FOTA" platforms
  • 20150126 The matching bleIO App (in wiki) doesn't support FOTA yet, use Android App "nRF Master Control Panel"

Feedback and ideas greatly appreciated!!!

Committer:
prussell
Date:
Sun Dec 21 16:26:46 2014 +0000
Revision:
11:7d02fe5ebea5
Parent:
10:ee3a359f7d3f
Child:
12:8bac5f5d3a3e
Properly reversed order of bytes in Advertised UUID128

Who changed what in which revision?

UserRevisionLine numberNew contents of line
prussell 0:0217a862b047 1 //=========Header (PR)
prussell 0:0217a862b047 2 // blePRv04, Initial: 20141210 Paul Russell (mbed user: prussell = PR)
prussell 0:0217a862b047 3 // This sample includes code from several projects found on http://developer.mbed.org, including but not limited to:
prussell 9:2d11beda333f 4 // - http://developer.mbed.org/users/jksoft/code/BLE_RCBController/ (Setting up a custom Service/Characteristic)
prussell 0:0217a862b047 5 // - http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/
prussell 0:0217a862b047 6 // - https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LoopbackUART/
prussell 1:4a25d917fb6a 7 // - https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/
prussell 6:5b6fb35b4450 8 // - https://developer.mbed.org/users/garimagupta002/notebook/ble-uart-lcd-demo/ (Advertise UUID16+UUID128)
prussell 10:ee3a359f7d3f 9 // - http://developer.mbed.org/teams/UCL-IoT/code/BLE_LED_Controller/ (With matching Android App, uses BLE-UART)
prussell 10:ee3a359f7d3f 10 // - https://developer.mbed.org/users/todotani/code/BLE_Health_Thermometer2-010/wiki/BLE_Health_Thermometer2-010 //Temperature handles
prussell 10:ee3a359f7d3f 11 // -
prussell 0:0217a862b047 12 // - miscellaneous adopted from more samples...
prussell 0:0217a862b047 13 // Reference:
prussell 0:0217a862b047 14 // - http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/
prussell 0:0217a862b047 15 // - Reference: http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/docs/tip/
prussell 0:0217a862b047 16 // - Reference: http://developer.mbed.org/teams/Bluetooth-Low-Energy/
prussell 0:0217a862b047 17 // Warnings:
prussell 0:0217a862b047 18 // - As of 20141210 it is necessary to use Android App [nRF-Master Control Panel] to ensure any previous connected
prussell 0:0217a862b047 19 // code on mkit is properly Disconnected before trying to connect other Android nRF Apps (nRFToolbox, nRF UART 2.0, etc.).
prussell 0:0217a862b047 20 // As UART device doesn't offer disconnect you may need to load a 3rf sample, then connect, then discoonect, to clear the link.
prussell 0:0217a862b047 21 // Notes:
prussell 10:ee3a359f7d3f 22 // - onDataSent() maybe only occuring when confirmed receive by phone, as onDataSent() didn't happen when phone moved out of range, and no onDataSent() with App nRF-MCP.
prussell 0:0217a862b047 23 // - onDisconnect(Reason:8) occured after ~20Tx with no onDataSent() after phone moved out of range, OnTimeout didn't occur at all.
prussell 0:0217a862b047 24 // ToDo: and ToCheck:
prussell 8:f187ba55aed2 25 // - Handle All return codes for all functions not void, including BLE not BLE_ERROR_NONE, as a minimum output some debug and log event in non-volatile memory for diagnostics.
prussell 0:0217a862b047 26 // - Re-check where voltatile needed
prussell 8:f187ba55aed2 27 // - Evaluate setting: IS_SRVC_CHANGED_CHARACT_PRESENT, see: https://devzone.nordicsemi.com/question/22751/nrftoobox-on-android-not-recognizing-changes-in-application-type-running-on-nordic-pcb/?answer=23097#post-id-23097
prussell 11:7d02fe5ebea5 28 // - invalid or untested reference code commented with "//x ", feel free to delete or experiment
prussell 11:7d02fe5ebea5 29 // - valid alternate code commented with "//a " prefix to indicate alternnative is valid code
prussell 0:0217a862b047 30 //==========End of PR's Header
prussell 0:0217a862b047 31
prussell 0:0217a862b047 32 //==========Historic Licencing from original imported sample from mbed website [BLE_HeartRate] ==========
prussell 0:0217a862b047 33 //From: http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/
prussell 0:0217a862b047 34 /* mbed Microcontroller Library
prussell 0:0217a862b047 35 * Copyright (c) 2006-2013 ARM Limited
prussell 0:0217a862b047 36 *
prussell 0:0217a862b047 37 * Licensed under the Apache License, Version 2.0 (the "License");
prussell 0:0217a862b047 38 * you may not use this file except in compliance with the License.
prussell 0:0217a862b047 39 * You may obtain a copy of the License at
prussell 0:0217a862b047 40 * http://www.apache.org/licenses/LICENSE-2.0
prussell 0:0217a862b047 41 * Unless required by applicable law or agreed to in writing, software
prussell 0:0217a862b047 42 * distributed under the License is distributed on an "AS IS" BASIS,
prussell 0:0217a862b047 43 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
prussell 0:0217a862b047 44 * See the License for the specific language governing permissions and
prussell 0:0217a862b047 45 * limitations under the License. */
prussell 0:0217a862b047 46 //==========end of Historic Licencing ==========
prussell 0:0217a862b047 47
prussell 0:0217a862b047 48
prussell 0:0217a862b047 49 //==========Compile Options==========
prussell 11:7d02fe5ebea5 50 #define ENABLE_uSerial 1 //PR: Enable Debug on mbed's USB Serial Debug, Setup: Serial 9600,8,N,1,NoFlowControl (TeraTerm: http://en.sourceforge.jp/projects/ttssh2/releases/)
prussell 11:7d02fe5ebea5 51 //x #define ENABLE_BLE_SLOW 0 //PR: Option to slow the connection interval possibly saving power (After Connected) -- Imported feature never tested
prussell 0:0217a862b047 52
prussell 0:0217a862b047 53 //==========Includes==========
prussell 9:2d11beda333f 54 #include "mbed.h" // mbed
prussell 1:4a25d917fb6a 55 #include "BLEDevice.h" // BLE
prussell 1:4a25d917fb6a 56 #include "nrf_soc.h" // nRF Internal Temperature Sensor
prussell 0:0217a862b047 57
prussell 0:0217a862b047 58 //Services
prussell 0:0217a862b047 59 #include "BatteryService.h"
prussell 0:0217a862b047 60 #include "DeviceInformationService.h"
prussell 5:d36bbb315e31 61 #include "HealthThermometerService.h"
prussell 0:0217a862b047 62 #include "HeartRateService.h"
prussell 5:d36bbb315e31 63 #include "LinkLossService.h" //TODO: How support this?
prussell 8:f187ba55aed2 64 //#include "DFUService" //TODO: DFU and FOTA Support
prussell 0:0217a862b047 65 //#include "UARTService.h" //TODO: Add a UART Channel for streaming data like logs?
prussell 0:0217a862b047 66
prussell 0:0217a862b047 67 //==========Debug Console==========
prussell 11:7d02fe5ebea5 68 #if ENABLE_uSerial
prussell 11:7d02fe5ebea5 69 //Restart TeraTerm just before Pressing Reset on mbed, Default Serual=9600-8N1(No Flow Control)
prussell 10:ee3a359f7d3f 70 //Using default baud rate to avoid issues with DEBUG in a constructor being at default baud rate before main()
prussell 11:7d02fe5ebea5 71 //TeraTerm: http://en.sourceforge.jp/projects/ttssh2/releases/
prussell 11:7d02fe5ebea5 72 Serial debug_userial(USBTX, USBRX); //PR: DebugSerialOverMbedUSB
prussell 11:7d02fe5ebea5 73 #define DEBUG(...) { debug_userial.printf(__VA_ARGS__); }
prussell 0:0217a862b047 74 #else
prussell 6:5b6fb35b4450 75 #define DEBUG(...) //Do Nothing, DEBUG() lines are ignored
prussell 0:0217a862b047 76 #endif
prussell 0:0217a862b047 77
prussell 2:c77c2b06d604 78 //========== This Section is to Create Debug output showing variable prep before main() ==========
prussell 2:c77c2b06d604 79 bool u8_prep_dummy(void) {
prussell 6:5b6fb35b4450 80 DEBUG("\n\nBLE: ____Prep Memory____\n"); //May comment this out if none of the following Objects/Classes call initiator functions with debug
prussell 2:c77c2b06d604 81 return true;
prussell 2:c77c2b06d604 82 }
prussell 2:c77c2b06d604 83 const bool bPrep = u8_prep_dummy();
prussell 2:c77c2b06d604 84
prussell 11:7d02fe5ebea5 85 //========== IO Hardware: Buttons, LEDs, PWM ==========
prussell 11:7d02fe5ebea5 86 // Inputs:
prussell 11:7d02fe5ebea5 87 DigitalIn bB1in(BUTTON1); //if(bB1in){}
prussell 11:7d02fe5ebea5 88 DigitalIn bB2in(BUTTON2); //if(bB2in){}
prussell 11:7d02fe5ebea5 89 InterruptIn B1int(BUTTON1); //B1int.rise(&onB1rise);
prussell 11:7d02fe5ebea5 90 InterruptIn B2int(BUTTON2); //B1int.fall(&onB1fall);
prussell 11:7d02fe5ebea5 91
prussell 11:7d02fe5ebea5 92 // Outputs:
prussell 11:7d02fe5ebea5 93 //DigitalOut bL1out(LED1); // Direct LED1 drive
prussell 11:7d02fe5ebea5 94 //DigitalOut bL2out(LED2); // Direct LED2 drive
prussell 11:7d02fe5ebea5 95 PwmOut fL1pwm(LED1); float fL1level = 0.1; // PWM LED1, brightness=float(0.0~1.0)
prussell 11:7d02fe5ebea5 96 PwmOut fL2pwm(LED2); float fL2level = 0.1; // PWM LED2, brightness=float(0.0~1.0)
prussell 11:7d02fe5ebea5 97
prussell 11:7d02fe5ebea5 98 // onButton Callbacks for InterruptIn
prussell 11:7d02fe5ebea5 99 // *When direct driving hardware consider adjusting drive within the onCallback to reduce response time
prussell 11:7d02fe5ebea5 100 //TODO: Check need of volatile for changes in interrupts affecting variables in non-interrupt code?
prussell 11:7d02fe5ebea5 101 volatile uint8_t uB1rise; void onB1rise(void){ uB1rise++; };// Flag Event, Counter helps detect missed events since last cleared
prussell 11:7d02fe5ebea5 102 volatile uint8_t uB1fall; void onB1fall(void){ uB1fall++; };// Flag Event, Counter helps detect missed events since last cleared
prussell 11:7d02fe5ebea5 103 volatile uint8_t uB2rise; void onB2rise(void){ uB2rise++; };// Flag Event, Counter helps detect missed events since last cleared
prussell 11:7d02fe5ebea5 104 volatile uint8_t uB2fall; void onB2fall(void){ uB2fall++; };// Flag Event, Counter helps detect missed events since last cleared
prussell 0:0217a862b047 105
prussell 0:0217a862b047 106 //==========BLE==========
prussell 11:7d02fe5ebea5 107 const static char pcDeviceName[] = "bleIOv04_pr"; //PR: Why can App nRF-MCP modify this even though flagged as Const, maybe only temporary mod till App restarts?
prussell 0:0217a862b047 108 BLEDevice ble;
prussell 10:ee3a359f7d3f 109 //Pointers to services for accesses outside main()
prussell 10:ee3a359f7d3f 110 HealthThermometerService *pServiceHTM; //tempChar.getValueAttribute().getHandle()
prussell 10:ee3a359f7d3f 111 BatteryService *pServiceBattery; //battLevel.getValueAttribute().getHandle()
prussell 8:f187ba55aed2 112 HeartRateService *pServiceHRM;
prussell 8:f187ba55aed2 113 DeviceInformationService *pServiceDeviceInfo;
prussell 8:f187ba55aed2 114 LinkLossService *pServiceLinkLoss;
prussell 10:ee3a359f7d3f 115 //x DFUService *pServiceDFU;
prussell 10:ee3a359f7d3f 116 //x UARTService *pServiceUART; //FromApp: pServiceUART->getTXCharacteristicHandle(); //ToApp: ble.updateCharacteristicValue(pServiceUART->getRXCharacteristicHandle(), pData, uLen);
prussell 10:ee3a359f7d3f 117 //x bleIOService *pServiceIO;
prussell 3:a98203f84063 118
prussell 6:5b6fb35b4450 119 //==========UUID==========
prussell 6:5b6fb35b4450 120 //UUID Info:
prussell 5:d36bbb315e31 121 // 20141213 From https://developer.bluetooth.org/community/lists/community%20discussion/flat.aspx?rootfolder=/community/lists/community+discussion/16+bit+uuid+vs.+128+uuid&folderctid=0x01200200e2f0e56e3d53004dba96bdf0c357551f
prussell 5:d36bbb315e31 122 /* All the custom GATT based services and characteristics must use a 128 bit UUID.
prussell 5:d36bbb315e31 123 The Bluetooth_Base_UUID is: 00000000-0000-1000-8000 00805F9B34FB.
prussell 5:d36bbb315e31 124 All the 16-bit Attribute UUIDs defined in the adopted specifications use the above base.
prussell 5:d36bbb315e31 125 Generating a 128 bit UUID for custom profiles: For the 128 bit UUID, please refer to The ITU-T Rec. X.667.
prussell 5:d36bbb315e31 126 You can download a free copy of ITU-T Rec. X.667 from http://www.itu.int/ITU-T/studygroups/com17/oid/X.667-E.pdf.
prussell 5:d36bbb315e31 127 In addition if you go to http://www.itu.int/ITU-T/asn1/uuid.html, you can generate a unique 128-bit UUID.
prussell 5:d36bbb315e31 128 Latency: Refer to Core Spec V4.0, Vol 3, Part F - 3.3.1, which is "Ready by Type Request".
prussell 5:d36bbb315e31 129 If you look at the PDU, you have to send 2 bytes UUID for adopted profiles and 16 bytes UUID for custom profiles.
prussell 5:d36bbb315e31 130 There is additional 14 extra bytes over head for the custom profiles for "Ready By Type Request Method"
prussell 5:d36bbb315e31 131 Note: All attribute types are effectively compared as 128-bit UUIDs, even if a 16-bit UUID is provided in this request or defined for an attribute. (See Section 3.2.1.)
prussell 10:ee3a359f7d3f 132 A suggestive alternative will be to use a notification method, (see 3.4.7.1), where you don't need the 128 bit UUID or the indication method (see 3.4.7.2) */
prussell 10:ee3a359f7d3f 133 // 16bit UUID uses: 128bit base(32 hex digits): 0000****-0000-1000-8000 00805F9B34FB (Careful to use unsigned to avoid negatives and overflows)
prussell 10:ee3a359f7d3f 134 // 32bit UUID uses: 128bit base(32 hex digits): ********-0000-1000-8000 00805F9B34FB (Careful to use unsigned to avoid negatives and overflows)
prussell 10:ee3a359f7d3f 135
prussell 10:ee3a359f7d3f 136 //UUID128 List(Only a single UUID128 can fit in advertising)
prussell 10:ee3a359f7d3f 137 //a uint8_t puUUID128_Bluetooth_Base_Rev[16] = {0xFB,0x34,0x9B,0x5F,0x80,0, 0,0x80, 0,0x10, 0,0, 0x00,0x00, 0,0};//0000****-0000-1000-8000 00805F9B34FB, where ****==UUID16
prussell 10:ee3a359f7d3f 138 //a uint8_t puUUID128_BatteryService[16] = {0xFB,0x34,0x9B,0x5F,0x80,0, 0,0x80, 0,0x10, 0,0, 0x0F,0x18, 0,0};//0000****-0000-1000-8000 00805F9B34FB, where ****==0x180F
prussell 4:976394791d7a 139
prussell 11:7d02fe5ebea5 140 //a //UUID16 List - Advertising these required by App nRF-Toolbox:HRM&HTM // Keep list short so advertizing not over 31bytes.
prussell 10:ee3a359f7d3f 141 /*static const uint16_t uuid16_list[] = { //Service List (Pre-defined standard 16bit services)
prussell 10:ee3a359f7d3f 142 // *Order here doesn't affect order in nRF-MCP Discovery of Services
prussell 10:ee3a359f7d3f 143 //BLE_UUID_GAP UUID_GENERIC_ACCESS //0x1800 //Included by Default, DeviceName, Appearance, PreferredConnectionParam
prussell 10:ee3a359f7d3f 144 //BLE_UUID_GATT UUID_GENERIC ATTRIBUTE //0x1801 //Included by Default, ServiceChanged,
prussell 10:ee3a359f7d3f 145 GattService::UUID_HEALTH_THERMOMETER_SERVICE, //0x1809 //HTM (Might need to be first for nRF App)
prussell 10:ee3a359f7d3f 146 GattService::UUID_HEART_RATE_SERVICE, //0x180D //HRM, BodyLocation, ControlPoint
prussell 10:ee3a359f7d3f 147 GattService::UUID_DEVICE_INFORMATION_SERVICE, //0x180A //sManufacturer, sModelNumber, sSerialNumber, sHWver, sFWver, sSWver
prussell 10:ee3a359f7d3f 148 GattService::UUID_BATTERY_SERVICE, //0x180F //BatteryLevel
prussell 10:ee3a359f7d3f 149 GattService::UUID_LINK_LOSS_SERVICE, //0x1803 //LinkLoss
prussell 10:ee3a359f7d3f 150 //x GattService::UUID_DFU, //0x1530 - See UARTServiceShortUUID in BLE_API:DFUService.cpp //
prussell 10:ee3a359f7d3f 151 //x GattService::UARTService, //0x0001~0x0003 - See DFUServiceShortUUID in BLE_API:UARTService.cpp //
prussell 10:ee3a359f7d3f 152 //Possibly useful:
prussell 10:ee3a359f7d3f 153 //x GattService::UUID_ALERT_NOTIFICATION_SERVICE, //0x1811
prussell 10:ee3a359f7d3f 154 //x GattService::UUID_CURRENT_TIME_SERVICE, //0x1805
prussell 10:ee3a359f7d3f 155 //x GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, //0x1812
prussell 10:ee3a359f7d3f 156 //x GattService::UUID_IMMEDIATE_ALERT_SERVICE, //0x1802
prussell 10:ee3a359f7d3f 157 //x GattService::UUID_PHONE_ALERT_STATUS_SERVICE, //0x180E
prussell 10:ee3a359f7d3f 158 //x GattService::UUID_REFERENCE_TIME_UPDATE_SERVICE, //0x1806
prussell 10:ee3a359f7d3f 159 //x GattService::UUID_SCAN_PARAMETERS_SERVICE, //0x1813
prussell 10:ee3a359f7d3f 160 };*/
prussell 2:c77c2b06d604 161
prussell 10:ee3a359f7d3f 162 //========== bleIO - A Custom Service for Handling IO with an App (GPIO,PWM,etc.)==========
prussell 10:ee3a359f7d3f 163 //UUID128 List - Note only the single Service UUID128 can fit in the advertising
prussell 9:2d11beda333f 164
prussell 9:2d11beda333f 165 // Custom UUID128 base for this service and its characteristics (from: http://www.itu.int/ITU-T/asn1/uuid.html)
prussell 9:2d11beda333f 166 // **This is a temporary UUID for testing by anyone, should be replaced before proceeding to real product.**
prussell 9:2d11beda333f 167 // Can check that UUID is correctly entered by viewing with Android App: nRF-Master Control panel
prussell 9:2d11beda333f 168 // UUID128 can be generated using: http://www.itu.int/ITU-T/asn1/uuid.html
prussell 9:2d11beda333f 169 // 20141218PR: [4d3281c0-86d1-11e4-b084-0002a5d5c51b] == OID[2.25.102612802202735078710424021169255859483]
prussell 10:ee3a359f7d3f 170 // Base UUID128 (1 digit modified):{0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x1*};// NormalOrder
prussell 10:ee3a359f7d3f 171 uint8_t puUUID128_IOService[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x10};// Service UUID
prussell 11:7d02fe5ebea5 172 uint8_t puUUID128_IOAdvertise[16]= {0x10,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Advertising Service UUID (=ReversedOrder)
prussell 10:ee3a359f7d3f 173 // Characteristic UUID: Initially using generic blocks of IO data that may be manually dividied up into Buttons, LEDs, PWM, ADC, etc.
prussell 10:ee3a359f7d3f 174 uint8_t puUUID128_IOw8[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x11};// Write[8byte] to mbed from phone
prussell 10:ee3a359f7d3f 175 uint8_t puUUID128_IOr4[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x12};// Read[4byte] from mbed to phone
prussell 10:ee3a359f7d3f 176 uint8_t puUUID128_IOn4[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x13};// Notify[4byte] from mbed to phone
prussell 10:ee3a359f7d3f 177 // Alternately can have a characterostic for each discrete item, such as:
prussell 10:ee3a359f7d3f 178 //a uint8_t puUUID128_bleConfig32[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Configuration 32bits
prussell 10:ee3a359f7d3f 179 //a uint8_t puUUID128_bleOut1[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Output 1bit
prussell 10:ee3a359f7d3f 180 //a uint8_t puUUID128_bleOutPWM100[16]= {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Output PWM (Range 0~100%)
prussell 10:ee3a359f7d3f 181 //a uint8_t puUUID128_bleOutPWM256[16]= {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Output PWM (256 levels, 0~255)
prussell 10:ee3a359f7d3f 182 //a uint8_t puUUID128_bleIn1[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Input 1bit
prussell 10:ee3a359f7d3f 183 //a uint8_t puUUID128_bleInADC12[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Input ADC 12bit
prussell 10:ee3a359f7d3f 184
prussell 10:ee3a359f7d3f 185 /* Android:
prussell 10:ee3a359f7d3f 186 //bleIO Service and Characteristics
prussell 10:ee3a359f7d3f 187 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c51b", "bleIO base");
prussell 10:ee3a359f7d3f 188 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c510", "bleIO Service");
prussell 10:ee3a359f7d3f 189 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c511", "bleIO w8");
prussell 10:ee3a359f7d3f 190 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c512", "bleIO r4");
prussell 10:ee3a359f7d3f 191 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c513", "bleIO n4");
prussell 9:2d11beda333f 192
prussell 10:ee3a359f7d3f 193 attributes.put("1bc5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev base");
prussell 10:ee3a359f7d3f 194 attributes.put("10c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev Service");
prussell 10:ee3a359f7d3f 195 attributes.put("11c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev w8");
prussell 10:ee3a359f7d3f 196 attributes.put("12c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev r4");
prussell 10:ee3a359f7d3f 197 attributes.put("13c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev n4");
prussell 10:ee3a359f7d3f 198 */
prussell 10:ee3a359f7d3f 199
prussell 10:ee3a359f7d3f 200 // Characteristic Value Storage (with Initial values, should match actual data type transferred, doesn't have to be byte array):
prussell 10:ee3a359f7d3f 201 uint8_t pIOw8[8] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17}; //Value Storage for Characteristic Write8
prussell 10:ee3a359f7d3f 202 uint8_t pIOr4[4] = {0x20,0x21,0x22,0x23}; //Value Storage for Characteristic Read4
prussell 10:ee3a359f7d3f 203 uint8_t pIOn4[4] = {0x30,0x31,0x32,0x33}; //Value Storage for Characteristic Notify4
prussell 9:2d11beda333f 204
prussell 9:2d11beda333f 205 //x GattCharacteristic::GattAttribute *descriptors[]; //How Set Characteristic Descriptors? Maybe in the App is easier?
prussell 9:2d11beda333f 206
prussell 10:ee3a359f7d3f 207 //IO Characteristics: //GattCharacteristics xx(*UUID, *Payload=*Value, LenInit, LenMax, Properties)
prussell 10:ee3a359f7d3f 208 //GattCharacteristic IOw8(puUUID128_IOw8, pIOw8, sizeof(pIOw8), sizeof(pIOw8), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
prussell 10:ee3a359f7d3f 209 GattCharacteristic IOw8(puUUID128_IOw8, pIOw8, sizeof(pIOw8), sizeof(pIOw8), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);// Allow Host to read back last setting
prussell 10:ee3a359f7d3f 210 GattCharacteristic IOr4(puUUID128_IOr4, pIOr4, sizeof(pIOr4), sizeof(pIOr4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); // Host can manually request
prussell 10:ee3a359f7d3f 211 //GattCharacteristic IOn4(puUUID128_IOn4, pIOn4, sizeof(pIOn4), sizeof(pIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
prussell 10:ee3a359f7d3f 212 GattCharacteristic IOn4(puUUID128_IOn4, pIOn4, sizeof(pIOn4), sizeof(pIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); // Host is notified of changes
prussell 10:ee3a359f7d3f 213 GattCharacteristic *pCustomCharacteristics[] = {&IOw8, &IOr4, &IOn4};//Table of Pointers to Characteristics
prussell 10:ee3a359f7d3f 214
prussell 10:ee3a359f7d3f 215 //IO Service: //GattService::GattService(*UUID, *GattCharacteristics, (numCharacteristics) ) :
prussell 10:ee3a359f7d3f 216 GattService ServiceIO(puUUID128_IOService, pCustomCharacteristics, (sizeof(pCustomCharacteristics)/sizeof(pCustomCharacteristics[0])));
prussell 10:ee3a359f7d3f 217 GattService *pServiceIO= &ServiceIO; // Pointer to Service
prussell 9:2d11beda333f 218
prussell 11:7d02fe5ebea5 219 //a Option: Allow device to modify its settings and inform host (Some devices may be able to self-modify host written settings)
prussell 10:ee3a359f7d3f 220 /*void vUpdate_IOw8(void) // Handle in device changes to w8 characteristic
prussell 9:2d11beda333f 221 {
prussell 10:ee3a359f7d3f 222 static uint8_t uw8; //Level: 0..2
prussell 10:ee3a359f7d3f 223 switch(++uw8){
prussell 10:ee3a359f7d3f 224 case 2: memcpy(pIOw8, "w2w2w2wc", sizeof(pIOw8)); DEBUG(" w8c"); break;
prussell 10:ee3a359f7d3f 225 case 1: memcpy(pIOw8, "w1w1w1wb", sizeof(pIOw8)); DEBUG(" w8b"); break;
prussell 10:ee3a359f7d3f 226 default: uw8=0; memcpy(pIOw8, "w0w0w0wa", sizeof(pIOw8)); DEBUG(" w8a"); break;
prussell 9:2d11beda333f 227 }
prussell 10:ee3a359f7d3f 228 //ble.updateCharacteristicValue(uint16_t handle, const uint8_t *value, uint16_t size, bool localOnly)
prussell 10:ee3a359f7d3f 229 ble.updateCharacteristicValue(IOw8.getValueHandle(), pIOw8, sizeof(pIOw8));
prussell 9:2d11beda333f 230 //pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE
prussell 10:ee3a359f7d3f 231 }*/
prussell 10:ee3a359f7d3f 232
prussell 11:7d02fe5ebea5 233 void vUpdate_IOr4(void) // Handle device updates r4 characteristics (Without Notify, Host must poll for these)
prussell 11:7d02fe5ebea5 234 { // For out project this would be: Temperature, Version Info, etc.
prussell 10:ee3a359f7d3f 235 static uint8_t ur4; //Level: 0..2
prussell 10:ee3a359f7d3f 236 switch(++ur4){
prussell 10:ee3a359f7d3f 237 case 2: memcpy(pIOr4, "r2r2", sizeof(pIOr4)); DEBUG(" r4c"); break;
prussell 10:ee3a359f7d3f 238 case 1: memcpy(pIOr4, "r1r1", sizeof(pIOr4)); DEBUG(" r4b"); break;
prussell 10:ee3a359f7d3f 239 default: ur4=0; memcpy(pIOr4, "r0r0", sizeof(pIOr4)); DEBUG(" r4a"); break;
prussell 10:ee3a359f7d3f 240 }
prussell 10:ee3a359f7d3f 241 ble.updateCharacteristicValue(IOr4.getValueHandle(), pIOr4, sizeof(pIOr4));
prussell 9:2d11beda333f 242 }
prussell 9:2d11beda333f 243
prussell 11:7d02fe5ebea5 244 void vUpdate_IOn4(void) // Handle device updates n4 characteristics (With notify)
prussell 11:7d02fe5ebea5 245 { // For our Device this would be: Dispenser count changes, Button Activity, Empty warning, Timeouts, etc.
prussell 10:ee3a359f7d3f 246 static uint8_t un4; //Level: 0..2
prussell 10:ee3a359f7d3f 247 switch(++un4){
prussell 10:ee3a359f7d3f 248 case 2: memcpy(pIOn4, "n2n2", sizeof(pIOr4)); DEBUG(" n4c"); break;
prussell 10:ee3a359f7d3f 249 case 1: memcpy(pIOn4, "n1n1", sizeof(pIOr4)); DEBUG(" n4b"); break;
prussell 10:ee3a359f7d3f 250 default: un4=0; memcpy(pIOn4, "n0n0", sizeof(pIOr4)); DEBUG(" n4a"); break;
prussell 9:2d11beda333f 251 }
prussell 10:ee3a359f7d3f 252 ble.updateCharacteristicValue(IOn4.getValueHandle(), pIOn4, sizeof(pIOn4));
prussell 9:2d11beda333f 253 }
prussell 9:2d11beda333f 254
prussell 0:0217a862b047 255 //==========Functions:BLE==========
prussell 0:0217a862b047 256 void Callback_BLE_onTimeout(void)
prussell 10:ee3a359f7d3f 257 { //PR: Haven't seen this, even when phone moved out of range and events occur like OnDisconnect(Reason0x08) or LinkLoss
prussell 10:ee3a359f7d3f 258 DEBUG("\nBLEi: Callback_BLE_onTimeout()\n" );
prussell 0:0217a862b047 259
prussell 0:0217a862b047 260 //DEBUG("\nBLE:Callback_BLE_onTimeout(), Restarting Advertising\n" );
prussell 0:0217a862b047 261 //ble.startAdvertising();
prussell 0:0217a862b047 262 }
prussell 0:0217a862b047 263
prussell 0:0217a862b047 264 void Callback_BLE_onDisconnect(Gap::Handle_t tHandle, Gap::DisconnectionReason_t eReason)
prussell 10:ee3a359f7d3f 265 { //PR: onDisconnect(Reason:8) occured after ~20Tx with no onDataSent() after phone moved out of range
prussell 0:0217a862b047 266
prussell 0:0217a862b047 267 // REMOTE_USER_TERMINATED_CONNECTION = 0x13 = 19,
prussell 0:0217a862b047 268 // LOCAL_HOST_TERMINATED_CONNECTION = 0x16 = 22,
prussell 0:0217a862b047 269 // CONN_INTERVAL_UNACCEPTABLE = 0x3B = 59,
prussell 0:0217a862b047 270 DEBUG("\nBLEi: Callback_BLE_Disconnect(Handle:%d, eReason:0x%02x), Restarting Advertising\n",tHandle,eReason );//PR: Occurs properly when click disconnect in App nRFToolbox:HRM
prussell 0:0217a862b047 271
prussell 0:0217a862b047 272 //DEBUG("Wait10sec...\n");wait(10.0); //PR: Optional to test effect on advertising
prussell 0:0217a862b047 273 ble.startAdvertising(); // restart advertising
prussell 0:0217a862b047 274 }
prussell 0:0217a862b047 275
prussell 0:0217a862b047 276 void Callback_BLE_onConnect(Gap::Handle_t tHandle, Gap::addr_type_t ePeerAddrType, const Gap::address_t c6PeerAddr, const Gap::ConnectionParams_t *params)
prussell 0:0217a862b047 277 {
prussell 0:0217a862b047 278 DEBUG("\nBLEi: Callback_BLE_Connect(Handle:%d, eType:%d, Add:%u ...)\n", tHandle, ePeerAddrType, c6PeerAddr);
prussell 0:0217a862b047 279
prussell 11:7d02fe5ebea5 280 //x #if ENABLE_BLE_SLOW //UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL //PR: Adopted optional code never tested
prussell 11:7d02fe5ebea5 281 //x // Updating connection parameters can be attempted only after a connection has been
prussell 11:7d02fe5ebea5 282 //x // established. Please note that the ble-Central is still the final arbiter for
prussell 11:7d02fe5ebea5 283 //x // the effective parameters; the peripheral can only hope that the request is
prussell 11:7d02fe5ebea5 284 //x // honored. Please also be mindful of the constraints that might be enforced by
prussell 11:7d02fe5ebea5 285 //x // the BLE stack on the underlying controller.
prussell 11:7d02fe5ebea5 286 //x #define MIN_CONN_INTERVAL 250 // Minimum connection interval (250 ms)
prussell 11:7d02fe5ebea5 287 //x #define MAX_CONN_INTERVAL 350 // Maximum connection interval (350 ms)
prussell 11:7d02fe5ebea5 288 //x #define CONN_SUP_TIMEOUT 6000 // Connection supervisory timeout (6 seconds)
prussell 11:7d02fe5ebea5 289 //x #define SLAVE_LATENCY 4
prussell 11:7d02fe5ebea5 290 //x Gap::ConnectionParams_t tGap_conn_params;
prussell 11:7d02fe5ebea5 291 //x tGap_conn_params.minConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(MIN_CONN_INTERVAL);
prussell 11:7d02fe5ebea5 292 //x tGap_conn_params.maxConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(MAX_CONN_INTERVAL);
prussell 11:7d02fe5ebea5 293 //x tGap_conn_params.connectionSupervisionTimeout = Gap::MSEC_TO_GAP_DURATION_UNITS(CONN_SUP_TIMEOUT);
prussell 11:7d02fe5ebea5 294 //x tGap_conn_params.slaveLatency = SLAVE_LATENCY;
prussell 11:7d02fe5ebea5 295 //x ble.updateConnectionParams(tHandle, &tGap_conn_params);
prussell 11:7d02fe5ebea5 296 //x #endif // #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL
prussell 0:0217a862b047 297 }
prussell 0:0217a862b047 298
prussell 0:0217a862b047 299 static volatile bool bSent = false; //Volatile, don't optimize, changes under interrupt control
prussell 0:0217a862b047 300 static volatile unsigned uSentBLE;
prussell 0:0217a862b047 301 void Callback_BLE_onDataSent(unsigned uSent){
prussell 0:0217a862b047 302 uSentBLE=uSent;
prussell 10:ee3a359f7d3f 303 DEBUG("BLEi: SentI(%u)", uSentBLE); //TODO: PR: Why uSent always "1", expected it to match sent bytes length
prussell 0:0217a862b047 304 bSent = true;
prussell 10:ee3a359f7d3f 305 //FYI: App nRF-MCP doesn't cause onDataSent(), while App nRF-ToolBox:HRM does cause onDataSent()
prussell 10:ee3a359f7d3f 306 //TBD: onDataSent() maybe only occuring when confirmed receive by phone, as onDataSent() didn't happen when phone moved out of range.
prussell 0:0217a862b047 307 }
prussell 0:0217a862b047 308
prussell 0:0217a862b047 309 void Callback_BLE_onDataWritten(const GattCharacteristicWriteCBParams *pParams)
prussell 9:2d11beda333f 310 { // This occurs when use nRF-MCP to save a new Characteristic Value (SingleUpASrrowIcon) such as New Heart Rate Control Point (unsent if incorrect length)
prussell 9:2d11beda333f 311 GattAttribute::Handle_t tHandle=pParams->charHandle;
prussell 0:0217a862b047 312
prussell 0:0217a862b047 313 //Warning: *data may not be NULL terminated
prussell 9:2d11beda333f 314 DEBUG("\nBLEi: Callback_BLE_onDataWritten(Handle:%d, eOp:%d, uOffset:%u uLen:%u Data0[0x%02x]=Data[%*s]\n", tHandle, pParams->op, pParams->offset, pParams->len, (char)(pParams->data[0]), pParams->len, pParams->data);
prussell 9:2d11beda333f 315
prussell 10:ee3a359f7d3f 316 /* //TODO: Add check of op type with tHandle check: switch(pParams->op){ case: GATTS_CHAR_OP_WRITE_REQ:}...
prussell 10:ee3a359f7d3f 317 switch(pParams->op){
prussell 10:ee3a359f7d3f 318 case GATTS_CHAR_OP_INVALID:
prussell 10:ee3a359f7d3f 319 case GATTS_CHAR_OP_WRITE_REQ
prussell 10:ee3a359f7d3f 320 case GATTS_CHAR_OP_WRITE_CMD
prussell 10:ee3a359f7d3f 321 case GATTS_CHAR_OP_SIGN_WRITE_CMD //< Signed Write Command
prussell 10:ee3a359f7d3f 322 case GATTS_CHAR_OP_PREP_WRITE_REQ //< Prepare Write Request
prussell 10:ee3a359f7d3f 323 case GATTS_CHAR_OP_EXEC_WRITE_REQ_CANCEL //< Execute Write Request: Cancel all prepared writes
prussell 10:ee3a359f7d3f 324 case GATTS_CHAR_OP_EXEC_WRITE_REQ_NOW //< Execute Write Request: Immediately execute all prepared writes
prussell 10:ee3a359f7d3f 325 default: Error?
prussell 10:ee3a359f7d3f 326 }*/
prussell 9:2d11beda333f 327
prussell 10:ee3a359f7d3f 328 //a These are equivalent ways of accessing characteristic handles:
prussell 10:ee3a359f7d3f 329 //a if (pParams->charHandle == IOw8.getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); }
prussell 10:ee3a359f7d3f 330 //a if (pParams->charHandle == ServiceIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); }
prussell 10:ee3a359f7d3f 331 //a if (IOw8.getValueHandle() == ServiceIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle equivalent %d\n\n", pParams->charHandle); }
prussell 9:2d11beda333f 332
prussell 9:2d11beda333f 333 //Handle Changes to Service Characteristics:
prussell 10:ee3a359f7d3f 334 if(!ble.getGapState().connected){ //Ensure BLE still connected
prussell 10:ee3a359f7d3f 335 DEBUG("BLEi: Callback_BLE_onDataWritten() while disconnected!!");
prussell 9:2d11beda333f 336 } else {
prussell 10:ee3a359f7d3f 337 uint16_t uLen;
prussell 10:ee3a359f7d3f 338 if (tHandle == IOw8.getValueHandle()) { // This occurs from Write by App nRF-MCP since has Write Property set
prussell 10:ee3a359f7d3f 339 ble.readCharacteristicValue(tHandle, pIOw8, &uLen);
prussell 10:ee3a359f7d3f 340 DEBUG(" IOw8[%d]:%02X %02X %02X %02X %02X %02X %02X %02X\n", uLen, pIOw8[0], pIOw8[1], pIOw8[2], pIOw8[3],pIOw8[4], pIOw8[5], pIOw8[6], pIOw8[7]);
prussell 11:7d02fe5ebea5 341 //TODO: Update Outputs and settings: Direct LED Control or Output Control, Or Configuration (Includes flags for: Factory Reset, Enter Test Mode, Enter Demo Mode)
prussell 11:7d02fe5ebea5 342 //} else if (tHandle == IOr4.getValueHandle()) { // Readonly, shouldn't occur
prussell 10:ee3a359f7d3f 343 // ble.readCharacteristicValue(tHandle, pIOr4, &uLen);
prussell 10:ee3a359f7d3f 344 // DEBUG(" IOr4[%d]:%02X %02X %02X %02X\n", uLen, pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]);
prussell 10:ee3a359f7d3f 345 // //TODO: Update Outputs
prussell 11:7d02fe5ebea5 346 //} else if (tHandle == IOn4.getValueHandle()) { // Readonly, shouldn't occur
prussell 10:ee3a359f7d3f 347 // ble.readCharacteristicValue(tHandle, pIOn4, &uLen);
prussell 10:ee3a359f7d3f 348 // DEBUG(" IOn4[%d]:%02X %02X %02X %02X\n", uLen, pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]);
prussell 10:ee3a359f7d3f 349 // //TODO: Update Outputs
prussell 10:ee3a359f7d3f 350 } else {
prussell 10:ee3a359f7d3f 351 DEBUG("\n Unknown onWrite(tHandle:%d)\n\n", tHandle);
prussell 10:ee3a359f7d3f 352 }
prussell 9:2d11beda333f 353 }
prussell 8:f187ba55aed2 354
prussell 10:ee3a359f7d3f 355 //LinkLoss:
prussell 10:ee3a359f7d3f 356 //x pServiceLinkLoss->setAlertLevel( AlertLevel_t newLevel )
prussell 10:ee3a359f7d3f 357 //x Triggered by BluetoothLEGatt sample changing Linkloss setting:
prussell 10:ee3a359f7d3f 358 //x Alert=1: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x01]=Data[])
prussell 10:ee3a359f7d3f 359 //x Alert=2: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x02]=Data[])
prussell 8:f187ba55aed2 360
prussell 10:ee3a359f7d3f 361 //Characteristic Status:
prussell 10:ee3a359f7d3f 362 DEBUG(" IOw8: Handle:%d Len:%d Initial:[%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), sizeof(pIOw8), pIOw8[0], pIOw8[1], pIOw8[2], pIOw8[3],pIOw8[4], pIOw8[5], pIOw8[6], pIOw8[7]);
prussell 10:ee3a359f7d3f 363 DEBUG(" IOr4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(pIOr4), pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]);
prussell 10:ee3a359f7d3f 364 DEBUG(" IOn4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(pIOn4), pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]);
prussell 0:0217a862b047 365 }
prussell 0:0217a862b047 366
prussell 0:0217a862b047 367 void Callback_BLE_onUpdatesEnabled(Gap::Handle_t tHandle)
prussell 10:ee3a359f7d3f 368 { //Triggered when click the UpdateContinuousIcon in nRF-MCP(ThreeDownArrows)
prussell 10:ee3a359f7d3f 369 if (tHandle == IOn4.getValueHandle()) { DEBUG(" onUpdates(Handle:%d) Enabled - IOn4\n", tHandle); //This Occurs when selected by App nRF-MCP as has Notify property set
prussell 11:7d02fe5ebea5 370 //} else if (tHandle == IOr4.getValueHandle()) { DEBUG(" onUpdates(Handle:%d) Enabled - IOr4\n", tHandle); // Notify not enabled on this Characteristic
prussell 11:7d02fe5ebea5 371 //} else if (tHandle == IOw8.getValueHandle()) { DEBUG(" onUpdates(Handle:%d) Enabled - IOw8\n", tHandle); // Notify not enabled on this Characteristic
prussell 10:ee3a359f7d3f 372 } else { DEBUG(" onUpdates(Handle:%d) Enabled - Characteristic?\n", tHandle);
prussell 9:2d11beda333f 373 }
prussell 0:0217a862b047 374 }
prussell 0:0217a862b047 375
prussell 5:d36bbb315e31 376 // Adopted 20141213 from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LinkLoss/file/440ee5e8595f/main.cpp
prussell 5:d36bbb315e31 377 void Callback_BLE_onLinkLoss(LinkLossService::AlertLevel_t level)
prussell 5:d36bbb315e31 378 {
prussell 10:ee3a359f7d3f 379 printf("\nBLEi: Link Loss Alert, Level:%d\n", level);
prussell 10:ee3a359f7d3f 380 //TODO: Handle Link Loss, maybe lower power mode and/or advertising mode
prussell 5:d36bbb315e31 381 }
prussell 10:ee3a359f7d3f 382 //==========BLE:HRM==========
prussell 8:f187ba55aed2 383 //Adopted 2014Dec from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/
prussell 8:f187ba55aed2 384 uint8_t update_hrm(void)//(bool bInit)
prussell 8:f187ba55aed2 385 {
prussell 8:f187ba55aed2 386 static uint8_t u8_hrm = 100;
prussell 10:ee3a359f7d3f 387 if (++u8_hrm >= 175) {
prussell 8:f187ba55aed2 388 u8_hrm = 100;
prussell 10:ee3a359f7d3f 389 DEBUG(" HRM>100");
prussell 10:ee3a359f7d3f 390 } else {
prussell 10:ee3a359f7d3f 391 DEBUG(" HRM:%d", u8_hrm);
prussell 10:ee3a359f7d3f 392 }
prussell 8:f187ba55aed2 393 pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE
prussell 8:f187ba55aed2 394 return(u8_hrm);
prussell 8:f187ba55aed2 395 }
prussell 10:ee3a359f7d3f 396 //==========BLE:HTM(Using nRF Internal Temperature)==========
prussell 10:ee3a359f7d3f 397 // *If not using nRF51822 IC then change this to a simple counter like BLE:Battery section
prussell 10:ee3a359f7d3f 398 // Adopted 2014Dec from: https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/
prussell 8:f187ba55aed2 399 // Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml
prussell 8:f187ba55aed2 400 // HTM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml
prussell 9:2d11beda333f 401 //****Although nRF-MCP displays float OK, the HTM Apps don't, it is likely IEEE format required like in the original project: BLE_HTM_by_InTempSensr
prussell 8:f187ba55aed2 402 float update_htm(void)
prussell 8:f187ba55aed2 403 {
prussell 8:f187ba55aed2 404 //static float fTemperature = 0;//-123.456;
prussell 8:f187ba55aed2 405 int32_t i32_temp;
prussell 8:f187ba55aed2 406 sd_temp_get(&i32_temp); //Read the nRF Internal Temperature (Die in 0.25'C steps, Counting From TBD), TODO:Check Scaling
prussell 8:f187ba55aed2 407 float fTemperature = (float(i32_temp)/4.0) - 16.0; // Scale&Shift (0.25'C from -16'C?)
prussell 8:f187ba55aed2 408
prussell 8:f187ba55aed2 409 //{//Force to IEEE format to match needs of Apps like nRF-HTM and nRF-Toolbox:HTM
prussell 8:f187ba55aed2 410 // PR: Didn't work 20141213, might need 5byte style from BLE_HTM_by_InTempSensr if this is really necessary. OK in nRF-MCP for now.
prussell 8:f187ba55aed2 411 // uint8_t exponent = 0xFE; //exponent is -2
prussell 8:f187ba55aed2 412 // uint32_t mantissa = (uint32_t)(fTemperature*100);
prussell 8:f187ba55aed2 413 // uint32_t temp_ieee11073 = ((((uint32_t)exponent) << 24) | (mantissa)); //Note: Assumes Mantissa within 24bits
prussell 8:f187ba55aed2 414 // memcpy(((uint8_t*)&fTemperature)+1, (uint8_t*)&temp_ieee11073, 4); //Overwrite with IEEE format float
prussell 8:f187ba55aed2 415 //}
prussell 8:f187ba55aed2 416
prussell 8:f187ba55aed2 417 pServiceHTM->updateTemperature( fTemperature );// Update Characteristic so sent by BLE
prussell 10:ee3a359f7d3f 418 DEBUG(" HTM%03d==%2.2f", i32_temp, fTemperature);
prussell 8:f187ba55aed2 419 return(fTemperature);
prussell 8:f187ba55aed2 420 }
prussell 10:ee3a359f7d3f 421 //==========BLE:Battery==========
prussell 8:f187ba55aed2 422 uint8_t update_batt(void)
prussell 8:f187ba55aed2 423 {
prussell 8:f187ba55aed2 424 static uint8_t u8_BattPercent=33; //Level: 0..100%
prussell 8:f187ba55aed2 425 u8_BattPercent <= 50 ? u8_BattPercent=100 : u8_BattPercent--; // Simulate Battery Decay
prussell 8:f187ba55aed2 426 pServiceBattery->updateBatteryLevel( u8_BattPercent ); // Update Characteristic so sent by BLE
prussell 10:ee3a359f7d3f 427 DEBUG(" Batt%03d%%", u8_BattPercent);
prussell 8:f187ba55aed2 428 return(u8_BattPercent);
prussell 8:f187ba55aed2 429 }
prussell 9:2d11beda333f 430
prussell 10:ee3a359f7d3f 431 //==========Functions:Timer==========
prussell 11:7d02fe5ebea5 432 static volatile uint8_t uTicker1;//Volatile, don't optimize, changes under interrupt control
prussell 10:ee3a359f7d3f 433 void CallbackTicker1(void)
prussell 10:ee3a359f7d3f 434 {
prussell 11:7d02fe5ebea5 435 static uint32_t u32_Counter; // Counter for checking Timing
prussell 11:7d02fe5ebea5 436 DEBUG("\nBLEi: Ticker(%04u) ", ++u32_Counter);
prussell 11:7d02fe5ebea5 437 fL1level+=0.1; if (fL1level>0.5){fL1level = 0.1;}; fL1pwm=fL1level;//PR: Ramp Blink
prussell 11:7d02fe5ebea5 438 uTicker1++; // Flag event, using counter so can detect missed events
prussell 10:ee3a359f7d3f 439 }
prussell 11:7d02fe5ebea5 440
prussell 0:0217a862b047 441 //==========main==========
prussell 0:0217a862b047 442 int main(void)
prussell 0:0217a862b047 443 {
prussell 11:7d02fe5ebea5 444 fL1level = 1; fL1pwm = fL1level;//Start LED1=OnMax
prussell 11:7d02fe5ebea5 445 fL2level = 1; fL2pwm = fL2level;//Start LED2=OnMax
prussell 6:5b6fb35b4450 446
prussell 6:5b6fb35b4450 447 //Restart TeraTerm just before Pressing Reset on mbed, 9600-8N1(No Flow Control)
prussell 6:5b6fb35b4450 448 DEBUG("\nBLE: ___%s___\n", pcDeviceName);
prussell 11:7d02fe5ebea5 449 DEBUG(" Built:[ %s %s ] Compiler:[ %s ]\n", __DATE__, __TIME__, __VERSION__);
prussell 8:f187ba55aed2 450 DEBUG("BLE: Connect App for Data: nRF-MCP, nRF-Toolbox:HRM/HTM, Android Sample BluetoothLeGatt, etc.\n");
prussell 8:f187ba55aed2 451 DEBUG("BLE: BluetoothLeGatt: App->mbed: LinkLoss->AlertLevel(UpArrow)\n");
prussell 8:f187ba55aed2 452 DEBUG("BLE: BluetoothLeGatt: App->mbed: BatteryService->BatteryLevel(DownArrow)\n");
prussell 8:f187ba55aed2 453 DEBUG("BLE: BluetoothLeGatt: mbed->App: BatteryService->BatteryLevel->EnableNotify(ThreeDownArrows), Also App Acks the send=DEBUG('SentI')\n");
prussell 0:0217a862b047 454
prussell 0:0217a862b047 455 Ticker ticker1; //PR: Timer Object(Structure)
prussell 0:0217a862b047 456 ticker1.attach(CallbackTicker1, 2.0); //PR: Timer Handler, Float=PeriodSeconds
prussell 0:0217a862b047 457
prussell 3:a98203f84063 458 //BLE1: Setup BLE Service (and event actions) //TODO: Check for services declared before main() - Is that OK?
prussell 3:a98203f84063 459 DEBUG("BLE: Setup BLE\n");
prussell 0:0217a862b047 460 ble.init();
prussell 0:0217a862b047 461 ble.onDisconnection(Callback_BLE_onDisconnect);
prussell 0:0217a862b047 462 ble.onConnection(Callback_BLE_onConnect); //PR: Not required if no actions enabled, enabled now just for debug printf()
prussell 0:0217a862b047 463 ble.onDataSent(Callback_BLE_onDataSent);
prussell 0:0217a862b047 464 ble.onDataWritten(Callback_BLE_onDataWritten);
prussell 0:0217a862b047 465 ble.onTimeout(Callback_BLE_onTimeout);
prussell 0:0217a862b047 466 ble.onUpdatesEnabled(Callback_BLE_onUpdatesEnabled);
prussell 8:f187ba55aed2 467
prussell 6:5b6fb35b4450 468 //BLE2: Setup Services (with their initial values and options)
prussell 3:a98203f84063 469 DEBUG("BLE: Setup Services\n");
prussell 4:976394791d7a 470 // *Order here affects order in nRF-MCP Discovery of Services
prussell 8:f187ba55aed2 471 DeviceInformationService ServiceDeviceInfo(ble, "Maker", pcDeviceName, "sn1234", "hw00", "fw00", "sw00");//(BLEDevice), pcManufacturer, pcModelNumber, pcSerialNumber, pcHWver, pcFWver, pcSWver
prussell 9:2d11beda333f 472 pServiceDeviceInfo = &ServiceDeviceInfo; //DEBUG(" Handle Service DeviceInfo:%d\n", ServiceDeviceInfo.XXXgetHandle());
prussell 8:f187ba55aed2 473 LinkLossService ServiceLinkLoss(ble, Callback_BLE_onLinkLoss, LinkLossService::HIGH_ALERT); //New20141213, TBD
prussell 8:f187ba55aed2 474 pServiceLinkLoss = &ServiceLinkLoss;
prussell 8:f187ba55aed2 475 BatteryService ServiceBattery(ble, 10);
prussell 8:f187ba55aed2 476 pServiceBattery = &ServiceBattery;
prussell 8:f187ba55aed2 477 HeartRateService ServiceHRM(ble, (uint8_t)111, HeartRateService::LOCATION_FINGER);
prussell 8:f187ba55aed2 478 pServiceHRM = &ServiceHRM;
prussell 8:f187ba55aed2 479 HealthThermometerService ServiceHTM(ble, 33.3, HealthThermometerService::LOCATION_EAR);
prussell 8:f187ba55aed2 480 pServiceHTM = &ServiceHTM;
prussell 8:f187ba55aed2 481 //UARTService ServiceUART(ble);
prussell 8:f187ba55aed2 482 // pServiceUART = &ServiceUART;
prussell 5:d36bbb315e31 483
prussell 9:2d11beda333f 484 //Start CustomIO Service:
prussell 10:ee3a359f7d3f 485 ble.addService(ServiceIO); //Pointer: pServiceIO
prussell 10:ee3a359f7d3f 486 DEBUG(" IOw8: Handle:%d Len:%d Initial:[%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), sizeof(pIOw8), pIOw8[0], pIOw8[1], pIOw8[2], pIOw8[3],pIOw8[4], pIOw8[5], pIOw8[6], pIOw8[7]);
prussell 10:ee3a359f7d3f 487 DEBUG(" IOr4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(pIOr4), pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]);
prussell 10:ee3a359f7d3f 488 DEBUG(" IOn4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(pIOn4), pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]);
prussell 10:ee3a359f7d3f 489 //TODO: Ensure outputs in correct Initial state
prussell 9:2d11beda333f 490
prussell 3:a98203f84063 491 //BLE3: Setup advertising
prussell 10:ee3a359f7d3f 492 // Note: the Advertising payload is limited to 31bytes, so careful don't overflow this. Multiple UUID16's or a single UUID128 are possible in advertising.
prussell 10:ee3a359f7d3f 493 // If there isn't enough space, then the accumulate payload won't add that block to the advertising, it won't add a partial block, and if name last then device shows as unnamed in App.
prussell 9:2d11beda333f 494 DEBUG("BLE: Setup Advertising - Apps like HRM/HTM/UART require matching UUID\n");
prussell 6:5b6fb35b4450 495 ble.clearAdvertisingPayload(); //Prep
prussell 6:5b6fb35b4450 496 ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000)); //PR: Advertise 1sec (1Hz)
prussell 6:5b6fb35b4450 497 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); //PR: TODO: To Study
prussell 6:5b6fb35b4450 498 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); //PR: BLE Only (Option:LE_LIMITED_DISCOVERABLE)
prussell 6:5b6fb35b4450 499 // Does "LE_GENERAL_DISCOVERABLE" affect whether UUID needs to be advertised to discover services?
prussell 10:ee3a359f7d3f 500 //a ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // Multiple UUID16 for Standard Services
prussell 11:7d02fe5ebea5 501 ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_IOAdvertise, sizeof(puUUID128_IOAdvertise)); //Single UUID128 for primary Service
prussell 11:7d02fe5ebea5 502
prussell 10:ee3a359f7d3f 503 //? PR: I'm not sure what these lines do, they were inherited from an example:
prussell 10:ee3a359f7d3f 504 //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
prussell 10:ee3a359f7d3f 505 //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER);
prussell 9:2d11beda333f 506
prussell 6:5b6fb35b4450 507 // Add LocalName last so if Advertising too long will easily see as Name won't be available for the device.
prussell 6:5b6fb35b4450 508 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)pcDeviceName, sizeof(pcDeviceName)-1);//PR: LocalName (No NULL)
prussell 6:5b6fb35b4450 509 ble.startAdvertising();
prussell 3:a98203f84063 510
prussell 10:ee3a359f7d3f 511 //Example Advertising (Max 31 bytes): 0x01020304050607080910111213141516171819202122232425262728293031
prussell 6:5b6fb35b4450 512 // Example Raw Advertising caught by App nRF-MCP: 0x020106070309180A180D180909626C655052763034
prussell 6:5b6fb35b4450 513 // = Len02 Type01 Value06 (Flags: BREDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE)
prussell 6:5b6fb35b4450 514 // = Len07 Type03 Values: 0918 0A18 0D18 (ListUUID16: 1809 180A 180D )
prussell 6:5b6fb35b4450 515 // = Len09 Type09 Values: 62 6C 65 50 52 76 30 34 (LocalName = "blePRv04")
prussell 6:5b6fb35b4450 516 // Example Raw Advertising caught by App nRF-MCP: 0x0201061106FB349B5F80000080001000000F180000070209180A180D18
prussell 6:5b6fb35b4450 517 // = Len02 Type01 Value06 (Flags: BREDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE)
prussell 6:5b6fb35b4450 518 // = Len11 Type06 ValueFB349B5F8000008000100000_0F18_0000 (UUID128: BluetoothBattery=0x180F)
prussell 6:5b6fb35b4450 519 // = Len07 Type02 Value 0918 0A18 0D18 (UUID16: 1809 180A 180D )
prussell 6:5b6fb35b4450 520 // = LocalName field wasn't appended as insufficient space, so Device won't be named when scanning.
prussell 0:0217a862b047 521
prussell 11:7d02fe5ebea5 522 // Setup InterruptIn for Buttons
prussell 11:7d02fe5ebea5 523 DEBUG("BLE: Enable Button Interrupts\n");
prussell 11:7d02fe5ebea5 524 B1int.fall(&onB1fall);// Button1 Press
prussell 11:7d02fe5ebea5 525 B1int.rise(&onB1rise);// Button1 Release
prussell 11:7d02fe5ebea5 526 B2int.fall(&onB2fall);// Button2 Press
prussell 11:7d02fe5ebea5 527 B2int.rise(&onB2rise);// Button2 Release
prussell 11:7d02fe5ebea5 528
prussell 0:0217a862b047 529 DEBUG("BLE: Main Loop\n");
prussell 0:0217a862b047 530 uint32_t u32_wakeevents=0, u32_wakelast=0; // Counter&Register for tracking Wake Events (to see monitor their Frequency)
prussell 0:0217a862b047 531 while (true) {
prussell 11:7d02fe5ebea5 532 if (uTicker1 && ble.getGapState().connected) { //If Ticker1 and Connected then Update Appropriate Data
prussell 11:7d02fe5ebea5 533 uTicker1 = 0; // Clear flag for next Ticker1, see CallbackTicker1(), TODO: log if missed any ticks
prussell 3:a98203f84063 534
prussell 3:a98203f84063 535 // Read Sensors, and update matching Service Characteristics (only if connected)
prussell 4:976394791d7a 536 // *Order here doesn't affect order in nRF-MCP Discovery of Services
prussell 6:5b6fb35b4450 537 //TBD: Maybe save power by not Tx unless enabled by App?
prussell 8:f187ba55aed2 538 // The Services are discovered if they were setup/started (They don't need update to be discovered)
prussell 8:f187ba55aed2 539 update_htm();
prussell 8:f187ba55aed2 540 update_hrm();
prussell 8:f187ba55aed2 541 update_batt();
prussell 8:f187ba55aed2 542
prussell 10:ee3a359f7d3f 543 //a vUpdate_IOw8();
prussell 10:ee3a359f7d3f 544 vUpdate_IOr4();
prussell 10:ee3a359f7d3f 545 vUpdate_IOn4();
prussell 9:2d11beda333f 546
prussell 10:ee3a359f7d3f 547 DEBUG(" BLE:Wakes:%u,Delta:%u ", u32_wakeevents, u32_wakeevents-u32_wakelast); //For Evaluating Timing
prussell 3:a98203f84063 548 u32_wakelast = u32_wakeevents;
prussell 11:7d02fe5ebea5 549 } else if (uTicker1) {
prussell 11:7d02fe5ebea5 550 uTicker1 = 0; // Clear flag for next Ticker1, see CallbackTicker1(), TODO: log if missed any ticks
prussell 3:a98203f84063 551 DEBUG("BLE: Tick while unconnected ");
prussell 0:0217a862b047 552 } else if (bSent){
prussell 0:0217a862b047 553 bSent=false; //clear flag
prussell 3:a98203f84063 554 //DEBUG("BLE: Sent %ubytes ", uSentBLE);
prussell 0:0217a862b047 555 } else {
prussell 11:7d02fe5ebea5 556 //DEBUG("BLE: Wait for Event\n\r"); //x Debug output here causes endless wakes from sleep()
prussell 11:7d02fe5ebea5 557 ble.waitForEvent(); //PR: Like sleep() with ble handling. TODO: Handle Error return
prussell 11:7d02fe5ebea5 558 fL2level+=0.25; if (fL2level>0.5){fL2level = 0.0;}; fL2pwm=fL2level;//PR: Ramp Blink
prussell 0:0217a862b047 559 u32_wakeevents++; //PR: Count events for frequency monitoring (20141207PR: nRF51822 mbed HRM = 50Hz)
prussell 0:0217a862b047 560 }
prussell 0:0217a862b047 561 }
prussell 0:0217a862b047 562 }
prussell 10:ee3a359f7d3f 563 //========== end of main.cpp ==========