Skip to content

About

Open-source cuffless blood pressure from pulse transit time: ECG, chest vibration (SCG) and finger PPG on an Arduino, with a browser app and a built-in simulator. Research prototype, not a medical device.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

Repository files navigation

PulseTransit BP: blood pressure without a cuff, from pulse transit time. ECG, chest-vibration and finger-pulse traces with the transit time of each beat marked.

Live demo · Hardware · How it works · Calibration · Limitations

Tests Live demo Arduino Mega 2560 MIT license

PulseTransit BP estimates blood pressure without a cuff. An ECG, an accelerometer on the breastbone and a pulse sensor on a fingertip time how long each heartbeat takes to travel from the heart to the finger: the pulse transit time (PTT). When blood pressure rises, the arteries stiffen and the pulse travels faster, so PTT falls. After a one-time calibration against a regular cuff, the app turns each 30-second recording into systolic and diastolic pressure.

It started as a student project and was rebuilt in 2026 for public release.

Warning

PulseTransit BP is a research prototype, not a medical device. Its readings are estimates that have not been validated. Do not use them for diagnosis or treatment.

Cuffless blood pressure is moving into products: in July 2025 the FDA cleared Aktiia's wrist-worn optical monitor (sold as Hilo) for over-the-counter use, with a cuff recalibration every 24 hours [1]. How to validate such devices is still being settled [2, 3]. PulseTransit BP is an open version of one of the main approaches, transit time from the aortic valve to the fingertip [4], built from hobby modules you can read, change and test.

Try it

Open the live demo. No hardware needed: the built-in simulator produces a person's ECG, chest vibration and finger pulse, at rest, after exercise or five minutes later, and you can run the whole flow:

  1. Calibrate. Record 30 s in each of the three situations. The simulator fills in its own cuff reading.
  2. Save the calibration, go to Measure and press Measure.
  3. Or, on the Calibrate screen, press Load demo person for a calibrated person with a week of readings.

Set the speed to 4× and each recording takes about 8 s.

The Measure screen: a reading of 119/77 mmHg with heart rate and transit time, and the ECG, chest vibration and finger pulse on ECG paper with each beat's transit time marked (124 to 132 ms).

The Calibrate screen: three situations with their cuff readings and transit times, and the fitted curves of systolic and diastolic pressure against transit time. The History screen: systolic and diastolic readings over a week and a table of readings.

To run it locally (Node 20 or later, no dependencies):

git clone https://github.com/abrilcode/pulsetransit-bp.git
cd pulsetransit-bp
node scripts/serve.mjs        # http://localhost:8000

Any static file server pointed at app/ works as well, for example python3 -m http.server --directory app 8000.

What it does

Live signals ECG, seismocardiogram (chest vibration) and PPG at 200 samples/s, with the R peak, aortic-valve opening, pulse foot and transit time of every beat marked.
Measurement 30 s recording; beat-by-beat PTT, outliers dropped, averaged; heart rate from the R-R intervals; systolic and diastolic pressure from the person's calibration.
Calibration Cuff reading plus 30 s of signal at rest, after exercise and five minutes later, fitted by least squares. Shows the curves and R².
Quality checks Flags an electrode that is off, lost samples, too few clean beats, and readings outside the calibrated range.
People and history One calibration per person, readings over time, CSV and JSON export, and a printable report. All stored in the browser.
Two links USB, or Bluetooth through an HC-05 that the computer sees as a serial port.

Hardware

Sensor placement: ECG electrodes RA and LA below the clavicles and RL on the lower left abdomen, the MPU-6050 on the lower sternum, the MAX30102 on an index finger, and a cuff on the upper arm for calibration only. The pulse transit time runs from the sternum to the fingertip, about 0.1 to 0.13 s at rest.

Part Role
Arduino Mega 2560 Samples all three sensors at 200 Hz and streams them
AD8232 ECG module + 3 electrodes R peaks: splits the recording into beats
MPU-6050 breakout (GY-521) Seismocardiogram: the aortic valve opening, start of the transit
MAX30102 breakout Finger PPG: the pulse arriving, end of the transit
HC-05 Bluetooth module, 1 kΩ and 2 kΩ resistors Optional wireless link
A validated upper-arm cuff Calibration only

Wiring: AD8232 OUTPUT to A0, LO+ to D10, LO- to D11, powered from 3.3 V; GY-521 and MAX30102 share I2C on pins 20 (SDA) and 21 (SCL) at 5 V; HC-05 TXD to RX1 (19) and RXD from TX1 (18) through a 1 kΩ / 2 kΩ divider.

Flash the firmware

Install these from the Arduino Library Manager: TimerThree by Paul Stoffregen, Adafruit MPU6050, Adafruit Unified Sensor, and SparkFun MAX3010x Pulse and Proximity Sensor Library. Then open firmware/pulsetransit_bp/pulsetransit_bp.ino, select Arduino Mega or Mega 2560 and upload. Or with arduino-cli:

arduino-cli core install arduino:avr
arduino-cli lib install "TimerThree" "Adafruit MPU6050" "Adafruit Unified Sensor" "SparkFun MAX3010x Pulse and Proximity Sensor Library"
arduino-cli compile --fqbn arduino:avr:mega firmware/pulsetransit_bp
arduino-cli upload  --fqbn arduino:avr:mega --port /dev/cu.usbmodem1101 firmware/pulsetransit_bp

For Bluetooth, set the HC-05 to 115200 baud once with the AT command AT+UART=115200,0,0, then pair it with the computer.

Connect

Connecting in four steps: plug in the board or pair the HC-05; in the app choose Device and press Connect; pick the port in the browser's list; the ECG, SCG and PPG signals appear with the PTT.

Open the app in Chrome or Edge on a desktop (Web Serial is not available in Safari or Firefox), choose Device, press Connect and pick the board's USB port or the paired HC-05.

How it works

One heartbeat: the ECG R peak, the aortic-valve opening on the chest vibration 100 ms later (pre-ejection period), and the foot of the finger pulse 129 ms after that (pulse transit time), found where the tangent at the steepest rise meets the minimum before it.

For each 30-second recording, app/js/analysis.js:

  1. Finds the R peaks in the ECG and splits the recording into beats.
  2. High-passes the chest signal at 2 Hz to remove breathing, and takes the aortic-valve opening as its largest deflection in the first 0.2 s after each R peak. The sign comes from the average beat, so the accelerometer can sit either way up.
  3. Flips the PPG, because the red count falls as blood arrives. It finds the foot of each pulse by intersecting tangents: where the tangent at the steepest rise crosses the minimum before it.
  4. Takes PTT as foot minus valve opening for each beat. It keeps beats between 0 and 0.65 s that lie within 2 SD of the mean, and averages them.

Filters run forwards and backwards, so they shift no peak in time. PTT falls as pressure rises because stiffer arteries carry the pulse faster [5]. Starting from the valve opening instead of the R peak leaves out the pre-ejection period. That period changes with how strongly the heart contracts, not only with pressure, and is a known weakness of ECG-based pulse arrival time [4, 6]. In healthy adults the aorta-to-fingertip transit time is about 120 ms at rest and shorter after exercise [7].

The analysis has no DOM code. The app, the tests and the figures in this README all run the same implementation; the figures are rendered from its output on the simulator (docs/figures).

Calibration

The relation between PTT and pressure differs from person to person, so each person calibrates once against a cuff:

SBP = a / PTT + b        DBP = c / PTT + d

Take a cuff reading while recording 30 s in three situations that move blood pressure: seated at rest, right after a minute of brisk exercise, and five minutes later. app/js/calibration.js fits a, b, c and d by least squares and reports R². It refuses readings whose PTTs are less than 5 ms apart, since those cannot separate slope from offset. Readings outside the calibrated PTT range are marked as extrapolated.

On the built-in simulator, calibrating on the three situations and then measuring two new ones lands within 4 mmHg systolic and 3 mmHg diastolic of the true values. That checks the software, not the method: the simulator follows the same model the calibration fits.

Serial protocol

Serial format: one line per sample, 200 per second at 115200 baud: n,ecg,scg,ppg,lo, for example 1532,512,-14,123456,0. The host sends 1 to start and 0 to stop; status lines start with #.

host -> board   1                     start streaming
                0                     stop
board -> host   # <message>           status
                n,ecg,scg,ppg,lo      one sample, 200 per second

The link runs at 115200 baud, on USB and on the HC-05.

  • n counts samples and wraps at 65535, so a gap means samples were lost. The app bridges short gaps and reports them.
  • ecg is A0, 0–1023.
  • scg is the z acceleration in 0.01 m/s².
  • ppg is the MAX30102 red count (18-bit).
  • lo is 1 when an ECG electrode is off.

The longest line is 26 bytes, so 200 lines a second use 45% of the link. app/js/simulator.js produces the same lines.

Project layout

app/                         the browser app: a static site, no build step
  js/analysis.js             R peaks, aortic-valve opening, pulse foot, PTT
  js/calibration.js          least-squares calibration and estimates
  js/session.js              live buffers, dropped samples, 30 s recordings
  js/protocol.js             serial line parser
  js/simulator.js            simulated person with known blood pressure
  js/signal.js               zero-phase Butterworth filters, statistics
  js/serial.js               Web Serial link
  js/store.js                people, calibration and readings in localStorage
  js/charts.js               signals, calibration fit and trend on canvas
firmware/pulsetransit_bp/    Arduino sketch
docs/figures/                sources of the images in this README
tests/                       test suite (node --test)
scripts/serve.mjs            local server

Development

npm test                        # or: node --test 'tests/*.test.mjs'
node docs/figures/build.mjs     # re-render docs/img (needs Google Chrome or Chromium)

The tests check against the simulator's true values:

  • filters, R peaks and heart rate in all three situations;
  • that the measured PTT follows the true PTT with a constant offset;
  • that the accelerometer can be mounted either way up;
  • that the calibration fit recovers the model;
  • the end-to-end blood-pressure error;
  • the serial parser, lost samples and 30-second recordings;
  • storage and export.

Limitations

  • No clinical validation. The processing has been tested on simulated signals. Validation would mean following the ESH recommendations for cuffless devices [2]. The universal standard for cuff devices asks for 85 subjects, a mean error within ±5 mmHg and an SD of at most 8 mmHg [8].
  • Calibration drifts. The PTT-pressure relation changes with time, posture and medication, so calibrations need repeating. The FDA-cleared cuffless device above recalibrates every 24 hours [1]. Three calibration points are the minimum, and the model is only trustworthy within the calibrated range.
  • Posture and hand height matter. Keep the hand with the finger sensor at heart level and sit the same way each time. Raising or lowering the hand changes the pressure at the finger.
  • The aortic-valve point is simplified. It is the largest early peak of the chest vibration. Published methods use more of the waveform, and the signal depends on placement and on the person.
  • Timing resolution. 200 Hz gives 5 ms per sample. Averaging about 30 beats improves the resolution, but motion and breathing add error. The tangent foot also sits a few milliseconds after the true onset; calibration absorbs a constant offset, not a variable one.
  • Hobby-grade parts. The AD8232 is not a diagnostic ECG, and the MAX30102 and MPU-6050 are not medical sensors.
  • Records stay in one browser. They are stored unencrypted in localStorage; export them to keep them.

Next steps

  • Test the analysis on the SCG-RHC wearable database [9]. It has a chest patch's ECG and seismocardiogram, synchronised with an arterial line and a pulse oximeter, from 73 patients during heart catheterisation.
  • Find beats from the chest signal alone, so the ECG becomes optional.
  • Try other calibration models, and decide when a calibration has expired.
  • Any study on people needs approval from an ethics committee.

References

  1. U.S. Food and Drug Administration (2025). 510(k) K250415: Aktiia G0 Blood Pressure Monitoring System, decided 2 July 2025. https://www.accessdata.fda.gov/scripts/cdrh/cfdocs/cfpmn/pmn.cfm?ID=K250415
  2. Stergiou, G. S., Avolio, A. P., Palatini, P., et al. (2023). European Society of Hypertension recommendations for the validation of cuffless blood pressure measuring devices. Journal of Hypertension, 41(12), 2074–2087. https://doi.org/10.1097/HJH.0000000000003483
  3. ISO 81060-3:2022. Non-invasive sphygmomanometers, Part 3: Clinical investigation of continuous automated measurement type. https://www.iso.org/standard/71161.html
  4. Carek, A. M., Conant, J., Joshi, A., Kang, H., & Inan, O. T. (2017). SeismoWatch: Wearable cuffless blood pressure monitoring using pulse transit time. Proceedings of the ACM on Interactive, Mobile, Wearable and Ubiquitous Technologies, 1(3), 40. https://doi.org/10.1145/3130905
  5. Mukkamala, R., Hahn, J.-O., Inan, O. T., et al. (2015). Toward ubiquitous blood pressure monitoring via pulse transit time: theory and practice. IEEE Transactions on Biomedical Engineering, 62(8), 1879–1901. https://doi.org/10.1109/TBME.2015.2441951
  6. Finnegan, E., Davidson, S., Harford, M., et al. (2021). Pulse arrival time as a surrogate of blood pressure. Scientific Reports, 11, 22767. https://doi.org/10.1038/s41598-021-01358-4
  7. Vaini, E., Lombardi, P., & Di Rienzo, M. (2015). Aortic-finger pulse transit time vs. R-derived pulse arrival time: a beat-to-beat assessment. Computing in Cardiology, 42, 253–256. https://doi.org/10.1109/CIC.2015.7408634
  8. Stergiou, G. S., Alpert, B., Mieke, S., et al. (2018). A universal standard for the validation of blood pressure measuring devices. Hypertension, 71(3), 368–374. https://doi.org/10.1161/HYPERTENSIONAHA.117.10237
  9. Chan, M., Klein, L., Fan, J., & Inan, O. (2023). SCG-RHC: Wearable seismocardiogram signal and right heart catheter database (version 1.0.0). PhysioNet. https://doi.org/10.13026/133d-pk11

Further reading: Ding, X., & Zhang, Y.-T. (2019). Pulse transit time technique for cuffless unobtrusive blood pressure measurement: from theory to algorithm. Biomedical Engineering Letters, 9(1), 37–52. https://doi.org/10.1007/s13534-019-00096-x

Author

Abril Herrlein.

License

Code under the MIT license. The Geist and Geist Mono fonts in app/fonts are under the SIL Open Font License 1.1.

About

Open-source cuffless blood pressure from pulse transit time: ECG, chest vibration (SCG) and finger PPG on an Arduino, with a browser app and a built-in simulator. Research prototype, not a medical device.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages