265 lines
8.3 KiB
C
265 lines
8.3 KiB
C
|
/* 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;
|
||
|
}
|