Snap4Arduino
Snap4Arduino copied to clipboard
Show only the serial ports where an Arduino board is connected
From @bromagosa on July 31, 2014 9:4
At s4aBlocks.js, change this function:
function(){ serialport.list(function (err, ports) { if(ports){ ports.forEach(function(each) { portList[each.comName] = each.comName })}})}
So that it filters out only the ports with an Arduino plugged in. It is pretty trivial to do, each serialport object has an attribute that can help with that (can't remember which one but it's easy to find out again).
Copied from original issue: edutec/Snap4Arduino-old-huge#17
From @elaval on July 31, 2014 9:22
I have tried this code (which I borrowed from johnny-five.js) and it works well:
SpriteMorph.prototype.getSerialPorts = function(callback) {
// Use node.js serialport to get port lists
var SerialPort = require("serialport");
// Get a list of active serial ports
SerialPort.list(function(err, result) {
if (err) {
callback(err, ports);
return;
} else {
var ports,
length;
// Filter the ports to select only those 'arduino' compatible
ports = result.filter(function(val) {
var available = true;
// Get compatible ports -> Match only ports that Arduino cares about
// ttyUSB#, cu.usbmodem#, COM#
var rport = /usb|acm|^com/i;
if (!rport.test(val.comName)) {
available = false;
}
// Check if the port is in the world's usedBoards list
if (world.hasOwnProperty('usedBoards') && world.usedBoards.indexOf(val.comName) > -1) {
available = false;
}
return available;
})
// Maps the array of ports (objects) into an array with the port path names (strings)
.map(function(val) {
return val.comName;
});
// Get number of compatible ports
length = ports.length;
// If no ports are detected when scanning /dev/, then there is
// nothing left to do and we can safely exit the program
if (!length) {
// Alert user that no devices were detected
console.log('Board', 'No USB devices detected');
// Return (not that it matters, but this is a good way
// to indicate to readers of the code that nothing else
// will happen in this function)
var newerr = new Error('No Arduino device found on USB ports');
newerr.name = 'NoUSBError';
callback(newerr);
return
} else {
// Return available ports
callback(err, ports)
}
}
}.bind(this));
}
By the way ... here I am using a "global" world array for keeping track of those boards that have been already connected:
world.usedBoards = []
This way we do not display board that have been already opened in other sprites. Do you agree on using a "global" variable on the "world" object for this?
From @bromagosa on July 31, 2014 10:4
Wow! This looks a bit too complex! I was thinking of just using a simple regex to check if that particular port had a field with .*arduino.* in it.
I misstyped the title of this issue. What I meant is, in the [connect Arduino at [port v]] block, show only these ports having the arduino string in the pnpId field, like this one:
{ comName: '/dev/ttyACM0',
manufacturer: undefined,
pnpId: 'usb-Arduino__www.arduino.cc__Arduino_Uno_649353432333517062D1-if00' }
This way, if you connect any other serial device to your USB, it won't show up in the list.
From @elaval on July 31, 2014 10:14
I am afraid that the port info riven by serial port is not standard en every platform. In the mac you get this for an Arduino Uno:
{ comName: "/dev/cu.usbmodemfd121"
locationId: "0xfd120000"
manufacturer: "Arduino (www.arduino.cc)"
pnpId: ""
productId: "0x0043"
serialNumber: "95333353936351315002"
vendorId: "0x2341"}
"If" they are consistent for different boards in each platform (i.e. on Mac you always get "Arduino" in the manufacturer) we could check for the 3 platforms.
I have no idea what port info you get with different Arduino Boards (I 'he just tried with UNO), but is some models do not follow the pattern, we might have some difficulties.
From @elaval on July 31, 2014 10:22
By the way ... the above code is not THAT complex ... it just has a lot of comments and callbacks for error detection :-)
From @bromagosa on July 31, 2014 10:24
I know it's ugly, but I've just tried this:
setInterval( function(){
serialport.list(function (err, ports) {
if(ports){
ports.forEach( function(each) {
if(JSON.stringify(each).match(/.*duino.*/gi)) {
portList[each.comName] = each.comName
}
})
}
})}, 500);
So we just stringify the whole port, with all of its fields, and if the string duino is found in there, we add it to the list. What do you think?
From @bromagosa on July 31, 2014 10:27
Yeah you're right, it's not that complex. We should remove all errors for "no Arduino found", because these would pop up all the time if the user starts Snap4Arduino and hasn't yet plugged a board, so in the end it's pretty much the same code :)
From @elaval on July 31, 2014 10:35
I would love to have an .duino. such as the one you propose. My only concern is if there are exceptions with some boards that do not provide the info.
I am sure that someone has worked on this before ... I am just reading at this page: https://code.google.com/p/arduino/issues/detail?id=223
It might give us some pointers.
From @bromagosa on July 31, 2014 10:42
Right... I think the only absolutely certain way to be sure would be to try to connect to all ports, which would take way too much time for a port list function...
Maybe we should just leave it as it is now and see if we find a better solution in the future?
From @elaval on July 31, 2014 10:56
Well ... checking for "/usb|acm|^com/i" in the comName at least filters out those ports which are not used for arduinos (e.g. Bluetooth ports). They have used this method for auto-detection of arduino boards for a long time here (johnny-five arduino nom package):
https://github.com/rwaldron/johnny-five/blob/master/lib/board.js
So I guess it is relatively stable (and works for most use cases).
BTW I was generating the "Arduino not found" error because I was exploring a functionality for trying to auroconnect when the user pushed a button (so I wanted to give feedback), but I guess that we can also check if the port list is empty.
Do we need to be polling for the PortList every 500ms??? If we implement an auto detect, we would be trying to connect with some interval, and we could get the port list in each attempt (and stop polling when auto detect is not active).
I understand that you also want to have the port list for displaying the port choices in the "connect arduino at" block. But maybe we can ask for the Port list when the user activates the block (Is this possible?)
From @bromagosa on July 31, 2014 11:21
Asking for the port list when the user unfolds the dropdown was my initial idea, but everything is async in firmataJS so that's why I needed to poll for it all the time... and I couldn't find a way to turn it into a sync function without the need for an extra, OS-specific, package (like fibers).
It is really hard to filter the USB device name because there are probably tens if not hundreds variants of Arduino boards, moreover Snap4Arduino works with any board with Firmata support, not just Arduino.
We might want to restrict ourselves with just detecting duino. It will be best to at least open the port and query for Firmata support, and firmware version. That way, any generic board that has Firmata loaded will be able to work with Snap4Arduino.