The objective of this tutorial is to create a simple IoT system that will alert us through a nice web interface whenever the temperature of our wine cellar is not between 10°C and 15°C. We will call this system FineWine.
System architecture:
This system, based on our understanding of intelligent micro-system, is composed of the following organs:
- Nerve center: where the hub and its data are located.
- Admin dashboard: the administration interface of your hub.
- VirtualTemperatureSensor organ: simulate the temperature sensor of the wine cellar continuously and synchronize the samples with the hub.
- Analytics organ: analyzes the temperature and alerts if the temperature is ok or not.
- Monitoring interface organ: a web interface, displaying the current status of our wine cellar.
Install spinal-system-basic
The first step is to create the directory where the project will be stored. We will call it fine-wine-system. Then, install inside the “spinal-system-basic” template. We suggest that before running the following command you initialize an npm project (with npm init):
~/$ mkdir fine-wine-system ~/$ cd fine-wine-system ~/fine-wine-system$ npm init -y ~/fine-wine-system$ npm i https://github.com/spinalcom/spinal-browser-admin.git
Launch spinal-system
~/fine-wine-system$ pm2 start launch.config.js
PM2 will automatically start the Spinal Hub and the organs. Take care, if you have another hub running on port 7777, your new hub will not be launched ! (here is a command to see what port are used on ubuntu: sudo netstat -lp --inet)
After this installation, only SpinalHub is running on port 7777. SpinalHub containes a web server that provide his own Admin interface. Here is the architecture of the system you have after this first install:
Connect to Admin UI
As we have done in the getting started, connect to the admin dashboard to see if you hub is running :
http://127.0.0.1:7777/html/admin
The default admin account is :
Username | client ID | Password |
---|---|---|
admin | 168 | JHGgcz45JKilmzknzelf65ddDadggftIO98P |
- .browser_organs : This is your web browser folder
- .config_env : Your env configuration
- nerve-center : The Spinalcom nerve center
- node_modules : npm (node package manager) & spinal-package
- .apps.json : installed package of your spinal system
- .config.json : spinalcom connection configuration file
- launch.config.js : Start your hub using pm2
- package.json : config of your package
The digital twin will be defined in the libraries.
The model is very simple, since we will not manage a lot of data. It is just a JavaScript function, which we will assume it's a class, from where we can create instances and store and load data.
The three important points to follow in the creation of a model are:
- The first instruction of the function must call the super() method passing its context as an argument. As this function will inherit from a Model class inside the SpinalCore library, we need to call the parent constructor.
- Every argument that needs to be synchronized should be passed using the add_attr() method like in the example below.
- To inherit from the Model class, we call the extend() method from the SpinalCore library.
Create a folder called spinal-model-wine-cellar at the root of your project and, inside it, create a file called model.js:
~/fine-wine-system$ mkdir spinal-model-wine-cellar
spinal-model-wine-cellar/model.js
// wineCave-model.js function WineCellarModel() { WineCellarModel.super(this); this.add_attr({ temperature: 0, danger: false }); } spinalCore.extend(WineCellarModel, Model); module.exports = WineCellarModel;
Now that we have our model, we need to monitor its state. for this we are going to simulate a temperature sensor that modify its attribute “temperature”. Later we will be able to connect with a real IoT sensor.
Make a new folder called virtualTempSensorOrgan where we are going to define our process.
~/fine-wine-system$ mkdir virtualTempSensorOrgan
An organ has to be linked with the Spinalhub via a connector. For this tutorial, we use the Node.js connector, which has already been installed at the first step.
Inside the organ folder, create the file called index.js with the code below.
virtualTempSensorOrgan/index.js
// 1 var spinalCore = require("spinal-core-connectorjs"); require("../spinal-model-wine-cellar/model.js"); // 2 console.log("Configuration Environment not found, using default config"); process.env.SPINALHUB_PORT = 7777; process.env.SPINALHUB_IP = "127.0.0.1"; process.env.SPINAL_USER_ID = 168; process.env.SPINAL_PASSWORD = "JHGgcz45JKilmzknzelf65ddDadggftIO98P"; var conn = spinalCore.connect( `http://${process.env.SPINAL_USER_ID}:${process.env.SPINAL_PASSWORD}@${ process.env.SPINALHUB_IP }:${process.env.SPINALHUB_PORT}/` ); // 3: Create and store the wineCave model or load it if it exists spinalCore.load( conn, "myCellar", function(myCellar) { //success callback console.log("load & sync existing model if it exists"); setSensorData(myCellar.temperature); }, function(myCellar) { //error callback myCellar = new WineCellarModel(); spinalCore.store(conn, myCellar, "myCellar", function() { console.log("store & sync new model, first connection"); setSensorData(myCellar.temperature); }); } ); i = 0; sampleRate = 2; function setSensorData(currentTemperature) { var simTemp; console.log("Sending sensored temperature"); simTemp = 15 + 5 * Math.sin(i); i += 0.5; currentTemperature.set(simTemp); setTimeout(function() { setSensorData(currentTemperature); }, sampleRate * 1000); }
These are the steps that the code is following:
- Requires connector and model.
- Establishes a connection with the hub, with write/read rights, using a user, password and environment variables. To understand more about configuration, please head to the configuration docs.
- Creates and synchronize an instance of our button model and stores it in the hub. We first try to load and sync an existing instance from the hub, if no instance exist, than we create a new one
- Changes the data periodically through the setSensorData() function.
The current system folder organization so far is:
Instead of run your organ with node, you can add your organ to the hub process, just add it into .apps.json.
{ "apps": [ { "name": "spinal-core-hub", "script": "spinalhub.js", "cwd": "./nerve-center/" }, { "name": "virtualTempSensorOrgan", "script": "index.js", "cwd": "./virtualTempSensorOrgan/" } ] }
Here is the architecture of the system you have after this step:
Now that we have our temperature Sensor organ, we need to monitor its danger zone. For this we are going to simulate a analytics organ that modify its attribute “danger”. Later we will be able to connect with a real IoT sensor.
Make a new folder called TempSensor where we are going to define our danger zone.
~/fine-wine-system$ mkdir analyticsTempOrgan
Inside the organ folder, create the file called index.js with the code below.
analyticsTempOrgan/index.js
// 1 var spinalCore = require("spinal-core-connectorjs"); require("../spinal-model-wine-cellar/model.js"); // 2 console.log("Configuration Environment not found, using default config"); process.env.SPINALHUB_PORT = 7777; process.env.SPINALHUB_IP = "127.0.0.1"; process.env.SPINAL_USER_ID = 168; process.env.SPINAL_PASSWORD = "JHGgcz45JKilmzknzelf65ddDadggftIO98P"; var conn = spinalCore.connect( `http://${process.env.SPINAL_USER_ID}:${process.env.SPINAL_PASSWORD}@${ process.env.SPINALHUB_IP }:${process.env.SPINALHUB_PORT}/` ); // 3: load the wineCave model if it exists spinalCore.load(conn, "myCellar", function(myCellar) { //success callback console.log("load & sync existing model if it exists"); // analyzeData(myCellar.temperature); analyzeData(myCellar); }); // 4 function analyzeData(myCellar) { myCellar.bind(function() { if (myCellar.temperature.get() < 10 || myCellar.temperature.get() > 16) { myCellar.danger.set(true); console.log("Danger"); } else { myCellar.danger.set(false); console.log("Fine Wine"); } }); }
These are the steps that the code is following:
- Requires connector and model.
- Establishes a connection with the hub, with write/read rights, using a user, password and environment variables. To understand more about configuration, please head to the configuration docs.
- Creates and synchronize an instance of our wineCellar model and stores it in the hub. We first try to load and sync an existing instance of the wine cellar from the hub.
- Changes the danger data everytime temperature value change. We used bind function from spinalConnector to synchronized the data. Function inside bind will be called everytime the data change.
The current system folder organization so far is:
Instead of run your organ with node, you can add your organ to the hub process, just add it into .apps.json.
{ "apps": [ { "name": "spinal-core-hub", "script": "spinalhub.js", "cwd": "./nerve-center/" }, { "name": "virtualTempSensorOrgan", "script": "index.js", "cwd": "./virtualTempSensorOrgan/" }, { "name": "analyticsTempOrgan", "script": "index.js", "cwd": "./analyticsTempOrgan/" } ] }
Here is the architecture of the system you have after this step:
For this step, we create a browser organ. This type of organ will create for you an new web page with the visualisation of your data. We will be able to create what we want to see. Actually we will do this for our fine wine cellar to know when temperature is in danger zone.
These are the steps that the code is following:
- Create your spinal browser folder
- Creates and synchronize an instance with Javascript
- browserify the code
- Launch the browser organ.
At the end of this part you will have final system folder organization so far is:
Create your spinal browser folder
Inside .browser_organs, make a new folder called finewine inside, create a file named index.html. we define our web page.
~/fine-wine-system/.browser_organ$ mkdir analyticsTempOrgan ~/fine-wine-system/.browser_organ$ cd analyticsTempOrgan ~/fine-wine-system/.browser_organ/analyticsTempOrgan$ touch index.html ~/fine-wine-system/.browser_organ/analyticsTempOrgan$ mkdir src ~/fine-wine-system/.browser_organ/analyticsTempOrgan$ touch src/inspectFineWine.js
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>FineWine: SpinalCore Quickstart</title> <link rel="stylesheet" href="https://bootswatch.com/_vendor/bootstrap/dist/css/bootstrap.min.css"> </head> <body> <div style="margin-top: 100px; margin-left: auto; margin-right: auto; width: 300px"> <div class="row" style="text-align: center;"> <div class="col-lg-offset-4"> <h1>FineWine</h1> <p>This simple IoT system will let you know whenever the temperature at your wine cave is not between 10°C and 16°C. Cheers!</p1> </div> </div> <div class="row"> <div class="col-lg-offset-4"> <hr /> </div> </div> <div class="row"> <div class="col-lg-offset-4" style="width: 100%"> <div class="bs-component"> <div class="alert alert-success" id="wine_temp_container"> <div style="font-size: 100px; text-align: center;"> <strong id="wine_temp">0</strong>°C</div> </div> </div> </div> </div> </div> <!-- SpinalCore API --> <script type="text/javascript" src="finewine/bundle.js"> </script> <script> FileSystem.CONNECTOR_TYPE = "Browser"; </script> </body> </html> </html>
2. Creates and synchronize an instance with Javascript
// 1 var spinalCore = require("spinal-core-connectorjs"); require("../../../spinal-model-wine-cellar/model.js"); // 2 console.log("Configuration Environment not found, using default config"); process.env.SPINALHUB_PORT = 7777; process.env.SPINALHUB_IP = "127.0.0.1"; process.env.SPINAL_USER_ID = 168; process.env.SPINAL_PASSWORD = "JHGgcz45JKilmzknzelf65ddDadggftIO98P"; var conn = spinalCore.connect( `http://${process.env.SPINAL_USER_ID}:${process.env.SPINAL_PASSWORD}@${ process.env.SPINALHUB_IP }:${process.env.SPINALHUB_PORT}/` ); // Load avatar spinalCore.load(conn, "myCellar", function(myCellar) { console.log("Model loaded!"); updateView(myCellar); }); // Listen to changes in the avatar and update view function updateView(myCellar) { myCellar.bind(function() { if (myCellar.temperature.has_been_modified()) { var new_temp = parseFloat(myCellar.temperature.get()).toFixed(1); document.getElementById("wine_temp").innerHTML = new_temp; } if (myCellar.danger.has_been_modified()) { var danger = myCellar.danger.get(); if (danger) document.getElementById("wine_temp_container").className = "alert alert-danger"; else document.getElementById("wine_temp_container").className = "alert alert-success"; } }); }
3. browserify the code
~/fine-wine-system/.browser_organ/analyticsTempOrgan$ ../../node_modules/.bin/browserify src/inspectFineWine.js -o bundle.js
4. Launch the browser organ.
That’s it. You have everything you need. You can check that everything is working by running the PM2 process file automatically generated:
~/button-system$ pm2 start launch.config.js
PM2 will automatically start the Spinal Hub and the organs.
Browser organe are little different than other organ, they provide a web page with his own route. Name of the folder in .browser_organs correspond to the route in your browser.
BaseURL
Your browser organ run on this page.
http://localhost:7777/html/finewine
After this installation, only SpinalHub is running on port 7777. SpinalHub containes a web server that provide your browser organ. Here is the architecture of the system you have after this first install: