Heartbeat Logger

A portable device that will log your ECG - the "waveform" of your heart - to your phone via bluetooth or to a memory card.

Similar projects worth following
Something odd about your fleshy engine? Your heart tumbling like a pair of sneakers in a washing machine?

I thought so myself a couple of years ago while still being a student and decided to make it an exercise in embedded hardware and software design. Since these odd sensations came without warning once a week or so, I needed a device that could measure the electro cardiogram (ECG), or the activity of the heart, and store it on a memory card to show a doctor later.

I've brought the prototype back from the shadows of my drawer, and have been working on revising it to make a more complete device. The first revision, the purple board, is able to do the following.

- Measure the electric activity in your heart with 3 leads
- Sample the signal @ 512 Hz, 12-bit and store the data on a microSD card
- Stream the sampled data wirelessly to a mobile device
- Operate for nearly 24 hours on a charge (850mAh battery)
- Save a timestamp with the push of a button

What makes you tick?

Within each individual muscle cell in our heart there is a difference of charge between the inside and the membrane. Ions start gushing out from the control node in the heart and into the resting cell, making this difference of charge decrease until the cell contracts. A moment later the ions flow back and the cell returns to it's resting state. This will spread like a wave through your heart, and is called a heartbeat!

While this wave of contracting cells is spreading throughout your heart, we can measure the difference of charge between the resting cells and the contracted cells as an electric potential, or voltage if you will. Amplifying this tiny signal and plotting this voltage on paper as it happens is basically the ECG -- the Electrocardiogram.

With the ECG you will be given a window into your heart so that serious abnormalities can be revealed. Patients can be examined with stationary equipment for a small period of time, and an analysis of the heart activity can indicate complications that should be thoroughly examined. However, abnormal heart activity in an otherwise perfectly normal heart might occur outside of the examination room.

The Heartbeat Logger is a portable device that that logs your ECG throughout the day and throughout the night, 24/7. While this certainly is nothing new, even as an open source project (see MobilECG), Heartbeat is a project that, aside being of personal value for me, is designed to be simple to use and understand, and might serve a purpose somewhere for someone.

Disclaimer: THIS IS BY NO MEANS A MEDICAL DEVICE. Use at your own risk.

Project index

Current status: Third revision arrived, awaiting time to continue.

Heartbeat Logger hardware and firmware files in GitHub

The heart and the ECG




Matlab function to read and plot the logged binary file.

m - 2.83 kB - 02/01/2016 at 15:15



A sample file from the first logger prototype. Noise free, no time stamps.

octet-stream - 12.00 kB - 02/01/2016 at 15:15


Source code for the prototype with Xmega A3BU micro controller.

application/zip - 259.46 kB - 12/23/2015 at 22:43


  • 1 × INA321 Amplifier and Linear ICs / Instrumentation Amplifiers
  • 1 × SAMD20 Microprocessors, Microcontrollers, DSPs / ARM, RISC-Based Microcontrollers
  • 1 × TSV634 Amplifier and Linear ICs / Operational Amplifiers
  • 1 × microSD card
  • 1 × MCP1700 Power Management ICs / Linear Voltage Regulators and LDOs

View all 7 components

  • All soldered up

    Ole Andreas Utstumo02/02/2017 at 11:22 4 comments

    It wouldn't be a successful migration to a new PCB tool without introducing a new hardware bug, would there? ;) That's why pad[1] of the SERCOM module is connected to RX of the BT module. However, pad[1] cannot be configured as SERCOM TX, only pad[0] and pad[2]. To solve that we could

    a) configure the pin connected to the RX of the bluetooth module as an input (high impedance), configure one of the unused pad[0] pins as the SERCOM TX and run a wire between them.

    b) emulated UART in the firmware like the SoftwareSerial library of Arduino. Since we're only sending data, this could work out all right.

    Going for a would be the quickest solution. However, I'll give b a try, keep the PCB clean and learn something new.

  • KiCAD files uploaded to GitHub

    Ole Andreas Utstumo01/28/2017 at 20:16 0 comments

    I removed the link to the old CircuitMaker project, and uploaded all project files to GitHub.

    Help yourselves:

  • PCBs for rev 3 arrive

    Ole Andreas Utstumo01/24/2017 at 11:36 0 comments

    Looking good! I couldn't resist, I just had to solder some of the components on it :)The bottom ground layer is without any major traces, set aside for the pads and traces for the memory card reader and the BT module where I removed all unnecessary pads, in order to improve the EMC.

    With the 3rd revision still using a discrete components in the analogue front end, the question might beg of why I didn't go for a packaged ECG front end IC. Well, I still wanted this project to be mostly educational, and I believe working with discrete, analogue components like opamps and inamps promotes a broader knowledge in circuit design. I added test points to the PCB now, so that tracking the signal, testing and verifying becomes much easier.

  • Fixed, smaller, a bit better

    Ole Andreas Utstumo11/22/2016 at 12:13 0 comments

    The logger is now redone in KiCad:

    Hardware bugs should be fixed, the bluetooth module can now be soldered directly to the backside of the PCB, there are mounting holes for the enclosure (if the battery allows it) and the whole board shrank in the process. Will be on the way to my mailbox soon.

    3.3V rail distribution is still not too great, but for any major improvement of that, the board should have been 4 layer, which costst a whole lot more.

    I did consider other more current efficient modules than the HC-06, but for now I know it works and is readily available at every Chinese silicon corner. It would have been fun to get acquainted with Nordic's nRF5xxx SoCs, though.

  • Streaming heartbeats

    Ole Andreas Utstumo10/28/2016 at 13:57 6 comments

    Giffed above is the Logger streaming data to my Surface via the HC-06 bluetooth module. All the bytes are sent to the virtual COM port and displayed using a processing sketch. It works out really well, in fact. The only issue is that there are no synchronization bytes, and since each sampled value is packaged into two bytes, there's a 50/50 chance of the sketch grabbing the correct byte first when you start it.

  • Firmware -- State machine

    Ole Andreas Utstumo06/01/2016 at 11:43 0 comments

    The system operates using a simple state machine.

    The states exist in the code as functions that are called over and over again:

    void die(void);
    void sampling(void);
    void sleep(void);
    void wake(void);
    To easily manage the states, we create a function pointer that can point to the state's function:
    void (*function_pointer)(void);

    To set a new state, all we have to do is to make the function pointer point to another state's function (the & character can be omitted):

    function_pointer = &wake;

    The function pointer is called repeatedly in an everlasting while loop in main, so that it constantly executes the current state:

    The state is changed by certain conditions. Take the wake state for example:
    void wake(void){
                    function_pointer = sampling;
    		buffer_counter = 0;
    		timestamp = 0;
    		buttoncounter = 0;	
    		buffer_current = buffer_a;
    		buffer_last = buffer_b;
    		usart_write_buffer_job(&usart_instance, buffer_current, 2);
    		function_pointer = die;

    Here, the next state is set to either sampling or die, depending on the success of the SD card initialization, just as described in the state machine diagram at the top. When the program returns from the function call, the function pointer will be called again, but this time it is pointing to a different state -- et voila!

  • Enclosure

    Ole Andreas Utstumo05/23/2016 at 13:50 0 comments

    I got a simpe enclosure printed for the logger a while ago, thanks to the guys at Moon Wearables.

    The battery is glued to the bottom half..

    ... while the top half has a hole for the push button and a little button that goes in it.

    The button rests on the pushbutton on the PCB, and has edges that keeps it from falling out.

    The PCB rests on ridges inside the bottom half and is glued in place. Ridges along the edge of the two halves and a bit of tape make sure that they lock in place. The HC06 bluetooth module is a bit awkwardly placed, I must admit that, but that means there will be room for improvement on the next revision, for example to let the module be soldered directly onto the PCB.

  • Firmware out on GitHub

    Ole Andreas Utstumo05/12/2016 at 09:23 0 comments

    You can find the firmware at the project's GitHub repo:

    The firmware is written using Atmel Studio 7.0 and ASF.

  • Firmware -- Event and interrupt driven sampling & writing

    Ole Andreas Utstumo05/03/2016 at 20:07 0 comments

    As the SAMD20 doesn't have DMA, I solved the sampling & writing to the SD card by having a timer time the ADC sampling with an event and using an interrupt on the ADC's result ready to store the result in a double FIFO buffer. The sampling frequency is low enough for it to pose no trouble at all. The vital thing is that the interrupt always must be serviced before the next sample is ready to ensure consistent sampling. Here's a flowchart of how it works:

    There are two buffers as below. As one of the buffers fills up, we switch them by assigning a pointer to each of the buffers. Data will always be stored into the same pointer, buffer_current, and data will simultaneously be written to a memory card from the same pointer, buffer_last. It's the buffers being pointed at that will switch place.

    char buffer_a[BUFFER_SIZE];
    char buffer_b[BUFFER_SIZE];
    char *buffer_current;
    char *buffer_last;

    To keep track of the buffer, we also need something to point the current array position:

    uint16_t buffer_counter = 0;

    The ADC ISR is set up in the form as a callback using ASF. When each sample is ready after a conversion has been triggered by the event system, the ADC_Handler callback will be called. The BUFFER_SIZE is set to 512, so it will fit 256 samples.

    void ADC_Handler(){
    	//Result is ready, put it in buffer
    	if(buffer_counter < BUFFER_SIZE){
    		uint16_t resultat = REG_ADC_RESULT;
    		//store the result as little endian
    		*(buffer_current + buffer_counter) = resultat;
    		*(buffer_current + buffer_counter + 1) = 0xFF & (resultat>>8);
    		if(buffer_counter >= BUFFER_SIZE){
    			if(battery_check_counter++ > BATTERY_CHECK_TIMER){
    				battery_check_counter = 0;	
    			flag_buffer_ready = 1;
    			buffer_counter = 0;
    			char * buffer_temp;
    			buffer_temp = buffer_current;
    			buffer_current = buffer_last;
    			buffer_last = buffer_temp;
    		//Buffer overflow
                    buffer_counter = 0;

    Once the buffer is full and have been switched, the flag_buffer_ready is set, and we can handle the writing in main. The function below is constantly being called as long as the logger is in the sample state.

    int8_t write_data(void){
            flag_buffer_ready = 0;   
            //write to sd card        
            if(!sdcard_write(buffer_last, BUFFER_SIZE))
                return -1;    
            return 1;
        return 0;

    If a new ADC sample is ready, the ADC_Handler will be called whatever the system is doing, including writing to the SD card.

    Here's a tip: By toggling a port pin at a certain point in the code, you can debug the timing of your system by hooking up an oscilloscope:

    Here, the code is toggling a pin at every ADC interrupt (red) and raises another pin while writing to the SD card (blue). Rock steady! I'll post the code on GitHub soon enough.

  • Vital signs: Good

    Ole Andreas Utstumo05/01/2016 at 12:06 0 comments

    It's been quite a lot of debugging, but here you go!

    12 bits, precisely about 512 Hz. The sampler is running on the internal 32kHz clock, which does have some tolerance in contrast to crystals.

    Working like a charm. Well, near enough :)

View all 34 project logs

Enjoy this project?



helge wrote 05/23/2016 at 21:08 point

It's so encouraging and satisfying to witness you seeing this project through. Thanks man.

  Are you sure? yes | no

Ole Andreas Utstumo wrote 05/24/2016 at 07:11 point

Thanks for your feedback, helge, that means a lot! I'll update with a video of the logger as soon as I get more electrode pads in the mail.

  Are you sure? yes | no

Jan--Henrik wrote 01/03/2016 at 19:54 point

Hi, if you're planning to also do some ECG recordings after wilson, you can also monitor the patients respiration by measuring the resistance between the electrodes.

It will increase as the patient will breath in.

  Are you sure? yes | no

Ole Andreas Utstumo wrote 01/03/2016 at 20:14 point

Thanks for the tip, Jan Henrik, I'll look into it, though I'm generally cautious of driving any current into the body for measurement as long as I'm not 110% sure of what I'm doing ;)

  Are you sure? yes | no

Jan--Henrik wrote 01/03/2016 at 21:02 point

Dont worry, you dont need to drive current into the body, you can measure the resistance in other ways :)

  Are you sure? yes | no

Ole Andreas Utstumo wrote 01/05/2016 at 17:18 point

You could apply a voltage, but I'd be even more careful with that. Otherwise I can't think of any other way... You wanna share your secret? :)

I did google this a bit and found an app note from Texas Instruments detailing this kind of measurement: Seems like the prevalent way is by injecting a high frequency current below 100µA into the chest and then measure the impedance.  

Another way of measuring respiration derives the respiration from the ECG readings themselves, and is shared here: This method doesn't require any additional hardware :)

  Are you sure? yes | no

dim liakos wrote 11/30/2015 at 22:06 point

Hi Ole Andreas,

Good Job!

I'm working on a similar project to receive an ecg with xmega a3bu  xplained board.

Will you post the c code? need to see the adc reading for ecg and bat status


  Are you sure? yes | no

Ole Andreas Utstumo wrote 11/30/2015 at 23:23 point

Thanks, dim!

I'm afraid that code is stored somewhere on my old laptop back home. I won't be able to post it untill the 23th of December.

The software employed DMA (Direct Memory Access) to read the ADC values and store them in RAM. I had some trouble with it, but this blog post sorted it all out:

For reading the ADC in an ordinary manner I would suggest creating an example project based on your Xplained board in Atmel Studio and build upon that. There are examples of reading the ADC and of sending the values over CDC to your computer.

Good luck, and check back in Christmas!

  Are you sure? yes | no

Peter Wasilewski wrote 09/14/2015 at 19:45 point

I have a question about the PCB. How did You manage to do so well-looking vias ? Are they a special kind of rivets ? 

Have You tried the lichtenberg's alloy ? It covers the board with a beautiful silver coating, very easy to solder at any time. I think it would preserve the copper  from oxidation.

  Are you sure? yes | no

Ole Andreas Utstumo wrote 09/14/2015 at 23:08 point

Well, thank you! It's just copper rivets carefully inserted with what I suspect is an ordinary riveting tool :) 

Oxidation is starting to become very visible by now, but the prototype already
contains several hardware bugs that should be corrected, so this thing
doesn't need to last long. I'll consider fixing them and order up some
crisp PCBs on the internet with solder mask and all. Making PCBs by etching copper boards, drilling and riveting is great for understanding the raw simplicity of electronics, it is however a lot of work... Thanks for the tip, though!

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates