#include "DMM_HID.h" #include #include #include #include #include #include #include #include #include #include #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 DMM_HID::get_dmm_devices() { vector 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 DMM_HID::get_readings(int timeout_ms) { vector 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(); }