Merge pull request #26 from ToSa27/development

Installation Instructions and RPi init script and fixes for use of serial gateway
This commit is contained in:
Bruce Lacey
2014-08-23 12:01:06 -07:00
5 changed files with 505 additions and 9 deletions
+8
View File
@@ -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 </dev/null &
CHILD="$!"
echo $CHILD > $PIDFILE
+160
View File
@@ -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 <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
:
+26 -8
View File
@@ -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() {
+309
View File
@@ -0,0 +1,309 @@
<h1>MySensors with NodeJS Controller and OTA bootloader on RPi</h1>
<p>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.
<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>nano NodeJsController.js (optional : removing sample firmwares)
<p>const fwHexFiles = <font color="#FF0000">[ ]</font>;</p>
<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
</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
</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</h2>
<ul>
<li>nano ~/Arduino/NodeJsController/NodeJsController.js
<p>const fwHexFiles = [ 'DallasTemperatureSensor.cpp.hex' ];<br/>
const fwDefaultType = 0;</p>
<li>sudo service mysensors restart
</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
</ul>
+2 -1
View File
@@ -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"
}
}