This program is given as a sample exercise. It has all the functionality to be used on a BLE Nano device and to connect to SimpleChat application for Android/ iOS from RebBearLab. The aim of the exercise is to read a voltage and then to convert as good as possible the appropriate temperature in Celsius degrees. AI5 pin is considered for reading the voltage for the termistor. The ADC of AI5 is called every second. The function to be updated : update_measurements() from main.cpp file.

Dependencies:   BLE_API mbed nRF51822

Fork of nRF51822_DataLogger_with_Chat by Valentin Tanasa

Committer:
tanasaro10
Date:
Fri May 06 18:14:18 2016 +0000
Revision:
11:baafa4f7a15e
Parent:
10:c7d53e4e0602
Child:
12:7772974713ac
minor updates

Who changed what in which revision?

UserRevisionLine numberNew contents of line
RedBearLab 1:1c058e553423 1 /*
RedBearLab 0:cffe8ac1bdf0 2
RedBearLab 1:1c058e553423 3 Copyright (c) 2012-2014 RedBearLab
RedBearLab 1:1c058e553423 4
tanasaro10 9:303d3628986a 5 Permission is hereby granted, free of charge, to any person obtaining a copy of this software
tanasaro10 9:303d3628986a 6 and associated documentation files (the "Software"), to deal in the Software without restriction,
tanasaro10 9:303d3628986a 7 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
tanasaro10 9:303d3628986a 8 and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
RedBearLab 1:1c058e553423 9 subject to the following conditions:
RedBearLab 1:1c058e553423 10 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
RedBearLab 1:1c058e553423 11
tanasaro10 9:303d3628986a 12 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
tanasaro10 9:303d3628986a 13 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
tanasaro10 9:303d3628986a 14 PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
tanasaro10 9:303d3628986a 15 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
RedBearLab 1:1c058e553423 16 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
RedBearLab 1:1c058e553423 17
RedBearLab 1:1c058e553423 18 */
RedBearLab 1:1c058e553423 19
RedBearLab 1:1c058e553423 20 /*
RedBearLab 1:1c058e553423 21 * The application works with the BLEController iOS/Android App.
RedBearLab 1:1c058e553423 22 * Type something from the Terminal to send
RedBearLab 1:1c058e553423 23 * to the BLEController App or vice verse.
RedBearLab 1:1c058e553423 24 * Characteristics received from App will print on Terminal.
tanasaro10 9:303d3628986a 25 * Read read_me.md file for more informations about the extended feature
RedBearLab 1:1c058e553423 26 */
tanasaro10 9:303d3628986a 27
tanasaro10 9:303d3628986a 28
RedBearLab 2:4b66b69c7ecb 29 #include "ble/BLE.h"
tanasaro10 11:baafa4f7a15e 30 //#include "LowPowerTicker.h"
tanasaro10 8:f28ad4600b0f 31 #include <myData.h>
tanasaro10 9:303d3628986a 32 #include <Gap.h>
tanasaro10 11:baafa4f7a15e 33 //#include "ble_flash.h"
tanasaro10 9:303d3628986a 34 #include "ble_flash.c"
RedBearLab 0:cffe8ac1bdf0 35
RedBearLab 0:cffe8ac1bdf0 36 #define BLE_UUID_TXRX_SERVICE 0x0000 /**< The UUID of the Nordic UART Service. */
RedBearLab 0:cffe8ac1bdf0 37 #define BLE_UUID_TX_CHARACTERISTIC 0x0002 /**< The UUID of the TX Characteristic. */
RedBearLab 0:cffe8ac1bdf0 38 #define BLE_UUIDS_RX_CHARACTERISTIC 0x0003 /**< The UUID of the RX Characteristic. */
RedBearLab 0:cffe8ac1bdf0 39
tanasaro10 9:303d3628986a 40 #define TXRX_BUF_LEN 20 /** For radio message transmission*/
tanasaro10 9:303d3628986a 41
tanasaro10 9:303d3628986a 42 #define MyASSERT(cond , serialpc, errVal) assert_error_app((bool)cond, serialpc, (uint16_t)errVal, __LINE__, __FILE__)
RedBearLab 0:cffe8ac1bdf0 43
RedBearLab 2:4b66b69c7ecb 44 BLE ble;
RedBearLab 0:cffe8ac1bdf0 45
RedBearLab 0:cffe8ac1bdf0 46 Serial pc(USBTX, USBRX);
RedBearLab 0:cffe8ac1bdf0 47
RedBearLab 0:cffe8ac1bdf0 48 // The Nordic UART Service
RedBearLab 0:cffe8ac1bdf0 49 static const uint8_t uart_base_uuid[] = {0x71, 0x3D, 0, 0, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
RedBearLab 0:cffe8ac1bdf0 50 static const uint8_t uart_tx_uuid[] = {0x71, 0x3D, 0, 3, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
RedBearLab 0:cffe8ac1bdf0 51 static const uint8_t uart_rx_uuid[] = {0x71, 0x3D, 0, 2, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
RedBearLab 0:cffe8ac1bdf0 52 static const uint8_t uart_base_uuid_rev[] = {0x1E, 0x94, 0x8D, 0xF1, 0x48, 0x31, 0x94, 0xBA, 0x75, 0x4C, 0x3E, 0x50, 0, 0, 0x3D, 0x71};
RedBearLab 0:cffe8ac1bdf0 53
tanasaro10 8:f28ad4600b0f 54 static const int8_t txPower = 0xCD;
RedBearLab 0:cffe8ac1bdf0 55
tanasaro10 11:baafa4f7a15e 56 uint8_t txPayload[TXRX_BUF_LEN] = {0, p28};
RedBearLab 0:cffe8ac1bdf0 57 uint8_t rxPayload[TXRX_BUF_LEN] = {0,};
RedBearLab 0:cffe8ac1bdf0 58
RedBearLab 0:cffe8ac1bdf0 59 static uint8_t rx_buf[TXRX_BUF_LEN];
RedBearLab 0:cffe8ac1bdf0 60 static uint8_t rx_len=0;
RedBearLab 0:cffe8ac1bdf0 61
tanasaro10 9:303d3628986a 62 static uint32_t gTimeInstant = 1; // TimerTick Resolution, in seconds
tanasaro10 8:f28ad4600b0f 63
tanasaro10 10:c7d53e4e0602 64 bool g_bIsConnected = false;
tanasaro10 9:303d3628986a 65 bool g_bIsAdvertising = false;
tanasaro10 10:c7d53e4e0602 66 bool g_bConnDisabled = false;
tanasaro10 9:303d3628986a 67 bool g_LogActive = false;
tanasaro10 9:303d3628986a 68 static myDataLog_t g_MyData;
tanasaro10 9:303d3628986a 69 uint8_t g_MyDataIdx=0;
tanasaro10 8:f28ad4600b0f 70
tanasaro10 9:303d3628986a 71 // pins connected for measuring
tanasaro10 8:f28ad4600b0f 72 DigitalOut led(LED1);
tanasaro10 8:f28ad4600b0f 73 PwmOut buzzer(p15);
tanasaro10 9:303d3628986a 74 InterruptIn event(p29); // button
tanasaro10 8:f28ad4600b0f 75 AnalogIn VP3(A3);
tanasaro10 8:f28ad4600b0f 76 AnalogIn VP4(A4);
tanasaro10 8:f28ad4600b0f 77 AnalogIn VP5(A5);
tanasaro10 9:303d3628986a 78 AnalogIn* VP[3]= {&VP3,&VP4,&VP5};
tanasaro10 9:303d3628986a 79 #define NUM_OF_READINGS (4u)
tanasaro10 9:303d3628986a 80 myPayload_t g_currMeasures; // last measurements
tanasaro10 8:f28ad4600b0f 81
tanasaro10 8:f28ad4600b0f 82 Timeout timeout_err; // timeout for buzz on error
tanasaro10 9:303d3628986a 83 Ticker periodicActions;
tanasaro10 8:f28ad4600b0f 84
tanasaro10 11:baafa4f7a15e 85 mdatetime_manager_t g_myDateTimeVar;
tanasaro10 8:f28ad4600b0f 86
tanasaro10 9:303d3628986a 87 GattCharacteristic txCharacteristic (uart_tx_uuid, txPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
RedBearLab 0:cffe8ac1bdf0 88
RedBearLab 0:cffe8ac1bdf0 89 GattCharacteristic rxCharacteristic (uart_rx_uuid, rxPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
tanasaro10 9:303d3628986a 90
RedBearLab 0:cffe8ac1bdf0 91 GattCharacteristic *uartChars[] = {&txCharacteristic, &rxCharacteristic};
RedBearLab 0:cffe8ac1bdf0 92
RedBearLab 0:cffe8ac1bdf0 93 GattService uartService(uart_base_uuid, uartChars, sizeof(uartChars) / sizeof(GattCharacteristic *));
RedBearLab 0:cffe8ac1bdf0 94
tanasaro10 9:303d3628986a 95 void sendRadioMsg(const uint8_t* buf, uint16_t length)
tanasaro10 9:303d3628986a 96 {
tanasaro10 9:303d3628986a 97 uint8_t retVal;
tanasaro10 9:303d3628986a 98 retVal = ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, length);
tanasaro10 9:303d3628986a 99 //pc.printf("Err=%d\r\n",retVal);
tanasaro10 9:303d3628986a 100 MyASSERT((retVal!=0),&pc, retVal);
tanasaro10 8:f28ad4600b0f 101 }
tanasaro10 8:f28ad4600b0f 102
RedBearLab 0:cffe8ac1bdf0 103 void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
RedBearLab 0:cffe8ac1bdf0 104 {
RedBearLab 0:cffe8ac1bdf0 105 pc.printf("Disconnected \r\n");
tanasaro10 9:303d3628986a 106 g_bIsConnected = false;
tanasaro10 9:303d3628986a 107 g_bIsAdvertising = false;
tanasaro10 10:c7d53e4e0602 108 pc.printf("R: %d\r",reason);
tanasaro10 10:c7d53e4e0602 109 if (reason != 0x16) {
tanasaro10 10:c7d53e4e0602 110 ble.startAdvertising();
tanasaro10 10:c7d53e4e0602 111 g_bIsAdvertising = true;
tanasaro10 10:c7d53e4e0602 112 }
RedBearLab 0:cffe8ac1bdf0 113 }
RedBearLab 0:cffe8ac1bdf0 114
tanasaro10 9:303d3628986a 115 void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
tanasaro10 9:303d3628986a 116 {
tanasaro10 9:303d3628986a 117 pc.printf("Connected \r\n");
tanasaro10 9:303d3628986a 118 g_bIsConnected = true;
tanasaro10 9:303d3628986a 119 g_bIsAdvertising = false;
tanasaro10 8:f28ad4600b0f 120 }
tanasaro10 8:f28ad4600b0f 121
tanasaro10 9:303d3628986a 122 void at_timeout_err()
tanasaro10 9:303d3628986a 123 {
tanasaro10 9:303d3628986a 124 // stop buzz
tanasaro10 9:303d3628986a 125 buzz_int(&buzzer, 0,0);
tanasaro10 9:303d3628986a 126 }
tanasaro10 10:c7d53e4e0602 127 void connectionUpdate(connection_update_t option)
tanasaro10 10:c7d53e4e0602 128 {
tanasaro10 10:c7d53e4e0602 129 if (g_bConnDisabled == false) {
tanasaro10 10:c7d53e4e0602 130 switch (option) {
tanasaro10 10:c7d53e4e0602 131 case eStartAdvertising: {
tanasaro10 10:c7d53e4e0602 132 if ((g_bIsConnected == false)&&(g_bIsAdvertising == false)) {
tanasaro10 10:c7d53e4e0602 133 pc.printf("Start Advertising\r");
tanasaro10 10:c7d53e4e0602 134 ble.startAdvertising();
tanasaro10 10:c7d53e4e0602 135 g_bIsAdvertising = true;
tanasaro10 10:c7d53e4e0602 136 }
tanasaro10 10:c7d53e4e0602 137 break;
tanasaro10 9:303d3628986a 138 }
tanasaro10 10:c7d53e4e0602 139 case eStopAdvertising: {
tanasaro10 10:c7d53e4e0602 140 if (g_bIsAdvertising == true) {
tanasaro10 10:c7d53e4e0602 141 pc.printf("Stop Advertising\r");
tanasaro10 10:c7d53e4e0602 142 ble.stopAdvertising();
tanasaro10 10:c7d53e4e0602 143 g_bIsAdvertising = false;
tanasaro10 10:c7d53e4e0602 144 }
tanasaro10 10:c7d53e4e0602 145 break;
tanasaro10 9:303d3628986a 146 }
tanasaro10 10:c7d53e4e0602 147 case eDisconnect: {
tanasaro10 10:c7d53e4e0602 148 if (g_bIsConnected == true) {
tanasaro10 10:c7d53e4e0602 149 pc.printf("Close connection\r");
tanasaro10 10:c7d53e4e0602 150 ble.disconnect((Gap::DisconnectionReason_t)0x12);
tanasaro10 10:c7d53e4e0602 151 } else if (g_bIsAdvertising == true) {
tanasaro10 10:c7d53e4e0602 152 pc.printf("Stop Advertising\r");
tanasaro10 10:c7d53e4e0602 153 ble.stopAdvertising();
tanasaro10 10:c7d53e4e0602 154 g_bIsAdvertising = false;
tanasaro10 10:c7d53e4e0602 155 }
tanasaro10 10:c7d53e4e0602 156 break;
tanasaro10 9:303d3628986a 157 }
tanasaro10 9:303d3628986a 158 }
tanasaro10 8:f28ad4600b0f 159 }
tanasaro10 9:303d3628986a 160 }
tanasaro10 9:303d3628986a 161 void write_data_to_flash(uint32_t *tick)
tanasaro10 10:c7d53e4e0602 162 {
tanasaro10 9:303d3628986a 163 uint32_t retVal=0;
tanasaro10 9:303d3628986a 164 uint8_t page_num=0;
tanasaro10 10:c7d53e4e0602 165
tanasaro10 9:303d3628986a 166 if (g_MyDataIdx==0) {
tanasaro10 9:303d3628986a 167 //initiate connection
tanasaro10 9:303d3628986a 168 connectionUpdate(eStartAdvertising);
tanasaro10 9:303d3628986a 169 // time and date used to initialize the g_MyData variable
tanasaro10 11:baafa4f7a15e 170 memcpy(&g_MyData.startData.datetime,&g_myDateTimeVar.currentDateTime, sizeof(mdate_time_t));
tanasaro10 9:303d3628986a 171 memcpy(&g_MyData.startData.data,&g_currMeasures, sizeof(myPayload_t));
tanasaro10 9:303d3628986a 172 } else {
tanasaro10 9:303d3628986a 173 // it should be logged here the time difference from last record...
tanasaro10 11:baafa4f7a15e 174 g_MyData.myData[g_MyDataIdx-1].minutes = (uint16_t)(*tick*gTimeInstant / 60);
tanasaro10 11:baafa4f7a15e 175 g_MyData.myData[g_MyDataIdx-1].seconds = (*tick*gTimeInstant% 60);
tanasaro10 10:c7d53e4e0602 176 memcpy(&g_MyData.myData[g_MyDataIdx-1].data,&g_currMeasures, sizeof(myPayload_t));
tanasaro10 9:303d3628986a 177 }
tanasaro10 9:303d3628986a 178 *tick = 0;
tanasaro10 9:303d3628986a 179
tanasaro10 9:303d3628986a 180 if (g_MyDataIdx==(MAXBUFFER-5)) {
tanasaro10 9:303d3628986a 181 //initiate disconnection
tanasaro10 10:c7d53e4e0602 182 connectionUpdate(eDisconnect);
tanasaro10 9:303d3628986a 183 }
tanasaro10 9:303d3628986a 184
tanasaro10 10:c7d53e4e0602 185 if (g_MyDataIdx == MAXBUFFER) {
tanasaro10 9:303d3628986a 186 // write2Flash the current page num
tanasaro10 9:303d3628986a 187 page_num=flash_currPage();
tanasaro10 9:303d3628986a 188 // write2Flash the current page data
tanasaro10 9:303d3628986a 189 retVal=ble_flash_page_write(page_num, (uint32_t*)&(g_MyData), 251u);
tanasaro10 11:baafa4f7a15e 190 pc.printf("retValWr: %d, Pg:%d, Min: %d \r\n",retVal, page_num,g_myDateTimeVar.currentDateTime.minutes);
tanasaro10 9:303d3628986a 191 flash_go_nextPage();
tanasaro10 9:303d3628986a 192 }
tanasaro10 10:c7d53e4e0602 193 g_MyDataIdx = (g_MyDataIdx+1)%(MAXBUFFER+1);
tanasaro10 8:f28ad4600b0f 194 }
tanasaro10 8:f28ad4600b0f 195
tanasaro10 9:303d3628986a 196
tanasaro10 9:303d3628986a 197 void on_error_radioMsg()
tanasaro10 9:303d3628986a 198 {
tanasaro10 9:303d3628986a 199 char myBuf[TXRX_BUF_LEN];
tanasaro10 9:303d3628986a 200
tanasaro10 9:303d3628986a 201 sprintf(myBuf,"%s","WrongSyntax");
tanasaro10 9:303d3628986a 202 buzz_int(&buzzer,5,3);
tanasaro10 9:303d3628986a 203 timeout_err.attach(&at_timeout_err, 2);
tanasaro10 9:303d3628986a 204 sendRadioMsg((uint8_t*)&myBuf[0], 12);
tanasaro10 8:f28ad4600b0f 205 }
tanasaro10 8:f28ad4600b0f 206
tanasaro10 9:303d3628986a 207 void flash_page_serial_dump(uint32_t* p_curr_addr)
tanasaro10 10:c7d53e4e0602 208 {
tanasaro10 10:c7d53e4e0602 209 myDataLogShort_t initialData;
tanasaro10 11:baafa4f7a15e 210 mdate_time_t * pdate;
tanasaro10 10:c7d53e4e0602 211 myDataL_t dataOut[2];
tanasaro10 10:c7d53e4e0602 212 uint8_t i;
tanasaro10 9:303d3628986a 213
tanasaro10 10:c7d53e4e0602 214 p_curr_addr += 2; // skip the magic number and the word count
tanasaro10 9:303d3628986a 215 memcpy((uint32_t*)&initialData, p_curr_addr, 6*sizeof(uint32_t));
tanasaro10 11:baafa4f7a15e 216 pdate = &initialData.startData.datetime;
tanasaro10 11:baafa4f7a15e 217 pc.printf("20%2d_%2d_%2d H:%2d P:%4x\r",pdate->year, pdate->month, pdate->day, pdate->hours, p_curr_addr);
tanasaro10 11:baafa4f7a15e 218 pc.printf("%2d:%2d;%3d;%3d;%3d \r",pdate->minutes, pdate->seconds, initialData.startData.data.light, initialData.startData.data.gndV, initialData.startData.data.temp);
tanasaro10 11:baafa4f7a15e 219 pc.printf("%2d:%2d;%3d;%3d;%3d;%2d\r",initialData.myData.minutes, initialData.myData.seconds, initialData.myData.data.light, initialData.myData.data.gndV, initialData.myData.data.temp);
tanasaro10 9:303d3628986a 220 p_curr_addr += 6;
tanasaro10 10:c7d53e4e0602 221
tanasaro10 9:303d3628986a 222 for (i=0; i<49; i++) {
tanasaro10 9:303d3628986a 223 memcpy((uint32_t*)&dataOut, p_curr_addr, 5*sizeof(uint32_t));
tanasaro10 11:baafa4f7a15e 224 pc.printf("%2d:%2d;%3d;%3d;%3d;%2d\r",dataOut[0].minutes, dataOut[0].seconds, dataOut[0].data.light, dataOut[0].data.gndV, dataOut[0].data.temp, i);
tanasaro10 11:baafa4f7a15e 225 pc.printf("%2d:%2d;%3d;%3d;%3d\r",dataOut[1].minutes, dataOut[1].seconds, dataOut[1].data.light, dataOut[1].data.gndV, dataOut[1].data.temp);
tanasaro10 9:303d3628986a 226 p_curr_addr += 5;
tanasaro10 10:c7d53e4e0602 227 }
tanasaro10 9:303d3628986a 228 }
tanasaro10 9:303d3628986a 229
tanasaro10 9:303d3628986a 230 int update_measurements()
tanasaro10 9:303d3628986a 231 {
tanasaro10 10:c7d53e4e0602 232 int retVal;
tanasaro10 10:c7d53e4e0602 233 static myPayload_t prevMeasures=(myPayload_t) {
tanasaro10 10:c7d53e4e0602 234 0, 0, 0, 0, 0
tanasaro10 10:c7d53e4e0602 235 };
tanasaro10 9:303d3628986a 236
tanasaro10 9:303d3628986a 237 g_currMeasures = (myPayload_t) {
tanasaro10 9:303d3628986a 238 VP[0]->read_u16(), VP[2]->read_u16(), VP[1]->read_u16(), led, 0
tanasaro10 9:303d3628986a 239 };
tanasaro10 9:303d3628986a 240 retVal = memcmp(&g_currMeasures,&prevMeasures,sizeof(myPayload_t));
tanasaro10 9:303d3628986a 241 memcpy(&prevMeasures,&g_currMeasures,sizeof(myPayload_t));
tanasaro10 9:303d3628986a 242 return retVal;
tanasaro10 10:c7d53e4e0602 243 }
tanasaro10 10:c7d53e4e0602 244
tanasaro10 9:303d3628986a 245 void at_eachInstant()
tanasaro10 10:c7d53e4e0602 246 {
tanasaro10 9:303d3628986a 247 static uint32_t tick=0;
tanasaro10 9:303d3628986a 248 int retVal;
tanasaro10 10:c7d53e4e0602 249
tanasaro10 8:f28ad4600b0f 250 // update time
tanasaro10 11:baafa4f7a15e 251 update_time(&g_myDateTimeVar, gTimeInstant);
tanasaro10 9:303d3628986a 252
tanasaro10 9:303d3628986a 253 //update measurements
tanasaro10 9:303d3628986a 254 retVal = update_measurements();
tanasaro10 10:c7d53e4e0602 255
tanasaro10 9:303d3628986a 256 // if there are changes in data save
tanasaro10 9:303d3628986a 257 if ((retVal!=0)&&(g_LogActive==true)) {
tanasaro10 10:c7d53e4e0602 258 write_data_to_flash(&tick);
tanasaro10 9:303d3628986a 259 }
tanasaro10 9:303d3628986a 260 tick++;
tanasaro10 9:303d3628986a 261 }
tanasaro10 9:303d3628986a 262
tanasaro10 9:303d3628986a 263 // Radio commands decode
tanasaro10 9:303d3628986a 264 void decode(uint8_t * buffer, uint16_t length)
tanasaro10 9:303d3628986a 265 {
tanasaro10 9:303d3628986a 266 uint16_t len;
tanasaro10 9:303d3628986a 267 char myBuf[TXRX_BUF_LEN];
tanasaro10 9:303d3628986a 268
tanasaro10 9:303d3628986a 269 switch (buffer[0]) {
tanasaro10 9:303d3628986a 270 case 'i': {// Analog Input Read Request
tanasaro10 9:303d3628986a 271 switch (buffer[1]) {
tanasaro10 9:303d3628986a 272 case '0': {
tanasaro10 9:303d3628986a 273 // display all inputs
tanasaro10 9:303d3628986a 274 sprintf(myBuf,"All:%3d,%3d,%3d,%2d", g_currMeasures.light,g_currMeasures.gndV,g_currMeasures.temp,g_currMeasures.led_on);
tanasaro10 9:303d3628986a 275 len = 18;
tanasaro10 9:303d3628986a 276 break;
tanasaro10 9:303d3628986a 277 }
tanasaro10 9:303d3628986a 278 case '1': {
tanasaro10 9:303d3628986a 279 sprintf(myBuf,"Input 1 = %3d", g_currMeasures.light);
tanasaro10 9:303d3628986a 280 len = 13;
tanasaro10 9:303d3628986a 281 break;
tanasaro10 9:303d3628986a 282 }
tanasaro10 9:303d3628986a 283 case '2': {
tanasaro10 9:303d3628986a 284 sprintf(myBuf,"Input 2 = %3d", g_currMeasures.gndV);
tanasaro10 9:303d3628986a 285 len = 13;
tanasaro10 9:303d3628986a 286 break;
tanasaro10 9:303d3628986a 287 }
tanasaro10 9:303d3628986a 288 case '3': {
tanasaro10 9:303d3628986a 289 sprintf(myBuf,"Input 3 = %3d", g_currMeasures.temp);
tanasaro10 9:303d3628986a 290 len = 13;
tanasaro10 9:303d3628986a 291 break;
tanasaro10 9:303d3628986a 292 }
tanasaro10 9:303d3628986a 293 case '4': {
tanasaro10 9:303d3628986a 294 sprintf(myBuf,"Input 4 = %2d", g_currMeasures.led_on);
tanasaro10 9:303d3628986a 295 len = 12;
tanasaro10 9:303d3628986a 296 break;
tanasaro10 9:303d3628986a 297 }
tanasaro10 9:303d3628986a 298 default: {
tanasaro10 9:303d3628986a 299 sprintf(myBuf,"All:%3d,%3d,%3d,%2d", g_currMeasures.light,g_currMeasures.gndV,g_currMeasures.temp,g_currMeasures.led_on);
tanasaro10 9:303d3628986a 300 len = 18;
tanasaro10 9:303d3628986a 301 break;
tanasaro10 8:f28ad4600b0f 302 }
tanasaro10 8:f28ad4600b0f 303 }
tanasaro10 9:303d3628986a 304 sendRadioMsg((uint8_t *)myBuf, len);
tanasaro10 9:303d3628986a 305 break;
tanasaro10 9:303d3628986a 306 }
tanasaro10 9:303d3628986a 307 case 'l': {// toogle led
tanasaro10 9:303d3628986a 308 led = ! led;
tanasaro10 9:303d3628986a 309 if (led==0) {
tanasaro10 9:303d3628986a 310 sprintf(myBuf,"%s","ON");
tanasaro10 9:303d3628986a 311 len = 2;
tanasaro10 9:303d3628986a 312 } else {
tanasaro10 9:303d3628986a 313 sprintf(myBuf,"%s","OFF");
tanasaro10 9:303d3628986a 314 len = 3;
tanasaro10 9:303d3628986a 315 }
tanasaro10 9:303d3628986a 316 sendRadioMsg((uint8_t *)myBuf, len);
tanasaro10 9:303d3628986a 317 break;
tanasaro10 9:303d3628986a 318 }
tanasaro10 9:303d3628986a 319 case 's': {// buzzer
tanasaro10 10:c7d53e4e0602 320 if (((buffer[1]>'9')||(buffer[1]<'0'))||((buffer[2]>'9')||(buffer[2]<'0'))) {
tanasaro10 9:303d3628986a 321 MyASSERT(true,&pc, buffer[1]); // notify on serial interface
tanasaro10 9:303d3628986a 322 on_error_radioMsg(); // notify on radio
tanasaro10 9:303d3628986a 323 break;
tanasaro10 9:303d3628986a 324 } else {
tanasaro10 9:303d3628986a 325 buzz_int(&buzzer, (buffer[1]-'0'),(buffer[2]-'0'));
tanasaro10 9:303d3628986a 326 sprintf(myBuf,"%s:%f","S",buzzer.read());
tanasaro10 9:303d3628986a 327 len= 7;
tanasaro10 9:303d3628986a 328 }
tanasaro10 9:303d3628986a 329 sendRadioMsg((uint8_t *)myBuf, len);
tanasaro10 9:303d3628986a 330 break;
tanasaro10 9:303d3628986a 331 }
tanasaro10 11:baafa4f7a15e 332 case 'd':
tanasaro10 11:baafa4f7a15e 333 case 't': {// date /time operations
tanasaro10 9:303d3628986a 334 switch (buffer[1]) {
tanasaro10 11:baafa4f7a15e 335 case 'i': { // date insert
tanasaro10 11:baafa4f7a15e 336 uint8_t i;
tanasaro10 11:baafa4f7a15e 337 uint8_t * pdata = &g_myDateTimeVar.newDateTime.year; // to insert data
tanasaro10 11:baafa4f7a15e 338
tanasaro10 11:baafa4f7a15e 339 if (buffer[0]=='t') {
tanasaro10 11:baafa4f7a15e 340 sprintf(myBuf," TimeInserted");
tanasaro10 11:baafa4f7a15e 341 pdata +=3;
tanasaro10 11:baafa4f7a15e 342 } else {sprintf(myBuf," DateInserted");}
tanasaro10 11:baafa4f7a15e 343 len= 14;
tanasaro10 11:baafa4f7a15e 344
tanasaro10 11:baafa4f7a15e 345 for (i=0;i<3;i++){
tanasaro10 11:baafa4f7a15e 346 memcpy(myBuf,&buffer[2+2*i],2);
tanasaro10 11:baafa4f7a15e 347 *pdata=atoi(myBuf); // TODO check if it is a number
tanasaro10 11:baafa4f7a15e 348 pdata= pdata+1;
tanasaro10 11:baafa4f7a15e 349 }
tanasaro10 11:baafa4f7a15e 350 g_myDateTimeVar.updateDateTime = true;
tanasaro10 11:baafa4f7a15e 351
tanasaro10 9:303d3628986a 352 sendRadioMsg((uint8_t *)myBuf, len);
tanasaro10 9:303d3628986a 353 break;
tanasaro10 9:303d3628986a 354 }
tanasaro10 11:baafa4f7a15e 355 case 'g': { // time/date get
tanasaro10 11:baafa4f7a15e 356 uint8_t * pdata1 = &g_myDateTimeVar.currentDateTime.year; // to get data
tanasaro10 11:baafa4f7a15e 357 if (buffer[0]=='t') {
tanasaro10 11:baafa4f7a15e 358 pdata1 +=3;
tanasaro10 11:baafa4f7a15e 359 sprintf(myBuf,"H:%2d:%2d:%2d",*pdata1,*(pdata1+1),*(pdata1+2));
tanasaro10 11:baafa4f7a15e 360 len = 11;
tanasaro10 11:baafa4f7a15e 361 }else {
tanasaro10 11:baafa4f7a15e 362 sprintf(myBuf,"D:20%2d:%2d:%2d",*pdata1,*(pdata1+1),*(pdata1+2));
tanasaro10 11:baafa4f7a15e 363 len = 13;
tanasaro10 11:baafa4f7a15e 364 }
tanasaro10 9:303d3628986a 365 sendRadioMsg((uint8_t *)myBuf, len);
tanasaro10 9:303d3628986a 366 break;
tanasaro10 9:303d3628986a 367 }
tanasaro10 9:303d3628986a 368 default:
tanasaro10 9:303d3628986a 369 MyASSERT(true,&pc, buffer[1]); // notify on serial interface
tanasaro10 9:303d3628986a 370 on_error_radioMsg(); // notify on radio
tanasaro10 9:303d3628986a 371 }
tanasaro10 9:303d3628986a 372 break;
tanasaro10 9:303d3628986a 373 }
tanasaro10 9:303d3628986a 374
tanasaro10 9:303d3628986a 375 case 'f': {// file operations
tanasaro10 9:303d3628986a 376 switch (buffer[1]) {
tanasaro10 11:baafa4f7a15e 377 case '1': {
tanasaro10 9:303d3628986a 378 sprintf(myBuf,"g_idx=%2d Page=%3d",g_MyDataIdx, flash_currPage());
tanasaro10 9:303d3628986a 379 len = 18;
tanasaro10 9:303d3628986a 380 sendRadioMsg((uint8_t *)myBuf, len);
tanasaro10 9:303d3628986a 381 break;
tanasaro10 9:303d3628986a 382 }
tanasaro10 10:c7d53e4e0602 383 case '2': { // start measuring
tanasaro10 9:303d3628986a 384 sprintf(myBuf,"Start Meas");
tanasaro10 9:303d3628986a 385 len = 12;
tanasaro10 9:303d3628986a 386 sendRadioMsg((uint8_t *)myBuf, len);
tanasaro10 9:303d3628986a 387 g_LogActive = true;
tanasaro10 9:303d3628986a 388 break;
tanasaro10 9:303d3628986a 389 }
tanasaro10 10:c7d53e4e0602 390 case '3': { // stop measuring
tanasaro10 9:303d3628986a 391 sprintf(myBuf,"Stop Meas");
tanasaro10 9:303d3628986a 392 len = 11;
tanasaro10 9:303d3628986a 393 sendRadioMsg((uint8_t *)myBuf, len);
tanasaro10 9:303d3628986a 394 g_LogActive = false;
tanasaro10 9:303d3628986a 395 break;
tanasaro10 9:303d3628986a 396 }
tanasaro10 9:303d3628986a 397 default: {
tanasaro10 9:303d3628986a 398 // error
tanasaro10 9:303d3628986a 399 }
tanasaro10 9:303d3628986a 400 }
tanasaro10 9:303d3628986a 401 break;
tanasaro10 9:303d3628986a 402 }
tanasaro10 9:303d3628986a 403 default: {
tanasaro10 9:303d3628986a 404 MyASSERT(true,&pc, buffer[1]); // notify on serial interface
tanasaro10 9:303d3628986a 405 on_error_radioMsg(); // notify on radio;
tanasaro10 9:303d3628986a 406 }
tanasaro10 8:f28ad4600b0f 407 }
tanasaro10 8:f28ad4600b0f 408 }
tanasaro10 8:f28ad4600b0f 409
tanasaro10 9:303d3628986a 410 // decode serial command that starts with x
tanasaro10 9:303d3628986a 411 static void decode_s(uint8_t * buffer, uint16_t length)
tanasaro10 9:303d3628986a 412 {
tanasaro10 9:303d3628986a 413 uint8_t page_nr;
tanasaro10 9:303d3628986a 414 char myBuf[5];
tanasaro10 9:303d3628986a 415 uint32_t * p_curr_addr;
tanasaro10 10:c7d53e4e0602 416
tanasaro10 9:303d3628986a 417 switch (buffer[0]) {
tanasaro10 9:303d3628986a 418 case 'f': { // info about selected flash page
tanasaro10 9:303d3628986a 419 if ((buffer[1]<='9')&&(buffer[1]>='0')) {
tanasaro10 9:303d3628986a 420 memcpy(myBuf,&buffer[1],3);
tanasaro10 10:c7d53e4e0602 421 page_nr= atoi(myBuf);
tanasaro10 10:c7d53e4e0602 422 uint8_t p_word_count;
tanasaro10 9:303d3628986a 423
tanasaro10 9:303d3628986a 424 pc.printf("buffer[1]: %c \r\n",buffer[1]);
tanasaro10 9:303d3628986a 425
tanasaro10 9:303d3628986a 426 p_curr_addr= (uint32_t *)((uint16_t)BLE_FLASH_PAGE_SIZE * (flash_currPage() - page_nr));
tanasaro10 9:303d3628986a 427 pc.printf("page_addr: %x, pgNr = %d \r\n",p_curr_addr,(flash_currPage() - page_nr));
tanasaro10 9:303d3628986a 428 p_curr_addr += 1;
tanasaro10 9:303d3628986a 429 pc.printf("page_addr: %x \r\n",p_curr_addr);
tanasaro10 9:303d3628986a 430 p_word_count = (uint8_t)(*(p_curr_addr));
tanasaro10 10:c7d53e4e0602 431 pc.printf("nr_of_words: %d \r\n",p_word_count);
tanasaro10 9:303d3628986a 432 flash_page_serial_dump((p_curr_addr-1));
tanasaro10 9:303d3628986a 433 }
tanasaro10 9:303d3628986a 434 break;
tanasaro10 9:303d3628986a 435 }
tanasaro10 9:303d3628986a 436 case 'd': { // full dump
tanasaro10 10:c7d53e4e0602 437 uint16_t page0;
tanasaro10 10:c7d53e4e0602 438 pc.printf("Full dump \r\n");
tanasaro10 10:c7d53e4e0602 439
tanasaro10 9:303d3628986a 440 page0 = flash_currPage();
tanasaro10 11:baafa4f7a15e 441 for (page_nr=0; page_nr<=(MAX_PAGE_NUM-MIN_PAGE_NUM); page_nr++) {
tanasaro10 10:c7d53e4e0602 442 if ((page0-page_nr)< MIN_PAGE_NUM) {
tanasaro10 9:303d3628986a 443 page0 = MAX_PAGE_NUM + page_nr;
tanasaro10 8:f28ad4600b0f 444 }
tanasaro10 9:303d3628986a 445 p_curr_addr= (uint32_t *)((uint16_t)BLE_FLASH_PAGE_SIZE * (page0-page_nr));
tanasaro10 10:c7d53e4e0602 446 flash_page_serial_dump(p_curr_addr);
tanasaro10 9:303d3628986a 447 }
tanasaro10 9:303d3628986a 448 break;
tanasaro10 9:303d3628986a 449 }
tanasaro10 10:c7d53e4e0602 450 case 'g': {
tanasaro10 10:c7d53e4e0602 451 pc.printf("g_MyDataIdx= %d\r", g_MyDataIdx);
tanasaro10 9:303d3628986a 452 break;
tanasaro10 9:303d3628986a 453 }
tanasaro10 9:303d3628986a 454 case 'c': {
tanasaro10 10:c7d53e4e0602 455 switch (buffer[1]) {
tanasaro10 9:303d3628986a 456 case 'a': {
tanasaro10 9:303d3628986a 457 connectionUpdate(eStartAdvertising);
tanasaro10 9:303d3628986a 458 break;
tanasaro10 9:303d3628986a 459 }
tanasaro10 9:303d3628986a 460 case 'c' : {
tanasaro10 9:303d3628986a 461 connectionUpdate(eDisconnect);
tanasaro10 9:303d3628986a 462 break;
tanasaro10 8:f28ad4600b0f 463 }
tanasaro10 9:303d3628986a 464 case 's' : {
tanasaro10 9:303d3628986a 465 connectionUpdate(eStopAdvertising);
tanasaro10 9:303d3628986a 466 break;
tanasaro10 8:f28ad4600b0f 467 }
tanasaro10 10:c7d53e4e0602 468 default:
tanasaro10 10:c7d53e4e0602 469 pc.printf("Not recognized cmd !\r");
tanasaro10 10:c7d53e4e0602 470 }
tanasaro10 9:303d3628986a 471 break;
tanasaro10 9:303d3628986a 472 }
tanasaro10 9:303d3628986a 473 default: {
tanasaro10 9:303d3628986a 474 // nothing
tanasaro10 8:f28ad4600b0f 475 }
tanasaro10 9:303d3628986a 476 }
tanasaro10 8:f28ad4600b0f 477 }
tanasaro10 9:303d3628986a 478
RedBearLab 2:4b66b69c7ecb 479 void WrittenHandler(const GattWriteCallbackParams *Handler)
tanasaro10 9:303d3628986a 480 {
tanasaro10 9:303d3628986a 481 uint8_t buf[TXRX_BUF_LEN+1]= {'R',':',0};
tanasaro10 9:303d3628986a 482 uint16_t bytesRead;
tanasaro10 9:303d3628986a 483
tanasaro10 9:303d3628986a 484 if (Handler->handle == txCharacteristic.getValueAttribute().getHandle()) {
tanasaro10 6:a574229993b8 485 ble.readCharacteristicValue(txCharacteristic.getValueAttribute().getHandle(), &buf[2], &bytesRead);
RedBearLab 0:cffe8ac1bdf0 486 memset(txPayload, 0, TXRX_BUF_LEN);
tanasaro10 9:303d3628986a 487 memcpy(txPayload, &buf[2], bytesRead);
tanasaro10 9:303d3628986a 488 if (txPayload[0] == 'x') {
tanasaro10 9:303d3628986a 489 decode(&txPayload[1],bytesRead);
tanasaro10 9:303d3628986a 490 }
tanasaro10 9:303d3628986a 491 //echo back
tanasaro10 9:303d3628986a 492 bytesRead+=2;
tanasaro10 9:303d3628986a 493 sendRadioMsg(buf, bytesRead);
tanasaro10 9:303d3628986a 494
tanasaro10 6:a574229993b8 495 // print on PC monitor
tanasaro10 11:baafa4f7a15e 496 buf[bytesRead]='\r';
tanasaro10 9:303d3628986a 497 pc.printf("%s",buf);
RedBearLab 0:cffe8ac1bdf0 498 }
RedBearLab 0:cffe8ac1bdf0 499 }
RedBearLab 0:cffe8ac1bdf0 500
RedBearLab 0:cffe8ac1bdf0 501 void uartCB(void)
tanasaro10 9:303d3628986a 502 {
tanasaro10 9:303d3628986a 503 while(pc.readable()) {
tanasaro10 9:303d3628986a 504 rx_buf[rx_len++] = pc.getc();
tanasaro10 9:303d3628986a 505 if(rx_len>=20 || rx_buf[rx_len-1]=='\0' || rx_buf[rx_len-1]=='\n') {
tanasaro10 9:303d3628986a 506 ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), rx_buf, rx_len);
tanasaro10 9:303d3628986a 507 if ((rx_buf[0]=='x')) {
tanasaro10 10:c7d53e4e0602 508 decode_s(&rx_buf[1],(rx_len-1)); // serial decode
tanasaro10 9:303d3628986a 509 }
tanasaro10 9:303d3628986a 510 rx_len= 0;
RedBearLab 0:cffe8ac1bdf0 511 break;
RedBearLab 0:cffe8ac1bdf0 512 }
RedBearLab 0:cffe8ac1bdf0 513 }
RedBearLab 0:cffe8ac1bdf0 514 }
RedBearLab 0:cffe8ac1bdf0 515
tanasaro10 9:303d3628986a 516 void button()
tanasaro10 9:303d3628986a 517 {
tanasaro10 8:f28ad4600b0f 518 uint8_t buf[TXRX_BUF_LEN+1];
tanasaro10 8:f28ad4600b0f 519 buf[0]='B';
tanasaro10 10:c7d53e4e0602 520 buf[1]='U';
tanasaro10 10:c7d53e4e0602 521 buf[2]='T';
tanasaro10 8:f28ad4600b0f 522 buf[3]='N';
tanasaro10 10:c7d53e4e0602 523
tanasaro10 9:303d3628986a 524 ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 4);
tanasaro10 10:c7d53e4e0602 525 g_bConnDisabled = !g_bConnDisabled;
tanasaro10 10:c7d53e4e0602 526 led = !led;
tanasaro10 10:c7d53e4e0602 527 if (g_bConnDisabled == true){
tanasaro10 11:baafa4f7a15e 528 if (g_bIsConnected == true){
tanasaro10 11:baafa4f7a15e 529 ble.disconnect((Gap::DisconnectionReason_t)0x12);
tanasaro10 11:baafa4f7a15e 530 g_bIsConnected = false;
tanasaro10 11:baafa4f7a15e 531 } else if (g_bIsAdvertising == true) {
tanasaro10 11:baafa4f7a15e 532 ble.stopAdvertising();
tanasaro10 11:baafa4f7a15e 533 g_bIsAdvertising = false;
tanasaro10 11:baafa4f7a15e 534 }
tanasaro10 10:c7d53e4e0602 535 } else {
tanasaro10 10:c7d53e4e0602 536 connectionUpdate(eStartAdvertising);
tanasaro10 10:c7d53e4e0602 537 }
tanasaro10 8:f28ad4600b0f 538 }
tanasaro10 8:f28ad4600b0f 539
tanasaro10 9:303d3628986a 540 void g_varInit()
tanasaro10 9:303d3628986a 541 {
tanasaro10 11:baafa4f7a15e 542 g_myDateTimeVar.updateDateTime = true;
tanasaro10 9:303d3628986a 543 /* retreive latest date, time and page flash available */
tanasaro10 11:baafa4f7a15e 544 search_latest_in_flash(&g_myDateTimeVar.newDateTime);
tanasaro10 8:f28ad4600b0f 545 }
tanasaro10 8:f28ad4600b0f 546
RedBearLab 0:cffe8ac1bdf0 547 int main(void)
RedBearLab 0:cffe8ac1bdf0 548 {
RedBearLab 0:cffe8ac1bdf0 549 ble.init();
tanasaro10 9:303d3628986a 550 g_varInit();
RedBearLab 0:cffe8ac1bdf0 551 ble.onDisconnection(disconnectionCallback);
tanasaro10 9:303d3628986a 552 ble.onConnection(connectionCallback);
tanasaro10 9:303d3628986a 553 ble.onDataWritten(WrittenHandler);
tanasaro10 8:f28ad4600b0f 554 event.rise(&button);
tanasaro10 10:c7d53e4e0602 555
tanasaro10 6:a574229993b8 556 pc.baud(19200);
RedBearLab 0:cffe8ac1bdf0 557 pc.printf("SimpleChat Init \r\n");
tanasaro10 9:303d3628986a 558
RedBearLab 0:cffe8ac1bdf0 559 pc.attach( uartCB , pc.RxIrq);
tanasaro10 9:303d3628986a 560 // setup advertising
RedBearLab 0:cffe8ac1bdf0 561 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
RedBearLab 0:cffe8ac1bdf0 562 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
RedBearLab 0:cffe8ac1bdf0 563 ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
tanasaro10 9:303d3628986a 564 (const uint8_t *)"MyBleVT", sizeof("MyBleVT") - 1);
RedBearLab 0:cffe8ac1bdf0 565 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
tanasaro10 9:303d3628986a 566 (const uint8_t *)uart_base_uuid_rev, sizeof(uart_base_uuid));
tanasaro10 9:303d3628986a 567 //ble.accumulateAdvertisingPayload(GapAdvertisingData::TX_POWER_LEVEL,(const uint8_t *)txPower, sizeof(txPower));
tanasaro10 9:303d3628986a 568 ble.setTxPower(txPower);
tanasaro10 9:303d3628986a 569 // 100ms; in multiples of 0.625ms.
RedBearLab 0:cffe8ac1bdf0 570 ble.setAdvertisingInterval(160);
tanasaro10 9:303d3628986a 571 /*
tanasaro10 9:303d3628986a 572 // activate radio notifications - usefull for flashwrite
tanasaro10 9:303d3628986a 573 void (*ptrFunc)(bool);
tanasaro10 9:303d3628986a 574 ptrFunc = ble_flash_on_radio_active_evt;
tanasaro10 9:303d3628986a 575 //needed for flash write
tanasaro10 9:303d3628986a 576 //ble.onRadioNotification(ptrFunc);
tanasaro10 10:c7d53e4e0602 577 */
RedBearLab 0:cffe8ac1bdf0 578 ble.addService(uartService);
tanasaro10 9:303d3628986a 579 ble.startAdvertising();
RedBearLab 0:cffe8ac1bdf0 580 pc.printf("Advertising Start \r\n");
tanasaro10 9:303d3628986a 581 periodicActions.attach(&at_eachInstant,gTimeInstant);
tanasaro10 9:303d3628986a 582 while(1) {
tanasaro10 9:303d3628986a 583 ble.waitForEvent();
RedBearLab 0:cffe8ac1bdf0 584 }
RedBearLab 0:cffe8ac1bdf0 585 }