Maxim Integrated / Mbed OS MAXREFDES220_HEART_RATE_MONITOR

Dependencies:   USBDevice max32630fthr

Fork of MAXREFDES220# by Maxim Integrated

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SSMAX30101Comm.cpp Source File

SSMAX30101Comm.cpp

00001 /*******************************************************************************
00002  * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a
00005  * copy of this software and associated documentation files (the "Software"),
00006  * to deal in the Software without restriction, including without limitation
00007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008  * and/or sell copies of the Software, and to permit persons to whom the
00009  * Software is furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included
00012  * in all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00015  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00016  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00017  * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
00018  * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  *
00022  * Except as contained in this notice, the name of Maxim Integrated
00023  * Products, Inc. shall not be used except as stated in the Maxim Integrated
00024  * Products, Inc. Branding Policy.
00025  *
00026  * The mere transfer of this software does not imply any licenses
00027  * of trade secrets, proprietary technology, copyrights, patents,
00028  * trademarks, maskwork rights, or any other form of intellectual
00029  * property whatsoever. Maxim Integrated Products, Inc. retains all
00030  * ownership rights.
00031  *******************************************************************************
00032  */
00033 
00034 #include "DSInterface.h"
00035 #include "SSMAX30101Comm.h"
00036 #include "Peripherals.h"
00037 #include "assert.h"
00038 #include "utils.h"
00039 #include "CRC8.h"
00040 
00041 #define ENABLE_OS24
00042 #define ENABLE_ACCEL
00043 #define ENABLE_WHRM_ANS_SP02
00044 
00045 static const char* const cmd_tbl[] = {
00046     "get_format ppg 0",
00047     "read ppg 0",
00048     "get_reg ppg",
00049     "set_reg ppg",
00050     "dump_reg ppg",
00051     "self_test ppg os24",
00052     "self_test ppg acc",
00053 };
00054 
00055 SSMAX30101Comm::SSMAX30101Comm(USBSerial *USB, SSInterface* ssInterface, DSInterface* dsInterface)
00056     :SensorComm("ppg", true), m_USB(USB), ss_int(ssInterface), ds_int(dsInterface)
00057 {
00058     max30101_mode1_data_req.data_size = SSMAX30101_MODE1_DATASIZE;
00059     max30101_mode1_data_req.callback = callback(this, &SSMAX30101Comm::max30101_data_rx);
00060 
00061     whrm_mode1_data_req.data_size = SSWHRM_MODE1_DATASIZE;
00062     whrm_mode1_data_req.callback = callback(this, &SSMAX30101Comm::whrm_data_rx);
00063 
00064     accel_mode1_data_req.data_size = SSACCEL_MODE1_DATASIZE;
00065     accel_mode1_data_req.callback = callback(this, &SSMAX30101Comm::accel_data_rx);
00066 
00067     queue_init(&max30101_queue, max30101_queue_buf, sizeof(max30101_mode1_data), sizeof(max30101_queue_buf));
00068     queue_init(&whrm_queue, whrm_queue_buf, sizeof(whrm_mode1_data), sizeof(whrm_queue_buf));
00069     queue_init(&accel_queue, accel_queue_buf, sizeof(accel_mode1_data), sizeof(accel_queue_buf));
00070 }
00071 
00072 void SSMAX30101Comm::stop()
00073 {
00074     comm_mutex.lock();
00075     ss_int->disable_irq();
00076     data_report_mode = 0;
00077     sample_count = 0;
00078     ss_int->disable_sensor(SS_SENSORIDX_OS24);
00079     ss_int->disable_sensor(SS_SENSORIDX_ACCEL);
00080     ss_int->disable_algo(SS_ALGOIDX_WHRM);
00081     ss_int->enable_irq();
00082     comm_mutex.unlock();
00083 }
00084 
00085 bool SSMAX30101Comm::parse_command(const char* cmd)
00086 {
00087     const char *ptr_ch = NULL;
00088     int ret;
00089     SS_STATUS status;
00090     bool recognizedCmd = false;
00091 
00092     if (!ss_int) {
00093         pr_err("No SmartSensor Interface defined!");
00094         return false;
00095     }
00096     if (!ds_int) {
00097         pr_err("No DeviceStudio Interface defined!");
00098         return false;
00099     }
00100 
00101     for (int i = 0; i < NUM_CMDS; i++) {
00102         ptr_ch = strstr(cmd, cmd_tbl[i]);
00103 
00104         if (*ptr_ch) {
00105             cmd_state_t user_cmd = (cmd_state_t)i;
00106             recognizedCmd = true;
00107 
00108             switch (user_cmd) {
00109                 case get_format_ppg_0:
00110                 {
00111 #ifdef ASCII_COMM
00112                     m_USB->printf("\r\n%s format=smpleCnt,irCnt,redCnt,led3,led4,accelX,accelY,accelZ,hr,hrconf,spo2,status err=0\r\n", ptr_ch);
00113 #else
00114                     m_USB->printf("\r\n%s enc=bin cs=1 format={smpleCnt,32},{irCnt,20},{redCnt,20},{led3,20},{led4,20},{accelX,14,3},{accelY,14,3},{accelZ,14,3},{hr,12,1},{hrconf,8},{spo2,11,1},{status,8} err=0\r\n", ptr_ch);
00115 #endif
00116                 } break;
00117 
00118                 case read_ppg_0:
00119                 {
00120                     sample_count = 0;
00121                     status = ss_int->reset();
00122                     if (status != SS_SUCCESS) {
00123                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR);
00124                         break;
00125                     }
00126 
00127                     status = ss_int->set_data_type(SS_DATATYPE_BOTH, false);
00128                     if (status != SS_SUCCESS) {
00129                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR);
00130                         break;
00131                     }
00132 
00133                     ss_int->disable_irq();
00134 #ifdef ENABLE_OS24
00135                     status = ss_int->enable_sensor(SS_SENSORIDX_OS24, 1, &max30101_mode1_data_req);
00136                     if (status != SS_SUCCESS) {
00137                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR);
00138                         ss_int->enable_irq();
00139                         break;
00140                     }
00141 #endif
00142 #ifdef ENABLE_ACCEL
00143                     status = ss_int->enable_sensor(SS_SENSORIDX_ACCEL, 1, &accel_mode1_data_req);
00144                     if (status != SS_SUCCESS) {
00145                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR);
00146                         ss_int->enable_irq();
00147                         break;
00148                     }
00149 
00150 #endif
00151 
00152 #ifdef ENABLE_WHRM_ANS_SP02
00153                     status = ss_int->enable_algo(SS_ALGOIDX_WHRM, 1, &whrm_mode1_data_req);
00154                     if (status != SS_SUCCESS) {
00155                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR);
00156                     } else {
00157                         comm_mutex.lock();
00158                         data_report_mode = read_ppg_0;
00159                         comm_mutex.unlock();
00160                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_SUCCESS);
00161                     }
00162 #endif
00163                     ss_int->enable_irq();
00164                 } break;
00165 
00166                 case get_reg_ppg:
00167                 {
00168                     uint8_t addr;
00169                     uint32_t val;
00170 
00171                     ret = parse_get_reg_cmd(ptr_ch, sensor_type, &addr);
00172                     if (!ret) {
00173                         status = ss_int->get_reg(SS_SENSORIDX_OS24, addr, &val);
00174                         if (status == SS_SUCCESS) {
00175                             m_USB->printf("\r\n%s reg_val=%02X err=%d\r\n", ptr_ch, (uint8_t)val, COMM_SUCCESS);
00176                         } else {
00177                             m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR);
00178                         }
00179                     } else {
00180                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_INVALID_PARAM);
00181                     }
00182                     
00183                 } break;
00184 
00185                 case set_reg_ppg:
00186                 {
00187                     uint8_t addr;
00188                     uint8_t val;
00189 
00190                     ret = parse_set_reg_cmd(ptr_ch, sensor_type, &addr, &val);
00191                     if (!ret) {
00192                         status = ss_int->set_reg(SS_SENSORIDX_OS24, addr, val, SSMAX30101_REG_SIZE);
00193                         if (status == SS_SUCCESS) {
00194                             m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_SUCCESS);
00195                         } else {
00196                             m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR);
00197                         }
00198                     } else {
00199                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_INVALID_PARAM);
00200                     }
00201 
00202                 } break;
00203 
00204                 case dump_reg_ppg:
00205                 {
00206                     int num_regs;
00207                     status = ss_int->dump_reg(SS_SENSORIDX_OS24, &reg_vals[0], ARRAY_SIZE(reg_vals), &num_regs);
00208                     if (status == SS_SUCCESS) {
00209                         int len = 0;
00210                         bool comma = false;
00211                         for (int reg = 0; reg < num_regs; reg++) {
00212                             if (comma) {
00213                                 len += snprintf(&charbuf[0] + len, sizeof(charbuf) - len - 1, ",");
00214                             }
00215                             len += snprintf(&charbuf[0] + len, sizeof(charbuf) - len - 1,
00216                                             "{%X,%lX}", reg_vals[reg].addr, reg_vals[reg].val);
00217                             comma = true;
00218                         }
00219                         charbuf[len] = '\0';
00220 
00221                         m_USB->printf("\r\n%s reg_val=%s err=%d\r\n", ptr_ch, &charbuf[0], COMM_SUCCESS);
00222 
00223                     } else {
00224                         m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR);
00225                     }
00226 
00227                 } break;
00228 
00229                 case self_test_ppg_os24:
00230                 {
00231                     ret = selftest_max30101();
00232                     m_USB->printf("%s selftest_max30101: err=<%d>\r\n", ptr_ch, ret);
00233                 } break;
00234 
00235                 case self_test_ppg_acc:
00236                 {
00237                     ret = selftest_accelerometer();
00238                     m_USB->printf("%s selftest_accelerometer: err=<%d>\r\n", ptr_ch, ret);
00239                 } break;
00240 
00241                 default:
00242                 {
00243                     assert_msg(false, "Invalid switch case!");
00244                 } break;
00245 
00246             }
00247             break;
00248         }
00249     }
00250 
00251     return recognizedCmd;
00252 }
00253 
00254 void SSMAX30101Comm::max30101_data_rx(uint8_t* data_ptr)
00255 {
00256     max30101_mode1_data sample;
00257     sample.led1 = (data_ptr[0] << 16) | (data_ptr[1] << 8) | data_ptr[2];
00258     sample.led2 = (data_ptr[3] << 16) | (data_ptr[4] << 8) | data_ptr[5];
00259     sample.led3 = (data_ptr[6] << 16) | (data_ptr[7] << 8) | data_ptr[8];
00260     sample.led4 = (data_ptr[9] << 16) | (data_ptr[10] << 8) | data_ptr[11];
00261 
00262     pr_info("led1=%.6X led2=%.6X led3=%.6X led4=%.6X\r\n", sample.led1, sample.led2, sample.led3, sample.led4);
00263 
00264     enqueue(&max30101_queue, &sample);
00265 }
00266 
00267 void SSMAX30101Comm::whrm_data_rx(uint8_t* data_ptr)
00268 {
00269     //See API doc for data format
00270     whrm_mode1_data sample;
00271     sample.hr = (data_ptr[0] << 8) | data_ptr[1];
00272     sample.hr_conf = data_ptr[2];
00273     sample.spo2 = (data_ptr[3] << 8) | data_ptr[4];
00274     sample.status = data_ptr[5];
00275 
00276     pr_info("hr=%.1f conf=%d spo2=%d status=%d\r\n", (float)sample.hr / 10.0, sample.hr_conf, sample.spo2, sample.status);
00277     enqueue(&whrm_queue, &sample);
00278 }
00279 void SSMAX30101Comm::accel_data_rx(uint8_t* data_ptr)
00280 {
00281     //See API doc for data format
00282     accel_mode1_data sample;
00283     sample.x = (data_ptr[0] << 8) | data_ptr[1];
00284     sample.y = (data_ptr[2] << 8) | data_ptr[3];
00285     sample.z = (data_ptr[4] << 8) | data_ptr[5];
00286 
00287     enqueue(&accel_queue, &sample);
00288 }
00289 
00290 int SSMAX30101Comm::data_report_execute(char* buf, int size)
00291 {
00292     uint8_t tmp_report_mode;
00293     max30101_mode1_data max30101_sample = { 0 };
00294     whrm_mode1_data whrm_sample = { 0 };
00295     accel_mode1_data accel_sample = { 0 };
00296     int16_t data_len = 0;
00297 
00298     if (size < 0)
00299         return 0;
00300 
00301     if (!is_enabled())
00302         return 0;
00303 
00304     comm_mutex.lock();
00305     tmp_report_mode = data_report_mode;
00306     comm_mutex.unlock();
00307 
00308     switch (tmp_report_mode) {
00309         case read_ppg_0:
00310         {
00311             if (
00312 
00313 #ifdef ENABLE_OS24
00314                 queue_len(&max30101_queue) > 0
00315 #endif
00316 #ifdef ENABLE_ACCEL
00317                 && queue_len(&accel_queue) > 0
00318 #endif
00319 #ifdef ENABLE_WHRM_ANS_SP02
00320                 && queue_len(&whrm_queue) > 0
00321 #endif
00322                 )
00323             {
00324 #ifdef ENABLE_OS24
00325                 dequeue(&max30101_queue, &max30101_sample);
00326 #endif
00327 #ifdef ENABLE_ACCEL
00328                 dequeue(&accel_queue, &accel_sample);
00329 #endif
00330 #ifdef ENABLE_WHRM_ANS_SP02
00331                 dequeue(&whrm_queue, &whrm_sample);
00332 #endif
00333 #ifdef ASCII_COMM
00334                 data_len = snprintf(buf, size - 1, "%u,%lu,%lu,%lu,%lu,%.3f,%.3f,%.3f,%.1f,%d,%.1f,%d\r\n",
00335                         sample_count++,
00336                         max30101_sample.led1,
00337                         max30101_sample.led2,
00338                         max30101_sample.led3,
00339                         max30101_sample.led4,
00340                         accel_sample.x * 0.001,
00341                         accel_sample.y * 0.001,
00342                         accel_sample.z * 0.001,
00343                         whrm_sample.hr * 0.1,
00344                         whrm_sample.hr_conf,
00345                         whrm_sample.spo2 * 0.1,
00346                         whrm_sample.status);
00347 #else
00348                 assert_msg(((uint32_t)size > sizeof(ds_pkt_data_mode1)), "data_report_execute buffer too small");
00349                 ds_pkt_data_mode1* pkt = (ds_pkt_data_mode1*)buf;
00350                 pkt->start_byte = DS_BINARY_PACKET_START_BYTE;
00351                 pkt->led1 = max30101_sample.led1;
00352                 pkt->led2 = max30101_sample.led2;
00353                 pkt->led3 = max30101_sample.led3;
00354                 pkt->led4 = max30101_sample.led4;
00355                 pkt->x = accel_sample.x;
00356                 pkt->y = accel_sample.y;
00357                 pkt->z = accel_sample.z;
00358                 pkt->hr = whrm_sample.hr;
00359                 pkt->spo2 = whrm_sample.spo2;
00360                 pkt->status = whrm_sample.status;
00361                 pkt->crc8 = crc8((uint8_t*)pkt, sizeof(ds_pkt_data_mode1) - sizeof(uint8_t));
00362                 data_len = sizeof(ds_pkt_data_mode1);
00363 #endif
00364             }
00365         } break;
00366 
00367         default:
00368             return 0;
00369     }
00370 
00371     if (data_len < 0) {
00372         pr_err("snprintf console_tx_buf failed");
00373     } else if (data_len > size) {
00374         pr_err("buffer is insufficient to hold data");
00375     }
00376 
00377     return data_len;
00378 }
00379 
00380 // TODO: convert this to PPG sensor test
00381 int SSMAX30101Comm::selftest_max30101(){
00382     int ret;
00383     uint8_t test_result;
00384     bool test_failed = false;
00385     m_USB->printf("starting selftest_max30101\r\n");
00386     // configure mfio pin for self test
00387     ss_int->mfio_selftest();
00388     ret = ss_int->self_test(SS_SENSORIDX_OS24, &test_result, 500);
00389     if(ret != SS_SUCCESS){
00390         m_USB->printf("ss_int->self_test(SS_SENSORIDX_OS24, &test_result) has failed err<-1>\r\n");
00391         test_failed = true;
00392     }
00393     // reset mfio pin to old state
00394     if(!ss_int->reset_mfio_irq()){
00395         m_USB->printf("smart sensor reset_mfio_irq has failed err<-1>\r\n");
00396         test_failed = true;
00397     }
00398     // reset the sensor to turn off the LED
00399     ret = ss_int->reset();
00400     if(test_failed | !self_test_result_evaluate("selftest_max30101", test_result)){
00401         return -1;
00402     }else{
00403         return SS_SUCCESS;
00404     }
00405 }
00406 
00407 int SSMAX30101Comm::selftest_accelerometer(){
00408     int ret;
00409     uint8_t test_result;
00410     bool test_failed = false;
00411     m_USB->printf("starting selftest_accelerometer\r\n");
00412     ret = ss_int->self_test(SS_SENSORIDX_ACCEL, &test_result, 1000);
00413     if(ret != SS_SUCCESS){
00414         m_USB->printf("ss_int->self_test(SS_SENSORIDX_ACCEL, &test_result) has failed err<-1>\r\n");
00415         test_failed = true;
00416     }
00417     // reset the sensor to turn off the LED
00418     ret = ss_int->reset();
00419     if(ret != SS_SUCCESS){
00420         m_USB->printf("smart sensor reset has failed err<-1>\r\n");
00421         test_failed = true;
00422     }
00423     if(test_failed | !self_test_result_evaluate("selftest_accelerometer", test_result)){
00424         return -1;
00425     }else{
00426         return SS_SUCCESS;
00427     }
00428 }
00429 
00430 bool SSMAX30101Comm::self_test_result_evaluate(const char *message, uint8_t result){
00431     // check i2c response status
00432     if(result != 0x00){
00433         m_USB->printf("%s has failed % 02X err<-1>\r\n", message, result);
00434         if((result & FAILURE_COMM))
00435             m_USB->printf("%s communication has failed err<-1>\r\n", message);
00436         if(result & FAILURE_INTERRUPT)
00437             m_USB->printf("%s interrupt pin check has failed err<-1>\r\n", message);
00438         return false;
00439     }
00440     return true;
00441 }