Blueprint

Arrythmix

A novel pipeline for the prediction of cardiac events through machine learning, integrated with a low cost, long term and reusable Holter monitor.

Created by ghost of ttf ghost of ttf 🚀

Tier 2

13 views

1 follower

CAN CAN ⚡🚀 approved Arrythmix ago

Tickets awarded: 813 tickets

Tier: 2

Awesome project!

CAN CAN ⚡🚀 submitted Arrythmix for ship review ago

ghost of ttf ghost of ttf 🚀 submitted Arrythmix for ship review ago

ghost of ttf ghost of ttf 🚀 added to the journal ago

DEMO DEMO DEMO! (also on gh)

It... Worked?

Note: this devlog kinda comes in between the previous one ie. the time period between building the pcb and the rerun app

Note 2: im just as shocked as you are

Video!

shout out to the guy

image
take an 0px image since for some reason videos dont count

ghost of ttf ghost of ttf 🚀 added to the journal ago

PCB Testing + Assembly

Before deploying production code, extensive testing is required[Citation Needed]

This devlog pretty much was just putting the seperate pieces together. We already had a case and pcb, so all we needed to do was integrate it with rerun and put it on the visualizer!

image

data's probably noisy because we were using bad electrodes

We also 3d printed a case for the PCB and finally assembled it into less... bomb-looking

image

image

this is what it looks like on johneshwar, our teammate (in spirit)

note: prior to integrating rerun, we tested the device on a patient using matplotlib (we were in a timecrunch), will upload after censoring

ghost of ttf ghost of ttf 🚀 added to the journal ago

Rerun (Visualisation)

the main goal of this devlog was, well, visualisation. (what did you expect)

the end objective was to have some way to see the following -

  • predictions
  • battery level
  • imu
  • ecg data

all in one neat page, with preferably a way to go back and forth in time (without a flux capacitor). Previously, we had our own implementation using gradio, which both looked bad and was annoying to work on

after a bit of intense research scrolling reddit , i eventually shortlisted three visualisers. These were

  1. Rerun
  2. Serial Studio
  3. TileIO

I discarded Serial Studio as it seemed unintuitive and also dated-looking. While TileIO did look better, it was abandoned without much documentation.

Hence, that left Rerun. It had a well-documented SDK (though t here were some issues), looked good and had an actual usable UI (shocking).

Since the visualiser could be ran as a server, the end architecture ended up being a client that communicates with the device through Bluetooth and asynchronously pushed it to the server. Of course, i immediately ran into a issue
image

Data being compressed due to expanding time window

Now, fixing it should be fairly easy with a sliding window -- Even Rerun had it's own example for it.

Here's the kicker. The example itself produced the above results. Clearly, something was broken.
After some messing around, it turned out that the sdk was bugged (it appears to have been fixed on git). Instead, i had to manually modify a different, well hidden, value to get a sliding window.
image

as you can see from my beautiful and informative illustrations (i will accept no criticism)

I fixed that and finally got this dashboard
image

sadly i don't have any recorded data
and after drafting some code

async def get_prediction():
    while True:
        try:
              rr.log("predictions", rr.TextLog(pred.getPredictionString(), level=rr.TextLogLevel.INFO))
        except Exception as e:
            pass
        await asyncio.sleep(15)
async def notification_handler(sender, data):
    global i, hrs, timestamps, zi
    print(int(data))
    heart_rate = int(data)

    print(f"Heart Rate: {heart_rate}")
    hrs.append(heart_rate)
    rr.set_time("ecg", timestamp=time.time())
    # rr.send_columns("ecg",indexes=[times],columns=rr.Scalars.columns(scalars=hrs))
    rr.log("ecg",rr.Scalars(heart_rate))

it worked.
bye
done
there's nothing beyond this point
i lied

ghost of ttf ghost of ttf 🚀 added to the journal ago

something

This(and the next devlog) is a kinda in-between devlog because chronologically this is before the last devlog(while we were waiting for the PCB, we did this stuff).

We decided to develop another prototype as a backup for the presentation in we got Murphy's Lawed.

image

Essentially, we got this really cool sensor, the heart bioamp candy which is this awesome ECG sensor(based on an op-amp) in a super compact footprint(it doesnt have leads off detection, so oh well).

We used this with a xiao nr52840(initially, then we broke the battery traces so we had to switch to a xiao esp32s3) and packed it into a super tight capsule like footprint. We also tested this on ourselves using the included electrode belt and got OKish readings.

features of this prototype

  • super tiny
  • easy to fix, unlike our PCB
  • better battery life because of the stripped down ecg
  • most of the same firmware stuff

ghost of ttf ghost of ttf 🚀 added to the journal ago

iris synopsis

we didn't qualify so here

A novel pipeline for the prediction of cardiac events through machine learning, integrated with a loww cost, long term and reusable Holter monitor.

Introduction

Cardiovascular disease is a major health concern globally, with it accounting for nearly 1/4th of all deaths per year in India (Volkov, 2015). The very abrupt nature of Sudden Cardiac Death(SCD) and other cardiac events leads to a slow response time, greatly increasing mortality. This represents three pressing concerns in healthcare:

(A) a lack of reusable Holter monitors made for long term monitoring,

(B) an absence of short-term (under one day) prediction of cardiac events like Sudden Cardiac Death and Myocardial Infarctions,

(C) a lack of integration of machine learning and other computer-assisted analytical methods with wearable ECG devices like Holter Monitors and fitness activity monitors.

The primary objective of our work is to solve these problems by creating an accurate, power-efficient method to predict cardiac events in the short term, integrated with a low-cost wearable Holter patch under $30 (~2600 INR).

Innovation

Our work introduces a low-cost, power-efficient system architecture for short-term cardiac event prediction through an integrated wearable and machine learning pipeline. Unlike conventional Holter monitors that depend on retrospective data analysis, the proposed design incorporates real-time preprocessing, motion-compensated noise suppression, wireless transmission and real-time analysis. Our novel machine learning framework comprises two stages: a BiLSTM-based arrhythmia classifier trained on the MIT-BIH Arrhythmia Database (F1-score: 99%) and a hybrid CNN-LSTM predictive model (F1-score: 92%) trained on the Sudden Cardiac Arrest Holter Database to anticipate ventricular fibrillation (VFib, a critical precursor to cardiac arrest) onset. To our knowledge, an ML-integrated low-cost reusable Holter monitor is not present in the current literature or deployment. Additionally, a short-term (> 24-hour timeframe) predictive model for cardiac events is also absent from the current literature.

Algorithmic Design

Several algorithmic components were integrated to ensure maximum predictive performance. Initial processing takes place on the device itself. The AD8232, which records the ECG, contains an onboard high pass, low pass and RFI filter. Additionally, motion artefact reduction is achieved using an adaptive filtering algorithm, specifically Recursive Least Squares (RLS) or Normalised Least Mean Squares (NLMS), with the LM6 accelerometer signal serving as the noise reference. The subsequent machine learning pipeline is split into two stages, with the first stage being a BiLSTM(Bi-Directional Long Short Term Memory) classifier trained on the MIT-BIH Arrhythmia Database to identify and label certain medically significant arrhythmias and beats, achieving an F1-score of 99% The second stage utilises a hybrid CNN-LSTM model trained on the Sudden Cardiac Arrest Holter Database to estimate ventricular fibrillation (VFib) onset, achieving an F1-score of 92% This model assigns ECG segments to four temporal risk categories—low (>24 hours), medium (<12 hours), high (6–12 hours), and critical (<6 hours). This pipeline is optimized for edge deployment, permitting host device flexibility.

Methodology

To ensure user comfort during prolonged use, a breathable, single-lead patch composed of soft, flexible bandaging material with three embedded electrodes was developed, addressing the discomfort and limitations associated with traditional foam electrodes unsuitable for extended monitoring. ECG acquisition is conducted via the AD8232 analog front-end, which integrates a two-pole high-pass, three-pole low-pass, and on-chip radio frequency interference filter to mitigate baseline drift, high-frequency noise, and electromagnetic interference. Real-time motion artefact mitigation employs adaptive filtering algorithms, either Recursive Least Squares (RLS) or Normalised Least Mean Squares (NLMS), using the LSM6DS3 accelerometer signal as a reference input for dynamic noise cancellation. The nRF52840 SoC utilises a 12-bit ADC sampling at 360 Hz and low-latency, energy-efficient Bluetooth Low Energy transmission to the host device. Before machine learning, the data was resampled to 250 Hz to maintain parity with our machine learning model. On the host, a two-stage machine learning pipeline executes inference: Initially, a BiLSTM classifier trained on the MIT-BIH Arrhythmia Database achieves a 99 % F1-score in arrhythmia detection; subsequently, a hybrid CNN-LSTM model trained on the Sudden Cardiac Arrest Holter Database (Greenwald, 1986) predicts wwventricular fibrillation risk across four temporal categories with a 92% F1-score. The models have been quantised for edge deployment and offer platform flexibility. Outputs and risk notifications are delivered to users via a dedicated mobile application, with inference currently performed on smartphones.

Outcomes and Conclusion

Our device and machine learning pipeline have demonstrated both theoretical and practical usability. Our device remains compact, easy to wear and long-lasting, with the onboard battery staying on for 3+ days at a time, at a current draw of approximately 15 mA. Our machine learning pipeline presents an F1-score of 99% and 92% in both stages, respectively. This pipeline has been tested live with our device and a laptop as the host device, and is demonstrating excellent accuracy. Please refer to our project video for a more in-depth demonstration of the same. Due to the inherent difficulties of testing in a real-world environment, it has been tested on the CU Ventricular Tachyarrhythmia dataset (F. M et. al, 1986). and shows a disregardable low false positive rate alongside a 94% accuracy. In the immediate future, our goals are to acquire more compute resources to be able to work with the extensive MIMIC (Medical Information Mart for Intensive Care) IV and III datasets. Additionally, we are awaiting the delivery of a compact PCB (Printed Circuit Board) to reduce the footprint of our device and reduce battery usage. Schematics and photos of the same are available in our Project Video.

References

Goldberger, A. L., Amaral, L. A. N., Glass, L., Hausdorff, J. M., Ivanov, P. C., Mark, R. G., Mietus, J. E., Moody, G. B., Peng, C.-K., & Stanley, H. E. (2000). PhysioBank, PhysioToolkit, and PhysioNet: Components of a new research resource for complex physiologic signals. Circulation, 101(23), e215–e220. https://doi.org/10.1161/01.CIR.101.23.e215

Greenwald, S. D. (1986). Development and analysis of a ventricular fibrillation detector (Master’s thesis). Massachusetts Institute of Technology.

Nolle, F. M., Badura, F. K., Catlett, J. M., Bowser, R. W., & Sketch, M. H. (1986). CREI-GARD, a new concept in computerized arrhythmia monitoring systems. Computers in Cardiology, 13, 515–518.

Patel, H. (2023). Outcomes of out of hospital sudden cardiac arrest in India: A review and proposed reforms. Indian Heart Journal, 75(5), 321–326. https://doi.org/10.1016/j.ihj.2023.08.005

Volkov, S. (2015). Cardiovascular diseases India. World Health Organization. https://www.who.int/india/health-topics/cardiovascular-diseases

image
not putting any pictures here since it's only for 0.1 hours (more but i didn't feel like logging them)

ghost of ttf ghost of ttf 🚀 added to the journal ago

PCB!!! PCB!!! PCB!!

We received the PCB!!!

However since JLC is stupid and stuff, they didn't have a Xiao in their standard parts selection, meaning if we wanted one, we would need to pay 45 DOLLARS!!! for a 15 dollar board.
So, i went to robu.in (my goat), where i found a semi-decent hotplate for 1900 INR. Before that, i spent a bit of time doing continuity testing
Upon getting the hotplate, i had to jack up the temp to nearly 400 degrees and some random glue leaked (pictured)

image

My dumbass forgot to ground battery so we had to manually resolder that with a jumper wire
wf

note: testing cfoming soon!

ghost of ttf ghost of ttf 🚀 added to the journal ago

ZEPHYR!!!! LINUX!!!!

hello guyj and today i weel prezenet the zephir rial tyme opperating sistem.
as part of my never-ending insanity, i rewrote the entire codebase in zephyr. why, you may ask? (even if you didn't) the truth is i am a masochist. i love suffering. yum yum. anyways i digress.
Basically, our main issue was that we were super inefficient. Our current method looked a bit like this

  • wait 2.23 ms or 360 hz
  • get data from ADC
  • send data through BLE
  • profit now obviously, this was a bit stupid for many reasons. It drastically increased our power consumption for zero benefit and wasted a whole lotta time cooling it's heels off. So, i spend hours learning zephyr and finally implement something??? i think??? it worked at least. I did most of this at home rather than school meaning i used my esp32c6 devkitM (best purchase of my life -- don't get a wroom) instead of an nRF52840, but thanks to Zephyr's clever (yet insanely obtuse) devicetree, the only change should be changing the node and it's address our new loop looks a bit like this
  • sleep for 2.23 ms
  • wake up, get data and add to buffer
  • if buffer has 10 instances send them so no more busywaiting. if you want to learn zephyr i'd recommend the digikey tutorials

image
zephyr in action. second usb is for debugging btw

image
dummy data since i don't have the other modules. courtesy of NRFConnect
note: GATT does not define a ECG service afaik, so i'm using the heartrate service instead

ghost of ttf ghost of ttf 🚀 added to the journal ago

Fab Prepping - Devlog 67 (Blueprint should add therapists)

I finally sent off the PCB for fabrication to JLC - however, this also somehow became an exercise in suffering. Firstly, I realised there were some major fuck ups on the PCB, especially where I had accidentally connected the AC/DC pin to 3v3 and made a bunch of other dumb mistakes, which I thankfully corrected. We also had to remove our button because I couldnt find anything that was

  • a toggle switch
  • SMD
  • small enough so I just put two plated holes and we’ll use an external button instead. I also prepared the BOM and removed some traces I had on the inner copper layers(bad practice, apparently). Lastly, I prepared the BOM for manufacture. Atm its looking somewhat dicey if we’re going to receive it on time (by the 19th of December for our big ass presentation) so ehheheheh AHHHHHHHHH(please add the therapists)

image
image
note: my teammate did this (i'll start marking the divisions properly from now on)

ghost of ttf ghost of ttf 🚀 added to the journal ago

Prediction Work (after a long hiatus

After weeks of procrastination, i finally started upon prediction. Rn the model architecture is as simple as it gets – a LSTM and a CNN. However, instead of feeding our data straight to the model (bad) i got it to extract significant features (S/T elevation, HRV) and feed it. I quickly setup a colab notebook with the PTB-XL Dataset (there was a CSV version) and after some data preprocessing and stuff got the model training. However, the accuracy was messed up
image
That’s not very good [citation needed]
I’m going to try a different approach soon enough:™:
image

ghost of ttf ghost of ttf 🚀 added to the journal ago

PCB (retroactive journal)

teammate made this a ~week ago but never sent me pics which is why i'm devlogging it now

I started on the PCB for this project, to make it super smol. Essentially, this device has 3 major components: a Xiao NRF52840, an AD8232 ECG front end and a basic LDO to step down my 3.7v from the LiPo to 3.3v(required by our AFE). I grabbed the AD design from the application note for "portable heart monitor" in the datasheet.

As I’m writing this, I’ve realized I forgot a crucial component: the BQ28570, a LiPo fuel gauge(essentially a battery level indicator which gives me data over i2c) so I will probably have to redo everything. Additionally, I should probably increase my resistor sizes because using a 0201 resistor for a 10 MEGAOHM resistor sounds like the recipe for an IED. We also managed to get a discount from seeed fusion
NOTE: he fixed those in this week.
image

ghost of ttf ghost of ttf 🚀 added to the journal ago

Software BS

While working on the BLE Scripts, I realised that we didn’t actually have a way to visualize both our data and predictions. I thus made an encapsulation class to handle classification easily. I then created a dashboard to tie the different components togheter
image
my dumbass teammate proceeded to call it "ugly" and vibecoded a "better" versiion. pls decide
image
pls decide so i can tell him to b quiet

ghost of ttf ghost of ttf 🚀 added to the journal ago

Made Custom Electrodes

In the quest to make the device more comfortable and more breathable, I decided to attempt to create my own electrode patch, just like the existing holter monitors and integrate the three electrodes into a bandage like patch that can comfortably stick on for days at a time. I used dynaplast, a kind of sticky plaster/bandage that is made of fibre. My first idea was to stick copper tape on to the dynaplast, make 3 distinct electrodes and then check the readings. I tried this out and the readings were HORRID.

Also, the copper tape was kind of sharp, and the thin-ness of it was NOT helping comfort. My second version worked a lot better, and I essentially cut out the centre part from ECG electrode and sewed it onto the dynaplast at three points. This worked BEAUTIFULLY and was super comfortable.
IMG_1104

ghost of ttf ghost of ttf 🚀 added to the journal ago

T-the machine’s learning?? How could it??

Better Arrhythmia Classification and Denoising

While snooping around the internet ("research", i found a github repository that turned out to be an absolute god-send.

It contained journals for near exactly what we needed, requiring only “mild” modifications. So i happily opened them up, got training, and exported a quantized Tensorflow Lite model, right? WRONG. The colabs were so atrociously messed up i wasted a good day and a half getting it to run. We had godforsaken dependency issues
image
Broken Types
image
EVEN MORE Broken Types
image

I did eventually manage to get this working after a day in the trenches, but that was only half the challenge. The ECG Denoising was slightly easier, taking me less time. However, i did waste an HOUR and a HALF on a cell for VISUALIZATION… without realising… We did get pretty outputs though

image

image

image

ghost of ttf ghost of ttf 🚀 added to the journal ago

More time wasted…

I attempted to begin figuring out ECG Denoisng with the IMU. along the way, i found a repository by “Woochan Hwang”, which was a surprisingly close match for what we wanted.
However, upon downloading it, there turned out to be multiple issues. The inbuilt data preprocessing failed multiple times, with numpy array issues which i was unable to fix. In addition, the option to use a self-trained dataset had multiple issues from incorrect path resolution to MORE numpy issues.
No pictures since i rage quit and delete the directory so take this
image

ghost of ttf ghost of ttf 🚀 added to the journal ago

Testing

Devlog 9 - First Test
We, for the first time, 40 hours into the project, finally decided to test it properly on a random kid we found. After correctly configuring the electrodes, we managed to get some good heartbeats. The kid did get a friction burn but it’s a sacrifice for science or smth idk.

image

ghost of ttf ghost of ttf 🚀 added to the journal ago

NRF52840 Build

Now we had to actually BUILD our device, and that was a challenge.

My problem with the old design is that the jumper wires were popping out of of the device and created an annoying amount of length, making our device much thiCCer than we wanted it to be.

Thus, I tried out solid-core wires on our breadboard, which reduced our height by a TON. I may have messed up too by inverting our wiring, something that took us a while to figure out. I then made a chill little case in Fusion, and then printed it out and put the device in. It was a snug fit(yay!!!)
(written by my teammate)
he actually made 2 cases since he messed up the measurements
image
blue wires are the solid coress

image
3D Printed Case + Status Light

image
Device - No Case

note: i will put pics of the case in fusion once my teammate responds to my message
extra note: said teammate is not part of HC since he thinks he'll get his ID leaked
extra extra note: i may have made him think that
extra extra extra note: dw we're splitting the shells

ghost of ttf ghost of ttf 🚀 added to the journal ago

NRF52840 Code

One fairly important factor of a wearable device is ensuring low power consumption. After a bit of looking around, we found the nRF52840 which seems near-purpose built. We settled on the Sense model for the second prototype ( i will eventually make a PCB with the bare SoC)

The nRF does have a integrated IMU which will make it easier to eventually denoise the waves. However, attempting to use our existing code led to issues as the BLE stack was pretty different. I eventually found the n-able-Arduino library, which allowed me to use it.

However, i quickly ran into a issue. PlatformIO requires a board_name attribute to be supplied, and despite support for the nRF52840 Sense being there, there was no board name mentioned.

I ended up taking a look at the raw boards.txt files, where i finally found it

image

ghost of ttf ghost of ttf 🚀 added to the journal ago

Machines CAN'T LEARN… THE CAKE IS A LIE

At this point, we started working on the real meat and potatoes of this
project: the ML. We managed to hack together a god-forsaken colab that used the MIT-BIH dataset and a BiLSTM – basically, i forget this; i remember this; and i output this; but bi-directional thingy.

For some unknown reason it wasn’t running on the Colab, forcing me to run it on my shitty laptop. This took a bunch of reformatting on the file formats – a lot relied on hosted runner specific paths –- as it was aimed at running on the Colab Environment. I finally managed to get it running and forced my laptop to suffer (gtx1650m goes hard) training the model over 100 epochs. it did end up giving us some nice outputs tho :
image
image
image

ghost of ttf ghost of ttf 🚀 added to the journal ago

getting the code to UPLOAD (crazy)

After having completed the hardware design, our focus shifted to software. There was just one tiny problem…

the device would very very unreliably show on lsusb and was even more unreliable in programming, randomly failing during the writing flash section. We eventually managed to get it programming by switching to PlatformIO over Arduino IDE and using a different boards config (i hate arduino). I then got BLE working and transmitting data on the characteristic using the NimBLE-Arduino library
NimBLEService* pService = pServer->createService("180D");
characteristic = pService->createCharacteristic(
"e2fd985e-ceb8-4ccb-9cd3-52563e4b5c62",
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY
);

180D is the id for medical monitoring services

image
Again, shitty data because we were just diddling the electrodes
I also wasted an hour and a half trying to get the device to show as manufactured by “Embedded Jankineers” which unfortunately failed (pls dont tell my teammate)
image

ghost of ttf ghost of ttf 🚀 added to the journal ago

Initial Hardware Design

We LOCKED IN for a school day since there was some shitass event going on

The issue with the Xiao (my beloved) turned out to be that the VOUT did not provide enough current to power the AD.

The good thing is that the Xiao (my beloved) has an inbuilt BMS, meaning we can power the AD and Xiao off the battery
image
However, the battery pads are absolutely tiny.
After a couple of soldering accidents (and nearly starting a fire because SOME idiot left the iron on 450 degrees) we finally had it connected. Having soldered the pads, we begun working on the physical device. We used a 950mAh battery (twas supposed to be 1200mAh but we got scammed :( )
A quick explanation:

  • The blue tape is thermal tape to prevent any overheating issues.
  • The cardboard is part of the antenna, and the button acts as a main power shutoff. We did some heatshrinking to make it look pretty which you can see next to the button.
  • The yellow light is from code already flashed. The actual circuit itself is pretty simple,
  • with the AD connected to the xiao with 3 pins -- output is our analog output, going to A0, while LO+ and LO- are our leads on/leads off indicators, when if both are high then the electrodes are on the body but if one of them is low then one or more electrodes is not making contact. image

note: sorry for not devlogging more but hackclub is blockjed on school wifi
note++: added a MPU6050 too to the circuit but wasn't able to get a phone for pictures

ghost of ttf ghost of ttf 🚀 added to the journal ago

Initial Testng

TL;DR
Crappy electrodes
Cable Interference
Shitty ADC Conversions
Shitty Arduinos
General Idiocy

We finally received the AD8232, the initial sensor we’re using. Our initial test was the good old Arduino. In our eternal unluck, we managed to get the one arduino with that could be programmed but was entirely borked otherwise. After being driven to near suicide, we ended up finding an Arduino that worked. A bit of messing around with the cable (a god-forsaken 3-way audio cable) and we got data logging. ( i will clean my laptop eventually)
image

image
Data sucks because we were just touching the nodes to see if they worked
We also tested it with a Xiao (my beloved) that sadly didn’t work

ghost of ttf ghost of ttf 🚀 added to the journal ago

Research and Ideation

Devlog - Research and Ideation

15hrs

Papers Read

  • Papers: Papers were a pretty big part of our initial ideation, as we wanted this project to be super duper scientific, and we also aim to submit this project to ISEF(awesome science fair) so I read a TON. As a sidenote, we’re 10th graders so the most complex math we’ve done is basic trigonometric identities and functions. This created tens of hours of pain and suffering, and I had to learn a LOT to even understand what these papers were doing – stuff that looked like arcane runes, which is why this research step took hours and hours:

Initial Software Design

After a long and lengthy discussion with ChatGPT, Perplexity and all the other usual suspects (i love outsourcing my thinking!) i setttled upon BLE (aka Bluetooth 5.0) for it’s low power as well as the fact that it can do constant streaming. BLE is also fairly old, which means it should have support on every modern mobile phone. For libraries, I decided to use Arduino initially because i’m not suicidal. However, i do aim to use FreeRTOS alongside eventually so we can perform denoising live with a MPU. The patient is most likely is not a couch dweller like me, meaning we need to eliminate motion artefacts. Here’s an extremely simplified flowchart
image

ghost of ttf ghost of ttf 🚀 started Arrythmix ago

11/3/2025 - Research and Ideation

Devlog - Research and Ideation

15hrs

Papers Read

  • Papers: Papers were a pretty big part of our initial ideation, as we wanted this project to be super duper scientific, and we also aim to submit this project to ISEF(awesome science fair) so I read a TON. As a sidenote, we’re 10th graders so the most complex math we’ve done is basic trigonometric identities and functions. This created tens of hours of pain and suffering, and I had to learn a LOT to even understand what these papers were doing – stuff that looked like arcane runes, which is why this research step took hours and hours:

Initial Software Design

After a long and lengthy discussion with ChatGPT, Perplexity and all the other usual suspects (i love outsourcing my thinking!) i setttled upon BLE (aka Bluetooth 5.0) for it’s low power as well as the fact that it can do constant streaming. BLE is also fairly old, which means it should have support on every modern mobile phone. For libraries, I decided to use Arduino initially because i’m not suicidal. However, i do aim to use FreeRTOS alongside eventually so we can perform denoising live with a MPU. The patient is most likely is not a couch dweller like me, meaning we need to eliminate motion artefacts. Here’s an extremely simplified flowchart
image

11/4/2025 10 AM - Initial Testng

TL;DR
Crappy electrodes
Cable Interference
Shitty ADC Conversions
Shitty Arduinos
General Idiocy

We finally received the AD8232, the initial sensor we’re using. Our initial test was the good old Arduino. In our eternal unluck, we managed to get the one arduino with that could be programmed but was entirely borked otherwise. After being driven to near suicide, we ended up finding an Arduino that worked. A bit of messing around with the cable (a god-forsaken 3-way audio cable) and we got data logging. ( i will clean my laptop eventually)
image

image
Data sucks because we were just touching the nodes to see if they worked
We also tested it with a Xiao (my beloved) that sadly didn’t work

11/4/2025 9 PM - Initial Hardware Design

We LOCKED IN for a school day since there was some shitass event going on

The issue with the Xiao (my beloved) turned out to be that the VOUT did not provide enough current to power the AD.

The good thing is that the Xiao (my beloved) has an inbuilt BMS, meaning we can power the AD and Xiao off the battery
image
However, the battery pads are absolutely tiny.
After a couple of soldering accidents (and nearly starting a fire because SOME idiot left the iron on 450 degrees) we finally had it connected. Having soldered the pads, we begun working on the physical device. We used a 950mAh battery (twas supposed to be 1200mAh but we got scammed :( )
A quick explanation:

  • The blue tape is thermal tape to prevent any overheating issues.
  • The cardboard is part of the antenna, and the button acts as a main power shutoff. We did some heatshrinking to make it look pretty which you can see next to the button.
  • The yellow light is from code already flashed. The actual circuit itself is pretty simple,
  • with the AD connected to the xiao with 3 pins -- output is our analog output, going to A0, while LO+ and LO- are our leads on/leads off indicators, when if both are high then the electrodes are on the body but if one of them is low then one or more electrodes is not making contact. image

note: sorry for not devlogging more but hackclub is blockjed on school wifi
note++: added a MPU6050 too to the circuit but wasn't able to get a phone for pictures

11/5/2025 - getting the code to UPLOAD (crazy)

After having completed the hardware design, our focus shifted to software. There was just one tiny problem…

the device would very very unreliably show on lsusb and was even more unreliable in programming, randomly failing during the writing flash section. We eventually managed to get it programming by switching to PlatformIO over Arduino IDE and using a different boards config (i hate arduino). I then got BLE working and transmitting data on the characteristic using the NimBLE-Arduino library
NimBLEService* pService = pServer->createService("180D");
characteristic = pService->createCharacteristic(
"e2fd985e-ceb8-4ccb-9cd3-52563e4b5c62",
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY
);

180D is the id for medical monitoring services

image
Again, shitty data because we were just diddling the electrodes
I also wasted an hour and a half trying to get the device to show as manufactured by “Embedded Jankineers” which unfortunately failed (pls dont tell my teammate)
image

11/7/2025 10 AM - Machines CAN'T LEARN… THE CAKE IS A LIE

At this point, we started working on the real meat and potatoes of this
project: the ML. We managed to hack together a god-forsaken colab that used the MIT-BIH dataset and a BiLSTM – basically, i forget this; i remember this; and i output this; but bi-directional thingy.

For some unknown reason it wasn’t running on the Colab, forcing me to run it on my shitty laptop. This took a bunch of reformatting on the file formats – a lot relied on hosted runner specific paths –- as it was aimed at running on the Colab Environment. I finally managed to get it running and forced my laptop to suffer (gtx1650m goes hard) training the model over 100 epochs. it did end up giving us some nice outputs tho :
image
image
image

11/7/2025 9 PM - NRF52840 Code

One fairly important factor of a wearable device is ensuring low power consumption. After a bit of looking around, we found the nRF52840 which seems near-purpose built. We settled on the Sense model for the second prototype ( i will eventually make a PCB with the bare SoC)

The nRF does have a integrated IMU which will make it easier to eventually denoise the waves. However, attempting to use our existing code led to issues as the BLE stack was pretty different. I eventually found the n-able-Arduino library, which allowed me to use it.

However, i quickly ran into a issue. PlatformIO requires a board_name attribute to be supplied, and despite support for the nRF52840 Sense being there, there was no board name mentioned.

I ended up taking a look at the raw boards.txt files, where i finally found it

image

11/9/2025 - NRF52840 Build

Now we had to actually BUILD our device, and that was a challenge.

My problem with the old design is that the jumper wires were popping out of of the device and created an annoying amount of length, making our device much thiCCer than we wanted it to be.

Thus, I tried out solid-core wires on our breadboard, which reduced our height by a TON. I may have messed up too by inverting our wiring, something that took us a while to figure out. I then made a chill little case in Fusion, and then printed it out and put the device in. It was a snug fit(yay!!!)
(written by my teammate)
he actually made 2 cases since he messed up the measurements
image
blue wires are the solid coress

image
3D Printed Case + Status Light

image
Device - No Case

note: i will put pics of the case in fusion once my teammate responds to my message
extra note: said teammate is not part of HC since he thinks he'll get his ID leaked
extra extra note: i may have made him think that
extra extra extra note: dw we're splitting the shells

11/10/2025 - Testing

Devlog 9 - First Test
We, for the first time, 40 hours into the project, finally decided to test it properly on a random kid we found. After correctly configuring the electrodes, we managed to get some good heartbeats. The kid did get a friction burn but it’s a sacrifice for science or smth idk.

image

11/11/2025 1 PM - More time wasted…

I attempted to begin figuring out ECG Denoisng with the IMU. along the way, i found a repository by “Woochan Hwang”, which was a surprisingly close match for what we wanted.
However, upon downloading it, there turned out to be multiple issues. The inbuilt data preprocessing failed multiple times, with numpy array issues which i was unable to fix. In addition, the option to use a self-trained dataset had multiple issues from incorrect path resolution to MORE numpy issues.
No pictures since i rage quit and delete the directory so take this
image

11/11/2025 4 PM - T-the machine’s learning?? How could it??

Better Arrhythmia Classification and Denoising

While snooping around the internet ("research", i found a github repository that turned out to be an absolute god-send.

It contained journals for near exactly what we needed, requiring only “mild” modifications. So i happily opened them up, got training, and exported a quantized Tensorflow Lite model, right? WRONG. The colabs were so atrociously messed up i wasted a good day and a half getting it to run. We had godforsaken dependency issues
image
Broken Types
image
EVEN MORE Broken Types
image

I did eventually manage to get this working after a day in the trenches, but that was only half the challenge. The ECG Denoising was slightly easier, taking me less time. However, i did waste an HOUR and a HALF on a cell for VISUALIZATION… without realising… We did get pretty outputs though

image

image

image

11/15/2025 - Made Custom Electrodes

In the quest to make the device more comfortable and more breathable, I decided to attempt to create my own electrode patch, just like the existing holter monitors and integrate the three electrodes into a bandage like patch that can comfortably stick on for days at a time. I used dynaplast, a kind of sticky plaster/bandage that is made of fibre. My first idea was to stick copper tape on to the dynaplast, make 3 distinct electrodes and then check the readings. I tried this out and the readings were HORRID.

Also, the copper tape was kind of sharp, and the thin-ness of it was NOT helping comfort. My second version worked a lot better, and I essentially cut out the centre part from ECG electrode and sewed it onto the dynaplast at three points. This worked BEAUTIFULLY and was super comfortable.
IMG_1104

11/18/2025 9:05 AM - Software BS

While working on the BLE Scripts, I realised that we didn’t actually have a way to visualize both our data and predictions. I thus made an encapsulation class to handle classification easily. I then created a dashboard to tie the different components togheter
image
my dumbass teammate proceeded to call it "ugly" and vibecoded a "better" versiion. pls decide
image
pls decide so i can tell him to b quiet

11/18/2025 9:11 AM - PCB (retroactive journal)

teammate made this a ~week ago but never sent me pics which is why i'm devlogging it now

I started on the PCB for this project, to make it super smol. Essentially, this device has 3 major components: a Xiao NRF52840, an AD8232 ECG front end and a basic LDO to step down my 3.7v from the LiPo to 3.3v(required by our AFE). I grabbed the AD design from the application note for "portable heart monitor" in the datasheet.

As I’m writing this, I’ve realized I forgot a crucial component: the BQ28570, a LiPo fuel gauge(essentially a battery level indicator which gives me data over i2c) so I will probably have to redo everything. Additionally, I should probably increase my resistor sizes because using a 0201 resistor for a 10 MEGAOHM resistor sounds like the recipe for an IED. We also managed to get a discount from seeed fusion
NOTE: he fixed those in this week.
image

11/28/2025 - Prediction Work (after a long hiatus

After weeks of procrastination, i finally started upon prediction. Rn the model architecture is as simple as it gets – a LSTM and a CNN. However, instead of feeding our data straight to the model (bad) i got it to extract significant features (S/T elevation, HRV) and feed it. I quickly setup a colab notebook with the PTB-XL Dataset (there was a CSV version) and after some data preprocessing and stuff got the model training. However, the accuracy was messed up
image
That’s not very good [citation needed]
I’m going to try a different approach soon enough:™:
image

12/9/2025 - Fab Prepping - Devlog 67 (Blueprint should add therapists)

I finally sent off the PCB for fabrication to JLC - however, this also somehow became an exercise in suffering. Firstly, I realised there were some major fuck ups on the PCB, especially where I had accidentally connected the AC/DC pin to 3v3 and made a bunch of other dumb mistakes, which I thankfully corrected. We also had to remove our button because I couldnt find anything that was

  • a toggle switch
  • SMD
  • small enough so I just put two plated holes and we’ll use an external button instead. I also prepared the BOM and removed some traces I had on the inner copper layers(bad practice, apparently). Lastly, I prepared the BOM for manufacture. Atm its looking somewhat dicey if we’re going to receive it on time (by the 19th of December for our big ass presentation) so ehheheheh AHHHHHHHHH(please add the therapists)

image
image
note: my teammate did this (i'll start marking the divisions properly from now on)

12/10/2025 - ZEPHYR!!!! LINUX!!!!

hello guyj and today i weel prezenet the zephir rial tyme opperating sistem.
as part of my never-ending insanity, i rewrote the entire codebase in zephyr. why, you may ask? (even if you didn't) the truth is i am a masochist. i love suffering. yum yum. anyways i digress.
Basically, our main issue was that we were super inefficient. Our current method looked a bit like this

  • wait 2.23 ms or 360 hz
  • get data from ADC
  • send data through BLE
  • profit now obviously, this was a bit stupid for many reasons. It drastically increased our power consumption for zero benefit and wasted a whole lotta time cooling it's heels off. So, i spend hours learning zephyr and finally implement something??? i think??? it worked at least. I did most of this at home rather than school meaning i used my esp32c6 devkitM (best purchase of my life -- don't get a wroom) instead of an nRF52840, but thanks to Zephyr's clever (yet insanely obtuse) devicetree, the only change should be changing the node and it's address our new loop looks a bit like this
  • sleep for 2.23 ms
  • wake up, get data and add to buffer
  • if buffer has 10 instances send them so no more busywaiting. if you want to learn zephyr i'd recommend the digikey tutorials

image
zephyr in action. second usb is for debugging btw

image
dummy data since i don't have the other modules. courtesy of NRFConnect
note: GATT does not define a ECG service afaik, so i'm using the heartrate service instead

12/27/2025 - PCB!!! PCB!!! PCB!!

We received the PCB!!!

However since JLC is stupid and stuff, they didn't have a Xiao in their standard parts selection, meaning if we wanted one, we would need to pay 45 DOLLARS!!! for a 15 dollar board.
So, i went to robu.in (my goat), where i found a semi-decent hotplate for 1900 INR. Before that, i spent a bit of time doing continuity testing
Upon getting the hotplate, i had to jack up the temp to nearly 400 degrees and some random glue leaked (pictured)

image

My dumbass forgot to ground battery so we had to manually resolder that with a jumper wire
wf

note: testing cfoming soon!

2/4/2026 - iris synopsis

we didn't qualify so here

A novel pipeline for the prediction of cardiac events through machine learning, integrated with a loww cost, long term and reusable Holter monitor.

Introduction

Cardiovascular disease is a major health concern globally, with it accounting for nearly 1/4th of all deaths per year in India (Volkov, 2015). The very abrupt nature of Sudden Cardiac Death(SCD) and other cardiac events leads to a slow response time, greatly increasing mortality. This represents three pressing concerns in healthcare:

(A) a lack of reusable Holter monitors made for long term monitoring,

(B) an absence of short-term (under one day) prediction of cardiac events like Sudden Cardiac Death and Myocardial Infarctions,

(C) a lack of integration of machine learning and other computer-assisted analytical methods with wearable ECG devices like Holter Monitors and fitness activity monitors.

The primary objective of our work is to solve these problems by creating an accurate, power-efficient method to predict cardiac events in the short term, integrated with a low-cost wearable Holter patch under $30 (~2600 INR).

Innovation

Our work introduces a low-cost, power-efficient system architecture for short-term cardiac event prediction through an integrated wearable and machine learning pipeline. Unlike conventional Holter monitors that depend on retrospective data analysis, the proposed design incorporates real-time preprocessing, motion-compensated noise suppression, wireless transmission and real-time analysis. Our novel machine learning framework comprises two stages: a BiLSTM-based arrhythmia classifier trained on the MIT-BIH Arrhythmia Database (F1-score: 99%) and a hybrid CNN-LSTM predictive model (F1-score: 92%) trained on the Sudden Cardiac Arrest Holter Database to anticipate ventricular fibrillation (VFib, a critical precursor to cardiac arrest) onset. To our knowledge, an ML-integrated low-cost reusable Holter monitor is not present in the current literature or deployment. Additionally, a short-term (> 24-hour timeframe) predictive model for cardiac events is also absent from the current literature.

Algorithmic Design

Several algorithmic components were integrated to ensure maximum predictive performance. Initial processing takes place on the device itself. The AD8232, which records the ECG, contains an onboard high pass, low pass and RFI filter. Additionally, motion artefact reduction is achieved using an adaptive filtering algorithm, specifically Recursive Least Squares (RLS) or Normalised Least Mean Squares (NLMS), with the LM6 accelerometer signal serving as the noise reference. The subsequent machine learning pipeline is split into two stages, with the first stage being a BiLSTM(Bi-Directional Long Short Term Memory) classifier trained on the MIT-BIH Arrhythmia Database to identify and label certain medically significant arrhythmias and beats, achieving an F1-score of 99% The second stage utilises a hybrid CNN-LSTM model trained on the Sudden Cardiac Arrest Holter Database to estimate ventricular fibrillation (VFib) onset, achieving an F1-score of 92% This model assigns ECG segments to four temporal risk categories—low (>24 hours), medium (<12 hours), high (6–12 hours), and critical (<6 hours). This pipeline is optimized for edge deployment, permitting host device flexibility.

Methodology

To ensure user comfort during prolonged use, a breathable, single-lead patch composed of soft, flexible bandaging material with three embedded electrodes was developed, addressing the discomfort and limitations associated with traditional foam electrodes unsuitable for extended monitoring. ECG acquisition is conducted via the AD8232 analog front-end, which integrates a two-pole high-pass, three-pole low-pass, and on-chip radio frequency interference filter to mitigate baseline drift, high-frequency noise, and electromagnetic interference. Real-time motion artefact mitigation employs adaptive filtering algorithms, either Recursive Least Squares (RLS) or Normalised Least Mean Squares (NLMS), using the LSM6DS3 accelerometer signal as a reference input for dynamic noise cancellation. The nRF52840 SoC utilises a 12-bit ADC sampling at 360 Hz and low-latency, energy-efficient Bluetooth Low Energy transmission to the host device. Before machine learning, the data was resampled to 250 Hz to maintain parity with our machine learning model. On the host, a two-stage machine learning pipeline executes inference: Initially, a BiLSTM classifier trained on the MIT-BIH Arrhythmia Database achieves a 99 % F1-score in arrhythmia detection; subsequently, a hybrid CNN-LSTM model trained on the Sudden Cardiac Arrest Holter Database (Greenwald, 1986) predicts wwventricular fibrillation risk across four temporal categories with a 92% F1-score. The models have been quantised for edge deployment and offer platform flexibility. Outputs and risk notifications are delivered to users via a dedicated mobile application, with inference currently performed on smartphones.

Outcomes and Conclusion

Our device and machine learning pipeline have demonstrated both theoretical and practical usability. Our device remains compact, easy to wear and long-lasting, with the onboard battery staying on for 3+ days at a time, at a current draw of approximately 15 mA. Our machine learning pipeline presents an F1-score of 99% and 92% in both stages, respectively. This pipeline has been tested live with our device and a laptop as the host device, and is demonstrating excellent accuracy. Please refer to our project video for a more in-depth demonstration of the same. Due to the inherent difficulties of testing in a real-world environment, it has been tested on the CU Ventricular Tachyarrhythmia dataset (F. M et. al, 1986). and shows a disregardable low false positive rate alongside a 94% accuracy. In the immediate future, our goals are to acquire more compute resources to be able to work with the extensive MIMIC (Medical Information Mart for Intensive Care) IV and III datasets. Additionally, we are awaiting the delivery of a compact PCB (Printed Circuit Board) to reduce the footprint of our device and reduce battery usage. Schematics and photos of the same are available in our Project Video.

References

Goldberger, A. L., Amaral, L. A. N., Glass, L., Hausdorff, J. M., Ivanov, P. C., Mark, R. G., Mietus, J. E., Moody, G. B., Peng, C.-K., & Stanley, H. E. (2000). PhysioBank, PhysioToolkit, and PhysioNet: Components of a new research resource for complex physiologic signals. Circulation, 101(23), e215–e220. https://doi.org/10.1161/01.CIR.101.23.e215

Greenwald, S. D. (1986). Development and analysis of a ventricular fibrillation detector (Master’s thesis). Massachusetts Institute of Technology.

Nolle, F. M., Badura, F. K., Catlett, J. M., Bowser, R. W., & Sketch, M. H. (1986). CREI-GARD, a new concept in computerized arrhythmia monitoring systems. Computers in Cardiology, 13, 515–518.

Patel, H. (2023). Outcomes of out of hospital sudden cardiac arrest in India: A review and proposed reforms. Indian Heart Journal, 75(5), 321–326. https://doi.org/10.1016/j.ihj.2023.08.005

Volkov, S. (2015). Cardiovascular diseases India. World Health Organization. https://www.who.int/india/health-topics/cardiovascular-diseases

image
not putting any pictures here since it's only for 0.1 hours (more but i didn't feel like logging them)

2/27/2026 - something

This(and the next devlog) is a kinda in-between devlog because chronologically this is before the last devlog(while we were waiting for the PCB, we did this stuff).

We decided to develop another prototype as a backup for the presentation in we got Murphy's Lawed.

image

Essentially, we got this really cool sensor, the heart bioamp candy which is this awesome ECG sensor(based on an op-amp) in a super compact footprint(it doesnt have leads off detection, so oh well).

We used this with a xiao nr52840(initially, then we broke the battery traces so we had to switch to a xiao esp32s3) and packed it into a super tight capsule like footprint. We also tested this on ourselves using the included electrode belt and got OKish readings.

features of this prototype

  • super tiny
  • easy to fix, unlike our PCB
  • better battery life because of the stripped down ecg
  • most of the same firmware stuff

3/1/2026 - Rerun (Visualisation)

the main goal of this devlog was, well, visualisation. (what did you expect)

the end objective was to have some way to see the following -

  • predictions
  • battery level
  • imu
  • ecg data

all in one neat page, with preferably a way to go back and forth in time (without a flux capacitor). Previously, we had our own implementation using gradio, which both looked bad and was annoying to work on

after a bit of intense research scrolling reddit , i eventually shortlisted three visualisers. These were

  1. Rerun
  2. Serial Studio
  3. TileIO

I discarded Serial Studio as it seemed unintuitive and also dated-looking. While TileIO did look better, it was abandoned without much documentation.

Hence, that left Rerun. It had a well-documented SDK (though t here were some issues), looked good and had an actual usable UI (shocking).

Since the visualiser could be ran as a server, the end architecture ended up being a client that communicates with the device through Bluetooth and asynchronously pushed it to the server. Of course, i immediately ran into a issue
image

Data being compressed due to expanding time window

Now, fixing it should be fairly easy with a sliding window -- Even Rerun had it's own example for it.

Here's the kicker. The example itself produced the above results. Clearly, something was broken.
After some messing around, it turned out that the sdk was bugged (it appears to have been fixed on git). Instead, i had to manually modify a different, well hidden, value to get a sliding window.
image

as you can see from my beautiful and informative illustrations (i will accept no criticism)

I fixed that and finally got this dashboard
image

sadly i don't have any recorded data
and after drafting some code

async def get_prediction():
    while True:
        try:
              rr.log("predictions", rr.TextLog(pred.getPredictionString(), level=rr.TextLogLevel.INFO))
        except Exception as e:
            pass
        await asyncio.sleep(15)
async def notification_handler(sender, data):
    global i, hrs, timestamps, zi
    print(int(data))
    heart_rate = int(data)

    print(f"Heart Rate: {heart_rate}")
    hrs.append(heart_rate)
    rr.set_time("ecg", timestamp=time.time())
    # rr.send_columns("ecg",indexes=[times],columns=rr.Scalars.columns(scalars=hrs))
    rr.log("ecg",rr.Scalars(heart_rate))

it worked.
bye
done
there's nothing beyond this point
i lied

3/4/2026 7 PM - PCB Testing + Assembly

Before deploying production code, extensive testing is required[Citation Needed]

This devlog pretty much was just putting the seperate pieces together. We already had a case and pcb, so all we needed to do was integrate it with rerun and put it on the visualizer!

image

data's probably noisy because we were using bad electrodes

We also 3d printed a case for the PCB and finally assembled it into less... bomb-looking

image

image

this is what it looks like on johneshwar, our teammate (in spirit)

note: prior to integrating rerun, we tested the device on a patient using matplotlib (we were in a timecrunch), will upload after censoring

3/4/2026 8 PM - DEMO DEMO DEMO! (also on gh)

It... Worked?

Note: this devlog kinda comes in between the previous one ie. the time period between building the pcb and the rerun app

Note 2: im just as shocked as you are

Video!

shout out to the guy

image
take an 0px image since for some reason videos dont count