Config working, add docs
@@ -0,0 +1,61 @@
|
||||
# Assembly
|
||||

|
||||
|
||||
Run through this guide once before building, ensuring you have all the parts required. You'll also need a soldering iron and solder.
|
||||
|
||||
## LEDs
|
||||
First up, the LEDs.
|
||||
|
||||

|
||||
|
||||
Take note, each LED point on the board has labelled connections for R, G and B. The long leg on the LEDs goes into the unmarked (ground) hole.
|
||||
|
||||

|
||||
|
||||
Insert the LEDs roughly to the red line, then bend them over:
|
||||

|
||||

|
||||
|
||||
Work on a slightly raised surface and place the board face down to solder, with the MACRO switch off the edge. This ensures all the LEDs are flush with the board surface.
|
||||

|
||||
|
||||
The bend doesn't have to be perfect - the acrylic will diffuse the light.
|
||||

|
||||
|
||||
## Encoders
|
||||
You can solder the encoders now or after you install the switches, but I prefer to do them sooner. Solder one of the small pins to keep it in place then pump lots of solder into the big connecting lugs. These need to take your abuse, so make it a good joint!
|
||||

|
||||
|
||||
## Switches
|
||||
**STOP NOW!** Have you finished soldering your LEDs? Have you tested them?
|
||||
Once you have soldered the switches you cannot fix anything without desoldering
|
||||
them again.
|
||||
|
||||
**WARNING:** The top 2 plates (those with encoder holes) are fragile and easily snap if dropped. Take care when assembling. Once assembled, they are protected by the rest of the controller and are much stronger.
|
||||
|
||||
Take your top clear plate and top black plate and place them over the LEDs. Take note of the clear plate - it has engraved edges for optimal lighting. It can be installed with the edges up or down, but they look best when facing up.
|
||||

|
||||
|
||||
Insert all your switches, and make sure they are seated properly. They are meant to be a tight fit into the PCB and should be completely flush with the surface.
|
||||

|
||||
|
||||
## Nuts and bolts
|
||||
Use the 1.5mm Allen key provided to screw the board together.
|
||||
Insert the top 8 bolts and 8 long nuts into the holes provided. Take note, there are 3 holes in the PCB that are unused. These nuts can sometimes be a tight fit - you may need to push them into the case with a hard surface.
|
||||

|
||||
|
||||
Stack the bottom 2 plates and insert the remaining 8 bolts. Take care to align the cutout over the USB port. The bolts are a fraction too short, you may need to back off the top bolts by about 2 turns to get the nut close enough.
|
||||

|
||||
|
||||
## Rubber feet
|
||||
Attach your 6 adhesive rubber feet. You can place these anywhere, but here is where I put them:
|
||||

|
||||
|
||||
# Final touches
|
||||
Using the Allen key provided, attach the knobs. The encoder shaft is shaped like a D - ensure you tighten the grub screw on the flat part of the D.
|
||||
|
||||
*Optional:* Add 7 o-rings to your keycaps.
|
||||
|
||||
Install keycaps by simply pushing them onto the switches.
|
||||
|
||||

|
||||
@@ -0,0 +1,17 @@
|
||||
# Configuration
|
||||
|
||||
Configure your controller with the web app at:
|
||||
https://mon.im/sdvx/config/
|
||||
|
||||
You will need Chrome 57+. You must be using SSL (https, not http).
|
||||
You will also need to go to chrome://flags and enable
|
||||
"Experimental Web Platform features". This step will not be required by
|
||||
June/July, as the WebUSB feature will be shipping in Chrome 60.
|
||||
|
||||
If you are using b\*\*\*\*\*tools, configure as normal.
|
||||
For fast configuration of the HID lighting, download and run [this exe](../Software/HidLights.exe)
|
||||
while config.exe is open and the controller is connected.
|
||||
|
||||
## HID Lighting fix
|
||||
By default, HID lighting will crash after a few minutes of gameplay. To fix
|
||||
this, simply patch libinput.dll using [this tool](https://mon.im/bemanipatcher/pocketvoltex.html).
|
||||
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 140 KiB |
|
After Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 225 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 468 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 111 KiB |
@@ -0,0 +1 @@
|
||||
AddType application/json .json
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 12,
|
||||
"md5": "6620eb96cde1ecdcc0c5caefac7f9d03"
|
||||
}
|
||||
@@ -6,33 +6,37 @@
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<script type="text/javascript" src="js/usbWrapper.js"></script>
|
||||
<script type="text/javascript" src="js/config.js"></script>
|
||||
<script type="text/javascript" src="js/settings.js"></script>
|
||||
<script type="text/javascript" src="js/updater.js"></script>
|
||||
<script type="text/javascript" src="js/md5.min.js"></script>
|
||||
<script type="text/javascript" src="js/jscolor.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function() {
|
||||
if(UsbWrapper.hasUSB) {
|
||||
document.getElementById("compat-failure").classList.add("hidden");
|
||||
//document.getElementById("configBox").classList.remove("hidden");
|
||||
document.getElementById("connectBtn").classList.remove("hidden");
|
||||
|
||||
config = new Config();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- Origin Trial Token, feature = WebUSB (For Chrome M57+), origin = https://mon.im, expires = 2017-07-02 -->
|
||||
<meta http-equiv="origin-trial" data-feature="WebUSB (For Chrome M57+)" data-expires="2017-07-02" content="AhzuiYytgNCrxPIuPxB9c3Eoyr7CbrPzl5hRLm4me+5OpcNSWk+eb6QGaPfktLvGaF/vMFPS531rJ48h7wiDWgwAAABHeyJvcmlnaW4iOiJodHRwczovL21vbi5pbTo0NDMiLCJmZWF0dXJlIjoiV2ViVVNCMiIsImV4cGlyeSI6MTQ5OTA1Njk5OH0=">
|
||||
</head>
|
||||
<body>
|
||||
<div id="compat-failure">
|
||||
This configurator requires Javascript, Windows 8+ (or OSX/Linux) and a WebUSB capable browser (Chrome 55+). If you lack these, give the exe config app a go.
|
||||
</div>
|
||||
<div class="center">
|
||||
<div id="compat-failure">
|
||||
This configurator requires Javascript, Windows 7+ (or OSX/Linux) and a WebUSB capable browser (Chrome 57+). If you lack these, harass mon for an exe config app.
|
||||
</div>
|
||||
<div>
|
||||
<div id="logview"></div>
|
||||
<!--<div id="configBox" class="configBox hidden disabled">
|
||||
</div>-->
|
||||
<div id="connect" class="connect">
|
||||
<button id="connectBtn" onclick="config.connect()">Connect</button>
|
||||
<button id="connectBtn" class="hidden" onclick="config.connect()">Connect</button>
|
||||
</div>
|
||||
<div id="configOptions" class="configOptions">
|
||||
</div>
|
||||
<div id="logview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -113,239 +113,14 @@ class BinaryBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
class Setting {
|
||||
setCallback(callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
fireCallback() {
|
||||
if(this.callback)
|
||||
this.callback(this);
|
||||
}
|
||||
}
|
||||
|
||||
// All settings must have createUI, read and write.
|
||||
// When the UI changes, call fireCallback to update the device config
|
||||
class SettingBool extends Setting {
|
||||
read(buffer) {
|
||||
this.box.checked = !!buffer.read(1);
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
buffer.write(this.box.checked ? 1 : 0);
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
var container = document.createElement('div');
|
||||
var check = document.createElement('input');
|
||||
check.type = 'checkbox';
|
||||
check.id = 'check-' + setting.id;
|
||||
check.onclick = this.fireCallback.bind(this);
|
||||
this.box = check;
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.htmlFor = check.id;
|
||||
label.textContent = setting.name;
|
||||
|
||||
container.appendChild(check);
|
||||
container.appendChild(label);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingRGB extends Setting {
|
||||
read(buffer) {
|
||||
var rgb = [];
|
||||
for(var i = 0; i < 3; i++)
|
||||
rgb[i] = (buffer.read(1) / BRIGHTNESS_MAX) * 255;
|
||||
this.picker.fromRGB(rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
for(var i = 0; i < 3; i++)
|
||||
buffer.write((this.picker.rgb[i] / 255) * BRIGHTNESS_MAX);
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
var label = document.createElement('p');
|
||||
label.textContent = setting.name + ':';
|
||||
|
||||
var colourPicker = document.createElement('input');
|
||||
colourPicker.className = 'colourPicker jscolor {valueElement:null}';
|
||||
colourPicker.onchange = this.fireCallback.bind(this);
|
||||
this.picker = new jscolor(colourPicker)
|
||||
|
||||
label.appendChild(colourPicker);
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingRadio extends Setting {
|
||||
read(buffer) {
|
||||
var val = buffer.read(1);
|
||||
|
||||
for(var i = 0; i < this.options.length; i++) {
|
||||
let opt = this.options[i];
|
||||
if(opt.val == val) {
|
||||
opt.radio.checked = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// sane default
|
||||
this.options[0].radio.checked = true;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
for(var i = 0; i < this.options.length; i++) {
|
||||
let opt = this.options[i];
|
||||
if(opt.radio.checked) {
|
||||
buffer.write(opt.val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// sane default
|
||||
buffer.write(this.options[0].val);
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
this.options = setting.options;
|
||||
var container = document.createElement('div');
|
||||
container.textContent = setting.name + ':';
|
||||
|
||||
var radioDiv = document.createElement('div');
|
||||
radioDiv.className = 'nestedSetting';
|
||||
|
||||
for(var i = 0; i < setting.options.length; i++) {
|
||||
var opt = setting.options[i];
|
||||
|
||||
var radio = document.createElement('input');
|
||||
radio.type = 'radio';
|
||||
radio.id = setting.id + '-' + i;
|
||||
radio.name = setting.id;
|
||||
radio.onclick = this.fireCallback.bind(this);
|
||||
opt.radio = radio;
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.htmlFor = radio.id;
|
||||
label.textContent = opt.name;
|
||||
|
||||
var radioContainer = document.createElement('p');
|
||||
radioContainer.appendChild(radio);
|
||||
radioContainer.appendChild(label);
|
||||
radioDiv.appendChild(radioContainer);
|
||||
}
|
||||
|
||||
container.appendChild(radioDiv);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingKeys extends Setting {
|
||||
constructor(switchCount, ignoreLast = false) {
|
||||
super();
|
||||
this.byteCount = switchCount;
|
||||
// hacks upon hacks, this stuff
|
||||
this.ignoreLast = ignoreLast;
|
||||
this.switchCount = ignoreLast ? switchCount - 1 : switchCount;
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
var keys = buffer.read(this.byteCount, true);
|
||||
for(var i = 0; i < this.switchCount; i++) {
|
||||
this.selects[i].value = keys[i];
|
||||
}
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
for(var i = 0; i < this.selects.length; i++) {
|
||||
buffer.write(this.selects[i].value);
|
||||
}
|
||||
if(this.ignoreLast) {
|
||||
buffer.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
var entry = document.createElement('p');
|
||||
entry.textContent = setting.name + ':';
|
||||
|
||||
this.selects = [];
|
||||
for(var i = 0; i < this.switchCount; i++) {
|
||||
var select = document.createElement("select");
|
||||
scancodes.forEach(function(code) {
|
||||
var option = document.createElement("option");
|
||||
option.value = code.value;
|
||||
option.textContent = code.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
select.onchange = this.fireCallback.bind(this);
|
||||
entry.appendChild(select);
|
||||
this.selects.push(select);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingPin extends Setting {
|
||||
createUI(setting) {
|
||||
var entry = document.createElement('p');
|
||||
entry.textContent = setting.name + ':';
|
||||
|
||||
var input = document.createElement('input');
|
||||
input.type = 'password';
|
||||
input.className = 'pinEntry';
|
||||
input.textContent = '0000';
|
||||
|
||||
entry.appendChild(input);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingMacro extends Setting {
|
||||
constructor() {
|
||||
super();
|
||||
this.action = new SettingRadio();
|
||||
this.keypress = new SettingKeys(1);
|
||||
this.action.setCallback(this.fireCallback.bind(this));
|
||||
this.keypress.setCallback(this.fireCallback.bind(this));
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
var entry = document.createElement('p');
|
||||
entry.textContent = setting.name + ':';
|
||||
|
||||
entry.appendChild(this.action.createUI({
|
||||
id: setting.id + '-action', name: 'Action',
|
||||
options : [{name: 'Keypress', val: 0},
|
||||
{name: 'Macro PIN', val: 1},
|
||||
{name: 'Cycle LED pattern', val: 2},
|
||||
{name: 'Toggle LEDs', val: 3}]
|
||||
}));
|
||||
entry.appendChild(this.keypress.createUI({name: 'Key'}));
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.action.read(buffer);
|
||||
this.keypress.read(buffer);
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
this.action.write(buffer);
|
||||
this.keypress.write(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigValues {
|
||||
constructor() {
|
||||
this.switches = new SettingKeys(SWITCH_COUNT, true);
|
||||
this.btColour = new SettingRGB();
|
||||
this.fxColour = new SettingRGB();
|
||||
this.breatheColour = new SettingRGB();
|
||||
this.knobL = new SettingRGB();
|
||||
this.knobR = new SettingRGB();
|
||||
this.btColour = new SettingRGB(BRIGHTNESS_MAX);
|
||||
this.fxColour = new SettingRGB(BRIGHTNESS_MAX);
|
||||
this.breatheColour = new SettingRGB(BRIGHTNESS_MAX);
|
||||
this.knobL = new SettingRGB(BRIGHTNESS_MAX);
|
||||
this.knobR = new SettingRGB(BRIGHTNESS_MAX);
|
||||
this.lightsOn = new SettingBool();
|
||||
this.hidLights = new SettingBool();
|
||||
this.keyLights = new SettingBool();
|
||||
@@ -423,10 +198,12 @@ class Config {
|
||||
return device.selectConfiguration(1)
|
||||
})
|
||||
.then(() => {
|
||||
if(device.productName == 'Voltex Boot') {
|
||||
if(device.deviceVersionSubminor == 2) { // bootloader
|
||||
return this.loadFirmware();
|
||||
} else {
|
||||
} else if(device.deviceVersionSubminor == 3) { // main program
|
||||
return this.loadConfig();
|
||||
} else {
|
||||
return Promise.reject("Invalid device found, is this a Tatacon converter?");
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -440,9 +217,7 @@ class Config {
|
||||
loadFirmware() {
|
||||
visibleLog("Downloading fimware...");
|
||||
return getLatest()
|
||||
.then(info => {
|
||||
return downloadLatest(info.version);
|
||||
})
|
||||
.then(downloadLatest)
|
||||
.then(firmware => {
|
||||
visibleLog("Flashing firmware...");
|
||||
return programLatest(device, firmware);
|
||||
@@ -460,10 +235,21 @@ class Config {
|
||||
return this.readVersion();
|
||||
})
|
||||
.then(version => {
|
||||
console.log("Found Pocket Voltex v" + version/10.0);
|
||||
this.version = version;
|
||||
visibleLog("Found Pocket Voltex v" + version/10.0);
|
||||
return getLatest();
|
||||
})
|
||||
.then(latestInfo => {
|
||||
if(latestInfo.version > this.version) {
|
||||
alert("Firmware update required. Device will be rebooted. Connect again to update");
|
||||
this.rebootToBootloader();
|
||||
return Promise.reject("Rebooted to bootloader, config read aborted");
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.enableUI();
|
||||
return this.readConfig();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
readVersion() {
|
||||
@@ -546,103 +332,4 @@ class Config {
|
||||
|
||||
window.Config = Config;
|
||||
|
||||
var scancodes = [
|
||||
{name: "None", value: 0x00},
|
||||
{name: "A", value: 0x04},
|
||||
{name: "B", value: 0x05},
|
||||
{name: "C", value: 0x06},
|
||||
{name: "D", value: 0x07},
|
||||
{name: "E", value: 0x08},
|
||||
{name: "F", value: 0x09},
|
||||
{name: "G", value: 0x0A},
|
||||
{name: "H", value: 0x0B},
|
||||
{name: "I", value: 0x0C},
|
||||
{name: "J", value: 0x0D},
|
||||
{name: "K", value: 0x0E},
|
||||
{name: "L", value: 0x0F},
|
||||
{name: "M", value: 0x10},
|
||||
{name: "N", value: 0x11},
|
||||
{name: "O", value: 0x12},
|
||||
{name: "P", value: 0x13},
|
||||
{name: "Q", value: 0x14},
|
||||
{name: "R", value: 0x15},
|
||||
{name: "S", value: 0x16},
|
||||
{name: "T", value: 0x17},
|
||||
{name: "U", value: 0x18},
|
||||
{name: "V", value: 0x19},
|
||||
{name: "W", value: 0x1A},
|
||||
{name: "X", value: 0x1B},
|
||||
{name: "Y", value: 0x1C},
|
||||
{name: "Z", value: 0x1D},
|
||||
{name: "1", value: 0x1E},
|
||||
{name: "2", value: 0x1F},
|
||||
{name: "3", value: 0x20},
|
||||
{name: "4", value: 0x21},
|
||||
{name: "5", value: 0x22},
|
||||
{name: "6", value: 0x23},
|
||||
{name: "7", value: 0x24},
|
||||
{name: "8", value: 0x25},
|
||||
{name: "9", value: 0x26},
|
||||
{name: "0", value: 0x27},
|
||||
{name: "-_", value: 0x2D},
|
||||
{name: "=+", value: 0x2E},
|
||||
{name: "[{", value: 0x2F},
|
||||
{name: "]}", value: 0x30},
|
||||
{name: "\\|", value: 0x31},
|
||||
{name: ";:", value: 0x33},
|
||||
{name: "'\"", value: 0x34},
|
||||
{name: "`~", value: 0x35},
|
||||
{name: ",<", value: 0x36},
|
||||
{name: ".>", value: 0x37},
|
||||
{name: "/?", value: 0x38},
|
||||
{name: "F1", value: 0x3A},
|
||||
{name: "F2", value: 0x3B},
|
||||
{name: "F3", value: 0x3C},
|
||||
{name: "F4", value: 0x3D},
|
||||
{name: "F5", value: 0x3E},
|
||||
{name: "F6", value: 0x3F},
|
||||
{name: "F7", value: 0x40},
|
||||
{name: "F8", value: 0x41},
|
||||
{name: "F9", value: 0x42},
|
||||
{name: "F10", value: 0x43},
|
||||
{name: "F11", value: 0x44},
|
||||
{name: "F12", value: 0x45},
|
||||
{name: "ESC", value: 0x29},
|
||||
{name: "BACKSPC", value: 0x2A},
|
||||
{name: "TAB", value: 0x2B},
|
||||
{name: "SPACE", value: 0x2C},
|
||||
{name: "ENTER", value: 0x28},
|
||||
{name: "CAPSLOCK", value: 0x39},
|
||||
{name: "PRTSCRN", value: 0x46},
|
||||
{name: "SCRL LCK", value: 0x47},
|
||||
{name: "PAUSE", value: 0x48},
|
||||
{name: "INSERT", value: 0x49},
|
||||
{name: "HOME", value: 0x4A},
|
||||
{name: "PG UP", value: 0x4B},
|
||||
{name: "DEL", value: 0x4C},
|
||||
{name: "END", value: 0x4D},
|
||||
{name: "PG DN", value: 0x4E},
|
||||
{name: "RIGHT", value: 0x4F},
|
||||
{name: "LEFT", value: 0x50},
|
||||
{name: "DOWN", value: 0x51},
|
||||
{name: "UP", value: 0x52},
|
||||
{name: "NUM LOCK", value: 0x53},
|
||||
{name: "NUM /", value: 0x54},
|
||||
{name: "NUM *", value: 0x55},
|
||||
{name: "NUM -", value: 0x56},
|
||||
{name: "NUM +", value: 0x57},
|
||||
{name: "NUM ENTER", value: 0x58},
|
||||
{name: "NUM 1", value: 0x59},
|
||||
{name: "NUM 2", value: 0x5A},
|
||||
{name: "NUM 3", value: 0x5B},
|
||||
{name: "NUM 4", value: 0x5C},
|
||||
{name: "NUM 5", value: 0x5D},
|
||||
{name: "NUM 6", value: 0x5E},
|
||||
{name: "NUM 7", value: 0x5F},
|
||||
{name: "NUM 8", value: 0x60},
|
||||
{name: "NUM 9", value: 0x61},
|
||||
{name: "NUM 0", value: 0x62},
|
||||
{name: "NUM .", value: 0x63}
|
||||
];
|
||||
|
||||
})(window, document);
|
||||
@@ -0,0 +1,340 @@
|
||||
(function(window, document) {
|
||||
"use strict";
|
||||
|
||||
class Setting {
|
||||
setCallback(callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
fireCallback() {
|
||||
if(this.callback)
|
||||
this.callback(this);
|
||||
}
|
||||
}
|
||||
|
||||
// All settings must have createUI, read and write.
|
||||
// When the UI changes, call fireCallback to update the device config
|
||||
class SettingBool extends Setting {
|
||||
read(buffer) {
|
||||
this.box.checked = !!buffer.read(1);
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
buffer.write(this.box.checked ? 1 : 0);
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
var container = document.createElement('div');
|
||||
var check = document.createElement('input');
|
||||
check.type = 'checkbox';
|
||||
check.id = 'check-' + setting.id;
|
||||
check.onclick = this.fireCallback.bind(this);
|
||||
this.box = check;
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.htmlFor = check.id;
|
||||
label.textContent = setting.name;
|
||||
|
||||
container.appendChild(check);
|
||||
container.appendChild(label);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingRGB extends Setting {
|
||||
constructor(maxBright) {
|
||||
super();
|
||||
this.maxBright = maxBright;
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
var rgb = [];
|
||||
for(var i = 0; i < 3; i++)
|
||||
rgb[i] = (buffer.read(1) / this.maxBright) * 255;
|
||||
this.picker.fromRGB(rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
for(var i = 0; i < 3; i++)
|
||||
buffer.write((this.picker.rgb[i] / 255) * this.maxBright);
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
var label = document.createElement('p');
|
||||
label.textContent = setting.name + ':';
|
||||
|
||||
var colourPicker = document.createElement('input');
|
||||
colourPicker.className = 'colourPicker jscolor {valueElement:null}';
|
||||
colourPicker.onchange = this.fireCallback.bind(this);
|
||||
this.picker = new jscolor(colourPicker)
|
||||
|
||||
label.appendChild(colourPicker);
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingRadio extends Setting {
|
||||
read(buffer) {
|
||||
var val = buffer.read(1);
|
||||
|
||||
for(var i = 0; i < this.options.length; i++) {
|
||||
let opt = this.options[i];
|
||||
if(opt.val == val) {
|
||||
opt.radio.checked = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// sane default
|
||||
this.options[0].radio.checked = true;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
for(var i = 0; i < this.options.length; i++) {
|
||||
let opt = this.options[i];
|
||||
if(opt.radio.checked) {
|
||||
buffer.write(opt.val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// sane default
|
||||
buffer.write(this.options[0].val);
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
this.options = setting.options;
|
||||
var container = document.createElement('div');
|
||||
container.textContent = setting.name + ':';
|
||||
|
||||
var radioDiv = document.createElement('div');
|
||||
radioDiv.className = 'nestedSetting';
|
||||
|
||||
for(var i = 0; i < setting.options.length; i++) {
|
||||
var opt = setting.options[i];
|
||||
|
||||
var radio = document.createElement('input');
|
||||
radio.type = 'radio';
|
||||
radio.id = setting.id + '-' + i;
|
||||
radio.name = setting.id;
|
||||
radio.onclick = this.fireCallback.bind(this);
|
||||
opt.radio = radio;
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.htmlFor = radio.id;
|
||||
label.textContent = opt.name;
|
||||
|
||||
var radioContainer = document.createElement('p');
|
||||
radioContainer.appendChild(radio);
|
||||
radioContainer.appendChild(label);
|
||||
radioDiv.appendChild(radioContainer);
|
||||
}
|
||||
|
||||
container.appendChild(radioDiv);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingKeys extends Setting {
|
||||
constructor(switchCount, ignoreLast = false) {
|
||||
super();
|
||||
this.byteCount = switchCount;
|
||||
// hacks upon hacks, this stuff
|
||||
this.ignoreLast = ignoreLast;
|
||||
this.switchCount = ignoreLast ? switchCount - 1 : switchCount;
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
var keys = buffer.read(this.byteCount, true);
|
||||
for(var i = 0; i < this.switchCount; i++) {
|
||||
this.selects[i].value = keys[i];
|
||||
}
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
for(var i = 0; i < this.selects.length; i++) {
|
||||
buffer.write(this.selects[i].value);
|
||||
}
|
||||
if(this.ignoreLast) {
|
||||
buffer.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
var entry = document.createElement('p');
|
||||
entry.textContent = setting.name + ':';
|
||||
|
||||
this.selects = [];
|
||||
for(var i = 0; i < this.switchCount; i++) {
|
||||
var select = document.createElement("select");
|
||||
scancodes.forEach(function(code) {
|
||||
var option = document.createElement("option");
|
||||
option.value = code.value;
|
||||
option.textContent = code.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
select.onchange = this.fireCallback.bind(this);
|
||||
entry.appendChild(select);
|
||||
this.selects.push(select);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingPin extends Setting {
|
||||
createUI(setting) {
|
||||
var entry = document.createElement('p');
|
||||
entry.textContent = setting.name + ':';
|
||||
|
||||
var input = document.createElement('input');
|
||||
input.type = 'password';
|
||||
input.className = 'pinEntry';
|
||||
input.textContent = '0000';
|
||||
|
||||
entry.appendChild(input);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingMacro extends Setting {
|
||||
constructor() {
|
||||
super();
|
||||
this.action = new SettingRadio();
|
||||
this.keypress = new SettingKeys(1);
|
||||
this.action.setCallback(this.fireCallback.bind(this));
|
||||
this.keypress.setCallback(this.fireCallback.bind(this));
|
||||
}
|
||||
|
||||
createUI(setting) {
|
||||
var entry = document.createElement('p');
|
||||
entry.textContent = setting.name + ':';
|
||||
|
||||
entry.appendChild(this.action.createUI({
|
||||
id: setting.id + '-action', name: 'Action',
|
||||
options : [{name: 'Keypress', val: 0},
|
||||
{name: 'Macro PIN', val: 1},
|
||||
{name: 'Cycle LED pattern', val: 2},
|
||||
{name: 'Toggle LEDs', val: 3}]
|
||||
}));
|
||||
entry.appendChild(this.keypress.createUI({name: 'Key'}));
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.action.read(buffer);
|
||||
this.keypress.read(buffer);
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
this.action.write(buffer);
|
||||
this.keypress.write(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
var scancodes = [
|
||||
{name: "None", value: 0x00},
|
||||
{name: "A", value: 0x04},
|
||||
{name: "B", value: 0x05},
|
||||
{name: "C", value: 0x06},
|
||||
{name: "D", value: 0x07},
|
||||
{name: "E", value: 0x08},
|
||||
{name: "F", value: 0x09},
|
||||
{name: "G", value: 0x0A},
|
||||
{name: "H", value: 0x0B},
|
||||
{name: "I", value: 0x0C},
|
||||
{name: "J", value: 0x0D},
|
||||
{name: "K", value: 0x0E},
|
||||
{name: "L", value: 0x0F},
|
||||
{name: "M", value: 0x10},
|
||||
{name: "N", value: 0x11},
|
||||
{name: "O", value: 0x12},
|
||||
{name: "P", value: 0x13},
|
||||
{name: "Q", value: 0x14},
|
||||
{name: "R", value: 0x15},
|
||||
{name: "S", value: 0x16},
|
||||
{name: "T", value: 0x17},
|
||||
{name: "U", value: 0x18},
|
||||
{name: "V", value: 0x19},
|
||||
{name: "W", value: 0x1A},
|
||||
{name: "X", value: 0x1B},
|
||||
{name: "Y", value: 0x1C},
|
||||
{name: "Z", value: 0x1D},
|
||||
{name: "1", value: 0x1E},
|
||||
{name: "2", value: 0x1F},
|
||||
{name: "3", value: 0x20},
|
||||
{name: "4", value: 0x21},
|
||||
{name: "5", value: 0x22},
|
||||
{name: "6", value: 0x23},
|
||||
{name: "7", value: 0x24},
|
||||
{name: "8", value: 0x25},
|
||||
{name: "9", value: 0x26},
|
||||
{name: "0", value: 0x27},
|
||||
{name: "-_", value: 0x2D},
|
||||
{name: "=+", value: 0x2E},
|
||||
{name: "[{", value: 0x2F},
|
||||
{name: "]}", value: 0x30},
|
||||
{name: "\\|", value: 0x31},
|
||||
{name: ";:", value: 0x33},
|
||||
{name: "'\"", value: 0x34},
|
||||
{name: "`~", value: 0x35},
|
||||
{name: ",<", value: 0x36},
|
||||
{name: ".>", value: 0x37},
|
||||
{name: "/?", value: 0x38},
|
||||
{name: "F1", value: 0x3A},
|
||||
{name: "F2", value: 0x3B},
|
||||
{name: "F3", value: 0x3C},
|
||||
{name: "F4", value: 0x3D},
|
||||
{name: "F5", value: 0x3E},
|
||||
{name: "F6", value: 0x3F},
|
||||
{name: "F7", value: 0x40},
|
||||
{name: "F8", value: 0x41},
|
||||
{name: "F9", value: 0x42},
|
||||
{name: "F10", value: 0x43},
|
||||
{name: "F11", value: 0x44},
|
||||
{name: "F12", value: 0x45},
|
||||
{name: "ESC", value: 0x29},
|
||||
{name: "BACKSPC", value: 0x2A},
|
||||
{name: "TAB", value: 0x2B},
|
||||
{name: "SPACE", value: 0x2C},
|
||||
{name: "ENTER", value: 0x28},
|
||||
{name: "CAPSLOCK", value: 0x39},
|
||||
{name: "PRTSCRN", value: 0x46},
|
||||
{name: "SCRL LCK", value: 0x47},
|
||||
{name: "PAUSE", value: 0x48},
|
||||
{name: "INSERT", value: 0x49},
|
||||
{name: "HOME", value: 0x4A},
|
||||
{name: "PG UP", value: 0x4B},
|
||||
{name: "DEL", value: 0x4C},
|
||||
{name: "END", value: 0x4D},
|
||||
{name: "PG DN", value: 0x4E},
|
||||
{name: "RIGHT", value: 0x4F},
|
||||
{name: "LEFT", value: 0x50},
|
||||
{name: "DOWN", value: 0x51},
|
||||
{name: "UP", value: 0x52},
|
||||
{name: "NUM LOCK", value: 0x53},
|
||||
{name: "NUM /", value: 0x54},
|
||||
{name: "NUM *", value: 0x55},
|
||||
{name: "NUM -", value: 0x56},
|
||||
{name: "NUM +", value: 0x57},
|
||||
{name: "NUM ENTER", value: 0x58},
|
||||
{name: "NUM 1", value: 0x59},
|
||||
{name: "NUM 2", value: 0x5A},
|
||||
{name: "NUM 3", value: 0x5B},
|
||||
{name: "NUM 4", value: 0x5C},
|
||||
{name: "NUM 5", value: 0x5D},
|
||||
{name: "NUM 6", value: 0x5E},
|
||||
{name: "NUM 7", value: 0x5F},
|
||||
{name: "NUM 8", value: 0x60},
|
||||
{name: "NUM 9", value: 0x61},
|
||||
{name: "NUM 0", value: 0x62},
|
||||
{name: "NUM .", value: 0x63}
|
||||
];
|
||||
|
||||
window.SettingBool = SettingBool;
|
||||
window.SettingRGB = SettingRGB;
|
||||
window.SettingRadio = SettingRadio;
|
||||
window.SettingKeys = SettingKeys;
|
||||
window.SettingPin = SettingPin;
|
||||
window.SettingMacro = SettingMacro;
|
||||
|
||||
})(window, document);
|
||||
@@ -31,15 +31,18 @@ function getLatest() {
|
||||
});
|
||||
};
|
||||
|
||||
function downloadLatest(version) {
|
||||
function downloadLatest(versionInfo) {
|
||||
return new Promise ((resolve, reject) => {
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", "firmwares/PocketVoltex_" + version + ".bin", true);
|
||||
req.open("GET", "firmwares/PocketVoltex_" + versionInfo.version + ".bin", true);
|
||||
req.responseType = "arraybuffer";
|
||||
|
||||
req.onload = function (oEvent) {
|
||||
var arrayBuffer = req.response;
|
||||
if (arrayBuffer) {
|
||||
if(md5(arrayBuffer) != versionInfo.md5) {
|
||||
reject(Error("Firmware md5 does not match, download may be corrupt!"));
|
||||
}
|
||||
var view = new Uint8Array(arrayBuffer);
|
||||
if(view.length <= flashSize) {
|
||||
resolve(view);
|
||||
@@ -47,7 +50,7 @@ function downloadLatest(version) {
|
||||
reject(Error("Firmware too big! Must be under " +flashSize + ", got " + arrayBuffer.length));
|
||||
}
|
||||
} else {
|
||||
reject(Error(req.status + ": Could not fetch firmware version " + version));
|
||||
reject(Error(req.status + ": Could not fetch firmware version " + versionInfo.version));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -79,13 +82,31 @@ function programLatest(device, hex) {
|
||||
recipient: 'device',
|
||||
request: CONFIG_ID,
|
||||
value: 0,
|
||||
index: COMMAND_STARTAPPLICATION}, []);
|
||||
index: COMMAND_STARTAPPLICATION}, new ArrayBuffer([]));
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
||||
function getBootloaderVersion(device) {
|
||||
// read bootloader version, 16 bits
|
||||
return device.controlTransferIn({
|
||||
requestType: 'vendor',
|
||||
recipient: 'device',
|
||||
request: CONFIG_ID,
|
||||
value: 0,
|
||||
index: COMMAND_VERSION}, 2)
|
||||
.then(retVal => {
|
||||
if(retVal.status == "stall") {
|
||||
return Promise.reject("Could not get bootloader version");
|
||||
}
|
||||
var view = new Uint16Array(retVal.data);
|
||||
return view[0];
|
||||
});
|
||||
}
|
||||
|
||||
window.downloadLatest = downloadLatest;
|
||||
window.getLatest = getLatest;
|
||||
window.programLatest = programLatest;
|
||||
window.getBootloaderVersion = getBootloaderVersion;
|
||||
|
||||
})(window, document);
|
||||