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:
Mon Jan 26 17:51:53 2015 +0000
Revision:
23:78aad4e53ae2
Parent:
22:533275e76f55
Child:
25:1c6c2895f729
Works with _bleIO_v06a.zip

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 23:78aad4e53ae2 11 // - https://developer.mbed.org/users/rosterloh84/code/Buddi_Blueband (Disconnect Reasons)
prussell 0:0217a862b047 12 // - miscellaneous adopted from more samples...
prussell 0:0217a862b047 13 // Reference:
prussell 22:533275e76f55 14 // - nRF51822:
prussell 22:533275e76f55 15 // - MKit Pins: http://developer.mbed.org/users/mbed_official/code/mbed-src/file/c80ac197fa6a/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/TARGET_NRF51822_MKIT/PinNames.h
prussell 22:533275e76f55 16 // - Platform: http://developer.mbed.org/platforms/Nordic-nRF51822/
prussell 22:533275e76f55 17 // - MCU: http://developer.mbed.org/users/mbed_official/code/mbed-src/file/d2c15dda23c1/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822
prussell 0:0217a862b047 18 // - http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/
prussell 0:0217a862b047 19 // - Reference: http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/docs/tip/
prussell 0:0217a862b047 20 // - Reference: http://developer.mbed.org/teams/Bluetooth-Low-Energy/
prussell 0:0217a862b047 21 // Warnings:
prussell 0:0217a862b047 22 // - As of 20141210 it is necessary to use Android App [nRF-Master Control Panel] to ensure any previous connected
prussell 0:0217a862b047 23 // code on mkit is properly Disconnected before trying to connect other Android nRF Apps (nRFToolbox, nRF UART 2.0, etc.).
prussell 22:533275e76f55 24 // As UART device doesn't offer disconnect you may need to load a non-uart sample, then use MCP to connect and discoonect, to clear the link.
prussell 0:0217a862b047 25 // Notes:
prussell 10:ee3a359f7d3f 26 // - 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 27 // - onDisconnect(Reason:8) occured after ~20Tx with no onDataSent() after phone moved out of range, OnTimeout didn't occur at all.
prussell 0:0217a862b047 28 // ToDo: and ToCheck:
prussell 8:f187ba55aed2 29 // - 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 30 // - Re-check where voltatile needed
prussell 23:78aad4e53ae2 31 // - ToCheck(Maybe in App): device.CreateBond(), device.SetPairingConfirmation(), gatt.refresh(to clear ble cache when changing services in devlopment)
prussell 8:f187ba55aed2 32 // - 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 33 // - invalid or untested reference code commented with "//x ", feel free to delete or experiment
prussell 11:7d02fe5ebea5 34 // - valid alternate code commented with "//a " prefix to indicate alternnative is valid code
prussell 0:0217a862b047 35 //==========End of PR's Header
prussell 0:0217a862b047 36
prussell 0:0217a862b047 37 //==========Historic Licencing from original imported sample from mbed website [BLE_HeartRate] ==========
prussell 0:0217a862b047 38 //From: http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/
prussell 0:0217a862b047 39 /* mbed Microcontroller Library
prussell 0:0217a862b047 40 * Copyright (c) 2006-2013 ARM Limited
prussell 0:0217a862b047 41 *
prussell 0:0217a862b047 42 * Licensed under the Apache License, Version 2.0 (the "License");
prussell 0:0217a862b047 43 * you may not use this file except in compliance with the License.
prussell 0:0217a862b047 44 * You may obtain a copy of the License at
prussell 0:0217a862b047 45 * http://www.apache.org/licenses/LICENSE-2.0
prussell 0:0217a862b047 46 * Unless required by applicable law or agreed to in writing, software
prussell 0:0217a862b047 47 * distributed under the License is distributed on an "AS IS" BASIS,
prussell 0:0217a862b047 48 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
prussell 0:0217a862b047 49 * See the License for the specific language governing permissions and
prussell 0:0217a862b047 50 * limitations under the License. */
prussell 0:0217a862b047 51 //==========end of Historic Licencing ==========
prussell 0:0217a862b047 52
prussell 0:0217a862b047 53
prussell 0:0217a862b047 54 //==========Compile Options==========
prussell 11:7d02fe5ebea5 55 #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 56 //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 57
prussell 0:0217a862b047 58 //==========Includes==========
prussell 9:2d11beda333f 59 #include "mbed.h" // mbed
prussell 1:4a25d917fb6a 60 #include "BLEDevice.h" // BLE
prussell 1:4a25d917fb6a 61 #include "nrf_soc.h" // nRF Internal Temperature Sensor
prussell 0:0217a862b047 62
prussell 0:0217a862b047 63 //Services
prussell 0:0217a862b047 64 #include "BatteryService.h"
prussell 0:0217a862b047 65 #include "DeviceInformationService.h"
prussell 5:d36bbb315e31 66 #include "HealthThermometerService.h"
prussell 0:0217a862b047 67 #include "HeartRateService.h"
prussell 5:d36bbb315e31 68 #include "LinkLossService.h" //TODO: How support this?
prussell 8:f187ba55aed2 69 //#include "DFUService" //TODO: DFU and FOTA Support
prussell 0:0217a862b047 70 //#include "UARTService.h" //TODO: Add a UART Channel for streaming data like logs?
prussell 0:0217a862b047 71
prussell 0:0217a862b047 72 //==========Debug Console==========
prussell 11:7d02fe5ebea5 73 #if ENABLE_uSerial
prussell 11:7d02fe5ebea5 74 //Restart TeraTerm just before Pressing Reset on mbed, Default Serual=9600-8N1(No Flow Control)
prussell 10:ee3a359f7d3f 75 //Using default baud rate to avoid issues with DEBUG in a constructor being at default baud rate before main()
prussell 11:7d02fe5ebea5 76 //TeraTerm: http://en.sourceforge.jp/projects/ttssh2/releases/
prussell 11:7d02fe5ebea5 77 Serial debug_userial(USBTX, USBRX); //PR: DebugSerialOverMbedUSB
prussell 11:7d02fe5ebea5 78 #define DEBUG(...) { debug_userial.printf(__VA_ARGS__); }
prussell 0:0217a862b047 79 #else
prussell 6:5b6fb35b4450 80 #define DEBUG(...) //Do Nothing, DEBUG() lines are ignored
prussell 0:0217a862b047 81 #endif
prussell 0:0217a862b047 82
prussell 2:c77c2b06d604 83 //========== This Section is to Create Debug output showing variable prep before main() ==========
prussell 2:c77c2b06d604 84 bool u8_prep_dummy(void) {
prussell 6:5b6fb35b4450 85 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 86 return true;
prussell 2:c77c2b06d604 87 }
prussell 2:c77c2b06d604 88 const bool bPrep = u8_prep_dummy();
prussell 2:c77c2b06d604 89
prussell 11:7d02fe5ebea5 90 //========== IO Hardware: Buttons, LEDs, PWM ==========
prussell 12:8bac5f5d3a3e 91 // Inputs: (Simultaneous DigitalIn and InteruptIn is OK)
prussell 22:533275e76f55 92 DigitalIn bB1in(BUTTON1); //nRF51822p0.16==pin22 //if(bB1in){}
prussell 22:533275e76f55 93 DigitalIn bB2in(BUTTON2); //nRF51822p0.17==pin25 //if(bB2in){}
prussell 23:78aad4e53ae2 94 InterruptIn B1int(BUTTON1); //nRF51822p0.16==pin22 //B1int.rise(&onB1rise);
prussell 23:78aad4e53ae2 95 InterruptIn B2int(BUTTON2); //nRF51822p0.17==pin25 //B1int.fall(&onB1fall);
prussell 11:7d02fe5ebea5 96
prussell 11:7d02fe5ebea5 97 // Outputs:
prussell 12:8bac5f5d3a3e 98 //a DigitalOut bL1out(LED1); // Direct LED1 drive
prussell 12:8bac5f5d3a3e 99 //a DigitalOut bL2out(LED2); // Direct LED2 drive
prussell 12:8bac5f5d3a3e 100 PwmOut fL1pwm(LED1); float fL1level = 0.1; // PWM LED1, brightness=float(0.0~1.0)
prussell 12:8bac5f5d3a3e 101 PwmOut fL2pwm(LED2); float fL2level = 0.1; // PWM LED2, brightness=float(0.0~1.0)
prussell 12:8bac5f5d3a3e 102 bool bAppControl = false; //Flag: Host has control of LEDs through BLE
prussell 11:7d02fe5ebea5 103
prussell 12:8bac5f5d3a3e 104 //a volatile uint8_t uB1rise; void onB1rise(void){ uB1rise++; };// Flag Event, Counter helps detect missed events since last cleared
prussell 12:8bac5f5d3a3e 105 //a volatile uint8_t uB1fall; void onB1fall(void){ uB1fall++; };// Flag Event, Counter helps detect missed events since last cleared
prussell 12:8bac5f5d3a3e 106 //a volatile uint8_t uB2rise; void onB2rise(void){ uB2rise++; };// Flag Event, Counter helps detect missed events since last cleared
prussell 12:8bac5f5d3a3e 107 //a volatile uint8_t uB2fall; void onB2fall(void){ uB2fall++; };// Flag Event, Counter helps detect missed events since last cleared
prussell 0:0217a862b047 108
prussell 22:533275e76f55 109 //========== ADC ==========
prussell 22:533275e76f55 110 // ADC/Analog: 20150106PR
prussell 22:533275e76f55 111 // - Options:
prussell 22:533275e76f55 112 // - AnalogIn, Default: http://developer.mbed.org/handbook/AnalogIn
prussell 22:533275e76f55 113 // - ADC Lib, http://developer.mbed.org/users/simonb/code/ADC_test/
prussell 22:533275e76f55 114 // - Fast, but burns power: http://developer.mbed.org/users/Sissors/code/FastAnalogIn/
prussell 22:533275e76f55 115 // Initially using the mbed standard: AnalogIn (Later may change when power or timing require it)
prussell 22:533275e76f55 116 // - adcX.read() returns a float, scaled so 0.0~1.0 == ADC range 0~Vcc (0.3 == 30% of Vcc)
prussell 22:533275e76f55 117 // - adcX.read_u16() returns uint16, raw (0x0000~0xFFFF)
prussell 22:533275e76f55 118 //nRF51822 mkit Analog Pins are only p1~p6 (P0_1~P0_6):
prussell 22:533275e76f55 119 //AnalogIn adc0(p26);// nRF51822p0.26==pin45==AIN0==XL2 (Reserve for low power 32KHz Crystal)
prussell 22:533275e76f55 120 //AnalogIn adc1(p27);// nRF51822p0.27==pin46==AIN1==XL1 (Reserve for low power 32KHz Crystal)
prussell 22:533275e76f55 121 //AnalogXX AREF0 // nRF51822p0.00==pin04 ==AREF0 (Reserve for Analog Reference)
prussell 22:533275e76f55 122 AnalogIn adcA(p1); // nRF51822p0.01==pin05==AIN2
prussell 22:533275e76f55 123 AnalogIn adcB(p2); // nRF51822p0.02==pin06==AIN3
prussell 22:533275e76f55 124 //AnalogIn adcC(p3); // nRF51822p0.03==pin07==AIN4
prussell 22:533275e76f55 125 //AnalogIn adcD(p4); // nRF51822p0.04==pin08==AIN5
prussell 22:533275e76f55 126 //AnalogIn adcE(p5); // nRF51822p0.05==pin09==AIN6
prussell 22:533275e76f55 127 //AnalogIn adcF(p6); // nRF51822p0.06==pin10==AIN7==AREF1 (Option: Reserve for Analog Reference)
prussell 22:533275e76f55 128
prussell 22:533275e76f55 129 void vShowADC(void)
prussell 22:533275e76f55 130 { //TODO: Check what the actual ADC register settings are to know how the nRF51822 is actually configured
prussell 22:533275e76f55 131 DEBUG(" ShowADC:\n");
prussell 22:533275e76f55 132 DEBUG(" p1=AIN2[%f=0x%04X]", adcA.read(), adcA.read_u16() );
prussell 22:533275e76f55 133 DEBUG(" p2=AIN3[%f=0x%04X]", adcB.read(), adcB.read_u16() );
prussell 22:533275e76f55 134 //DEBUG(" p3=AIN4[%f=0x%04X]", adcC.read(), adcC.read_u16() );
prussell 22:533275e76f55 135 //DEBUG(" p4=AIN5[%f=0x%04X]", adcD.read(), adcD.read_u16() );
prussell 22:533275e76f55 136 //DEBUG(" p5=AIN6[%f=0x%04X]", adcE.read(), adcE.read_u16() );
prussell 22:533275e76f55 137 //DEBUG(" p6=AIN7[%f=0x%04X]", adcF.read(), adcF.read_u16() );
prussell 22:533275e76f55 138 DEBUG(" \n");
prussell 22:533275e76f55 139 }
prussell 22:533275e76f55 140
prussell 22:533275e76f55 141
prussell 0:0217a862b047 142 //==========BLE==========
prussell 13:1c67c03bbf53 143 //const static char pcDeviceName[] = "bleIOv04_pr"; //Too Long for advertising with a UUID128
prussell 13:1c67c03bbf53 144 const static char pcDeviceName[] = "bleIOv04"; //Advertised device name (Max length depends on available space with other Advertising data)
prussell 0:0217a862b047 145 BLEDevice ble;
prussell 10:ee3a359f7d3f 146 //Pointers to services for accesses outside main()
prussell 8:f187ba55aed2 147 HeartRateService *pServiceHRM;
prussell 15:b2c8bdef2d20 148 HealthThermometerService *pServiceHTM;
prussell 15:b2c8bdef2d20 149 BatteryService *pServiceBattery;
prussell 8:f187ba55aed2 150 DeviceInformationService *pServiceDeviceInfo;
prussell 8:f187ba55aed2 151 LinkLossService *pServiceLinkLoss;
prussell 10:ee3a359f7d3f 152 //x DFUService *pServiceDFU;
prussell 10:ee3a359f7d3f 153 //x UARTService *pServiceUART; //FromApp: pServiceUART->getTXCharacteristicHandle(); //ToApp: ble.updateCharacteristicValue(pServiceUART->getRXCharacteristicHandle(), pData, uLen);
prussell 10:ee3a359f7d3f 154 //x bleIOService *pServiceIO;
prussell 3:a98203f84063 155
prussell 6:5b6fb35b4450 156 //==========UUID==========
prussell 6:5b6fb35b4450 157 //UUID Info:
prussell 5:d36bbb315e31 158 // 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 159 /* All the custom GATT based services and characteristics must use a 128 bit UUID.
prussell 5:d36bbb315e31 160 The Bluetooth_Base_UUID is: 00000000-0000-1000-8000 00805F9B34FB.
prussell 5:d36bbb315e31 161 All the 16-bit Attribute UUIDs defined in the adopted specifications use the above base.
prussell 5:d36bbb315e31 162 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 163 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 164 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 165 Latency: Refer to Core Spec V4.0, Vol 3, Part F - 3.3.1, which is "Ready by Type Request".
prussell 5:d36bbb315e31 166 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 167 There is additional 14 extra bytes over head for the custom profiles for "Ready By Type Request Method"
prussell 5:d36bbb315e31 168 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 169 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 170 // 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 171 // 32bit UUID uses: 128bit base(32 hex digits): ********-0000-1000-8000 00805F9B34FB (Careful to use unsigned to avoid negatives and overflows)
prussell 10:ee3a359f7d3f 172
prussell 10:ee3a359f7d3f 173 //UUID128 List(Only a single UUID128 can fit in advertising)
prussell 10:ee3a359f7d3f 174 //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 175 //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 176
prussell 11:7d02fe5ebea5 177 //a //UUID16 List - Advertising these required by App nRF-Toolbox:HRM&HTM // Keep list short so advertizing not over 31bytes.
prussell 10:ee3a359f7d3f 178 /*static const uint16_t uuid16_list[] = { //Service List (Pre-defined standard 16bit services)
prussell 10:ee3a359f7d3f 179 // *Order here doesn't affect order in nRF-MCP Discovery of Services
prussell 10:ee3a359f7d3f 180 //BLE_UUID_GAP UUID_GENERIC_ACCESS //0x1800 //Included by Default, DeviceName, Appearance, PreferredConnectionParam
prussell 10:ee3a359f7d3f 181 //BLE_UUID_GATT UUID_GENERIC ATTRIBUTE //0x1801 //Included by Default, ServiceChanged,
prussell 10:ee3a359f7d3f 182 GattService::UUID_HEALTH_THERMOMETER_SERVICE, //0x1809 //HTM (Might need to be first for nRF App)
prussell 10:ee3a359f7d3f 183 GattService::UUID_HEART_RATE_SERVICE, //0x180D //HRM, BodyLocation, ControlPoint
prussell 10:ee3a359f7d3f 184 GattService::UUID_DEVICE_INFORMATION_SERVICE, //0x180A //sManufacturer, sModelNumber, sSerialNumber, sHWver, sFWver, sSWver
prussell 10:ee3a359f7d3f 185 GattService::UUID_BATTERY_SERVICE, //0x180F //BatteryLevel
prussell 10:ee3a359f7d3f 186 GattService::UUID_LINK_LOSS_SERVICE, //0x1803 //LinkLoss
prussell 10:ee3a359f7d3f 187 //x GattService::UUID_DFU, //0x1530 - See UARTServiceShortUUID in BLE_API:DFUService.cpp //
prussell 10:ee3a359f7d3f 188 //x GattService::UARTService, //0x0001~0x0003 - See DFUServiceShortUUID in BLE_API:UARTService.cpp //
prussell 10:ee3a359f7d3f 189 //Possibly useful:
prussell 10:ee3a359f7d3f 190 //x GattService::UUID_ALERT_NOTIFICATION_SERVICE, //0x1811
prussell 10:ee3a359f7d3f 191 //x GattService::UUID_CURRENT_TIME_SERVICE, //0x1805
prussell 10:ee3a359f7d3f 192 //x GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, //0x1812
prussell 10:ee3a359f7d3f 193 //x GattService::UUID_IMMEDIATE_ALERT_SERVICE, //0x1802
prussell 10:ee3a359f7d3f 194 //x GattService::UUID_PHONE_ALERT_STATUS_SERVICE, //0x180E
prussell 10:ee3a359f7d3f 195 //x GattService::UUID_REFERENCE_TIME_UPDATE_SERVICE, //0x1806
prussell 10:ee3a359f7d3f 196 //x GattService::UUID_SCAN_PARAMETERS_SERVICE, //0x1813
prussell 10:ee3a359f7d3f 197 };*/
prussell 2:c77c2b06d604 198
prussell 10:ee3a359f7d3f 199 //========== bleIO - A Custom Service for Handling IO with an App (GPIO,PWM,etc.)==========
prussell 10:ee3a359f7d3f 200 //UUID128 List - Note only the single Service UUID128 can fit in the advertising
prussell 9:2d11beda333f 201
prussell 9:2d11beda333f 202 // Custom UUID128 base for this service and its characteristics (from: http://www.itu.int/ITU-T/asn1/uuid.html)
prussell 9:2d11beda333f 203 // **This is a temporary UUID for testing by anyone, should be replaced before proceeding to real product.**
prussell 9:2d11beda333f 204 // Can check that UUID is correctly entered by viewing with Android App: nRF-Master Control panel
prussell 9:2d11beda333f 205 // UUID128 can be generated using: http://www.itu.int/ITU-T/asn1/uuid.html
prussell 9:2d11beda333f 206 // 20141218PR: [4d3281c0-86d1-11e4-b084-0002a5d5c51b] == OID[2.25.102612802202735078710424021169255859483]
prussell 10:ee3a359f7d3f 207 // 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 208 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 209 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 210 // Characteristic UUID: Initially using generic blocks of IO data that may be manually dividied up into Buttons, LEDs, PWM, ADC, etc.
prussell 10:ee3a359f7d3f 211 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 212 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 213 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 214 // Alternately can have a characterostic for each discrete item, such as:
prussell 10:ee3a359f7d3f 215 //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 216 //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 217 //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 218 //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 219 //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 220 //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 221
prussell 10:ee3a359f7d3f 222 /* Android:
prussell 10:ee3a359f7d3f 223 //bleIO Service and Characteristics
prussell 10:ee3a359f7d3f 224 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c51b", "bleIO base");
prussell 10:ee3a359f7d3f 225 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c510", "bleIO Service");
prussell 10:ee3a359f7d3f 226 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c511", "bleIO w8");
prussell 10:ee3a359f7d3f 227 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c512", "bleIO r4");
prussell 10:ee3a359f7d3f 228 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c513", "bleIO n4");
prussell 9:2d11beda333f 229
prussell 10:ee3a359f7d3f 230 attributes.put("1bc5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev base");
prussell 10:ee3a359f7d3f 231 attributes.put("10c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev Service");
prussell 10:ee3a359f7d3f 232 attributes.put("11c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev w8");
prussell 10:ee3a359f7d3f 233 attributes.put("12c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev r4");
prussell 10:ee3a359f7d3f 234 attributes.put("13c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev n4");
prussell 10:ee3a359f7d3f 235 */
prussell 10:ee3a359f7d3f 236
prussell 10:ee3a359f7d3f 237 // Characteristic Value Storage (with Initial values, should match actual data type transferred, doesn't have to be byte array):
prussell 12:8bac5f5d3a3e 238 //a uint8_t pIOw8[8] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17}; //Value Storage for Characteristic Write8
prussell 12:8bac5f5d3a3e 239 //a uint8_t pIOr4[4] = {0x20,0x21,0x22,0x23}; //Value Storage for Characteristic Read4
prussell 12:8bac5f5d3a3e 240 //a uint8_t pIOn4[4] = {0x30,0x31,0x32,0x33}; //Value Storage for Characteristic Notify4
prussell 12:8bac5f5d3a3e 241
prussell 12:8bac5f5d3a3e 242 union { // Option: split long setup from short updates to minimize BLE power and traffic
prussell 12:8bac5f5d3a3e 243 struct {// First item in union defines the initializers
prussell 12:8bac5f5d3a3e 244 uint8_t L1pwm100; // LED1 Brightness (0%~100%)
prussell 12:8bac5f5d3a3e 245 uint8_t L2pwm255; // LED2 Brightness (0~255)
prussell 12:8bac5f5d3a3e 246 //float xxxx; //Option: If both smartphone and device use same type of float, else put converter in smartphone code (phone has larger battery and memory)
prussell 12:8bac5f5d3a3e 247 uint16_t px[3]; // Spare 3*16bit (Test: incremented by Ticker1
prussell 12:8bac5f5d3a3e 248 } s;
prussell 12:8bac5f5d3a3e 249 uint8_t pu[]; //Direct Byte access
prussell 12:8bac5f5d3a3e 250 char pc[]; //Character Access (No Terminating Null)
prussell 12:8bac5f5d3a3e 251 } sIOw8 = {100,255,0xA00A, 0xB000, 0xC000}; // Structure/union for Characteristic IOw8
prussell 12:8bac5f5d3a3e 252 // sIOw8.s.L1pwm100, sIOw8.s.L2pwm255, sIOw8.s.px, sIOw8.pu
prussell 12:8bac5f5d3a3e 253
prussell 12:8bac5f5d3a3e 254 union { // Longer maybe OK since only read upon command
prussell 12:8bac5f5d3a3e 255 struct {// First item in union defines the initializers
prussell 12:8bac5f5d3a3e 256 uint8_t bB1p:1,bB2p:1; // Button status, 1/true=Pressed, bit1==Button1, bit2==Button2
prussell 12:8bac5f5d3a3e 257 uint8_t uB1p:6; // Count Button1 Presses (6bit)
prussell 12:8bac5f5d3a3e 258 uint8_t uB1r; // Count Button1 Releases
prussell 12:8bac5f5d3a3e 259 uint8_t uB2p; // Count Button2 Presses
prussell 12:8bac5f5d3a3e 260 uint8_t uB2r; // Count Button2 Releases
prussell 12:8bac5f5d3a3e 261 // // Option: ADC or other inputs or device status
prussell 12:8bac5f5d3a3e 262 } s;
prussell 12:8bac5f5d3a3e 263 uint8_t pu[]; //Direct Byte access
prussell 12:8bac5f5d3a3e 264 char pc[]; //Character Access (No Terminating Null)
prussell 12:8bac5f5d3a3e 265 } sIOr4 = { true, false, 0x3F, 255, 255, 255}; // Structure/union for Characteristic IOr4
prussell 12:8bac5f5d3a3e 266 // sIOr4.s.bB1p, sIOr4.s.bB1p, sIOr4.s.uB1p, sIOr4.s.bB1r, sIOr4.s.uB2p, sIOr4.s.uB2r, sIOr4.pu
prussell 12:8bac5f5d3a3e 267
prussell 12:8bac5f5d3a3e 268 union { // Option: Shorten content to reduce BLE power and traffic
prussell 12:8bac5f5d3a3e 269 struct {// First item in union defines the initializers
prussell 12:8bac5f5d3a3e 270 uint8_t bB1p:1,bB2p:1; // Button status, 1/true=Pressed, bit1==Button1, bit2==Button2
prussell 12:8bac5f5d3a3e 271 uint8_t uB1p:6; // Count Button2 Presses (6bit) *Notify*
prussell 12:8bac5f5d3a3e 272 uint8_t uB2r; // Count Button2 Releases *Notify*
prussell 12:8bac5f5d3a3e 273 int16_t iTempC100; // Temperature in hundreths of 'C (i.e. float = iTempC10/100)
prussell 12:8bac5f5d3a3e 274 // // Option: ADC or other inputs or device status
prussell 12:8bac5f5d3a3e 275 } s;
prussell 12:8bac5f5d3a3e 276 uint8_t pu[]; //Direct Byte access
prussell 12:8bac5f5d3a3e 277 char pc[]; //Character Access (No Terminating Null)
prussell 12:8bac5f5d3a3e 278 } sIOn4 = { false, true, 0x3F, 255, -2345/*=-23.45'C*/}; // Structure/union for Characteristic IOn4
prussell 12:8bac5f5d3a3e 279 // sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, sIOn4.s.iTempC100, sIOn4.pu
prussell 9:2d11beda333f 280
prussell 9:2d11beda333f 281 //x GattCharacteristic::GattAttribute *descriptors[]; //How Set Characteristic Descriptors? Maybe in the App is easier?
prussell 9:2d11beda333f 282
prussell 10:ee3a359f7d3f 283 //IO Characteristics: //GattCharacteristics xx(*UUID, *Payload=*Value, LenInit, LenMax, Properties)
prussell 18:c676e79d5d3e 284 //GattCharacteristic IOw8(puUUID128_IOw8, sIOw8.pu, sizeof(sIOw8), sizeof(sIOw8), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
prussell 12:8bac5f5d3a3e 285 GattCharacteristic IOw8(puUUID128_IOw8, sIOw8.pu, sizeof(sIOw8), sizeof(sIOw8), 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 18:c676e79d5d3e 286 GattCharacteristic IOr4(puUUID128_IOr4, sIOr4.pu, sizeof(sIOr4), sizeof(sIOr4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); // Host can manually request
prussell 19:ebe7b59d9c76 287 GattCharacteristic IOn4(puUUID128_IOn4, sIOn4.pu, sizeof(sIOn4), sizeof(sIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);// Host is Notified of changes (Same as HRM), *Works but first read not until a channge
prussell 19:ebe7b59d9c76 288 //GattCharacteristic IOn4(puUUID128_IOn4, sIOn4.pu, sizeof(sIOn4), sizeof(sIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); // Host is notified of changes (Same as HTM & BattLevel), Buggy until can queue BLE Operations
prussell 12:8bac5f5d3a3e 289 GattCharacteristic *pCustomCharacteristics[] = {&IOw8, &IOr4, &IOn4};//Table of Pointers to GattCharacteristics
prussell 10:ee3a359f7d3f 290
prussell 10:ee3a359f7d3f 291 //IO Service: //GattService::GattService(*UUID, *GattCharacteristics, (numCharacteristics) ) :
prussell 10:ee3a359f7d3f 292 GattService ServiceIO(puUUID128_IOService, pCustomCharacteristics, (sizeof(pCustomCharacteristics)/sizeof(pCustomCharacteristics[0])));
prussell 10:ee3a359f7d3f 293 GattService *pServiceIO= &ServiceIO; // Pointer to Service
prussell 9:2d11beda333f 294
prussell 12:8bac5f5d3a3e 295 //========== Functions for IO Characteristics ========
prussell 12:8bac5f5d3a3e 296 void vShowIO(void)
prussell 12:8bac5f5d3a3e 297 { //TODO: For Read Characteristics get data actually written to Characteristic, versus the memory expected to be in characteristic.
prussell 12:8bac5f5d3a3e 298 uint8_t puBuf8[8];
prussell 12:8bac5f5d3a3e 299 uint16_t uLen;
prussell 12:8bac5f5d3a3e 300
prussell 12:8bac5f5d3a3e 301 DEBUG(" ShowIO:\n");
prussell 12:8bac5f5d3a3e 302
prussell 12:8bac5f5d3a3e 303 // IOw8 Write
prussell 16:3e83f2babdfb 304 uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOw8.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value
prussell 21:f92bb1c80538 305 DEBUG(" sIOw8: Handle:%d Len:%d L1:%d L2:%d %04X %04X %04X\n", IOw8.getValueHandle(), uLen, sIOw8.s.L1pwm100, sIOw8.s.L2pwm255, sIOw8.s.px[0], sIOw8.s.px[1], sIOw8.s.px[2]);
prussell 12:8bac5f5d3a3e 306 //DEBUG(" pIOw8: Handle:%d Len:%d [%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 12:8bac5f5d3a3e 307 DEBUG(" sIOw8: Handle:%d Len:%d [%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), sizeof(sIOw8), sIOw8.pu[0],sIOw8.pu[1],sIOw8.pu[2],sIOw8.pu[3],sIOw8.pu[4],sIOw8.pu[5],sIOw8.pu[6],sIOw8.pu[7]);
prussell 12:8bac5f5d3a3e 308 DEBUG(" cIOw8: Handle:%d Len:%d [%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3], puBuf8[4], puBuf8[5], puBuf8[6], puBuf8[7] );
prussell 12:8bac5f5d3a3e 309
prussell 12:8bac5f5d3a3e 310 // IOr4 Read
prussell 16:3e83f2babdfb 311 uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOr4.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value
prussell 12:8bac5f5d3a3e 312 DEBUG("\n sIOr4: Handle:%d Len:%d B1:%d B2:%d B1p:%d B1r:%d B2p:%d B2r:%d\n", IOr4.getValueHandle(), uLen, sIOr4.s.bB1p, sIOr4.s.bB2p, sIOr4.s.uB1p, sIOr4.s.uB1r, sIOr4.s.uB2p, sIOr4.s.uB2r);
prussell 12:8bac5f5d3a3e 313 //DEBUG(" pIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(pIOr4), pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]);
prussell 12:8bac5f5d3a3e 314 //DEBUG(" sIOr4: Handle:%d Len:%d [%02X %02X %02X %02X][%.4s]\n", IOr4.getValueHandle(), sizeof(sIOr4), sIOr4.pu[0], sIOr4.pu[1], sIOr4.pu[2], sIOr4.pu[3], sIOr4.pc );
prussell 12:8bac5f5d3a3e 315 DEBUG(" sIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(sIOr4), sIOr4.pu[0], sIOr4.pu[1], sIOr4.pu[2], sIOr4.pu[3]);
prussell 12:8bac5f5d3a3e 316 // Read Actual IOr4 Characteristic's data back from Service (i.e. last written to service):
prussell 12:8bac5f5d3a3e 317 DEBUG(" cIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] );
prussell 12:8bac5f5d3a3e 318
prussell 12:8bac5f5d3a3e 319 // IOn4 Notify
prussell 16:3e83f2babdfb 320 uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOn4.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value
prussell 12:8bac5f5d3a3e 321 DEBUG("\n sIOn4: Handle:%d Len:%d B1:%d B2:%d B1p:%d B2r:%d %.2f\n", IOn4.getValueHandle(), uLen, sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, ((float)sIOn4.s.iTempC100/100.0));
prussell 12:8bac5f5d3a3e 322 //DEBUG("\n pIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(pIOn4), pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]);
prussell 12:8bac5f5d3a3e 323 DEBUG(" sIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(sIOn4), sIOn4.pu[0], sIOn4.pu[1], sIOn4.pu[2], sIOn4.pu[3]);
prussell 12:8bac5f5d3a3e 324 DEBUG(" cIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] );
prussell 15:b2c8bdef2d20 325
prussell 15:b2c8bdef2d20 326 //DEBUG("\n Handles for Standard Services' Characteristics:\n");
prussell 15:b2c8bdef2d20 327 //DEBUG(" HRM: Rate:%d Location:%d Control:%d", pServiceHRM->getCharacteristic(0)->getValueHandle(),pServiceHRM->getCharacteristic(1)->getValueHandle(),pServiceHRM->getCharacteristic(2)->getValueHandle());
prussell 15:b2c8bdef2d20 328 //DEBUG(" HTM: Temp:%d Type:%d", pServiceHTM->getCharacteristic(0)->getValueHandle(),pServiceHTM->getCharacteristic(1)->getValueHandle());
prussell 15:b2c8bdef2d20 329 //DEBUG(" Batt: Level:%d", pServiceBattery->getCharacteristic(0)->getHandle());
prussell 15:b2c8bdef2d20 330 //DEBUG(" DeviceInfo: %d~%d", pServiceDeviceInfo->getCharacteristic(0)->getHandle(), pServiceDeviceInfo->getCharacteristic(5)->getHandle());
prussell 15:b2c8bdef2d20 331 //DEBUG(" LinkLoss: AlertLevel:%d", pServiceLinkLoss->getCharacteristic(0)->getHandle());
prussell 15:b2c8bdef2d20 332 //DEBUG(" DFU:%d", pServiceDFU.getValueAttribute().getHandle());
prussell 15:b2c8bdef2d20 333 //DEBUG(" UART:%d\n", pServiceUART.getValueAttribute().getHandle());
prussell 22:533275e76f55 334
prussell 22:533275e76f55 335 vShowADC();
prussell 12:8bac5f5d3a3e 336 }
prussell 12:8bac5f5d3a3e 337
prussell 12:8bac5f5d3a3e 338 //==== Soft Updates to Characteristics for initial tests: (Most replaced by real buttons/sensors/timers )
prussell 10:ee3a359f7d3f 339 /*void vUpdate_IOw8(void) // Handle in device changes to w8 characteristic
prussell 12:8bac5f5d3a3e 340 {//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 341 static uint8_t uw8; //Level: 0..2
prussell 10:ee3a359f7d3f 342 switch(++uw8){
prussell 10:ee3a359f7d3f 343 case 2: memcpy(pIOw8, "w2w2w2wc", sizeof(pIOw8)); DEBUG(" w8c"); break;
prussell 10:ee3a359f7d3f 344 case 1: memcpy(pIOw8, "w1w1w1wb", sizeof(pIOw8)); DEBUG(" w8b"); break;
prussell 10:ee3a359f7d3f 345 default: uw8=0; memcpy(pIOw8, "w0w0w0wa", sizeof(pIOw8)); DEBUG(" w8a"); break;
prussell 9:2d11beda333f 346 }
prussell 10:ee3a359f7d3f 347 //ble.updateCharacteristicValue(uint16_t handle, const uint8_t *value, uint16_t size, bool localOnly)
prussell 10:ee3a359f7d3f 348 ble.updateCharacteristicValue(IOw8.getValueHandle(), pIOw8, sizeof(pIOw8));
prussell 9:2d11beda333f 349 //pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE
prussell 12:8bac5f5d3a3e 350 vShowIO();
prussell 10:ee3a359f7d3f 351 }*/
prussell 10:ee3a359f7d3f 352
prussell 12:8bac5f5d3a3e 353 //==== Hard Updates to Characteristics: [onButton()] Notify: B1press or B2release
prussell 12:8bac5f5d3a3e 354 // *When direct driving hardware consider adjusting drive within the onCallback to reduce response time
prussell 12:8bac5f5d3a3e 355 //TODO: Check need of volatile for changes in interrupts affecting variables in non-interrupt code?
prussell 12:8bac5f5d3a3e 356
prussell 12:8bac5f5d3a3e 357 volatile uint8_t uB1press; // Events since last handled in main()
prussell 12:8bac5f5d3a3e 358 volatile uint8_t uB1release;// Events since last handled in main()
prussell 12:8bac5f5d3a3e 359 volatile uint8_t uB2press; // Events since last handled in main()
prussell 12:8bac5f5d3a3e 360 volatile uint8_t uB2release;// Events since last handled in main()
prussell 12:8bac5f5d3a3e 361
prussell 12:8bac5f5d3a3e 362 void vFillIO(void) // Fill the structures for the Characteristics, but leave any Notify to calling function
prussell 12:8bac5f5d3a3e 363 { //Structures are filled completely so available for BLE read at any time
prussell 12:8bac5f5d3a3e 364 // sIOr4.s.bB1p, sIOr4.s.bB1p, sIOr4.s.uB1p, sIOr4.s.bB1r, sIOr4.s.uB2p, sIOr4.s.uB2r, sIOr4.pu
prussell 12:8bac5f5d3a3e 365 sIOr4.s.bB1p = !bB1in; //Button1 Pressed (nRF51822 mkit: Pressed=Lo)
prussell 12:8bac5f5d3a3e 366 sIOr4.s.bB2p = !bB2in; //Button2 Pressed (nRF51822 mkit: Pressed=Lo)
prussell 12:8bac5f5d3a3e 367 sIOr4.s.uB1p = uB1press;// Button1 Presses (Truncates to size)
prussell 12:8bac5f5d3a3e 368 sIOr4.s.uB1r = uB1release;
prussell 12:8bac5f5d3a3e 369 sIOr4.s.uB2p = uB2press;
prussell 12:8bac5f5d3a3e 370 sIOr4.s.uB2r = uB2release;
prussell 12:8bac5f5d3a3e 371
prussell 12:8bac5f5d3a3e 372 // sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, sIOn4.s.iTempC100, sIOn4.pu
prussell 12:8bac5f5d3a3e 373 sIOn4.s.bB1p = !bB1in; //Button1 Pressed (nRF51822 mkit: Pressed=Lo)
prussell 12:8bac5f5d3a3e 374 sIOn4.s.bB2p = !bB2in; //Button2 Pressed (nRF51822 mkit: Pressed=Lo)
prussell 12:8bac5f5d3a3e 375 sIOn4.s.uB1p = uB1press;//Button1 Presses (Truncates to size)
prussell 12:8bac5f5d3a3e 376 sIOn4.s.uB2r = uB2release;
prussell 12:8bac5f5d3a3e 377
prussell 12:8bac5f5d3a3e 378 int32_t i32_C_nRF; sd_temp_get(&i32_C_nRF); //Read the nRF Internal Temperature (Die in 0.25'C steps, offset +16'C)
prussell 12:8bac5f5d3a3e 379 sIOn4.s.iTempC100 = (i32_C_nRF*25)-(1600); //Save temperature in hundreths (0.01'C steps relative to 0'C)(.25'C * 25=.01'C steps)
prussell 12:8bac5f5d3a3e 380 //float fTemperature = (float(i32_temp)/4.0) - 16.0; // Scale&Shift (0.25'C from -16'C?)
prussell 20:fb286f736dc4 381
prussell 20:fb286f736dc4 382 ble.updateCharacteristicValue(IOr4.getValueHandle(), sIOr4.pu, sizeof(sIOr4)); // Update r4 data so ready for read (Doesn't Trigger Notify)
prussell 9:2d11beda333f 383 }
prussell 9:2d11beda333f 384
prussell 23:78aad4e53ae2 385 //PR: Notify: Doing Notify on both Press and Release since n4 contains status of multiple inputs so need both bits to be correct. If Notify Characteristic is single button then notify could be on just press or just release.
prussell 12:8bac5f5d3a3e 386 void onB1press(void) // B1 Press == *Notify*
prussell 12:8bac5f5d3a3e 387 { // Update Characteristics that include this button
prussell 12:8bac5f5d3a3e 388 uB1press++; // Flag/Count Events (allows detect any missed processing)
prussell 12:8bac5f5d3a3e 389 vFillIO(); // Prepare IO Characteristic data
prussell 20:fb286f736dc4 390 ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Triggers Notify (Reading n4 at other times may have old data)
prussell 12:8bac5f5d3a3e 391 DEBUG("\n B1p%d", uB1press); vShowIO();
prussell 12:8bac5f5d3a3e 392 };
prussell 12:8bac5f5d3a3e 393 void onB1release(void) // B1 Release
prussell 12:8bac5f5d3a3e 394 { // Update Characteristics that include this button
prussell 12:8bac5f5d3a3e 395 uB1release++; // Flag/Count Events (allows detect any missed processing)
prussell 12:8bac5f5d3a3e 396 vFillIO(); // Prepare IO Characteristic data
prussell 20:fb286f736dc4 397 ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Triggers Notify (Reading n4 at other times may have old data)
prussell 12:8bac5f5d3a3e 398 DEBUG("\n B1r%d", uB1release); vShowIO();
prussell 12:8bac5f5d3a3e 399 };
prussell 12:8bac5f5d3a3e 400 void onB2press(void) // B2 Press
prussell 12:8bac5f5d3a3e 401 { // Update Characteristics that include this button
prussell 12:8bac5f5d3a3e 402 uB2press++; // Flag/Count Events (allows detect any missed processing)
prussell 12:8bac5f5d3a3e 403 vFillIO(); // Prepare IO Characteristic data
prussell 20:fb286f736dc4 404 ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Triggers Notify (Reading n4 at other times may have old data)
prussell 12:8bac5f5d3a3e 405 DEBUG("\n B2p%d", uB2press); vShowIO();
prussell 12:8bac5f5d3a3e 406 };
prussell 12:8bac5f5d3a3e 407 void onB2release(void) // B2 Release
prussell 12:8bac5f5d3a3e 408 { // Update Characteristics that include this button
prussell 12:8bac5f5d3a3e 409 uB2release++; // Flag/Count Events (allows detect any missed processing)
prussell 12:8bac5f5d3a3e 410 vFillIO(); // Prepare IO Characteristic data
prussell 20:fb286f736dc4 411 ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Triggers Notify (Reading n4 at other times may have old data)
prussell 12:8bac5f5d3a3e 412 DEBUG("\n B2r%d", uB2release); vShowIO();
prussell 12:8bac5f5d3a3e 413 };
prussell 9:2d11beda333f 414
prussell 0:0217a862b047 415 //==========Functions:BLE==========
prussell 0:0217a862b047 416 void Callback_BLE_onTimeout(void)
prussell 10:ee3a359f7d3f 417 { //PR: Haven't seen this, even when phone moved out of range and events occur like OnDisconnect(Reason0x08) or LinkLoss
prussell 12:8bac5f5d3a3e 418 DEBUG("\n\n\n\n**** BLEi: Callback_BLE_onTimeout() ****\n\n\n\n" );
prussell 0:0217a862b047 419
prussell 0:0217a862b047 420 //DEBUG("\nBLE:Callback_BLE_onTimeout(), Restarting Advertising\n" );
prussell 0:0217a862b047 421 //ble.startAdvertising();
prussell 0:0217a862b047 422 }
prussell 0:0217a862b047 423
prussell 0:0217a862b047 424 void Callback_BLE_onDisconnect(Gap::Handle_t tHandle, Gap::DisconnectionReason_t eReason)
prussell 10:ee3a359f7d3f 425 { //PR: onDisconnect(Reason:8) occured after ~20Tx with no onDataSent() after phone moved out of range
prussell 0:0217a862b047 426
prussell 23:78aad4e53ae2 427
prussell 0:0217a862b047 428 // REMOTE_USER_TERMINATED_CONNECTION = 0x13 = 19,
prussell 0:0217a862b047 429 // LOCAL_HOST_TERMINATED_CONNECTION = 0x16 = 22,
prussell 0:0217a862b047 430 // CONN_INTERVAL_UNACCEPTABLE = 0x3B = 59,
prussell 23:78aad4e53ae2 431 DEBUG("\nBLEi: Callback_BLE_Disconnect(Handle:%d, eReason:0x%02x=", tHandle, eReason );//PR: Occurs properly when click disconnect in App nRFToolbox:HRM
prussell 23:78aad4e53ae2 432 switch(eReason) {
prussell 23:78aad4e53ae2 433 case Gap::REMOTE_USER_TERMINATED_CONNECTION: DEBUG("REMOTE_USER_TERMINATED_CONNECTION"); break;
prussell 23:78aad4e53ae2 434 case Gap::CONN_INTERVAL_UNACCEPTABLE: DEBUG("CONN_INTERVAL_UNACCEPTABLE"); break;
prussell 23:78aad4e53ae2 435 case Gap::LOCAL_HOST_TERMINATED_CONNECTION: DEBUG("LOCAL_HOST_TERMINATED_CONNECTION"); break;
prussell 23:78aad4e53ae2 436 default: DEBUG("UNKNOWN"); break;
prussell 23:78aad4e53ae2 437 }
prussell 23:78aad4e53ae2 438 DEBUG("), Restarting Advertising\n", tHandle, eReason );//PR: Occurs properly when click disconnect in App nRFToolbox:HRM
prussell 0:0217a862b047 439
prussell 0:0217a862b047 440 //DEBUG("Wait10sec...\n");wait(10.0); //PR: Optional to test effect on advertising
prussell 0:0217a862b047 441 ble.startAdvertising(); // restart advertising
prussell 23:78aad4e53ae2 442
prussell 23:78aad4e53ae2 443 //TODO: LED Mode to Indicate Advertising until something else changes LED Mode?
prussell 0:0217a862b047 444 }
prussell 0:0217a862b047 445
prussell 23:78aad4e53ae2 446
prussell 0:0217a862b047 447 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 448 {
prussell 0:0217a862b047 449 DEBUG("\nBLEi: Callback_BLE_Connect(Handle:%d, eType:%d, Add:%u ...)\n", tHandle, ePeerAddrType, c6PeerAddr);
prussell 0:0217a862b047 450
prussell 11:7d02fe5ebea5 451 //x #if ENABLE_BLE_SLOW //UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL //PR: Adopted optional code never tested
prussell 11:7d02fe5ebea5 452 //x // Updating connection parameters can be attempted only after a connection has been
prussell 11:7d02fe5ebea5 453 //x // established. Please note that the ble-Central is still the final arbiter for
prussell 11:7d02fe5ebea5 454 //x // the effective parameters; the peripheral can only hope that the request is
prussell 11:7d02fe5ebea5 455 //x // honored. Please also be mindful of the constraints that might be enforced by
prussell 11:7d02fe5ebea5 456 //x // the BLE stack on the underlying controller.
prussell 11:7d02fe5ebea5 457 //x #define MIN_CONN_INTERVAL 250 // Minimum connection interval (250 ms)
prussell 11:7d02fe5ebea5 458 //x #define MAX_CONN_INTERVAL 350 // Maximum connection interval (350 ms)
prussell 11:7d02fe5ebea5 459 //x #define CONN_SUP_TIMEOUT 6000 // Connection supervisory timeout (6 seconds)
prussell 11:7d02fe5ebea5 460 //x #define SLAVE_LATENCY 4
prussell 11:7d02fe5ebea5 461 //x Gap::ConnectionParams_t tGap_conn_params;
prussell 11:7d02fe5ebea5 462 //x tGap_conn_params.minConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(MIN_CONN_INTERVAL);
prussell 11:7d02fe5ebea5 463 //x tGap_conn_params.maxConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(MAX_CONN_INTERVAL);
prussell 11:7d02fe5ebea5 464 //x tGap_conn_params.connectionSupervisionTimeout = Gap::MSEC_TO_GAP_DURATION_UNITS(CONN_SUP_TIMEOUT);
prussell 11:7d02fe5ebea5 465 //x tGap_conn_params.slaveLatency = SLAVE_LATENCY;
prussell 11:7d02fe5ebea5 466 //x ble.updateConnectionParams(tHandle, &tGap_conn_params);
prussell 11:7d02fe5ebea5 467 //x #endif // #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL
prussell 0:0217a862b047 468 }
prussell 0:0217a862b047 469
prussell 23:78aad4e53ae2 470 #ifdef NOT_DEFINED
prussell 23:78aad4e53ae2 471 void Callback_BLE_onDataRead(const GattCharacteristicReadAuthCBParams *pParams) //NotAvailableOnlineYet20150114
prussell 23:78aad4e53ae2 472 {
prussell 23:78aad4e53ae2 473 GattAttribute::Handle_t tHandle=pParams->charHandle;
prussell 23:78aad4e53ae2 474
prussell 23:78aad4e53ae2 475 //Handle Changes to Service Characteristics:
prussell 23:78aad4e53ae2 476 if(!ble.getGapState().connected){ //Ensure BLE still connected
prussell 23:78aad4e53ae2 477 DEBUG("BLEi: Callback_BLE_onDataRead() while disconnected!!");
prussell 23:78aad4e53ae2 478 } else if (tHandle == IOr4.getValueHandle()) { // IOr4 Characteristic?
prussell 23:78aad4e53ae2 479 DEBUG("BLEi: Callback_BLE_onDataRead() while disconnected!!");
prussell 23:78aad4e53ae2 480 vFillIO(); // Update the data for this Characteristic
prussell 23:78aad4e53ae2 481 vShowIO();
prussell 23:78aad4e53ae2 482 //} else if (tHandle == IOw8.getValueHandle()) { // Writeonly, Continue with no changes, App can read back whatever was last written
prussell 23:78aad4e53ae2 483 //} else if (tHandle == IOr4.getValueHandle()) { // Notifyonly, shouldn't occur, is automatically sent when Notify changes, anything needed should be in IOr4
prussell 23:78aad4e53ae2 484 } else {
prussell 23:78aad4e53ae2 485 DEBUG("\n Unknown onRead(tHandle:%d)\n\n", tHandle);
prussell 23:78aad4e53ae2 486 }
prussell 23:78aad4e53ae2 487 }
prussell 23:78aad4e53ae2 488 #endif
prussell 23:78aad4e53ae2 489
prussell 12:8bac5f5d3a3e 490 static volatile bool bNotifySent = false; //Volatile, don't optimize, changes under interrupt control
prussell 12:8bac5f5d3a3e 491 static volatile unsigned uNotifyBLE;
prussell 12:8bac5f5d3a3e 492 void Callback_BLE_onNotifySent(unsigned uNotify) //TODO: Consider renaming to onNotifySent(uNotified)
prussell 12:8bac5f5d3a3e 493 { // Appears to get called when a Characteristic flagged for Notify is Updated/Sent (not for a characteristic being Read by the app).
prussell 12:8bac5f5d3a3e 494 uNotifyBLE=uNotify;
prussell 12:8bac5f5d3a3e 495 DEBUG(" Senti(%u)", uNotifyBLE); //TODO: PR: Why uSent always "1", expected it to match sent bytes length
prussell 12:8bac5f5d3a3e 496 bNotifySent = true;
prussell 0:0217a862b047 497 }
prussell 0:0217a862b047 498
prussell 0:0217a862b047 499 void Callback_BLE_onDataWritten(const GattCharacteristicWriteCBParams *pParams)
prussell 9:2d11beda333f 500 { // 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 501 GattAttribute::Handle_t tHandle=pParams->charHandle;
prussell 23:78aad4e53ae2 502 uint16_t uLen;
prussell 0:0217a862b047 503
prussell 0:0217a862b047 504 //Warning: *data may not be NULL terminated
prussell 9:2d11beda333f 505 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 506
prussell 10:ee3a359f7d3f 507 /* //TODO: Add check of op type with tHandle check: switch(pParams->op){ case: GATTS_CHAR_OP_WRITE_REQ:}...
prussell 10:ee3a359f7d3f 508 switch(pParams->op){
prussell 10:ee3a359f7d3f 509 case GATTS_CHAR_OP_INVALID:
prussell 10:ee3a359f7d3f 510 case GATTS_CHAR_OP_WRITE_REQ
prussell 10:ee3a359f7d3f 511 case GATTS_CHAR_OP_WRITE_CMD
prussell 10:ee3a359f7d3f 512 case GATTS_CHAR_OP_SIGN_WRITE_CMD //< Signed Write Command
prussell 10:ee3a359f7d3f 513 case GATTS_CHAR_OP_PREP_WRITE_REQ //< Prepare Write Request
prussell 10:ee3a359f7d3f 514 case GATTS_CHAR_OP_EXEC_WRITE_REQ_CANCEL //< Execute Write Request: Cancel all prepared writes
prussell 10:ee3a359f7d3f 515 case GATTS_CHAR_OP_EXEC_WRITE_REQ_NOW //< Execute Write Request: Immediately execute all prepared writes
prussell 10:ee3a359f7d3f 516 default: Error?
prussell 10:ee3a359f7d3f 517 }*/
prussell 9:2d11beda333f 518
prussell 10:ee3a359f7d3f 519 //a These are equivalent ways of accessing characteristic handles:
prussell 10:ee3a359f7d3f 520 //a if (pParams->charHandle == IOw8.getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); }
prussell 10:ee3a359f7d3f 521 //a if (pParams->charHandle == ServiceIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); }
prussell 10:ee3a359f7d3f 522 //a if (IOw8.getValueHandle() == ServiceIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle equivalent %d\n\n", pParams->charHandle); }
prussell 9:2d11beda333f 523
prussell 9:2d11beda333f 524 //Handle Changes to Service Characteristics:
prussell 10:ee3a359f7d3f 525 if(!ble.getGapState().connected){ //Ensure BLE still connected
prussell 10:ee3a359f7d3f 526 DEBUG("BLEi: Callback_BLE_onDataWritten() while disconnected!!");
prussell 23:78aad4e53ae2 527 } else if (tHandle == IOw8.getValueHandle()) { // This occurs from Write by App nRF-MCP since has Write Property set
prussell 23:78aad4e53ae2 528 ble.readCharacteristicValue(tHandle, sIOw8.pu, &uLen); //Update Characteristic with new information (Option: Verify length or a checksum first)
prussell 23:78aad4e53ae2 529 vShowIO();
prussell 23:78aad4e53ae2 530 bAppControl = true; //Host has taken control of LEDs
prussell 23:78aad4e53ae2 531 fL1level = (((float)sIOw8.s.L1pwm100)/100.0); if(fL1level>1.0){fL1level=1.0;}; fL1pwm=fL1level; //Set LED1 Level
prussell 23:78aad4e53ae2 532 fL2level = (((float)sIOw8.s.L2pwm255)/255.0); if(fL2level>1.0){fL2level=1.0;}; fL2pwm=fL2level; //Set LED1 Level
prussell 23:78aad4e53ae2 533 DEBUG(" L1:%d==%f L2:%d==%f\n", sIOw8.s.L1pwm100, fL1level, sIOw8.s.L2pwm255, fL2level);
prussell 23:78aad4e53ae2 534 //TODO: Update Outputs and settings (Includes flags for: Factory Reset, Enter Test Mode, Enter Demo Mode)
prussell 23:78aad4e53ae2 535
prussell 23:78aad4e53ae2 536 //} else if (tHandle == LinkLossHandle) { // TODO: Handle LinkLoss Changes
prussell 23:78aad4e53ae2 537 //x pServiceLinkLoss->setAlertLevel( AlertLevel_t newLevel )
prussell 23:78aad4e53ae2 538 //x Can be Triggered by MCP or BluetoothLEGatt sample changing Linkloss setting:
prussell 23:78aad4e53ae2 539 //x Alert=1: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x01]=Data[])
prussell 23:78aad4e53ae2 540 //x Alert=2: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x02]=Data[])
prussell 23:78aad4e53ae2 541 //} else if (tHandle == IOr4.getValueHandle()) { // Readonly, shouldn't occur
prussell 23:78aad4e53ae2 542 //} else if (tHandle == IOn4.getValueHandle()) { // Notifyonly, shouldn't occur
prussell 9:2d11beda333f 543 } else {
prussell 23:78aad4e53ae2 544 DEBUG("\n Unknown onWrite(tHandle:%d)\n\n", tHandle);
prussell 9:2d11beda333f 545 }
prussell 0:0217a862b047 546 }
prussell 0:0217a862b047 547
prussell 15:b2c8bdef2d20 548 void Callback_BLE_onUpdatesEnabled(Gap::Handle_t tHandle) // Notifications
prussell 15:b2c8bdef2d20 549 { //Triggered when click the UpdateContinuousIcon in nRF-MCP(ThreeDownArrows)
prussell 17:93538044f003 550 if (tHandle == IOn4.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - IOn4 == Use Buttons to Trigger Update\n", tHandle); //This Occurs when selected by App nRF-MCP as has Notify property set
prussell 16:3e83f2babdfb 551 } else if (tHandle == IOr4.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - IOr4\n", tHandle); // Notify not enabled on this Characteristic
prussell 16:3e83f2babdfb 552 } else if (tHandle == IOw8.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - IOw8\n", tHandle); // Notify not enabled on this Characteristic
prussell 16:3e83f2babdfb 553 } else { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - Characteristic?\n", tHandle);
prussell 15:b2c8bdef2d20 554 }
prussell 16:3e83f2babdfb 555 //TODO: process Enable for Each Notify Characteriistic of each Service
prussell 15:b2c8bdef2d20 556 }
prussell 15:b2c8bdef2d20 557
prussell 15:b2c8bdef2d20 558 void Callback_BLE_onUpdatesDisabled(Gap::Handle_t tHandle) // Notifications
prussell 10:ee3a359f7d3f 559 { //Triggered when click the UpdateContinuousIcon in nRF-MCP(ThreeDownArrows)
prussell 16:3e83f2babdfb 560 if (tHandle == IOn4.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Disabled - IOn4\n", tHandle); //This Occurs when selected by App nRF-MCP as has Notify property set
prussell 16:3e83f2babdfb 561 } else if (tHandle == IOr4.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Disabled - IOr4\n", tHandle); // Notify not enabled on this Characteristic
prussell 16:3e83f2babdfb 562 } else if (tHandle == IOw8.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Disabled - IOw8\n", tHandle); // Notify not enabled on this Characteristic
prussell 16:3e83f2babdfb 563 } else { DEBUG("\nBLEi: onUpdates(Handle:%d) Disabled - Characteristic?\n", tHandle);
prussell 9:2d11beda333f 564 }
prussell 16:3e83f2babdfb 565 //TODO: process Disable for Each Notify Characteriistic of each Service
prussell 0:0217a862b047 566 }
prussell 15:b2c8bdef2d20 567 void Callback_BLE_onConfirmRx(Gap::Handle_t tHandle) // Confirmations??
prussell 15:b2c8bdef2d20 568 {
prussell 15:b2c8bdef2d20 569 DEBUG("\nBLEi: ConfirmationRx(Handle:%d)\n", tHandle);
prussell 15:b2c8bdef2d20 570 }
prussell 15:b2c8bdef2d20 571
prussell 0:0217a862b047 572
prussell 5:d36bbb315e31 573 // Adopted 20141213 from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LinkLoss/file/440ee5e8595f/main.cpp
prussell 5:d36bbb315e31 574 void Callback_BLE_onLinkLoss(LinkLossService::AlertLevel_t level)
prussell 5:d36bbb315e31 575 {
prussell 10:ee3a359f7d3f 576 printf("\nBLEi: Link Loss Alert, Level:%d\n", level);
prussell 10:ee3a359f7d3f 577 //TODO: Handle Link Loss, maybe lower power mode and/or advertising mode
prussell 5:d36bbb315e31 578 }
prussell 10:ee3a359f7d3f 579 //==========BLE:HRM==========
prussell 8:f187ba55aed2 580 //Adopted 2014Dec from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/
prussell 8:f187ba55aed2 581 uint8_t update_hrm(void)//(bool bInit)
prussell 8:f187ba55aed2 582 {
prussell 8:f187ba55aed2 583 static uint8_t u8_hrm = 100;
prussell 10:ee3a359f7d3f 584 if (++u8_hrm >= 175) {
prussell 8:f187ba55aed2 585 u8_hrm = 100;
prussell 10:ee3a359f7d3f 586 DEBUG(" HRM>100");
prussell 10:ee3a359f7d3f 587 } else {
prussell 10:ee3a359f7d3f 588 DEBUG(" HRM:%d", u8_hrm);
prussell 10:ee3a359f7d3f 589 }
prussell 8:f187ba55aed2 590 pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE
prussell 8:f187ba55aed2 591 return(u8_hrm);
prussell 8:f187ba55aed2 592 }
prussell 10:ee3a359f7d3f 593 //==========BLE:HTM(Using nRF Internal Temperature)==========
prussell 10:ee3a359f7d3f 594 // *If not using nRF51822 IC then change this to a simple counter like BLE:Battery section
prussell 10:ee3a359f7d3f 595 // Adopted 2014Dec from: https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/
prussell 8:f187ba55aed2 596 // Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml
prussell 8:f187ba55aed2 597 // HTM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml
prussell 9:2d11beda333f 598 //****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 599 float update_htm(void)
prussell 8:f187ba55aed2 600 {
prussell 8:f187ba55aed2 601 //static float fTemperature = 0;//-123.456;
prussell 8:f187ba55aed2 602 int32_t i32_temp;
prussell 12:8bac5f5d3a3e 603 sd_temp_get(&i32_temp); //Read the nRF Internal Temperature (Die in 0.25'C steps, Offset:TBD), TODO:Check Scaling
prussell 8:f187ba55aed2 604 float fTemperature = (float(i32_temp)/4.0) - 16.0; // Scale&Shift (0.25'C from -16'C?)
prussell 8:f187ba55aed2 605
prussell 8:f187ba55aed2 606 //{//Force to IEEE format to match needs of Apps like nRF-HTM and nRF-Toolbox:HTM
prussell 8:f187ba55aed2 607 // 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 608 // uint8_t exponent = 0xFE; //exponent is -2
prussell 8:f187ba55aed2 609 // uint32_t mantissa = (uint32_t)(fTemperature*100);
prussell 8:f187ba55aed2 610 // uint32_t temp_ieee11073 = ((((uint32_t)exponent) << 24) | (mantissa)); //Note: Assumes Mantissa within 24bits
prussell 8:f187ba55aed2 611 // memcpy(((uint8_t*)&fTemperature)+1, (uint8_t*)&temp_ieee11073, 4); //Overwrite with IEEE format float
prussell 8:f187ba55aed2 612 //}
prussell 8:f187ba55aed2 613
prussell 8:f187ba55aed2 614 pServiceHTM->updateTemperature( fTemperature );// Update Characteristic so sent by BLE
prussell 10:ee3a359f7d3f 615 DEBUG(" HTM%03d==%2.2f", i32_temp, fTemperature);
prussell 8:f187ba55aed2 616 return(fTemperature);
prussell 8:f187ba55aed2 617 }
prussell 10:ee3a359f7d3f 618 //==========BLE:Battery==========
prussell 8:f187ba55aed2 619 uint8_t update_batt(void)
prussell 8:f187ba55aed2 620 {
prussell 8:f187ba55aed2 621 static uint8_t u8_BattPercent=33; //Level: 0..100%
prussell 8:f187ba55aed2 622 u8_BattPercent <= 50 ? u8_BattPercent=100 : u8_BattPercent--; // Simulate Battery Decay
prussell 8:f187ba55aed2 623 pServiceBattery->updateBatteryLevel( u8_BattPercent ); // Update Characteristic so sent by BLE
prussell 10:ee3a359f7d3f 624 DEBUG(" Batt%03d%%", u8_BattPercent);
prussell 8:f187ba55aed2 625 return(u8_BattPercent);
prussell 8:f187ba55aed2 626 }
prussell 9:2d11beda333f 627
prussell 10:ee3a359f7d3f 628 //==========Functions:Timer==========
prussell 11:7d02fe5ebea5 629 static volatile uint8_t uTicker1;//Volatile, don't optimize, changes under interrupt control
prussell 10:ee3a359f7d3f 630 void CallbackTicker1(void)
prussell 10:ee3a359f7d3f 631 {
prussell 11:7d02fe5ebea5 632 static uint32_t u32_Counter; // Counter for checking Timing
prussell 11:7d02fe5ebea5 633 DEBUG("\nBLEi: Ticker(%04u) ", ++u32_Counter);
prussell 12:8bac5f5d3a3e 634 if (!bAppControl) { fL1level+=0.1; if (fL1level>0.5){fL1level = 0.1;}; fL1pwm=fL1level;}//If host not controlling LED then ramp blink to show life
prussell 11:7d02fe5ebea5 635 uTicker1++; // Flag event, using counter so can detect missed events
prussell 10:ee3a359f7d3f 636 }
prussell 11:7d02fe5ebea5 637
prussell 0:0217a862b047 638 //==========main==========
prussell 0:0217a862b047 639 int main(void)
prussell 12:8bac5f5d3a3e 640 {
prussell 14:b968df367145 641 fL1level = 1.0; fL1pwm = fL1level;//Start LED1=OnMax
prussell 14:b968df367145 642 fL2level = 0.2; fL2pwm = fL2level;//Start LED2=Dim
prussell 6:5b6fb35b4450 643
prussell 6:5b6fb35b4450 644 //Restart TeraTerm just before Pressing Reset on mbed, 9600-8N1(No Flow Control)
prussell 6:5b6fb35b4450 645 DEBUG("\nBLE: ___%s___\n", pcDeviceName);
prussell 14:b968df367145 646
prussell 14:b968df367145 647 //ARM Predefines: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/BABJFEFG.html
prussell 14:b968df367145 648 // *Some are numbers not strings so printf(%s) not always appropriate
prussell 14:b968df367145 649 DEBUG(" ARM Compiler Predefines:\n");
prussell 14:b968df367145 650 DEBUG(" Built UTC:[ %s %s ]\n", __DATE__, __TIME__);
prussell 14:b968df367145 651 DEBUG(" __arm__[%d]", __arm__);
prussell 14:b968df367145 652 DEBUG(" __ARMCC_VERSION [%d]=[P:major V:minor bbbb:build]\n", __ARMCC_VERSION);
prussell 14:b968df367145 653 DEBUG(" __VERSION__(gnu Compiler)[" __VERSION__ "]\n");
prussell 14:b968df367145 654 //DEBUG(" __STDC_VERSION__ [%d]\n", __STDC_VERSION__);
prussell 14:b968df367145 655 //DEBUG(" __BIG_ENDIAN[%d] ", __BIG_ENDIAN);
prussell 14:b968df367145 656 DEBUG(" __OPTIMISE_LEVEL[%d]", __OPTIMISE_LEVEL);
prussell 14:b968df367145 657 //DEBUG(" __OPTIMISE_SPACE[%d]", __OPTIMISE_SPACE);
prussell 14:b968df367145 658 DEBUG(" __OPTIMISE_TIME[%d]", __OPTIMISE_TIME);
prussell 14:b968df367145 659 DEBUG("\n __MODULE__[" __MODULE__ "] __FILE__[" __FILE__ "] __BASE_FILE__[" __BASE_FILE__ "]\n");
prussell 14:b968df367145 660 DEBUG(" __FUNCTION__[%s] __PRETTY_FUNCTION__[%s]\n", __FUNCTION__, __PRETTY_FUNCTION__)
prussell 14:b968df367145 661
prussell 15:b2c8bdef2d20 662 //vShowIO(); Show Raw initialized values before any BLE
prussell 0:0217a862b047 663
prussell 0:0217a862b047 664 Ticker ticker1; //PR: Timer Object(Structure)
prussell 12:8bac5f5d3a3e 665 ticker1.attach(CallbackTicker1, 5.0); //PR: Timer Handler, Float=PeriodSeconds (Note BLE default wakes about 50Hz/20msec)
prussell 0:0217a862b047 666
prussell 3:a98203f84063 667 //BLE1: Setup BLE Service (and event actions) //TODO: Check for services declared before main() - Is that OK?
prussell 14:b968df367145 668 DEBUG("\nBLE: Connect App for Data: nRF-MCP, nRF-Toolbox:HRM/HTM, Android Sample BluetoothLeGatt, etc.\n");
prussell 14:b968df367145 669 DEBUG(" nRF-MCP: App->mbed: LinkLoss->AlertLevel(UpArrow)\n");
prussell 14:b968df367145 670 DEBUG(" nRF-MCP: App->mbed: BatteryService->BatteryLevel(DownArrow)\n");
prussell 14:b968df367145 671 DEBUG(" nRF-MCP: mbed->App: BatteryService->BatteryLevel->EnableNotify(ThreeDownArrows), Also App Acks the send=DEBUG('SentI')\n");
prussell 14:b968df367145 672
prussell 3:a98203f84063 673 DEBUG("BLE: Setup BLE\n");
prussell 0:0217a862b047 674 ble.init();
prussell 12:8bac5f5d3a3e 675 ble.onDisconnection(Callback_BLE_onDisconnect); //PR: Host disconnects, restart advertising, clear any unnecessary functions to save power
prussell 12:8bac5f5d3a3e 676 ble.onConnection(Callback_BLE_onConnect); //PR: Host connects (Not required if no actions enabled, enabled now just for debug)
prussell 12:8bac5f5d3a3e 677 ble.onDataSent(Callback_BLE_onNotifySent); //PR: Occurs when a Notify Characteristic has been updated and sent over BLE
prussell 12:8bac5f5d3a3e 678 ble.onDataWritten(Callback_BLE_onDataWritten); //PR: Occurs when an update to a Characteristic has been received over BLE
prussell 12:8bac5f5d3a3e 679 ble.onTimeout(Callback_BLE_onTimeout); //PR: ??? Hasn't occured, TODO: Monitor and find out what causes this
prussell 12:8bac5f5d3a3e 680 ble.onUpdatesEnabled(Callback_BLE_onUpdatesEnabled);//PR: Occurs when host enables notify on a Characteristic that has Notify property set
prussell 15:b2c8bdef2d20 681 ble.onUpdatesDisabled(Callback_BLE_onUpdatesDisabled); //TODO:
prussell 15:b2c8bdef2d20 682 ble.onConfirmationReceived(Callback_BLE_onConfirmRx); //TODO:
prussell 23:78aad4e53ae2 683 //NotAvailableOnlineYet20150114 ble.setReadAuthorizationCallback(Callback_BLE_onDataRead);//TODO: Test new callback "setReadAuthorizationCallback()", then can read sensors live instead of polling them continuously.
prussell 8:f187ba55aed2 684
prussell 6:5b6fb35b4450 685 //BLE2: Setup Services (with their initial values and options)
prussell 3:a98203f84063 686 DEBUG("BLE: Setup Services\n");
prussell 4:976394791d7a 687 // *Order here affects order in nRF-MCP Discovery of Services
prussell 8:f187ba55aed2 688 DeviceInformationService ServiceDeviceInfo(ble, "Maker", pcDeviceName, "sn1234", "hw00", "fw00", "sw00");//(BLEDevice), pcManufacturer, pcModelNumber, pcSerialNumber, pcHWver, pcFWver, pcSWver
prussell 9:2d11beda333f 689 pServiceDeviceInfo = &ServiceDeviceInfo; //DEBUG(" Handle Service DeviceInfo:%d\n", ServiceDeviceInfo.XXXgetHandle());
prussell 8:f187ba55aed2 690 LinkLossService ServiceLinkLoss(ble, Callback_BLE_onLinkLoss, LinkLossService::HIGH_ALERT); //New20141213, TBD
prussell 8:f187ba55aed2 691 pServiceLinkLoss = &ServiceLinkLoss;
prussell 8:f187ba55aed2 692 BatteryService ServiceBattery(ble, 10);
prussell 8:f187ba55aed2 693 pServiceBattery = &ServiceBattery;
prussell 8:f187ba55aed2 694 HeartRateService ServiceHRM(ble, (uint8_t)111, HeartRateService::LOCATION_FINGER);
prussell 8:f187ba55aed2 695 pServiceHRM = &ServiceHRM;
prussell 8:f187ba55aed2 696 HealthThermometerService ServiceHTM(ble, 33.3, HealthThermometerService::LOCATION_EAR);
prussell 8:f187ba55aed2 697 pServiceHTM = &ServiceHTM;
prussell 8:f187ba55aed2 698 //UARTService ServiceUART(ble);
prussell 8:f187ba55aed2 699 // pServiceUART = &ServiceUART;
prussell 5:d36bbb315e31 700
prussell 12:8bac5f5d3a3e 701 DEBUG("BLE: Setup Custom IO Service\n");
prussell 10:ee3a359f7d3f 702 ble.addService(ServiceIO); //Pointer: pServiceIO
prussell 15:b2c8bdef2d20 703 vShowIO(); // Show Initialized values with bleIO initialized (includes tHandle)
prussell 12:8bac5f5d3a3e 704 //TODO: Ensure outputs in correct Initial state
prussell 9:2d11beda333f 705
prussell 3:a98203f84063 706 //BLE3: Setup advertising
prussell 10:ee3a359f7d3f 707 // 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 708 // 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 709 DEBUG("BLE: Setup Advertising - Apps like HRM/HTM/UART require matching UUID\n");
prussell 6:5b6fb35b4450 710 ble.clearAdvertisingPayload(); //Prep
prussell 6:5b6fb35b4450 711 ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000)); //PR: Advertise 1sec (1Hz)
prussell 6:5b6fb35b4450 712 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); //PR: TODO: To Study
prussell 6:5b6fb35b4450 713 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); //PR: BLE Only (Option:LE_LIMITED_DISCOVERABLE)
prussell 6:5b6fb35b4450 714 // Does "LE_GENERAL_DISCOVERABLE" affect whether UUID needs to be advertised to discover services?
prussell 10:ee3a359f7d3f 715 //a ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // Multiple UUID16 for Standard Services
prussell 11:7d02fe5ebea5 716 ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_IOAdvertise, sizeof(puUUID128_IOAdvertise)); //Single UUID128 for primary Service
prussell 11:7d02fe5ebea5 717
prussell 10:ee3a359f7d3f 718 //? PR: I'm not sure what these lines do, they were inherited from an example:
prussell 10:ee3a359f7d3f 719 //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
prussell 10:ee3a359f7d3f 720 //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER);
prussell 9:2d11beda333f 721
prussell 6:5b6fb35b4450 722 // Add LocalName last so if Advertising too long will easily see as Name won't be available for the device.
prussell 6:5b6fb35b4450 723 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)pcDeviceName, sizeof(pcDeviceName)-1);//PR: LocalName (No NULL)
prussell 6:5b6fb35b4450 724 ble.startAdvertising();
prussell 3:a98203f84063 725
prussell 10:ee3a359f7d3f 726 //Example Advertising (Max 31 bytes): 0x01020304050607080910111213141516171819202122232425262728293031
prussell 6:5b6fb35b4450 727 // Example Raw Advertising caught by App nRF-MCP: 0x020106070309180A180D180909626C655052763034
prussell 6:5b6fb35b4450 728 // = Len02 Type01 Value06 (Flags: BREDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE)
prussell 6:5b6fb35b4450 729 // = Len07 Type03 Values: 0918 0A18 0D18 (ListUUID16: 1809 180A 180D )
prussell 6:5b6fb35b4450 730 // = Len09 Type09 Values: 62 6C 65 50 52 76 30 34 (LocalName = "blePRv04")
prussell 6:5b6fb35b4450 731 // Example Raw Advertising caught by App nRF-MCP: 0x0201061106FB349B5F80000080001000000F180000070209180A180D18
prussell 6:5b6fb35b4450 732 // = Len02 Type01 Value06 (Flags: BREDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE)
prussell 6:5b6fb35b4450 733 // = Len11 Type06 ValueFB349B5F8000008000100000_0F18_0000 (UUID128: BluetoothBattery=0x180F)
prussell 6:5b6fb35b4450 734 // = Len07 Type02 Value 0918 0A18 0D18 (UUID16: 1809 180A 180D )
prussell 6:5b6fb35b4450 735 // = LocalName field wasn't appended as insufficient space, so Device won't be named when scanning.
prussell 0:0217a862b047 736
prussell 12:8bac5f5d3a3e 737 // Setup InterruptIn for Buttons (coded for nRF51822-mkit polarities on these buttons)
prussell 11:7d02fe5ebea5 738 DEBUG("BLE: Enable Button Interrupts\n");
prussell 12:8bac5f5d3a3e 739 B1int.fall(&onB1press); // Button1 Press/fall
prussell 12:8bac5f5d3a3e 740 B1int.rise(&onB1release); // Button1 Release/rise
prussell 12:8bac5f5d3a3e 741 B2int.fall(&onB2press); // Button2 Press/fall
prussell 12:8bac5f5d3a3e 742 B2int.rise(&onB2release); // Button2 Release/rise
prussell 11:7d02fe5ebea5 743
prussell 0:0217a862b047 744 DEBUG("BLE: Main Loop\n");
prussell 0:0217a862b047 745 uint32_t u32_wakeevents=0, u32_wakelast=0; // Counter&Register for tracking Wake Events (to see monitor their Frequency)
prussell 0:0217a862b047 746 while (true) {
prussell 11:7d02fe5ebea5 747 if (uTicker1 && ble.getGapState().connected) { //If Ticker1 and Connected then Update Appropriate Data
prussell 11:7d02fe5ebea5 748 uTicker1 = 0; // Clear flag for next Ticker1, see CallbackTicker1(), TODO: log if missed any ticks
prussell 3:a98203f84063 749
prussell 3:a98203f84063 750 // Read Sensors, and update matching Service Characteristics (only if connected)
prussell 4:976394791d7a 751 // *Order here doesn't affect order in nRF-MCP Discovery of Services
prussell 6:5b6fb35b4450 752 //TBD: Maybe save power by not Tx unless enabled by App?
prussell 8:f187ba55aed2 753 // The Services are discovered if they were setup/started (They don't need update to be discovered)
prussell 8:f187ba55aed2 754 update_htm();
prussell 8:f187ba55aed2 755 update_hrm();
prussell 8:f187ba55aed2 756 update_batt();
prussell 8:f187ba55aed2 757
prussell 10:ee3a359f7d3f 758 //a vUpdate_IOw8();
prussell 12:8bac5f5d3a3e 759 //a vUpdate_IOr4();
prussell 12:8bac5f5d3a3e 760 //a vUpdate_IOn4();
prussell 9:2d11beda333f 761
prussell 12:8bac5f5d3a3e 762 DEBUG(" BLE:Wakes:%04u,Delta:%03u ", u32_wakeevents, u32_wakeevents-u32_wakelast); //For Evaluating Timing
prussell 3:a98203f84063 763 u32_wakelast = u32_wakeevents;
prussell 11:7d02fe5ebea5 764 } else if (uTicker1) {
prussell 11:7d02fe5ebea5 765 uTicker1 = 0; // Clear flag for next Ticker1, see CallbackTicker1(), TODO: log if missed any ticks
prussell 12:8bac5f5d3a3e 766 DEBUG(" BLE: Tick while unconnected ");
prussell 12:8bac5f5d3a3e 767 } else if (bNotifySent){
prussell 12:8bac5f5d3a3e 768 bNotifySent=false; //clear flag Notify Characteristic Transmitted
prussell 12:8bac5f5d3a3e 769 DEBUG(" BLE: Notify(%u) ", uNotifyBLE);
prussell 0:0217a862b047 770 } else {
prussell 11:7d02fe5ebea5 771 //DEBUG("BLE: Wait for Event\n\r"); //x Debug output here causes endless wakes from sleep()
prussell 12:8bac5f5d3a3e 772 ble.waitForEvent(); //PR: Like sleep() with ble handling. TODO: Handle Error return
prussell 0:0217a862b047 773 u32_wakeevents++; //PR: Count events for frequency monitoring (20141207PR: nRF51822 mbed HRM = 50Hz)
prussell 12:8bac5f5d3a3e 774 if (!bAppControl) { fL2level+=0.25; if (fL2level>0.5){fL2level = 0.0;}; fL2pwm=fL2level;} //If host not controling LED then Ramp Blink to show life
prussell 12:8bac5f5d3a3e 775 }
prussell 0:0217a862b047 776 }
prussell 0:0217a862b047 777 }
prussell 10:ee3a359f7d3f 778 //========== end of main.cpp ==========
prussell 23:78aad4e53ae2 779
prussell 23:78aad4e53ae2 780