= Sensor node/mote Connect2Server
Apostolos rootApostolos@swarmlab.io
// Metadata:
:description: IoT  Διαδίκτυο των Αντικειμένων
:keywords: iot, imu, AHRS
:data-uri:
:toc: right
:toc-title: Πίνακας περιεχομένων
:toclevels: 4
:source-highlighter: highlight
:icons: font
:sectnums: 

include::header.adoc[]


{empty} +

== Create a mote with Raspberry Pi

The Raspberry Pi is a low-cost credit-card sized single-board computer. The Raspberry Pi was created in the UK by the Raspberry Pi Foundation. The Raspberry Pi Foundation's goal is to "advance the education of adults and children, particularly in the field of computers, computer science and related subjects." 

https://simple.wikipedia.org/wiki/Raspberry_Pi[Wikipedia]


.Raspberry Pi 2 Model B
image:./Raspberry_Pi_2_Model_B_v1.1_top_new.jpg[alt="Raspberry Pi 2 Model B"]

[NOTE]
.Remember
====
A mote is a node but a node is not always a mote!

image:./arduino-connect-pi.jpg[alt="Raspberry Pi and Arduino"]

====


=== Install Raspberry Pi 

==== Step 1: Download Raspbian

https://www.raspberrypi.org/downloads/raspbian/[Download] the Raspbian disc image  - Choose Raspbian Lite

[NOTE]
.Why Raspbian Lite?
====
Because it is a lightweight version of the Raspbian and it doesn’t have a graphical user interface installed. 

This means that it doesn’t have any unnecessary software installed that we don’t need for our  projects, so this makes it the perfect solution for future automation projects.
====

==== Step 2: Unzip the file


- Windows users, you’ll want 7-Zip.
- Linux users will use the appropriately named Unzip.


==== Step 3: Write the disc image to your microSD card

Next, pop your microSD card into your computer and write the disc image to it. You’ll need a specific program to do this:

- Windows users, your answer is https://sourceforge.net/projects/win32diskimager/[Win32 Disk Imager].
- Linux people, https://www.balena.io/etcher/[Etcher – which also works on Windows – is what the Raspberry Pi Foundation recommends.]

The process of actually writing the image will be slightly different across these programs, but it’s pretty self-explanatory no matter what you’re using. 

- Each of these programs will have you select the destination (make sure you’ve picked your microSD card!) and the disc image (the unzipped Raspbian file). 
- Choose, double-check, and then hit the button to write.


==== Step 4: Enabling SSH

- Windows users

.Create ssh file (no extension)
image:./ssh-file-to-sd-card.jpg[alt="Create ssh file"]

- Linux Users

.Create ssh file
[source,bash]
----
sudo fdisk -l
# find dev and Boot partition
sudo mkdir /mnt/sdcardP1
sudo mount /dev/device_partion_boot /mnt/sdcardP1 -rw
cd /mnt/sdcardP1
sudo touch ssh
----

==== Step 5: Put the microSD card in your Pi and boot up

Your default credentials are username **pi** and password **raspberry**


==== Step 6: Access via SSH


-  The boot protocol for the ethernet interface is set to DHCP by default 

You can find the open SSH ports on your network using the nmap utility:


.find ports on Network
[source,bash]
----
nmap -p 22 --open -sV 192.168.1.0/24
----

You should find your pi listed in the output along with the IP assigned to the pi.


- You can change the boot protocol to static and define a static IP address for the pi by editing the ifcfg-eth0 file:

.static IP address
[source,bash]
----
sudo fdisk -l
# find dev and Boot partition
sudo mkdir /mnt/sdcardP1
sudo mount /dev/device_partion_ext /mnt/sdcardP1 -rw
cd /mnt/sdcardP1
vi /etc/sysconfig/network-scripts/ifcfg-eth0 
----

Then edit the file to suit your needs

.static IP address
[source,bash]
----
DEVICE=eth0
BOOTPROTO=static
ONBOOT=yes
NETWORK=192.168.1.0
NETMASK=255.255.255.0
IPADDR=192.168.1.200
GATEWAY=192.168.1.1
----


==== Step 7: Configure your Raspberry Pi.

**raspi-config** is the Raspberry Pi configuration tool 


.config Pi
[source,bash]
----
sudo raspi-config
----

It has the following options available:

.config options
[source,bash]
----

┌───────────────────┤ Raspberry Pi Software Configuration Tool (raspi-config) ├────────────────────┐
│                                                                                                  │
│        1 Change User Password Change password for the current user                               │
│        2 Network Options      Configure network settings                                         │
│        3 Boot Options         Configure options for start-up                                     │
│        4 Localisation Options Set up language and regional settings to match your location       │
│        5 Interfacing Options  Configure connections to peripherals                               │
│        6 Overclock            Configure overclocking for your Pi                                 │
│        7 Advanced Options     Configure advanced settings                                        │
│        8 Update               Update this tool to the latest version                             │
│        9 About raspi-config   Information about this configuration tool                          │
│                                                                                                  │
│                                                                                                  │
│                                                                                                  │
│                           <Select>                           <Finish>                            │
│                                                                                                  │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘

----


=== Arduino Uno Raspberry Pi Serial Communication

==== Serial config on Raspi


.config 1 (recommended)
[source,bash]
----
whoami
sudo usermod -a -G dialout pi
reboot
----

This gives read/write permission for all users to the Raspberry Pi (potentially unsafe):

.config 2
[source,bash]
----
sudo chmod 777 /dev/ttyACM0
----

This provides some configuration for the Arduino serial connection:

.configuration for the Arduino serial connection
[source,bash]
----
sudo stty -F /dev/ttyACM0 cs8 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts
----

==== Reading in  arduino

.C code in the arduino
[source,bash]
----
void loop() {
    meas = analogRead(a);
    if (Serial.available())
    {
        if (Serial.read() == '1')
        {
            Serial.println(meas);
        }
    }
}
----

==== Python

.Python code in Raspberry Pi
[source,python]
----
import serial
from datetime import datetime
from time import sleep
now = datetime.now()

ser = serial.Serial('/dev/ttyACM0', 9600)
ser.write("1".encode())
sleep(0.05);
s = ser.readline()

file = open("dataset", "a")
file.write(now.strftime("%Y-%m-%d %H:%M") + " Sensor Value:" + str(s)+ "\n")
file.close()
----

==== PHP

[NOTE]
.PHP Class
====
https://gist.github.com/gravataLonga/6c89821b845d15e939a0/archive/0d0063684d388a8ff53df8e73e55f4cb1187d7cd.zip[Download Class]
====

.PHP code in Raspberry Pi - read
[source,php]
----
<?php

include "php_serial.class.php";

$serial = new phpSerial();
$serial->deviceSet("/dev/ttyACM0");
$serial->confBaudRate(9600);
$serial->confParity("none");
$serial->confCharacterLength(8);
$serial->confStopBits(1);
$serial->confFlowControl("none");

$serial->deviceOpen();
$read = $serial->readPort();
$serial->deviceClose();

echo $read
----


Sends a string to the Arduino.

.PHP code in Raspberry Pi - send
[source,php]
----
<?php

error_reporting(E_ALL);
ini_set('display_errors', '1');
include "php_serial.class.php";

$serial = new phpSerial;
$serial->deviceSet("/dev/ttyAMA0");
$serial->confBaudRate(115200);
$serial->confParity("none");
$serial->confCharacterLength(8);
$serial->confStopBits(1);
$serial->deviceOpen();
$serial->sendMessage("Hello from my PHP script, say hi back!");

$serial->deviceClose();

echo "I've sended a message! \n\r";
----

==== NodeJS

[NOTE]
====
Read the writing carefully on your Raspberry Pi circuit board to confirm it indicates something like “Raspberry Pi 4 Model B” or “Raspberry Pi 2 Model B”. If in doubt, run the following command in the terminal:

    $ uname -m

If the result returned starts with **“armv6”**, you are running a Raspberry Pi based on the older ARMv6 chipset and the next Node.js installation step **will not work**; otherwise, you are ready for the next step.
====


.Install NodeJS
[source,bash]
----
curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
sudo apt install -y nodejs
npm install raspi-serial
----


.Install NodeJS - armv6
[source,bash]
----
cd ~
wget http://nodejs.org/dist/v6.2.1/node-v6.2.1-linux-armv6l.tar.gz
tar -xzf node-v6.2.1-linux-armv6l.tar.gz
cd node-v6.2.1-linux-armv6l/
sudo cp -R * /usr/local/
export PATH=$PATH:/usr/local/bin
npm install raspi-serial
----


.NodeJS code in Raspberry Pi - read
[source,c]
----
mport { init } from 'raspi';
import { Serial } from 'raspi-serial';
 
init(() => {
  var serial = new Serial();
  serial.open(() => {
    serial.on('data', (data) => {
      process.stdout.write(data);
    });
    serial.write('Hello from raspi-serial');
  });
});
----



=== Send data2server


==== NodeJS

.NodeJS code in Raspberry Pi - send 
[source,c]
----

...
var serverIOT=IP_SERVER
	const socket = require('socket.io-client')('https://'+serverIOT+':9080');
	socket.on('connect', function () {
		socket.emit('subscribe', log);
 		var obj = new Object();
   	obj.room = log;
   	obj.message  = data;
   	var text = JSON.stringify(obj);
		var text1 = Buffer.from(text);
		var text5 = text1.toString('base64'); 
		socket.emit('log', text5, log )
        //console.log(util.inspect(text5, false, null, true /* enable colors */))

    res.json({
        'message':"ok"
    });
	});

...

----

==== PHP

.PHP code in Raspberry Pi - send 
[source,php]
----
require "vendor/autoload.php";

$client = new \GuzzleHttp\Client(["base_uri" => "http://SERVER"]);

$options = [

    'form_params' => [

        "fruit" => "apple"

       ]

   ]; 

$response = $client->post("/post", $options);

echo $response->getBody();
----


[NOTE]
====
composer require guzzlehttp/guzzle
====



== IoT Server

.Imaging a swarm
image:./Swarming2.png[alt="Swarm"]


.Architecture of swarm communication
image:./swarmlabn-1.png[alt="Swarm Gateway"]

- Red Note: Sensor Node and Gateway Role
- Black and Red Note:  Sensor Node - Client

=== NodeJS

==== Gateway

Gateway forwards ore Client Data to the correct application - Server

.NodeJS code in Raspberry Pi - Server Gateway
[source,c]
----
// Setup basic express server
var express = require('express');
var app = express();
var path = require('path');
var server = require('http').createServer(app);
var io = require('../..')(server);
//const util = require('util')
var bodyParser = require('body-parser')
var serverIoT=SERVER
app.use(bodyParser.json())

const { check, validationResult } = require('express-validator');
app.post('/log', [
  check('serverdebug').isFQDN({ require_tld: true, allow_underscores: false, allow_trailing_dot: false }),
  check('log').isBase64(),
  check('data').isBase64()
], (req, res) => {
  const errors = validationResult(req)
  if (!errors.isEmpty()) {
    return res.status(422).json({ errors: errors.array() })
  }
        //console.log(util.inspect(req, false, null, true /* enable colors */))
        //console.log(util.inspect(req.body, false, null, true /* enable colors */))
        var log = req.body.log
        var data = req.body.data
        var serverdebug = req.body.serverdebug
	data = Buffer.from(data, 'base64').toString('utf-8')
	const socket = require('socket.io-client')('https://'+serverIoT+':9080');
	socket.on('connect', function () {
		socket.emit('subscribe', log);
 		var obj = new Object();
   	obj.room = log;
   	obj.message  = data;
   	var text = JSON.stringify(obj);
		var text1 = Buffer.from(text);
		var text5 = text1.toString('base64'); 
		socket.emit('log', text5, log )
        //console.log(util.inspect(text5, false, null, true /* enable colors */))

    res.json({
        'message':"ok"
    });
	});
});
app.get('/test', (req, res) => {
    var text5 = "ok";
    res.json({
        'message':text5
    });
});

// Change the 404 message modifing the middleware
app.use(function(req, res, next) {
    res.status(404).send("Sorry, that route doesn't exist. Have a nice day :)");
});

// start the server in the port 3000 !
app.listen(9089, function () {
    console.log('Example app listening on port 9089.');
});

----

==== Server - Broadcast


Broadcast data to swarm clients

The server receives data from its client and echoes its back.

.NodeJS code in Raspberry Pi - Server broadcast
[source,c]
----
// Setup basic express server
var express = require('express');
var app = express();
var path = require('path');
var server = require('http').createServer(app);
var io = require('../..')(server);
var port = process.env.PORT || 9080;
const util = require('util')



server.listen(port, () => {
  console.log('Server listening at port %d', port);
});

// Routing
app.use(express.static(path.join(__dirname, 'public')));

var numUsers = 0;

io.on('connection', (socket) => {

    socket.on('subscribe', function(room) { 
        console.log('joining room', room);
        socket.join(room); 
    })

    socket.on('unsubscribe', function(room) {  
        console.log('leaving room', room);
        socket.leave(room); 
    })

  // when the client emits 'new message', this listens and executes
  socket.on('log', (data, room) => {
			console.log(util.inspect(data, false, null, true /* enable colors */))
      socket.broadcast.to(room).emit('message', data)
        console.log('broadcast', room);

  });


});


----

==== Client Connection Raspberry

Swarm client recieves data 


.NodeJS code in Raspberry Pi - Client
[source,c]
----
// Setup basic express server
var express = require('express');
var app = express();
var path = require('path');
var server = require('http').createServer(app);
var io = require('../..')(server);
var port = process.env.PORT || 9000;

server.listen(port, () => {
  console.log('Server listening at port %d', port);
});

// Routing
app.use(express.static(path.join(__dirname, 'public')));


var numUsers = 0;

io.on('connection', (socket) => {
  var addedUser = false;

  // when the client emits 'new message', this listens and executes
  socket.on('new message', (data, room) => {
  	console.log('Server listening at port %d', data);
    });

});

----

==== Client Connection - Web Server

.Javascript code 
[source,c]
----
$(function() {
  var FADE_TIME = 150; // ms
  var TYPING_TIMER_LENGTH = 400; // ms
  var COLORS = [
    '#e21400', '#91580f', '#f8a700', '#f78b00',
    '#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
    '#3b88eb', '#3824aa', '#a700ff', '#d300e7'
  ];

  // Initialize variables
  var $window = $(window);
  var $usernameInput = $('.usernameInput'); // Input for username
  var $messages = $('.messages'); // Messages area
  var $inputMessage = $('.inputMessage'); // Input message input box

  var $loginPage = $('.login.page'); // The login page
  var $chatPage = $('.chat.page'); // The room page

  // Prompt for setting a username
  var username;
  var connected = false;
  var typing = false;
  var lastTypingTime;
  var $currentInput = $usernameInput.focus();

  var socket = io();

  const addParticipantsMessage = (data) => {
    var message = '';
    if (data.numUsers === 1) {
      message += "there's 1 participant";
    } else {
      message += "there are " + data.numUsers + " participants";
    }
    log(message);
  }

  // Sets the client's username
  const setUsername = () => {
    username = cleanInput($usernameInput.val().trim());

    // If the username is valid
    if (username) {
      $loginPage.fadeOut();
      $chatPage.show();
      $loginPage.off('click');
      $currentInput = $inputMessage.focus();

      // Tell the server your username
      socket.emit('add user', username);
    }
  }

  // Sends a  message
  const sendMessage = () => {
    var message = $inputMessage.val();
    // Prevent markup from being injected into the message
    message = cleanInput(message);
    // if there is a non-empty message and a socket connection
    if (message && connected) {
      $inputMessage.val('');
      addChatMessage({
        username: username,
        message: message
      });
      // tell server to execute 'new message' and send along one parameter
      socket.emit('new message', message);
    }
  }

  // Log a message
    const log = (message, options) => {
    var $el = $('<li>').addClass('log').text(message);
    addMessageElement($el, options);
  }

  // Adds the visual  message to the message list
  const addChatMessage = (data, options) => {
    // Don't fade the message in if there is an 'X was typing'
    var $typingMessages = getTypingMessages(data);
    options = options || {};
    if ($typingMessages.length !== 0) {
      options.fade = false;
      $typingMessages.remove();
    }

    var $usernameDiv = $('<span class="username"/>')
      .text(data.username)
      .css('color', getUsernameColor(data.username));
    var $messageBodyDiv = $('<span class="messageBody">')
      .text(data.message);

    var typingClass = data.typing ? 'typing' : '';
    var $messageDiv = $('<li class="message"/>')
      .data('username', data.username)
      .addClass(typingClass)
      .append($usernameDiv, $messageBodyDiv);

    addMessageElement($messageDiv, options);
  }

  // Adds the visual  typing message
  const addChatTyping = (data) => {
    data.typing = true;
    data.message = 'is typing';
    addChatMessage(data);
  }

  // Removes the visual  typing message
  const removeChatTyping = (data) => {
    getTypingMessages(data).fadeOut(function () {
      $(this).remove();
    });
  }

  // Adds a message element to the messages and scrolls to the bottom
  // el - The element to add as a message
  // options.fade - If the element should fade-in (default = true)
  // options.prepend - If the element should prepend
  //   all other messages (default = false)
  const addMessageElement = (el, options) => {
    var $el = $(el);

    // Setup default options
    if (!options) {
      options = {};
    }
    if (typeof options.fade === 'undefined') {
      options.fade = true;
    }
    if (typeof options.prepend === 'undefined') {
      options.prepend = false;
    }

    // Apply options
    if (options.fade) {
      $el.hide().fadeIn(FADE_TIME);
    }
    if (options.prepend) {
      $messages.prepend($el);
    } else {
      $messages.append($el);
    }
    $messages[0].scrollTop = $messages[0].scrollHeight;
  }

  // Prevents input from having injected markup
  const cleanInput = (input) => {
    return $('<div/>').text(input).html();
  }

  // Updates the typing event
  const updateTyping = () => {
    if (connected) {
      if (!typing) {
        typing = true;
        socket.emit('typing');
      }
      lastTypingTime = (new Date()).getTime();

      setTimeout(() => {
        var typingTimer = (new Date()).getTime();
        var timeDiff = typingTimer - lastTypingTime;
        if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
          socket.emit('stop typing');
          typing = false;
        }
      }, TYPING_TIMER_LENGTH);
    }
  }

  // Gets the 'X is typing' messages of a user
  const getTypingMessages = (data) => {
    return $('.typing.message').filter(function (i) {
      return $(this).data('username') === data.username;
    });
  }

  // Gets the color of a username through our hash function
  const getUsernameColor = (username) => {
    // Compute hash code
    var hash = 7;
    for (var i = 0; i < username.length; i++) {
       hash = username.charCodeAt(i) + (hash << 5) - hash;
    }
    // Calculate color
    var index = Math.abs(hash % COLORS.length);
    return COLORS[index];
  }

  // Keyboard events

  $window.keydown(event => {
    // Auto-focus the current input when a key is typed
    if (!(event.ctrlKey || event.metaKey || event.altKey)) {
      $currentInput.focus();
    }
    // When the client hits ENTER on their keyboard
    if (event.which === 13) {
      if (username) {
        sendMessage();
        socket.emit('stop typing');
        typing = false;
      } else {
        setUsername();
      }
    }
  });

  $inputMessage.on('input', () => {
    updateTyping();
  });

  // Click events

  // Focus input when clicking anywhere on login page
  $loginPage.click(() => {
    $currentInput.focus();
  });

  // Focus input when clicking on the message input's border
  $inputMessage.click(() => {
    $inputMessage.focus();
  });

  // Socket events

  // Whenever the server emits 'login', log the login message
  socket.on('login', (data) => {
    connected = true;
    // Display the welcome message
    var message = "Welcome to Swarmlab Chat – ";
    log(message, {
      prepend: true
    });
    addParticipantsMessage(data);
  });

  // Whenever the server emits 'new message', update the  body
  socket.on('new message', (data) => {
    addChatMessage(data);
  });

  // Whenever the server emits 'user joined', log it in the  body
  socket.on('user joined', (data) => {
    log(data.username + ' joined');
    addParticipantsMessage(data);
  });

  // Whenever the server emits 'user left', log it in the  body
  socket.on('user left', (data) => {
    log(data.username + ' left');
    addParticipantsMessage(data);
    removeChatTyping(data);
  });

  // Whenever the server emits 'typing', show the typing message
  socket.on('typing', (data) => {
    addChatTyping(data);
  });

  // Whenever the server emits 'stop typing', kill the typing message
  socket.on('stop typing', (data) => {
    removeChatTyping(data);
  });

  socket.on('disconnect', () => {
    log('you have been disconnected');
  });

  socket.on('reconnect', () => {
    log('you have been reconnected');
    if (username) {
      socket.emit('add user', username);
    }
  });

  socket.on('reconnect_error', () => {
    log('attempt to reconnect has failed');
  });

});

----




[appendix]
== Config

- https://www.raspberrypi.org/documentation/configuration/raspi-config.md





:hardbreaks:

{empty} +
{empty} +
{empty} 

:!hardbreaks:

'''

.Reminder
[NOTE]
====
:hardbreaks:
Caminante, no hay camino, 
se hace camino al andar.

Wanderer, there is no path, 
the path is made by walking.

*Antonio Machado* Campos de Castilla
====
