hybridgroup / cylon-firmata

Cylon adaptor for the Firmata protocol
http://cylonjs.com
Other
45 stars 10 forks source link

Crash with Unhandled error: Serialport not open #32

Closed cylinderStudio closed 9 years ago

cylinderStudio commented 9 years ago

I'm using the leapmotion and firmata Cylon modules. Controlling two servos on an Arduino based on hand movements with the Leap Motion. (Note: I was unable to install Firmata using Gort, so I installed the Standard Firmata from the Arduino API.)

Everything starts up and runs fine if I do a series of slow or normal/medium speed hand movements. But if I do hand movements that cause a servo to make a rapid series of angle changes, I get a crash:

events.js:72 throw er; // Unhandled 'error' event ^ Error: Serialport not open. at SerialPortFactory.SerialPort.write (/Users/wys3/node_modules/cylon-firmata/node_modules/firmata/node_modules/serialport/serialport.js:211:17) at Board.pinMode (/Users/wys3/node_modules/cylon-firmata/node_modules/firmata/lib/firmata.js:601:13) at Adaptor.pinMode (/Users/wys3/node_modules/cylon-firmata/lib/firmata.js:117:14) at Adaptor.servoWrite (/Users/wys3/node_modules/cylon-firmata/lib/firmata.js:90:8) at Servo.angle (/Users/wys3/node_modules/cylon-gpio/lib/servo.js:57:19) at Device. (/Users/wys3/Documents/cylonjs/leap_arduino.js:28:46) at Device.emit (events.js:95:17) at Connection. (/Users/wys3/node_modules/cylon-leapmotion/node_modules/cylon/lib/basestar.js:59:22) at Connection.emit (events.js:95:17) at Leap.loop.frame (/Users/wys3/node_modules/cylon-leapmotion/lib/adaptor.js:39:12) at emit (events.js:95:17)

edgarsilva commented 9 years ago

This seems to be an issue with node-serialport itself, for what you are saying it is probably not handling all the commands you are sending to the Arduino fast enough, and the serial port locks.

Maybe you could try catching that error, displaying the info and handling it with a timeout or just discarding it.

If that does not work let me know, I can add the necessary code to bubble-up the so you can catch it, in fact I'll do that right away, since it seems useful to have it anyways.

Let me know how it goes, also are you on OSX?

cylinderStudio commented 9 years ago

Oops, yes I am on OSX.

I think you are right about the serial port choking. I am starting to think it's because I'm attempting to rotate angles on both servos at the frame rate of the LeapMotion (which, according to the Visualizer with debugging turned on, is around 100 fps). I'm not seeing a way to set the frame rate of LeapMotion using cylon-leapmotion, but I'll attempt to introduce delays or slowing down the angle changes I'm making and see if that fixes the problem.

cylinderStudio commented 9 years ago

Well, nuts. Instead of making simultaneous and constant angle changes on the servos on the 100fps frame rate, I tried doing it every 100 milliseconds. But it still craps out if I make a long series of very rapid changes.

edgarsilva commented 9 years ago

I'm working on adding err handling to the drivers for this, that would at least gives a way to handle this type of err in the code, the serialport should not choke up with that few commands send to it, my guess is that it is the arduino the one not handling the instructions fast enough, which causes the serialport to lose communication.

Also the servo will never be that fast to change angle, it will probably never reach the prev angle if it is too long, but if you are changing it gradually it should work as expected.

I have all the hardware to test this, could you share the code you are using here so I can give it a try?

cylinderStudio commented 9 years ago

Yeah, I'm OK with the servos not reaching the angle asked for. I was aiming for sort of a constant adjustment scenario where the servos are constantly reacting to the hand movements, and only reach an angle if the hand is left in that position. (On another user's suggestion, I tried limiting the instructions to 100hz as opposed to the LeapMotion's 'frame' event, which appeared to improve the situation, but didn't solve the problem.)

Thanks for looking into this. Here's a gist: https://gist.github.com/cylinderStudio/03d12ec18ef9a62ae481

edgarsilva commented 9 years ago

Ok, I did two thinkgs and seems to work a bit better, but I modified your example to use LEDs since servos require too much power and I don't have an external power source right now. First I change the Leapmotion settings to use LowResource Mode, then I remove your timing settings to make it as fast as possible.

The only issue I saw was the leapmotion stop reading the hand and then resume a few seconds later, it even disappear from the visualizer.

var cylon = require('cylon');

cylon.robot({
  connections: [
    { name: 'leapmotion', adaptor: 'leapmotion', port: '127.0.0.1:6437'},
    {name: 'arduino', adaptor: 'firmata', port: '/dev/ttyACM0'}
  ],

  devices: [
    { name: 'leapmotion', driver: 'leapmotion', connection: 'leapmotion'},
    // {name: 'led', driver: 'led', pin: 13, connection: 'arduino'},
    {name: 'servo1', driver: 'servo', pin: 9, connection: 'arduino'},
    {name: 'servo2', driver: 'servo', pin: 10, connection: 'arduino'}
  ],

  work: function(my) {
    my.leapmotion.on('frame', function(frame) {
      var hand = null,
          roll = 0,
          pitch = 0;

      if (frame.hands.length > 0) {
        hand = frame.hands[0];
        roll = hand.roll() * (180/Math.PI);
        roll = Math.floor(roll + 90);
        pitch = hand.pitch() * (180/Math.PI);
        pitch = Math.floor(pitch + 90);

        console.log('pitch:', pitch);
        console.log('roll:', roll);

        //my.led.turnOn();
        if (roll > -1 && roll < 255) { my.servo1.angle(roll); }
        if (pitch > -1 && pitch < 255) { my.servo2.angle(pitch); }
      } else {
        // my.led.turnOff();
        my.servo1.angle(90);
        my.servo2.angle(90);
      }
    });
  }
}).start();

Give it a try let me know how it goes, I'm checking how to make it only send a command to the servo after it is done with the prev one.

cylinderStudio commented 9 years ago

Ok, gave that a try. Unfortunately didn't help. I didn't see the LeapMotion lose track of the hands at all on Low Resource Mode, but it still crashes on a series of rapid hand movements.

I hope I'm not trying to do something with servos or Arduino that they aren't technically capable of doing, but my real aim really is to have the servo in question change direction and start moving to a new angle when the hand rotates, even if the previous angle requested from a prior hand movement wasn't reached yet.

edgarsilva commented 9 years ago

Yeah taht sounds feasible, I've done the same with four leds changing the brightness from 0-255, which is pretty much the same example.

Ok let me try to replicate, I can't replicate on my computer not sure if it might be an issue with the libusb lib not working properly with node-serialport on macs or something of the sort.

Anyways let me give it another try, trying to replicate and see if I can find anything.

cylinderStudio commented 9 years ago

Is there any info I can provide about libusb on my machine that might help?

edgarsilva commented 9 years ago

Yeah, but I do not have a mac or any OSx to try, I'll ask one of the other guys in the team with a mac, see if they can reproduce. They should know better what info might be useful.

cylinderStudio commented 9 years ago

OK. By the way, I got some advice & code that showed me that the same issue crops up when this same scenario is coded with Johnny Five (with the leapjs node module), which I guess also uses node-serialport.

edgarsilva commented 9 years ago

You are correct it also uses node-serialport, then I think our assumptions might be correct and this is an issue with the libusb libraries in OSx.

Anyhow we'll continue to debug, see if we can find a workaround or another fix.

cylinderStudio commented 9 years ago

This might also be useful: I just modified the code to only use one servo, and I can't make the serialport crash no matter how rapidly I change hand position/send angles to the servo. Two servos is a dealbreaker.

edgarsilva commented 9 years ago

mmm... I;ve used as many as 4 leds connected to an arduino and sending pwm commands, which is pretty similar to the servos, with no issues... maybe we can try something with the firmata commands or the timing, the fps we send to the board.

cylinderStudio commented 9 years ago

Forgot to mention, I have an Arduino UNO Rev 3, in case that matters at all.

cylinderStudio commented 9 years ago

This turns out to be an issue with power for multiple servos. When I use 9v battery to power the experience, it works fine. The wall-wart adapter is the culprit. I will likely have to look at voltage regulators + capacitors to reduce noise.