Controlling Electronic Devices Using The Internet – NodeJS || Arduino Communication Project

Introduction

This project in a nutshell: Controlling an Arduino micro controller from a web interface. Therefore control any electrical device from anywhere with internet.

I wanted to stretch my understanding on programming and web development and what better way to do this then doing a simple but effective project. The hardware is kept at a minimal so I could focus more on software. Hence i went with a simple led , pot setup. The pot will send data and the led will receive (pwm brightness). Using NodeJS the serial data was read (potentiometer value) and written (led brightness). The difficult part of this project was getting input data from a remote location (web server) 

I have broken down the project into small byte size chunks, this helped me debug programs errors (I had few), and ease of manageability.   

Software Skills

  • Node.JS
  • Arduino Language
  • PHP
  • CSS
  • SQL
  • HTML

Hardware Skills

  • LED 
  • Potentiometer  

Software Logic

Potentiometer Data:

This starts at the Arduino, read pot value is serial printed. However, this time we will use Node.JS to read the value. NodeJS will open serial communication to the same port as the Arduino is connected to and read the printed pot value. NodeJS will then upload the data to a remote SQL database, this will happen every time a new pot value is printed. A webpage will connect to the SQL database set interval and retrieve the potentiometer value. This then will be displayed on the webpage. 

Led Data:

For the led the pwm brightness will be set by the user on a remote webpage, so its journey starts at the opposite end of the spectrum. The input data is saved to a SQL database, every set interval the database is checked for change in led pwm, this is done by NodeJS. If the value is different to the previous value then the new value will be sent to the Arduino via serial bus. The Arduino changes the output pwm value of the led to change its brightness. 

Ohm’s Law Calculator

Ohms law calculator uses the formula’s V = IR and P = IV = I²R = V²/R 

For this project, I will be using a blue led. This is important because as the light frequency increases the voltage drop also increases. Since blue light has a higher frequency compared to something like a red led. This means a higher forward voltage. Depending on make, type and size the working range will vary. For my setup I used a 220 Ω resistor in series, negative to ground and positive to a pwm pin on an arduino. The pot was connected to an analogue pin. With 5VCC one end GND the other and the middle pin connected to an analogue pin (A0 in my case).

Arduino

First the led and pot were checked if they were working as expected. This was down by a simple program where the pot value controls the led. I used constrain function to change pot range of 0 to 1023 to 0 to 255, but a simple /4 works as well. The pot value was smoothed out by taking the mean average from 10 consecutive readings, this is to remove spikes. (Hoever this smoothing caused issues with NodeJS so this was removed later on the project – more on that) 

/*
/*
  Coded by Masum Ahmed www.mahmed.tech for NodeJs <---> Arduino Communication Project
  Date: 12/07/2017
  Version: 1.00
*/
//--------Global Const---------------------
const int pot = A7;
const int led = 12;
//-----------------------------------------

//---------Smoothing Pot Val --------------
//Mean average deafult set to 10
const int numbReadings = 10;
int total;
int averagePot;
int previousPotVal = 0;
int counter = 0;
int arrayOfReadings[numbReadings]; //array list to store values
//-----------------------------------------

void setup() {
  pinMode(pot, INPUT);
  pinMode(led, OUTPUT);
  Serial.begin(9600);

  //Initilazlised all reading in array to be 0
  for (int x = 0; x < numbReadings; x++) {
    arrayOfReadings[x] = 0;
  }
  delay(3000);
}

void loop() {
   runingPotAvg(); // Calls runningPotAvg function
   ledControl(); // Calls ledControl function

}

void runingPotAvg () {
  total -= arrayOfReadings[counter]; //Deletes the previous reults
  arrayOfReadings[counter] = analogRead(pot); 
  total += arrayOfReadings[counter];
  averagePot = total / numbReadings;
  counter++;
  if (counter >= numbReadings) {
    counter = 0;
  }
}

void ledControl() {
  ledBrightness = constrain(averagePot, 0, 255);
  analogWrite(led, ledBrightness);
}

Read / Write Serial

The next step is to take user input via the serial monitor windows provided by the arduino ide to set the brightness. To do this, the serial.parseInt() is used which takes integer value and ignores string.  Also an error checking is added to the code. The valid range of a pwm value is 0 – 255, when a user enters >255 then it assigns the value 255 and if the user enters value <0 then assigns 0. The pot value is printed on the serial monitor, however this only happens when the current pot value is > or < +/-5 I have done this to make the reading more stable since it was fluctuation. Why this is a big problem related to SQL updating, more on that later. 

void readSerialPort() {
  // send data only when you receive data:
  if (Serial.available() > 0) {

    // read the incoming integer:
    serialRead = Serial.parseInt();

    // say what you got for debugging only
    // Serial.print("LED Value: ");
    // Serial.println(serialRead);
  }
  if (serialRead > 255) {
    ledBrightness = 255;
  }
  else if (serialRead < 0) {
    ledBrightness = 0;
  }
  else {
    ledBrightness = serialRead;
  }
}

//slight change to ledControl() function -->
void ledControl() {
  //ledBrightness = constrain(averagePot, 0, 255);
  analogWrite(led, ledBrightness);
}

void sendPotVal() {
  if (averagePot < previousPotVal - 5 || averagePot > previousPotVal + 5) {
    previousPotVal = averagePot;
    Serial.println(averagePot);
    delay(2000);
  }
}
//the 2 new function needs to be called in the loop function, like wise
void loop() {
  runingPotAvg(); // Calls runningPotAvg function
  sendPotVal(); // Calls sendPotVal function
  readSerialPort(); // Calls readSerialPort function
  ledControl(); // Calls ledControl function

}

NodeJS

There are 3 main aspect to the NodeJS program:

  • Read Serial Data
  • Write Serial Data
  • Update SQL Database

To make a serial connect within NodeJS, a module called serialport has to to be downloaded which can be done using npm command. Open CMD on the folder where NodeJS program will be kept, install by typing: npm install serialport Also the SQL module has to be installed to be able to connect to the sql database: npm install mysql 

NodeJS – Serial Port

My first step with the NodeJS program was to read the printed data and send pwm brightness to the Arduino. This was done by opening serial connect at the same braudrate and port. Once the connection was established I read incoming messages and printed it onto the console window. Problem raised when I tried to write the pwm value to control the brightness. It kept throwing errors: Port Not Open, my initial solution was to call the write function when there’s incoming data. However this was a bad fix and I was quite unsatisfied with the solution, even though it worked it would only send when pot value was changed. The example code for the serial module would not work either throwing the same error. I later found out the program was trying to execute the write function without opening the port, which resulted in that error. I came around this problem by using setInterval() function

// include the various libraries that you'll use:
var myPort = require('serialport');       // includes the serialport library
var ledBrightness = '0';
var newValue = true;

//---------- New Serial Port Instance ---------
//Communication speed must match arduino setting. 

// configure the serial port:
	//SerialPort = serialport.SerialPort,       // make a local instance of serialport
    portName = process.argv[2],                 // get serial port name from the command line               
var serialOptions = {                           // serial communication options
      baudRate: 9600,                           // data rate: 9600 bits per second
      parser: delimiterparser: myPort.parsers.readline("\n") // newline generates a data event
    };


// If no port name was defined then show message and excit the program
if (typeof portName === "undefined") {
  console.log("You need to specify the serial port, like so:\n");
  console.log("    node myscript.js portname");
  process.exit(1);
}
// open the serial port:
var myPort = new myPort(portName, serialOptions);

// set up event listeners for the serial events:
myPort.on('open', showPortOpen);
myPort.on('data', sendSerialData);
myPort.on('close', showPortClose);
myPort.on('error', showError);


function readSQL(data){
	con.query(data, function (err, result) {
		if (err) throw err;
		console.log(result.affectedRows + " record(s) updated");
  });
}

function writeSQL(data){
	con.query(data, function (err, result, fields) {
    if (err) throw err;
    //console.log(result[0].LedPWM);
	if(ledBrightness != result[0].LedPWM){
		ledBrightness = result[0].LedPWM.toString();
		newValue = true;
		console.log("New Brightness: "+ledBrightness);
	}
  });
}

// this is called when the serial port is opened:
function showPortOpen() {
  console.log('port open. Data rate: ' + myPort.options.baudRate);
}

// this is called when new data comes into the serial port:
function sendSerialData(data) {
  console.log("PotValue: "+ Number(data));
}

function showPortClose() {
   console.log('port closed.');
}
// this is called when the serial port has an error:
function showError(error) {
  console.log('Serial port error: ' + error);
}

function sendToSerial(data) {
  myPort.write(data);
}

//-------------------------------------------- Main Loop ------------------------
function mainLoop(){
	sendToSerial('255'); //sends a pwm value of 255 in string form to set led brightness to 100%
}

setInterval(mainLoop, 5000);  // Will call mainLoop() every 5 Seconds (default)

NodeJS – MySQL

The MySQL library was used (npm install mysql) to connect to the sql database since the server in a remote location the ip address of the server was used instead of localhost. 

var con holds the connection information in JSON format, once connection has been made successfully, the database can bed queried. 2 functions were created one for updating the table other selecting with parameters taking in the sql query. The update table is called when a new pot value is received and brightness check query will run periodically.  

/*
  Coded by Masum Ahmed www.mahmed.tech for NodeJs <---> Arduino Communication Project
  Date: 12/07/2017
  Version: 1.00
*/
//-------------------------- Program ------------------------------
// include the various libraries that you'll use:
var myPort = require('serialport');  // include the serialport library
var mysql = require('mysql');		// include the mysql library
var ledBrightness = '0';
var newValue = true;
var portOpen = false;

// ------ SQL Databse Details -----------------
  var con = mysql.createConnection({
  host: "Server IP",
  user: "Your_Username",
  password: "Your_Passowrd",
  database : "Database_Name"
});
//---------------------------------------------

//----------- Make SQL Connection -------------
con.connect(function(err, result) {
  if (err) throw err;
  console.log("Connected to SQL Database");
});
//---------------------------------------------

//Communication speed must match arduino setting. 
// Serial port configuration:
	//SerialPort = serialport.SerialPort,             // make a local instance of serialport
    portName = process.argv[2],                 // get serial port name from the command line
    delimiter = 'n';                // serial parser to use, from command line
var serialOptions = {                           // serial communication options
      baudRate: 9600,                           // data rate: 9600 bits per second
      parser: delimiter // newline generates a data event
    };

// if the delimiter is n, use readline as the parser:
if (delimiter === 'n' ) {
  serialOptions.parser = myPort.parsers.readline('\n');
}

if (typeof delimiter === 'undefined') {
  serialOptions.parser = null;
}

// If the user didn't give a serial port name, exit the program:
if (typeof portName === "undefined") {
  console.log("You need to specify the serial port, like so:\n");
  console.log("    node myscript.js portname");
  process.exit(1);
}

//---------- New Serial Port Instance ---------
var myPort = new myPort(portName, serialOptions);
//---------------------------------------------


// set up event listeners for the serial events:
myPort.on('open', showPortOpen);
myPort.on('data', sendSerialData);
myPort.on('close', showPortClose);
myPort.on('error', showError);


function readSQL(data){
	con.query(data, function (err, result) {
		if (err) throw err;
		console.log(result.affectedRows + " record(s) updated");
  });
}

function writeSQL(data){
	con.query(data, function (err, result, fields) {
    if (err) throw err;
    //console.log(result[0].LedPWM);
	if(ledBrightness != result[0].LedPWM){
		ledBrightness = result[0].LedPWM.toString();
		newValue = true;
		console.log("New Brightness: "+ledBrightness);
	}
  });
}

// this is called when the serial port is opened:
function showPortOpen() {
  console.log('port open. Data rate: ' + myPort.options.baudRate);
  //portOpen = true;
}

// this is called when new data comes into the serial port:
function sendSerialData(data) {
  writeQuery("UPDATE ArduinoJS SET Value = '"+data+"' WHERE ID = 1");
  console.log("PotValue: "+ Number(data));
}

function showPortClose() {
   console.log('port closed.');
}
// this is called when the serial port has an error:
function showError(error) {
  console.log('Serial port error: ' + error);
}

//This function is called to write data to serial port
function sendToSerial(data) {
  myPort.write(data);
}

//Makes a update (write) query to the sql table
function writeQuery(querya){
	con.query(querya, function (err, result) {
		if (err) throw err;
		console.log(result.affectedRows + " record(s) updated");
  });
}

//makes a select query on a sql table
function readQuery(queryb){
	con.query(queryb, function (err, result, fields) {
    if (err) throw err;
    //console.log("SQL LED PWM Value: "+result[0].LedPWM);  //for debugging
	//will only write to serial if the value changes from the previous stored brightness aka user has inputed new pwm value
	if(ledBrightness != result[0].LedPWM){  
		console.log("\n  Old Brightness: "+ledBrightness+"\n  New Brightness: "+result[0].LedPWM.toString()+"\n");
		//The results is in a Array hence this notation this can be seen printing the results directly 
		ledBrightness = result[0].LedPWM.toString();
		sendToSerial(ledBrightness);
	}
  });
}
//-------------------------------------------- Main Loop ------------------------
function mainLoop(){
	readQuery("SELECT LedPWM FROM ArduinoJS WHERE ID = 1");  //selects pwm value 
}

setInterval(mainLoop, 5000);  // Will call mainLoop every 5 Seconds (default)

Web Interface

The main webpage was written in PHP since I already had some had some experience from my CO323 Databases and the Web module at university. Html table & form was used to display the sql data. 

<?php
//all of this code used for refreshing the page
$page = $_SERVER['PHP_SELF'];
$sec  = "12";
?>
<?php
$servername = "localhost";
$username   = "Your_username";
$password   = "Your_password";
$dbname     = "Your_database";

// Create connection
$conn = mysqli_connect($servername, $username, $password, $dbname);
// Check connection
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}

$sql    = "SELECT * FROM ArduinoJS";
$result = $conn->query($sql);

//print some nice text at the top
echo "<h1><span class='blue'>&lt;</span>NodeJs - Ardunio  <span class='blue'>&gt;</span> <span class='yellow'>Communication Project</pan></h1>";
echo "<h2>Made By <a rel='nofollow' rel='noreferrer' href='http://www.mahmed.tech' target='_blank'>Masum Ahmed</a></h2>";

//draw the table
echo "<table class = 'container'>
<thread>
<tr>
<th><h1>ID</h1></th>
<th><h1><h1>LED PWM</h1></th>
<th><h1>POT VALUE</h1></th>
</tr></thread>";
//loop through the table and print the data into the table
while ($row = mysqli_fetch_array($result)) {
    
    echo "<tbody><tr>";
    $unit_id = $row['ID'];
    echo "<td>" . $row['ID'] . "</td>";
    $column      = "LedPWM";
    $current_LED = $row['LedPWM'];
    echo "<td><form action= change_SQL.php method= 'post'>
<input type='text' name='ledpwmval' value=$current_LED >
<input type='hidden' name='unit' value= $unit_id >
<input type='hidden' name='column' value= $column >
<input type= 'submit' name= 'change_but' style='text-align:center' value='Change LED Brightness'></form></td>";
    echo "<td>" . $row['Value'] . "</td>";
    
} //while

echo "</tr><tbody></table>";

?>
 

CSS Styling  & Javascript

 

<style>
@charset "UTF-8";
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,700);
@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,700);
body {
  font-family: 'Ubuntu', Open Sans, sans-serif;
  font-weight: 300;
  line-height: 1.42em;
  color:#A7A1AE;
  background-color:#1F2739;
}
h1 {
  font-size:3em; 
  font-weight: 300;
  line-height:1em;
  text-align: center;
  color: #4DC3FA;
}
h2 {
  font-size:1em; 
  font-weight: 300;
  text-align: center;
  display: block;
  line-height:1em;
  padding-bottom: 2em;
  color: #FB667A;
}
h2 a {
  font-weight: 700;
  text-transform: uppercase;
  color: #FB667A;
  text-decoration: none;
}
.blue { color: #185875; }
.yellow { color: #89f442; }
.container th h1 {
    font-weight: bold;
    font-size: 2em;
    text-align: left;
  color: #185875;
}
.container td {
      font-weight: normal;
      font-size: 1.8em;
  -webkit-box-shadow: 0 2px 2px -2px #0E1119;
       -moz-box-shadow: 0 2px 2px -2px #0E1119;
            box-shadow: 0 2px 2px -2px #0E1119;
}
.container {
      text-align: left;
      overflow: hidden;
      width: 80%;
      margin: 0 auto;
  display: table;
  padding: 0 0 8em 0;
}
.container td, .container th {
      padding-bottom: 2%;
      padding-top: 2%;
      padding-left:2%;  
}
/* Background-color of the odd rows */
.container tr:nth-child(odd) {
      background-color: #323C50;
}
/* Background-color of the even rows */
.container tr:nth-child(even) {
      background-color: #2C3446;
}
.container th {
      background-color: #1F2739;
}
.container td:first-child { color: #FB667A; }
.container tr:hover {
   background-color: #464A52;
-webkit-box-shadow: 0 6px 6px -6px #0E1119;
       -moz-box-shadow: 0 6px 6px -6px #0E1119;
            box-shadow: 0 6px 6px -6px #0E1119;
}
.container td:hover {
  background-color: #1F2739;
  color: #403E10;
  font-weight: bold;
  
  box-shadow: #7F7C21 -1px 1px, #7F7C21 -2px 2px, #7F7C21 -3px 3px, #7F7C21 -4px 4px, #7F7C21 -5px 5px, #7F7C21 -6px 6px;
  transform: translate3d(6px, -6px, 0);
  
  transition-delay: 0s;
      transition-duration: 0.4s;
      transition-property: all;
  transition-timing-function: line;
    }
    form {
  background: none;
  margin: 0px 5% 0;
  padding: 0px;
  width: 360px;
}
input {
  display: inline-block;
  font-size: 1em;
  width: 310px;
  min-width: 100px;
  margin: 10px 10px;
  padding: 10px 8px 10px 8px;
  border-radius: 5px;
  box-shadow: inset 0 1px 2px rgba(0,0,0, .55), 0px 1px 1px rgba(255,255,255,.5);
  border: 1px solid #666;
}
input {
  opacity: 0.5;
}
input:hover,
input:focus {
  opacity: .7;
  color:#08c;
  border: 1px solid #08c;
   box-shadow: 0px 1px 0px rgba(255,255,255,.25),inset 0px 3px 6px rgba(0,0,0,.25);
}
input[type="text"]:focus,
input[type="password"]:focus {
  box-shadow: inset 0 1px 2px rgba(255,255,255, .35), 0px 1px 15px rgba(0,246,255,.5);
  border: 1px solid #08c;
  outline: none;
}
input[type="submit"] {
  font-size: 1em;
  appearance: none;
  opacity: .90;
  width: auto;
  background: #08c;
  box-shadow: inset 0 1px 2px rgba(255,255,255, .35), 0px 1px 6px rgba(0,246,255,.5);
  border: 1px solid #0a5378;
  border-radius: 4px;
  color: #eee;
  cursor: pointer;
  text-shadow:0px -1px 0px rgba(0,0,0,.5);
}
input[type="submit"]:hover {
  background: #08c;
  width:Auto;
  border: 1px solid #0a5378;
  border-radius: 3px;
  box-shadow: inset 0px 3px 16px rgba(0,0,0,.25),0px 1px 10px rgba(255,255,255,.5),inset 0px -1px 2px rgba(255,255,255,.35);
  text-shadow:0px 1px 1px rgba(0,0,0,.65);
  -webkit-transition: all 0.40s ease-out;
  transition: all 0.40s ease-out;
}
}
    </style>
    <script>
  window.console = window.console || function(t) {};
</script>
  <script>
  if (document.location.search.match(/type=embed/gi)) {
    window.parent.postMessage("resize", "*");
  }
</script>
 

Arduino Nano 

Went from a breadboard design which used an arduino mega to an Arduino nano.