salesforce HeartRate monitor sample application. This application sends periodic heart rate values into salesforce.com via the mbed SalesforceInterface API.

Dependencies:   BufferedSerial C12832 EthernetInterface GroveEarbudSensor Logger SalesforceInterface mbed-rtos mbed

Fork of df-2014-salesforce-hrm-k64f by Doug Anson

Committer:
ansond
Date:
Thu Sep 25 03:38:14 2014 +0000
Revision:
3:3a5fdfdabca3
Parent:
1:a71236906eed
Child:
4:73f9d7560e93
updates

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ansond 0:a298d18da239 1 /* Copyright C2014 ARM, MIT License
ansond 0:a298d18da239 2 *
ansond 0:a298d18da239 3 * Author: Doug Anson (doug.anson@arm.com)
ansond 0:a298d18da239 4 *
ansond 0:a298d18da239 5 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
ansond 0:a298d18da239 6 * and associated documentation files the "Software", to deal in the Software without restriction,
ansond 0:a298d18da239 7 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
ansond 0:a298d18da239 8 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
ansond 0:a298d18da239 9 * furnished to do so, subject to the following conditions:
ansond 0:a298d18da239 10 *
ansond 0:a298d18da239 11 * The above copyright notice and this permission notice shall be included in all copies or
ansond 0:a298d18da239 12 * substantial portions of the Software.
ansond 0:a298d18da239 13 *
ansond 0:a298d18da239 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
ansond 0:a298d18da239 15 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
ansond 0:a298d18da239 16 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
ansond 0:a298d18da239 17 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
ansond 0:a298d18da239 18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ansond 0:a298d18da239 19 */
ansond 0:a298d18da239 20
ansond 0:a298d18da239 21 #include "Definitions.h" // definitions including platform specifics...
ansond 0:a298d18da239 22 #include "ErrorHandler.h"
ansond 0:a298d18da239 23
ansond 0:a298d18da239 24 // HRM sensor defines
ansond 0:a298d18da239 25 #define HRM_OFF 0 // earbud sensor is offline
ansond 0:a298d18da239 26 #define HRM_MIN 10 // min HRM
ansond 0:a298d18da239 27 #define HRM_MAX 250 // max HRM
ansond 0:a298d18da239 28
ansond 0:a298d18da239 29 // include salesforce.com credentials
ansond 0:a298d18da239 30 #include "sf_creds.h"
ansond 0:a298d18da239 31
ansond 0:a298d18da239 32 // our Serial port
ansond 0:a298d18da239 33 #include "BufferedSerial.h"
ansond 0:a298d18da239 34 BufferedSerial pc(USBTX, USBRX);
ansond 0:a298d18da239 35 #define LOG_CONSOLE(...) { pc.printf(__VA_ARGS__); }
ansond 0:a298d18da239 36
ansond 0:a298d18da239 37 // mbed AppBoard Shield LCD
ansond 0:a298d18da239 38 C12832 lcd(D11, D13, D12, D7, D10);
ansond 0:a298d18da239 39
ansond 0:a298d18da239 40 // Earbud
ansond 0:a298d18da239 41 InterruptIn earbud(D0);
ansond 0:a298d18da239 42 Timer t;
ansond 0:a298d18da239 43
ansond 0:a298d18da239 44 // Ethernet
ansond 0:a298d18da239 45 #include "EthernetInterface.h"
ansond 0:a298d18da239 46 EthernetInterface ethernet;
ansond 0:a298d18da239 47
ansond 0:a298d18da239 48 // HTTP
ansond 0:a298d18da239 49 #include "HTTPClient.h"
ansond 0:a298d18da239 50 HTTPClient http;
ansond 0:a298d18da239 51
ansond 0:a298d18da239 52 // Salesforce.com Interface
ansond 0:a298d18da239 53 #include "SalesforceInterface.h"
ansond 0:a298d18da239 54
ansond 1:a71236906eed 55 // Sensor Lat/Long
ansond 1:a71236906eed 56 #define SENSOR_LATITUDE "37.404120"
ansond 1:a71236906eed 57 #define SENSOR_LONGITUDE "-121.973195"
ansond 1:a71236906eed 58
ansond 0:a298d18da239 59 // Salesforce.com Configuration/Tunables
ansond 0:a298d18da239 60 char *hrm_object_name = "HeartRate__c"; // custom object
ansond 0:a298d18da239 61 char *hrm_bpm_field_name = "bpm__c"; // heartrate field
ansond 0:a298d18da239 62 char *hrm_user_field_name = "Name"; // heartrate user
ansond 0:a298d18da239 63 char *hrm_counter_field_name = "count__c"; // heartrate counter
ansond 1:a71236906eed 64 char *hrm_latitude = "latitude__c"; // sensor latitude
ansond 1:a71236906eed 65 char *hrm_longitude = "longitude__c"; // sensor longitude
ansond 0:a298d18da239 66 char *hrm_user = "Doug Anson"; // whos heartrate is this?
ansond 0:a298d18da239 67
ansond 0:a298d18da239 68 // main loop toggle
ansond 0:a298d18da239 69 bool do_loop = true;
ansond 0:a298d18da239 70
ansond 0:a298d18da239 71 // # retries before we give up and exit
ansond 0:a298d18da239 72 int num_retries = MAX_TRIES;
ansond 0:a298d18da239 73
ansond 0:a298d18da239 74 // heartrate values and iteration counter
ansond 3:3a5fdfdabca3 75 volatile int iteration_counter = 1;
ansond 0:a298d18da239 76 volatile int oldhrmCounter = HRM_MAX+1;
ansond 0:a298d18da239 77 volatile int hrmCounter = HRM_OFF;
ansond 0:a298d18da239 78
ansond 0:a298d18da239 79 // Earbud sensor tunables
ansond 0:a298d18da239 80 #define NUM_SLOTS 11 // larger numbers give more fidelity but slow down the acquisition response rate
ansond 0:a298d18da239 81 volatile unsigned char counter = 0;
ansond 0:a298d18da239 82 volatile unsigned long temp[NUM_SLOTS];
ansond 0:a298d18da239 83 volatile unsigned long sub = 0;
ansond 0:a298d18da239 84 volatile bool data_effect = true;
ansond 0:a298d18da239 85 const int max_heartpluse_duty = 2000; // Change to follow your system's request. System returns error if the duty overtrips by 2 seconds. (in MS)
ansond 0:a298d18da239 86
ansond 0:a298d18da239 87 // Salesforce.com record ID
ansond 0:a298d18da239 88 DEFINE_SML_BUFFER(bpm_record_id);
ansond 0:a298d18da239 89
ansond 0:a298d18da239 90 // initialize the array
ansond 0:a298d18da239 91 void arrayInit() {
ansond 0:a298d18da239 92 for(int i=0;i<(NUM_SLOTS-1);++i) temp[i]=0;
ansond 0:a298d18da239 93 temp[NUM_SLOTS-1] = t.read_ms();
ansond 0:a298d18da239 94 }
ansond 0:a298d18da239 95
ansond 0:a298d18da239 96 // summation function
ansond 0:a298d18da239 97 void sum() {
ansond 0:a298d18da239 98 if(data_effect) {
ansond 0:a298d18da239 99 int tmp = 60 * (NUM_SLOTS-1) * 1000;
ansond 0:a298d18da239 100 hrmCounter = tmp/(temp[NUM_SLOTS-1]-temp[0]);
ansond 3:3a5fdfdabca3 101 LOG_CONSOLE("New heartrate: %d bpm\r\n",hrmCounter);
ansond 0:a298d18da239 102 }
ansond 0:a298d18da239 103 data_effect=1; //sign bit
ansond 0:a298d18da239 104 }
ansond 0:a298d18da239 105
ansond 0:a298d18da239 106 // interrupt() function for earbud
ansond 0:a298d18da239 107 void interrupt() {
ansond 0:a298d18da239 108 temp[counter] = t.read_ms();
ansond 0:a298d18da239 109 switch(counter) {
ansond 0:a298d18da239 110 case 0:
ansond 0:a298d18da239 111 sub=temp[counter]-temp[NUM_SLOTS-1];
ansond 0:a298d18da239 112 break;
ansond 0:a298d18da239 113 default:
ansond 0:a298d18da239 114 sub=temp[counter]-temp[counter-1];
ansond 0:a298d18da239 115 break;
ansond 0:a298d18da239 116 }
ansond 0:a298d18da239 117 if(sub > max_heartpluse_duty) { //set 2 seconds as max heart pluse duty
ansond 0:a298d18da239 118 data_effect=0;//sign bit
ansond 0:a298d18da239 119 counter=0;
ansond 0:a298d18da239 120 LOG_CONSOLE("Heart rate measure error. Restarting...\r\n");
ansond 0:a298d18da239 121 arrayInit();
ansond 0:a298d18da239 122 t.stop();
ansond 0:a298d18da239 123 t.start();
ansond 0:a298d18da239 124 }
ansond 0:a298d18da239 125 if (counter == (NUM_SLOTS-1) && data_effect) {
ansond 0:a298d18da239 126 counter=0;
ansond 0:a298d18da239 127 sum();
ansond 0:a298d18da239 128 }
ansond 0:a298d18da239 129 else if(counter != (NUM_SLOTS-1) && data_effect) {
ansond 0:a298d18da239 130 counter++;
ansond 0:a298d18da239 131 }
ansond 0:a298d18da239 132 else {
ansond 0:a298d18da239 133 counter=0;
ansond 0:a298d18da239 134 data_effect=1;
ansond 0:a298d18da239 135 }
ansond 0:a298d18da239 136 }
ansond 0:a298d18da239 137
ansond 0:a298d18da239 138 // Get the heartrate from the sensor
ansond 0:a298d18da239 139 int get_heartrate() {
ansond 0:a298d18da239 140 return hrmCounter;
ansond 0:a298d18da239 141 }
ansond 0:a298d18da239 142
ansond 0:a298d18da239 143 // Create the heart rate record in Salesforce.com
ansond 0:a298d18da239 144 void create_heartrate_record(ErrorHandler *logger,SalesforceInterface *sf) {
ansond 0:a298d18da239 145 // create a new record
ansond 0:a298d18da239 146 MbedJSONValue bpm_record;
ansond 3:3a5fdfdabca3 147 int heartrate = (int)get_heartrate();
ansond 3:3a5fdfdabca3 148 bpm_record[hrm_bpm_field_name] = heartrate;
ansond 0:a298d18da239 149 bpm_record[hrm_user_field_name] = hrm_user;
ansond 0:a298d18da239 150 bpm_record[hrm_counter_field_name] = (int)iteration_counter;
ansond 1:a71236906eed 151 bpm_record[hrm_latitude] = SENSOR_LATITUDE;
ansond 1:a71236906eed 152 bpm_record[hrm_longitude] = SENSOR_LONGITUDE;
ansond 0:a298d18da239 153
ansond 0:a298d18da239 154 logger->log("ARM Salesforce HRM v%s\r\nCreate: new bpm record(%d): %d\r\nSending...",APP_VERSION,iteration_counter,hrmCounter);
ansond 0:a298d18da239 155 logger->logConsole("Initializing BPM record: %s",bpm_record.serialize().c_str());
ansond 0:a298d18da239 156
ansond 0:a298d18da239 157 // create the BPM record in salesforce.com
ansond 0:a298d18da239 158 MbedJSONValue response = sf->createRecord(hrm_object_name,bpm_record);
ansond 0:a298d18da239 159
ansond 0:a298d18da239 160 // display the result
ansond 0:a298d18da239 161 char *result = (char *)response.serialize().c_str();
ansond 0:a298d18da239 162 if (result != NULL && strlen(result) > 0 && strcmp(result,"null") != 0) {
ansond 0:a298d18da239 163 // save off the token if we succeeded
ansond 0:a298d18da239 164 logger->log("ARM Salesforce HRM v%s\r\nCreate: new bpm record(%d): %d\r\nSending...SUCCESS",APP_VERSION,iteration_counter,hrmCounter);
ansond 0:a298d18da239 165 logger->logConsole("Create: result: %s http_code = %d",result,sf->httpResponseCode());
ansond 0:a298d18da239 166 RESET_SML_BUFFER(bpm_record_id);
ansond 0:a298d18da239 167 strcpy(bpm_record_id,(char *)response["id"].get<std::string>().c_str());
ansond 0:a298d18da239 168 logger->turnLEDGreen();
ansond 3:3a5fdfdabca3 169 if (heartrate > 0) oldhrmCounter = heartrate;
ansond 3:3a5fdfdabca3 170 ++iteration_counter;
ansond 0:a298d18da239 171 }
ansond 0:a298d18da239 172 else {
ansond 0:a298d18da239 173 // failure
ansond 0:a298d18da239 174 logger->log("ARM Salesforce HRM v%s\r\nCreate: new bpm record(%d): %d\r\nSending...FAILED",APP_VERSION,iteration_counter,hrmCounter);
ansond 0:a298d18da239 175 logger->logConsole("Create: FAILED http_code=%d",sf->httpResponseCode());
ansond 0:a298d18da239 176 logger->turnLEDRed();
ansond 0:a298d18da239 177 do_loop = false;
ansond 0:a298d18da239 178 }
ansond 0:a298d18da239 179 }
ansond 0:a298d18da239 180
ansond 0:a298d18da239 181 // Update the heart rate record in Salesforce.com
ansond 0:a298d18da239 182 void update_heartrate_record(ErrorHandler *logger,SalesforceInterface *sf) {
ansond 0:a298d18da239 183 logger->turnLEDOrange();
ansond 0:a298d18da239 184
ansond 3:3a5fdfdabca3 185 // get our latest heartrate
ansond 3:3a5fdfdabca3 186 int heartrate = get_heartrate();
ansond 0:a298d18da239 187
ansond 3:3a5fdfdabca3 188 // only update SF if we have a valid change
ansond 3:3a5fdfdabca3 189 if (heartrate > 0 && heartrate != oldhrmCounter) {
ansond 3:3a5fdfdabca3 190 // update am existing record - assume "name" is the proper key for the record you wish to update...
ansond 3:3a5fdfdabca3 191 MbedJSONValue bpm_record;
ansond 3:3a5fdfdabca3 192 bpm_record[hrm_bpm_field_name] = heartrate;
ansond 3:3a5fdfdabca3 193 bpm_record[hrm_user_field_name] = hrm_user;
ansond 3:3a5fdfdabca3 194 bpm_record[hrm_counter_field_name] = (int)iteration_counter;
ansond 3:3a5fdfdabca3 195 bpm_record[hrm_latitude] = SENSOR_LATITUDE;
ansond 3:3a5fdfdabca3 196 bpm_record[hrm_longitude] = SENSOR_LONGITUDE;
ansond 3:3a5fdfdabca3 197
ansond 3:3a5fdfdabca3 198 // DEBUG
ansond 3:3a5fdfdabca3 199 logger->log("ARM Salesforce HRM v%s\r\nUpdate HR(%d): %d bpm\r\nSending...",APP_VERSION,iteration_counter,heartrate);
ansond 3:3a5fdfdabca3 200 logger->logConsole("Update: updated record: %s",bpm_record.serialize().c_str());
ansond 3:3a5fdfdabca3 201
ansond 3:3a5fdfdabca3 202 // update the BPM record in salesforce.com
ansond 3:3a5fdfdabca3 203 bool updated = sf->updateRecord(hrm_object_name,bpm_record_id,bpm_record);
ansond 3:3a5fdfdabca3 204
ansond 3:3a5fdfdabca3 205 // display the result
ansond 3:3a5fdfdabca3 206 if (updated) {
ansond 3:3a5fdfdabca3 207 // SUCCESS
ansond 3:3a5fdfdabca3 208 logger->log("ARM Salesforce HRM v%s\r\nUpdate HR(%d): %d bpm\r\nSending...SUCCESS",APP_VERSION,iteration_counter,heartrate);
ansond 3:3a5fdfdabca3 209 logger->logConsole("Update: successful! http_code=%d",sf->httpResponseCode());
ansond 3:3a5fdfdabca3 210 logger->turnLEDGreen();
ansond 3:3a5fdfdabca3 211 if (heartrate > 0) { oldhrmCounter = heartrate; ++iteration_counter; }
ansond 3:3a5fdfdabca3 212 }
ansond 3:3a5fdfdabca3 213 else {
ansond 3:3a5fdfdabca3 214 if (num_retries > 0) {
ansond 3:3a5fdfdabca3 215 // retry state
ansond 3:3a5fdfdabca3 216 logger->turnLEDPurple();
ansond 3:3a5fdfdabca3 217
ansond 3:3a5fdfdabca3 218 // OAUTH token may have expired - reset and retry
ansond 3:3a5fdfdabca3 219 logger->logConsole("Retrying update (%d of %d)...",(MAX_TRIES - num_retries)+1, MAX_TRIES);
ansond 3:3a5fdfdabca3 220 if (num_retries == MAX_TRIES) --iteration_counter; // one time only in retries...
ansond 3:3a5fdfdabca3 221 --num_retries;
ansond 3:3a5fdfdabca3 222 sf->resetSalesforceToken();
ansond 3:3a5fdfdabca3 223 update_heartrate_record(logger,sf);
ansond 3:3a5fdfdabca3 224 }
ansond 3:3a5fdfdabca3 225 else {
ansond 3:3a5fdfdabca3 226 // failure
ansond 3:3a5fdfdabca3 227 logger->log("ARM Salesforce HRM v%s\r\nUpdate HR(%d): %d bpm\r\nSending...FAILED",APP_VERSION,iteration_counter,heartrate);
ansond 3:3a5fdfdabca3 228 logger->logConsole("Update: FAILED http_code=%d",sf->httpResponseCode());
ansond 3:3a5fdfdabca3 229 do_loop = false;
ansond 3:3a5fdfdabca3 230
ansond 3:3a5fdfdabca3 231 // give-up state
ansond 3:3a5fdfdabca3 232 logger->turnLEDRed();
ansond 3:3a5fdfdabca3 233 }
ansond 3:3a5fdfdabca3 234 }
ansond 0:a298d18da239 235 }
ansond 0:a298d18da239 236 else {
ansond 3:3a5fdfdabca3 237 logger->logConsole("Heartrate unchanged...");
ansond 3:3a5fdfdabca3 238 logger->turnLEDGreen();
ansond 0:a298d18da239 239 }
ansond 0:a298d18da239 240 }
ansond 0:a298d18da239 241
ansond 0:a298d18da239 242 // Report heart rate to Salesforce.com
ansond 0:a298d18da239 243 void report_heartrate(ErrorHandler *logger,SalesforceInterface *sf) {
ansond 3:3a5fdfdabca3 244 if (iteration_counter == 1) create_heartrate_record(logger,sf);
ansond 0:a298d18da239 245 else update_heartrate_record(logger,sf);
ansond 0:a298d18da239 246 }
ansond 0:a298d18da239 247
ansond 0:a298d18da239 248 // Main Task...
ansond 0:a298d18da239 249 void mainTask(void const *v) {
ansond 0:a298d18da239 250
ansond 0:a298d18da239 251 // create our object instances
ansond 0:a298d18da239 252 ErrorHandler logger(&pc,&lcd);
ansond 0:a298d18da239 253 SalesforceInterface *sf = NULL;
ansond 0:a298d18da239 254
ansond 0:a298d18da239 255 // announce
ansond 0:a298d18da239 256 logger.log("\r\n\r\nARM Salesforce HRM v%s",APP_VERSION);
ansond 0:a298d18da239 257 logger.turnLEDBlue();
ansond 0:a298d18da239 258 wait(1);
ansond 0:a298d18da239 259
ansond 0:a298d18da239 260 // initialize Ethernet
ansond 0:a298d18da239 261 logger.log("Initializing Ethernet...");
ansond 0:a298d18da239 262 ethernet.init();
ansond 0:a298d18da239 263
ansond 0:a298d18da239 264 // get a DHCP address and bring the network interface up
ansond 0:a298d18da239 265 logger.log("Getting IP Address...");
ansond 0:a298d18da239 266 logger.turnLEDYellow();
ansond 0:a298d18da239 267 if (ethernet.connect() == 0) {
ansond 0:a298d18da239 268 // log our IP address (DHCP)
ansond 0:a298d18da239 269 logger.log("IP Address: %s",ethernet.getIPAddress());
ansond 0:a298d18da239 270
ansond 0:a298d18da239 271 // allocate the Salesforce.com interface
ansond 0:a298d18da239 272 logger.logConsole("Allocating the Saleforce.com interface...");
ansond 0:a298d18da239 273 sf = new SalesforceInterface(&logger,&http);
ansond 0:a298d18da239 274
ansond 0:a298d18da239 275 // set our Salesforce.com credentials
ansond 0:a298d18da239 276 logger.logConsole("Setting credentials in the Salesforce.com interface...");
ansond 0:a298d18da239 277 sf->setCredentials(username,password,client_id,client_secret);
ansond 0:a298d18da239 278
ansond 0:a298d18da239 279 // initialize the earbud
ansond 0:a298d18da239 280 LOG_CONSOLE("Initializing Earbud...\r\n");
ansond 0:a298d18da239 281 t.start();
ansond 0:a298d18da239 282 arrayInit();
ansond 0:a298d18da239 283 earbud.rise(&interrupt);
ansond 0:a298d18da239 284 hrmCounter = HRM_OFF;
ansond 0:a298d18da239 285
ansond 0:a298d18da239 286 // enter main loop
ansond 0:a298d18da239 287 logger.logConsole("Beginning main event loop...");
ansond 0:a298d18da239 288 while(do_loop) {
ansond 0:a298d18da239 289 report_heartrate(&logger,sf);
ansond 0:a298d18da239 290 Thread::wait(WAIT_TIME_MS);
ansond 0:a298d18da239 291 }
ansond 0:a298d18da239 292
ansond 0:a298d18da239 293 // if we fell out of the loop exit...
ansond 0:a298d18da239 294 logger.log("Application Exiting...");
ansond 0:a298d18da239 295 logger.turnLEDRed();
ansond 0:a298d18da239 296 exit(1);
ansond 0:a298d18da239 297 }
ansond 0:a298d18da239 298 else {
ansond 0:a298d18da239 299 logger.log("No Network... Exiting...");
ansond 0:a298d18da239 300 logger.turnLEDRed();
ansond 0:a298d18da239 301 exit(1);
ansond 0:a298d18da239 302 }
ansond 0:a298d18da239 303 }
ansond 0:a298d18da239 304
ansond 0:a298d18da239 305 // main entry
ansond 0:a298d18da239 306 int main() {
ansond 0:a298d18da239 307 Thread workerTask(mainTask, NULL, osPriorityNormal, STACK_SIZE);
ansond 0:a298d18da239 308 while (true) {
ansond 0:a298d18da239 309 Thread::wait(10*WAIT_TIME_MS);
ansond 0:a298d18da239 310 }
ansond 0:a298d18da239 311 }