Last active
October 27, 2015 23:23
-
-
Save dexterlabora/c5b48702785c8206c762 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// circuit-master.js | |
// This program uses multiple microcontrollers in the same app to control a living Lego city. | |
// ~~ IoL City ~~ | |
// www.InternetOfLego.com | |
// ~~~~ CIRCUIT ~~~~~~~ | |
// Includes | |
var five = require('johnny-five'); | |
var Raspi = require("raspi-io"); | |
// Timing | |
var temporal = require("temporal"); | |
// Lego IR lidr | |
var sys = require('sys'); | |
var exec = require('child_process').exec; | |
// used for debugging purposes | |
var util = require('util'); | |
//Example: console.log(util.inspect(configs, false, null)); | |
// Communicate with parent server | |
process.send("Hello from the circuit!"); | |
process.on("message", function(message){ | |
console.log("circuit received message from server: ", message); | |
}); | |
// Global Functions | |
// Linux access to infrared transmitter daemon lidr | |
// Code concept from: https://github.com/dspinellis/lego-power-scratch | |
function pfir(command){ | |
exec('irsend SEND_ONCE LEGO_Single_Output ' + command, function(error, stdout, stderr){ | |
if(error) | |
console.log("Error sending command: " + command); | |
// else | |
// console.log("Successfully sent command: " + command); | |
}); | |
} | |
// Define Johnny-Five Boards | |
var ports = [ | |
{ id: "mega", port: "/dev/ttyACM0" }, | |
{ id: "motor", port: "/dev/ttyUSB0" }, | |
{ id: "rpi", io: new Raspi()} | |
]; | |
var board = new five.Boards(ports).on("ready", function(){ | |
mega = this[0]; | |
motor = this[1]; | |
rpi = this[2]; | |
// Devices | |
// Disco Light | |
var discoLed = new five.Led({ | |
pin: 28, | |
board: mega | |
}); | |
// City Lights | |
var cityLed = new five.Led({ | |
pin: 8, | |
board: mega | |
}); | |
// test LED | |
var led = new five.Led({ | |
pin: 13, | |
board: mega | |
}); | |
// Track Switches | |
var trackSwitchLedA = new five.Led({ | |
pin: 26, | |
board: mega | |
}); | |
var trackSwitchLedB = new five.Led({ | |
pin: 27, | |
board: mega | |
}); | |
var trackSwitchServo = new five.Servo({ | |
pin: 2, | |
board: mega | |
}); | |
var trackSwitch2Servo = new five.Servo({ | |
pin: 3, | |
board: mega | |
}); | |
var trackSwitchButton = new five.Button({ | |
pin: 45, | |
board: mega | |
}); | |
// Traffic Signal | |
var trafficSignalLedRG = new five.Led({ | |
pin: 49, | |
board: mega | |
}); | |
var trafficSignalLedY = new five.Led({ | |
pin: 50, | |
board: mega | |
}); | |
var trafficSignalLedGR = new five.Led({ | |
pin: 51, | |
board: mega | |
}); | |
// Track Road Crossing | |
//crossing light 1 | |
var crossingLed1 = new five.Led({ | |
pin: 22, | |
board: mega | |
}); | |
//crossing light 2 | |
var crossingLed2 = new five.Led({ | |
pin: 23, | |
board: mega | |
}); | |
// crossing arm | |
var crossingServo = new five.Servo({ | |
pin: 6, | |
rate: 0.05, | |
board: mega | |
}); | |
var crossingSensor = new five.Sensor.Digital({ //crossing sensor | |
pin: 53, | |
freq: 150, // how often to read the sensor in milliseconds | |
board: mega | |
}); | |
// City Station | |
var cityStationIRSensor = new five.Sensor.Digital({ | |
pin: 'GPIO6', | |
board: rpi | |
}); | |
// Motion Sensor | |
var motionSensor = new five.Sensor.Digital({ | |
pin: 52, | |
board: mega | |
}); | |
var motionLed = new five.Led({ | |
pin: 42, | |
board: mega | |
}); | |
// Elevator | |
// Assign motorshield config to arduino board | |
var configs = five.Motor.SHIELD_CONFIGS.ADAFRUIT_V1; | |
configs.M4.board = motor; // adds "motor" port as property of config | |
// Use for troubleshooting & analysis | |
//console.log("configs"); | |
//console.log(util.inspect(configs, false, null)); | |
var elevatorMotor = new five.Motor(configs.M4); | |
var elevatorProximity = new five.Proximity({ | |
controller: "HCSR04", | |
pin: 2, | |
freq: 50, | |
board: motor | |
}); | |
// ***** REPL COMMAND LINE ***** | |
// Add devices to REPL (optional) | |
this.repl.inject({ | |
crossingServo: crossingServo, | |
crossingLed1: crossingLed1, | |
crossingLed2: crossingLed2, | |
trackSwitchLedA: trackSwitchLedA, | |
trackSwitchLedB: trackSwitchLedB, | |
crossingSensor: crossingSensor, | |
trackSwitchServo: trackSwitchServo, | |
trackSwitch2Servo: trackSwitch2Servo, | |
trackSwitchButton: trackSwitchButton, | |
trafficSignal: trafficSignal, | |
trafficSignalLedRG: trafficSignalLedRG, | |
trafficSignalLedY: trafficSignalLedY, | |
trafficSignalLedGR: trafficSignalLedGR, | |
motionLed: motionLed, | |
motionSensor: motionSensor, | |
cityLed: cityLed, | |
elevatorMotor: elevatorMotor, // range: 30 - 100 | |
elevatorDown: elevatorDown, | |
elevatorUp: elevatorUp, | |
elevatorToggle: elevatorToggle, | |
elevatorStop: elevatorStop, | |
trackSwitch: trackSwitch, //toggles | |
trackSwitch2: trackSwitch2, //toggles | |
trackSwitchServo: trackSwitchServo, // range: 90 - 130 | |
discoParty: discoParty, | |
pfir_1R_4: pfir_1R_4, | |
train_1R_forward: train_1R_forward, | |
pfir_1R_brake: pfir_1R_brake | |
}); | |
// Receive the command from the server | |
process.on('message', function(msg){ | |
var command = msg.command; | |
console.log("command received: ", command); | |
// Check command received and execute actions | |
if (command === "disco"){ | |
discoParty(); | |
} | |
if (command === "elevator_up"){ | |
elevatorUp(); | |
} | |
if (command === "elevator_down"){ | |
elevatorDown(); | |
} | |
if (command === "elevator_stop"){ | |
elevatorStop(); | |
} | |
if (command === "elevator_toggle"){ | |
elevatorToggle(); | |
} | |
if (command === "lights_on"){ | |
cityLed.on(); | |
} | |
if (command === "lights_off"){ | |
cityLed.off(); | |
} | |
if (command === "track_straight"){ | |
trackSwitch("straight"); | |
} | |
if (command === "track_turn"){ | |
trackSwitch("turn"); | |
} | |
if (command === "pfir_1B_M5"){ | |
pfir_1B_M5(); | |
} | |
if (command === "pfir_1B_brake"){ | |
pfir_1B_brake(); | |
} | |
if (command === "pfir_1R_4"){ | |
pfir_1R_4(); | |
} | |
if (command === "pfir_1R_brake"){ | |
pfir_1R_brake(); | |
} | |
if (command === "pfir_1R_forward"){ | |
pfir_1R_4(); | |
} | |
if (command === "train_crossing"){ | |
crossingActivate(); | |
crossingDelayed(); | |
} | |
}); | |
// DO INTERESTING STUFF HERE | |
// Track Switch | |
trackSwitchButton.on("down", function(){ | |
console.log("trackSwitchButton pressed"); | |
trackSwitch(); | |
}); | |
// Disco | |
function discoParty(){ | |
console.log("Disco Party!!"); | |
process.send({"news":"Disco Party!"}); | |
discoLed.on(); | |
setTimeout(function(){ | |
console.log("Party's over! you don't have to go home, but you can't stay here.") | |
discoLed.off() | |
},5000); | |
} | |
// Elevator | |
var elevatorState = "stopped"; | |
var elevatorPosition; | |
//Smoothing algorithm variables to average proximity data in real-time | |
var evNumReadings = 10; // the number of readings per average | |
var evReadings = [] ; // the readings from the analog input | |
var evReadIndex = 0; // the index of the current reading | |
var evTotal = 0; // the running total | |
var evAverage = 0; // the average | |
for (evThisReading = 0; evThisReading < evNumReadings; evThisReading++) { | |
evReadings[evThisReading] = 0; // initialize data | |
} | |
elevatorProximity.on("change", function() { | |
// console.log("Elevator moving"); | |
}); | |
elevatorProximity.on("data", function() { | |
//console.log(this.cm + "cm" + " avg " + evAverage + " state = " + elevatorState + ": position = " + elevatorPosition); | |
//process.send("message",{"evData": this.cm}); | |
// Smoothing sensor data | |
// subtract the last reading: | |
evTotal = evTotal - evReadings[evReadIndex]; | |
// read from the sensor: | |
evReadings[evReadIndex] = this.cm; | |
// add the reading to the total: | |
evTotal = evTotal + evReadings[evReadIndex]; | |
// advance to the next position in the array: | |
evReadIndex = evReadIndex + 1; | |
// if we're at the end of the array... | |
if (evReadIndex >= evNumReadings) { | |
// ...wrap around to the beginning: | |
evReadIndex = 0; | |
} | |
// calculate the average: | |
evAverage = evTotal / evNumReadings; | |
// stop elevator automatically | |
if (elevatorState != "stopped"){ | |
// bottom distance in cm and allow elevator to move in correct direction | |
if (evAverage < 4.75 && elevatorState != "moving-up"){ | |
console.log("elevator stopping"); | |
elevatorStop(); | |
elevatorPosition = "bottom"; | |
// top distance in cm | |
}else if (evAverage > 19.85 && elevatorState != "moving-down"){ | |
console.log("elevator stopping"); | |
elevatorStop(); | |
elevatorPosition = "top"; | |
}else{ | |
elevatorPosition = "middle"; | |
} | |
} | |
}); | |
function elevatorUp(){ | |
if (elevatorPosition != "top"){ | |
console.log("Elevator going up!"); | |
elevatorState = "moving-up"; | |
elevatorMotor.forward(120); | |
}else{ | |
console.log("Elevator is already at the top"); | |
} | |
} | |
function elevatorDown(){ | |
if (elevatorPosition != "bottom"){ | |
console.log("Elevator going down!"); | |
elevatorState = "moving-down"; | |
elevatorMotor.reverse(90); | |
}else{ | |
console.log("Elevator is already at the bottom"); | |
} | |
} | |
function elevatorStop(){ | |
console.log("Elevator Stopped"); | |
elevatorState = "stopped"; | |
elevatorMotor.brake(); | |
} | |
function elevatorToggle(){ | |
console.log("Elevator Toggled"); | |
if (elevatorPosition == "bottom"){ | |
elevatorUp(); | |
}else{ | |
elevatorDown(); | |
} | |
} | |
// Turn on Disco Lights | |
// discoLed.on(); | |
// Turn on Testing LED | |
// led.blink(); | |
// Turn on City Lights | |
cityLed.on(); | |
// City Station Routine | |
var stationActive = false; // initialize state | |
cityStationIRSensor.on('change', function(){ | |
console.log("IR Sensor triggered"); | |
console.log("CityStation is active? ",stationActive); | |
// If train is not already detected, begin routine. | |
if (stationActive == false){ | |
console.log("Train has arrived and stopping"); | |
stationActive = true; | |
process.send({'cityStation':"Train has arrived"}); | |
// Stop Red Train | |
temporal.queue([ | |
{ | |
delay: 300, | |
task: function(){ | |
console.log("Slowing train"); | |
pfir_1R_3(); | |
} | |
}, | |
{ | |
delay: 300, | |
task: function(){ | |
console.log("Slowing train"); | |
pfir_1R_2(); | |
} | |
}, | |
{ | |
delay: 300, | |
task: function(){ | |
console.log("Slowing train"); | |
pfir_1R_1(); | |
} | |
}, | |
{ | |
delay: 100, //(delay to align passenger coach) | |
task: function(){ | |
console.log("Train stopping"); | |
pfir_1R_brake(); | |
} | |
}, | |
{ | |
delay: 5000, | |
task: function(){ | |
console.log("Moving forward"); | |
pfir_1R_4(); | |
} | |
}, | |
{ | |
delay: 5000, | |
task: function(){ | |
console.log("Train has cleared station"); | |
stationActive = false; | |
console.log("CityStation is active? ",stationActive); | |
} | |
} | |
]); | |
} | |
}); | |
// Track Switch with Lights | |
var trackSwitchState = "straight"; | |
function trackSwitch(trackSwitchState){ | |
if (trackSwitchState == "straight"){ | |
console.log("track switched: Straight"); | |
trackSwitchLedA.on(); | |
trackSwitchLedB.off(); | |
trackSwitchServo.to(90); | |
trackSwitchState = "straight"; | |
} else { | |
console.log("track switched: Turn"); | |
trackSwitchLedA.off(); | |
trackSwitchLedB.on(); | |
trackSwitchServo.to(130); | |
trackSwitchState = "turn"; | |
} | |
} | |
// Track Switch 2 | |
var trackSwitch2State = "straight"; | |
function trackSwitch2(){ | |
if (trackSwitch2State != "straight"){ | |
console.log("track switched: Straight"); | |
trackSwitch2Servo.to(90); | |
trackSwitch2State = "straight"; | |
} else { | |
console.log("track switched: Turn"); | |
trackSwitch2Servo.to(120); | |
trackSwitch2State = "turn"; | |
} | |
} | |
// Traffic Signal Logic | |
var trafficSignalState = 0; | |
function trafficSignal(){ | |
process.send({'trafficSignalState':trafficSignalState}); | |
if (trafficSignalState == 0){ // red | |
//console.log("traffic crossing RG"); | |
trafficSignalLedRG.on(); | |
trafficSignalLedY.off(); | |
trafficSignalLedGR.off(); | |
trafficSignalState = 1; | |
setTimeout(trafficSignal,6000); | |
} else if (trafficSignalState == 1){ // yellow | |
//console.log("traffic crossing Y"); | |
trafficSignalLedRG.off(); | |
trafficSignalLedY.on(); | |
trafficSignalLedGR.off(); | |
trafficSignalState = 2; | |
setTimeout(trafficSignal,2000); | |
} else if (trafficSignalState == 2){ // green | |
//console.log("traffic crossing GR"); | |
trafficSignalLedRG.off(); | |
trafficSignalLedY.off(); | |
trafficSignalLedGR.on(); | |
trafficSignalState = 3; | |
setTimeout(trafficSignal,6000); | |
} else if (trafficSignalState == 3){ // yellow | |
//console.log("traffic crossing Y"); | |
trafficSignalLedRG.off(); | |
trafficSignalLedY.on(); | |
trafficSignalLedGR.off(); | |
trafficSignalState = 0; | |
setTimeout(trafficSignal,2000); | |
} | |
} | |
trafficSignal(); | |
// Train Crossing State Variables | |
var crossingDelay; // Used to delay crossing deactivation | |
var crossingActive = false; | |
// Train Crossing Sensor | |
crossingSensor.on("change", function() { | |
console.log('crossingSensor raw: ' + this.value ); | |
if (this.value == 0){ // Inverted sensor, where 0 equals detection | |
if (!crossingActive){ // Detect if the crossing is already active | |
crossingActivate(); // Activate Crossing | |
}else{ | |
crossingDelayed(); // Delay Deactivation | |
} | |
} | |
}); | |
// Activate Crossing Arm and Lights | |
function crossingActivate(){ | |
console.log("crossingActivate()"); | |
crossingActive = true; | |
// Lights | |
crossingLed1.blink(500); | |
// Delay second light for alternating effect | |
setTimeout(function() { | |
crossingLed2.blink(500); // blink frequency in milliseconds | |
}, 500); // delay in milliseconds | |
// Servo Arm | |
crossingServo.to(70); // Adjust this servo angle as needed | |
crossingDelay = setTimeout(function(){ // Assign the timeout to a variable so we can clear it later if needed | |
crossingDeactivate(); | |
},3000); // Delay time in milliseconds | |
} | |
// Deactivate Crossing | |
function crossingDeactivate(){ | |
console.log("crossingDeactivate()"); | |
crossingActive = false; | |
crossingLed1.stop().off(); // Stop the blinking effect | |
crossingLed2.stop().off(); | |
crossingServo.to(150); // Adjust this servo angle as needed | |
} | |
// Delay deactivation because the train is still passing. | |
function crossingDelayed(){ | |
console.log("crossingDelayed()"); | |
clearTimeout(crossingDelay); // Reset delay timer (or the crossing goes crazy!) | |
crossingDelay = setTimeout(function(){ | |
crossingDeactivate(); // Deactivate crossing after delay time | |
},3000); // Delay time in milliseconds | |
} | |
// Sleep Mode Logic | |
var sleepMode; | |
motionSensor.on('change', function(){ | |
motionLed.on(); | |
setTimeout(function(){ | |
motionLed.off(); | |
},2000); | |
// Wake up these | |
cityLed.on(); | |
// discoLed.on(); | |
clearTimeout(sleepMode); | |
console.log("sleep timer reset"); | |
sleepMode = setTimeout(function() { | |
console.log("going to sleep"); | |
// sleep these | |
cityLed.off(); | |
//discoLed.off(); | |
}, 1000*60*5); | |
}); | |
// Power Functions Ir | |
function pfir_1B_M5(){ | |
console.log("pfir_1B_M5"); | |
pfir("1B_M5"); | |
} | |
function pfir_1B_brake(){ | |
console.log("pfir_1B_brake"); | |
pfir("1B_BRAKE"); | |
} | |
function pfir_1R_5(){ | |
console.log("pfir_1R_5"); | |
pfir("1R_5"); | |
} | |
function pfir_1R_4(){ | |
console.log("pfir_1R_4"); | |
pfir("1R_4"); | |
} | |
function pfir_1R_3(){ | |
console.log("pfir_1R_3"); | |
pfir("1R_3"); | |
} | |
function pfir_1R_2(){ | |
console.log("pfir_1R_2"); | |
pfir("1R_2"); | |
} | |
function pfir_1R_1(){ | |
console.log("pfir_1R_1"); | |
pfir("1R_1"); | |
} | |
function pfir_1R_brake(){ | |
console.log("pfir_1R_brake"); | |
pfir("1R_BRAKE"); | |
} | |
function train_1R_forward(){ | |
pfir_1R_4(); // Setting normal train speed | |
} | |
}); // END new five.Boards(ports).on("ready", function(){ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment