Advertisement
CanadianVice

Useful Data Logging of BLUETOOTH PULSE OXIMETER from China - Guide

Mar 4th, 2021
1,629
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.61 KB | None | 0 0
  1. BETTER METHOD TO LOG DATA FROM CHEAP CHINESE BLUETOOTH OXIMETERS (ALIEXPRESS, EBAY, AMAZON, ETC.)
  2. ==================================
  3.  
  4. There are lots of cheap oximeters that can pair with your phone via bluetooth floating around on Aliexpress, ebay, gearbest, basically any of the sites that make a business of selling Chinese manufactured goods to consumers. However, as I have found myself - the apps that you install to use these are - almost universally awful.
  5.  
  6. So, this will require a degree of technical aptitude, but anyone who knows much about tech should be able to easily assist those who do not, or use this guide themselves.
  7.  
  8. This guide will be written with a specific app and Android in mind, but the broader principles can likely be used with some adaptation for other devices or apps. My specific Bluetooth Oximeter is a F7. Unbranded, more or less. It's blue border, white, and has that cheap screen - quite small.
  9.  
  10. YOU WILL NEED:
  11. 1. Cheap Chinese Bluetooth Oximeter
  12. 2. nRF-Connect App: https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en_CA&gl=US
  13. 3. nRF Logger App: https://play.google.com/store/apps/details?id=no.nordicsemi.android.log&hl=en_CA&gl=US
  14.     NOTE: The essence of these apps that is important is the ability to connect to and monitor a Bluetooth Low-Energy (BLE) device and sniff its data stream; The code I post here will be formatted to the spec of the log format this one uses, but you can take inspiration from it for your own if it's different, I'm sure.
  15. 4. QPython or a Computer (latter is better; ideally with python installed on the desktop) - this will let you run this script to parse your data into a more readable format you can use in a Spreadsheet app of your choice.
  16.  
  17. INSTRUCTIONS FOR INITIAL SETUP
  18. 1. Download and load these apps
  19. 2. Turn on the Bluetooth oximeter, make sure your device's BT is on
  20. 3. Keep your finger in it to prevent shutdown if it has this mechanism
  21. 4. Soon after powering it on, you need to connect. If you wait, it will not continue broadcasting and you will have to restart it to allow connection.
  22. 5. Once that's ready, open nRF-connect, and scan for devices. You should see your Oximeter show up - mine is named F7... you *can* look in the normal Android BT menu to see its name easier, but note it will not connect properly via this menu.
  23. 6. Once found in the device scan, connect to it in the nRF-connect app
  24. 7. Once connected, look at its details page, and note the multiple subsections of data. You will want to find the one that says it has something to do with NOTIFY.
  25. 8. Expand this entry, and to its right, you will see an icon looking like 3-ish arrows. Press it to enable that channel.
  26. 9. Swipe right, check to see the console window is outputting connection details and a periodic packet ping.
  27. 10. Upon finishing logging, whatever it is you need it for, click "Save Log" (Diskette icon) - save the file, move it to where you need to work to run the code.
  28.  
  29. THE DATA AND PRINCIPLE
  30. You should see this packet expressed as hexidecimal - numbers that are from A-F and 0-9. These represent binary data. If you use an alternative logging app, you will need to do some footwork to figure out which bytes (a pair of two of these digits) correspond with the values the oximeter is reading.
  31.  
  32. For mine, it was the last 3. The easy way to tell is when you have a fairly stable reading, note what it is - then take the hexidecimal you see in the log entry and tell google to tell you what number it is. NOTE: Do this by the byte. Whichever one corresponds is the byte that refers to your data you will need. This information is in case you need to restructure the filtering code I wrote for different location of said data.
  33.  
  34. Anyway, once the app is getting that periodic ping, you can let your phone screen turn off or whatever. It won't disrupt this functionality.
  35.  
  36. PARSING THE DATA INTO SOMETHING USEFUL
  37. So, this is where the python (3.8+) code comes in. You will have to make edits if you're not running it on desktop, mostly to do away with the file-system based stuff. I make small comments on what to change for this, but if you can't figure it out, someone techy should be readily able to do so.
  38.  
  39. Options for running "this" code are:
  40. 1. QPython (Android)
  41. 2. Repl.it/languages/python3
  42. 3. [BEST] Install python to your computer from the official site
  43.  
  44. THE SCRIPT
  45. Open a file in notepad, vscode, notepad++ - but don't use a full-feature editor like Word. Basic is good here. Copy and paste the following (designed to run on Windows) code into said file, and save it with a .py extension. If running online, make the requisite changes before opting to run the code.
  46.  
  47. ########### BEGIN PYTHON PARSING SCRIPT
  48. # Take a log input file from and parse out key values into a TSV file for use with spreadsheet apps.
  49.  
  50. import re
  51. import os
  52. import datetime
  53.  
  54. # hex2int(hex):
  55. # param: str hex: A byte written as hexidecimal in string format
  56. # return: int -: The hex value represented as a decimal base integer
  57. # Convert Hexidecimal bytes to their integer equivalents in normal base 10
  58. def hex2int(hex):
  59.    res = re.match("[A-F0-9]", str(hex).upper())
  60.    if (res != None and res.group(0) != None):
  61.        return int(hex, 16)
  62.  
  63. # ParseEntry(entry)
  64. # param: str entry: A single line entry in the log pulled from nFC Connect's logs containing an update packet's data
  65. # return: dict values: A dictionary containing the values from the packet as [str TIME, int SPO2, int BPM, int PI]
  66. # Parse out the time and last 3 hexidecimal values from a BLE packet logged by nFC-Connect app. Create a dictionary to encapsulate key entry values.
  67. def parseEntry(entry):
  68.     # 12:06:02.949  Notification received from 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) AB-00-06-FF-30-C0-56-61-0D
  69.  
  70.    patt = r".*?(?P<time>\d+:\d+:\d+\.\d+)\s+Notification received.+?\(0x\) ([A-F0-9]{2}\-){6}(?P<bpm>[A-F0-9]{2})\-(?P<spo2>[A-F0-9]{2})\-(?P<pi>[A-F0-9]{2})"
  71.    values = {
  72.        "time": None,
  73.        "SpO2": None,
  74.        "BPM": None,
  75.        "PI": None
  76.    }
  77.  
  78.    matchResult = re.match(patt, entry)
  79.    if (matchResult != None):
  80.        values["time"] = matchResult.group("time")
  81.        values["SpO2"] = hex2int(matchResult.group("spo2"))
  82.        values["BPM"] = hex2int(matchResult.group("bpm"))
  83.        values["PI"] = hex2int(matchResult.group("pi"))
  84.  
  85.    return values
  86.  
  87. def main():
  88.    res = []
  89.  
  90. # If you are running this code online, and not locally on your machine, replace this segment with:
  91. # logItems = "<copy/paste your log file contents here>".split("\n")
  92. # for entry in logItems.... (basically follow the code from "For entry in fp.readlines()")
  93.    dir = str(input("Please enter the path of the log file: ")).replace("\"", "")
  94.    with open(os.path.join(dir), 'r') as fp:
  95.        for entry in fp.readlines():
  96.            if ("value: (0x)") in entry:
  97.                temp = parseEntry(entry)
  98.                if ( None in temp.values() ): continue
  99.                res.append(parseEntry(entry))
  100.            else:
  101.                continue
  102.  
  103.        fp.close()
  104.    
  105. # Note this portion can be more or less disregarded IF you are running it online. Otherwise, keep it - this will save the TSV file.
  106. # Instead of this, just use the for loop below. Take out all references to sfp since we are not using a file if you are doing this online.
  107.  
  108.    saveDir = str(input("Please enter where you want to save the file: ")).replace("\"", "")
  109.    saveName = str(input("Enter a filename (without ext): "))
  110.    if (not saveName.endswith(".tsv")): saveName += ".tsv"
  111.    with  open(os.path.join(saveDir, saveName), 'w') as sfp:
  112.        sfp.write('TIME\tSPO2\tBPM\tPI\n')
  113.        for item in res:
  114.            strTemp = [str(item[val]) if val != 'PI' else str(item[val])[:-1] + "." + str(item[val])[-1] for val in item.keys() ]
  115.            sfp.write("\t".join(strTemp) + "\n")
  116.        sfp.close()
  117.  
  118. main()
  119.  
  120. ################ END PYTHON SCRIPT
  121.  
  122. SUMMARY
  123. This will take the key 3 values (perfusion index, blood spo2, beats per minute) and put them in a columnar Tab-separated values (think CSV) file that will be easy to read into a spreadsheet program as raw data. From there, do what you want with it. Note, if you don't have a . in PI: it's often saved as an integer and the decimal inserted manually. This value can only be from 0%-20%, so use that to guide where to place the decimal. IF you use the same device I did, this code shouldn't need that correction as it's already implemented. If you use another device, be aware you may need to use google to convert the hex (by byte!) to numbers to figure out where in the packet the core data is. Edit the regular expression accordingly, you can test this with a line of data from the log + going to regex101.com
  124.  
  125. Hopefully this helps! I get it's a bit roundabout, but it's consistent, clean data and you don't have to install some app that'll beg for a billion permissions. nRF wasn't "designed" for this, but it's proper open source and has a good pedigree.
  126.  
  127. KEYWORDS - IGNORE THIS SECTION
  128. This is just in case someone is googling for information like this, ideally google will shunt it in front of them. I don't care myself, but if it can help others that's the goal. They'd have to find it first though, hence...
  129.  
  130. ALIEXPRESS
  131. AMAZON
  132. EBAY
  133. GEARBEST
  134. BANGGOOD
  135. BLUETOOTH OXIMETER
  136. OXIMETER
  137. HOW DO LOG DATA FROM MY CHINESE OXIMETER
  138. BLUETOOTH OXIMETER APP SUCKS, HOW CAN I READ THE DATA?
  139. HOW TO READ DATA FROM BLUETOOTH OXIMETER MANUALLY
  140.  
  141. Anyhow, thanks for reading. If you happened to get one of these neat devices, hopefully this helps you make it a fair bit more useful! Especially good if you want to log this data over time for whatever reason and your default app doesn't have this built in yet!
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement