mirror of
https://github.com/rainloreley/artnet-usbdmx-converter.git
synced 2025-03-13 16:55:05 +01:00
191 lines
5.2 KiB
TypeScript
191 lines
5.2 KiB
TypeScript
import HID from "node-hid";
|
|
|
|
/*
|
|
Return codes
|
|
* 0: OK
|
|
* 1: invalid channel
|
|
* 2: invalid value
|
|
* 3: invalid mode
|
|
* 4: invalid array size
|
|
* 5: other error
|
|
*/
|
|
|
|
/**
|
|
* Represents a connected DMX interface and handles all communication with it
|
|
*/
|
|
class DMXInterface {
|
|
|
|
path: string;
|
|
serial: string;
|
|
manufacturer: string | undefined;
|
|
product: string | undefined;
|
|
currentMode = 0;
|
|
hidDevice: HID.HID;
|
|
dmxout: number[];
|
|
|
|
usbdmxInputCallback: (start: number, values: number[]) => void;
|
|
|
|
constructor(
|
|
path: string,
|
|
serial: string,
|
|
manufacturer: string | undefined = undefined,
|
|
product: string | undefined = undefined
|
|
) {
|
|
this.path = path;
|
|
this.serial = serial;
|
|
this.manufacturer = manufacturer;
|
|
this.product = product;
|
|
|
|
this.hidDevice = new HID.HID(path);
|
|
this.hidDevice.on("data", (data: Buffer) => {
|
|
// received buffer contains 33 bytes, the first one (data[0]) is the page and the rest are the dmx channel values
|
|
// this means we get 32 dmx channels in one package
|
|
|
|
const values: number[] = [];
|
|
for (let i = 1; i < 33; i++) {
|
|
values.push(data[i]);
|
|
}
|
|
this.usbdmxInputCallback(data[0] * 32, values);
|
|
|
|
})
|
|
this.dmxout = Array(512).fill(0);
|
|
}
|
|
|
|
/**
|
|
* Opens the interface and returns {@link DMXInterface}
|
|
* @param path HID path of the interface
|
|
* @param serial Serial number
|
|
* @param manufacturer Manufacturer ID
|
|
* @param product Product ID
|
|
*/
|
|
static open = (
|
|
path: string,
|
|
serial: string,
|
|
manufacturer: string | undefined = undefined,
|
|
product: string | undefined = undefined
|
|
): Promise<DMXInterface> => {
|
|
return new Promise<DMXInterface>((resolve) => {
|
|
resolve(new DMXInterface(path, serial, manufacturer, product));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Closes the HID connection to the interface
|
|
*/
|
|
close = () => {
|
|
this.setMode(0);
|
|
this.hidDevice.close();
|
|
}
|
|
|
|
/*
|
|
Description of how DMX + HID works:
|
|
Each HID buffer sent contains 33 bytes.
|
|
The first byte (buffer[0]) is the "page", the rest are 32 DMX channels.
|
|
To get to the other DMX channels, increase the first byte
|
|
e.g. DMX channel = 33 -> buffer[0] = 1; buffer[1] = value for ch. 33
|
|
|
|
* Buffer values
|
|
* Index 0 (buffer[0])
|
|
* - 0-15: DMX?
|
|
* - 16: Mode
|
|
* - 17: Config?
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Modes:
|
|
* 0: Do nothing - Standby
|
|
* 1: DMX In -> DMX Out
|
|
* 2: PC Out -> DMX Out
|
|
* 3: DMX In + PC Out -> DMX Out
|
|
* 4: DMX In -> PC In
|
|
* 5: DMX In -> DMX Out & DMX In -> PC In
|
|
* 6: PC Out -> DMX Out & DMX In -> PC In
|
|
* 7: DMX In + PC Out -> DMX Out & DMX In -> PC In
|
|
*/
|
|
|
|
/**
|
|
* Sets the DMX mode of the interface
|
|
* @param mode mode as a number from 0-7
|
|
*/
|
|
setMode = (mode: number): number => {
|
|
// check if mode is between 0 and 7
|
|
if (mode > 7 || mode < 0) return 3;
|
|
|
|
// create data buffer
|
|
const _buffer = Buffer.alloc(34);
|
|
_buffer[1] = 16;
|
|
_buffer[2] = mode;
|
|
|
|
try {
|
|
this.hidDevice.write(_buffer);
|
|
this.currentMode = mode;
|
|
return 0;
|
|
}
|
|
catch (err) {
|
|
console.log(err);
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes DMX data to the interface
|
|
* @param data DMX data object array
|
|
*/
|
|
write = (data: DMXCommand[] | undefined): number => {
|
|
let returnStatus = 0;
|
|
|
|
if (data !== undefined) {
|
|
// update dmx out array with new values
|
|
for (const _entry of data) {
|
|
if (_entry.channel < 1 || _entry.channel > 512) {
|
|
returnStatus = 1;
|
|
continue;
|
|
}
|
|
if (_entry.value < 0 || _entry.value > 255) {
|
|
returnStatus = 2;
|
|
continue;
|
|
}
|
|
this.dmxout[_entry.channel - 1] = _entry.value
|
|
}
|
|
}
|
|
|
|
// loop through the 16 "pages" of commands (each write command can hold 32 channels)
|
|
for (let i = 0; i < 16; i++) {
|
|
const _buffer = Buffer.alloc(34);
|
|
// first byte needs to be 0 according to node-hid documentation (reportId)
|
|
_buffer[0] = 0x00;
|
|
// set second byte to page number
|
|
_buffer[1] = i;
|
|
for (let j = 2; j < 34; j++) {
|
|
// get value for corresponding channel (i * 32 for the page)
|
|
_buffer[j] = this.dmxout[(i * 32) + j - 2];
|
|
}
|
|
this.hidDevice.write(_buffer);
|
|
}
|
|
return returnStatus;
|
|
|
|
}
|
|
|
|
/**
|
|
* Writes an entire universe to the interface
|
|
* @param array Array of all DMX values with a length of 512
|
|
*/
|
|
writeMap = (array: number[]): number => {
|
|
if (array.length !== 512) return 4;
|
|
this.dmxout = array;
|
|
this.write(undefined);
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Data structure containing a pair of a DMX channel and its value
|
|
*/
|
|
interface DMXCommand {
|
|
channel: number;
|
|
value: number;
|
|
}
|
|
|
|
export { DMXInterface, DMXCommand }; |