Source: examples/servers/node/controllers/ThreeWheelDistanceSensingRobotController.js

var hcsr04 = require("HC-SR04");
var ir = require("IRReceiver");

/**
 * @classdesc Class representing an infrared receiver.
 * @author Loris Tissino (http://loris.tissino.it)
 * @release 0.71
 * @license MIT
 * @constructor
 * @mixes ThreeWheelDistanceSensingRobotVirtualizer
 */
var IRR = function ( pin, codesMap, leftSubtract ) {
    this.codesMap = codesMap;
    this.pin = pin;
    this.received = '';
    this.leftSubtract = leftSubtract;
    this.code = '';  // we keep track of the last code executed for managing kept pressed keys
    this.interval = false;
    var self = this;
};

/**
 * Enables the infrared receiver.
 * @param {function} callback - The function to execute with the received code
*/
IRR.prototype.enable = function ( callback ) {
    var self = this;
    this.callback = callback;
    ir.connect(this.pin, function ( receivedCode ) {
        self.received = receivedCode;
		if ( self.received.length > 1 ) {
			// new code
			var decimalValue = parseInt(self.received.substring(self.leftSubtract), 2);
			if ( decimalValue > 1 ) {
				self.code = decimalValue.toString();
			}
		}
    });
    this.interval = setInterval ( function () {
		if ( self.received == '1' ) {
			self.received = '';
		}
		else if (self.received.length <= 1) {
			self.code = '0';
		}
		self.callback();
			
	}, 200);
};

/**
 * @classdesc Class representing a sonar.
 * @author Loris Tissino (http://loris.tissino.it)
 * @release 0.71
 * @license MIT
 * @param {Pin} triggerPin - The pin used to trigger the sonar
 * @param {Pin} echoPin - The pin used to check the echo
 * @constructor
 */
var Sonar = function ( triggerPin, echoPin, frequency ) {
    this.triggerPin = triggerPin;
    this.echoPin = echoPin;
    this.frequency = frequency;
    this.distance = Infinity;
    this.interval = false;
};

/**
 * Enables the sonar.
*/
Sonar.prototype.enable = function () {
    var sonar = this;
    this.connection = hcsr04.connect ( this.triggerPin, this.echoPin, function ( dist ) {
        sonar.distance = dist;
    });
    this.interval = setInterval ( function () {
        sonar.connection.trigger();
    }, this.frequency ); 
};

/**
 * Disables the sonar.
*/
Sonar.prototype.disable = function () {
    if ( this.interval !== false ) {
        clearInterval( this.interval );
    }
    this.distance = Infinity;
    this.interval = false;
};

/**
 * @classdesc Class representing a driving wheel.
 * @author Loris Tissino (http://loris.tissino.it)
 * @release 0.71
 * @license MIT
 * @param {Pin} enablePin - The pin used to enable the motor of the wheel
 * @param {array} controlPins - An array of two Pins used to control the wheel
 * @param {float} calibrationFactor - A value which is multiplied by speed before applying
 * @constructor
 */
var Wheel = function ( enablePin, controlPins, calibrationFactor) {
    this.enablePin = enablePin;
    this.controlPins = controlPins;
    this.calibrationFactor = calibrationFactor;
    this.setSpeed(0);
};

/**
 * Sets the speed of the wheel
 * @param {float} speed - The speed to set (between 0.0 and 1.0)
 */
Wheel.prototype.setSpeed = function ( speed ) {
    this._speed = speed;
    analogWrite( this.enablePin, this._speed * this.calibrationFactor, { freq: 100 } );
    return this;
};

/**
 * Returns the speed set for the wheel
 * @return {float} The value set as current speed
 */
Wheel.prototype.getSpeed = function () {
    return this._speed;
};

/**
 * Write values to control pins
 * @param {int} v0 - Value to write to controlPin[0]
 * @param {int} v1 - Value to write to controlPin[1]
 */
Wheel.prototype.writeToControlPins = function (v0, v1) {
    this.controlPins[0].write(v0);
    this.controlPins[1].write(v1);
};

/**
 * Sets the wheel to go forward
 */
Wheel.prototype.forward = function () {
    this.writeToControlPins( 0, 1 );
};

/**
 * Sets the wheel to go backward
 */
Wheel.prototype.backward = function () {
    this.writeToControlPins( 1, 0 );
};

/**
 * Sets the wheel to stopped
 */
Wheel.prototype.stop = function () {
    this.writeToControlPins( 0, 0 );
};

/**
 * @classdesc Class representing a LED.
 * @author Loris Tissino (http://loris.tissino.it)
 * @release 0.71
 * @license MIT
 * @param {Pin} pin - The pin associated with the LED
 * @param {int} blinkingTime - Milliseconds between on/off operations
 * @constructor
 */
var Led = function ( pin, blinkingTime ) {
    this.pin = pin;
    this.blinkingTime = blinkingTime;
    this.status = 0;
    this.pin.write( this.status );
    this.interval = false;
};

/**
 * Toggles the LED.
 */
Led.prototype.toggle = function () {
    this.pin.write( this.status = 1 - this.status );
};

/**
 * Clears the interval associated to the LED.
 */
Led.prototype.clearInterval = function () {
    console.log ( this );
    if ( this.interval !== false ) {
        clearInterval ( this.interval );
    }
    this.interval = false;
};

/**
 * Switches the LED on.
 */
Led.prototype.switchOn = function () {
    this.pin.write( this.status = 1 );
    this.clearInterval();
};

/**
 * Switches the LED off.
 */
Led.prototype.switchOff = function () {
    this.pin.write( this.status = 0 );
    this.clearInterval();
};

/**
 * @classdesc Class representing a buzzer.
 * @author Loris Tissino (http://loris.tissino.it)
 * @release 0.71
 * @license MIT
 * @param {Pin} pin - The pin associated with the buzzer
 * @param {float} frequency - The default frequency
 * @param {float} duration - The default duration (milliseconds)
 * @constructor
 */
var Buzzer = function ( pin, frequency, duration ) {
    this.pin = pin;
    this.defaultFrequency = frequency;
    this.defaultDuration = duration;
    this.status = 0;  // 0: off;  1: beeping;  2: replicated
};

/**
 * Beeps.
 * @param {float} frequency - The frequency
 * @param {float} duration - The duration (milliseconds)
 */
Buzzer.prototype.beep = function ( frequency, duration ) {
    var self = this;
    this.frequency = frequency || this.defaultFrequency;
    this.duration = duration || this.defaultDuration;
    if ( this.status !== 1 ) {
        analogWrite(self.pin, 0.5, { freq: self.frequency } );
        this.status = 1;
        setTimeout ( function() {
            digitalWrite(self.pin, 0);
            this.status = 0;
        }, self.duration );
    }
};

/**
 * @classdesc Class representing a robot's application controller.
 * @author Loris Tissino (http://loris.tissino.it)
 * @release 0.71
 * @license MIT
 * @param {string} id - The id of the robot
 * @constructor
 */
var ThreeWheelDistanceSensingRobotController = function ( id ) {
    this.id = id;
    this.data = {};
    this.config = {};
    this.playing = false;
};

/**
 * Configures the robot.
 * @param {Object} options - The configuration settings (currently unused)
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.setup = function ( options ) {
    this.infraredReader = new IRR ( B3, {
            '16605343': { exec: 'moveForward', repeatOnKeptPressed: true },
            '16607383': { exec: 'moveBackward', repeatOnKeptPressed: true },
            '16603813': { exec: 'turnLeft', repeatOnKeptPressed: true },
            '16635943': { exec: 'turnRight', repeatOnKeptPressed: true },
            '16603303': { exec: 'stop', repeatOnKeptPressed: false },
            '66502975': { exec: 'decreaseSpeed', repeatOnKeptPressed: true },
            '66462175': { exec: 'increaseSpeed', repeatOnKeptPressed: true },
            '66478495': { exec: 'stop', repeatOnKeptPressed: false },
            '0'       : { exec: 'stop', repeatOnKeptPressed: false },
            '16597693': { exec: 'toggleLedBlinking1', repeatOnKeptPressed: false },
            '16581373': { exec: 'toggleLedBlinking2', repeatOnKeptPressed: false },
            '16629823': { exec: 'toggleLedBlinking3', repeatOnKeptPressed: false },
            '16599733': { exec: 'rightForward', repeatOnKeptPressed: true },
            '16607893': { exec: 'rightBackward', repeatOnKeptPressed: true },
            '16631863': { exec: 'leftForward', repeatOnKeptPressed: true },
            '16640023': { exec: 'leftBackward', repeatOnKeptPressed: true },
            '16609423': { exec: 'play', repeatOnKeptPressed: false },
            '16599223': { exec: 'pause', repeatOnKeptPressed: false },
        },
        1
    );
    this.leftWheel = new Wheel ( A2, [ A4, A3 ], 1);
    this.rightWheel = new Wheel ( A7, [ A5, A6 ], 0.75);
    this.speed = 0;
    this.leds = {
        '1': new Led ( LED1, 700 ),
        '2': new Led ( LED2, 500 ),
        '3': new Led ( LED3, 300 )
    };
    this.sonars = {
        'left': new Sonar ( C1, C0, 250 ),
        'front': new Sonar ( C3, C2, 250 ),
        'right': new Sonar ( C4, C5, 250 )
    };
    this.buzzer = new Buzzer( B6, 110, 100 );
    
    this.checkForObstacles = { enabled: false };
    
    return this;
};

/**
 * Enables the infrared reader.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.enableInfraredReader = function () {
    this.infraredReader.enable ( this.handleInfraredReaderCode.bind(this) );
    return this;
};

/**
 * Handles the code read by the infrared reader.
 */
ThreeWheelDistanceSensingRobotController.prototype.handleInfraredReaderCode = function () {
    if ( typeof this.infraredReader.code === 'undefined') {
        return;
    }
    if ( this.infraredReader.codesMap.hasOwnProperty ( this.infraredReader.code ) ) {
        var action = this.infraredReader.codesMap[this.infraredReader.code];
        if ( action.repeatOnKeptPressed === true || this.infraredReader.received != '1' ) {
            this[action.exec]();
        }
    }
    else {
        console.log ( 'IR code read: ' + this.infraredReader.code + ' (not handled)');
    }
};

/**
 * Activates the wheels to move forward.
 */
ThreeWheelDistanceSensingRobotController.prototype.moveForward = function () {
    this.leftWheel.forward();
    this.rightWheel.forward();
};

/**
 * Activates the wheels to move backward.
 */
ThreeWheelDistanceSensingRobotController.prototype.moveBackward = function () {
    this.leftWheel.backward();
    this.rightWheel.backward();
};

/**
 * Activates the wheels to turn left.
 */
ThreeWheelDistanceSensingRobotController.prototype.turnLeft = function () {
    this.leftWheel.backward();
    this.rightWheel.forward();
};

/**
 * Activates the wheels to turn right.
 */
ThreeWheelDistanceSensingRobotController.prototype.turnRight = function () {
    this.leftWheel.forward();
    this.rightWheel.backward();
};

/**
 * Activates the left wheel to move forward.
 */
ThreeWheelDistanceSensingRobotController.prototype.leftForward = function () {
    this.leftWheel.forward();
};

/**
 * Activates the left wheel to move backward.
 */
ThreeWheelDistanceSensingRobotController.prototype.leftBackward = function () {
    this.leftWheel.backward();
};

/**
 * Activates the right wheel to move forward.
 */
ThreeWheelDistanceSensingRobotController.prototype.rightForward = function () {
    this.rightWheel.forward();
};

/**
 * Activates the right wheel to move backward.
 */
ThreeWheelDistanceSensingRobotController.prototype.rightBackward = function () {
    this.rightWheel.backward();
};

/**
 * Stops both wheels.
 */
ThreeWheelDistanceSensingRobotController.prototype.stop = function stop () {
    this.leftWheel.stop();
    this.rightWheel.stop();
};

/**
 * Makes a backup maneuvre: backward, turn, forward again.
 */
ThreeWheelDistanceSensingRobotController.prototype.backup = function () {
    this.inManeuver = true;
    this.beep();
    var self = this;
    var lr = self.sonars.left.distance > self.sonars.right.distance ? 'L': 'R';
    self.moveBackward();
    setTimeout( function() {
        lr == 'L' ? self.turnLeft(): self.turnRight();
        setTimeout( function() {
            self.inManeuver = false;
            self.moveForward(); // forward again
        }, 500);
    }, 2000);
};

/**
 * Sets the speed of the wheels.
 * @param {float} speed - The speed (between 0.0 and 1.0, otherwise clipped)
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.setSpeed = function ( speed ) {
    this.speed = E.clip ( speed, 0.0, 1.0 );
    this.leftWheel.setSpeed(this.speed);
    this.rightWheel.setSpeed(this.speed);
    return this;
};

/**
 * Decreases speed of 0.05.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.decreaseSpeed = function ( ) {
    return this.setSpeed ( this.speed - 0.05 );
};

/**
 * Increases speed of 0.05.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.increaseSpeed = function ( ) {
    return this.setSpeed ( this.speed + 0.05 );
};

/**
 * Toggles LED's blinking.
 * @param {string} led - The code associated to the LED
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.toggleLedBlinking = function ( led ) {
    var robot = this;
    if ( this.leds[led].interval === false ) {
        this.leds[led].interval = setInterval ( function() {
            robot.leds[led].toggle();
        }, this.leds[led].blinkingTime );
    }
    else {
        robot.leds[led].switchOff();
        this.leds[led].clearInterval();
    }
    return this;
};

/**
 * Toggles LED1's blinking.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.toggleLedBlinking1 = function ( ) {
    return this.toggleLedBlinking( '1' );
};

/**
 * Toggles LED2's blinking.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.toggleLedBlinking2 = function ( ) {
    return this.toggleLedBlinking( '2' );
};

/**
 * Toggles LED3's blinking.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.toggleLedBlinking3 = function ( ) {
    return this.toggleLedBlinking( '3' );
};

/**
 * Enables all the sonars.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.enableSonars = function ( ) {
    for ( var key in this.sonars ) {
        this.sonars[key].enable();
    }
    return this;
};

/**
 * Makes the buzzer beep.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.beep = function ( ) {
    this.buzzer.beep();
    return this;
};

/**
 * Makes the robot play.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.play = function () {
    var self = this;
    this.inManeuver = false;
    if (!this.playing) {
        this.moveForward();
        this.playing = setInterval(function() {
            if ( self.sonars.front.distance < 20 && !self.inManeuver ) {
                self.backup();
            }
        }, 50);
    }
    return this;
};

/**
 * Makes the robot pause.
 * @return {ThreeWheelDistanceSensingRobotController} - The controller
 */
ThreeWheelDistanceSensingRobotController.prototype.pause = function () {
    this.stop();
    if ( this.playing ) {
        clearInterval( this.playing );
        this.playing = false;
    }
    return this;
};

module.exports = ThreeWheelDistanceSensingRobotController;