web-u2/u2_usb.c

265 lines
8.3 KiB
C
Raw Normal View History

2019-09-30 13:16:49 +02:00
/* Copyright 2019 King Kévin <kingkevin@cuvoodoo.info>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* This program connects to the WEB/WITRN/Qway U2(p) USB meter using the USB HID interface and reads the measurement values.
*
* The device's USB VID:PID is 0716:5030.
* Don't forget to grant USB permission to connect to the device.
* This program does not support connecting to multiple devices, and will only connect to the first it finds.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <hidapi/hidapi.h>
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
static void die(const char* format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(1);
}
/* original trigger message from vendor software
"\xff\x55\x58\x8a\x13\x79\x06\x57\x1a\x01\x0a\x02\x00\x00\x00\x00" \
"\x5e\x00\x00\x00\xff\x55\x2f\xb2\x8b\xdc\x5a\xd4\x1a\x2c\xa4\x00" \
"\xa4\x40\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x74\x21\x4f\x40\x75" \
"\x23\x19\x40\x75\xcc\x01\x01\x00\x02\x04\x00\x00\x5e\x00\xe7\x06";
*/
// simplified trigger message (without checksum) to get a couple of measurements
static char trigger[] = \
"\xff\x55\x00\x00\x00\x00\x00\x00\x1a\x01\x0a\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
static hid_device *handle = NULL;
struct u2_measurement_t {
uint8_t payload[64];
uint8_t seconds;
uint8_t increment1;
uint8_t increment2;
float increment3;
uint8_t increment4;
float vbus_voltage1; // in V
float vbus_current1; // in A
float vbus_current2; // in A
float vbus_current3; // in A
float vbus_power; // in W
float dp_voltage; // in V
float dm_voltage; // in V
float temperature_internal; // in °C
float temperature_external; // in °C
float vbus_voltage2; // in V
float vbus_current4; // in A
bool checksum1;
bool checksum2;
};
static void trigger_mesurement(void)
{
if (NULL == handle) {
die("open a HID device first\n");
}
// update checksum
uint8_t checksum = 0;
for (uint8_t i = 8; i < 62; i++) {
checksum += trigger[i];
}
trigger[62] = checksum;
checksum = 0;
for (uint8_t i = 0; i < 62; i++) {
checksum += trigger[i];
}
trigger[63] = checksum;
// add report ID prefix to message
unsigned char report[65] = {0};
memcpy(&report[1], trigger, 64);
int rc = hid_write(handle, report, ARRAY_SIZE(report));
if (rc < 0) {
die("could not write\n");
}
}
int main(int argc, char* argv[])
{
int rc; // return code
char str[200]; // general strings
wchar_t wstr[100]; // wide strings (used by hidapi)
bool debug = false;
if (2 == argc && 0 == strcmp("-d", argv[1])) {
debug = true;
}
rc = hid_init();
if (rc < 0) {
die("could not initialize HID\n");
}
handle = hid_open(0x0716, 0x5030, NULL); // open Qway/WITRN.U2
if (handle) {
if (debug) {
printf("WEB-U2 opened\n");
}
} else {
die("could not open WEB-U2 (make sure it is connected and you have access rights)\n");
}
rc = hid_get_manufacturer_string(handle, wstr, ARRAY_SIZE(wstr));
if (rc < 0) {
die("could not get manufacturer string\n");
}
if (debug) {
wcstombs(str, wstr, ARRAY_SIZE(str));
printf("manufacturer: %s\n", str);
}
rc = hid_get_product_string(handle, wstr, ARRAY_SIZE(wstr));
if (rc < 0) {
die("could not get product string\n");
}
if (debug) {
wcstombs(str, wstr, ARRAY_SIZE(str));
printf("product: %s\n", str);
}
// Read the Serial Number String
rc = hid_get_serial_number_string(handle, wstr, ARRAY_SIZE(wstr));
if (rc < 0) {
die("could not get serial string\n");
}
if (debug) {
wcstombs(str, wstr, ARRAY_SIZE(str));
printf("serial: %s\n", str);
}
if (!debug) { // print CSV header
printf("timer (in s),super fast timer,fast timer,timer,slow timer,VBUS voltage 1 (in V),VBUS current 1 (in A),VBUS current 2 (in A),VBUS current 3 (in A),VBUS power (in W),D+ voltage (in V),D- voltage (in V),internal temperature (in °C),external temperature 8in °C),VBUS voltage 2 (in V),VBUS current 4 (in A)\n");
}
bool run = true;
struct u2_measurement_t meas;
while (run) {
rc = hid_read_timeout(handle, meas.payload, ARRAY_SIZE(meas.payload), 50); // wait for max 50 ms for a measurement
if (rc < 0) {
die("could not read\n");
} else if (0 == rc) { // timeout
trigger_mesurement();
if (debug) {
printf("trigger measurement\n");
}
}
// parse measurement values
meas.seconds = meas.payload[2];
meas.increment1 = meas.payload[3];
meas.increment2 = meas.payload[4];
meas.increment3 = meas.payload[5] + meas.payload[6] / 100.0;
meas.increment4 = meas.payload[7];
meas.vbus_voltage1 = *(float*)(&meas.payload[10]); // in V
meas.vbus_current1 = *(float*)(&meas.payload[14]); // in A
meas.vbus_current2 = *(float*)(&meas.payload[18]); // in A
meas.vbus_current3 = *(float*)(&meas.payload[22]); // in A
meas.vbus_power = *(float*)(&meas.payload[26]); // in W
meas.dp_voltage = *(float*)(&meas.payload[30]); // in V
meas.dm_voltage = *(float*)(&meas.payload[34]); // in V
meas.temperature_internal = *(float*)(&meas.payload[38]); // in °C
meas.temperature_external = *(float*)(&meas.payload[42]); // in °C
meas.vbus_voltage2 = *(float*)(&meas.payload[46]); // in V
meas.vbus_current4 = *(float*)(&meas.payload[50]); // in A
// calculate checksums
// the 64th byte is the sum of the first 62 bytes
uint8_t checksum = 0;
for (uint8_t i = 0; i < 62; i++) {
checksum += meas.payload[i];
}
meas.checksum2 = (checksum == meas.payload[63]);
// the 63th byte is the sum of bytes 8 to 62 (without the timer information)
checksum = 0;
for (uint8_t i = 8; i < 62; i++) {
checksum += meas.payload[i];
}
meas.checksum1 = (checksum == meas.payload[62]);
if (debug) { // print payload and values aligned
for (uint8_t i = 0; i < ARRAY_SIZE(meas.payload); i++) {
printf("%02x ", meas.payload[i]);
}
printf("\n");
if (meas.checksum1 && meas.checksum2) {
printf("const ");
printf("%03us ", meas.seconds);
//printf("++"); // meas.increment1 is simply incrementing
printf("++"); // meas.increment2 is simply incrementing
printf("%03u.%02u ", (uint8_t)meas.increment3, (uint8_t)(meas.increment3 * 100) % 100);
printf("++"); // meas.increment4 is simply incrementing
printf(" const ");
printf("U:%.04fV ", meas.vbus_voltage1);
printf("I:%.04fA ", meas.vbus_current1);
printf("I:%.04fA ", meas.vbus_current2);
printf("I:%.04fA ", meas.vbus_current3);
printf("P:%.04fW ", meas.vbus_power);
printf("D+:%.04fV ", meas.dp_voltage);
printf("D-:%.04fV ", meas.dm_voltage);
printf("Int:%.02fC ", meas.temperature_internal);
printf("Ext:%.02fC ", meas.temperature_external);
printf("U:%.04fV ", meas.vbus_voltage2);
printf("I:%.04fA ", meas.vbus_current4);
printf("\n");
} else {
printf("invalid checksum\n");
}
} else { // print CSV line
if (meas.checksum1 && meas.checksum2) {
printf("%u,", meas.seconds);
printf("%u,", meas.increment1);
printf("%u,", meas.increment2);
printf("%.02f,", meas.increment3);
printf("%u,", meas.increment4);
printf("%.04f,", meas.vbus_voltage1);
printf("%.04f,", meas.vbus_current1);
printf("%.04f,", meas.vbus_current2);
printf("%.04f,", meas.vbus_current3);
printf("%.04f,", meas.vbus_power);
printf("%.04f,", meas.dp_voltage);
printf("%.04f,", meas.dm_voltage);
printf("%.02f,", meas.temperature_internal);
printf("%.02f,", meas.temperature_external);
printf("%.04f,", meas.vbus_voltage2);
printf("%.04f", meas.vbus_current4);
printf("\n");
}
}
}
hid_close(handle);
handle = NULL;
rc = hid_exit();
if (rc < 0) {
die("could not exit HID\n");
}
return 0;
}