Added connection management
parent
fe00a30268
commit
4aa558eda7
|
@ -1,8 +1,20 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Sent by host when connection is initiated.
|
// Sent by host when connection is initiated.
|
||||||
|
//
|
||||||
|
// Packet structure:
|
||||||
|
// Data: | cmd |
|
||||||
|
// # of Bytes: | 1 |
|
||||||
#define CMD_HELLO 0x00
|
#define CMD_HELLO 0x00
|
||||||
|
|
||||||
|
// Sent periodically by host to test connection.
|
||||||
|
// Keyboard should ignore this command.
|
||||||
|
//
|
||||||
|
// Packet structure:
|
||||||
|
// Data: | cmd |
|
||||||
|
// # of Bytes: | 1 |
|
||||||
|
#define CMD_RUTHERE 0x01
|
||||||
|
|
||||||
// Send keyboard state to host.
|
// Send keyboard state to host.
|
||||||
//
|
//
|
||||||
// Packet structure:
|
// Packet structure:
|
||||||
|
@ -13,7 +25,7 @@
|
||||||
// 0x00: RGBMatrix disabled
|
// 0x00: RGBMatrix disabled
|
||||||
// 0x01: normal animation, no HID data.
|
// 0x01: normal animation, no HID data.
|
||||||
// 0x02: FFT Animation
|
// 0x02: FFT Animation
|
||||||
#define CMD_SEND_STATE 0x01
|
#define CMD_SEND_STATE 0x02
|
||||||
|
|
||||||
|
|
||||||
// Animation data. Sent by host.
|
// Animation data. Sent by host.
|
||||||
|
@ -27,7 +39,7 @@
|
||||||
//
|
//
|
||||||
// data:
|
// data:
|
||||||
// Animation data. Content depends on data type.
|
// Animation data. Content depends on data type.
|
||||||
#define CMD_ANIM_DATA 0x02
|
#define CMD_ANIM_DATA 0x03
|
||||||
|
|
||||||
// Data for FFT animation.
|
// Data for FFT animation.
|
||||||
// Data segment consists of 10 bits, each representing the height of a column.
|
// Data segment consists of 10 bits, each representing the height of a column.
|
||||||
|
|
|
@ -11,10 +11,10 @@ Ergodox::Ergodox(
|
||||||
product_id(product_id),
|
product_id(product_id),
|
||||||
usage(usage),
|
usage(usage),
|
||||||
usage_page(usage_page),
|
usage_page(usage_page),
|
||||||
handle(NULL)
|
handle(NULL),
|
||||||
|
connected(false)
|
||||||
{
|
{
|
||||||
hid_init();
|
hid_init();
|
||||||
open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,21 +54,23 @@ Ergodox& Ergodox::init(
|
||||||
// Instance is a static function variable,
|
// Instance is a static function variable,
|
||||||
// and will be deleted at end of program.
|
// and will be deleted at end of program.
|
||||||
Ergodox::~Ergodox() {
|
Ergodox::~Ergodox() {
|
||||||
close();
|
disconnect();
|
||||||
hid_exit();
|
hid_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Try to open this keyboard's hid device.
|
Try to open this keyboard's hid device.
|
||||||
Throws a runtime error if device is already open,
|
Throws a runtime error if device is already open.
|
||||||
or if we can't find a device with the given params.
|
|
||||||
|
|
||||||
If we successfully open a device, this method sends a
|
If we successfully open a device, this method sends a
|
||||||
CMD_HELLO packet.
|
CMD_HELLO packet.
|
||||||
|
|
||||||
|
@returns True if connection was successful, false otherwise.
|
||||||
*/
|
*/
|
||||||
void Ergodox::open() {
|
bool Ergodox::try_connect() {
|
||||||
if (handle != NULL) {
|
if (connected) {
|
||||||
throw std::runtime_error("Device already open");
|
throw std::runtime_error("Device already open");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,24 +91,47 @@ void Ergodox::open() {
|
||||||
cur_dev = cur_dev->next;
|
cur_dev = cur_dev->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return false if connection failed
|
||||||
if (path_to_open) {
|
if (path_to_open) {
|
||||||
|
connected = true;
|
||||||
|
|
||||||
handle = hid_open_path(path_to_open);
|
handle = hid_open_path(path_to_open);
|
||||||
|
hid_set_nonblocking(handle, 1);
|
||||||
hid_free_enumeration(devs);
|
hid_free_enumeration(devs);
|
||||||
|
write(CMD_HELLO, NULL, 0);
|
||||||
|
|
||||||
|
#define MAX_STR 255
|
||||||
|
wchar_t wstr[MAX_STR];
|
||||||
|
|
||||||
|
|
||||||
|
wprintf(L"\nConnected to device!\n");
|
||||||
|
|
||||||
|
// Read metadata
|
||||||
|
wstr[0] = 0x0000;
|
||||||
|
hid_get_manufacturer_string(handle, wstr, MAX_STR);
|
||||||
|
wprintf(L"Manufacturer String: %ls\n", wstr);
|
||||||
|
|
||||||
|
wstr[0] = 0x0000;
|
||||||
|
hid_get_product_string(handle, wstr, MAX_STR);
|
||||||
|
wprintf(L"Product String: %ls\n", wstr);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
connected = false;
|
||||||
|
|
||||||
hid_free_enumeration(devs);
|
hid_free_enumeration(devs);
|
||||||
throw std::runtime_error("Could not open hid device");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hid_set_nonblocking(handle, 1);
|
return connected;
|
||||||
|
|
||||||
write(CMD_HELLO, NULL, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Close hid device if it is open.
|
// Close hid device if it is open.
|
||||||
void Ergodox::close() {
|
void Ergodox::disconnect() {
|
||||||
|
connected = false;
|
||||||
if (handle != NULL) {
|
if (handle != NULL) {
|
||||||
hid_close(handle);
|
hid_close(handle);
|
||||||
|
handle = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,14 +141,15 @@ Send data to Ergodox.
|
||||||
|
|
||||||
@param cmd Command byte
|
@param cmd Command byte
|
||||||
@param data Data to send
|
@param data Data to send
|
||||||
@returns data_len Data length. Must be shorter than packet_size.
|
@param data_len Data length. Must be shorter than packet_size.
|
||||||
|
@returns True if successful, false otherwise.
|
||||||
*/
|
*/
|
||||||
int Ergodox::write(uint8_t cmd, const uint8_t* data, uint8_t data_len) const {
|
bool Ergodox::write(uint8_t cmd, const uint8_t* data, uint8_t data_len) {
|
||||||
if (handle == NULL) {
|
if (!connected) {
|
||||||
throw std::runtime_error("Cannot write, device is not open!");
|
throw std::runtime_error("Not connected, cannot write!");
|
||||||
}
|
} else if (handle == NULL) {
|
||||||
|
throw std::runtime_error("Tried to write a null handle, something is very wrong.");
|
||||||
if (data_len > packet_size) {
|
} else if (data_len > packet_size) {
|
||||||
throw std::runtime_error("Data length exceeds packet size");
|
throw std::runtime_error("Data length exceeds packet size");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +164,15 @@ int Ergodox::write(uint8_t cmd, const uint8_t* data, uint8_t data_len) const {
|
||||||
// Copy data into rest of packet
|
// Copy data into rest of packet
|
||||||
std::copy(data, data + data_len, packet + 2);
|
std::copy(data, data + data_len, packet + 2);
|
||||||
|
|
||||||
return hid_write(handle, packet, packet_size + 1);
|
ssize_t res;
|
||||||
|
res = hid_write(handle, packet, packet_size + 1);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
wprintf(L"Lost device, disconnecting.\n");
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,16 +182,21 @@ Read data from Ergodox into read_buf. If a CMD_SEND_STATE packet is received, pr
|
||||||
@returns True if a new command was received, false otherwise.
|
@returns True if a new command was received, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool Ergodox::read() {
|
bool Ergodox::read() {
|
||||||
if (handle == NULL) {
|
if (!connected) {
|
||||||
throw std::runtime_error("Cannot write, device is not open!");
|
throw std::runtime_error("Not connected, cannot read!");
|
||||||
|
} else if (handle == NULL) {
|
||||||
|
throw std::runtime_error("Tried to read a null handle, something is very wrong.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t res;
|
ssize_t res;
|
||||||
res = hid_read(handle, read_buf, packet_size);
|
res = hid_read(handle, read_buf, packet_size);
|
||||||
|
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
return false;
|
return false;
|
||||||
} else if (res < 0) {
|
} else if (res < 0) {
|
||||||
throw std::runtime_error("Read failed.");
|
wprintf(L"Lost device, disconnecting.\n");
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(read_buf[0]) {
|
switch(read_buf[0]) {
|
||||||
|
@ -173,5 +212,11 @@ bool Ergodox::read() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res > 0;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Simple connectivity check
|
||||||
|
void Ergodox::test_connection() {
|
||||||
|
write(CMD_RUTHERE, NULL, 0);
|
||||||
}
|
}
|
|
@ -32,15 +32,22 @@ class Ergodox {
|
||||||
|
|
||||||
~Ergodox();
|
~Ergodox();
|
||||||
|
|
||||||
int write(uint8_t cmd, const uint8_t* data, uint8_t data_len) const;
|
bool try_connect();
|
||||||
|
void disconnect();
|
||||||
|
void test_connection();
|
||||||
|
|
||||||
bool read();
|
bool read();
|
||||||
|
bool write(uint8_t cmd, const uint8_t* data, uint8_t data_len);
|
||||||
|
|
||||||
|
|
||||||
// Read buffer, len = packet_size.
|
// Read buffer, len = packet_size.
|
||||||
// Filled by read().
|
// Filled by read().
|
||||||
uint8_t read_buf[RAW_EPSIZE];
|
uint8_t read_buf[RAW_EPSIZE];
|
||||||
|
|
||||||
|
|
||||||
|
// Getter methods
|
||||||
uint8_t get_animation_mode() const { return animation_mode; }
|
uint8_t get_animation_mode() const { return animation_mode; }
|
||||||
|
bool is_connected() const { return connected; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ergodox(
|
Ergodox(
|
||||||
|
@ -55,14 +62,16 @@ class Ergodox {
|
||||||
//Ergodox(Ergodox& other);
|
//Ergodox(Ergodox& other);
|
||||||
//Ergodox& operator=(Ergodox& other);
|
//Ergodox& operator=(Ergodox& other);
|
||||||
|
|
||||||
void open();
|
|
||||||
void close();
|
|
||||||
|
|
||||||
// HID device.
|
// HID device.
|
||||||
// NULL if not opened.
|
// NULL if not opened.
|
||||||
hid_device* handle;
|
hid_device* handle;
|
||||||
|
|
||||||
// Keyboard state variables.
|
// Keyboard state variables.
|
||||||
// Updated by read().
|
// Updated by read().
|
||||||
|
|
||||||
|
// Which animation is the keyboard running right now?
|
||||||
|
// See CMD_SEND_STATE in commands.h for docs.
|
||||||
uint8_t animation_mode;
|
uint8_t animation_mode;
|
||||||
|
// Are we connected to a keyboard right now?
|
||||||
|
bool connected;
|
||||||
};
|
};
|
67
src/main.cpp
67
src/main.cpp
|
@ -5,6 +5,9 @@
|
||||||
// For reading FIFO
|
// For reading FIFO
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
// For sleep
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
// Local files
|
// Local files
|
||||||
#include "utility/bitmap.hpp"
|
#include "utility/bitmap.hpp"
|
||||||
|
@ -82,37 +85,57 @@ int main(int argc, char *argv[]) {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Write buffer
|
// Write buffer
|
||||||
uint8_t hid_buf[12];
|
uint8_t hid_buf[Dox.packet_size];
|
||||||
|
|
||||||
|
wprintf(L"Trying to connect...\n");
|
||||||
|
while (!Dox.is_connected()) {
|
||||||
|
Dox.try_connect();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
|
if (Dox.is_connected()) {
|
||||||
|
if (Dox.get_animation_mode() == 0x02) {
|
||||||
|
buf.update();
|
||||||
|
fft.update(buf);
|
||||||
|
|
||||||
if (Dox.get_animation_mode() == 0x02) {
|
hid_buf[0] = CMD_ANIM_DATA_fft;
|
||||||
buf.update();
|
for (size_t i = 0; i < 10; i++) {
|
||||||
fft.update(buf);
|
// Get height from fft, apply bottom_skip
|
||||||
|
ssize_t h = fft.get_output()[i] - bottom_skip;
|
||||||
|
|
||||||
hid_buf[0] = CMD_ANIM_DATA_fft;
|
// Enforce max and min
|
||||||
for (size_t i = 0; i < 10; i++) {
|
// max implicitly enforces top_skip
|
||||||
// Get height from fft, apply bottom_skip
|
h = h>kb_resolution ? kb_resolution : h;
|
||||||
ssize_t h = fft.get_output()[i] - bottom_skip;
|
h = h<0 ? 0 : h;
|
||||||
|
|
||||||
// Enforce max and min
|
hid_buf[i + 1] = h;
|
||||||
// max implicitly enforces top_skip
|
}
|
||||||
h = h>kb_resolution ? kb_resolution : h;
|
|
||||||
h = h<0 ? 0 : h;
|
|
||||||
|
|
||||||
hid_buf[i + 1] = h;
|
|
||||||
}
|
}
|
||||||
Dox.write(CMD_ANIM_DATA, hid_buf, 13);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a packet if there is a packet to read
|
Dox.write(CMD_ANIM_DATA, hid_buf, Dox.packet_size);
|
||||||
if (Dox.read()) {
|
|
||||||
uint8_t cmd = Dox.read_buf[0];
|
|
||||||
|
|
||||||
switch(cmd) {
|
// Dox.write might detect that we've been disconnected,
|
||||||
case CMD_SEND_STATE:
|
// and Dox.read will fail if we are.
|
||||||
break;
|
// This check prevents it from doing that, and instead jumps to reconnect.
|
||||||
|
if (!Dox.is_connected()) { continue; }
|
||||||
|
|
||||||
|
// Read a packet if there is a packet to read
|
||||||
|
if (Dox.read()) {
|
||||||
|
uint8_t cmd = Dox.read_buf[0];
|
||||||
|
|
||||||
|
switch(cmd) {
|
||||||
|
case CMD_SEND_STATE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wprintf(L"Trying to connect...\n");
|
||||||
|
while (!Dox.is_connected()) {
|
||||||
|
Dox.try_connect();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue