Skip to content

Instantly share code, notes, and snippets.

@fivdi
Last active August 29, 2015 14:14
Show Gist options
  • Save fivdi/2e0529983a15bdb54daa to your computer and use it in GitHub Desktop.
Save fivdi/2e0529983a15bdb54daa to your computer and use it in GitHub Desktop.
answer

Here are the methods that a J5 IO-Plugin is expected to implement:

  • i2cWrite(address, inBytes)
  • i2cWrite(address, register, inBytes)
  • i2cWriteReg(address, register, value)
  • i2cRead(address, register, bytesToRead, handler)
  • i2cRead(address, bytesToRead, handler)
  • i2cReadOnce(address, register, bytesToRead, handler)
  • i2cReadOnce(address, bytesToRead, handler)

Here are the functions available in WiringPi:

  • int wiringPiI2CSetup(int devId);
  • int wiringPiI2CRead(int fd);
  • int wiringPiI2CWrite(int fd, int data);
  • int wiringPiI2CWriteReg8(int fd, int reg, int data);
  • int wiringPiI2CWriteReg16(int fd, int reg, int data);
  • int wiringPiI2CReadReg8(int fd, int reg);
  • int wiringPiI2CReadReg16(int fd, int reg);

WiringPi has functions to read or write a byte, read or write a byte register, and read or write a word register. Note that WiringPi doesn't have functions for reading or writing blocks of data, but most J5 methods are block methods.

The following table shows the direct mapping from J5 methods to WiringPi functions:

Johnny-Five WiringPi
i2cWrite(address, inBytes) no block write available
i2cWrite(address, register, inBytes) no block write to register available
i2cWriteReg(address, register, value) wiringPiI2CWriteReg8
i2cRead(address, register, bytesToRead, handler) no block read available
i2cRead(address, bytesToRead, handler) no block read available
i2cReadOnce(address, register, bytesToRead, handler) no block read available
i2cReadOnce(address, bytesToRead, handler) no block read available

Perhaps it's possible to implement the J5 block methods by calling WiringPi byte/word functions multiple times. If not, new functions could be implemented to perform the task.

The next question is "which thread does the code that reads and writes I2C data run in?" There are two possible answers here:

  • In the same thread as the JavaScript code
  • In one or more worker threads

The worker thread option is available because Raspi-io uses C++ addons.

In Galileo-IO the code that reads and writes I2C data runs in the JavaScript thread. This is because Galileo-IO uses mraa under the covers and mraa only has blocking synchronous calls on offer. AFAIK, this is something Rick isn't to happy about. On the other hand, this make things a lot more deterministic and a lot easier to reason about. For example, because only one thread is involved, only one piece of code can be accessing the I2C bus at any point in time.

In BeagleBone-IO the code that reads I2C data runs in worker threads. The code that writes I2C data runs in the JavaScript thread. BeagleBone-IO uses the i2c-bus package under the covers and i2c-bus offers both blocking synchronous calls and non-blocking asynchronous calls. This means that multiple threads can be accessing the I2C bus at the same point in time. Even deeper under the covers, i2c-bus uses the Linux ioctl call. Multiple threads will end up calling this function at practically the same time. However, Linux has things organized so that these calls will not be stopped half way through. Each call will run to completion before the next call runs, perhaps in another thread.

In an early version of the BeagleBone-IO implementation there was code similar to the following in the continuous i2cRead method:

BeagleBone.prototype.i2cRead = function(address, register, bytesToRead, handler) {
  ...
  setTimeout(function read() {
    ...
    this.i2cWrite(address, register);
    _i2c.i2cRead(address, bytesToRead, data, function(err) {
      ...
      setTimeout(read.bind(this), _i2cDelay);
    }.bind(this));
  }.bind(this), _i2cDelay);

  return this;
};

In a test program, this continuous read method was being called for two devices at the same time.

The call to this.i2cWrite is a blocking synchronous call and runs in the JavaScript thread, the call to _i2c.i2cRead results in a non-blocking worker thread reading the data asynchronously.

Between the call to this.i2cWrite and _i2c.i2cRead, there were sometimes context switches to worker threads and the I2C bus was accessed by the worker threads. This access to the bus messed up the prerequisites for the later call to _i2c.i2cRead.

In the current BeagleBone-IO implementation the code in the continuous i2cRead is similar to this:

BeagleBone.prototype.i2cRead = function(address, register, bytesToRead, handler) {
  ...
  setTimeout(function read() {
    ...
    _i2c.readI2cBlock(address, register, bytesToRead, data,  function(err) {
      ...
      setTimeout(read.bind(this), _i2cDelay);
    }.bind(this));
  }.bind(this), _i2cDelay);

  return this;
};

The two calls to this.i2cWrite and _i2c.i2cRead were replaced by a single call to _i2c.readI2cBlock and everything works as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment