Search code examples
javascriptmeteornode-fibersnode-serialport

Fiber Error with npm package serial-port with meteor


I'm using the SerialPort npm package with meteor. I've used wrapAsync to list Serial ports but i don't know how to do with the serialPort.on method. I've an error when i want to inser datas in my Cars collection :

Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

Code :

Meteor.startup(function () {
  SerialPort = Meteor.npmRequire('serialport');
  // Wrap method SerialPort.list to call it Synchronously
  listSerialPorts = function(callback) {
    SerialPort.list(function (err, ports) {
      callback(null, ports);
    });  
  }
  // Reset cars collection

});



Meteor.methods({
  serialPortsRefresh: function () {
    // TODO : problem when several arduinos ?
    Config.remove({key:'serialPorts'});
    // Call SerialPort.list
    var asyncListSerialPorts = Meteor.wrapAsync(listSerialPorts);
    var resultsListSerialPorts = asyncListSerialPorts();
    // Insert results in database
    var configSerialPorts = {key: "serialPorts", value: resultsListSerialPorts[0].comName };
    Config.insert(configSerialPorts);
  },
  // Connect Serial port
  serialPortConnect: function (port) {
    // debugger;
    // serialPort = new SerialPort(port.value, {baudrate: 9600});
    serialPort = new SerialPort.SerialPort("/dev/ttyUSB0", {baudrate: 9600, parser: SerialPort.parsers.readline("\n")});
    // connectSerialPort(port);
    serialPort.on('open', function() {
        console.log('Port ' + port.value + ' open');
    });    
    serialPort.on('data', function(data) {
          dispatchMessages(data);
        //Watchdog.insert({key: "Receiving data", value: data })
    });  
    sendToArduino = function(message) {
      console.log(message);
      serialPort.write(message);
    };      
    dispatchMessages = function(data) {
      console.log(data);
      //Split data
      var datas = data.split(" ");
      if (datas[1] == "OK") {
        console.log("Car " + datas[0] + " is here");
        // Add car to database
        Cars.insert({
          cid: datas[0],
          active: true
        });
      }

    };    
  },  
  // Ping bridge
  ping: function () {
    sendToArduino("LED13\n");
  }    


});

Solution

  • The problem is that the callbacks you're passing to serialPort.on will not run within the same fiber as your method when they're invoked. In fact, they won't run within a fiber at all, unless you wrap them appropriately.

    Meteor.bindEnvironment runs the passed function within a fiber, but also copies in the surrounding environment, which is necessary as Meteor stores all sorts of variables within the current fiber which might be required to run the callback in question.

    So, if you do this it should work:

    serialPort.on('open', Meteor.bindEnvironment(function() {
        // Wrapping this one is unnecessary at present as it doesn't
        // do anything that needs to be run in a fiber, but you should
        // probably wrap it anyway so that you can safely add more code
        // if required.
        console.log('Port ' + port.value + ' open');
    }, function(e) {
        // This is an error-handler - you don't have to pass one, but
        // if you don't it can make debugging a nightmare.
        throw e;
    }));    
    serialPort.on('data', Meteor.bindEnvironment(function(data) {
        dispatchMessages(data);
        //Watchdog.insert({key: "Receiving data", value: data })
    }, function(e) {
        throw e;
    }));  
    

    Note that you also need to wrap callbacks within callbacks, etc., which can become quite verbose (and makes putting something like var mBE = Meteor.bindEnvironment at the top of your methods file quite a good idea).