top of page

Bangle.JS 2 & Dealing with noisy PPG signals

I recently got the latest Bangle.JS watch, mainly because I wanted to do some analysis with raw PPG data and the Espruino based Bangle is the easiest to work with from what I've used. I did some overviews of the Bangle.JS 1 and made a few apps for it - probably the most useful was this one for working out HRV. The Node version I made to process it from logged data explains the process better:

I would actually change a bunch of things in this based on what I know now to make it at least 80% more efficient and not sure the Bangle app actually works now with some of the new firmware updates. I wanted to see how the new model compares and work on that first.

Aside from the tech, this watch looks a lot better than the first one. I didn't really care so much when the initial model came out because I'm mainly using it for my own research projects but I've been wearing this most days, it's super comfortable and looks better. It's definitely priced right and great value for money I think, especially when you factor the development side of things that support it.

The Espruino ecosystem still looks vibrant, a bunch of different devices and still quite a lot of active development. It's probably the best open source project I've worked with, very well documented in terms of hardware and software references and developing apps for these things is pretty easy.

In terms of functional differences with the first Bangle, there are a few things I've noticed the past week:

  1. the Bluetooth transfer speed is faster, it uses the more recent Nordic NRF52840, one of the best allround BLE systems still. However I've found the Bangle 2 to be more temperamental when connecting initially, the first one is more reliable, not sure why but even still, I always get it to work after a reset or switching windows etc.

  2. The always on display is also a lot more practical although the sacrifice here is the brighter screen that was on the original but I think that's fine.

  3. The single button and touchscreen display works well for me, I thought I'd find this a pain because I was happy with just 3 buttons on the first one but I prefer UI controls on the new Bangle. Only thing is a lot the default apps like the launcher (menu) is all touch based to select things but I think a fully swipe based menu would be a lot better, the screen is too small for trying to select small menu items, especially if you have potato fingers :)

  4. The one major aspect is output for the HR sensor is very different to the original. The first Bangle had quite a forgiving raw PPG signal to work with - it only ever needed basic rolling averarges. I've noticed a bandpass filtered version of the signal has been added in newer firmware that is available to use, which is probably to help with more accurate HR readings. The Bangle 2 on the other hand is very different and needs more effort to work with.

Filtering PPG Data

One thing I wanted to do was impliment a way for getting HR interval data from the PPG as well as some other spectral analysis.

On the original bangle, this is how the signal often appears after just doing a rolling average on the raw data:

It would still need a little more filtering but it's a good signal, the peaks generally stay in the same kind of region and overal it's quite stationary the whole time, it's very easy to get accurate interval data from this. On the other hand this is how it looks like on the Bangle.JS 2 most of the time:

It's a more zoomed out view but you can see how it generally wobbles around, this particular signal has already been bandpass filtered. This is still probably enough to be able to derive heartrate but it could get choppy.

I looked at sticking with 25Hz for now rather than increasing the poll rate and I ignore the filtered signal and just use the raw data because the filtering has issues with clipping. The next step I did was to apply differencing to the array to make the signal stationary - you basically just subtract an element by the previous one to remove trends:

This brings the signal back in allignment somewhat and is a quick high-pass filter. It's clear though that this would need more than just rolling averages to filter out at this point. I've been looking at Gaussian filters more recently and they've proven very good a tidying up data like this. Here is what the signal looks like after convolving it with a smooth gaussian filter:

You can see I've also encorporated basic peak detection and it captures all the relevant peaks. There are a couple at the end that could still be noise here but I filter these outliers out by extracting any intervals outside the 90th percentile of the mean.

There are a lot of ways to do this kind of thing, one other option is to use butterworth or similar type of bandpass filter and then go straight into peak detection but I think the above would run faster on embedded hardware.

I prefer working with the Bangle 1 for PPG because the signal is much cleaner but as I say, the newer model is still a lot more practical and easier to use as an actual watch so I'll wear this more day to day. Here's another view with a watchface I made with moon phase and that tracks and updates planetary positions in the sky relative to your GPS location:

There's a lot of other great apps and watchfaces on the Bangle Apploader store and because it's all Javascript you can get a good idea of how they work and even modify yourself to custimize easily:

Currently the above filtering is all done with Python but I'm working on an app that you'll be able to run on the Bangle if I can get it to run fast enough. I'll add a link to it here once it's finished and in the Apploader if I can get it working efficiently.

53 views0 comments

Recent Posts

See All


bottom of page