Introduction
Beurer BM55
This device is designed to measure Systolic and Diastolic pressure.
Additionally it has features like pulse rate measurement and arrhythmia
indicator. It also maintains the time at which the measurements are
taken. This device has a capability to measure and maintain 60 readings
of 2 users namely 'A' and 'B'.
Please refer below link for more details:
Beurer BF480
This device is designed to measure body weight, body fat, muscles and
water content percentages. It also maintains the time at which the
measurements are taken. This device has a capability to measure and
maintain 60 readings of 10 users.The collected readings for both the devices can be transferred to PC/mobile phones over the USB. This blog explains the protocol used to communicate with USB devices and provides the java code which collects the readings.
Procedure for USB communication
- Find the device using Product id and Vendor id
- Get USB connection to the device
- Open the USB connection
- Initialize the device so that it is ready to transfert the data to the requested system (PC/mobile)
- Get number of reading that are available in this device so that you can iterate that many times (if needed)
- Read the stored measurements and add them to a collection
- Terminate the device communication (if needed)
- Close the USB connection
This code has been integrated into MediPi which is an initiative started by NHS digital. MediPi is an open source software which is being piloted at Hertfordshire trust in England. I have contributed to this open source project by designing & developing Clinical front end monitoring system and made this open source software more robust.
Please refer below links for detailed information about MediPi:
Information: www.medipi.org
Source code: https://github.com/rprobinson/MediPi/tree/master/MediPiPatient
Beurer BM55
Below code snippet can be used to transfer the measurements from Beurer BM55 to a java program over USB.
public Collection<?> getMeasurements(String user) throws DeviceNotFoundException, DeviceConnectionException, SecurityException, UsbException, InterruptedException { //User A or B for which readings needs to be transferred BM55User readingsUser = BM55User.valueOf(user); List<BM55Measurement> measurements = new ArrayList<BM55Measurement>(); //Find Beurer BM55 USB device with VENDOR_ID = (short) 0x0c45 and PRODUCT_ID = (short) 0x7406 UsbDevice device = getUSBDevice(VENDOR_ID, PRODUCT_ID); //Get USB connection with interface number 0 and endpoint number -127 UsbPipe connectionPipe = getUSBConnection(device, 0, -127); byte requestType = 33; byte request = 0x09; short value = 521; short index = 0; //Get usbControl with the above specified parameter. This object is needed to send data to the USB device UsbControlIrp usbControl = getUSBControl(device, requestType, request, value, index); try { //Open the USB communication connectionPipe.open(); //prepare the device to communicate over the USB by writing {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} byte array to device. //Read the data available on read port after writing data to the USB device. initialiseDevice(device, usbControl, connectionPipe); //Get the number of readings available in the device by writing {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} byte array to device. //Read the data available on read port after writing data to the USB device. This read data represents number of readings available with the device irrespective of user A & B (It will return total readings of A+B). int numberOfReadings = getNumberOfReadings(device, usbControl, connectionPipe); BM55Measurement measurement; byte[] data; //Iterate for the numberOfReadings returned for(int readingsCounter = 1; readingsCounter < numberOfReadings; readingsCounter++) { //To get each reading write {0xA3, (byte) readingsCounter, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} byte to device. writeDataToInterface(device, usbControl, new byte[] {(byte) 0xA3, (byte) readingsCounter}, BM55USBService.DEFAULT_BYTE_ARRAY_LENGTH_8, BM55USBService.PADDING_BYTE_0xF4); //Read the data available on read port after writing above data to the USB device. This read data represents the byte array of {readingCounter} measurement stored in the device. data = readData(connectionPipe, 8); //Pass the received byte array to BM55Measurement constructor which will decode the byte array to different attributes. measurement = new BM55Measurement(data); //Add this measurement to the list only if the user passed to this method matches with decoded BM55Measurement user attribute. if(measurement.getUser().equals(readingsUser)) { measurements.add(new BM55Measurement(data)); } } //Once all the measurements are captured, terminate the device communication by writing {0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} byte array to device. terminateDeviceCommunication(device, usbControl, connectionPipe); } finally { //Close the USB device communication if(connectionPipe != null && connectionPipe.isOpen()) { try { connectionPipe.close(); connectionPipe.getUsbEndpoint().getUsbInterface().release(); } catch(UsbException e) { //Do nothing } } } return measurements; }
BM55Measurement constructor:
public BM55Measurement(final byte[] reading) {
//Systolic pressure is byte[0] + 25
this.systolicPressure = BytesManipulator.getUnsignedInteger((byte) (reading[0] + 25));
//Diastolic pressure is byte[0] + 25
this.diastolicPressure = BytesManipulator.getUnsignedInteger((byte) (reading[1] + 25));
//Pulse rate is byte[2]
this.pulseRate = BytesManipulator.getUnsignedInteger(reading[2]);
int day = 0;
int month = 0;
int year = 0;
int hour = 0;
int minute = 0;
//if byte[3] < 0 then the resting indicator is on and the measured month would be byte[3] + 128
if(reading[3] < 0) {
this.restingIndicator = true;
month = reading[3] + 128;
} else {
month = reading[3];
}
//if byte[4] < 0 then B is the user for which the measurement was taken and the measured day would be byte[4] + 128
//else the measurement taken was for user A and measured day would be byte[4]
if(reading[4] < 0) {
this.user = BM55User.B;
day = reading[4] + 128;
} else {
this.user = BM55User.A;
day = reading[4];
}
//Hour and minutes are in simple representation of byte[5] and byte[6] respectively
hour = reading[5];
minute = reading[6];
//if byte[7] < 0 then the arrhythmia indicator is on and the measured year would be byte[7] + 2000 + 128
//else the arrhythmia indicator is off and the measured year would be byte[7] + 2000
if(reading[7] < 0) {
this.arrhythmia = true;
year = reading[7] + 128 + 2000;
} else {
year = reading[7] + 2000;
}
//Convert the yyyy-mm-dd HH:mm to ZonedDateTime or you can convert this to simple java.util.Date or java.sql.Timestamp
try {
ZonedDateTime zdt = LocalDateTime.of(year, month, day, hour, minute).atZone(ZoneId.of("Z"));
this.measuredTime = zdt.toInstant();
} catch(final Exception e) {
e.printStackTrace();
}
}
Beurer BF480
Below code snippet can be used to transfer the measurements from Beurer BF480 to a java program over USB.
public Collection<?> getMeasurements(String user) throws DeviceNotFoundException, DeviceConnectionException, SecurityException, UsbException {
//User number (1 to 10) for which readings needs to be transferred
int userNumber = Integer.valueOf(user);
//This variable will be used to identify starting point in a transposed array for the given user
int readingStartByteNumber = (userNumber - 1) * 6;
//Find Beurer BM55 USB device with VENDOR_ID = (short) 0x04d9 and PRODUCT_ID = (short) 0x8010
List<BF480Measurement> measurements = new ArrayList<BF480Measurement>();
UsbDevice device = getUSBDevice(VENDOR_ID, PRODUCT_ID);
//Get USB connection with interface number 0 and endpoint number -127
UsbPipe connectionPipe = getUSBConnection(device, 0, -127);
byte requestType = 33;
byte request = 0x09;
short value = 521;
short index = 0;
//Get usbControl with the above specified parameter. This object is needed to send data to the USB device
UsbControlIrp usbControl = getUSBControl(device, requestType, request, value, index);
try {
//Open the USB communication
connectionPipe.open();
//prepare the device to communicate over the USB by writing {0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} byte array to device.
//Read the data available on read port after writing data to the USB device.
initialiseDevice(device, usbControl, connectionPipe);
//No need to get number of readings as maximum number of readings for this device are 64
//Read the data from USB device by iterating 64 times and collect the readings in 128 byte array
//Read 128 x 64 data from serial interface
byte[][] rawReadings = new byte[BF480USBService.MAX_NUMBER_OF_READINGS][BF480USBService.BYTE_ARRAY_LENGTH_128];
for(int readingsCounter = 0; readingsCounter < BF480USBService.MAX_NUMBER_OF_READINGS; readingsCounter++) {
rawReadings[readingsCounter] = readData(connectionPipe, 128);
}
//Each value is represented with 2 adjacent bytes. Convert 2 adjacent bytes to one integer. e.g. [W0, W0, W1, W1,...W59,W59] will be converted to [W0,W1,... W59]
//Convert 128 x 64 data to 64 x 64
int[][] readings = new int[BF480USBService.MAX_NUMBER_OF_READINGS][BF480USBService.BYTE_ARRAY_LENGTH_128 / 2];
for(int readingsCounter = 0; readingsCounter < BF480USBService.MAX_NUMBER_OF_READINGS; readingsCounter++) {
readings[readingsCounter] = BytesManipulator.convertBytesToIntegers(rawReadings[readingsCounter]);
}
//Transpose the data matrix to retrieve each patients information
int[][] userReadings = BytesManipulator.transpose(readings);
//convert all 64 readings to an object by iterating over rows of the matrix
for(int readingsCounter = 0; readingsCounter < BF480USBService.MAX_NUMBER_OF_READINGS; readingsCounter++) {
if(userReadings[readingsCounter][readingStartByteNumber + 4] == 0) {
//break when it is indicated that there are no more readings available
break;
}
//Pass the transposed byte array along with starting position for the give user to BF480Measurement constructor which will decode the byte array to different parameters of selected measurement.
measurements.add(new BF480Measurement(userReadings[readingsCounter], readingStartByteNumber));
}
//No need to terminate the device communication for BF480
//Sort the readings according to measured time
Collections.sort(measurements);
} finally {
//Close the USB device communication
if(connectionPipe != null && connectionPipe.isOpen()) {
try {
connectionPipe.close();
connectionPipe.getUsbEndpoint().getUsbInterface().release();
} catch(UsbException e) {
//Do nothing
}
}
}
return measurements;
}
Data received over USB from beurer BF480 with byte array matrix representation
BF480Measurement constructor:
public BF480Measurement(final int[] reading, final int readingStartByteNumber) { //Representation of transposed row passed to this constructor //startingbyte=0 |startingbyte=6 | |startingbyte=54 //[ User 1 | User 2| |User 10] //[W0, B0, w0, M0, D0, T0, W0, B0, w0, M0, D0, T0, . . …………………….., W0, B0, w0, M0, D0, T0] //Measurement timestamp conversion :START final int date = reading[readingStartByteNumber + 4]; final int time = reading[readingStartByteNumber + 5]; //Calculate the year by right shifting date by 9 + 1920 final int year = 1920 + (date >> 9); //Calculate the month by right shifting date by 5 & 0xf final int month = date >> 5 & 0xf; //Calculate the day by date & 0x1f final int day = date & 0x1f; //Calculate the hour by right shifting time by 8 final int hour = time >> 8; //Calculate the minute by time & 0xff final int minute = time & 0xff; //Convert the yyyy-mm-dd HH:mm to ZonedDateTime or you can convert this to simple java.util.Date or java.sql.Timestamp try { ZonedDateTime zdt = LocalDateTime.of(year, month, day, hour, minute).atZone(ZoneId.of("Z")); this.measuredTime = zdt.toInstant(); } catch(final Exception e) { e.printStackTrace(); } //Measurement timestamp conversion :END //Assign double values to weight, body fat %, water % and muscle % this.weight = Double.valueOf(reading[readingStartByteNumber + 0]) / 10; this.bodyFat = Double.valueOf(reading[readingStartByteNumber + 1]) / 10; this.water = Double.valueOf(reading[readingStartByteNumber + 2]) / 10; this.muscles = Double.valueOf(reading[readingStartByteNumber + 3]) / 10; }


