Beurer BG64
This scale can measure your weight and percentage of different body components. The control panel is recording 12 variables:
- weight kg
- body fat %
- water %
- muscles %
- bones kg
- date
- upper body fat % (not for BG64)
- lower body fat % (not for BG64)
- upper body muscles % (not for BG64)
- lower body muscles % (not for BG64)
- BMR
- AMR
All values are 16 bit words.
Up to 10 users can store thier data for 32 measurements.
Beacause date has no time only one measurement per day is possible.
The following python program produces a CSV file with all variables available on BG64 for the first user (can be specified as a command parameter):
#!/usr/bin/env python """ ID 04d9:8010 Holtek Semiconductor, Inc. """ import usb.core import usb.util as util from array import array import sys from optparse import OptionParser, make_option import pickle from struct import unpack dev = None VID = 0x04d9 PID = 0x8010 # define HID constants REQ_HID_GET_REPORT = 0x01 REQ_HID_GET_IDLE = 0x02 REQ_HID_GET_PROTOCOL = 0x03 REQ_HID_SET_REPORT = 0x09 REQ_HID_SET_IDLE = 0x0A REQ_HID_SET_PROTOCOL = 0x0B HID_REPORT_TYPE_INPUT = 1<<8 HID_REPORT_TYPE_OUTPUT = 2<<8 HID_REPORT_TYPE_FEATURE = 3<<8 def openDev(): global dev dev = usb.core.find(idVendor=VID, idProduct=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) def setReport(reportId, data): r = dev.ctrl_transfer( util.build_request_type(util.CTRL_OUT, util.CTRL_TYPE_CLASS, util.CTRL_RECIPIENT_INTERFACE), # bmRequestType, REQ_HID_SET_REPORT, HID_REPORT_TYPE_OUTPUT | reportId, 0, # feature_interface_num data, # data 3000 # timeout (1000 was too small for C_FREE) ) return r def read(): openDev() try: dev.read(0x81,64) #flush except usb.core.USBError: pass setReport(9, array('B',(0x10,0,0,0,0,0,0,0))) # Every user can have upto 12 variables # and additional 8*64 for user data r = [] for i in xrange(120+8): x = dev.read(0x81,64) if not x: print >> sys.stderr, "Read failed" exit(1) if x[0]==0xff and (i<120): print >> sys.stderr,"Invalid data",x exit(1) r.append(x) # Each variable can have upto 32 values frmt = "!" + "H"*32 s = [] for i in xrange(120) : x = unpack(frmt, r[i]) # Variables 5 .. 10 are not available on my scale if i>5 and i<10: err = [item for item in x if item!=0] else: err = [item for item in x if item==0xffff] if len(err): print >> sys.stderr, "INVALID:", err exit(1) s.append(x) return s def printUser(s, user): # Transpose from 12x32 to 32x12 and format j = 12*user for i in xrange(32): x = s[j+5][i] # date if not x: break print "{:d}-{:02d}-{:02d}T07:00:00 {:.1f} {:.1f} {:.1f} {:.1f} {:.1f} {:d} {:d}".format( 1920+(x>>9), x>>5&0xf, x&0x1f, s[j+0][i]/10., s[j+1][i]/10., s[j+2][i]/10., s[j+3][i]/10., s[j+4][i]/10., s[j+10][i], s[j+11][i]) def main(): option_list = [ make_option("-c", "--cached", action="store_true", dest="cached"), #make_option("-w", "--write",action="store", dest="output_file"), make_option('-u', '--user', action='store', dest='user'), ] parser = OptionParser(option_list=option_list, usage='Usage: %prog [options]') options, args = parser.parse_args() if options.cached: with open("dump.pickle") as f: s = pickle.load(f) else: s = read() with open("dump.pickle","w") as f: pickle.dump(s,f) user = 0 if options.user: user=int(options.user)-1 printUser(s, user) if __name__ == '__main__': main()
To call it enter at command prompt:
./beurer.py > user1.csv
Pingback: Creating a Python module for the Beurer BM65 blood pressure monitor | The ramblings of atbrask
This was very helpful.
I just upgraded EasyFit on Windows and it stopped working. Couldn’t find the old version.
Thanks for your script!
By adapting the line
HID_REPORT_TYPE_OUTPUT | reportId, to:
0x0300,
I got my BF100 working, excellent thank you!
Thanks for your script. I try to adapt it to a Sanitas BGF 48 USB scale (having exactly the same Holtek ID 04d9:8010), but could not figure out the commands and data structure of the scale. Could you give me hint how you succeded to read them out? Thanks in advance.
Just borrowed my wife’s Windows machine and sniffed the original program. I think i used the http://benoit.papillault.free.fr/usbsnoop/ or one of its derivatives. BG64 was relatively easy, compared to Nike or Polar.
http://sourceforge.net/projects/usbsnoop/
to make it work for older python version i needed to add field names to get rid of the “ValueError: zero length field name in format” error:
line 103-5:
print “{0:d}-{1:02d}-{2:02d}T07:00:00 {3:.1f} {4:.1f} {5:.1f} {6:.1f} {7:.1f} {8:d} {9:d}”.format(1920+(x>>9), x>>5&0xf, x&0x1f,s[j+0][i]/10., s[j+1][i]/10., s[j+2][i]/10., s[j+3][i]/10., s[j+4][i]/10., s[j+10][i], s[j+11][i])
In order to be able to get multiple readings per day i’m running your script on a machine that’s on all the time, the beurer is connected using a switchable usb hub which you toggle on after weighing, wait for the usb end message and switch it off again. The script calls a slightly modified version of your python routine to add a timestamp and another routine to extract the most recent records only. Thanks for making this very easy, you’ve done all the hard work!
Hi Nicholas, can you give me any pointers please? I’m trying to run this script on a Mac and not having much luck! I have assumed that I need PyUsb and libusb as well as Python. Which version of python are you using for example?
could you please make your modiffied script public available 🙂 i would like to use it with my sanitas sbf 48 usb 🙂 thank you
I got the Sanitas SBF 48 working with some modifications to your script (thx for that!), it has 64 slots instead of 32 and it also saves date+time. But it does not calculate bones/AMR/BMR. Technically that’s all the differences to the Beurer BG 64.
However, one last question (i don’t want to do 64 measurements): What happens after 64 measurements? The manual says it should say “full”. Do I have to delete every single value? Can this be done via usb?
Sanitas software “Healthcare” does not seem to delete any entries in the scale itself, but I did not test what happens if all measurement slots for one person are used.
Any experience on your beurer BG 64? Hopefully you already did 32 measurements 😉
Simple reading seems to clear the “Full” flag.
Thanks for posting this script! I’m trying to access the Beurer BG 64 on my Mac and hoped this was the answer to my problem but i can’t get it to run properly in Terminal. Has anyone got any suggestions please? I have zero experience with Python i’m afraid, was just hoping to copy and paste to Terminal!
Got this error message:
Traceback (most recent call last):
File “beurer64.py”, line 6, in
import usb.core
ImportError: No module named usb.core
!?Sanitas SBF48 – Does anybody have already evaluated explicite commands ?
Does this scale answer to “0x10,0,0,0,0,0,0,0”-command as the Beurer BG64 ?
Don’t have a Windows system right now, to do the usbsnoop myself.
Thanks for this prepared script and anybody,who may advise me, to use with Sanitas scale.
Sanitas SBF 48: As mentioned from geekparadise: only Data-BLOB-Format differs:
so scripts needs some adaptions:
for i in xrange(6*10): x = dev.read(0x81,128) # weight kg , body fat % , water % , muscles % , date , time
…
for i in xrange(6*10) : x = unpack(frmt, r[i])
…
# Transpose from 10×64 to 64×10 and format
j = 6*user
for i in xrange(64):
x = s[j+4][i] # date
…
print “{:d}-{:02d}-{:02d}Zeit{:d}:{:d} {:.1f} {:.1f} {:.1f} {:.1f}”.format( 1920+(x>>9), x>>5&0xf, x&0x1f, s[j+5][i]>>8 &0xff,s[j+5][i] &0xff, s[j+0][i]/10., s[j+1][i]/10., s[j+2][i]/10., s[j+3][i]/10.)
I tried your suggested adaptations for Sanitas SBF48 but the script exits as invalid data is read. Can you explain, why your dev.read(0x81,128) has 128 as second parameter?
Needs the frmt = “!” + “H”*64 to be adopted to 64 instead of 32 as well?
Can you post your running file for SBF48 please?
You should try to understand a little bit, what is going on:
Sure: there is an obsolete (Can’t find a situation – this needs to be checked ) hard exit in the original script: “#if x[0]==0xff and (i<120): … ; exit(1)" – remove/comment out and go on or set (i<60)
"why your dev.read(0x81,128) has 128 as second parameter?"
Beurer: 8KB-BLOB (120+8) * 64 Bytes ~ 32 16bit-Integers, Sanitas: 8KB-BLOB 60(+4)*128 (!64 Slots)
ok: missed to mention frmt = "!" + "H"*64 -adaption in Line 78 – sorry
By the way: Discussion on how to read the BLOB was already here: http://www.mikrocontroller.net/topic/352939 "Sanitas USB-Protokoll"
One other point is, there is a V-usb-Signal from the Holtek USB-Serial-Modul ( Yes, it's somethink alike PL2302 and FTDI with internally having Wires to Serial (TxD/RxD,RTS/CTS) , which the Base-Scale-Microcontroller provides – but the USB is just HID-driver detected in this case, no real USB-Serial Driver – though there are 3 Jumpers on the Modul-PCB, which may change this ). Would have been nice, to control this V-usb directly (possibly to rewire to RTS or so) – while it's valid, the scale is in PC-Mode, no Weight-Operation possible.
So you are on a workaround like Nicolas mentioned.
@admin: Don't got the requested email on Comment !?
after all mentioned modifications … script only returns Invalid data array(‘B’, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0])
shared code ive used for sanitas device http://codeshare.io/Yhgqg
error on line 42 of your script … returns exception: File “/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py”, line 811, in claim_interface
_check(self.lib.libusb_claim_interface(dev_handle.handle, intf))
ctypes.ArgumentError: argument 2: : wrong type
could you give me any advice ? Thank you.
util.claim_interface(dev, 0) instead of util.claim_interface(dev, None) propably solves that error …