{"id":325,"date":"2014-06-06T17:19:16","date_gmt":"2014-06-06T17:19:16","guid":{"rendered":"http:\/\/www.thinkering.de\/cms\/?p=325"},"modified":"2017-02-20T08:16:54","modified_gmt":"2017-02-20T08:16:54","slug":"blood-pressure-monitor-hack","status":"publish","type":"post","link":"http:\/\/www.thinkering.de\/cms\/?p=325","title":{"rendered":"Blood Pressure Monitor Hack"},"content":{"rendered":"<p>Blood pressure Monitor SBM30 (hl868ba) Arduino hack<\/p>\n<p>In some of my internships in hospital, I was wondering if the Schellong Test could be performed automatically. In order to do that one would have to build a programmable blood pressure measuring device. But wait, can&#8217;t we just hack an existing one? I had an SBM30 lying around, which is technically the same as this device.  this <a href=\"http:\/\/jdesbonnet.blogspot.de\/2011\/01\/sanitas-smb30-aka-hl868ba-teardown.html\" title=\"post by Joe Desbonnet\" target=\"_blank\" class=\"external external_icon\" rel=\"nofollow\">post by Joe Desbonnet<\/a> covers the hacking of it. I continued his work. The goal was to make the device completely controllable through its handy testpoint interface. This is what I ended up with:<br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<br style=\u201dclear:both;\u201d \/><\/p>\n<p><a href=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-jack.jpg\"><img loading=\"lazy\" src=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-jack-300x225.jpg\" alt=\"sbm30 jack\" width=\"300\" height=\"225\" class=\"alignnone size-medium wp-image-327\" srcset=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-jack-300x225.jpg 300w, http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-jack-1024x768.jpg 1024w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<a href=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-ribbon-cable.jpg\"><img loading=\"lazy\" src=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-ribbon-cable-300x225.jpg\" alt=\"sbm30 ribbon cable\" width=\"300\" height=\"225\" class=\"alignnone size-medium wp-image-328\" srcset=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-ribbon-cable-300x225.jpg 300w, http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-ribbon-cable-1024x768.jpg 1024w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<a href=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-testpoints-soldered.jpg\"><img loading=\"lazy\" src=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-testpoints-soldered-300x225.jpg\" alt=\"sbm30 testpoints soldered\" width=\"300\" height=\"225\" class=\"alignnone size-medium wp-image-329\" srcset=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-testpoints-soldered-300x225.jpg 300w, http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30-testpoints-soldered-1024x768.jpg 1024w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<a href=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30+arduino-mega-3.jpg\"><img loading=\"lazy\" src=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30+arduino-mega-3-300x225.jpg\" alt=\"sbm30+arduino mega 3\" width=\"300\" height=\"225\" class=\"alignnone size-medium wp-image-330\" srcset=\"http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30+arduino-mega-3-300x225.jpg 300w, http:\/\/www.thinkering.de\/cms\/wp-content\/uploads\/2014\/06\/sbm30+arduino-mega-3-1024x768.jpg 1024w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<iframe width=\"560\" height=\"315\" src=\"\/\/www.youtube.com\/embed\/lNDmzOzxFpY?rel=0\" frameborder=\"0\" allowfullscreen><\/iframe><br \/>\n<br style=\u201dclear:both;\u201d \/><br \/>\n<br style=\u201dclear:both;\u201d \/><\/p>\n<pre class=\"brush: cpp; collapse: true; light: false; title: ; toolbar: true; notranslate\" title=\"\">\r\n\/**\r\nSANITAS SBM30 \/ HL868BA \/ HL168Y blood pressure monitor hack\r\n\r\n\r\n * I2C bus snooper. Written to eavesdrop on MCU to EEPROM \r\n * communications in a HL168Y blood pressure monitor. SCL \r\n * is connected to Arduino pin 2 and SDA to pin 3 (UNO).\r\n * This version will decode read and write operations to \r\n * EEPROM outputting heart rate and blood pressure to the serial port \r\n\r\n * Adapted to ARDUINO UNO: PIND2: D2 -&gt; SCL of EEPROM; PIND3: D3 -&gt; SDA of EEPROM\r\n \r\n *\/\r\n \r\n\/\/watchdog timer include \r\n#include &lt;avr\/wdt.h&gt; \r\n\r\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\r\n\/\/pins to connect the the data and clock line to. \r\n\/\/PORTD 2 &amp; 3 on the UNO: use digital pins 2 and 3\r\n\/\/digital pins 18 and 19 are PORTD 2 &amp; 3 on the Arduino Mega, so you need to change Sclock to 18 and Sdata to 19\r\n\/\/any GPIO pins can be chosen, but the direct port manipulations in the takeMeasurement() function have to be changed!!\r\nint Sclock = 2;\r\nint Sdata = 3;\r\n\r\n\/\/the pin to drive the on\/off\/start button of the device\r\nint startPin = 12; \/\/attach pin 12 to the field of the start button or the according test point!\r\nint buttonPin = 4; \/\/attach a pushbutton from pin 4 to GND, pressing it will start the measurement and print values to serial monitor (11200 baud)\r\n\r\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\r\n\r\n\r\nchar hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};\r\n\r\n#define LOG_SIZE 255\r\nunsigned char datalog[LOG_SIZE];\r\nunsigned char logptr;\r\n\/\/stores the amount of etracted R aaa vv\\n lines after each blood pressure measurement\r\nunsigned char bytesOfData = 0;\r\nboolean measuring_now = false;\r\nboolean measuring_failed = false;\r\n\r\n\/\/bytes to store the final and most important values\r\nunsigned char heart_rate = 0;\r\nunsigned char diast_press = 0;\r\nunsigned char syst_press = 0;\r\n\r\nunsigned char diast_press_hunderter = 0;\r\nunsigned char diast_press_zehner = 0;\r\nunsigned char syst_press_hunderter = 0;\r\nunsigned char syst_press_zehner = 0;\r\n\r\n\r\n\/\/******************************************************************************************************************************************\r\n\/\/                                                  SETUP and LOOP\r\n\/\/******************************************************************************************************************************************\r\n\r\nvoid setup()   {                \r\n  pinMode(Sclock, INPUT);  \r\n  pinMode(Sdata, INPUT); \r\n  pinMode(startPin, OUTPUT); \r\n  pinMode(buttonPin, INPUT); \r\n  digitalWrite(startPin, HIGH);  \/\/HIGH = not pressed\r\n  digitalWrite(buttonPin, HIGH);\r\n  Serial.begin(115200);\r\n\r\n}\r\n\r\nvoid loop()                     \r\n{\r\n \r\n    if (digitalRead(buttonPin)==LOW)  \/\/ Button pressed\r\n    {\r\n      \/\/send signal to BPM\r\n      startButtonActivate();\r\n      \/\/set flag that measurment is running\r\n      \/\/used for breaking out of the waiting loops in the takeMeasurement() function\r\n      measuring_now = true;\r\n    \r\n      takeMeasurement();\r\n    }\r\n\r\n  \r\n  \/\/wdt_reset();\r\n\r\n}\r\n\/\/******************************************************************************************************************************************\r\n\/\/                                                  FUNCTION DEFINITIONS\r\n\/\/******************************************************************************************************************************************\r\n\r\n\/\/Algorithm to passively sniff into the MCU-EEPROM i2c communication of the SBM-30 blood pressure monitor\r\n\/\/based on the code by Joe Desbonnet ( http:\/\/jdesbonnet.blogspot.de\/2010\/05\/using-arduino-as-simple-logic-analyzer.html )\r\nvoid takeMeasurement(){\r\n  \r\n  unsigned char s, b, byteCounter, bitCounter, rwFlag;\r\n  unsigned char addr_hi, addr_lo;\r\n  unsigned int t = 0;\r\n  \r\n  logptr = 0;\r\n  \r\n  waitForStart:\r\n\r\n \r\n   \/\/ Expect both SLC and SDA to be high\r\n  while ( (PIND &amp; 0b00001100) != 0b00001100) {\r\n\r\n    }\r\n  \/\/ both SLC and SDA high at this point\r\n  \r\n  \/\/ Looking for START condition. Ie SDA transitioning from \r\n  \/\/ high to low while SLC is high.\r\n  while ( (PIND &amp; 0b00001100) != 0b00000100) {\r\n    \/\/break out of an infinite loop after a measurement! HERE!\r\n    if(measuring_now == false)\r\n      return;\r\n\r\n      \r\n    if ( (--t == 0) &amp;&amp;  (logptr &gt; 0) ) {   \r\n        writeData();\r\n    }\r\n  }\r\n  \r\n  firstBit:\r\n\r\n      \r\n  byteCounter = 0;\r\n  bitCounter = 0;\r\n  \r\n  nextBit:\r\n\r\n  \/\/ If SCL high, wait for SCL low\r\n  while ( (PIND &amp; 0b00000100) != 0) {\r\n\r\n    }\r\n    \r\n  \/\/ Wait for SCL to transition high. Nothing of interest happens when SCL is low.\r\n  while ( (PIND &amp; 0b00000100) == 0) {\r\n\r\n    }\r\n    \r\n  \/\/ Sample SDA at the SCL low-&gt;high transition point. Don't know yet if this is a\r\n  \/\/ data bit or a STOP or START condition.\r\n  s = PIND &amp; 0b00001000;\r\n    \r\n  \/\/ Wait for SCL to transition low while monitoring SDA for a transition.\r\n  \/\/ No transition means we have data or ACK bit (sample in 's'). A hight to\r\n  \/\/ low SDA = START, a low to high SDA transition = STOP.\r\n  if (s == 0) {\r\n    \/\/ loop while SCL high and SDA low\r\n    while ( (PIND &amp; 0b00001100) == 0b00000100) {\r\n\r\n    }\r\n    if ( (PIND &amp; 0b00001100) == 0b00001100) {\r\n         \/\/ STOP condition detected\r\n         if (logptr &gt; LOG_SIZE - 20) {\r\n        \r\n           writeData();\r\n         }\r\n         goto waitForStart;\r\n    }\r\n  } else {\r\n    \/\/ loop while SCL high and SDA high\r\n    while ( (PIND &amp; 0b00001100) == 0b00001100) {\r\n\r\n    }\r\n    if ( (PIND &amp; 0b00001100) == 0b00000100) {\r\n        \/\/ START condition.\r\n        goto firstBit;\r\n    }\r\n  }\r\n  \r\n  \/\/ OK. This is a data bit.\r\n  bitCounter++;\r\n  \r\n  if (bitCounter &lt; 9) {    \r\n    b &lt;&lt;= 1;\r\n    \/\/ if data bit is '1' set it in LSB position (will default to 0 after the shift op)\r\n    if (s != 0) {\r\n      b |= 0b00000001;\r\n    }\r\n    \r\n    goto nextBit;\r\n  }\r\n  \r\n  \/\/ 9th bit (ack\/noack)\r\n  \r\n  bitCounter = 0;\r\n  byteCounter++;\r\n  \r\n  switch (byteCounter) {\r\n    case 1: \/\/ 1010AAAW where AAA upper 3 bits of address, W = 0 for writer, 1 for read\r\n    if ( (b &amp; 0b11110000) != 0b10100000) {\r\n      goto waitForStart;\r\n    }\r\n    \/\/ Set A9,A8 bits of address\r\n    addr_hi = (b&gt;&gt;1) &amp; 0b00000011;\r\n    rwFlag = b &amp; 0b00000001;\r\n    break;\r\n    \r\n    case 2: \/\/ data if rwFlag==1 else lower 8 bits of address\r\n    if (rwFlag == 1) {\r\n      \/\/ data read from eeprom. Expect this to be the last byte before P\r\n      \/\/datalog[logptr++] = ' ';\r\n      datalog[logptr++] = 'R';\r\n      datalog[logptr++] = ' ';\r\n      datalog[logptr++] = hexval[addr_hi];\r\n      datalog[logptr++] = hexval[addr_lo&gt;&gt;4];\r\n      datalog[logptr++] = hexval[addr_lo &amp; 0b00001111];\r\n      datalog[logptr++] = ' ';\r\n      datalog[logptr++] = hexval[b &gt;&gt; 4];\r\n      datalog[logptr++] = hexval[b &amp; 0b00001111];\r\n      datalog[logptr++] = '\\n';\r\n    } else {\r\n      addr_lo = b;\r\n    }\r\n    break;\r\n   \r\n    case 3: \/\/ only have 3rd byte if rwFlag==0. This will be the data.\r\n    if (rwFlag == 0) {\r\n      \/\/datalog[logptr++] = ' ';\r\n      datalog[logptr++] = 'W';\r\n      datalog[logptr++] = ' ';\r\n      datalog[logptr++] = hexval[addr_hi];\r\n      datalog[logptr++] = hexval[addr_lo&gt;&gt;4];\r\n      datalog[logptr++] = hexval[addr_lo &amp; 0b00001111];\r\n      datalog[logptr++] = ' ';\r\n      datalog[logptr++] = hexval[b&gt;&gt;4];\r\n      datalog[logptr++] = hexval[b &amp; 0b00001111];\r\n      datalog[logptr++] = '\\n';\r\n\r\n      if (logptr &gt; LOG_SIZE - 10) {\r\n        writeData();\r\n      }\r\n      \r\n      break;\r\n    }\r\n    \r\n  } \/\/ end switch\r\n \r\n  goto nextBit;\r\n}\r\n\r\n\/\/******************************************************************************************************************************************\r\n\r\n\/\/start working with the lines (= &quot;important data bytes&quot;) that are written to the EEPROM after a measurement was taken!\r\nvoid writeData () {\r\n  \/\/we only need the data that is &quot;written&quot; , in particular the heart rate and blood pressure, which are two chars that encode hex numbers\r\n  if(datalog[0]=='W'){\r\n     \/\/sometimes two lines are extracted at once, here the data extraction is done and the calculateValues function is called with the values\r\n      if(logptr &gt; 10){\r\n        calculateValues(datalog[6], datalog[7]);\r\n        bytesOfData++;\r\n        \r\n        calculateValues(datalog[15], datalog[16]);\r\n        bytesOfData++;\r\n      } else {\r\n        \/\/when only one line (= &quot;important data byte&quot;) is extracted from the eeprom write procedure after the measurement\r\n        calculateValues(datalog[6], datalog[7]);\r\n        bytesOfData++;\r\n      }\r\n       \r\n    }\r\n    \r\n   measuring_failed = false; \/\/if that comes after \r\n   \/\/device reads from eeprom when you start measuring, so reset the counter for the most important &quot;data bytes&quot;\r\n   if(datalog[0]=='R'){\r\n     bytesOfData = 0;\r\n   }else if(bytesOfData &gt; 9){\r\n     bytesOfData = 0;\r\n     writeToSerial();\r\n   }\/*else if(bytesOfData &gt;= 8){  \/\/this means that a measurement has failed\r\n     wdt_enable(WDTO_4S);                \/\/WATCHDOG!! if no data is written to the screen, because only 9 bytes are extracted (sometimes fucking happens)\r\n     measuring_failed = true;\r\n   }*\/\r\n  \r\n  \/\/clear datalog[] and logptr  \r\n  for (int i = 0; i &lt; logptr; i++) {\r\n    \/\/Serial.write(datalog[i]); \/\/use Serial.write() to send BYTES!!!!!!!!!   \r\n    datalog[i] = 0;\r\n  }\r\n  \/\/Serial.println(logptr);\r\n  logptr=0; \r\n  \/\/Serial.write('\\n');\r\n}\r\n\r\n\/\/******************************************************************************************************************************************\r\n\r\n\/\/make hex numbers out of ASCII characters\r\nbyte getVal(char c)\r\n{\r\n if(c &gt;= '0' &amp;&amp; c &lt;= '9')\r\n   return (byte)(c - '0');\r\n else\r\n   return (byte)(c-'A'+10);\r\n}\r\n\/\/******************************************************************************************************************************************\r\n\r\n\/\/LCD display function, prints out the new values\r\n\/\/gets called at the very end of the measurement and data acquisition process\r\nvoid writeToSerial(){\r\n \r\n  \r\n  Serial.print(&quot;heart rate: &quot;);\r\n  Serial.println(heart_rate);\r\n  \r\n  Serial.print(&quot;pressure: &quot;);\r\n  Serial.print(syst_press);\r\n  Serial.print(&quot;\/&quot;);\r\n  Serial.println(diast_press);\r\n  \/\/once written, you can continue doing other stuff\r\n  \r\n  measuring_now = false;\r\n  \r\n}\r\n\r\n\/\/******************************************************************************************************************************************\r\n\r\n\/\/calculates HR from HEX number and decodes the syst. and diast. blood pressure values from line 5-7\r\nvoid calculateValues(char char6, char char7){\r\n  if( bytesOfData == 9){\r\n    \/\/calculate the pressures \r\n    syst_press = syst_press_hunderter + syst_press_zehner;\r\n    diast_press = diast_press_hunderter + diast_press_zehner;\r\n\r\n  }\r\n  if( bytesOfData == 8){\r\n    \/\/heart rate is encoded in two hex chars\r\n    \/\/make a byte out of two nibbles\r\n    heart_rate = getVal(char7) + (getVal(char6) &lt;&lt; 4);\r\n    \r\n  }\r\n  \/\/diastolic value last two digits\r\n  if( bytesOfData == 7){\r\n    diast_press_zehner = (char6 - '0') * 10 +  (char7 - '0');\r\n  }\r\n  \/\/systolic value last two digits\r\n  if( bytesOfData == 6){\r\n    syst_press_zehner = (char6 - '0') * 10 +  (char7 - '0');\r\n  }\r\n  \/\/diastolic and systolic value first digit\r\n  if( bytesOfData == 5){\r\n    diast_press_hunderter = (char7 - '0') * 100;\r\n    syst_press_hunderter = (char6 - '0') * 100;\r\n  }\r\n          \r\n}\r\n\r\n\/\/******************************************************************************************************************************************\r\n\r\n\/\/Sequence of pulling the test point of the SBM-30 low twice to turn on and\/or make the device start measuring\r\n\r\nvoid startButtonActivate(){\r\n    digitalWrite(startPin, LOW);\r\n    delay(100);\r\n    digitalWrite(startPin, HIGH);\r\n    delay(400);\r\n    digitalWrite(startPin, LOW);\r\n    delay(100);\r\n    digitalWrite(startPin, HIGH);\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Blood pressure Monitor SBM30 (hl868ba) Arduino hack In some of my internships in hospital, I was wondering if the Schellong Test could be performed automatically. In order to do that one would have to build a programmable blood pressure measuring device. But wait, can&#8217;t we just hack an existing one? I had an SBM30 lying &hellip; <a href=\"http:\/\/www.thinkering.de\/cms\/?p=325\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Blood Pressure Monitor Hack<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0},"categories":[1],"tags":[],"_links":{"self":[{"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=\/wp\/v2\/posts\/325"}],"collection":[{"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=325"}],"version-history":[{"count":4,"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=\/wp\/v2\/posts\/325\/revisions"}],"predecessor-version":[{"id":916,"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=\/wp\/v2\/posts\/325\/revisions\/916"}],"wp:attachment":[{"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=325"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=325"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.thinkering.de\/cms\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=325"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}