Upgraded hid interface
parent
dacb8d820d
commit
6f2c2e313e
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Sent by host when connection is initiated.
|
||||||
|
#define CMD_HELLO 0x00
|
||||||
|
|
||||||
|
// Send keyboard state to host.
|
||||||
|
//
|
||||||
|
// Packet structure:
|
||||||
|
// Data: | cmd | anim state |
|
||||||
|
// # of Bytes: | 1 | 1 |
|
||||||
|
//
|
||||||
|
// anim state:
|
||||||
|
// 0x00: RGBMatrix disabled
|
||||||
|
// 0x01: normal animation, no HID data.
|
||||||
|
// 0x02: FFT Animation
|
||||||
|
#define CMD_SEND_STATE 0x01
|
||||||
|
|
||||||
|
|
||||||
|
// Animation data. Sent by host.
|
||||||
|
//
|
||||||
|
// Packet structure:
|
||||||
|
// Data: | cmd | data type | data |
|
||||||
|
// # of Bytes: | 1 | 1 | ? |
|
||||||
|
//
|
||||||
|
// data type:
|
||||||
|
// Which animation this data is for. These are defined below.
|
||||||
|
//
|
||||||
|
// data:
|
||||||
|
// Animation data. Content depends on data type.
|
||||||
|
#define CMD_ANIM_DATA 0x02
|
||||||
|
|
||||||
|
// Data for FFT animation.
|
||||||
|
// Data segment consists of 10 bits, each representing the height of a column.
|
||||||
|
// Minimum height is 0, maximum is 250.
|
||||||
|
#define CMD_ANIM_DATA_fft 0x00
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// USB packet size, in bytes.
|
||||||
|
// Usually 32, but depends on keyboard.
|
||||||
|
#define RAW_EPSIZE 32
|
|
@ -1,7 +1,6 @@
|
||||||
#include "ergodox.hpp"
|
#include "ergodox.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ergodox::Ergodox(
|
Ergodox::Ergodox(
|
||||||
unsigned short vendor_id,
|
unsigned short vendor_id,
|
||||||
unsigned short product_id,
|
unsigned short product_id,
|
||||||
|
@ -64,6 +63,9 @@ Ergodox::~Ergodox() {
|
||||||
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.
|
or if we can't find a device with the given params.
|
||||||
|
|
||||||
|
If we successfully open a device, this method sends a
|
||||||
|
CMD_HELLO packet.
|
||||||
*/
|
*/
|
||||||
void Ergodox::open() {
|
void Ergodox::open() {
|
||||||
if (handle != NULL) {
|
if (handle != NULL) {
|
||||||
|
@ -94,6 +96,10 @@ void Ergodox::open() {
|
||||||
hid_free_enumeration(devs);
|
hid_free_enumeration(devs);
|
||||||
throw std::runtime_error("Could not open hid device");
|
throw std::runtime_error("Could not open hid device");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hid_set_nonblocking(handle, 1);
|
||||||
|
|
||||||
|
write(CMD_HELLO, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,14 +114,64 @@ void Ergodox::close() {
|
||||||
/*
|
/*
|
||||||
Send data to Ergodox.
|
Send data to Ergodox.
|
||||||
|
|
||||||
|
@param cmd Command byte
|
||||||
@param data Data to send
|
@param data Data to send
|
||||||
@param length How many bytes to send
|
@returns data_len Data length. Must be shorter than packet_size.
|
||||||
@returns Actual number of bytes written, or -1 on error.
|
|
||||||
*/
|
*/
|
||||||
int Ergodox::write(uint8_t* data, size_t length) const {
|
int Ergodox::write(uint8_t cmd, const uint8_t* data, uint8_t data_len) const {
|
||||||
if (handle == NULL) {
|
if (handle == NULL) {
|
||||||
throw std::runtime_error("Cannot write, device is not open!");
|
throw std::runtime_error("Cannot write, device is not open!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return hid_write(handle, data, length);
|
if (data_len > packet_size) {
|
||||||
|
throw std::runtime_error("Data length exceeds packet size");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == NULL) {
|
||||||
|
data = {0x00};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t packet[packet_size];
|
||||||
|
packet[0] = 0x00; // Report number. Not seen by keyboard.
|
||||||
|
packet[1] = cmd; // First byte is always command
|
||||||
|
|
||||||
|
// Copy data into rest of packet
|
||||||
|
std::copy(data, data + data_len, packet + 2);
|
||||||
|
|
||||||
|
return hid_write(handle, packet, packet_size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read data from Ergodox into read_buf. If a CMD_SEND_STATE packet is received, process it and return false.
|
||||||
|
|
||||||
|
@returns True if a new command was received, false otherwise.
|
||||||
|
*/
|
||||||
|
bool Ergodox::read() {
|
||||||
|
if (handle == NULL) {
|
||||||
|
throw std::runtime_error("Cannot write, device is not open!");
|
||||||
|
}
|
||||||
|
ssize_t res;
|
||||||
|
res = hid_read(handle, read_buf, packet_size);
|
||||||
|
|
||||||
|
if (res == 0) {
|
||||||
|
return false;
|
||||||
|
} else if (res < 0) {
|
||||||
|
throw std::runtime_error("Read failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(read_buf[0]) {
|
||||||
|
|
||||||
|
// If keyboard sends a state packet, parse it.
|
||||||
|
case CMD_SEND_STATE:
|
||||||
|
if (animation_mode != read_buf[1]) {
|
||||||
|
wprintf(L"Mode set to %#x\n", read_buf[1]);
|
||||||
|
animation_mode = read_buf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main code should not parse state packets.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res > 0;
|
||||||
}
|
}
|
|
@ -5,12 +5,24 @@
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include "hidapi.h"
|
#include "hidapi.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "commands.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A singleton Ergodox interface. Wraps all hidapi methods, including
|
A singleton Ergodox interface. Wraps all hidapi methods, including
|
||||||
hid_init() and hid_exit().
|
hid_init() and hid_exit().
|
||||||
*/
|
*/
|
||||||
class Ergodox {
|
class Ergodox {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// USB Device paramaters
|
||||||
|
const unsigned short vendor_id;
|
||||||
|
const unsigned short product_id;
|
||||||
|
const unsigned short usage;
|
||||||
|
const unsigned short usage_page;
|
||||||
|
const uint8_t packet_size = RAW_EPSIZE;
|
||||||
|
|
||||||
static Ergodox& init(
|
static Ergodox& init(
|
||||||
unsigned short vendor_id,
|
unsigned short vendor_id,
|
||||||
unsigned short product_id,
|
unsigned short product_id,
|
||||||
|
@ -20,11 +32,15 @@ class Ergodox {
|
||||||
|
|
||||||
~Ergodox();
|
~Ergodox();
|
||||||
|
|
||||||
int write(
|
int write(uint8_t cmd, const uint8_t* data, uint8_t data_len) const;
|
||||||
uint8_t* data,
|
|
||||||
size_t length
|
|
||||||
) const;
|
|
||||||
|
|
||||||
|
bool read();
|
||||||
|
|
||||||
|
// Read buffer, len = packet_size.
|
||||||
|
// Filled by read().
|
||||||
|
uint8_t read_buf[RAW_EPSIZE];
|
||||||
|
|
||||||
|
uint8_t get_animation_mode() const { return animation_mode; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ergodox(
|
Ergodox(
|
||||||
|
@ -42,13 +58,11 @@ class Ergodox {
|
||||||
void open();
|
void open();
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
// USB Device paramaters
|
|
||||||
const unsigned short vendor_id;
|
|
||||||
const unsigned short product_id;
|
|
||||||
const unsigned short usage;
|
|
||||||
const unsigned short usage_page;
|
|
||||||
|
|
||||||
// HID device.
|
// HID device.
|
||||||
// NULL if not opened.
|
// NULL if not opened.
|
||||||
hid_device* handle;
|
hid_device* handle;
|
||||||
|
|
||||||
|
// Keyboard state variables.
|
||||||
|
// Updated by read().
|
||||||
|
uint8_t animation_mode;
|
||||||
};
|
};
|
57
src/test.cpp
57
src/test.cpp
|
@ -2,7 +2,6 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
// For reading FIFO
|
// For reading FIFO
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -12,6 +11,7 @@
|
||||||
#include "utility/buffer.hpp"
|
#include "utility/buffer.hpp"
|
||||||
#include "signal_processing/fft.hpp"
|
#include "signal_processing/fft.hpp"
|
||||||
#include "ergodox.hpp"
|
#include "ergodox.hpp"
|
||||||
|
#include "commands.h"
|
||||||
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
|
@ -38,8 +38,19 @@ void draw_spectrum_bitmap(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// How many keys in a column * resolution per key.
|
||||||
|
// this MUST fit inside a uint8_t.
|
||||||
|
const uint8_t kb_resolution = 5 * 50;
|
||||||
|
|
||||||
|
// How many resolution steps to skip at the top and bottom.
|
||||||
|
const size_t bottom_skip = 25;
|
||||||
|
const size_t top_skip = 50;
|
||||||
|
|
||||||
|
|
||||||
const size_t width = 12;
|
const size_t width = 12;
|
||||||
const size_t height = 150;
|
const size_t height = bottom_skip + kb_resolution + top_skip;
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
@ -52,7 +63,7 @@ int main(int argc, char *argv[]) {
|
||||||
// FFT generator
|
// FFT generator
|
||||||
FFT_Visualizer fft = FFT_Visualizer(
|
FFT_Visualizer fft = FFT_Visualizer(
|
||||||
width, height,
|
width, height,
|
||||||
100, 10000
|
100, 5000
|
||||||
);
|
);
|
||||||
|
|
||||||
// Audio buffer
|
// Audio buffer
|
||||||
|
@ -70,24 +81,40 @@ int main(int argc, char *argv[]) {
|
||||||
0xFF60
|
0xFF60
|
||||||
);
|
);
|
||||||
|
|
||||||
// Data buffer
|
// Write buffer
|
||||||
uint8_t hid_buf[12];
|
uint8_t hid_buf[12];
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
buf.update();
|
|
||||||
fft.update(buf);
|
|
||||||
|
|
||||||
hid_buf[0] = 0x01;
|
|
||||||
hid_buf[1] = 0x02;
|
if (Dox.get_animation_mode() == 0x02) {
|
||||||
for (size_t i = 0; i < 10; i++) {
|
buf.update();
|
||||||
ssize_t h = fft.get_output()[i + 1] - 10;
|
fft.update(buf);
|
||||||
h = h>100 ? 100 : h;
|
|
||||||
h = h<0 ? 0 : h;
|
hid_buf[0] = CMD_ANIM_DATA_fft;
|
||||||
hid_buf[i + 2] = h;
|
for (size_t i = 0; i < 10; i++) {
|
||||||
|
// Get height from fft, apply bottom_skip
|
||||||
|
ssize_t h = fft.get_output()[i + 1] - bottom_skip;
|
||||||
|
|
||||||
|
// Enforce max and min
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Dox.write(hid_buf, 12);
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue