NodeJS controller example moved to separate repository
This commit is contained in:
@@ -1,17 +0,0 @@
|
|||||||
const dbAddress = '127.0.0.1';
|
|
||||||
const dbPort = 27017;
|
|
||||||
const dbName = 'MySensorsDb';
|
|
||||||
|
|
||||||
var dbc = require('mongodb').MongoClient;
|
|
||||||
dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err, db) {
|
|
||||||
if (err) {
|
|
||||||
console.log('Error connecting to database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName);
|
|
||||||
} else {
|
|
||||||
db.dropDatabase(function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log('Error dropping database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName);
|
|
||||||
else
|
|
||||||
console.log('Successfully dropped database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# pidfile must be first argument
|
|
||||||
PIDFILE=$1
|
|
||||||
mkdir -p /home/pi/logs
|
|
||||||
cd /home/pi/Arduino/NodeJsController
|
|
||||||
/opt/node/bin/node NodeJsController.js >>/home/pi/logs/NodeJsController.log 2>&1 </dev/null &
|
|
||||||
CHILD="$!"
|
|
||||||
echo $CHILD > $PIDFILE
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
### BEGIN INIT INFO
|
|
||||||
# Provides: mysensors
|
|
||||||
# Required-Start: $remote_fs $syslog $mongodb
|
|
||||||
# Required-Stop: $remote_fs $syslog
|
|
||||||
# Default-Start: 2 3 4 5
|
|
||||||
# Default-Stop: 0 1 6
|
|
||||||
# Short-Description: MySensors NodeJs Controller
|
|
||||||
# Description: MySensors NodeJs Controller
|
|
||||||
### END INIT INFO
|
|
||||||
|
|
||||||
# Author: Foo Bar <foobar@baz.org>
|
|
||||||
#
|
|
||||||
# Please remove the "Author" lines above and replace them
|
|
||||||
# with your own name if you copy and modify this script.
|
|
||||||
|
|
||||||
# Do NOT "set -e"
|
|
||||||
|
|
||||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
|
||||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
|
||||||
DESC="MySensors NodeJs Controller"
|
|
||||||
NAME="MySensors.sh"
|
|
||||||
PIDFILE="/usr/local/var/run/$NAME.pid"
|
|
||||||
DAEMON="/home/pi/Arduino/NodeJsController/$NAME"
|
|
||||||
DAEMON_ARGS="$PIDFILE"
|
|
||||||
DAEMON_USER="pi"
|
|
||||||
SCRIPTNAME=/etc/init.d/$NAME
|
|
||||||
|
|
||||||
# Exit if the package is not installed
|
|
||||||
[ -x "$DAEMON" ] || exit 0
|
|
||||||
|
|
||||||
# Read configuration variable file if it is present
|
|
||||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
|
||||||
|
|
||||||
# Load the VERBOSE setting and other rcS variables
|
|
||||||
. /lib/init/vars.sh
|
|
||||||
|
|
||||||
# Define LSB log_* functions.
|
|
||||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
|
||||||
# and status_of_proc is working.
|
|
||||||
. /lib/lsb/init-functions
|
|
||||||
|
|
||||||
#
|
|
||||||
# Function that starts the daemon/service
|
|
||||||
#
|
|
||||||
do_start()
|
|
||||||
{
|
|
||||||
# Return
|
|
||||||
# 0 if daemon has been started
|
|
||||||
# 1 if daemon was already running
|
|
||||||
# 2 if daemon could not be started
|
|
||||||
start-stop-daemon --start --quiet --chuid $DAEMON_USER --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
|
||||||
|| return 1
|
|
||||||
start-stop-daemon --start --quiet --chuid $DAEMON_USER --pidfile $PIDFILE --exec $DAEMON -- \
|
|
||||||
$DAEMON_ARGS \
|
|
||||||
|| return 2
|
|
||||||
# Add code here, if necessary, that waits for the process to be ready
|
|
||||||
# to handle requests from services started subsequently which depend
|
|
||||||
# on this one. As a last resort, sleep for some time.
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Function that stops the daemon/service
|
|
||||||
#
|
|
||||||
do_stop()
|
|
||||||
{
|
|
||||||
# Return
|
|
||||||
# 0 if daemon has been stopped
|
|
||||||
# 1 if daemon was already stopped
|
|
||||||
# 2 if daemon could not be stopped
|
|
||||||
# other if a failure occurred
|
|
||||||
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
|
|
||||||
RETVAL="$?"
|
|
||||||
[ "$RETVAL" = 2 ] && return 2
|
|
||||||
# Wait for children to finish too if this is a daemon that forks
|
|
||||||
# and if the daemon is only ever run from this initscript.
|
|
||||||
# If the above conditions are not satisfied then add some other code
|
|
||||||
# that waits for the process to drop all resources that could be
|
|
||||||
# needed by services started subsequently. A last resort is to
|
|
||||||
# sleep for some time.
|
|
||||||
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
|
|
||||||
[ "$?" = 2 ] && return 2
|
|
||||||
# Many daemons don't delete their pidfiles when they exit.
|
|
||||||
rm -f $PIDFILE
|
|
||||||
return "$RETVAL"
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Function that sends a SIGHUP to the daemon/service
|
|
||||||
#
|
|
||||||
do_reload() {
|
|
||||||
#
|
|
||||||
# If the daemon can reload its configuration without
|
|
||||||
# restarting (for example, when it is sent a SIGHUP),
|
|
||||||
# then implement that here.
|
|
||||||
#
|
|
||||||
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
|
||||||
do_start
|
|
||||||
case "$?" in
|
|
||||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
|
||||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
|
||||||
do_stop
|
|
||||||
case "$?" in
|
|
||||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
|
||||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
|
||||||
;;
|
|
||||||
#reload|force-reload)
|
|
||||||
#
|
|
||||||
# If do_reload() is not implemented then leave this commented out
|
|
||||||
# and leave 'force-reload' as an alias for 'restart'.
|
|
||||||
#
|
|
||||||
#log_daemon_msg "Reloading $DESC" "$NAME"
|
|
||||||
#do_reload
|
|
||||||
#log_end_msg $?
|
|
||||||
#;;
|
|
||||||
restart|force-reload)
|
|
||||||
#
|
|
||||||
# If the "reload" option is implemented then remove the
|
|
||||||
# 'force-reload' alias
|
|
||||||
#
|
|
||||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
|
||||||
do_stop
|
|
||||||
case "$?" in
|
|
||||||
0|1)
|
|
||||||
do_start
|
|
||||||
case "$?" in
|
|
||||||
0) log_end_msg 0 ;;
|
|
||||||
1) log_end_msg 1 ;; # Old process is still running
|
|
||||||
*) log_end_msg 1 ;; # Failed to start
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Failed to stop
|
|
||||||
log_end_msg 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
|
|
||||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
|
||||||
exit 3
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
:
|
|
||||||
|
|
||||||
@@ -1,713 +0,0 @@
|
|||||||
//const gwType = 'Ethernet';
|
|
||||||
//const gwAddress = '10.0.1.99';
|
|
||||||
//const gwPort = 9999;
|
|
||||||
|
|
||||||
const gwType = 'Serial';
|
|
||||||
//const gwPort = 'COM4';
|
|
||||||
const gwPort = '/dev/ttyAMA0';
|
|
||||||
const gwBaud = 115200;
|
|
||||||
|
|
||||||
const dbAddress = '127.0.0.1';
|
|
||||||
const dbPort = 27017;
|
|
||||||
const dbName = 'MySensorsDb';
|
|
||||||
|
|
||||||
const fwSketches = [ ];
|
|
||||||
const fwDefaultType = 0xFFFF; // index of hex file from array above (0xFFFF
|
|
||||||
|
|
||||||
const FIRMWARE_BLOCK_SIZE = 16;
|
|
||||||
const BROADCAST_ADDRESS = 255;
|
|
||||||
const NODE_SENSOR_ID = 255;
|
|
||||||
|
|
||||||
const C_PRESENTATION = 0;
|
|
||||||
const C_SET = 1;
|
|
||||||
const C_REQ = 2;
|
|
||||||
const C_INTERNAL = 3;
|
|
||||||
const C_STREAM = 4;
|
|
||||||
|
|
||||||
const V_TEMP = 0;
|
|
||||||
const V_HUM = 1;
|
|
||||||
const V_LIGHT = 2;
|
|
||||||
const V_DIMMER = 3;
|
|
||||||
const V_PRESSURE = 4;
|
|
||||||
const V_FORECAST = 5;
|
|
||||||
const V_RAIN = 6;
|
|
||||||
const V_RAINRATE = 7;
|
|
||||||
const V_WIND = 8;
|
|
||||||
const V_GUST = 9;
|
|
||||||
const V_DIRECTION = 10;
|
|
||||||
const V_UV = 11;
|
|
||||||
const V_WEIGHT = 12;
|
|
||||||
const V_DISTANCE = 13;
|
|
||||||
const V_IMPEDANCE = 14;
|
|
||||||
const V_ARMED = 15;
|
|
||||||
const V_TRIPPED = 16;
|
|
||||||
const V_WATT = 17;
|
|
||||||
const V_KWH = 18;
|
|
||||||
const V_SCENE_ON = 19;
|
|
||||||
const V_SCENE_OFF = 20;
|
|
||||||
const V_HEATER = 21;
|
|
||||||
const V_HEATER_SW = 22;
|
|
||||||
const V_LIGHT_LEVEL = 23;
|
|
||||||
const V_VAR1 = 24;
|
|
||||||
const V_VAR2 = 25;
|
|
||||||
const V_VAR3 = 26;
|
|
||||||
const V_VAR4 = 27;
|
|
||||||
const V_VAR5 = 28;
|
|
||||||
const V_UP = 29;
|
|
||||||
const V_DOWN = 30;
|
|
||||||
const V_STOP = 31;
|
|
||||||
const V_IR_SEND = 32;
|
|
||||||
const V_IR_RECEIVE = 33;
|
|
||||||
const V_FLOW = 34;
|
|
||||||
const V_VOLUME = 35;
|
|
||||||
const V_LOCK_STATUS = 36;
|
|
||||||
|
|
||||||
const I_BATTERY_LEVEL = 0;
|
|
||||||
const I_TIME = 1;
|
|
||||||
const I_VERSION = 2;
|
|
||||||
const I_ID_REQUEST = 3;
|
|
||||||
const I_ID_RESPONSE = 4;
|
|
||||||
const I_INCLUSION_MODE = 5;
|
|
||||||
const I_CONFIG = 6;
|
|
||||||
const I_PING = 7;
|
|
||||||
const I_PING_ACK = 8;
|
|
||||||
const I_LOG_MESSAGE = 9;
|
|
||||||
const I_CHILDREN = 10;
|
|
||||||
const I_SKETCH_NAME = 11;
|
|
||||||
const I_SKETCH_VERSION = 12;
|
|
||||||
const I_REBOOT = 13;
|
|
||||||
|
|
||||||
const S_DOOR = 0;
|
|
||||||
const S_MOTION = 1;
|
|
||||||
const S_SMOKE = 2;
|
|
||||||
const S_LIGHT = 3;
|
|
||||||
const S_DIMMER = 4;
|
|
||||||
const S_COVER = 5;
|
|
||||||
const S_TEMP = 6;
|
|
||||||
const S_HUM = 7;
|
|
||||||
const S_BARO = 8;
|
|
||||||
const S_WIND = 9;
|
|
||||||
const S_RAIN = 10;
|
|
||||||
const S_UV = 11;
|
|
||||||
const S_WEIGHT = 12;
|
|
||||||
const S_POWER = 13;
|
|
||||||
const S_HEATER = 14;
|
|
||||||
const S_DISTANCE = 15;
|
|
||||||
const S_LIGHT_LEVEL = 16;
|
|
||||||
const S_ARDUINO_NODE = 17;
|
|
||||||
const S_ARDUINO_REPEATER_NODE = 18;
|
|
||||||
const S_LOCK = 19;
|
|
||||||
const S_IR = 20;
|
|
||||||
const S_WATER = 21;
|
|
||||||
const S_AIR_QUALITY = 22;
|
|
||||||
|
|
||||||
const ST_FIRMWARE_CONFIG_REQUEST = 0;
|
|
||||||
const ST_FIRMWARE_CONFIG_RESPONSE = 1;
|
|
||||||
const ST_FIRMWARE_REQUEST = 2;
|
|
||||||
const ST_FIRMWARE_RESPONSE = 3;
|
|
||||||
const ST_SOUND = 4;
|
|
||||||
const ST_IMAGE = 5;
|
|
||||||
|
|
||||||
const P_STRING = 0;
|
|
||||||
const P_BYTE = 1;
|
|
||||||
const P_INT16 = 2;
|
|
||||||
const P_UINT16 = 3;
|
|
||||||
const P_LONG32 = 4;
|
|
||||||
const P_ULONG32 = 5;
|
|
||||||
const P_CUSTOM = 6;
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var requestify = require('requestify');
|
|
||||||
var appendedString="";
|
|
||||||
|
|
||||||
function crcUpdate(old, value) {
|
|
||||||
var c = old ^ value;
|
|
||||||
for (var i = 0; i < 8; ++i) {
|
|
||||||
if ((c & 1) > 0)
|
|
||||||
c = ((c >> 1) ^ 0xA001);
|
|
||||||
else
|
|
||||||
c = (c >> 1);
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pullWord(arr, pos) {
|
|
||||||
return arr[pos] + 256 * arr[pos + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
function pushWord(arr, val) {
|
|
||||||
arr.push(val & 0x00FF);
|
|
||||||
arr.push((val >> 8) & 0x00FF);
|
|
||||||
}
|
|
||||||
|
|
||||||
function pushDWord(arr, val) {
|
|
||||||
arr.push(val & 0x000000FF);
|
|
||||||
arr.push((val >> 8) & 0x000000FF);
|
|
||||||
arr.push((val >> 16) & 0x000000FF);
|
|
||||||
arr.push((val >> 24) & 0x000000FF);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFirmware(fwtype, fwversion, sketch, db) {
|
|
||||||
var filename = path.basename(sketch);
|
|
||||||
console.log("compiling firmware: " + filename);
|
|
||||||
var req = {
|
|
||||||
files: [{
|
|
||||||
filename: filename,
|
|
||||||
content: fs.readFileSync(sketch).toString()
|
|
||||||
}],
|
|
||||||
format: "hex",
|
|
||||||
version: "105",
|
|
||||||
build: {
|
|
||||||
mcu: "atmega328p",
|
|
||||||
f_cpu: "16000000L",
|
|
||||||
core: "arduino",
|
|
||||||
variant: "standard"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
requestify.post('https://codebender.cc/utilities/compile/', req).then(function(res) {
|
|
||||||
var body = JSON.parse(res.getBody());
|
|
||||||
if (body.success) {
|
|
||||||
console.log("loading firmware: " + filename);
|
|
||||||
fwdata = [];
|
|
||||||
var start = 0;
|
|
||||||
var end = 0;
|
|
||||||
var pos = 0;
|
|
||||||
var hex = body.output.split("\n");
|
|
||||||
for(l in hex) {
|
|
||||||
line = hex[l].trim();
|
|
||||||
if (line.length > 0) {
|
|
||||||
while (line.substring(0, 1) != ":")
|
|
||||||
line = line.substring(1);
|
|
||||||
var reclen = parseInt(line.substring(1, 3), 16);
|
|
||||||
var offset = parseInt(line.substring(3, 7), 16);
|
|
||||||
var rectype = parseInt(line.substring(7, 9), 16);
|
|
||||||
var data = line.substring(9, 9 + 2 * reclen);
|
|
||||||
var chksum = parseInt(line.substring(9 + (2 * reclen), 9 + (2 * reclen) + 2), 16);
|
|
||||||
if (rectype == 0) {
|
|
||||||
if ((start == 0) && (end == 0)) {
|
|
||||||
if (offset % 128 > 0)
|
|
||||||
throw new Error("error loading hex file - offset can't be devided by 128");
|
|
||||||
start = offset;
|
|
||||||
end = offset;
|
|
||||||
}
|
|
||||||
if (offset < end)
|
|
||||||
throw new Error("error loading hex file - offset lower than end");
|
|
||||||
while (offset > end) {
|
|
||||||
fwdata.push(255);
|
|
||||||
pos++;
|
|
||||||
end++;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < reclen; i++) {
|
|
||||||
fwdata.push(parseInt(data.substring(i * 2, (i * 2) + 2), 16));
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
end += reclen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var pad = end % 128; // ATMega328 has 64 words per page / 128 bytes per page
|
|
||||||
for (var i = 0; i < 128 - pad; i++) {
|
|
||||||
fwdata.push(255);
|
|
||||||
pos++;
|
|
||||||
end++;
|
|
||||||
}
|
|
||||||
var blocks = (end - start) / FIRMWARE_BLOCK_SIZE;
|
|
||||||
var crc = 0xFFFF;
|
|
||||||
for (var i = 0; i < blocks * FIRMWARE_BLOCK_SIZE; ++i) {
|
|
||||||
var v = crc;
|
|
||||||
crc = crcUpdate(crc, fwdata[i]);
|
|
||||||
}
|
|
||||||
db.collection('firmware', function(err, c) {
|
|
||||||
c.update({
|
|
||||||
'type': fwtype,
|
|
||||||
'version': fwversion
|
|
||||||
}, {
|
|
||||||
$set: {
|
|
||||||
'filename': filename,
|
|
||||||
'blocks': blocks,
|
|
||||||
'crc': crc,
|
|
||||||
'data': fwdata
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
upsert: true
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log('Error writing firmware to database');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
console.log("loading firmware done. blocks: " + blocks + " / crc: " + crc);
|
|
||||||
} else {
|
|
||||||
console.log("error: %j", res.body);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
function decode(msg) {
|
|
||||||
var msgs = msg.toString().split(";");
|
|
||||||
rsender = +msgs[0];
|
|
||||||
rsensor = +msgs[1];
|
|
||||||
rcommand = +msgs[2];
|
|
||||||
rtype = +msgs[3];
|
|
||||||
var pl = msgs[4].trim();
|
|
||||||
rpayload = [];
|
|
||||||
for (var i = 0; i < pl.length; i+=2) {
|
|
||||||
var b = parseInt(pl.substring(i, i + 2), 16);
|
|
||||||
rpayload.push(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function encode(destination, sensor, command, acknowledge, type, payload) {
|
|
||||||
var msg = destination.toString(10) + ";" + sensor.toString(10) + ";" + command.toString(10) + ";" + acknowledge.toString(10) + ";" + type.toString(10) + ";";
|
|
||||||
if (command == 4) {
|
|
||||||
for (var i = 0; i < payload.length; i++) {
|
|
||||||
if (payload[i] < 16)
|
|
||||||
msg += "0";
|
|
||||||
msg += payload[i].toString(16);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
msg += payload;
|
|
||||||
}
|
|
||||||
msg += '\n';
|
|
||||||
return msg.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveProtocol(sender, payload, db) {
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.update({
|
|
||||||
'id': sender
|
|
||||||
}, {
|
|
||||||
$set: {
|
|
||||||
'protocol': payload
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
upsert: true
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing protocol to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveSensor(sender, sensor, type, db) {
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.update({
|
|
||||||
'id': sender
|
|
||||||
}, {
|
|
||||||
$addToSet: {
|
|
||||||
sensor: type
|
|
||||||
}
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing sensor to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveValue(sender, sensor, type, payload, db) {
|
|
||||||
var cn = "Value-" + sender.toString() + "-" + sensor.toString();
|
|
||||||
db.createCollection(cn, function(err, c) {
|
|
||||||
c.save({
|
|
||||||
'timestamp': new Date().getTime(),
|
|
||||||
'type': type,
|
|
||||||
'value': payload
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing value to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveBatteryLevel(sender, payload, db) {
|
|
||||||
var cn = "BatteryLevel-" + sender.toString();
|
|
||||||
db.createCollection(cn, function(err, c) {
|
|
||||||
c.save({
|
|
||||||
'timestamp': new Date().getTime(),
|
|
||||||
'value': payload
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing battery level to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveSketchName(sender, payload, db) {
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.update({
|
|
||||||
'id': sender
|
|
||||||
}, {
|
|
||||||
$set: {
|
|
||||||
'sketchName': payload
|
|
||||||
}
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing sketch name to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveSketchVersion(sender, payload, db) {
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.update({
|
|
||||||
'id': sender
|
|
||||||
}, {
|
|
||||||
$set: {
|
|
||||||
'sketchVersion': payload
|
|
||||||
}
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing sketch version to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendTime(destination, sensor, gw) {
|
|
||||||
var payload = new Date().getTime()/1000;
|
|
||||||
var command = C_INTERNAL;
|
|
||||||
var acknowledge = 0; // no ack
|
|
||||||
var type = I_TIME;
|
|
||||||
var td = encode(destination, sensor, command, acknowledge, type, payload);
|
|
||||||
console.log('-> ' + td.toString());
|
|
||||||
gw.write(td);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendNextAvailableSensorId(db, gw) {
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.find({
|
|
||||||
$query: { },
|
|
||||||
$orderby: {
|
|
||||||
'id': 1
|
|
||||||
}
|
|
||||||
}).toArray(function(err, results) {
|
|
||||||
if (err)
|
|
||||||
console.log('Error finding nodes');
|
|
||||||
var id = 1;
|
|
||||||
for (var i = 0; i < results.length; i++)
|
|
||||||
if (results[i].id > i + 1) {
|
|
||||||
id = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (id < 255) {
|
|
||||||
c.save({
|
|
||||||
'id': id
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log('Error writing node to database');
|
|
||||||
var destination = BROADCAST_ADDRESS;
|
|
||||||
var sensor = NODE_SENSOR_ID;
|
|
||||||
var command = C_INTERNAL;
|
|
||||||
var acknowledge = 0; // no ack
|
|
||||||
var type = I_ID_RESPONSE;
|
|
||||||
var payload = id;
|
|
||||||
var td = encode(destination, sensor, command, acknowledge, type, payload);
|
|
||||||
console.log('-> ' + td.toString());
|
|
||||||
gw.write(td);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendConfig(destination, gw) {
|
|
||||||
var payload = "M";
|
|
||||||
var sensor = NODE_SENSOR_ID;
|
|
||||||
var command = C_INTERNAL;
|
|
||||||
var acknowledge = 0; // no ack
|
|
||||||
var type = I_CONFIG;
|
|
||||||
var td = encode(destination, sensor, command, acknowledge, type, payload);
|
|
||||||
console.log('-> ' + td.toString());
|
|
||||||
gw.write(td);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendFirmwareConfigResponse(destination, fwtype, fwversion, db, gw) {
|
|
||||||
// keep track of type/versin info for each node
|
|
||||||
// at the same time update the last modified date
|
|
||||||
// could be used to remove nodes not seen for a long time etc.
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.update({
|
|
||||||
'id': destination
|
|
||||||
}, {
|
|
||||||
$set: {
|
|
||||||
'type': fwtype,
|
|
||||||
'version': fwversion,
|
|
||||||
'reboot': 0
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
upsert: true
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing node type and version to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (fwtype == 0xFFFF) {
|
|
||||||
// sensor does not know which type / blank EEPROM
|
|
||||||
// take predefined type (ideally selected in UI prior to connection of new sensor)
|
|
||||||
if (fwDefaultType == 0xFFFF)
|
|
||||||
throw new Error('No default sensor type defined');
|
|
||||||
fwtype = fwDefaultType;
|
|
||||||
}
|
|
||||||
db.collection('firmware', function(err, c) {
|
|
||||||
c.findOne({
|
|
||||||
$query: {
|
|
||||||
'type': fwtype
|
|
||||||
},
|
|
||||||
$orderby: {
|
|
||||||
'version': -1
|
|
||||||
}
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log('Error finding firmware for type ' + fwtype);
|
|
||||||
else if (!result)
|
|
||||||
console.log('No firmware found for type ' + fwtype);
|
|
||||||
else {
|
|
||||||
var payload = [];
|
|
||||||
pushWord(payload, result.type);
|
|
||||||
pushWord(payload, result.version);
|
|
||||||
pushWord(payload, result.blocks);
|
|
||||||
pushWord(payload, result.crc);
|
|
||||||
var sensor = NODE_SENSOR_ID;
|
|
||||||
var command = C_STREAM;
|
|
||||||
var acknowledge = 0; // no ack
|
|
||||||
var type = ST_FIRMWARE_CONFIG_RESPONSE;
|
|
||||||
var td = encode(destination, sensor, command, acknowledge, type, payload);
|
|
||||||
console.log('-> ' + td.toString());
|
|
||||||
gw.write(td);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendFirmwareResponse(destination, fwtype, fwversion, fwblock, db, gw) {
|
|
||||||
db.collection('firmware', function(err, c) {
|
|
||||||
c.findOne({
|
|
||||||
'type': fwtype,
|
|
||||||
'version': fwversion
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log('Error finding firmware version ' + fwversion + ' for type ' + fwtype);
|
|
||||||
var payload = [];
|
|
||||||
pushWord(payload, result.type);
|
|
||||||
pushWord(payload, result.version);
|
|
||||||
pushWord(payload, fwblock);
|
|
||||||
for (var i = 0; i < FIRMWARE_BLOCK_SIZE; i++)
|
|
||||||
payload.push(result.data[fwblock * FIRMWARE_BLOCK_SIZE + i]);
|
|
||||||
var sensor = NODE_SENSOR_ID;
|
|
||||||
var command = C_STREAM;
|
|
||||||
var acknowledge = 0; // no ack
|
|
||||||
var type = ST_FIRMWARE_RESPONSE;
|
|
||||||
var td = encode(destination, sensor, command, acknowledge, type, payload);
|
|
||||||
console.log('-> ' + td.toString());
|
|
||||||
gw.write(td);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveRebootRequest(destination, db) {
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.update({
|
|
||||||
'id': destination
|
|
||||||
}, {
|
|
||||||
$set: {
|
|
||||||
'reboot': 1
|
|
||||||
}
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing reboot request to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkRebootRequest(destination, db, gw) {
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.find({
|
|
||||||
'id': destination
|
|
||||||
}, function(err, item) {
|
|
||||||
if (err)
|
|
||||||
console.log('Error checking reboot request');
|
|
||||||
else if (item.reboot == 1)
|
|
||||||
sendRebootMessage(destination, gw);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendRebootMessage(destination, gw) {
|
|
||||||
var sensor = NODE_SENSOR_ID;
|
|
||||||
var command = C_INTERNAL;
|
|
||||||
var acknowledge = 0; // no ack
|
|
||||||
var type = I_REBOOT;
|
|
||||||
var payload = "";
|
|
||||||
var td = encode(destination, sensor, command, acknowledge, type, payload);
|
|
||||||
console.log('-> ' + td.toString());
|
|
||||||
gw.write(td);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function appendData(str, db, gw) {
|
|
||||||
pos=0;
|
|
||||||
while (str.charAt(pos) != '\n' && pos < str.length) {
|
|
||||||
appendedString=appendedString+str.charAt(pos);
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
if (str.charAt(pos) == '\n') {
|
|
||||||
rfReceived(appendedString.trim(), db, gw);
|
|
||||||
appendedString="";
|
|
||||||
}
|
|
||||||
if (pos < str.length) {
|
|
||||||
appendData(str.substr(pos+1,str.length-pos-1), db, gw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function rfReceived(data, db, gw) {
|
|
||||||
if ((data != null) && (data != "")) {
|
|
||||||
console.log('<- ' + data);
|
|
||||||
// decoding message
|
|
||||||
var datas = data.toString().split(";");
|
|
||||||
var sender = +datas[0];
|
|
||||||
var sensor = +datas[1];
|
|
||||||
var command = +datas[2];
|
|
||||||
var ack = +datas[3];
|
|
||||||
var type = +datas[4];
|
|
||||||
var rawpayload="";
|
|
||||||
if (datas[5]) {
|
|
||||||
rawpayload = datas[5].trim();
|
|
||||||
}
|
|
||||||
var payload;
|
|
||||||
if (command == C_STREAM) {
|
|
||||||
payload = [];
|
|
||||||
for (var i = 0; i < rawpayload.length; i+=2)
|
|
||||||
payload.push(parseInt(rawpayload.substring(i, i + 2), 16));
|
|
||||||
} else {
|
|
||||||
payload = rawpayload;
|
|
||||||
}
|
|
||||||
// decision on appropriate response
|
|
||||||
switch (command) {
|
|
||||||
case C_PRESENTATION:
|
|
||||||
if (sensor == NODE_SENSOR_ID)
|
|
||||||
saveProtocol(sender, payload, db);
|
|
||||||
saveSensor(sender, sensor, type, db);
|
|
||||||
break;
|
|
||||||
case C_SET:
|
|
||||||
saveValue(sender, sensor, type, payload, db);
|
|
||||||
break;
|
|
||||||
case C_REQ:
|
|
||||||
break;
|
|
||||||
case C_INTERNAL:
|
|
||||||
switch (type) {
|
|
||||||
case I_BATTERY_LEVEL:
|
|
||||||
saveBatteryLevel(sender, payload, db);
|
|
||||||
break;
|
|
||||||
case I_TIME:
|
|
||||||
sendTime(sender, sensor, gw);
|
|
||||||
break;
|
|
||||||
case I_VERSION:
|
|
||||||
break;
|
|
||||||
case I_ID_REQUEST:
|
|
||||||
sendNextAvailableSensorId(db, gw);
|
|
||||||
break;
|
|
||||||
case I_ID_RESPONSE:
|
|
||||||
break;
|
|
||||||
case I_INCLUSION_MODE:
|
|
||||||
break;
|
|
||||||
case I_CONFIG:
|
|
||||||
sendConfig(sender, gw);
|
|
||||||
break;
|
|
||||||
case I_PING:
|
|
||||||
break;
|
|
||||||
case I_PING_ACK:
|
|
||||||
break;
|
|
||||||
case I_LOG_MESSAGE:
|
|
||||||
break;
|
|
||||||
case I_CHILDREN:
|
|
||||||
break;
|
|
||||||
case I_SKETCH_NAME:
|
|
||||||
saveSketchName(sender, payload, db);
|
|
||||||
break;
|
|
||||||
case I_SKETCH_VERSION:
|
|
||||||
saveSketchVersion(sender, payload, db);
|
|
||||||
break;
|
|
||||||
case I_REBOOT:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case C_STREAM:
|
|
||||||
switch (type) {
|
|
||||||
case ST_FIRMWARE_CONFIG_REQUEST:
|
|
||||||
var fwtype = pullWord(payload, 0);
|
|
||||||
var fwversion = pullWord(payload, 2);
|
|
||||||
sendFirmwareConfigResponse(sender, fwtype, fwversion, db, gw);
|
|
||||||
break;
|
|
||||||
case ST_FIRMWARE_CONFIG_RESPONSE:
|
|
||||||
break;
|
|
||||||
case ST_FIRMWARE_REQUEST:
|
|
||||||
var fwtype = pullWord(payload, 0);
|
|
||||||
var fwversion = pullWord(payload, 2);
|
|
||||||
var fwblock = pullWord(payload, 4);
|
|
||||||
sendFirmwareResponse(sender, fwtype, fwversion, fwblock, db, gw);
|
|
||||||
break;
|
|
||||||
case ST_FIRMWARE_RESPONSE:
|
|
||||||
break;
|
|
||||||
case ST_SOUND:
|
|
||||||
break;
|
|
||||||
case ST_IMAGE:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
checkRebootRequest(sender, db, gw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbc = require('mongodb').MongoClient;
|
|
||||||
dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err, db) {
|
|
||||||
if(err) {
|
|
||||||
console.log('Error connecting to database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log('Connected to database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName);
|
|
||||||
db.createCollection('node', function(err, collection) { });
|
|
||||||
db.createCollection('firmware', function(err, collection) { });
|
|
||||||
|
|
||||||
// ToDo : check for new hex files / only load if new / get type and version from filename
|
|
||||||
for (var i = 0; i < fwSketches.length; i++)
|
|
||||||
loadFirmware(i, 1, fwSketches[i], db);
|
|
||||||
|
|
||||||
var gw;
|
|
||||||
if (gwType == 'Ethernet') {
|
|
||||||
gw = require('net').Socket();
|
|
||||||
gw.connect(gwPort, gwAddress);
|
|
||||||
gw.setEncoding('ascii');
|
|
||||||
gw.on('connect', function() {
|
|
||||||
console.log('connected to ethernet gateway at ' + gwAddress + ":" + gwPort);
|
|
||||||
}).on('data', function(rd) {
|
|
||||||
appendData(rd.toString(), db, gw);
|
|
||||||
}).on('end', function() {
|
|
||||||
console.log('disconnected from gateway');
|
|
||||||
}).on('error', function() {
|
|
||||||
console.log('connection error - trying to reconnect');
|
|
||||||
gw.connect(gwPort, gwAddress);
|
|
||||||
gw.setEncoding('ascii');
|
|
||||||
});
|
|
||||||
} else if (gwType == 'Serial') {
|
|
||||||
var SerialPort = require('serialport').SerialPort;
|
|
||||||
gw = new SerialPort(gwPort, { baudrate: gwBaud });
|
|
||||||
gw.open();
|
|
||||||
gw.on('open', function() {
|
|
||||||
console.log('connected to serial gateway at ' + gwPort);
|
|
||||||
}).on('data', function(rd) {
|
|
||||||
appendData(rd.toString(), db, gw);
|
|
||||||
}).on('end', function() {
|
|
||||||
console.log('disconnected from gateway');
|
|
||||||
}).on('error', function() {
|
|
||||||
console.log('connection error - trying to reconnect');
|
|
||||||
gw.open();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error('unknown Gateway type');
|
|
||||||
}
|
|
||||||
setInterval(function() {
|
|
||||||
sendTime(BROADCAST_ADDRESS, NODE_SENSOR_ID, gw);
|
|
||||||
}, 5*60*1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -1,306 +0,0 @@
|
|||||||
<h1>MySensors with NodeJS Controller and OTA bootloader on RPi</h1>
|
|
||||||
<h2>Hardware</h2>
|
|
||||||
<p>Headless Raspberry Pi model B with wired Ethernet</p>
|
|
||||||
<p>I'm connecting a monitor only for first time boot to get the IP address and then connect via Putty/xRDP</p>
|
|
||||||
<h2>Download and install latest Raspbian version on a fresh SD-Card</h2>
|
|
||||||
<p>Tutorials for this task are available on the internet (<a href="http://downloads.raspberrypi.org/raspbian_latest">download</a>, <a href="http://sourceforge.net/projects/win32diskimager">copy to SD</a>)</p>
|
|
||||||
<h2>Setup base parameters</h2>
|
|
||||||
<p>(adjust to your needs - here: RPi-IP: 10.0.1.5 / Router-IP: 10.0.1.1 / password: test / timezone: Europe,Berlin / hostname: mysensors)</p>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / raspberry
|
|
||||||
<li>sudo nano /etc/network/interfaces
|
|
||||||
<p><font color="#FF0000">#</font>iface eth0 inet dhcp<br/>
|
|
||||||
<font color="#FF0000">iface eth0 inet static<br/>
|
|
||||||
address 10.0.1.5<br/>
|
|
||||||
netmask 255.255.255.0<br/>
|
|
||||||
gateway 10.0.1.1<br/>
|
|
||||||
nameserver 10.0.1.1</font></p>
|
|
||||||
<li>sudo raspi-config
|
|
||||||
<ul>
|
|
||||||
<li>1. Expand Filesystem
|
|
||||||
<li>2. Change User Password<br/>
|
|
||||||
<p>test</p>
|
|
||||||
<li>4. Internationalisation Options
|
|
||||||
<ul>
|
|
||||||
<li>I2. Change Timezone
|
|
||||||
<p>Europe / Berlin</p>
|
|
||||||
</ul>
|
|
||||||
<li>8. Advanced Options
|
|
||||||
<ul>
|
|
||||||
<li>A2. Hostname
|
|
||||||
<p>mysensors</p>
|
|
||||||
<li>A3. Memory Split
|
|
||||||
<p>16</p>
|
|
||||||
</ul>
|
|
||||||
</ul>
|
|
||||||
<li>Finish
|
|
||||||
<li>Reboot
|
|
||||||
</ul>
|
|
||||||
<h2>Install latest updates</h2>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>sudo apt-get update
|
|
||||||
<li>sudo apt-get upgrade
|
|
||||||
<li>sudo reboot
|
|
||||||
</ul>
|
|
||||||
<!--
|
|
||||||
<h2>Install Remote Desktop</h2>
|
|
||||||
<p>Optional… my RPi is headless and I use xRDP to connect from a Windows PC to run a graphical UI if needed (e.g. for Arduino IDE)</p>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>sudo apt-get install xrdp
|
|
||||||
<li><i>to confirm it's working as expected try to connect from a remote computer to the RPi (e.g. from a Windows PC using the "Remote Desktop Connection" tool)</i>
|
|
||||||
</ul>
|
|
||||||
-->
|
|
||||||
<h2>Install NodeJS (here: latest stable version available for RPi: 0.10.28)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>sudo mkdir /opt/node
|
|
||||||
<li>wget http://nodejs.org/dist/v0.10.28/node-v0.10.28-linux-arm-pi.tar.gz
|
|
||||||
<li>tar xvzf node-v0.10.28-linux-arm-pi.tar.gz
|
|
||||||
<li>rm node-v0.10.28-linux-arm-pi.tar.gz
|
|
||||||
<li>sudo cp -r node-v0.10.28-linux-arm-pi/* /opt/node
|
|
||||||
<li>rm -rf node-v0.10.28-linux-arm-pi/
|
|
||||||
<li>sudo nano /etc/profile
|
|
||||||
<p><font color="#FF0000">NODE_JS_HOME="/opt/node"<br/>
|
|
||||||
PATH="$PATH:$NODE_JS_HOME/bin"</font><br/>
|
|
||||||
export PATH</p>
|
|
||||||
<li>sudo reboot
|
|
||||||
<li><i>to confirm it's working as expected login as pi/test and run "node -v" which should return the installed node version (here: v0.10.28)</i>
|
|
||||||
</ul>
|
|
||||||
<h2>Install MongoDB</h2>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>sudo useradd -c mongodb -d /home/mongodb -m -U mongodb
|
|
||||||
<li>sudo mkdir -p /opt/mongo/bin
|
|
||||||
<li>sudo mkdir /var/lib/mongodb
|
|
||||||
<li>sudo chown mongodb:nogroup /var/lib/mongodb
|
|
||||||
<li>sudo mkdir /var/log/mongodb
|
|
||||||
<li>sudo chown mongodb:nogroup /var/log/mongodb
|
|
||||||
<li>wget https://github.com/brice-morin/ArduPi/raw/master/mongodb-rpi/mongo/bin/mongo
|
|
||||||
<li>wget https://github.com/brice-morin/ArduPi/raw/master/mongodb-rpi/mongo/bin/mongod
|
|
||||||
<li>wget https://github.com/brice-morin/ArduPi/raw/master/mongodb-rpi/mongo/bin/mongodump
|
|
||||||
<li>wget https://github.com/brice-morin/ArduPi/raw/master/mongodb-rpi/mongo/bin/bsondump
|
|
||||||
<li>sudo mv mongo /opt/mongo/bin/
|
|
||||||
<li>sudo mv mongod /opt/mongo/bin/
|
|
||||||
<li>sudo mv mongodump /opt/mongo/bin/
|
|
||||||
<li>sudo mv bsondump /opt/mongo/bin/
|
|
||||||
<li>sudo chmod -R 775 /opt/mongo/bin
|
|
||||||
<li>sudo ln -s /opt/mongo/bin/mongod /usr/bin/mongod
|
|
||||||
<li>wget https://raw.githubusercontent.com/skrabban/mongo-nonx86/master/debian/mongodb.conf
|
|
||||||
<li>wget https://raw.githubusercontent.com/skrabban/mongo-nonx86/master/debian/init.d
|
|
||||||
<li>sudo mv mongodb.conf /etc/mongodb.conf
|
|
||||||
<li>sudo mv init.d /etc/init.d/mongodb
|
|
||||||
<li>sudo chmod 755 /etc/init.d/mongodb
|
|
||||||
<li>sudo update-rc.d mongodb defaults
|
|
||||||
<li>sudo reboot
|
|
||||||
<li><i>to confirm it's working as expected login as pi/test and run "/opt/mongo/bin/mongo" which should start the mongo shell (type "exit" to close the shell again)</i>
|
|
||||||
<li><i>if the RPi loses power without proper shutdown while mongod is running, you can't restart without manually removing the pid file first. To do so run “sudo rm /var/lib/mongodb/mongod.lock”</i>
|
|
||||||
</ul>
|
|
||||||
<!--
|
|
||||||
<h2>Install Arduino (and update to latest 1.0.5)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>sudo apt-get install arduino
|
|
||||||
<li>wget http://arduino.googlecode.com/files/arduino-1.0.5-linux32.tgz
|
|
||||||
<li>tar zxvf arduino-1.0.5-linux32.tgz
|
|
||||||
<li>rm arduino-1.0.5-linux32.tgz
|
|
||||||
<li>cd arduino-1.0.5
|
|
||||||
<li>rm -rf hardware/tools
|
|
||||||
<li>sudo cp -ru lib /usr/share/arduino
|
|
||||||
<li>sudo cp -ru libraries /usr/share/arduino
|
|
||||||
<li>sudo cp -ru tools /usr/share/arduino
|
|
||||||
<li>sudo cp -ru hardware /usr/share/arduino
|
|
||||||
<li>sudo cp -ru examples /usr/share/doc/arduino-core
|
|
||||||
<li>sudo cp -ru reference /usr/share/doc/arduino-core
|
|
||||||
<li>cd ~
|
|
||||||
<li>rm -rf arduino-1.0.5
|
|
||||||
</ul>
|
|
||||||
<h2>Enable programming via GPIO UART (for serial gateway only)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>wget https://github.com/SpellFoundry/avrdude-rpi/archive/master.zip
|
|
||||||
<li>unzip master.zip
|
|
||||||
<li>rm master.zip
|
|
||||||
<li>cd avrdude-rpi-master
|
|
||||||
<li>sudo cp autoreset /usr/bin
|
|
||||||
<li>sudo cp avrdude-autoreset /usr/bin
|
|
||||||
<li>sudo mv /usr/bin/avrdude /usr/bin/avrdude-original
|
|
||||||
<li>sudo ln -s /usr/bin/avrdude-autoreset /usr/bin/avrdude
|
|
||||||
<li>cd ..
|
|
||||||
<li>rm -rf avrdude-rpi-master
|
|
||||||
</ul>
|
|
||||||
-->
|
|
||||||
<h2>Install MySensors from Git branch development (here: password: test)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>git clone https://github.com/mysensors/Arduino.git
|
|
||||||
<li>cd Arduino
|
|
||||||
<li>git checkout development
|
|
||||||
<li>cd NodeJsController
|
|
||||||
<li>npm install
|
|
||||||
<li><i>to confirm that the NodeJsController is working as expected and connects to the local mongo DB: "node NodeJsController.js" which should start the controller, connect to the database and run into an error connecting to the gateway. Stop node again via Ctrl-c</i>
|
|
||||||
</ul>
|
|
||||||
<h2>Start NodeJsController on boot</h2>
|
|
||||||
<ul>
|
|
||||||
<li>cd ~/Arduino/NodeJsController
|
|
||||||
<li>chmod 755 *.sh
|
|
||||||
<li>sudo cp MySensorsInitScript.sh /etc/init.d/mysensors
|
|
||||||
<li>sudo mkdir -p /usr/local/var/run
|
|
||||||
<li>sudo chmod -R 777 /usr/local/var/run
|
|
||||||
<li>sudo update-rc.d mysensors defaults
|
|
||||||
<li>sudo reboot
|
|
||||||
<li><i>to confirm that NodeJsController is starting at boot, check the log file /home/pi/logs/NodeJsController.log</i>
|
|
||||||
</ul>
|
|
||||||
<!--
|
|
||||||
<h2>Configure Arduino</h2>
|
|
||||||
<ul>
|
|
||||||
<li>start graphical UI (startx on RPi or via RDP)
|
|
||||||
<li>start Arduino IDE
|
|
||||||
<li>File - Preferences
|
|
||||||
<ul>
|
|
||||||
<li>set sketchbook location to /home/pi/Arduino
|
|
||||||
<li>enable verbose output for compilation
|
|
||||||
<li>enable verbose output for upload
|
|
||||||
</ul>
|
|
||||||
<li>Close Arduino IDE
|
|
||||||
</ul>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<h1>Serial Gateway</h1>
|
|
||||||
<h2>Hardware</h2>
|
|
||||||
<ul>
|
|
||||||
<li>as described for the standard Serial Gateway - using the standard Serial Gateway sketch with DEBUG (in MyConfig.h) disabled
|
|
||||||
<li>I'm using the version with inclusion mode button and LEDs based on no Arduino but a plain ATmega328p on a breadboard
|
|
||||||
<li>Connect directly to the serial GPIO pins of the RPi:
|
|
||||||
<p>Arduino GND <-> Raspberry Pi GND (pin 6)<br/>
|
|
||||||
Arduino RX <-> Raspberry Pi TX (pin 8)<br/>
|
|
||||||
Arduino TX <-> Raspberry Pi RX (pin 10)<br/>
|
|
||||||
Arduino RESET <-> Raspberry Pi GPIO22 (pin 15)</p>
|
|
||||||
<li>Right now I'm using a separate power supply for the serial gateway instead of the 3.3V or 5V pins of the RPi because I ran in some issues with power consumption during programming
|
|
||||||
</ul>
|
|
||||||
<h2>Disable default use of RPi serial port</h2>
|
|
||||||
<ul>
|
|
||||||
<li>sudo nano /etc/inittab
|
|
||||||
<p><font color="#FF0000">#</font>T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100</p>
|
|
||||||
<li>sudo nano /boot/cmdline.txt - remove part of the file:
|
|
||||||
<p><font color="#FF0000"><strike>console=ttyAMA0,115200</strike></font></p>
|
|
||||||
<li>cd /dev
|
|
||||||
<li>sudo ln -s ttyAMA0 ttyUSB9
|
|
||||||
<li>sudo apt-get install minicom
|
|
||||||
<li>sudo reboot
|
|
||||||
</ul>
|
|
||||||
<!--
|
|
||||||
<h2>Flash (assuming that the ATmega has a valid Arduino bootloader installed)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>start Arduino IDE
|
|
||||||
<ul>
|
|
||||||
<li>Tools - Board - Arduino - Arduino Pro or Pro Mini (5V, 16 MHz) w/ ATmega328
|
|
||||||
<li>Tools - Serial Port - /dev/ttyUSB9
|
|
||||||
<li>File - Sketchbook - SerialGateway
|
|
||||||
<li>File - Upload
|
|
||||||
</ul>
|
|
||||||
</ul>
|
|
||||||
-->
|
|
||||||
<h2>Setup NodeJsController</h2>
|
|
||||||
<ul>
|
|
||||||
<li>nano ~/Arduino/NodeJsController/NodeJsController.js
|
|
||||||
<p><font color="#FF0000">//</font>const gwType = 'Ethernet';<br/>
|
|
||||||
<font color="#FF0000">//</font>const gwAddress = '10.0.1.99';<br/>
|
|
||||||
<font color="#FF0000">//</font>const gwPort = 9999;<br/>
|
|
||||||
<font color="#FF0000"><strike>//</strike></font>const gwType = 'Serial';<br/>
|
|
||||||
<font color="#FF0000"><strike>//</strike></font>const gwPort = '<font color="#FF0000">/dev/ttyAMA0</font>';<br/>
|
|
||||||
<font color="#FF0000"><strike>//</strike></font>const gwBaud = 115200;</p>
|
|
||||||
<li>sudo service mysensors restart (at this point stop/restart doesn't work. use "sudo killall node" followed by "sudo service mysensors start" instead)
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h1>Ethernet Gateway</h1>
|
|
||||||
<h2>Hardware</h2>
|
|
||||||
<ul>
|
|
||||||
<li>as described for the standard Ethernet Gateway - using the standard Ethernet Gateway sketch with DEBUG (in MyConfig.h) disabled
|
|
||||||
<li>I'm using the version with inclusion mode button and LEDs based on no Arduino but a plain ATmega328p on a breadboard and the ENC28J60 Ethernet module
|
|
||||||
<li>here: 10.0.1.99 / port: 9999
|
|
||||||
</ul>
|
|
||||||
<!--
|
|
||||||
<h2>Adjust Sourcecode (here: address 10.0.1.99 / port: 9999)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>[I you are running into compilation errors due to missing files in the utility directory, do the following]
|
|
||||||
cp -R ~/Arduino/libraries/UIPEthernet/src/utility ~/Arduino/libraries/UIPEthernet/utility
|
|
||||||
<li>nano ~/Arduino/libraries/MySensors/MyConfig.h
|
|
||||||
<p><font color="#FF0000">//</font>#define DEBUG</p>
|
|
||||||
<li>start Arduino IDE
|
|
||||||
<ul>
|
|
||||||
<li>File - Sketchbook - EthernetGateway
|
|
||||||
<li>Edit source code
|
|
||||||
<p>#define IP_PORT 9999<br/>
|
|
||||||
IPAddress myIp (10, 0, 1, 99);</p>
|
|
||||||
<li>Optional : adjust source code to given hardware
|
|
||||||
<ul>
|
|
||||||
<li>Correct include for Ethernet module type (if not using ENC28J60)
|
|
||||||
<li>Switch MyGateway constructor if using inclusion mode button and LEDs
|
|
||||||
</ul>
|
|
||||||
<li>File - Save
|
|
||||||
</ul>
|
|
||||||
<h2>Flash (assuming that the ATmega has a valid Arduino bootloader installed)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Connect ethernet gateway to RPi (setup as above for serial gateway or via USB FTDI cable
|
|
||||||
<li>login as pi / test
|
|
||||||
<li>start Arduino IDE
|
|
||||||
<ul>
|
|
||||||
<li>Tools - Board - Arduino - Arduino Pro or Pro Mini (5V, 16 MHz) w/ ATmega328
|
|
||||||
<li>Tools - Serial Port - /dev/ttyUSB9 (or /dev/ttyUSB0 if connecting through USB cable)
|
|
||||||
<li>File - Sketchbook - EthernetGateway
|
|
||||||
<li>File - Upload
|
|
||||||
</ul>
|
|
||||||
</ul>
|
|
||||||
-->
|
|
||||||
<h2>Setup NodeJsController</h2>
|
|
||||||
<ul>
|
|
||||||
<li>nano ~/Arduino/NodeJsController/NodeJsController.js
|
|
||||||
<p>const gwAddress = '<font color="#FF0000">10.0.1.99</font>';</p>
|
|
||||||
<li>sudo service mysensors restart (at this point stop/restart doesn't work. use "sudo killall node" followed by "sudo service mysensors start" instead)
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h1>Sensor Node Bootloader</h1>
|
|
||||||
<h2>Hardware</h2>
|
|
||||||
<ul>
|
|
||||||
<li>as described for a simple Sensor Node
|
|
||||||
<li>here: no Arduino but a plain ATmega328p on a breadboard
|
|
||||||
<li>using DallasTemperature example (one sensor connected to pin 3)
|
|
||||||
</ul>
|
|
||||||
<h2>Compile Bootloader (can be skipped but good to see if everything is working)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>cd ~/Arduino/Bootloader
|
|
||||||
<li>make
|
|
||||||
<li><i>this should compile the bootloader and produce three files in the same directory: MyOtaBootloader.o / MyOtaBootloader.elf / MyOtaBootloader.hex</i>
|
|
||||||
</ul>
|
|
||||||
<h2>Flash Bootloader</h2>
|
|
||||||
<ul>
|
|
||||||
<li>cd ~/Arduino/Bootloader
|
|
||||||
<li>nano Makefile
|
|
||||||
<li>adjust avrdude settings to the available programmer. In my case (mySmartUSB MK3 programmer temporarily connected to RPi USB port):
|
|
||||||
<p>ISP_PORT = /dev/ttyUSB0<br/>
|
|
||||||
ISP_SPEED = 115200<br/>
|
|
||||||
ISP_PROTOCOL = stk500v2<br/>
|
|
||||||
ISP_MCU = m328p</p>
|
|
||||||
<li>make load
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h1>End-to-End Test</h1>
|
|
||||||
<h2>Add Firmware to Controller (here: DallasTemperatureSensor)</h2>
|
|
||||||
<ul>
|
|
||||||
<li>nano ~/Arduino/NodeJsController/NodeJsController.js
|
|
||||||
<p>const fwHexFiles = [ <font color="#FF0000">'../libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino'</font> ];<br/>
|
|
||||||
const fwDefaultType = <font color="#FF0000">0</font>;</p>
|
|
||||||
<li>sudo service mysensors restart (at this point stop/restart doesn't work. use "sudo killall node" followed by "sudo service mysensors start" instead)
|
|
||||||
</ul>
|
|
||||||
<h2>Flash Firmware OTA</h2>
|
|
||||||
<ul>
|
|
||||||
<li>tail -f ~/logs/NodeJsController.log
|
|
||||||
<li>Power up sensor node with OTA bootloader
|
|
||||||
</ul>
|
|
||||||
<h2>Check functionality</h2>
|
|
||||||
<ul>
|
|
||||||
<li>in addition to watching the log file (tail command above) you can connect to the mongo db using a client (e.g. <a href="http://robomongo.org">Robomongo</a>) and check if new values are written to the database as a real end-to-end test once the bootloader finished uploading the firmware
|
|
||||||
</ul>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
var NodeIdToReboot = 1;
|
|
||||||
|
|
||||||
const dbAddress = '127.0.0.1';
|
|
||||||
const dbPort = 27017;
|
|
||||||
const dbName = 'MySensorsDb';
|
|
||||||
|
|
||||||
var dbc = require('mongodb').MongoClient;
|
|
||||||
dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err, db) {
|
|
||||||
if (err) {
|
|
||||||
console.log('Error connecting to database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName);
|
|
||||||
} else {
|
|
||||||
db.collection('node', function(err, c) {
|
|
||||||
c.update({
|
|
||||||
'id': NodeIdToReboot
|
|
||||||
}, {
|
|
||||||
$set: {
|
|
||||||
'reboot': 1
|
|
||||||
}
|
|
||||||
}, function(err, result) {
|
|
||||||
if (err)
|
|
||||||
console.log("Error writing reboot request to database");
|
|
||||||
else
|
|
||||||
console.log("Reboot request saved to database");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "NodeJsController",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "MySensors Controller based on Node JS",
|
|
||||||
"dependencies": {
|
|
||||||
"mongodb": "~1.4.7",
|
|
||||||
"net": "~1.0.2",
|
|
||||||
"requestify": "^0.1.16",
|
|
||||||
"serialport": "^1.4.5"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user