• PC-60NW BT Oximeter connected to the Linux PC

    From Knoppix User@21:1/5 to All on Sun Dec 10 15:42:06 2017
    XPost: alt.os.linux

    Hi,

    I needed to connect the PC-60NW oximeter from Creative Medical
    to an embedded Linux system. Unfortunately the manufacturer
    does not document the Bluetooth protocol, and delivers software
    only for Windows.
    However in the original CD there was a documentation for
    an USB-UART bridge, and that was a pointer that their oximeters
    may use serial port protocol.

    Indeed, after I pared th oximeter with my PC, using the sdptool
    I have found, that it offers the SPP service.
    I could binf rfcomm to it, but I was not able to receive any
    response.

    I have installed the original software on a Windows machine
    and started wireshark. Unfortunately, wireshark on Windows is not
    able to sniff BT communication.
    I switched off the built-in BT adapter, and connected an USB BT
    dongle. Then I have started recording of the USB traffic in
    the Wireshark.

    Indeed, in the recorded packets, I couls easily find the BT
    packets with SPP protocol, and I could identify the transmitted
    and received frames (S - transmitted, R - received)

    The following transmitted frames were found:
    S1: "\xaa\x55\x0f\x02\x83\x77"
    S1b: "\xaa\x55\xf0\x02\x83\xa5"
    S2: "\xaa\x55\xf0\x02\x81\x19"
    S3: "\xaa\x55\x0f\x03\x84\x01\xe0"
    S4: "\xaa\x55\x0f\x03\x85\x01\x24"
    S5: "\xaa\x55\x0f\x03\x80\x02\x39"

    There was a lot of different received frames, so I list only
    a few examples:

    R3: '\xaaU\x0f\x0b\x03 \x10PC-60NW\x9e' - in Python notation
    it is the name and the version? of the device

    byte: 1 2 3 4 5 6 7 8 9 10 11 12
    R1a: "\xaa\x55\x0f\x08\x01\x60\x3f\x00\x2d\x00\x38\x8c"
    R1b: "\xaa\x55\x0f\x08\x01\x5f\x47\x00\x81\x00\x18\x78"
    It looks, like the above frames transmit the parameters
    calculated by the device. The 6th byte transmits the saturation.
    The 7th byte transmits the heart rate. At the moment
    I don't know what is the meaning of the 9th byte.
    The 12th byte seems to transmit the battery voltage multiplied
    by 10 (0x18 - 24 - 2.4V), but the bit 0x20 seems to encode
    the state of the pletysmographic curve transmission
    (0x18 - battery 2.4V no transmission, 0x38 - battery 2.4V
    transmission is on).

    The frames below transmit the pletysmographic curve:

    byte: 1 2 3 4 5 6 7 8 9 10 11
    R2a: "\xaa\x55\x0f\x07\x02\x28\x26\x24\x23\x22\x96"
    R2b: "\xaa\x55\x0f\x07\x02\x23\x27\x2e\x38\x44\x74"
    R2c: "\xaa\x55\x0f\x07\x02\x4f\x57\x5b\x5c\x5b\x8b"
    R2d: "\xaa\x55\x0f\x07\x02\x58\x54\x50\x4b\x48\x12"

    Each frame:
    1. begins with bytes 0xaa 0x55
    2. The 3rd byte is either 0x0f or 0xf0. It seems, that the PC
    sends alternately frames with 0x0f and 0xf0. I don't know
    what is the real meaning of that byte.
    3. The 4th byte defines the length of the frame (number of bytes
    after that byte including CRC)
    4. The 5th byte defines the type of the frame.
    In the received frames:
    0x01 - parameters and status
    0x02 - pletysmographic curve
    0x03 - name and version of device
    The transmitted frames use both 5th and 6th byte
    0x02 0x83 - sending this frame asks the device to transmit
    status and parameters?
    0x02 0x81 - meaning unclear
    0x03 0x80 - followed by 0x02 unclear (is sent every 2 seconds
    by the original Windows software)
    0x03 0x84 - when followed by 0x01, switches on transmission
    of parameters?
    0x03 0x85 - when followed by 0x01, switches on transmission
    of pletismographic curve?

    The last byte is the CRC. The list of popular 8-bit CRCs may
    be found at https://users.ece.cmu.edu/~koopman/crc/crc8.html

    I have created a simple Python script to test them all with one
    frame:

    import crcmod
    for p in [0x1cf, 0x14d, 0x11d, 0x163, 0x17f, 0x107, \
    0x12f, 0x131, 0x19b, 0x137, 0x1d5, 0x11b, 0x139, \
    0x1d7, 0x101]:
    for iv in [ -1L, 0L ]:
    for rev in [ True, False]:
    a=crcmod.mkCrcFun(p,iv,rev)
    if a("\xaa\x55\xf0\x02\x81")==0x19:
    print "full:"+hex(p)+" iv="+str(iv)+" rev="+str(rev)
    if a("\xf0\x02\x81")==0x19:
    print "no header:"+hex(p)+" iv="+str(iv)+" rev="+str(rev)

    And I have got the following result:
    full:0x131 iv=0 rev=True
    So the CRC used is the DOWCRC

    Finally I have prepared the simple script, that allows to connect
    to PC-60NW and dump the received data.
    Before using the script, you should bind the adapter (you will need
    to change the MAC):

    rfcomm bind 0 88:1B:96:11:4A:73 6

    import serial
    import time
    p=serial.Serial('/dev/rfcomm0')
    p.timeout=1
    cm1="\xaa\x55\x0f\x02\x83\x77" # Transmit name
    cm1b="\xaa\x55\xf0\x02\x83\xa5" # Transmit name
    cm2="\xaa\x55\xf0\x02\x81\x19" # ???
    cm3="\xaa\x55\x0f\x03\x84\x01\xe0" # Switch on params? cm4="\xaa\x55\x0f\x03\x85\x01\x24" # Switch on wave? cm6="\xaa\x55\x0f\x03\x80\x02\x39" # ? is send every 2 secs by PC soft
    # Try to connect to the oximeter
    while True:
    p.write(cm1+cm2)
    s=p.read(100)
    if len(s)>0:
    break
    # It has responded,so switch on the wave transmission
    p.write(cm3)
    p.write(cm4)
    p.write(cm2)
    p.timeout=0.1
    tlast = time.time()
    frame=""
    state=0 # Wait for \xaa
    while True:
    #Wait for the beginning of the frame
    tnew=time.time()
    if tnew > tlast+1:
    p.write(cm1+cm6)
    tlast=tnew
    s=p.read()
    if len(s) > 0:
    #Process the character
    if state==0: # wait for \xaa
    if s=='\xaa':
    frame += s
    state=1 #Wait for \x55
    else:
    frame=""
    state=0
    elif state==1: #Wait for \x55
    if s=='\x55':
    frame += s
    state=2 #Wait for 0xf0 or 0x0f
    else:
    frame=""
    state=0
    elif state==2: #Wait for \f0 or 0f
    if (s=='\x0f') or (s=='\xf0'):
    frame += s
    state=3 #Wait for length
    else:
    frame=""
    state=0
    elif state==3: #Wait for length
    frame += s
    length=ord(s)
    state=4 #Wait for data
    elif state==4: #Wait for length
    frame += s
    length-=1
    if length==0:
    #Complete frame received
    #We should check the CRC, but now we only dump the data
    l=""
    for i in frame:
    l+="%2.2x:" % ord(i)
    print l
    frame=""
    state=0 #Wait for data

    Of course I do not understand all the details of the protocol.
    If you manage to find more details, please publish a correction.
    Good luck!
    W

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)