To make the OpenBCI 32bit board work at higher sampling rates wirelessly I’ve designed an adapter board for the HC-05 bluetooth module.
Now there is no need for a separate USB dongle, the board can connect with Android devices and I think this addon even improves robustness of the system (fewer connection issues).
Github Repo of the project
Monthly Archives: February 2017
2 Channel Biopotential Amplifier
All project files are on Github:
Physio – Open Source “Patient Monitor” Platform
This project unites the blood pressure monitor and pulse oximeter projects and adds tons of other features.
The first version of the project can be found on Github:
Pulse Oximeter Hack
The pulse oximeter is a very useful and affordable medical gadget. It incorporates a quick and easy way of measuring your heart rate as well as calculating the blood oxygen saturation in one go! After having a look inside the Contec CMS50C and CMS50D which are both MSP430-based devices with OLED displays, it quickly became apparent, which one is easier to hack to extract the data.
The CMS50C (the one with the color OLED, available for <15$ on the usual platforms) has a nice line of well accessible and labeled pads, among which is also a UART interface that is streaming all of the useful values such as 7bit photoplethismographic curve data, the "bar graph", the SPO2 and heart rate value, a "beat detection bit" and various diagnostic bits at a sampling rate of 60Hz.
An overview over the entire communication protocol can be found here. The serial port settings are a bit unusual: 4800 baud, and EVEN parity, so watch out for that, but other than that it’s a quite clever protocol that is storing all this information in only 5 bytes.
The most essential connections are GND, TX, RST, the right pin of the trigger pushbutton and Batt. After that you should be able to completely control the thing from a microcontroller and harvest the data.
A code sample for Teensy 3.x is found below:
//sketch for reading the CMS50C pulse oximeter data using a Teensy 3.x #define TRIG_PULSEOX 3 // has to be pulled to 3.3V to start the CMS50C #define RST_PULSEOX 15 //vars for pulseOx char incomingPulseOxbyte; unsigned int byteCntPulse = 0; char pulseCurve_PulseOx = 0; char SPO2; char HR_PulseOx; boolean lastBitPulseOx = 0; //the MSB of the heart rate byte is hidden in a different byte - the one that holds the bar graph data unsigned long pulseOxTrigTmr=0; unsigned long pulseOxTrigInt=600000; // length of the pulse that triggers the CMS50C unsigned long pulseOxRstTmr=0; unsigned long pulseOxRstInt=800000; boolean triggeredOx = false; boolean resettedOx = false; //packet management bytes for the Amarino Android app char startFlag = 18; char ack = 19; char delimiter = 59; //';' in case we use more than 1 channel void setup() { // put your setup code here, to run once: Serial1.begin(115200); // Bluetooth port Serial3.begin(4800, SERIAL_8E1); // Pulse oximeter CONTEC CMS 50C, annoying fact: it has EVEN PARITY! // make the pins outputs: pinMode(RST_PULSEOX, OUTPUT); pinMode(TRIG_PULSEOX, OUTPUT); digitalWrite(RST_PULSEOX, HIGH); triggerOx(); //turn on the PulseOx } void loop() { // put your main code here, to run repeatedly: //waiting to pull TRIG_PULSEOX LOW again if((micros()-pulseOxTrigTmr)>pulseOxTrigInt && triggeredOx == true){ digitalWrite(TRIG_PULSEOX, LOW); triggeredOx=false; } readPulseOx(); } void readPulseOx() { //--------------------Pulse Oximeter data analysis---------------- if (Serial3.available() > 0) { //pulseox data incomingPulseOxbyte = Serial3.read(); if (byteCntPulse == 1) { //the second byte contains a 7 bit pulse wave value, Fs=60Hz pulseCurve_PulseOx = incomingPulseOxbyte; byteCntPulse++; } else if (byteCntPulse == 2) { //the 0-3rd bits of the third byte is the bar graph value . //the 6th bit is bit 7 of the heart rate value! if ( bitRead(incomingPulseOxbyte, 6)) { //if the heart rate is >= 128, this happens lastBitPulseOx = 1; } else lastBitPulseOx = 0; byteCntPulse++; //move on } else if (byteCntPulse == 3) { // if (lastBitPulseOx) //if the heart rate is >= 128, this happens HR_PulseOx = 128 + incomingPulseOxbyte; else HR_PulseOx = incomingPulseOxbyte; byteCntPulse++; } else if (byteCntPulse == 4) { //the last byte contains the most important SPO2 value SPO2 = incomingPulseOxbyte; byteCntPulse = 0; //we're done the data packet has 5 bytes in total } else { Serial1.print(startFlag); Serial1.print("P"); Serial1.print(delimiter); Serial1.print(pulseCurve_PulseOx, DEC); //send the pulse curve value Serial1.print(delimiter); Serial1.print(HR_PulseOx, DEC); //send the heart rate Serial1.print(delimiter); Serial1.print(SPO2, DEC); //send SPO2 value! Serial1.print(ack); } //analyze the first byte. it has plenty of information about the data //the fourth bit of the first packet byte means "OK signal" if zero, otherwise it stands for "searching too long" //the seventh bit of the first byte has to be always set!!!!! (the only byte where this is the case) all other bytes have a bit 7 == 0 if ( bitRead(incomingPulseOxbyte, 7)) { byteCntPulse++; } } } void triggerOx(){ //function to trigger the pulse oximeter button digitalWrite(TRIG_PULSEOX,HIGH); pulseOxTrigTmr=micros(); triggeredOx=true; }