Presented by Carlos Justiniano / NYC JavaScript @ Flatiron / @cjus / ver: 1.0.0
One of the key differences with SBCs is that you get general purpose input output pins, referred to as GPIO pins
Allow you to connect to other devices such as sensors, motors, switches and other controllers
This is like the hello world program of embedded systems ;-)
Sorry, but we won't do this process live, because it can take up to 30 minutes to complete
$ sudo dd if=./2016-05-27-raspbian-jessie-lite.img of=/dev/disk2
These settings will allow us to network our Pi over USB
You should be able to ping it...
$ ping raspberrypi.local
PING raspberrypi.local (169.254.2.0): 56 data bytes
64 bytes from 169.254.2.0: icmp_seq=1 ttl=64 time=0.588 ms
64 bytes from 169.254.2.0: icmp_seq=2 ttl=64 time=0.651 ms
64 bytes from 169.254.2.0: icmp_seq=3 ttl=64 time=0.576 ms
64 bytes from 169.254.2.0: icmp_seq=4 ttl=64 time=0.626 ms
If the former works you can SSH into your Pi with a default user of pi and a password of raspberry
$ ssh pi@raspberrypi.local
pi@raspberrypi.local's password:
$ sudo apt-get install wget
$ mkdir nodejs
$ cd nodejs
$ wget https://nodejs.org/dist/v6.5.0/node-v6.5.0-linux-armv6l.tar.xz
$ tar -xvf node-v6.5.0-linux-armv6l.tar.xz
$ cd node-v6.5.0-linux-armv6l/
$ sudo cp -R * /usr/local/
$ cd ~
$ node --version
v6.5.0
const piblaster = require('pi-blaster.js');
const LED_PIN = 4;
let y = 0;
let intervalID = setInterval(() => {
let s = Math.abs(Math.sin(y/10));
piblaster.setPwm(LED_PIN, s);
y += 1;
if (y > 180) {
y = 0;
}
}, 100);
process.on('SIGINT', () => {
clearInterval(intervalID);
piblaster.setPwm(LED_PIN, 0);
setInterval(() => {
process.exit();
}, 1000);
});
const piblaster = require('pi-blaster.js');
const LED_PIN = 4;
const LED_ON = 1;
const LED_OFF = 0;
const SOS_PATTERN = [
LED_ON, LED_ON, LED_ON,
LED_OFF,
LED_ON, LED_OFF, LED_ON, LED_OFF, LED_ON,
LED_OFF, LED_OFF,
LED_ON, LED_ON, LED_ON,
LED_OFF, LED_OFF, LED_OFF, LED_OFF, LED_OFF, LED_OFF];
let i = 0;
let intervalID = setInterval(() => {
piblaster.setPwm(LED_PIN, SOS_PATTERN[i]);
if (i > SOS_PATTERN.length - 1) {
i = 0;
} else {
i += 1;
}
}, 500);
process.on('SIGINT', () => {
clearInterval(intervalID);
piblaster.setPwm(LED_PIN, 0);
setInterval(() => {
process.exit();
}, 1000);
});
We'll see even more displays later in this presentation
Playing with single board computers is really cool.
Getting them to communicate with one another is even cooler!
There are a lot of ways to handle communication:
In our next demos we'll look at serial communication and messaging via Redis pub/sub and ExpressJS APIs
Let's get back to communication
Using the Hydra Cluster we'll examine:
#include
#include
#include
#define LCD_WIDTH 16
#define LCD_HEIGHT 2
OneWire ds(11); // on pin 11
LiquidCrystal lcd(12, 2, 4, 5, 6, 7);
void setup() {
Serial.begin(9600);
lcd.begin(LCD_WIDTH, LCD_HEIGHT);
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("HYDRA CLUSTER");
}
void loop() {
displayUptime();
displayTemperature();
delay(1000);
}
void displayUptime() {
char buf[LCD_WIDTH];
unsigned int days = 0;
unsigned int hours = 0;
unsigned int mins = 0;
unsigned int secs = 0;
secs = millis() / 1000;
mins = secs / 60;
hours = mins / 60;
days = hours / 24;
secs = secs - (mins * 60);
mins = mins - (hours * 60);
hours = hours - (days * 24);
sprintf(buf, "%02d %02d:%02d:%02d", days, hours, mins, secs);
lcd.setCursor(0, 1);
lcd.print(buf);
Serial.write(buf);
Serial.write('|');
}
void displayTemperature() {
int HighByte, LowByte, TReading, SignBit, Tc_100, Tf_100, Whole, Fract;
char buf[LCD_WIDTH];
byte i, sensor;
byte present = 0;
byte data[12];
byte addr[8];
ds.reset_search();
if (!ds.search(addr)) {
lcd.setCursor(0,0);
lcd.print("No more addr.");
ds.reset_search();
delay(250);
return;
}
if (OneWire::crc8(addr, 7) != addr[7]) {
lcd.setCursor(0,0);
lcd.print("CRC not valid");
return;
}
if (addr[0] != 0x28) {
lcd.setCursor(0,0);
lcd.print("Not a DS18B20.");
return;
}
ds.reset();
ds.select(addr);
ds.write(0x44, 1); // start conversion, with parasite power on at the end
present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for (i = 0; i < 9; i++) {
data[i] = ds.read();
}
LowByte = data[0];
HighByte = data[1];
TReading = (HighByte << 8) + LowByte;
SignBit = TReading & 0x8000; // test most sig bit
if (SignBit) { // negative
TReading = (TReading ^ 0xffff) + 1; // 2's comp
}
Tc_100 = (6 * TReading) + TReading / 4; // multiply by (100 * 0.0625) or 6.25
Tf_100 = ((Tc_100 * 9.0) / 5.0 + 32 * 100) / 100;
sprintf(buf, "%dF", Tf_100);
int pos = LCD_WIDTH - strlen(buf);
lcd.setCursor(pos, 1);
lcd.print(buf);
Serial.write(buf);
Serial.write('\n');
}
const SerialPort = require('serialport');
let portName = '/dev/ttyUSB0';
let myPort = new SerialPort(portName, {
baudRate: 9600,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false
});
myPort.on('open', () => {
console.log('Open Connection');
});
myPort.on('data', (data) => {
console.log(data.toString());
});
/**
* @name Service
* @description This is the service entry point
*/
const http = require('http');
const cluster = require('cluster');
const os = require('os');
const hydra = require('@flywheelsports/hydra');
const Utils = require('@flywheelsports/jsutils');
const version = require('./package.json').version;
const handler = require('./handler');
let config = require('@flywheelsports/config');
config.init('./config/config.json')
.then(() => {
config.version = version;
config.hydra.serviceVersion = version;
/**
* Handling for process invocation as a process master or child process.
*/
if (config.cluster !== true) {
initWorker();
} else {
if (cluster.isMaster) {
const numWorkers = config.processes || os.cpus().length;
console.log(`${config.hydra.serviceName} (v.${config.version})`);
console.log(`Using environment: ${config.environment}`);
console.log('info', `Master cluster setting up ${numWorkers} workers...`);
for (let i = 0; i < numWorkers; i++) {
cluster.fork();
}
/**
* @param {object} worker - worker process object
*/
cluster.on('online', (worker) => {
console.log(`Worker ${worker.process.pid} is online`);
});
/**
* @param {object} worker - worker process object
* @param {number} code - process exit code
* @param {number} signal - signal that caused the process shutdown
*/
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died with code ${code}, and signal: ${signal}`);
console.log('Starting a new worker');
cluster.fork();
});
} else {
initWorker();
}
}
});
/**
* @name initWorker
* @summary Initialize the core process functionality.
*/
function initWorker() {
/**
* Initialize hydra.
*/
hydra.init(config.hydra)
.then(() => {
return hydra.registerService();
})
.then((serviceInfo) => {
let logEntry = `Starting hydra-router service ${serviceInfo.serviceName} on port ${serviceInfo.servicePort}`;
hydra.sendToHealthLog('info', logEntry);
console.log(logEntry);
hydra.on('log', (entry) => {
console.log('>>>> ', entry);
});
let channel = `iotnode:${serviceInfo.serviceName}`;
console.log('listening on channel', channel);
hydra.openSubscriberChannel(channel);
hydra.subscribeToChannel(channel, function(message) {
console.log('recieve message', message);
handler.process(message);
});
})
.catch((err) => {
console.log('err', err);
});
}
/**
* @name Handler
* @description Handle incoming hydra message
*/
const hydra = require('@flywheelsports/hydra');
const Utils = require('@flywheelsports/jsutils');
const XProcess = require('xprocess');
class Handler {
constructor() {
this.xProcess = null;
}
process(message) {
console.log('message.bdy.cmd', message.bdy.cmd);
if (this.xProcess) {
this.xProcess.close();
}
this.xProcess = XProcess.createClient();
this.xProcess.run('python', [`${message.bdy.cmd}.py`]);
}
}
module.exports = new Handler();
Hopefully this presentation has given you a sense of how to use Node in small places.
Want to learn more? Checkout these cool projects: