2 * visomat-data-downloader - download data from Visomat Double Comfort
4 * The Visomat Double Comfort is a blood pressure meter with a USB port.
6 * Copyright (C) 2013 Antonio Ospite <ospite@studenti.unina.it>
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #define VISOMAT_DEVICE_VID 0x1247
29 #define VISOMAT_DEVICE_PID 0x00f8
30 #define VISOMAT_CONFIGURATION 1
31 #define VISOMAT_INTERFACE 1
32 #define VISOMAT_EP_IN 0x82
33 #define VISOMAT_EP_OUT 0x03
38 #define BASE_YEAR 2000
41 VISOMAT_CMD_DUMP_EEPROM,
42 VISOMAT_CMD_UNKNOWN1, /* maybe the firmware revision */
43 VISOMAT_CMD_GET_DATETIME,
44 VISOMAT_CMD_START_MEASURE1,
45 VISOMAT_CMD_UNKNOWN2, /* XXX transmission hangs */
49 static char command_codes[][3] = {
50 [VISOMAT_CMD_DUMP_EEPROM] = "R00",
51 [VISOMAT_CMD_UNKNOWN1] = "R01",
52 [VISOMAT_CMD_GET_DATETIME] = "R02",
53 [VISOMAT_CMD_START_MEASURE1] = "R03",
54 [VISOMAT_CMD_UNKNOWN2] = "R04",
55 [VISOMAT_CMD_UNKNOWN3] = "R05",
67 unsigned int flag; /* XXX Maybe this means arrhythmia? */
68 unsigned int systolic;
69 unsigned int diastolic;
73 static inline int extract_datetime(unsigned char *buffer, struct datetime *d)
77 ret = sscanf((char *)buffer, "%02u%02u%02u%02u%02u",
92 * NOTE: the CSV output is meant to be compatible with one from the Windows
93 * program, which uses the %d/%m/%Y date format, so that's not my fault.
95 static void print_record_csv_compat(struct datetime *d, struct pressure *p)
97 unsigned int pulse_pressure;
99 printf("%02u/%02u/%04u;%02u.%02u;",
100 d->day, d->month, d->year, d->hour, d->minute);
102 printf("%u;%u;%u;", p->systolic, p->diastolic, p->pulses);
104 pulse_pressure = p->systolic - p->diastolic;
105 printf("%u;", pulse_pressure);
111 /* The original software does not seem to be doing that */
113 printf("tachycardia");
114 else if (p->pulses < 60)
115 printf("bradycardia");
121 /* TODO separate better decoding data from printing it */
122 static int decode_eeprom(unsigned char *buffer,
124 unsigned int user_mask)
130 unsigned int user_id;
131 unsigned int num_records;
135 if (buffer[0] != STX || buffer[1] != 'M' || buffer[len - 1] != ETX)
138 /* skip the initial STX */
141 /* and the final ETX */
145 /* Each user record begins with 'M', maybe for "Memory" */
146 if (buffer[i] == 'M') {
147 /* i tracks the bytes consumed */
150 ret = sscanf((char *)(buffer + i), "%1u%02u",
151 &user_id, &num_records);
155 /* user_id and num_records take 3 bytes */
158 for (j = 0; j < num_records; j++) {
159 ret = extract_datetime(buffer + i, &d);
163 /* datetime takes 10 bytes */
166 ret = sscanf((char *)(buffer + i),
175 /* pressure data is 10 bytes */
178 /* TODO: split out the printing part */
179 if (user_id & user_mask) {
181 printf("# User: %d\n", user_id);
182 print_record_csv_compat(&d, &p);
191 static int decode_datetime(unsigned char *buffer, unsigned int len)
194 unsigned char code[4] = { 0 };
196 unsigned char *pbuffer = buffer;
205 ret = extract_datetime(pbuffer + 4, &d);
209 printf("# (%s) Date: %04d/%02d/%02d %02d:%02d\n",
210 code, d.year, d.month, d.day, d.hour, d.minute);
215 static int send_command(libusb_device_handle *dev, visomat_command cmd)
219 unsigned char request[5];
222 request[1] = command_codes[cmd][0];
223 request[2] = command_codes[cmd][1];
224 request[3] = command_codes[cmd][2];
228 ret = libusb_bulk_transfer(dev, VISOMAT_EP_OUT, request, sizeof(request), &transferred, 0);
229 if (ret != 0 || transferred != sizeof(request)) {
230 fprintf(stderr, "Error: sending request: %d (%s)\ttransferred: %d (expected %zu)\n",
231 ret, libusb_error_name(ret), transferred, sizeof(request));
237 static int get_response(libusb_device_handle *dev,
238 unsigned char *buffer,
243 unsigned char response[64] = { 0 };
251 ret = libusb_bulk_transfer(dev, VISOMAT_EP_IN, response, sizeof(response), &transferred, 5000);
253 fprintf(stderr, "Error getting response: %d (%s)\ttransferred: %d (expected %zu)\n",
254 ret, libusb_error_name(ret), transferred, sizeof(response));
258 for (j = 0; j < (unsigned int)transferred; j++) {
259 buffer[i] = response[j];
263 } while (buffer[i - 1] != ETX && i < len);
265 /* Check the buffer is a valid response packet */
266 if (buffer[0] != STX || buffer[i - 1] != ETX)
272 /* Candidates for a future public API, if a shared library will ever be made */
273 #define visomat_device libusb_device_handle
274 static int visomat_dump_eeprom(visomat_device *dev, unsigned int user_mask)
276 /* Assuming an EEPROM of 1 KiB */
277 unsigned char buffer[1024] = { 0 };
280 ret = send_command(dev, VISOMAT_CMD_DUMP_EEPROM);
284 ret = get_response(dev, buffer, sizeof(buffer));
288 ret = decode_eeprom(buffer, ret, user_mask);
295 static int visomat_get_datetime(visomat_device *dev)
297 unsigned char buffer[255] = { 0 };
300 ret = send_command(dev, VISOMAT_CMD_GET_DATETIME);
304 ret = get_response(dev, buffer, sizeof(buffer));
308 ret = decode_datetime(buffer, ret);
318 libusb_device_handle *dev;
320 ret = libusb_init(NULL);
322 fprintf(stderr, "error (%d): cannot initialize libusb.\n", ret);
326 libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_INFO);
328 dev = libusb_open_device_with_vid_pid(NULL,
332 fprintf(stderr, "error (%d): cannot open device.\n", errno);
334 goto out_libusb_exit;
337 ret = libusb_set_configuration(dev, VISOMAT_CONFIGURATION);
339 fprintf(stderr, "error (%d): cannot set configuration %d.\n",
340 ret, VISOMAT_CONFIGURATION);
341 goto out_libusb_close;
344 libusb_set_auto_detach_kernel_driver(dev, 1);
346 ret = libusb_claim_interface(dev, VISOMAT_INTERFACE);
348 fprintf(stderr, "error (%d): cannot claim interface %d.\n",
349 ret, VISOMAT_INTERFACE);
350 goto out_libusb_close;
353 ret = visomat_get_datetime(dev);
355 goto out_libusb_release_interface;
357 ret = visomat_dump_eeprom(dev, 0x01 | 0x02);
359 goto out_libusb_release_interface;
361 out_libusb_release_interface:
362 libusb_release_interface(dev, VISOMAT_INTERFACE);