Heartbeat Logger

A small, portable device that will continuously log your ECG - the "waveform" of your heart - onto a memory card for analysis.

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: Second revision on its way in the mail.

Heartbeat Logger hardware files in Circuit Maker

Heartbeat Logger 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 logger. Noise free, no time stamps.

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



The design of the first prototype. See

Adobe Portable Document Format - 446.10 kB - 01/07/2016 at 09:41

Preview Download

Source code for the prototype with Xmega A3BU micro controller.

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


View all 4 files

  • 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
  • 1 × MCP73831T Power Management ICs / Power Supply Support
  • 1 × 32.768 kHz crystal

  • 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.

    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 :)

  • Medic!

    Ole Andreas Utstumo04/24/2016 at 20:19 0 comments

    Well, finally got steady sampling and writing to the memory card using a timer, the event system, ADC interrupts, some buffers and bit banging SPI to the SD card. How did the data look? I should be slightly worried about my medical condition :)

    Rest assured, I hooked up my oscilloscope on the amplifier exit and saw a perfectly normal heartbeat amid a dose of 50 Hz mains noise. There's nothing a little debugging won't fix.

  • A change of micro controller

    Ole Andreas Utstumo04/16/2016 at 15:30 0 comments

    I dunno about you, but I find it really easy to believe that the SAMD20 sports a DMA controller by glancing at this page:

    Well, it doesn't... Check yer datasheets, always!

    DMA is convenient for the logger, if the system should read from the ADC at an exact frequency while the CPU is free to write to the memory card or do anything else. Plus, both the DMA and the ADC can run in sleep mode, so that they can work together to fill up a buffer, and then wake up the microcontroller to write the buffer to the memory card, thus conserving battery energy. The sampling and writing could be solved by using a FIFO buffer and pausing the write to put a sample into the buffer, but DMA does this elegantly and saves power at the same time.

    Save the DMA for the next revision. I'll try getting by without for now, or solder up another board with a DMA enabled D21 instead.

  • Hardware -- Battery charger

    Ole Andreas Utstumo02/16/2016 at 10:40 0 comments

    Charging the li-ion battery takes a special kind of procedure. If the voltage of the battery is under a set threshold, the battery must first be preconditioned with a small charging current. Then, when it reaches a second threshold, the battery must be charged using constant current untill it reaches 80% of its voltage. The last 20% will be charged using a constant voltage of 4.2V. Battery management ICs exist for exactly this purpose, and can be simple to implement.

    A suitable IC for a single cell li-ion battery pack is the MCP73831, supporting a charging current of up to 500mA. The IC will take any input voltage between 4.2V to 5V. It is recommended to keep the charging current under 1C, meaning that for our 850mAh battery, the charging current must be kept under 850mA. Charging current is programmed using a resistor as describedin the datasheet.

    The MCP73831 features a status pin that will be high-Z (high impedance) when no voltage source is connected or the battery is fully charged, and pulled to GND when the battery is charging. Connecting this pin to the microcontroller lets the device tell the user via a LED when the battery is charged up. An internal pull up must be activated in order to pull the signal HIGH during high-Z.

    I must mention that though charging li-ion cells to 4.2V is standard practice for maximising charge, charging the cell to 4.1V or 4.0V can prolong the lifetime of the cell substantially. Read more about that at Battery University. Few charger ICs offer you to set this limit, though some of them can be set to 4.1V.

View all 31 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