-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from bytemind-de/dev
added USB support for GPIO items + tests + demo file
- Loading branch information
Showing
6 changed files
with
321 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
//might be required (change group 'gpio' as you like): | ||
//- sudo su -c "echo 'SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2886\", ATTRS{idProduct}==\"0018\", GROUP=\"gpio\", MODE=\"0666\"' > /etc/udev/rules.d/99-ReSpeakerUSB.rules" | ||
//- sudo usermod -a -G gpio $USER | ||
//- reboot or replug USB device | ||
const USB = require('usb'); | ||
//const BMRT = require('bmrequesttype'); | ||
|
||
/* | ||
Required interface functions: | ||
- description(): Info about the item options and commands | ||
- Class: | ||
- constructor(options) | ||
- init(successCallback, errorCallback) | ||
- writeData(data, successCallback, errorCallback) | ||
- readData(options, successCallback, errorCallback) | ||
- release(successCallback, errorCallback) | ||
- test(options): Function to test item (init, write, read, release) | ||
*/ | ||
|
||
function description(){ | ||
return { | ||
type: "ledArrayUsb", | ||
options: [ | ||
{name: "mode", type: "string", values: ["sepia", "seeed"]}, | ||
{name: "brightness", type: "number", min: 1, max: 31} | ||
], | ||
writeInterface: [ | ||
{name: "state", type: "string", values: [ | ||
"idle", "loading", "listening", "speaking", "awaitDialog", "wakeWordActive", "wakeWordInactive"]}, | ||
{name: "rgb0Array", type: "array"} | ||
], | ||
info: "ReSpeaker Mic Array v2.0 USB LED interface." | ||
}; | ||
} | ||
|
||
class PixelRing { | ||
constructor(device, mode) { | ||
this.device = device; | ||
this.mode = mode; | ||
this.state = ""; | ||
this.vadState = ""; | ||
|
||
//init. LED buffer | ||
this._numOfLeds = 12; | ||
this._ledBits = this._numOfLeds * 4; //rgb0 | ||
this.ledArray = new Array(this._ledBits); //full LEDs configuration | ||
this.ledArray.fill(0); | ||
|
||
//set LED array | ||
this.setLedArray = function(ledIndex, rgbRed, rgbGreen, rgbBlue, brightness){ | ||
let currentLed = (ledIndex - 1) * 4; | ||
this.ledArray[currentLed + 0] = rgbRed; | ||
this.ledArray[currentLed + 1] = rgbGreen; | ||
this.ledArray[currentLed + 2] = rgbBlue; | ||
this.ledArray[currentLed + 3] = brightness; | ||
} | ||
//set array for all LEDs | ||
this.setLedArrayAll = function(red, green, blue, brightness){ | ||
for (let i=0; i < this._numOfLeds; i++) { | ||
this.setLedArray(i, red, green, blue, brightness); | ||
} | ||
} | ||
} | ||
|
||
get availableStates() { | ||
return ["idle", "loading", "listening", "speaking", "awaitDialog", "wakeWordActive", "wakeWordInactive", "custom"]; | ||
} | ||
get currentState() { | ||
return {state: this.state, vad: this.vadState}; | ||
} | ||
|
||
write(cmd, data, stateInfo) { | ||
if (data == undefined) data = [0]; | ||
const buffer = Buffer.from(data); | ||
//var bmRt = BMRT.bmRequestType(BMRT.DIRECTION.Out, BMRT.TYPE.Vendor, BMRT.RECIPIENT.Device); | ||
var bmRt = 0x40; | ||
var that = this; | ||
return new Promise(function(resolve, reject){ | ||
that.device.controlTransfer(bmRt, 0, cmd, 0x1C, buffer, function(err, data){ | ||
if (err){ | ||
reject(err); | ||
}else{ | ||
if (stateInfo) that.state = stateInfo; | ||
resolve(data); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
//settings | ||
traceMode() { | ||
return this.write(0, [0], "trace"); | ||
} | ||
vadLedMode(i) { | ||
this.vadState = i; //TODO: unchecked state | ||
return this.write(0x22, [i]); //0 - off, 1 - on, 3 - auto | ||
} | ||
brightness(i) { | ||
return this.write(0x20, [i]); //range: 0x00 to 0x1F - or 0 to 31 | ||
} | ||
|
||
//states | ||
custom(rgb0Array, stateInfo) { | ||
return this.write(6, rgb0Array, stateInfo); //TODO | ||
} | ||
idle() { | ||
return this.write(1, [0, 0, 0, 0], "idle"); | ||
} | ||
loading() { | ||
return this.write(5, [0], "loading"); | ||
} | ||
listening() { | ||
return this.write(2, [0], "listening"); | ||
} | ||
speaking() { | ||
return this.write(4, [0], "speaking"); | ||
} | ||
awaitDialog() { | ||
this.setLedArray(1, 120, 120, 0, 0); | ||
return this.custom(this.ledArray, "awaitDialog"); //TODO: test | ||
} | ||
wakeWordActive() { | ||
return this.vadLedMode(1); //TODO: test | ||
} | ||
wakeWordInactive() { | ||
return this.vadLedMode(0); //TODO: test | ||
} | ||
} | ||
|
||
class GpioItem { | ||
//ReSpeaker Mic Array v2.0 vendor specific USB LED interface. | ||
//Reference 1: https://github.com/respeaker/pixel_ring/blob/master/pixel_ring/usb_pixel_ring_v2.py | ||
//Reference 2: https://wiki.seeedstudio.com/ReSpeaker_Mic_Array_v2.0/#control-the-leds | ||
//Node USB API: https://github.com/node-usb/node-usb#legacy-api | ||
|
||
constructor(options){ | ||
if (!options) options = {}; | ||
|
||
this.isReady = false; | ||
|
||
//USB Device | ||
this._device = USB.findByIds(0x2886, 0x0018); //hardcoded vendor/product ID | ||
this._device.timeout = 8000; | ||
this.pixelRing; | ||
|
||
//settings | ||
this._mode = options.mode || "sepia"; //TODO: implement | ||
this._brightness = options.brightness || 10; | ||
} | ||
|
||
init(successCallback, errorCallback){ | ||
if (!this._device){ | ||
errorCallback({name: "MissingDevice", message: "USB device not found"}); | ||
}else{ | ||
this.pixelRing = new PixelRing(this._device, this._mode); | ||
var that = this; | ||
try { | ||
//open device | ||
this._device.open(); | ||
//set inital modes (VAD LED off, brightness half) | ||
that.pixelRing.vadLedMode(0).then(function(){ | ||
that.pixelRing.brightness(that._brightness) | ||
}).then(function(){ | ||
//done | ||
that.isReady = true; | ||
successCallback(); | ||
}).catch(function(err){ | ||
errorCallback(err); | ||
}); | ||
}catch (err){ | ||
errorCallback(err); | ||
} | ||
} | ||
} | ||
|
||
writeData(data, successCallback, errorCallback){ | ||
if (!this.isReady){ | ||
errorCallback({name: "NotReady", message: "Interface not yet ready"}); | ||
}else if (!data || !data.state){ | ||
errorCallback({name: "MissingData", message: "Required: state"}); | ||
}else if (this.pixelRing.availableStates.indexOf(data.state) < 0){ | ||
errorCallback({name: "WrongData", message: "Unknown state"}); | ||
}else{ | ||
try { | ||
//write state | ||
this.pixelRing[data.state](data.rgb0Array).then(function(res){ | ||
successCallback(); | ||
}).catch(function(err){ | ||
errorCallback(err); | ||
}); | ||
}catch (err){ | ||
errorCallback(err); | ||
} | ||
} | ||
} | ||
|
||
readData(options, successCallback, errorCallback){ | ||
if (this.pixelRing){ | ||
var res = this.pixelRing.currentState; | ||
successCallback({result: res}); | ||
}else{ | ||
errorCallback({name: "NoData", message: "No data found"}); | ||
} | ||
} | ||
|
||
release(successCallback, errorCallback){ | ||
if (this._device){ | ||
//switch all off | ||
if (this.pixelRing){ | ||
//write state | ||
var that = this; | ||
this.pixelRing.idle().then(function(res){ | ||
//done | ||
that._device.close(); | ||
successCallback(); | ||
}).catch(function(err){ | ||
//done | ||
that._device.close(); | ||
errorCallback(err); | ||
}); | ||
}else{ | ||
this._device.close(); | ||
successCallback(); | ||
} | ||
}else{ | ||
successCallback(); | ||
} | ||
} | ||
} | ||
|
||
//console test call example: node -e 'require("./respeaker-usb-array-v2").test({init: {}, write: {state: "speaking"}})' | ||
function test(options){ | ||
if (!options) options = {}; | ||
var desc = description(); | ||
console.log("GPIO Item Test: " + desc.info); | ||
var gpioItem = new GpioItem(options.init || { | ||
mode: "sepia" | ||
}); | ||
console.log("GPIO init"); | ||
gpioItem.init(function(){ | ||
console.log("GPIO init: success - NEXT: write"); | ||
gpioItem.writeData(options.write || { | ||
state: "loading" | ||
}, function(){ | ||
console.log("GPIO write: success - NEXT: read in 3s"); | ||
setTimeout(function(){ | ||
gpioItem.readData(options.read || {}, function(data){ | ||
console.log("GPIO read: success - data:", data, "- NEXT: release"); | ||
gpioItem.release(function(data){ | ||
console.log("GPIO release: success - Item test: DONE"); | ||
}, function(err){ | ||
console.error("GPIO release: error", err); | ||
}); | ||
}, function(err){ | ||
console.error("GPIO read: error", err); | ||
}); | ||
}, 3000); | ||
}, function(err){ | ||
console.error("GPIO write: error", err); | ||
}); | ||
}, function(err){ | ||
console.error("GPIO init: error", err); | ||
}); | ||
} | ||
|
||
module.exports = { | ||
description: description, | ||
GpioItem: GpioItem, | ||
test: test, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters