# **An EKG heart rate monitoring system** by Anton Nyström, an224ik. This project is intended to provide basic insights to the world of IoT and how related ideas can give rise to a useful connected device. Throughout this tutorial, I will walk the reader through the steps I have taken and the lessons I have learnt along the way. The device consists of three bioelectrodes attached to the body which are in turn connected to a Heart Rate sensor. The sensor circuit processes the signal and transmits the data to the microcontroller which sends the data to a cloud platform using wifi where the time series is displayed graphically. It is possible to complete this tutorial in a single day and have a functioning device, however as will be discussed later the project can be expanded upon which would require more time. ## Objective Measuring the heart rate is a quick and easy means of roughly gauging in real-time a persons health. A normal resting heart rate for instance lies between 60 and 1000 bmp for an adult. If a person knows his or her normal resting heart rate even smaller deviations from it could be interesting to discover early on. Furthermore, heart rate can be used in junction with excersize to gain better a better control over the users workouts for more efficient training as it allows the user to see at what capacity they are at the moment. These types of applications are what I indend to explore with this project. I decided early on to work with bioelectrodes for several reasons. Partly, I was curius about signal processing and how it can be used in IoT, but I also wanted to work with bodily signals such as heart rate (ECG), muscle activity (EMG) or brain activity (EEG). I settled on a heart rate monitor as it is perhaps the simplest of three and by far the most recognizable. The classic heart beat line is known by most and would anchor the project to something known and familiar. Thus it fullfils its first purpose in letting me and the reader explore IoT in relation to a known setting and how these devices can be used to solve a practical problem. I think the project has made me understand how a typical IoT device functions and exposed me to sample problems that I needed to solve. These newlyfound insights can now be shared with the reader throughout this tutorial. Furthermore, the tutorial will show how bodily signals can be measured and monitored without using large cumbersome machines you might only find in a hospital. The device used is small enough to be carried on the body even if an external powersupply is attached. ## Material - **Pycom FiPY** - a programmable microcontroller with several connectivity options which carries out the given instructions and processes all requests. - **Pycom Pysense 2.0X** - an expansion board which allows for easier use of the microcontroller by connecting it using a USB cable. Also has some built in sensors. - **A breadboard** - allows for temporarily connecting all components without any soldering. - **AD8232 Heart Beat sensor** - the heart beat sensor integrated circuit. Processes incomming signals. Has an LED which flickers with the detected heart beat. - **Bioelectrodes and cables** - attached to the body and used to measure the heart rate. - **Jumperwires** - used to connect all components propperly. - **A micro-USB cable** - used to connect the device to a computer, giving it power and to upload project code and flash the firmware. The AD8232 and bioelectrodes + cables were bought in a bundle off of Amazon which cost me around 150 SEK. The rest of the material was bought in a second bundle from Electrokit together with a lot of other things and cost 1500 SEK. ## Computer setup for Windows 11 For the programming base, Atom IDE and micropython was used in this project. The first thing that has to be done is to flash the firmware. Before starting this, we need to download and install Atom and node.js. It is important to download the LTS version of node.js and not the current version. Also make sure to download the pymakr plugin in Atom by going to file >> Settings >> Install and searching for "pymakr". When this is done, the Pycom Firmware Update tool also needs to be installed (can be found [here](https://software.pycom.io/findupgrade?product=pycom-firmware-updater&type=all&platform=win32&redirect=true)). Now we can start the flashing process by attaching the FiPy to the Pysense board and plugging it into a computer using the micro-USB cable. First, use the Windows Device Manager to find the port used by the USB cable. Then open the Firmware Update tool (also make sure to close Atom or any other program using the port). Click continue twice and select the port your device is connected to and 'development' as the Type. then click continue. In the Advanced Settings make sure all boxes are ticked and that the File System selected is FatFS, also select the correct LoRa Region if you intend to use LoRa. Press continue and wait for the update to finish. Now you can close down the application and open Atom. In Atom select the port of the device in pymakr and create a project folder. In that folder create a file named 'main.py'. In this file you can write your code and then select the project in pymakr and press the "upload project to device" button to upload the code to the device which will also run it. ## Putting everything together Make sure to unplugg the device from any power supply and connect all components according to the diagram below. ![](https://i.imgur.com/eQHY4C6.png) As seen in the image, the sensor is supplied a 3.3V voltage through the 3V3 pin on the FiPy since the data sheet of the AD8232 specifies a voltage of between 3 and 3.6V for use. The AD8232 has a single output pin from which the signal is read. This is an analog signal and therefore PIN16 is used which allows for analog to digital conversion. The two other readable pins of the AD8232 are the LO- and LO+ pins which are digital pins which indicate whether or not all three bioelectrodes are attached or not. These carry a low voltage (corresponding to 0) if their respective pad is connected and a high voltage (corresponding to 1) if it is not. These pins are connected to the digital pins PIN11 and PIN7 respectively on the FiPy. Finally the bioelectrodes are attached to the body as shown in the image below. *Note that this setup is purely for development purposes and is quite unsteady. For production, a better option would be soldering and manufacture a hard casing.* ![](https://i.imgur.com/zBftk0k.png) (Image taken from [Sparkfun, 2014](https://learn.sparkfun.com/tutorials/ad8232-heart-rate-monitor-hookup-guide/all) and edited) According to my measurements the system consumes about 100 mA during active time and about 0.3 mA during deepsleep. With a duty cycle of 1.6% and cycle time of 300 seconds (which is covered later) we get an average energy consumption during one cycle of `(0.3*(1-0.016)+100*0.016) * 300/3600=0.016 mAh`. Thus one 3000 mAh battery, such as a regular phone battery would power the device for about 18 600 cycles, or nearly 1600 hours. ## Platform The choice of platform is based on preference and needs. There are three main categories of platforms, the low code options which are simple and do not require any major coding, Big Data solutions which are preferable when handling large amounts of data and self-hosted solutions which are based on a local network and is therefore often used in more data sensitive projects which require extra security and for proofs of concept. Since this project is inteded to only handle a single channel of data and there is no major reason for locality, I decided to pursue the low code solutions. Withing these there are several options such as Pybytes and Datacakes, however I decided to work with Adafruit IO which uses the MQTT protocol enabling devices to subscribe or publish to an MQTT broker and a certain data feed. The device will serve as a publisher whereas the interface will subscribe to the same data feed. If one uses triggers then the device will also need to subscribe to the same feed. Adafruit IO allows me to easily create a dashboard and transmit the data picked up by the sensor and forwarded by the microcontroller. The dashboard is simple and easy to read and can be reached from a web browser in any location, allowing the user to access the device's data feed at their leisure such as when monitoring another persons heart rate at a distance. Adafruit IO is also free to use. The platform does have a throttle which is activated when too much data is sent too quickly which is midigated by the intermittent monitoring described in a later part of the tutorial. ## The code The code below shows the main.py file. It also references the MQTTClient class found in mqtt.py which can be found [here](https://github.com/iot-lnu/applied-iot/blob/master/network-examples/mqtt_ubidots/mqtt.py). ```python from network import WLAN # For operation of WiFi network import time # Allows use of time.sleep() for delays import pycom # Base library for Pycom devices from mqtt import MQTTClient # For use of MQTT protocol to talk to Adafruit IO import ubinascii # Needed to run any MicroPython code import machine # Interfaces with hardware components import micropython # Needed to run any MicroPython code from machine import Pin # BEGIN SETTINGS # Wireless network WIFI_SSID = "NETWORK NAME" # Network name WIFI_PASS = "PASSWORD" # password # Adafruit IO (AIO) configuration AIO_SERVER = "io.adafruit.com" AIO_PORT = 1883 AIO_USER = "ADAFRUIT USERNAME" # Adafruit IO username AIO_KEY = "ADAFRUIT KEY" # Adafruit IO key AIO_CLIENT_ID = ubinascii.hexlify(machine.unique_id()) # Can be anything # all feeds used AIO_DATA_FEED = "URL TO DATA FEED" # END SETTINGS # RGBLED # Disable the on-board heartbeat (blue flash every 4 seconds) # We'll use the LED to respond to messages from Adafruit IO pycom.heartbeat(False) time.sleep(0.1) # Workaround for a bug. # Above line is not actioned if another # process occurs immediately afterwards pycom.rgbled(0xff0000) # Status red = not working # WIFI # We need to have a connection to WiFi for Internet access wlan = WLAN(mode=WLAN.STA) wlan.connect(WIFI_SSID, auth=(WLAN.WPA2, WIFI_PASS), timeout=5000) while not wlan.isconnected(): # Code waits here until WiFi connects machine.idle() pycom.rgbled(0xffd7000) # Status orange: partially working # Function to publish data to the MQTT broker. def send_data(data): try: client.publish(topic=AIO_DATA_FEED, msg=str(data)) except Exception as e: print("FAILED") # Use the MQTT protocol to connect to Adafruit IO client = MQTTClient(AIO_CLIENT_ID, AIO_SERVER, AIO_PORT, AIO_USER, AIO_KEY) pycom.rgbled(0x00ff00) # Status green: online to Adafruit IO adc = machine.ADC() inpin = adc.channel(pin='P16') p7 = Pin('P7', mode=Pin.IN, pull=Pin.PULL_UP) p11 = Pin('P11', mode=Pin.IN, pull=Pin.PULL_UP) try: last_random_sent_ticks=time.ticks_ms() #save current time COLLECT_INTERVAL=5000 # monitors every 15 minutes while True: #collect data for 20 seconds while (time.ticks_ms() - last_random_sent_ticks) < COLLECT_INTERVAL: if(p7()==1 | p11()==1): print("A pad has been disconnected") time.sleep(3) mv=inpin.voltage() send_data(mv) print(mv) time.sleep(0.1) last_random_sent_ticks = time.ticks_ms() print('sleeping') machine.deepsleep(60000*5) # sleep for 15 minutes finally: # If an exception is thrown ... client.disconnect() # ... disconnect the client and clean up. client = None wlan.disconnect() wlan = None pycom.rgbled(0x000022)# Status blue: stopped print("Disconnected from Adafruit IO.") ``` ## Connectivity Since the device in its current state is intended to display the heart rate in real time, it has to collect data continuously in order to produce a realiable singal. Since the heart rate often lies between 0.5 and 1Hz, the sample rate theoretically has to be larger than 2Hz in order to be able to discern the heartbeat and avoid ailiasing. However data is collected more often in order to get a reasonable signal with more detail and avoid the risk of 'missing the beat'. In this case i chose to collect data every 100 ms resulting in a sample frequency of 10Hz. Ideally this would be higher, but Adafruit's throttle sets limits on how often data can be sent. The data is then immediately sent via Wifi. However, if one intends to monitor the heart rate over a longer period of time, this would be a very inefficient continuous solutuion since it is expensive energywise to broadcast constantly. Therefore deep sleep turns the monitoring off intermittently such that the monitoring only takes place for 5 seconds every 5 minutes resulting in a 1.6% Duty Cycle. As mentioned MQTT was the protocol used for transmission with the reasoning already stated earlier. Worth mentioning is that I experimented some with Discord Webhooks for a bit sending messages whenever the heartrate dropped out of the interval 60-100 pbm. This could serve as an alert for when the heartrate leaves a comfortable resting state but varies from person to person and could therefore be set according to the users normal heart rate. The reason for using WiFi in this case was that the device was inteded to be used at home which means WiFi is available and the power can be supplied from the grid at all times. Thus the range and power problems that WiFi imply are not limiting to functionality. I did also experiment with LoRa using an antenna and the Helium network which could be a great option if the device is repurposed into a wearable version and Helium has coverage in the area. In this case an external power supply would be required as well. Because of LoRa's low power consumption and high range this would be much better than WiFi for that use-case. However, since Helium is not free to use and my area had poor service in TTN, I opted for WiFi as a simple free option for use at home. ## Presenting the data The dashboard looks as shown in the picture below. It is very simple and contains one time series graph displaying the heart rate signal over the latest hour and a guage showing the current heart rate. Data is saved as soon as it is recieved by the subscriber from the MQTT broker and stored in the Adafruit database. Thus data is saved every 0.1 seconds for five seconds during the active period and is saved for an hour in its current state. This saved time can of course be extended by changing the diagram to cover a longer period of time, which can easily be done in Adafruit IO. ![](https://i.imgur.com/6WuxcWq.png) The data is stored in time-series databases called a **feed** with the time stamp of when the data was recieved and some other metadata such as location of one chooses to attach that. This can be used to compliment the device if going for the wearable version such that the person wearing the device is also geotagged. This could be an additional feature to the project however it would require the device to regularly trangulate its position with something like a GPS module which would make it more energy demanding. As mentioned, when experimenting with webhooks I added two reactive triggers for when the heartrate got too high and too low respectively which sent a message to my personal discord server, however due to problems with reading the actual sensor values I left this feature out as the dashboard provided the information needed. The feature is however not a bad option as it can serve as an alert for when the heart rate deviates from the norm. ## Final thoughts and points of improvement One unfortunate reality I have to mention is that I never got the sensor to measure correctly and actually recieve a heart beat time series. All I managed to get was a flatline at 1057 mV which I hope is not how my heart actually behaves. The reason for this is unknown to me. The sensor itself has a small LED which flashes when it detects a heartbeat which seems to be functioning as intended. Therefore something with the reading of the sensor value did not function properly. I have spent quite a lot of time scouring the web for libraries and code that might fix the issue but can only seem to find libraries written in C, Python or Java, which all are unusable to me since I have been using MicroPython. The Python library contained modules that are not native to MicroPython and despite efforts to find substitutions and installabe modules, I was unable to make them work. Therefore I had to rely on my own code, which should work fine, but again, does not for some reason. If I had managed to read the signal, I would have kept the webhooks I mentioned. The final setup for the project is shown below alongside a depressing view of the time-series feed and guage displayed on Adafruit IO. ![](https://i.imgur.com/4eUjXBJ.jpg) ![](https://i.imgur.com/aZYgzjT.png) One idea of improving the project has already been discussed in making it wareable and in doing that swapping WiFi for LoRa and adding an external power supply such as a battery case. That project would also benefit from a manufactured casing to protect the device from environmental damages if worn outside. It would also make the device easier to carry than in its current state. It might be a good idea to solder the pins and wires together in that case as well instead of using a temporary breadboard. The possibility of additional geotagging has also been discussed without the need to change platform. What I originally intended for this project was to move beyond heart rate detection and EKG to instead use signal processing to repurpose the AD8232 to monitor muscle activity using EMG. This would then be used to track certain unwanted nocturnal muscle activity such as grinding of teeth and jaw clenching during sleep. The details of the project would be similar as I would still be using WiFi and Adafruit IO, but the uses would have been more practically interesing to me. This project would take some trial and error to fine tune the required signal filters which is the main reason I chose to scrap the idea and stop at the heart rate. Since I never managed to get a meaningful reading from the AD8232 I could therefore never use any results as feedback to tweak the paramterers of the filters making the project near impossible.