2022-08-30 23:52:37 +02:00
|
|
|
const express = require('express')
|
|
|
|
const http = require('http')
|
|
|
|
const WebSocket = require('ws')
|
|
|
|
const osc = require('osc')
|
|
|
|
const readline = require('readline')
|
|
|
|
const fs = require('fs')
|
|
|
|
|
2023-01-19 14:26:29 +01:00
|
|
|
const SerialPort = require('serialport')
|
|
|
|
|
2022-08-30 23:52:37 +02:00
|
|
|
const port = 6676
|
|
|
|
// Vzemi iz argumenta
|
|
|
|
const tty = process.argv[2]
|
|
|
|
|
|
|
|
let eulerRotation = [0, 0, 0]
|
|
|
|
|
|
|
|
let baudrate = parseInt(process.argv[3])
|
|
|
|
if (!baudrate) {
|
|
|
|
baudrate = 115200
|
|
|
|
}
|
|
|
|
|
|
|
|
const include_files = [
|
|
|
|
'/anim.js',
|
|
|
|
'/control.js',
|
|
|
|
'/osctl.js',
|
|
|
|
'/test.js',
|
|
|
|
'/node_modules/three/build/three.min.js',
|
|
|
|
'/node_modules/nouislider/distribute/nouislider.min.js',
|
|
|
|
'/node_modules/nouislider/distribute/nouislider.min.css',
|
|
|
|
'/node_modules/osc/dist/osc-browser.js'
|
|
|
|
];
|
|
|
|
|
|
|
|
const app = express();
|
|
|
|
const server = http.Server(app);
|
|
|
|
|
|
|
|
const DEBUG = true
|
|
|
|
|
|
|
|
// Odprti serijski OSC link
|
|
|
|
let scon = null
|
|
|
|
|
2022-09-02 08:26:37 +02:00
|
|
|
let buttonHeld = [false, false, false, false, false, false, false, false]
|
|
|
|
let buttonValue = [false, false, false, false, false, false, false, false]
|
2022-08-31 15:55:49 +02:00
|
|
|
|
|
|
|
let switchHeld = [false, false, false, false]
|
|
|
|
let switchValue = [false, false, false, false]
|
|
|
|
|
|
|
|
|
2022-08-30 23:52:37 +02:00
|
|
|
function openSerial() {
|
|
|
|
console.log('opening ', tty, baudrate)
|
|
|
|
|
|
|
|
scon = new osc.SerialPort({
|
|
|
|
devicePath: tty,
|
|
|
|
bitrate: baudrate,
|
|
|
|
autoOpen: true,
|
|
|
|
useSLIP: true
|
|
|
|
})
|
|
|
|
scon.open()
|
|
|
|
|
|
|
|
scon.on('open', e => {
|
|
|
|
console.log('serial connection opened')
|
|
|
|
//console.log(scon)
|
|
|
|
})
|
|
|
|
scon.on('error', e => {
|
|
|
|
console.error('tty error', e)
|
|
|
|
//scon.close()
|
|
|
|
})
|
|
|
|
scon.on('close', e => {
|
|
|
|
console.warn('serial connection closed, restarting in 1 second')
|
|
|
|
})
|
|
|
|
|
|
|
|
// Arduino OSC gre v web
|
|
|
|
scon.on('message', msg => {
|
|
|
|
// Debug incoming osc
|
|
|
|
if (DEBUG) {
|
2022-09-01 14:53:31 +02:00
|
|
|
//console.log('osc msg', msg)
|
2022-08-30 23:52:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sendAll(msg, null, null, osclients)
|
|
|
|
})
|
|
|
|
|
|
|
|
if (scon._closeCode) {
|
|
|
|
scon = null
|
|
|
|
console.log('restarting serial connection')
|
|
|
|
setTimeout(openSerial, 1000)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tty) {
|
|
|
|
openSerial(baudrate)
|
|
|
|
}
|
|
|
|
|
|
|
|
app.get('/', (req, res) => {
|
|
|
|
res.sendFile(__dirname + '/index.html');
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get('/ctl', (req, res) => {
|
|
|
|
res.sendFile(__dirname + '/control.html');
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get('/test', (req, res) => {
|
|
|
|
res.sendFile(__dirname + '/test.html');
|
|
|
|
});
|
|
|
|
|
|
|
|
let settings = {};
|
|
|
|
app.get('/settings', function(req, res) {
|
|
|
|
res.send(settings);
|
|
|
|
});
|
|
|
|
|
|
|
|
include_files.map(function(file) {
|
|
|
|
app.get(file, function(req, res){
|
|
|
|
res.sendFile(__dirname + file);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
server.listen(port, () => console.log('listening on *:' + port))
|
|
|
|
|
|
|
|
// Websocket init
|
|
|
|
const wss = new WebSocket.Server({ server })
|
|
|
|
|
|
|
|
|
|
|
|
// Relay multicast to websockets
|
|
|
|
// @TODO still sends to supercollider? Do we need two sockets?
|
|
|
|
//var dgram = require('dgram');
|
|
|
|
//var sss = dgram.createSocket('udp4');
|
|
|
|
//sss.on('listening', () => {
|
|
|
|
// sss.addMembership('224.0.1.9');
|
|
|
|
//})
|
|
|
|
//sss.bind(57120);
|
|
|
|
|
|
|
|
const scudp = new osc.UDPPort({
|
|
|
|
//socket: sss
|
|
|
|
remotePort: 57121,
|
|
|
|
localPort: 57100
|
|
|
|
})
|
|
|
|
|
|
|
|
scudp.on('open', () => {
|
|
|
|
console.log("UDP to OSC open")
|
2022-09-07 12:04:52 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// turn on and off osc messages sent to UDP / Supercollider
|
|
|
|
scudp.on('message', (msg) => {
|
|
|
|
if (msg.address == "/xyzc"){
|
|
|
|
if ( msg.args[0] == '1' )
|
|
|
|
{ console.log("XYZ ON ---- ", msg.args[0]); osconoff = 1; }
|
|
|
|
else { console.log("XYZ OFF ---- "); console.log("ddddd", msg.args[0]); osconoff = 0 }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-08-30 23:52:37 +02:00
|
|
|
|
|
|
|
scudp.on('message', (msg) => {
|
2022-09-07 12:04:52 +02:00
|
|
|
|
2022-08-30 23:52:37 +02:00
|
|
|
console.log('got UDP msg', msg);
|
|
|
|
osclients.forEach( client => {
|
|
|
|
if (client) {
|
|
|
|
//console.log("sending", msg, info)
|
|
|
|
client.send(msg)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
2022-09-07 12:04:52 +02:00
|
|
|
|
2022-08-30 23:52:37 +02:00
|
|
|
scudp.on('error', (e) => {
|
|
|
|
console.log('UDP OSC error!', e)
|
|
|
|
})
|
|
|
|
scudp.open()
|
|
|
|
|
|
|
|
function eulerFromQuaternion(quaternion) {
|
|
|
|
// Quaternion to matrix.
|
|
|
|
const w = quaternion[0], x = quaternion[1], y = quaternion[2], z = quaternion[3];
|
|
|
|
const x2 = x + x, y2 = y + y, z2 = z + z;
|
|
|
|
const xx = x * x2, xy = x * y2, xz = x * z2;
|
|
|
|
const yy = y * y2, yz = y * z2, zz = z * z2;
|
|
|
|
const wx = w * x2, wy = w * y2, wz = w * z2;
|
|
|
|
const matrix = [
|
|
|
|
1 - ( yy + zz ), xy + wz, xz - wy, 0,
|
|
|
|
xy - wz, 1 - ( xx + zz ), yz + wx, 0,
|
|
|
|
xz + wy, yz - wx, 1 - ( xx + yy ), 0,
|
|
|
|
0, 0, 0, 1
|
|
|
|
];
|
|
|
|
// Matrix to euler
|
|
|
|
function clamp( value, min, max ) {
|
|
|
|
return Math.max( min, Math.min( max, value ) );
|
|
|
|
}
|
|
|
|
const m11 = matrix[ 0 ], m12 = matrix[ 4 ], m13 = matrix[ 8 ];
|
|
|
|
const m21 = matrix[ 1 ], m22 = matrix[ 5 ], m23 = matrix[ 9 ];
|
|
|
|
const m31 = matrix[ 2 ], m32 = matrix[ 6 ], m33 = matrix[ 10 ];
|
|
|
|
var euler = [ 0, 0, 0 ];
|
|
|
|
|
|
|
|
euler[1] = Math.asin( clamp( m13, - 1, 1 ) );
|
|
|
|
if ( Math.abs( m13 ) < 0.9999999 ) {
|
|
|
|
euler[0] = Math.atan2( - m23, m33 );
|
|
|
|
euler[2] = Math.atan2( - m12, m11 );
|
|
|
|
} else {
|
|
|
|
euler[0] = Math.atan2( m32, m22 );
|
|
|
|
euler[2] = 0;
|
|
|
|
}
|
|
|
|
return euler;
|
|
|
|
}
|
|
|
|
|
2022-08-31 15:55:49 +02:00
|
|
|
|
2022-09-02 08:26:37 +02:00
|
|
|
var threshold = 15;
|
|
|
|
var thresholds = [15,15,15,15,15,15,15,15];
|
|
|
|
var floatthresholds = [15,15,15,15,15,15,15,15];
|
|
|
|
var threshdiff = 4;
|
|
|
|
var touchvalue = [40,40,40,40,40,40,40,40];
|
|
|
|
var touchcount = [0,0,0,0,0,0,0,0];
|
2022-08-30 23:52:37 +02:00
|
|
|
|
2022-09-07 12:04:52 +02:00
|
|
|
var osconoff = 0;
|
|
|
|
|
2022-08-30 23:52:37 +02:00
|
|
|
const sendAll = (msg, info, oscWS, osclients) => {
|
|
|
|
|
2022-09-07 12:04:52 +02:00
|
|
|
//if (msg.address == '/XYZcontrols') console.log("XYZ ON ------------------------------------ ", msg[1]);
|
|
|
|
|
2022-08-30 23:52:37 +02:00
|
|
|
if (msg.address == '/keys') {
|
2022-08-31 15:55:49 +02:00
|
|
|
|
2022-09-07 12:04:52 +02:00
|
|
|
//console.log("TOUCH MSG",msg.args);
|
2022-09-02 08:26:37 +02:00
|
|
|
|
|
|
|
for (let i = 1; i < 8; i++) {
|
|
|
|
touchvalue[i]=msg.args[i];
|
|
|
|
}
|
|
|
|
touchvalue[0]=msg.args[0];
|
|
|
|
// LOW PASS FILTER BUTTON 0
|
|
|
|
// touchvalue[0]=touchvalue[0]*0.95+msg.args[0]*0.05;
|
|
|
|
//console.log("TOUCH VAL",touchvalue);
|
|
|
|
|
|
|
|
// SELF-CALIBRATING TOUCH THRESHOLDS
|
|
|
|
for (let i = 0; i < 8; i++) {
|
|
|
|
if (floatthresholds[i]==15) thresholds[i]=floatthresholds[i]=msg.args[i]-threshdiff;
|
|
|
|
|
|
|
|
if (msg.args[i]>(thresholds[i]+(threshdiff/2))) {
|
|
|
|
floatthresholds[i]=0.99 * floatthresholds[i] + 0.01*(msg.args[i]-threshdiff);
|
|
|
|
thresholds[i]=Math.round(floatthresholds[i]);
|
|
|
|
}
|
|
|
|
}
|
2022-09-07 12:04:52 +02:00
|
|
|
//console.log("TOUCH FLTHR ",floatthresholds);
|
|
|
|
//console.log("TOUCH THR ",thresholds);
|
2022-09-01 00:58:53 +02:00
|
|
|
|
|
|
|
|
2022-09-02 08:26:37 +02:00
|
|
|
/* // LOGIC FOR SHIFT BUTTON 0
|
|
|
|
for (let i = 0; i < 1; i++) {
|
|
|
|
// momentary state between 0 and 1
|
|
|
|
if (msg.args[i] <= thresholds[i] ) {
|
2022-09-01 14:53:31 +02:00
|
|
|
buttonValue[i] = 1;
|
|
|
|
} else {
|
|
|
|
buttonValue[i] = 0;
|
|
|
|
}
|
2022-09-02 08:26:37 +02:00
|
|
|
msg.args.push(buttonValue[i])
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
// LOGIC FOR MOMENTARY BUTTONS (1 - 3)
|
|
|
|
for (let i = 0; i < 8; i++) {
|
2022-09-01 14:53:31 +02:00
|
|
|
// momentary state between 0 and 1
|
2022-09-02 08:26:37 +02:00
|
|
|
|
|
|
|
buttonValue[i] = 0;
|
|
|
|
if (touchvalue[i] <= thresholds[i] ) {
|
|
|
|
if (buttonHeld[i] == false) {
|
|
|
|
if (i!=0 || touchcount[i]) {
|
2022-09-01 00:58:53 +02:00
|
|
|
buttonHeld[i] = true
|
2022-09-02 08:26:37 +02:00
|
|
|
buttonValue[i] = 1;
|
|
|
|
console.log("TOUCH PRESS ",i,touchvalue[i],msg.args[i],thresholds[i],floatthresholds[i]);
|
2022-09-01 00:58:53 +02:00
|
|
|
}
|
|
|
|
}
|
2022-09-02 08:26:37 +02:00
|
|
|
touchcount[i]++;
|
|
|
|
} else if (touchvalue[i] > thresholds[i]+(threshdiff/2)) {
|
|
|
|
if (buttonHeld[i]) console.log("TOUCH PRESS RELEASE ",i,touchvalue[i],msg.args[i],thresholds[i],floatthresholds[i],touchcount[i]);
|
|
|
|
buttonHeld[i] = false;
|
|
|
|
touchcount[i]=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//0 IS SHIFT KEY, SENDS 1 ALL TIME WHEN HELD
|
|
|
|
if (i==0 && buttonHeld[0]) buttonValue[0]=1;
|
|
|
|
|
|
|
|
msg.args.push(buttonValue[i])
|
|
|
|
|
2022-09-01 00:58:53 +02:00
|
|
|
}
|
|
|
|
|
2022-09-02 08:26:37 +02:00
|
|
|
// keys 0 + 3 = euler reset
|
|
|
|
if (buttonHeld[0] && buttonHeld[3]) {
|
|
|
|
eulerRotation = [0, 0, 0];
|
|
|
|
console.log("EULER ROTATION RESET");
|
|
|
|
}
|
2022-08-31 15:55:49 +02:00
|
|
|
|
2022-09-02 08:26:37 +02:00
|
|
|
|
|
|
|
|
2022-08-31 15:55:49 +02:00
|
|
|
// LOGIC FOR TOGGLE SWITCHES (4-7)
|
|
|
|
|
2022-09-01 14:53:31 +02:00
|
|
|
// for (let i = 4; i < 8; i++) {
|
|
|
|
// // toggle button 2 state between 0 and 1
|
|
|
|
// if (msg.address == '/keys') {
|
|
|
|
// if (msg.args[i] <= threshold ) {
|
|
|
|
// if (switchHeld[i-4] == false) {
|
|
|
|
// switchValue[i-4] = !switchValue[i-4]
|
|
|
|
// switchHeld[i-4] = true
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// if (switchHeld[i-4]) {
|
|
|
|
// switchHeld[i-4] = false
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// msg.args.push(switchValue[i-4] ? 1 : 0)
|
|
|
|
// }
|
|
|
|
// }
|
2022-08-31 15:55:49 +02:00
|
|
|
//------------------------------------------------
|
2022-09-02 08:26:37 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-08-30 23:52:37 +02:00
|
|
|
// Convert quaternion diff to euler angle diff
|
|
|
|
if (msg.address == '/quaternionDiff') {
|
|
|
|
const euler = eulerFromQuaternion(msg.args, 'XYZ');
|
|
|
|
sendAll({
|
|
|
|
address: '/eulerDiff',
|
|
|
|
args: euler
|
|
|
|
}, info, oscWS, osclients)
|
|
|
|
|
|
|
|
eulerRotation[0] += euler[0]
|
|
|
|
eulerRotation[1] += euler[1]
|
|
|
|
eulerRotation[2] += euler[2]
|
|
|
|
|
|
|
|
sendAll({
|
|
|
|
address: '/euler',
|
|
|
|
args: eulerRotation
|
|
|
|
}, info, oscWS, osclients)
|
|
|
|
}
|
|
|
|
|
|
|
|
osclients.forEach( client => {
|
|
|
|
if (client && oscWS != client) {
|
|
|
|
// console.log("sending", msg, info)
|
|
|
|
client.send(msg)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if (scudp) {
|
2022-09-07 12:04:52 +02:00
|
|
|
if (osconoff == 1)
|
|
|
|
console.log("SENDING UDP", msg); scudp.send(msg)
|
|
|
|
|
2022-09-01 00:58:53 +02:00
|
|
|
//msg.slice(7,15);
|
|
|
|
//scudp.send(msg.slice(7,15))
|
2022-08-30 23:52:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const osclients = []
|
|
|
|
|
|
|
|
wss.on('connection', function (ws) {
|
|
|
|
console.log('new client connection')
|
|
|
|
const oscWS = new osc.WebSocketPort({
|
|
|
|
socket: ws,
|
|
|
|
metadata: false
|
|
|
|
});
|
|
|
|
|
|
|
|
// Vsi OSC sem grejo naprej na kliente OSC
|
|
|
|
oscWS.on('packet', (packet, info) => {
|
|
|
|
// Broadcast adjust msg
|
|
|
|
const [address, args] = packet
|
|
|
|
sendAll({ address, args}, info, oscWS, osclients)
|
|
|
|
})
|
|
|
|
|
|
|
|
oscWS.on('error', error => {
|
|
|
|
console.warn('Ignoring invalid OSC')
|
|
|
|
console.warn(error)
|
|
|
|
})
|
|
|
|
osclients.push(oscWS)
|
|
|
|
})
|
|
|
|
|