Show:

File: src/core/I2CBase.js

/**
 * Copyright (c) 2011-2014 Jeff Hoefs <soundanalogous@gmail.com>
 * Released under the MIT license. See LICENSE file for details.
 */

JSUTILS.namespace('BO.I2CBase');

/**
 * @namespace BO
 */
BO.I2CBase = (function() {
  "use strict";

  var I2CBase;

  // dependencies
  var Pin = BO.Pin,
    EventDispatcher = JSUTILS.EventDispatcher,
    IOBoardEvent = BO.IOBoardEvent;

  /**
   * A base class for I2C objects. Extend this class when creating an
   * interface for a new I2C device. I2CBase should not be instantiated
   * directly.
   *
   * @class I2CBase
   * @constructor
   * @uses JSUTILS.EventDispatcher
   * @param {IOBoard} board A reference to the IOBoard instance
   * @param {Number} address The I2C address of the device
   * @param {Number} delayUS The number of microseconds ...
   */
  I2CBase = function(board, address, delayUS) {

    if (board === undefined) {
      return;
    }

    this.name = "I2CBase";
    /** @protected*/
    this.board = board;

    var _delay = delayUS || 0,
      _delayInMicrosecondsLSB = _delay & 0xFF,
      _delayInMicrosecondsMSB = (_delay >> 7) & 0xFF;

    /** @protected */
    this._address = address;
    this._evtDispatcher = new EventDispatcher(this);

    // if the pins are not set as I2C, set them now
    var i2cPins = board.getI2cPins();
    if (i2cPins.length === 2) {
      if (board.getPin(i2cPins[0]).getType() !== Pin.I2C) {
        board.getPin(i2cPins[0]).setType(Pin.I2C);
        board.getPin(i2cPins[1]).setType(Pin.I2C);
      }
    } else {
      // to do: proper error handling
      console.log("Error, this board does not support i2c");
      return;
    }

    board.addEventListener(IOBoardEvent.SYSEX_MESSAGE, this.onSysExMessage.bind(this));

    // call this for each board in case delay is set
    board.sendSysex(I2CBase.I2C_CONFIG, [_delayInMicrosecondsLSB, _delayInMicrosecondsMSB]);

  };


  I2CBase.prototype = {

    constructor: I2CBase,

    /**
     * [read-only] The address of the i2c device.
     * @property address
     * @type Number
     */
    get address() {
      return this._address;
    },

    // private methods:
    /**
     * @private
     * onSysExMessage
     */
    onSysExMessage: function(event) {
      var message = event.message;
      var addr = this.board.getValueFromTwo7bitBytes(message[1], message[2]);
      var data = [];

      if (message[0] != I2CBase.I2C_REPLY) {
        return;
      } else {
        //console.log(this);
        //console.log("addr = " + this._address);
        // to do: make sure i2c address in message matches the i2c address of the subclass
        // return if no match;
        if (addr != this._address) {
          return;
        }

        for (var i = 3, len = message.length; i < len; i += 2) {
          data.push(this.board.getValueFromTwo7bitBytes(message[i], message[i + 1]));
        }
        this.handleI2C(data);
      }

    },

    // public methods:

    /**
     * Send an i2c request command to the board
     * @protected
     * @method sendI2CRequest
     * @param {Number} command
     * @param {Number[]} data
     */
    sendI2CRequest: function(data) {

      // to do: support 10-bit i2c address
      var tempData = [];
      var address = data[1];
      var readWriteMode = data[0];

      tempData[0] = address;
      tempData[1] = readWriteMode << 3;

      for (var i = 2, len = data.length; i < len; i++) {
        tempData.push(data[i] & 0x007F);
        tempData.push((data[i] >> 7) & 0x007F);
      }

      this.board.sendSysex(I2CBase.I2C_REQUEST, tempData);

    },

    /**
     * To be implemented in subclass
     * @protected
     * @method update
     */
    update: function() {
      // To be implemented in sublasses
    },

    /**
     * To be implemented in subclass. Data should be: slave address,
     * register, data0, data1...
     * @protected
     * @method handleI2C
     */
    handleI2C: function(data) {
      // To be implemented in sublasses
      // data should be: slave address, register, data0, data1...
    },

    /* implement EventDispatcher */

    /**
     * @param {String} type The event type
     * @param {Function} listener The function to be called when the event is fired
     */
    addEventListener: function(type, listener) {
      this._evtDispatcher.addEventListener(type, listener);
    },

    /**
     * @param {String} type The event type
     * @param {Function} listener The function to be called when the event is fired
     */
    removeEventListener: function(type, listener) {
      this._evtDispatcher.removeEventListener(type, listener);
    },

    /**
     * @param {String} type The event type
     * return {boolean} True is listener exists for this type, false if not.
     */
    hasEventListener: function(type) {
      return this._evtDispatcher.hasEventListener(type);
    },

    /**
     * @param {Event} type The Event object
     * @param {Object} optionalParams Optional parameters to assign to the event object.
     * return {boolean} True if dispatch is successful, false if not.
     */
    dispatchEvent: function(event, optionalParams) {
      return this._evtDispatcher.dispatchEvent(event, optionalParams);
    }
  };


  /**
   * @property I2CBase.I2C_REQUEST
   * @static
   */
  I2CBase.I2C_REQUEST = 0x76;
  /**
   * @property I2CBase.I2C_REPLY
   */
  I2CBase.I2C_REPLY = 0x77;
  /**
   * @property I2CBase.I2C_CONFIG
   * @static
   */
  I2CBase.I2C_CONFIG = 0x78;

  /**
   * @property I2CBase.WRITE
   * @static
   */
  I2CBase.WRITE = 0;
  /**
   * @property I2CBase.READ
   * @static
   */
  I2CBase.READ = 1;
  /**
   * @property I2CBase.READ_CONTINUOUS
   * @static
   */
  I2CBase.READ_CONTINUOUS = 2;
  /**
   * @property I2CBase.STOP_READING
   * @static
   */
  I2CBase.STOP_READING = 3;

  return I2CBase;

}());