Ashwini kumar sinha
Created January 30, 2021 © CC BY

Remote Health Monitoring System & Anlaytics

Monitor the patient health located in remote location and give live graph and analytic to doctor to do checkup form remote location.

AdvancedProtip20 hours49

Things used in this project

Hardware components

AVR-IoT WG Development Board
Microchip AVR-IoT WG Development Board
×1
MikroE HeartClick
×1
MikroE EMG Sensor
×1

Software apps and online services

Atmel START
Microchip Atmel START
MPLAB X IDE
Microchip MPLAB X IDE
MPLAB Code Configurator
Microchip MPLAB Code Configurator
Cloud IoT Core
Google Cloud IoT Core

Story

Read more

Schematics

untitled_U5ZdIQbJvZ.png

Code

application_manager.c

C Header File
/*
 * application_manager.c
 *
 * Created: 10/4/2018 1:37:19 PM
 *  Author: I17643
 */

#include <string.h>
#include <time.h>
#include <stdio.h>
#include <atomic.h>
#include <avr/wdt.h>
#include "application_manager.h"
#include "atmel_start_pins.h"
#include "atmel_start.h"
#include "config/IoT_Sensor_Node_config.h"
#include "cloud/cloud_service.h"
#include "cloud/crypto_client/cryptoauthlib_main.h"
#include "cloud/crypto_client/crypto_client.h"
#include "cloud/wifi_service.h"
#if CFG_ENABLE_CLI
#include "cli/cli.h"
#endif
#include "credentials_storage/credentials_storage.h"
#include "led.h"
#include "debug_print.h"

#define MAIN_DATATASK_INTERVAL 100L
// The debounce time is currently close to 2 Seconds.
#define SW_DEBOUNCE_INTERVAL 1460000L

#define SW0_TOGGLE_STATE SW0_get_level()
#define SW1_TOGGLE_STATE SW1_get_level()

// This will contain the device ID, before we have it this dummy value is the init value which is non-0
char                       attDeviceID[20] = "BAAAAADD1DBAAADD1D";
shared_networking_params_t shared_networking_params;
ATCA_STATUS                retValCryptoClientSerialNumber;

absolutetime_t MAIN_dataTask(void *payload);
timer_struct_t MAIN_dataTasksTimer = {MAIN_dataTask};

void wifiConnectionStateChanged(uint8_t status);

void application_init()
{
	uint8_t  mode          = WIFI_DEFAULT;
	uint32_t sw0CurrentVal = 0;
	uint32_t sw1CurrentVal = 0;
	uint32_t i             = 0;

	wdt_disable();

	// Initialization of modules before interrupts are enabled
	atmel_start_init();

	LED_test();
#if CFG_ENABLE_CLI
	CLI_init();
	CLI_setdeviceId(attDeviceID);
#endif
	debug_init(attDeviceID);

	ENABLE_INTERRUPTS();

	// Initialization of modules where the init needs interrupts to be enabled
	cryptoauthlib_init();

	if (cryptoDeviceInitialized == false) {
		debug_printError("APP: CryptoAuthInit failed");
	}
	// Get serial number from the ECC608 chip
	retValCryptoClientSerialNumber = CRYPTO_CLIENT_printSerialNumber(attDeviceID);
	if (retValCryptoClientSerialNumber != ATCA_SUCCESS) {
		switch (retValCryptoClientSerialNumber) {
		case ATCA_GEN_FAIL:
			debug_printError("APP: DeviceID generation failed, unspecified error");
			break;
		case ATCA_BAD_PARAM:
			debug_printError("APP: DeviceID generation failed, bad argument");
		default:
			debug_printError("APP: DeviceID generation failed");
			break;
		}
	}
#if CFG_ENABLE_CLI
	CLI_setdeviceId(attDeviceID);
#endif
	debug_setPrefix(attDeviceID);

	// Blocking debounce
	for (i = 0; i < SW_DEBOUNCE_INTERVAL; i++) {
		sw0CurrentVal += SW0_TOGGLE_STATE;
		sw1CurrentVal += SW1_TOGGLE_STATE;
	}
	if (sw0CurrentVal < (SW_DEBOUNCE_INTERVAL / 2)) {
		if (sw1CurrentVal < (SW_DEBOUNCE_INTERVAL / 2)) {
			strcpy(ssid, CFG_MAIN_WLAN_SSID);
			strcpy(pass, CFG_MAIN_WLAN_PSK);
			sprintf((char *)authType, "%d", CFG_MAIN_WLAN_AUTH);
			LED_startBlinkingGreen();
		} else {
			mode = WIFI_SOFT_AP;
		}
	}
	wifi_init(wifiConnectionStateChanged, mode);

	if (mode == WIFI_DEFAULT) {
		CLOUD_init(attDeviceID);
		scheduler_timeout_create(&MAIN_dataTasksTimer, MAIN_DATATASK_INTERVAL);
	}

	LED_test();
}

void application_post_provisioning(void)
{
	CLOUD_init(attDeviceID);
	scheduler_timeout_create(&MAIN_dataTasksTimer, MAIN_DATATASK_INTERVAL);
}

// React to the WIFI state change here. Status of 1 means connected, Status of 0 means disconnected
void wifiConnectionStateChanged(uint8_t status)
{
	// If we have no AP access we want to retry
	if (status != 1) {
		// Restart the WIFI module if we get disconnected from the WiFi Access Point (AP)
		CLOUD_reset();
	}
}

// This scheduler will check all tasks and timers that are due and service them
void runScheduler(void)
{
	scheduler_timeout_call_next_callback();
}

// This could be better done with a function pointer (DI) but in the interest of simplicity
//     we avoided that. This is being called from MAIN_dataTask below
void sendToCloud(void);

// This gets called by the scheduler approximately every 100ms
absolutetime_t MAIN_dataTask(void *payload)
{
	static time_t previousTransmissionTime = 0;

	// Get the current time. This uses the C standard library time functions
	time_t timeNow = time(NULL);

	// Example of how to send data when MQTT is connected every 1 second based on the system clock
	if (CLOUD_isConnected()) {
		// How many seconds since the last time this loop ran?
		int32_t delta = difftime(timeNow, previousTransmissionTime);

		if (delta >= CFG_SEND_INTERVAL) {
			previousTransmissionTime = timeNow;

			// Call the data task in main.c
			sendToCloud();
		}
	}

	LED_BLUE_set_level(!shared_networking_params.haveAPConnection);
	LED_RED_set_level(!shared_networking_params.haveERROR);
	if (LED_isBlinkingGreen() == false) {
		LED_GREEN_set_level(!CLOUD_isConnected());
	}

	// This is milliseconds managed by the RTC and the scheduler, this return makes the
	//      timer run another time, returning 0 will make it stop
	return MAIN_DATATASK_INTERVAL;
}

click_example.c

C Header File
/**
 * \file
 *
 *
 (c) 2018 Microchip Technology Inc. and its subsidiaries.

    Subject to your compliance with these terms,you may use this software and
    any derivatives exclusively with Microchip products.It is your responsibility
    to comply with third party license terms applicable to your use of third party
    software (including open source software) that may accompany Microchip software.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
    PARTICULAR PURPOSE.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
    BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
    FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
    ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
    THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
 *
 */

#include <stdio.h>
#include <string.h>
#include <click_example.h>
#include <clock_config.h>
#include <util/delay.h>

#include <Heartrate_click.h>
#include <max30100.h>

#define DC_REMOVER_ALPHA 0.95
#define BEATDETECTOR_INIT_HOLDOFF 2000            // in ms, how long to wait before counting
#define BEATDETECTOR_MASKING_HOLDOFF 200          // in ms, non-retriggerable window after beat detection
#define BEATDETECTOR_BPFILTER_ALPHA 0.6           // EMA factor for the beat period value
#define BEATDETECTOR_MIN_THRESHOLD 20             // minimum threshold (filtered) value
#define BEATDETECTOR_MAX_THRESHOLD 800            // maximum threshold (filtered) value
#define BEATDETECTOR_STEP_RESILIENCY 30           // maximum negative jump that triggers the beat edge
#define BEATDETECTOR_THRESHOLD_FALLOFF_TARGET 0.3 // thr chasing factor of the max value when beat
#define BEATDETECTOR_THRESHOLD_DECAY_FACTOR 0.99  // thr chasing factor when no beat
#define BEATDETECTOR_INVALID_READOUT_DELAY 2000   // in ms, no-beat time to cause a reset
#define BEATDETECTOR_SAMPLES_PERIOD 10            // in ms, 1/Fs
#define FALSE 0
#define TRUE 1
#define REPORTING_PERIOD_MS 1000

/**
  Section: Variable Definitions
 */

typedef struct {
	float v_ctr[2];
	float dcW;
} filter_t;

typedef enum {
	BEATDETECTOR_STATE_INIT,
	BEATDETECTOR_STATE_WAITING,
	BEATDETECTOR_STATE_FOLLOWING_SLOPE,
	BEATDETECTOR_STATE_MAYBE_DETECTED,
	BEATDETECTOR_STATE_MASKING
} BeatMonitorState_t;

static uint32_t timer_ms;
static uint16_t sampRate;

static BeatMonitorState_t state;
static float              bpmRate;
static uint16_t           irData;
static filter_t           irFilters;
static float              threshold;
static float              beatPeriod;
static float              lastMaxValue;
static uint32_t           tsLastBeat;
static uint32_t           tsLastSample;
static uint32_t           tsLastReport;

/**
  Section: Private function prototypes
 */

void     TMR0MS_ISR(void);
uint32_t checkTimeDiff(uint32_t time);
uint16_t getSampRate(void);
void     checkSample(void);
float    dcRemoval(float x);
float    filterBuLp1(uint16_t x);
uint8_t  checkForBeat(float value);
void     decreaseThreshold(void);
float    getBPMRate(void);

/**
  Section: Example Code
 */

void HeartRate_example(void)
{
	irFilters.dcW      = 0;
	irFilters.v_ctr[0] = 0;
	irFilters.v_ctr[1] = 0;
	tsLastSample       = 0;
	irData             = 0;
	state              = BEATDETECTOR_STATE_INIT;
	threshold          = BEATDETECTOR_MIN_THRESHOLD;
	beatPeriod         = 0;
	lastMaxValue       = 0;
	tsLastBeat         = 0;
	tsLastReport       = 0;
	bpmRate            = 0;

	sampRate = getSampRate();

	while (1) {
		TMR0MS_ISR();
		checkSample();
		if (checkTimeDiff(tsLastReport) > REPORTING_PERIOD_MS) {
			bpmRate      = getBPMRate();
			tsLastReport = timer_ms;
		}
	}
}

// TMR0 Interrupt Handler (millisecond counter)
void TMR0MS_ISR(void)
{
	if (timer_ms < 0x7FFFFFFF) {
		timer_ms++;
	} else {
		timer_ms = 0;
	}
}

// Period-checker bet. time-stamps
uint32_t checkTimeDiff(uint32_t time)
{
	if (time < timer_ms) {
		return (timer_ms - time);
	} else {
		return ((0x7FFFFFFF - time) + timer_ms);
	}
}

// Sample Rate from HeartRate Click Set-up
uint16_t getSampRate(void)
{
	uint16_t sRate;
	switch (DEFAULT_SAMP_RATE) {
	case MAX30100_SR50:
		sRate = 50;
		break;
	case MAX30100_SR100:
		sRate = 100;
		break;
	case MAX30100_SR167:
		sRate = 167;
		break;
	case MAX30100_SR200:
		sRate = 200;
		break;
	case MAX30100_SR400:
		sRate = 400;
		break;
	case MAX30100_SR600:
		sRate = 600;
		break;
	case MAX30100_SR800:
		sRate = 800;
		break;
	case MAX30100_SR1000:
		sRate = 1000;
		break;
	default:
		sRate = 100;
		break;
	}
	return sRate;
}

// HELPER FUNCTIONS (https://github.com/oxullo/Arduino-MAX30100)

void checkSample(void)
{
	if (checkTimeDiff(tsLastSample) > 1.0 / sampRate * 1000.0) {
		tsLastSample = timer_ms;

		HeartRate_readSensors();
		irData = HeartRate_getIRdata();

		float irACValue = dcRemoval(irData);

		// The signal fed to the beat detector is mirrored since the cleanest monotonic spike is below zero
		float filteredPulseValue = filterBuLp1((uint16_t)-irACValue);

		uint8_t beatDetected = checkForBeat(filteredPulseValue);

		if (beatDetected) {
			bpmRate = getBPMRate();
			printf("HR:%u bpm\t", (uint16_t)bpmRate);
		}
		printf("EKG:%i\r\n", (int16_t)filteredPulseValue);
	}
}

// FILTERS

// http://sam-koblenski.blogspot.co.uk/2015/11/everyday-dsp-for-programmers-dc-and.html
float dcRemoval(float x)
{
	float olddcW  = irFilters.dcW;
	irFilters.dcW = (float)x + (DC_REMOVER_ALPHA * irFilters.dcW);

	return (irFilters.dcW - olddcW);
}

// http://www.schwietering.com/jayduino/filtuino/
float filterBuLp1(uint16_t x)
{
	irFilters.v_ctr[0] = irFilters.v_ctr[1];

	// FS = 100Hz, Fc = 10Hz, 1st order
	irFilters.v_ctr[1] = (2.452372752527856026e-1 * x) + (0.50952544949442879485 * irFilters.v_ctr[0]);
	return (irFilters.v_ctr[0] + irFilters.v_ctr[1]);
}

// PULSE CHECKER (https://github.com/oxullo/Arduino-MAX30100)

uint8_t checkForBeat(float sample)
{
	static uint8_t beatDetected = FALSE;

	switch (state) {
	case BEATDETECTOR_STATE_INIT:
		if (timer_ms > BEATDETECTOR_INIT_HOLDOFF) {
			state = BEATDETECTOR_STATE_WAITING;
		}
		break;

	case BEATDETECTOR_STATE_WAITING:
		if (sample > threshold) {
			threshold = sample < BEATDETECTOR_MAX_THRESHOLD ? sample : BEATDETECTOR_MAX_THRESHOLD;
			state     = BEATDETECTOR_STATE_FOLLOWING_SLOPE;
		}

		// Tracking lost, resetting
		if (checkTimeDiff(tsLastBeat) > BEATDETECTOR_INVALID_READOUT_DELAY) {
			beatPeriod   = 0;
			lastMaxValue = 0;
		}

		decreaseThreshold();
		break;

	case BEATDETECTOR_STATE_FOLLOWING_SLOPE:
		if (sample < threshold) {
			state = BEATDETECTOR_STATE_MAYBE_DETECTED;
		} else {
			threshold = sample < BEATDETECTOR_MAX_THRESHOLD ? sample : BEATDETECTOR_MAX_THRESHOLD;
		}
		break;

	case BEATDETECTOR_STATE_MAYBE_DETECTED:
		if ((sample + BEATDETECTOR_STEP_RESILIENCY) < threshold) {
			// Found a beat
			beatDetected = TRUE;
			lastMaxValue = sample;
			state        = BEATDETECTOR_STATE_MASKING;
			float delta  = checkTimeDiff(tsLastBeat);
			if (delta) {
				beatPeriod = BEATDETECTOR_BPFILTER_ALPHA * delta + (1 - BEATDETECTOR_BPFILTER_ALPHA) * beatPeriod;
			}
			tsLastBeat = timer_ms;
		} else {
			state = BEATDETECTOR_STATE_FOLLOWING_SLOPE;
		}
		break;

	case BEATDETECTOR_STATE_MASKING:
		if (checkTimeDiff(tsLastBeat) > BEATDETECTOR_MASKING_HOLDOFF) {
			state = BEATDETECTOR_STATE_WAITING;
		}
		decreaseThreshold();
		break;
	}

	return beatDetected;
}

void decreaseThreshold(void)
{
	// When a valid beat rate readout is present
	if (lastMaxValue > 0 && beatPeriod > 0) {
		threshold
		    -= lastMaxValue * (1 - BEATDETECTOR_THRESHOLD_FALLOFF_TARGET) / (beatPeriod / BEATDETECTOR_SAMPLES_PERIOD);
	} else {
		// Asymptotic decay
		threshold *= BEATDETECTOR_THRESHOLD_DECAY_FACTOR;
	}

	if (threshold < BEATDETECTOR_MIN_THRESHOLD) {
		threshold = BEATDETECTOR_MIN_THRESHOLD;
	}
}

float getBPMRate(void)
{
	if (beatPeriod != 0) {
		return ((1 / beatPeriod) * 1000 * 60);
	} else {
		return 0;
	}
}

#include <stdio.h>
#include <string.h>
#include <click_example.h>
#include <clock_config.h>
#include <util/delay.h>

#include <EMG_click.h>

#define EMG_TOP_LIMIT 0x300   // Dummy value, this has no meaning
#define EMG_BOTTOM_LIMIT 0xFF // Dummy value, this has no meaning

/**
  Section: EMG Click Example Code
*/
void EMG_Example(void)
{
	uint16_t rawEmgData = EMG_GetReading();

	if (rawEmgData > EMG_TOP_LIMIT) {
		printf("The EMG result:0x%04X : Above Limit\r\n", rawEmgData);
	} else if (rawEmgData < EMG_BOTTOM_LIMIT) {
		printf("The EMG result:0x%04X : Below Limit\r\n", rawEmgData);
	} else {
		printf("The EMG result:0x%04X : Normal\r\n", rawEmgData);
	}
}

main.c

C Header File
/*
\file   main.c

\brief  Main source file.

(c) 2018 Microchip Technology Inc. and its subsidiaries.

Subject to your compliance with these terms, you may use Microchip software and any
derivatives exclusively with Microchip products. It is your responsibility to comply with third party
license terms applicable to your use of third party software (including open source software) that
may accompany Microchip software.

THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY
IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS
FOR A PARTICULAR PURPOSE.

IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP
HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO
THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL
CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT
OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS
SOFTWARE.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "application_manager.h"
#include "led.h"
#include "sensors_handling.h"
#include "cloud/cloud_service.h"
#include "debug_print.h"

// This handles messages published from the MQTT server when subscribed
void receivedFromCloud(uint8_t *topic, uint8_t *payload)
{
	char *toggleToken = "\"toggle\":";
	char *subString;

	if ((subString = strstr((char *)payload, toggleToken))) {
		LED_holdYellowOn(subString[strlen(toggleToken)] == '1');
	}

	debug_printer(SEVERITY_NONE, LEVEL_NORMAL, "topic: %s", topic);
	debug_printer(SEVERITY_NONE, LEVEL_NORMAL, "payload: %s", payload);
}

// This will get called every 1 second only while we have a valid Cloud connection
void sendToCloud(void)
{
	static char json[70];

	// This part runs every CFG_SEND_INTERVAL seconds
	int rawTemperature = SENSORS_getTempValue();
	int light          = SENSORS_getLightValue();
    int emg = EMG_GetReading();
    int bmp= 89;

	int len
	    = sprintf(json, "{\"Light\":%d,\"EMG\":%d,\"BMP\":%d,\"Temp\":\"%d.%02d\"}", light,emg, bmp , rawTemperature / 100, abs(rawTemperature) % 100);

	if (len > 0) {
		CLOUD_publishData((uint8_t *)json, len);
		LED_flashYellow();
	}
}

int main(void)
{
	application_init();

	while (1) {
		runScheduler();
	}

	return 0;
}

Serial data for analytics

Python
import serial
import csv
import datetime
ser = serial.Serial('/dev/ttyACM0')
ser.flushInput()

while True:
    try:
        ser_bytes = ser.readline()
        decoded_bytes = float(ser_bytes[0:len(ser_bytes)-2].decode("utf-8"))
        print(decoded_bytes)
        with open("healthdata.csv","a") as f:
            writer = csv.writer(f,Healthdata=",")
            writer.writerow([e.strftime("%Y-%m-%d %H:%M:%S"),decoded_bytes])
    except:
        print("Keyboard Interrupt")
        break

Untitled file

Python
import numpy as np
import matplotlib.pyplot as plt
import datetime
%matplotlib inline 
import scipy.integrate as integrate
from scipy.optimize import curve_fit
pd.options.display.max_rows = 200
data = pd.read_csv("../healthdata.csv", sep = ' ',header = None, names = ['date', 'time','EMG','Heart','temp','Light'])
data.head()

Credits

Ashwini kumar sinha

Ashwini kumar sinha

29 projects • 72 followers
Ashwini kumar sinha a Robotic lover and electronics hobbyist. Works at EFY-I

Comments