Skill Level | Area of Focus | Operating System | Platform/Hardware | Cloud Services/Platform |
---|---|---|---|---|
Intermediate | Healthcare, Sensors | RTOS | MDM920x LTE for IoT | Gizwits Cloud Platform |
This project is designed to use a heart rate sensor with the Qualcomm® MDM9206 LTE modem, and when the detected data is higher than the set threshold, the LED light is illuminated.
Objective
The main objective of this project is to connect the heart rate sensor to the ADC interface of the MDM9206 modem provided by the GoKit development board and collect heart rate information. The LED is designed to turn ON when the detected heart rate is higher than the threshold value.
Materials Required / Parts List / Tools
Source Code / Source Examples / Application Executable
Build / Assembly Instructions
Parts used
Below are the items used in this project:
- Win7 PC
- Pulse Sensor, used to collect heart rate information
- LED, when the detected heart rate value exceeds the set threshold, the LED lights up
- DuPont cable, used to connect other components as wires
- Oscilloscope, used to view the detected heart rate waveform in real time
How does it work?
The application is designed to upload the data to the Gizwits cloud. Below are the different functions implemented in the project.
Now let's introduce the demo-smart-heart-rate-detector’s workflow.
gagentMain---->sensorInit----->led_init---->Pulsesensor_init. demo-Smart-Heart-rate-detector/main/main.c void gagentMain(void) { getFreeHeap(); sensorInit(); gizwitsInit(); timer_init(); timer_start(); }
The function named "gagentMain" was called by "GAgent". The main role of GAgent is data forwarding, which is a data interaction bridge between device data, Wit Cloud, and application end (APP). Function "sensorInit" does the sensor initialization:
void sensorInit(void) { gizLog(LOG_INFO, "Sensor initialization ...\n"); led_init(); Pulsesensor_init(); } void led_init() { gizLog(LOG_INFO, "in led init...\n"); led_gpio_config(); led_on_off(false, led_red); } void Pulsesensor_init() { qapi_Status_t status = QAPI_ERROR; const char *Channel_Name_ADC0 = ADC_INPUT_ADC0; qapi_Timer_Sleep(2, QAPI_TIMER_UNIT_SEC, true); status = adc_open_handle(); if(status != QAPI_OK) { //IOT_DEBUG("Get ADC Handle ERROR!"); gizLog(LOG_INFO,"adc open handle error...\n"); return; } status = adc_get_properties(Channel_Name_ADC0, &Properties_ADC0); if(status != QAPI_OK) { //IOT_DEBUG("Get ADC channel-%s Configuration ERROR!", Channel_Name_ADC1); gizLog(LOG_INFO,"Get ADC channel-%s Configuration ERROR...\n", Channel_Name_ADC0); return; } } demo-Smart-Heart-rate-detector/driver/timer/timer.c qapi_Status_t timer_init(void) //init { qapi_Status_t status = QAPI_OK; memset(&timer_def_attr, 0, sizeof(timer_def_attr)); timer_def_attr.cb_type = QAPI_TIMER_FUNC1_CB_TYPE; timer_def_attr.deferrable = false; timer_def_attr.sigs_func_ptr = timer1_handler; timer_def_attr.sigs_mask_data = 0x11; status = qapi_Timer_Def(&timer_handle, &timer_def_attr); return status; } qapi_Status_t timer_start(void) { qapi_Status_t status = QAPI_OK; memset(&timer_set_attr, 0, sizeof(timer_set_attr)); timer_set_attr.reload = 100; timer_set_attr.time = 10; timer_set_attr.unit = QAPI_TIMER_UNIT_MSEC; status = qapi_Timer_Set(timer_handle, &timer_set_attr); return status; } void timer1_handler(uint32_t data) // timer callback { static bool led_red_status = true; getHeartRateValue(&heartrate); //get heartvalue if(!heartrate) //no value { return; } else if(((heartrate > HEART_RATE_THRESHOLD_HIGH) || (heartrate < HEART_RATE_THRESHOLD_LOW))) // value is not vaild { led_on_off(true, led_red); //red on //led_on_off(led_red_status, led_red); //led_red_status = !led_red_status; } else // value is vaild { led_on_off(false,led_red); //red off } } demo-Smart-Heart-rate-detector/driver/plusensor/plusensor.c uint8_t getHeartRateValue(uint32_t* heartrate ) //Algorithm implementation for calculating heart rate { gizLog(LOG_INFO,"in getHeartRateValue...\n"); ....... memset(&result, 0, sizeof(result)); status = qapi_ADC_Read_Channel(adc_handle, &Properties_ADC0, &result); // read the Pulse Sensor ........ sampleCounter += 10; // keep track of the time in mS with this variable Num = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise // find the peak and trough of the pulse wave if(Signal < thresh && Num > (IBI/5)*3) // avoid dichrotic noise by waiting 3/5 of last IBI { if(Signal < T) // T is the trough { T = Signal; // keep track of lowest point in pulse wave gizLog(LOG_INFO,"Find trough, T = %d\n", T); } } if(Signal > thresh && Signal > P) // thresh condition helps avoid noise { P = Signal; // P is the peak,keep track of highest point in pulse wave gizLog(LOG_INFO,"Find peak, P = %d\n", P); } // NOW IT'S TIME TO LOOK FOR THE HEART BEAT // signal surges up in value every time there is a pulse if (Num > 600) { // avoid high frequency noise if ( (Signal > thresh) && (Pulse == false) && (Num > (IBI/5)*3) ) { Pulse = true; // set the Pulse flag when we think there is a pulse IBI = sampleCounter - lastBeatTime; // measure time between beats in mS lastBeatTime = sampleCounter; // keep track of time for next pulse if(secondBeat) // if this is the second beat, if secondBeat == TRUE { secondBeat = false; // clear secondBeat flag for(int i=0; i<=9; i++) { rate[i] = IBI; // seed the running total to get a realisitic BPM at startup } } if(firstBeat) // if it's the first time we found a beat, if firstBeat == TRUE { firstBeat = false; // clear firstBeat flag secondBeat = true; // set the second beat flag return 0; // IBI value is unreliable so discard it } // keep a running total of the last 10 IBI values runningTotal = 0; // clear the runningTotal variable for(int i=0; i<=8; i++) { rate[i] = rate[i+1]; // shift data in the rate array // and drop the oldest IBI value runningTotal += rate[i]; // add up the 9 oldest IBI values } rate[9] = IBI; // add the latest IBI to the rate array runningTotal += rate[9]; // add the latest IBI to runningTotal runningTotal /= 10; // average the last 10 IBI values BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM! *heartrate = BPM; gizLog(LOG_INFO,"BPM = %d\n", *heartrate); QS = true; // set Quantified Self flag // QS FLAG IS NOT CLEARED INSIDE THIS ISR } } if (Signal < thresh && Pulse == true) { // when the values are going down, the beat is over Pulse = false; // reset the Pulse flag so we can do it again amp = P - T; // get amplitude of the pulse wave thresh = amp/2 + T; // set thresh at 50% of the amplitude P = thresh; // reset these for next time T = thresh; } if (Num > 2500) { // if 2.5 seconds go by without a beat thresh = 512; // set thresh default P = 512; // set P default T = 512; // set T default lastBeatTime = sampleCounter; // bring the lastBeatTime up to date firstBeat = true; // set these to avoid noise secondBeat = false; // when we get the heartbeat back } }
Usage Instructions
- Download code from GitHub repository: “https://github.com/ThunderSoft-XA/demo-Smart-Heart-rate-detector”
- Compile the code and flash the image to GoKit4 development kit.
- Connect the heart rate sensor to the ADC1 channel of the GoKit development board.
- Connect one pin of the led to the D9 pin of the development board, and the other pin is connected to the Vcc.
- Turn on the oscilloscope, adjust to the appropriate gear position, connect the heart rate sensor's pulse signal output pin and GND to the oscilloscope.
- Use the USB data cable to connect the PC and GoKit development board.
- Touch your finger to the signal acquisition surface of the heart rate sensor.
- Open the serial debugging assistant, and you can see the collected data in real time.
When the detected data exceeds the threshold, you can see that the LED is lit.
Contributors
Name | Title/Company | |
---|---|---|
Zhen | [email protected] | Thundersoft |
Rong | [email protected] | Thundersoft |
Jie | [email protected] | Thundersoft |
Kou | [email protected] | Thundersoft |
Eric | [email protected] | Thundersoft |