Omron MIT Elite Plus HEM-7301-ITKE7

This upper arm blood pressure monitor has memory for last 90 measurements. It records date, time, systolic/diastolic pressure and pulse.
It has somewhat more complicated USB protocol using request/response packets. It has also support for wakeup request.The following python program produces a CSV file with all variables available:

#!/usr/bin/env python

# ID 0590:0028
# MIT Elite Plus HEM-7301-ITKE7

from operator import xor
from datetime import datetime
import usb.core
import usb.util as util
from array import array
import sys

# omron device modes
OMRON_NULL_MODE = 0,0
OMRON_VID = 0x0590
OMRON_PID = 0x0028
OMRON_IN_ENDPT  = 0x81
OMRON_OUT_ENDPT = 0x02
debug = 0

def setMode(mode):
    r = dev.ctrl_transfer(
        util.build_request_type(util.CTRL_OUT, util.CTRL_TYPE_CLASS, util.CTRL_RECIPIENT_INTERFACE), # bmRequestType,
        9,                 # bRequest: REQ_HID_SET_REPORT
        0x300,             # wValue=0: (HID_REPORT_TYPE_FEATURE<<8)|feature_report_id,
        0,                 # wIndex=0: feature_interface_num
        mode,              # data_or_wLength=None:  feature_report
        1000               # timeout = None
    )

def _put(b, timeout=2000):
    r = dev.write(OMRON_OUT_ENDPT,b,0,timeout)
    return r

def _get(validate=0, timeout=2000):
    while 1:
        r = dev.read(OMRON_IN_ENDPT,8,0,timeout)
        if r[0] == 8:
            continue  #not ready, wait
        if r[0]<1 or r[0]>7:
            raise ValueError('Invalid byte count')

        if validate and ( r[1]!=ord('O') or r[2]!=ord('K')):
            raise ValueError('Bad response')
        return r[1:r[0]+1]

def _check(a):
    if reduce(xor, a[3:], 0) :
        raise ValueError('Bad checksum')
    return a

def wakeup():
    "Write zero packets until OK is received"
    try:
        r=_get(0,500)
    except: pass
    for i in range(5):
        _put((7,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0))
        try:
           r=_get(1,500)
           break
        except: pass

def shutdown():
    _put(array('B',"\005END00"))
    _get()

def getNoOfMeasurements():
    _put(array('B',"\005CNT00"))
    return _check(_get(1) + _get())[4]

def getMeasurement(i):
    _put(array('B', (7,ord('M'),ord('E'),ord('S'),0,0,i,i)))
    return _check(_get(1) + _get() + _get())

def delete(): # fails with "NO"
    _put(array('B',"\005MCL00"))
    r=_get(1)

def check_time():
    _put(array('B',"\005GCL00"))
    r= _check(_get(1)+_get()) # returns 10 bytes: O K \x00 YY MM DD HH NN SS CRC
    t = datetime(2000+r[3],r[4],r[5],r[6],r[7],r[8] )
    t = abs(t-t.now()).total_seconds()
    if t > 300: # enough to detect DST
        print >> sys.stderr, "WARNING! Time difference is", t, "sec."

dev = usb.core.find(idVendor=OMRON_VID, idProduct=OMRON_PID)
if dev is None:
    raise ValueError('Device not found')

if dev.is_kernel_driver_active(0):
    dev.detach_kernel_driver(0)
dev.set_configuration() # do reset and set active conf. Must be done before claim.
util.claim_interface(dev, None)

setMode(OMRON_NULL_MODE) # mode value seems to have no effect
wakeup()
check_time()
cnt = getNoOfMeasurements()
for i in range(cnt):
    r = getMeasurement(i)
    print "%d-%02d-%02dT%02d:%02d:%02d %d %d %d" % (2000+r[3], r[4],r[5],r[6],r[7],r[8],r[11],r[12],r[13])
    #sys.stdout.flush()

shutdown()

To call it enter at command prompt:

./omron.py > pressure.csv
7 comments
  1. George Pappas said:

    Great work.
    You only got the delete command wrong, instead of CTD00 try MCL00.
    You should also check the device clock with the command GCL00. It should return 10 bytes: O K \x00 YY MM DD HH NN SS CRC (where CRC is the XOR of all bytes after OK\x00)
    Alas in models after 705IT you can not set it (that command was SCL\x00).
    Still, if the clock is way off, you can prompt yourself to set it using the device buttons so your data doesn’t get out of whack.
    By the way, last byte of every command is the CRC, so you can check the results you get from MES\x00 and CNT00 commands too.

    • Thanks, that was helpful. I have updated the code.

  2. Bib said:

    Hmmm, this code is just broken…

  3. Bib said:

    Line 40 is wrong. Correcting it to r[0] == 7 brings up a byte count error – I just deleted the ‘if’ block
    Indentation between 59 & 62 is wrong

    once they are sorted, it runs as expected.

    • Thanks. Yeah it’s silly to maintain code here.

  4. xychix said:

    Is his likely to run against other USB enables Omron meters? How did you get to this? All other resources are more tgan welcome 🙂 As I intend to use a raspberry pi to create a live monitoring device.

    I want to monitor a person for ~8 hours and record:
    – external temp
    – internal temp
    – blood pressure + heartbeat every 5 minutes

    Nice to have
    – heartbeat every minute / continuously (For version 0.1 i’ll stick with ther heartbeat from the Omron)
    – oxygen saturation

    • I just run a sniffer on a Windows machine. I have no overview of Omron devices, but i think they are similar.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: