449 lines
7.1 KiB
C++
449 lines
7.1 KiB
C++
#include "DMM_HID.h"
|
|
|
|
#include <linux/hidraw.h>
|
|
#include <sys/ioctl.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <libudev.h>
|
|
|
|
#include <stdint.h>
|
|
#include <poll.h>
|
|
#include <limits>
|
|
|
|
#define VENDOR_ID 0x2571
|
|
#define PRODUCT_ID 0x4100
|
|
|
|
using std::vector;
|
|
using std::string;
|
|
|
|
constexpr int DMM_BUFSIZE = 8;
|
|
|
|
// Checks if a hidraw device matches the vendor id and product id
|
|
// If *_id is < 0 - it is not checked
|
|
bool check_hidraw_device(string dev, int vendor_id, int product_id)
|
|
{
|
|
int fd;
|
|
int res;
|
|
struct hidraw_devinfo info;
|
|
bool result = false;
|
|
bool vendor = true;
|
|
bool product = true;
|
|
|
|
fd = open(dev.c_str(), O_RDONLY | O_NONBLOCK);
|
|
if (fd < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
// Get Raw Info
|
|
res = ioctl(fd, HIDIOCGRAWINFO, &info);
|
|
if (res >= 0)
|
|
{
|
|
if ((vendor_id > 0) && (vendor_id != info.vendor))
|
|
{
|
|
vendor = false;
|
|
}
|
|
|
|
if ((product_id > 0) && (product_id != info.product))
|
|
{
|
|
product = false;
|
|
}
|
|
|
|
if (vendor && product)
|
|
{
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Reads exact number of bytes from a fd for X milisec
|
|
bool read_exact(int fd, uint8_t *buf, int size, int timeout_ms)
|
|
{
|
|
struct pollfd poll_fd;
|
|
int bytes;
|
|
int bytes_read;
|
|
|
|
if ((fd < 0) || (nullptr == buf) || (size <= 0) || (timeout_ms <= 0))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
poll_fd.fd = fd;
|
|
poll_fd.events = POLLIN;
|
|
|
|
bytes_read = 0;
|
|
while (bytes_read < size)
|
|
{
|
|
bytes = poll(&poll_fd, 1, timeout_ms);
|
|
if (bytes < 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bytes = read(fd, buf + bytes_read, size - bytes_read);
|
|
if (bytes < 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bytes_read += bytes;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
vector<string> DMM_HID::get_dmm_devices()
|
|
{
|
|
vector<string> result;
|
|
struct udev *udev_handle = nullptr;
|
|
struct udev_enumerate *udev_enum_handle = nullptr;
|
|
struct udev_list_entry *udev_entry = nullptr;
|
|
struct udev_device *device = nullptr;
|
|
const char *syspath = nullptr;
|
|
string path;
|
|
int err;
|
|
|
|
// Create udev
|
|
udev_handle = udev_new();
|
|
if (nullptr == udev_handle)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// Create enumerator
|
|
udev_enum_handle = udev_enumerate_new(udev_handle);
|
|
if (nullptr == udev_enum_handle)
|
|
{
|
|
goto udev_out;
|
|
}
|
|
|
|
// Search only for "hidraw" devices
|
|
err = udev_enumerate_add_match_subsystem(udev_enum_handle, "hidraw");
|
|
if (err < 0)
|
|
{
|
|
goto udev_out;
|
|
}
|
|
|
|
// Scan devices
|
|
err = udev_enumerate_scan_devices(udev_enum_handle);
|
|
if (err < 0)
|
|
{
|
|
goto udev_out;
|
|
}
|
|
|
|
udev_entry = udev_enumerate_get_list_entry(udev_enum_handle);
|
|
if (nullptr == udev_entry)
|
|
{
|
|
goto udev_out;
|
|
}
|
|
|
|
// Iterate through devices
|
|
while (nullptr != udev_entry)
|
|
{
|
|
// Name is syspath
|
|
syspath = udev_list_entry_get_name(udev_entry);
|
|
if (nullptr != syspath)
|
|
{
|
|
device = udev_device_new_from_syspath(udev_handle, syspath);
|
|
if (nullptr != device)
|
|
{
|
|
// devnode is needed /dev/hidraw* path
|
|
path = udev_device_get_devnode(device);
|
|
|
|
// Check vendor id and product id pair
|
|
// There's no exit from the loop
|
|
// so it will return the last found device
|
|
if (check_hidraw_device(path, VENDOR_ID, PRODUCT_ID))
|
|
{
|
|
result.push_back(path);
|
|
}
|
|
|
|
udev_device_unref(device);
|
|
}
|
|
}
|
|
udev_entry = udev_list_entry_get_next(udev_entry);
|
|
}
|
|
|
|
udev_out:
|
|
// Cleanup
|
|
if (nullptr != udev_enum_handle)
|
|
{
|
|
udev_enumerate_unref(udev_enum_handle);
|
|
}
|
|
udev_unref(udev_handle);
|
|
|
|
return result;
|
|
}
|
|
|
|
DMM_HID::DMM_HID()
|
|
:m_fd(-1)
|
|
{
|
|
|
|
}
|
|
|
|
DMM_HID::~DMM_HID()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
void DMM_HID::Open(const std::string& dev)
|
|
{
|
|
int fd;
|
|
|
|
if (is_open())
|
|
{
|
|
Close();
|
|
}
|
|
|
|
fd = open(dev.c_str(), O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_fd = fd;
|
|
}
|
|
|
|
void DMM_HID::Close()
|
|
{
|
|
if (is_open())
|
|
{
|
|
close(m_fd);
|
|
}
|
|
|
|
m_fd = -1;
|
|
}
|
|
|
|
bool DMM_HID::is_open()
|
|
{
|
|
if (m_fd < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
vector<Reading> DMM_HID::get_readings(int timeout_ms)
|
|
{
|
|
vector<Reading> result;
|
|
uint8_t buf[DMM_BUFSIZE];
|
|
Reading reading;
|
|
|
|
if (m_fd < 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
while(read_exact(m_fd, buf, DMM_BUFSIZE, timeout_ms))
|
|
{
|
|
reading.value[0] = 0;
|
|
reading.value[1] = 0;
|
|
reading.value[2] = 0;
|
|
reading.value[3] = 0;
|
|
reading.modifier = 0;
|
|
reading.dot_pos = 0;
|
|
reading.is_inf = false;
|
|
reading.is_neg = false;
|
|
reading.is_DC = false;
|
|
reading.is_rel = false;
|
|
reading.is_hold = false;
|
|
reading.is_min = false;
|
|
reading.is_max = false;
|
|
reading.type = Reading::reading_type_max;
|
|
|
|
// Check for infinity
|
|
if ((0x4F == buf[1]) && (0x4C == buf[2]))
|
|
{
|
|
reading.is_inf = true;
|
|
}
|
|
|
|
// BCD 4-bit values
|
|
reading.value[0] = (buf[1] >> 4);
|
|
reading.value[1] = (buf[1] & 0x0F);
|
|
reading.value[2] = (buf[2] >> 4);
|
|
reading.value[3] = (buf[2] & 0x0F);
|
|
|
|
// Byte 0 Bit 6 - Negative
|
|
if (buf[0] & 0x40)
|
|
{
|
|
reading.is_neg = true;
|
|
}
|
|
|
|
// Byte 0 second half - Dot position
|
|
{
|
|
switch (buf[0] & 0x0F)
|
|
{
|
|
case 0:
|
|
// Do nothing
|
|
break;
|
|
|
|
case 1:
|
|
reading.dot_pos = 1;
|
|
break;
|
|
|
|
case 2:
|
|
reading.dot_pos = 2;
|
|
break;
|
|
|
|
case 3:
|
|
reading.dot_pos = 3;
|
|
break;
|
|
|
|
case 4:
|
|
reading.dot_pos = 3;
|
|
break;
|
|
|
|
case 7:
|
|
// Assuming 3&4 overlap
|
|
reading.dot_pos = 3;
|
|
break;
|
|
|
|
default:
|
|
// Do nothing
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Unit Modifier
|
|
{
|
|
// Byte 4 Bit 1
|
|
if (buf[4] & 0x02)
|
|
{
|
|
// nano = 10^-9
|
|
reading.modifier = -9;
|
|
}
|
|
// Byte 5 Bit 7
|
|
else if (buf[5] & 0x80)
|
|
{
|
|
// micro = 10^-6
|
|
reading.modifier = -6;
|
|
}
|
|
// Byte 5 Bit 6
|
|
else if (buf[5] & 0x40)
|
|
{
|
|
// mili = 10^-3
|
|
reading.modifier = -3;
|
|
}
|
|
// Byte 5 Bit 5
|
|
else if (buf[5] & 0x20)
|
|
{
|
|
// kilo = 10^3
|
|
reading.modifier = 3;
|
|
}
|
|
// Byte 5 Bit 4
|
|
else if (buf[5] & 0x10)
|
|
{
|
|
// mega = 10^6
|
|
reading.modifier = 6;
|
|
}
|
|
}
|
|
|
|
// AC/DC
|
|
// Byte 3 Bit 4
|
|
if (buf[3] & 0x10)
|
|
{
|
|
reading.is_DC = true;
|
|
}
|
|
|
|
// Hold / min / max / rel
|
|
{
|
|
// Byte 3 Bit 1
|
|
if (buf[3] & 0x02)
|
|
{
|
|
reading.is_hold = true;
|
|
}
|
|
|
|
// Byte 3 Bit 2
|
|
if (buf[3] & 0x04)
|
|
{
|
|
reading.is_rel = true;
|
|
}
|
|
|
|
// Byte 4 Bit 5
|
|
if (buf[4] & 0x20)
|
|
{
|
|
reading.is_max = true;
|
|
}
|
|
|
|
// Byte 4 Bit 4
|
|
if (buf[4] & 0x10)
|
|
{
|
|
reading.is_min = true;
|
|
}
|
|
}
|
|
|
|
// Reading type
|
|
{
|
|
// if (buf[5] & 0x08)
|
|
// {
|
|
// reading.type = Reading::reading_continuity;
|
|
// }
|
|
// else if (buf[5] & 0x04)
|
|
// {
|
|
// reading.type = Reading::reading_diode;
|
|
// }
|
|
// else
|
|
if (buf[5] & 0x02)
|
|
{
|
|
reading.type = Reading::reading_percent;
|
|
}
|
|
else if (buf[6] & 0x80)
|
|
{
|
|
reading.type = Reading::reading_volts;
|
|
}
|
|
else if (buf[6] & 0x40)
|
|
{
|
|
reading.type = Reading::reading_amps;
|
|
}
|
|
else if (buf[6] & 0x20)
|
|
{
|
|
reading.type = Reading::reading_ohms;
|
|
}
|
|
else if (buf[6] & 0x10)
|
|
{
|
|
reading.type = Reading::reading_hFE;
|
|
}
|
|
else if (buf[6] & 0x08)
|
|
{
|
|
reading.type = Reading::reading_hertz;
|
|
}
|
|
else if (buf[6] & 0x04)
|
|
{
|
|
reading.type = Reading::reading_farads;
|
|
}
|
|
else if (buf[6] & 0x02)
|
|
{
|
|
reading.type = Reading::reading_celsius;
|
|
}
|
|
else if (buf[6] & 0x01)
|
|
{
|
|
reading.type = Reading::reading_fahrenheit;
|
|
}
|
|
}
|
|
|
|
result.push_back(reading);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void DMM_HID::lock()
|
|
{
|
|
m_mutex.lock();
|
|
}
|
|
|
|
void DMM_HID::unlock()
|
|
{
|
|
m_mutex.unlock();
|
|
}
|