So, in my previous post, I wrote a bit about retrieving live 60 Hz data from a Contec CMS50D+ pulse oximeter. As mentioned, the device also has another standalone mode where it records pulse rate and blood SpO2 at 1 Hz for up to 24 hours. That is, if your batteries last that long. This thing is quite power hungry.
You can read all about the recording mode in the manual. In this post I’ll focus on the actual data download from the device. There’s really not much to it, so it will be a short post this time…
Let’s look at some recorded data
Whereas the live-mode is strictly one-way, the recorded mode involves a tiny bit of two-way communication to work. You should enable xonxoff to convince Python to talk to the device. The protocol goes as follows:
- Open a connection at 19200 baud, 1O8 with xonxoff enabled.
- Listen for live data. If we get none, the device is disconnected or turned off.
- Send [0xF5, 0xF5]. This switches the device to download mode.
- Wait for the preamble. It’s three times [0xF2, 0x80, 0x00]. In the beginning we might also have some leftover live data.
- Then we get the content length as three bytes. See below for an explanation.
- Receive the specified number of bytes. Each measurement is three bytes. See below for an explanation. Sometimes the download fails and halts midway for some reason and has to be restarted.
- Send [0xF6, 0xF6, 0xF6]. This switches the device back into live mode.
Now you should have a bunch of data to insert into a spreadsheet or whatever.
The length header
The length header tells us how many bytes of data the device will send. It consists of three bytes. The first two bytes always have their MSB set while it’s never set on the last. This gives us 21 useful bits which is enough. If we have recorded 24 hours of data, this will yield 24 * 60 * 60 = 86400 measurements. And if each measurement is three bytes, then the maximum content length will be 259200 bytes. This only requires 18 bits.
Curiously enough, the content length is always one off compared to the actual data length. So we need to add 1 to the result. Let’s look at an example:
- We have received the length header [0x81, 0x8A, 0x2C].
- Validate and strip off MSBs from the first and second byte. Now we have [0x01, 0x0A, 0x2C].
- Left-shift the first byte by 14 bits and the second byte by 7 bits. Combine the three numbers by using the bitwise OR operator. Now we have 0x452C or 17708 in decimal.
- Add 1 to the result. This means that the content length is 17709 bytes.
- Each measurement is three bytes, so we have 17709 / 3 = 5903 measurements. As the device samples at 1 Hz, this means 1 hour 38 minutes and 23 seconds worth of data.
Each measurement consists of three bytes.
- The first byte is always 0xF0 or 0xF1. The 1 is the MSB of the pulse rate in the next byte.
- The second byte is the pulse rate. As the device only utilizes 7 bits per byte for data, the MSB is moved to the first byte. A human pulse rate can quite easily go over 127 BPM…
- The third byte is the SpO2 percentage.
Again, all code for this project can be found on GitHub. If you have any questions, please comment below.