From f4b75d7398f90d977a19059caf65b319b66164e3 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 18 Apr 2016 21:26:45 -0400 Subject: [PATCH] Make mcopy-arduino work with multiple devices TODO: partition the queue and locks so commands can overlap when passing to different devices --- app/js/app.js | 32 +++++--- app/lib/mcopy-arduino.js | 161 +++++++++++++++++++++++++++------------ app/main.js | 100 +++++++++++++++++++----- app/src/index.js | 32 +++++--- 4 files changed, 234 insertions(+), 91 deletions(-) diff --git a/app/js/app.js b/app/js/app.js index 9c8e9bb..a86ccb8 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -2565,7 +2565,7 @@ log.info = function (action, service, status, time) { 'use strict'; var obj = log.display(action, service, status, time); log.report(obj); - console.log(obj); + //console.log(obj); }; /****** @@ -2588,13 +2588,16 @@ seq.run = function () { } if (seq.stop()) { $('.row input').removeClass('h'); - console.log('Sequence stepped'); + $('#numbers div').removeClass('h'); + //console.log('Sequence stepped'); + log.info('Sequence stopped', 'SERIAL', true); return false; } if (seq.i <= mcopy.state.sequence.arr.length && c !== undefined) { log.info('Step ' + seq.i + ' command ' + c, 'SEQUENCE', true); //gui action $('.row input').removeClass('h'); + $('#numbers div').removeClass('h'); $('.row input[x=' + seq.i + ']').addClass('h'); $('#numbers div[x=' + seq.i + ']').addClass('h'); if (c === 'CF'){ @@ -2617,6 +2620,7 @@ seq.run = function () { if (mcopy.loopCount < mcopy.loop) { log.info('Loop ' + mcopy.loopCount + ' completed', 'SEQUENCE', true); $('.row input').removeClass('h'); + $('#numbers div').removeClass('h'); seq.i = 0; seq.run(); } else { @@ -2733,7 +2737,7 @@ cmd.proj_forward = function (callback) { if (callback) { callback(ms); } }; if (!mcopy.state.projector.direction) { - proj.set(true, function (ms) { + proj.set(true, function (ms) { setTimeout(function () { proj.move(res); }, mcopy.cfg.arduino.serialDelay); @@ -2766,24 +2770,28 @@ cmd.cam_forward = function (rgb, callback) { 'use strict'; var res = function (ms) { gui.updateState(); - light.set([0, 0, 0], function () { - if (callback) { callback(ms); } - }); + setTimeout(function () { + light.set([0, 0, 0], function () { + if (callback) { callback(ms); } + }); + }, mcopy.cfg.arduino.serialDelay); }; if (!mcopy.state.camera.direction) { cam.set(true, function () { - setTimeout(function () { + setTimeout( function () { light.set(rgb, function () { - cam.move(res); + setTimeout( function () { + cam.move(res); + }, mcopy.cfg.arduino.serialDelay); }); }, mcopy.cfg.arduino.serialDelay); }); } else { - setTimeout(function () { - light.set(rgb, function () { + light.set(rgb, function () { + setTimeout(function () { cam.move(res); - }); - }, mcopy.cfg.arduino.serialDelay); + }, mcopy.cfg.arduino.serialDelay); + }); } }; cmd.black_forward = function (callback) { diff --git a/app/lib/mcopy-arduino.js b/app/lib/mcopy-arduino.js index e5cb0c4..56f7a5a 100644 --- a/app/lib/mcopy-arduino.js +++ b/app/lib/mcopy-arduino.js @@ -7,7 +7,7 @@ var serialport = require('serialport'), Arduino handlers *******/ mcopy.arduino = { - path : '', + path : {}, known: [ '/dev/tty.usbmodem1a161', '/dev/tty.usbserial-A800f8dk', @@ -16,19 +16,24 @@ mcopy.arduino = { '/dev/tty.usbserial-a900f6de', '/dev/tty.usbmodem1a141' ], - serial : {}, + serial : { + connect : {}, + projector : {}, + camera : {} + }, baud : 57600, queue : {}, timer : 0, lock : false }; -mcopy.arduino.init = function (callback) { +mcopy.arduino.enumerate = function (callback) { 'use strict'; console.log('Searching for devices...'); var cmd = 'ls /dev/tty.*'; exec(cmd, function (e, std) { var devices = std.split('\n'), matches = []; + if (e) { return callback(e); } devices.pop(); for (var i = 0; i < devices.length; i++) { if (devices[i].indexOf('usbserial') !== -1 @@ -37,25 +42,20 @@ mcopy.arduino.init = function (callback) { } } if (matches.length === 0) { - //console.log('No USB devices found.'); if (callback) { callback('No USB devices found'); } } else if (matches.length > 0) { - //console.log('Found ' + matches[0]); - mcopy.arduino.path = matches[0]; - //once connected to the arduino - //start user interface - if (callback) { callback(null, mcopy.arduino.path); } + if (callback) { callback(null, matches); } } }); }; //commands which respond to a sent char -mcopy.arduino.send = function (cmd, res) { +mcopy.arduino.send = function (serial, cmd, res) { 'use strict'; if (!mcopy.arduino.lock) { mcopy.arduino.lock = true; mcopy.arduino.queue[cmd] = res; setTimeout(function () { - mcopy.arduino.serial.write(cmd, function (err, results) { + mcopy.arduino.serial[serial].write(cmd, function (err, results) { if (err) { console.log(err); } mcopy.arduino.lock = false; mcopy.arduino.timer = new Date().getTime(); @@ -64,16 +64,21 @@ mcopy.arduino.send = function (cmd, res) { } }; //send strings, after char triggers firmware to accept -mcopy.arduino.string = function (str) { +mcopy.arduino.string = function (serial, str) { 'use strict'; setTimeout(function () { - mcopy.arduino.serial.write(str, function (err, results) { - if (err) { console.log(err); } - //console.log('sent: ' + str); - }); + if (typeof mcopy.arduino.serial[serial].fake !== 'undefined' + && mcopy.arduino.serial[serial].fake) { + mcopy.arduino.serial[serial].string(str); + } else { + mcopy.arduino.serial[serial].write(str, function (err, results) { + if (err) { console.log(err); } + //console.log('sent: ' + str); + }); + } }, mcopy.cfg.arduino.serialDelay); }; -//with same over serial when done +//respond with same char over serial when done mcopy.arduino.end = function (data) { 'use strict'; var end = new Date().getTime(), @@ -81,59 +86,115 @@ mcopy.arduino.end = function (data) { if (mcopy.arduino.queue[data] !== undefined) { mcopy.arduino.lock = false; console.log('Command ' + data + ' took ' + ms + 'ms'); - mcopy.arduino.queue[data](ms); - delete mcopy.arduino.queue[data]; //add timestamp? + mcopy.arduino.queue[data](ms); //execute callback + delete mcopy.arduino.queue[data]; } else { - console.log('Received stray "' + data + '" from ' + mcopy.arduino.path); //silent to user + console.log('Received stray "' + data + '"'); //silent to user } }; -mcopy.arduino.connect = function (callback) { +mcopy.arduino.connect = function (serial, device, confirm, callback) { 'use strict'; - console.log('Connecting to ' + mcopy.arduino.path + '...'); - mcopy.arduino.serial = new SerialPort(mcopy.arduino.path, { - baudrate: mcopy.cfg.arduino.baud, - parser: serialport.parsers.readline("\n") + mcopy.arduino.path[serial] = device; + mcopy.arduino.serial[serial] = new SerialPort(mcopy.arduino.path[serial], { + baudrate: mcopy.cfg.arduino.baud, + parser: serialport.parsers.readline("\n") }, false); - mcopy.arduino.serial.open(function (error) { + mcopy.arduino.serial[serial].open(function (error) { if (error) { + if (callback) { callback(error); } return console.log('failed to open: '+ error); } else { - console.log('Opened connection with ' + mcopy.arduino.path); - mcopy.arduino.serial.on('data', function (data) { - data = data.replace('\r', ''); - mcopy.arduino.end(data); - }); - setTimeout(function () { - console.log('Verifying firmware...'); - mcopy.arduino.send(mcopy.cfg.arduino.cmd.connect, function () { - console.log('Firmware verified'); - console.log('Optical printer ready!'); - if (callback) { callback(); } + console.log('Opened connection with ' + mcopy.arduino.path[serial]); + if (!confirm) { + mcopy.arduino.serial[serial].on('data', function (data) { + data = data.replace('\r', ''); + mcopy.arduino.end(data); }); - }, 2000); + } else { + mcopy.arduino.serial[serial].on('data', function (data) { + data = data.replace('\r', ''); + mcopy.arduino.confirmEnd(data); + }); + } + if (callback) { + callback(null, mcopy.arduino.path[serial]); + } } }); }; -mcopy.arduino.fakeConnect = function (callback) { +mcopy.arduino.confirmExec = {}; +mcopy.arduino.confirmEnd = function (data) { + 'use strict'; + if (data === mcopy.cfg.arduino.cmd.connect + || data === mcopy.cfg.arduino.cmd.proj_identifier + || data === mcopy.cfg.arduino.cmd.cam_identifier) { + mcopy.arduino.confirmExec(null, data); + mcopy.arduino.confirmExec = {}; + } +}; +mcopy.arduino.verify = function (callback) { + 'use strict'; + mcopy.arduino.confirmExec = function (err, data) { + if (data === mcopy.cfg.arduino.cmd.connect) { + callback(null, true); + } + }; + setTimeout(function () { + mcopy.arduino.serial['connect'].write(mcopy.cfg.arduino.cmd.connect, function (err, results) { + if (err) { + return console.log(err); + } + }); + }, mcopy.cfg.arduino.serialDelay); +}; +mcopy.arduino.distinguish = function (callback) { + 'use strict'; + mcopy.arduino.confirmExec = function (err, data) { + if (data === mcopy.cfg.arduino.cmd.proj_identifier) { + callback(null, 'projector'); + } else if (data === mcopy.cfg.arduino.cmd.cam_identifier) { + callback(null, 'camera'); + } + }; + setTimeout(function () { + mcopy.arduino.serial['connect'].write(mcopy.cfg.arduino.cmd.mcopy_identifier, function (err, results) { + if (err) { + return console.log(err); + } + }); + }, mcopy.cfg.arduino.serialDelay); +}; + +mcopy.arduino.close = function (callback) { + 'use strict'; + mcopy.arduino.serial['connect'].close(function (err) { + if (callback) { + callback(err); + } + }); +}; + +mcopy.arduino.fakeConnect = function (serial, callback) { console.log('Connecting to fake arduino...'); - mcopy.arduino.serial = { + mcopy.arduino.serial[serial] = { write : function (cmd, res) { var t = { - c : mcopy.cfg.arduino.cam.time + mcopy.cfg.arduino.cam.delay, - p : mcopy.cfg.arduino.proj.time + mcopy.cfg.arduino.proj.delay - }, - timeout = t[cmd]; - if (typeof timeout === 'undefined') timeout = 10; - mcopy.arduino.timer = +new Date(); - setTimeout(function () { - mcopy.arduino.end(cmd); - }, timeout); + c : mcopy.cfg.arduino.cam.time + mcopy.cfg.arduino.cam.delay, + p : mcopy.cfg.arduino.proj.time + mcopy.cfg.arduino.proj.delay + }, + timeout = t[cmd]; + if (typeof timeout === 'undefined') timeout = 10; + mcopy.arduino.timer = +new Date(); + setTimeout(function () { + mcopy.arduino.end(cmd); + }, timeout); }, string : function (str) { //do nothing return true; - } + }, + fake : true }; console.log('Connected to fake arduino! Not real! Doesn\'t exist!'); if (callback) callback(); diff --git a/app/main.js b/app/main.js index d526af1..815ca5a 100644 --- a/app/main.js +++ b/app/main.js @@ -13,7 +13,9 @@ var electron = require('electron'), mcopy = {}, mainWindow, mscript, - arduino; + arduino, + projector, + camera; mcopy.cfg = {}; mcopy.cfgFile = './data/cfg.json'; @@ -43,21 +45,85 @@ var init = function () { arduino = require('./lib/mcopy-arduino.js')(mcopy.cfg); mscript = require('./lib/mscript.js'); + setTimeout(function () { - arduino.init(function (err, device) { - if (err) { - log.info(err, 'SERIAL', false, true); - arduino.fakeConnect(function () { - log.info('Connected to fake USB device', 'SERIAL', true, true); + arduino.enumerate(enumerateDevices); + }, 1000); +}; + +var enumerateDevices = function (err, devices) { + 'use strict'; + if (err) { + log.info(err, 'SERIAL', false, true); + arduino.fakeConnect('projector', function () { + log.info('Connected to fake PROJECTOR device', 'SERIAL', true, true); + }); + arduino.fakeConnect('camera', function () { + log.info('Connected to fake CAMERA device', 'SERIAL', true, true); + }); + } else { + log.info('Found ' + devices.length + ' USB devices', 'SERIAL', true, true); + distinguishDevices(devices); + } +}; + +var distinguishDevice = function (device, callback) { + 'use strict'; + var connectCb = function (err, device) { + if (err) { + return console.error(err); + } + setTimeout(function () { + arduino.verify(verifyCb); + }, 2000); + }, + verifyCb = function (err, success) { + if (err) { + return console.error(err); + } + setTimeout(function () { + arduino.distinguish(distinguishCb); + }, 1000); + }, + distinguishCb = function (err, type) { + if (err) { + return console.error(err); + } + if (callback) { callback(err, type); } + } + arduino.connect('connect', device, true, connectCb); +}; + +//Cases for 1 or 2 arduinos connected +var distinguishDevices = function (devices) { + 'use strict'; + var distinguishOne = function (err, type) { + arduino.close(function () { + if (type === 'projector') { + arduino.connect('projector', devices[0], false, function () { + log.info('Connected to ' + devices[0] + ' as PROJECTOR', 'SERIAL', true, true); }); - } else { - log.info('Found device ' + device, 'SERIAL', true, true); - arduino.connect(function () { - log.info('Connected to device ' + device, 'SERIAL', true, true); + if (devices.length === 1) { + arduino.fakeConnect('camera', function () { + log.info('Connected to fake CAMERA device', 'SERIAL', true, true); + }); + } + } else if (type === 'camera') { + arduino.connect('camera', devices[0], false, function () { + log.info('Connected to ' + devices[0] + ' as CAMERA', 'SERIAL', true, true); }); + if (devices.length === 1) { + arduino.fakeConnect('projector', function () { + log.info('Connected to fake PROJECTOR device', 'SERIAL', true, true); + }); + } } }); - }, 1000); + }, + distinguishTwo = function (err, type) { + + }; + distinguishDevice(devices[0], distinguishOne); }; var createMenu = function () { @@ -233,10 +299,10 @@ light.listen = function () { light.set = function (rgb, id) { 'use strict'; var str = rgb.join(','); - arduino.send(mcopy.cfg.arduino.cmd.light, function (ms) { + arduino.send('projector', mcopy.cfg.arduino.cmd.light, function (ms) { light.end(rgb, id, ms); }); - arduino.string(str); + arduino.string('projector', str); }; light.end = function (rgb, id, ms) { 'use strict'; @@ -261,13 +327,13 @@ proj.set = function (dir, id) { cmd = mcopy.cfg.arduino.cmd.proj_backward; } proj.state.dir = dir; - arduino.send(cmd, function (ms) { + arduino.send('projector', cmd, function (ms) { proj.end(cmd, id, ms); }); }; proj.move = function (frame, id) { 'use strict'; - arduino.send(mcopy.cfg.arduino.cmd.projector, function (ms) { + arduino.send('projector', mcopy.cfg.arduino.cmd.projector, function (ms) { proj.end(mcopy.cfg.arduino.cmd.projector, id, ms); }); }; @@ -319,13 +385,13 @@ cam.set = function (dir, id) { cmd = mcopy.cfg.arduino.cmd.cam_backward; } cam.state.dir = dir; - arduino.send(cmd, function (ms) { + arduino.send('camera', cmd, function (ms) { cam.end(cmd, id, ms); }); }; cam.move = function (frame, id) { 'use strict'; - arduino.send(mcopy.cfg.arduino.cmd.camera, function (ms) { + arduino.send('camera', mcopy.cfg.arduino.cmd.camera, function (ms) { cam.end(mcopy.cfg.arduino.cmd.camera, id, ms); }); }; diff --git a/app/src/index.js b/app/src/index.js index bae903c..05d1c80 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -109,7 +109,7 @@ log.info = function (action, service, status, time) { 'use strict'; var obj = log.display(action, service, status, time); log.report(obj); - console.log(obj); + //console.log(obj); }; /****** @@ -132,13 +132,16 @@ seq.run = function () { } if (seq.stop()) { $('.row input').removeClass('h'); - console.log('Sequence stepped'); + $('#numbers div').removeClass('h'); + //console.log('Sequence stepped'); + log.info('Sequence stopped', 'SERIAL', true); return false; } if (seq.i <= mcopy.state.sequence.arr.length && c !== undefined) { log.info('Step ' + seq.i + ' command ' + c, 'SEQUENCE', true); //gui action $('.row input').removeClass('h'); + $('#numbers div').removeClass('h'); $('.row input[x=' + seq.i + ']').addClass('h'); $('#numbers div[x=' + seq.i + ']').addClass('h'); if (c === 'CF'){ @@ -161,6 +164,7 @@ seq.run = function () { if (mcopy.loopCount < mcopy.loop) { log.info('Loop ' + mcopy.loopCount + ' completed', 'SEQUENCE', true); $('.row input').removeClass('h'); + $('#numbers div').removeClass('h'); seq.i = 0; seq.run(); } else { @@ -277,7 +281,7 @@ cmd.proj_forward = function (callback) { if (callback) { callback(ms); } }; if (!mcopy.state.projector.direction) { - proj.set(true, function (ms) { + proj.set(true, function (ms) { setTimeout(function () { proj.move(res); }, mcopy.cfg.arduino.serialDelay); @@ -310,24 +314,28 @@ cmd.cam_forward = function (rgb, callback) { 'use strict'; var res = function (ms) { gui.updateState(); - light.set([0, 0, 0], function () { - if (callback) { callback(ms); } - }); + setTimeout(function () { + light.set([0, 0, 0], function () { + if (callback) { callback(ms); } + }); + }, mcopy.cfg.arduino.serialDelay); }; if (!mcopy.state.camera.direction) { cam.set(true, function () { - setTimeout(function () { + setTimeout( function () { light.set(rgb, function () { - cam.move(res); + setTimeout( function () { + cam.move(res); + }, mcopy.cfg.arduino.serialDelay); }); }, mcopy.cfg.arduino.serialDelay); }); } else { - setTimeout(function () { - light.set(rgb, function () { + light.set(rgb, function () { + setTimeout(function () { cam.move(res); - }); - }, mcopy.cfg.arduino.serialDelay); + }, mcopy.cfg.arduino.serialDelay); + }); } }; cmd.black_forward = function (callback) {