Added initial support for HID DMM
This commit is contained in:
parent
426976cbde
commit
023b9a3100
2
98-hidraw.rules
Normal file
2
98-hidraw.rules
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#Allow all to read hidraws
|
||||||
|
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"
|
||||||
448
DMM_HID.cpp
Normal file
448
DMM_HID.cpp
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
#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();
|
||||||
|
}
|
||||||
75
DMM_HID.h
Normal file
75
DMM_HID.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#ifndef DMM_HID_H_
|
||||||
|
#define DMM_HID_H_
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
constexpr int DMM_TIMEOUT_MS = 100;
|
||||||
|
|
||||||
|
struct Reading
|
||||||
|
{
|
||||||
|
enum reading_type
|
||||||
|
{
|
||||||
|
reading_continuity,
|
||||||
|
reading_diode,
|
||||||
|
reading_percent,
|
||||||
|
reading_volts,
|
||||||
|
reading_amps,
|
||||||
|
reading_ohms,
|
||||||
|
reading_hFE,
|
||||||
|
reading_hertz,
|
||||||
|
reading_farads,
|
||||||
|
reading_celsius,
|
||||||
|
reading_fahrenheit,
|
||||||
|
reading_type_max
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t value[4];
|
||||||
|
int8_t modifier;
|
||||||
|
uint8_t dot_pos;
|
||||||
|
bool is_inf;
|
||||||
|
bool is_neg;
|
||||||
|
bool is_DC;
|
||||||
|
bool is_rel;
|
||||||
|
bool is_hold;
|
||||||
|
bool is_min;
|
||||||
|
bool is_max;
|
||||||
|
reading_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DMM_HID
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// File descriptor to DMM device
|
||||||
|
int m_fd;
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DMM_HID();
|
||||||
|
~DMM_HID();
|
||||||
|
|
||||||
|
// Opens the dev file and prepares to get readings
|
||||||
|
// Returns true on success, false on failure
|
||||||
|
void Open(const std::string& dev);
|
||||||
|
|
||||||
|
// Closes the dev file
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
bool is_open();
|
||||||
|
|
||||||
|
// Returns all readings since opening or since last call
|
||||||
|
std::vector<Reading> get_readings(int timeout_ms = DMM_TIMEOUT_MS);
|
||||||
|
|
||||||
|
// Returns all devpaths to DMM devices
|
||||||
|
static std::vector<std::string> get_dmm_devices();
|
||||||
|
|
||||||
|
// Locks the mutex
|
||||||
|
void lock();
|
||||||
|
|
||||||
|
// Unlocks the mutex
|
||||||
|
void unlock();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DMM_H_
|
||||||
24
Makefile
Normal file
24
Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
src_hid += main_hid.cpp
|
||||||
|
src_hid += DMM_HID.cpp
|
||||||
|
src_hid += common.cpp
|
||||||
|
|
||||||
|
#src_serial += main_serial.cpp
|
||||||
|
#src_serial += DMM_Serial.cpp
|
||||||
|
#src_serial += common.cpp
|
||||||
|
|
||||||
|
libs += -lSDL2
|
||||||
|
libs += -lSDL2_ttf
|
||||||
|
|
||||||
|
libs_hid += -ludev
|
||||||
|
|
||||||
|
all:
|
||||||
|
make hid
|
||||||
|
# make serial
|
||||||
|
|
||||||
|
hid:
|
||||||
|
g++ -o dmm_hid ${src_hid} ${libs} ${libs_hid} -DUSE_HID -Wall -Wextra -Wpedantic
|
||||||
|
|
||||||
|
#serial:
|
||||||
|
# g++ -o dmm_serial ${src_serial} ${libs} -DUSE_Serial -Wall -Wextra -Wpedantic
|
||||||
|
|
||||||
|
.PHONY: all hid serial
|
||||||
23
README.md
23
README.md
@ -1,3 +1,22 @@
|
|||||||
# dmm_display
|
# DMM Display
|
||||||
|
Reads measurements from PeakTech 2025 and uses SDL to display it on screen
|
||||||
|
The DMM has 2 types of interfaces:
|
||||||
|
* HID
|
||||||
|
* Serial
|
||||||
|
|
||||||
Reads measurements from PeakTech 2025 and uses SDL to display it on screen
|
# Requirements
|
||||||
|
`sudo apt install libsdl2-dev libsdl2-ttf-dev`
|
||||||
|
|
||||||
|
# Compiling
|
||||||
|
`make hid`
|
||||||
|
or
|
||||||
|
`make serial`
|
||||||
|
|
||||||
|
# Notes
|
||||||
|
When using HID you need to add hidraw rules in order to use without sudo
|
||||||
|
`sudo cp 98-hidraw.rules /etc/udev/rules.d/`
|
||||||
|
`sudo udevadm control --reload-rules`
|
||||||
|
`sudo udevadm trigger`
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
No serial support yet
|
||||||
|
|||||||
207
common.cpp
Normal file
207
common.cpp
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using std::cout;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
bool init_sdl(SDL_Window **window)
|
||||||
|
{
|
||||||
|
if (nullptr == window)
|
||||||
|
{
|
||||||
|
cout << "Nowhere to store window" << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SDL Initialization
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||||
|
{
|
||||||
|
cout << "SDL could not initialize! SDL - " << SDL_GetError() << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*window) = SDL_CreateWindow("DMM Display",
|
||||||
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
|
1280, 720,
|
||||||
|
SDL_WINDOW_SHOWN);
|
||||||
|
if (nullptr == (*window))
|
||||||
|
{
|
||||||
|
cout << "Window could not be created! SDL - " << SDL_GetError() << endl;
|
||||||
|
SDL_Quit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 != TTF_Init())
|
||||||
|
{
|
||||||
|
cout << "Could not init TTF! SDL - "<< SDL_GetError() << endl;
|
||||||
|
SDL_DestroyWindow(*window);
|
||||||
|
SDL_Quit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void quit_sdl(SDL_Window *window)
|
||||||
|
{
|
||||||
|
TTF_Quit();
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_quit()
|
||||||
|
{
|
||||||
|
SDL_Event e;
|
||||||
|
|
||||||
|
while(SDL_PollEvent(&e))
|
||||||
|
{
|
||||||
|
if (SDL_QUIT == e.type)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void calc_text(std::string& text, std::vector<Reading>& readings)
|
||||||
|
{
|
||||||
|
if (readings.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reading last = *(readings.rbegin());
|
||||||
|
text = "";
|
||||||
|
|
||||||
|
if (last.is_neg)
|
||||||
|
{
|
||||||
|
text += '-';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text += ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last.is_inf)
|
||||||
|
{
|
||||||
|
text += " O L ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
text += ('0' + last.value[i]);
|
||||||
|
text += ' ';
|
||||||
|
}
|
||||||
|
text.erase(text.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last.dot_pos != 0)
|
||||||
|
{
|
||||||
|
text[last.dot_pos * 2] = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (last.modifier)
|
||||||
|
{
|
||||||
|
case -9:
|
||||||
|
text += 'n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -6:
|
||||||
|
text += "μ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -3:
|
||||||
|
text += 'm';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
text += ' ';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
text += 'k';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
text += 'M';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
text += ' ';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (last.type)
|
||||||
|
{
|
||||||
|
case Reading::reading_percent:
|
||||||
|
text += "% ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Reading::reading_volts:
|
||||||
|
text += "V ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Reading::reading_amps:
|
||||||
|
text += "A ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Reading::reading_ohms:
|
||||||
|
text += "Ω ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Reading::reading_hFE:
|
||||||
|
text += "hFE";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Reading::reading_hertz:
|
||||||
|
text += "Hz ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Reading::reading_farads:
|
||||||
|
text += "F ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Reading::reading_celsius:
|
||||||
|
text += "°C ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Reading::reading_fahrenheit:
|
||||||
|
text += "°F ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
text += "???";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_text(const std::string& text, SDL_Surface *surface, TTF_Font *font)
|
||||||
|
{
|
||||||
|
SDL_Surface *txt_surface;
|
||||||
|
SDL_Rect rect;
|
||||||
|
SDL_Color fg;
|
||||||
|
SDL_Color bg;
|
||||||
|
|
||||||
|
fg.r = 0;
|
||||||
|
fg.g = 0;
|
||||||
|
fg.b = 0;
|
||||||
|
|
||||||
|
bg.r = 255;
|
||||||
|
bg.g = 255;
|
||||||
|
bg.b = 255;
|
||||||
|
|
||||||
|
if ((NULL == surface) || (NULL == font) || (0 == text.size()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
txt_surface = TTF_RenderUTF8_Shaded(font, text.c_str(), fg, bg);
|
||||||
|
|
||||||
|
rect.x = 0;
|
||||||
|
rect.y = surface->h / 2 - txt_surface->h / 2;
|
||||||
|
|
||||||
|
SDL_BlitSurface(txt_surface, NULL, surface, &rect);
|
||||||
|
SDL_FreeSurface(txt_surface);
|
||||||
|
}
|
||||||
22
common.h
Normal file
22
common.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef COMMON_H_
|
||||||
|
#define COMMON_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_ttf.h>
|
||||||
|
|
||||||
|
#ifdef USE_HID
|
||||||
|
# include "DMM_HID.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define UI_FPS 30
|
||||||
|
|
||||||
|
bool init_sdl(SDL_Window **window);
|
||||||
|
void quit_sdl(SDL_Window *window);
|
||||||
|
bool check_quit();
|
||||||
|
void calc_text(std::string& text, std::vector<Reading>& readings);
|
||||||
|
void draw_text(const std::string& text, SDL_Surface *surface, TTF_Font *font);
|
||||||
|
|
||||||
|
#endif // COMMON_H_
|
||||||
113
main_hid.cpp
Normal file
113
main_hid.cpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "DMM_HID.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
using std::cout;
|
||||||
|
using std::endl;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
bool open_dmm(DMM_HID& dmm)
|
||||||
|
{
|
||||||
|
vector<string> devs = DMM_HID::get_dmm_devices();
|
||||||
|
vector<Reading> readings;
|
||||||
|
|
||||||
|
if (0 == devs.size())
|
||||||
|
{
|
||||||
|
cout << "DMM not found" << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devs.size() > 1)
|
||||||
|
{
|
||||||
|
cout << "Multiple DMMs found" << endl;
|
||||||
|
cout << "Using: " << devs[0] << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
dmm.Open(devs[0]);
|
||||||
|
|
||||||
|
// Wait a sec
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
// DMM is open. Now check its config
|
||||||
|
readings = dmm.get_readings(1);
|
||||||
|
|
||||||
|
if (readings.empty())
|
||||||
|
{
|
||||||
|
cout << "DMM communication is not ON - Press and hold USB/REL key" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
bool quit = false;
|
||||||
|
|
||||||
|
DMM_HID dmm;
|
||||||
|
vector<Reading> readings;
|
||||||
|
|
||||||
|
Uint32 next_draw = 0;
|
||||||
|
Uint32 ms;
|
||||||
|
|
||||||
|
SDL_Window *window;
|
||||||
|
SDL_Surface *surface;
|
||||||
|
TTF_Font *font;
|
||||||
|
|
||||||
|
string text;
|
||||||
|
|
||||||
|
// Keep compiler happy
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
ok = open_dmm(dmm);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = init_sdl(&window);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
font = TTF_OpenFont("./font.ttf", 100);
|
||||||
|
|
||||||
|
while (!quit)
|
||||||
|
{
|
||||||
|
ms = SDL_GetTicks();
|
||||||
|
if (next_draw - ms > 1000 / UI_FPS)
|
||||||
|
{
|
||||||
|
surface = SDL_GetWindowSurface(window);
|
||||||
|
|
||||||
|
// Fill the surface white
|
||||||
|
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 255, 255));
|
||||||
|
|
||||||
|
// Get Readings
|
||||||
|
readings = dmm.get_readings();
|
||||||
|
|
||||||
|
// Calculate Text
|
||||||
|
calc_text(text, readings);
|
||||||
|
|
||||||
|
// TODO: Draw Text
|
||||||
|
draw_text(text, surface, font);
|
||||||
|
|
||||||
|
next_draw = ms + 1000 / UI_FPS;
|
||||||
|
|
||||||
|
// Update the window
|
||||||
|
SDL_UpdateWindowSurface(window);
|
||||||
|
|
||||||
|
quit = check_quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TTF_CloseFont(font);
|
||||||
|
quit_sdl(window);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user