diff --git a/NodeJsController/MySensors.sh b/NodeJsController/MySensors.sh new file mode 100644 index 0000000..99bf500 --- /dev/null +++ b/NodeJsController/MySensors.sh @@ -0,0 +1,8 @@ +#!/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 $PIDFILE diff --git a/NodeJsController/MySensorsInitScript.sh b/NodeJsController/MySensorsInitScript.sh new file mode 100644 index 0000000..38a9feb --- /dev/null +++ b/NodeJsController/MySensorsInitScript.sh @@ -0,0 +1,160 @@ +#! /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 +# +# 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 + +: + diff --git a/NodeJsController/NodeJsController.js b/NodeJsController/NodeJsController.js index f033e1c..6c52630 100644 --- a/NodeJsController/NodeJsController.js +++ b/NodeJsController/NodeJsController.js @@ -4,6 +4,7 @@ const gwPort = 9999; //const gwType = 'Serial'; //const gwPort = 'COM4'; +//const gwPort = '/dev/ttyUSB0'; //const gwBaud = 115200; const dbAddress = '127.0.0.1'; @@ -116,6 +117,7 @@ const P_ULONG32 = 5; const P_CUSTOM = 6; var fs = require('fs'); +var appendedString=""; function crcUpdate(old, value) { var c = old ^ value; @@ -516,6 +518,22 @@ function sendRebootMessage(destination, gw) { 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); @@ -526,7 +544,10 @@ function rfReceived(data, db, gw) { var command = +datas[2]; var ack = +datas[3]; var type = +datas[4]; - var rawpayload = datas[5].trim(); + var rawpayload=""; + if (datas[5]) { + rawpayload = datas[5].trim(); + } var payload; if (command == C_STREAM) { payload = []; @@ -635,9 +656,7 @@ dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err gw.on('connect', function() { console.log('connected to ethernet gateway at ' + gwAddress + ":" + gwPort); }).on('data', function(rd) { - var rds = rd.toString().split('\n'); - for (var i = 0; i < rds.length; i++) - rfReceived(rds[i].trim(), db, gw); + appendData(rd.toString(), db, gw); }).on('end', function() { console.log('disconnected from gateway'); }).on('error', function() { @@ -646,14 +665,13 @@ dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err gw.setEncoding('ascii'); }); } else if (gwType == 'Serial') { - gw = require('serialport').SerialPort(gwPort, { baudrate: gwBaud }, false); + 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) { - var rds = rd.toString().split('\n'); - for (var i = 0; i < rds.length; i++) - rfReceived(rds[i].trim(), db, gw); + appendData(rd.toString(), db, gw); }).on('end', function() { console.log('disconnected from gateway'); }).on('error', function() { diff --git a/NodeJsController/Readme.html b/NodeJsController/Readme.html new file mode 100644 index 0000000..6766851 --- /dev/null +++ b/NodeJsController/Readme.html @@ -0,0 +1,309 @@ +

MySensors with NodeJS Controller and OTA bootloader on RPi

+

My RPi runs Arduino to compile and flash sketches and the bootloader. With this setup no separate PC/Mac is needed. Exactly the same could be achieved (faster) using a PC/Mac for all the Arduino compilation and flashing activities – just skip the steps and execute on PC/Mac instead. +

Hardware

+

Headless Raspberry Pi model B with wired Ethernet

+

I'm connecting a monitor only for first time boot to get the IP address and then connect via Putty/xRDP

+

Download and install latest Raspbian version on a fresh SD-Card

+

Tutorials for this task are available on the internet (download, copy to SD)

+

Setup base parameters

+

(adjust to your needs - here: RPi-IP: 10.0.1.5 / Router-IP: 10.0.1.1 / password: test / timezone: Europe,Berlin / hostname: mysensors)

+ +

Install latest updates

+ + +

Install NodeJS (here: latest stable version available for RPi: 0.10.28)

+ +

Install MongoDB

+ + +

Install MySensors from Git branch development (here: password: test)

+ +

Start NodeJsController on boot

+ + + +

Serial Gateway

+

Hardware

+ +

Disable default use of RPi serial port

+ + +

Setup NodeJsController

+ + +

Ethernet Gateway

+

Hardware

+ + +

Setup NodeJsController

+ + +

Sensor Node Bootloader

+

Hardware

+ +

Compile Bootloader (can be skipped but good to see if everything is working)

+ +

Flash Bootloader

+ + +

End-to-End Test

+

Add Firmware to Controller

+ +

Flash Firmware OTA

+ +

Check functionality

+ diff --git a/NodeJsController/package.json b/NodeJsController/package.json index f9ec6ff..a4866a6 100644 --- a/NodeJsController/package.json +++ b/NodeJsController/package.json @@ -3,7 +3,8 @@ "version": "0.0.1", "description": "MySensors Controller based on Node JS", "dependencies": { + "mongodb": "~1.4.7", "net": "~1.0.2", - "mongodb": "~1.4.7" + "serialport": "^1.4.5" } }