in this instructable, I'll show you how to create a lightweight and incredibly responsive web server with WebSockets.

I'll use it to control a LED using an Arduino but the concept can be applied to many other projects.

Since this project does not use any on-board peripherals, it will work with just about any computer, but running it on a low power machine like the Raspberry PI makes sense for continuous operation.

Why use Node.Js or WebSockets?

There are a few tutorials on the web that show how to use a Raspberry Pi for home automation, but many use php and simple http requests to send data back to the server. This is fine for simply switching on and off some lights but quickly reaches it's limitations when you want to run code server-side or if you want bidirectional communication.

Node.Js allows you to write programs in JavaScript and the vast amount of community-made libraries enables you to write very intricate programs in just a few lines of code.

Websockets have a few advantages over simple http requests:

  • Speed: A normal http request has to establish a connection before any transactions can happen which takes a lot of time. A websocket is always open and ready to send or receive data.
    This means that the lag can be as low as your ping, so just a millisecond or two in most cases
  • Bidirectional: Websockets allow data to be sent in both directions, this also means that the server can trigger events in the client.

Step 1: Setting Up the Environment

Installing Node.JS

Node recently released the new version 4.0.0 which comes with a ARM build (http://blog.wia.io/installing-node-js-v4-0-0-on-a-...):

wget https://nodejs.org/dist/v4.0.0/node-v4.0.0-linux-armv7l.tar.gz
tar -xvf node-v4.0.0-linux-armv7l.tar.gz cd node-v4.0.0-linux-armv7l sudo cp -R * /usr/local

Creating your project

mkdir ~/node_led
cd ~/node_led
mkdir public

create your project folder and a folder that contains static webpages

Installing libraries

npm install express
npm install socket.io
npm install serialport

Be sure to do this is the node_led folder or use the '-g' flag to install the libraries globally.

express: http server wrapper

socket.io: WebSockets library

serialport: Serial port wrapper

Step 2: Prepare the Arduino

Upload this code to your Arduino:

void setup()
Serial.begin(115200); }
void loop()
{ while(!Serial.available()); //wait until a byte was received analogWrite(3, Serial.read());//output received byte }

and connect an LED with a current-limiting resistor to D3 (image shows D10, there is no difference if you use the correct pin in your code).

The code just waits for a byte from the serial stream and sets the PWM level of D3 accordingly.

Now you can connect the Arduino to the Pi via USB and correct the serial port name in main.js.
To find the assigned serial port you can use this command:

ls /dev | grep ttyACM

Instead of a single LED you can connect any peripheral you can imagine to the Arduino. Just to name a few ideas:
-LED stips (maybe RGB, the code can easily be adjusted for 3 sliders)
-Motors / fans
Naturally, both require a transitor to handle the higher current and voltage as shown in the third image. The flyback diode is only necessary for inductive loads such as motors, solenoids or relays.

Step 3: Writing the Server Script

Create a new file called 'main.js' in the node_led folder, it will contain all the code that is necessary for running the server. Use your favorite text editor (I prefer Notepad++ with the NppFTP plugin).

Set up the webserver and websocket server

express = require('express');  //web server
app = express();
server = require('http').createServer(app);
io = require('socket.io').listen(server); //web socket server server.listen(8080); //start the webserver on port 8080
app.use(express.static('public')); //tell the server that ./public/ contains the static webpages

Open the serial port

var SerialPort = require("serialport").SerialPort
var serialPort = new SerialPort("/dev/ttyACM0", { baudrate: 115200 });

Define the WebSocket behavior

var brightness = 0; //static variable to hold the current brightness
io.sockets.on('connection', function (socket) { //gets called whenever a client connects
socket.emit('led', {value: brightness}); //send the new client the current brightness

socket.on('led', function (data) { //makes the socket react to 'led' packets by calling this function
brightness = data.value; //updates brightness from the data object
var buf = new Buffer(1); //creates a new 1-byte buffer
buf.writeUInt8(brightness, 0); //writes the pwm value to the buffer
serialPort.write(buf); //transmits the buffer to the arduino
io.sockets.emit('led', {value: brightness}); //sends the updated brightness to all connected clients
}); });

this is all the required code to run the web- and websocket-server, you can add the line 'console.log("running");' to indicate that the startup procedure has finished.

full code

Step 4: Add the Static Html Content

in the 'public' folder, add following files:


when I paste html code into instructables, it automatically renders it as part of the page, so I made the file available on pastebin: http://pastebin.com/jJ9L0RF8


body {
	text-align: center;
	margin-top: 50px;
	background: #50D0A0;

	-webkit-appearance: none;
	width: 80%;

input[type=range]::-webkit-slider-runnable-track {
	height: 10px;
	background: #ddd;
	border: none;
	border-radius: 3px;

input[type=range]::-webkit-slider-thumb {
	-webkit-appearance: none;
	border: none;
	height: 32px;
	width: 32px;
	border-radius: 50%;
	background: goldenrod;
	margin-top: -12px;

input[type=range]:focus {
	outline: none;

input[type=range]:focus::-webkit-slider-runnable-track {
	background: #ccc;

this stylesheet is optimized for webkit browsers such as Google Chrome or the chromium browser.
The website will work just fine without it, but it looks much nicer with.

The website should also render and work fine on most smartphones and tablets (tested with Chrome).

Step 5: Run the Server

go back into 'node_led' folder and run this command:

nodejs main.js

Now you can visit the website under [raspberry pi IP]:8080

If you visit the site with multiple clients simultaneously, the sliders will move synchronized on all clients.

The LED's brightness should follow the slider with little to no lag at all

Step 6: Conclusion

With Node.Js you can create websites and services with very little code or background knowledge.

Until three days ago, I despised JavaScript as a language and had never used it before. I'm just blown away by the simplicity and the possibilities of Node.Js.

If you want to learn more, here are some websites that might help you:

If you liked this instructable, please consider giving me a vote in one of the contests I participated in, thank you!

<p>Is it possible to do the same with a jquery slider.ui instead of html slider?</p><p>I mean this great feature to change the slider value on smartphone and for example see changes on PC.</p>
Having trouble installing npm serialport on RaspberryPi 3 (raspian pixel). Error says failed at serialport@4.0.1 install script node-pre-gyp install --fallback-to-build.<br>This is most likely serialport package....<br><br>How do I fix this?
<p>Have you had any luck getting it to work? I am having the same issue...</p>
<p>you should ask this question over at their github:<br><a href="https://github.com/EmergingTechnologyAdvisors/node-serialport" rel="nofollow">https://github.com/EmergingTechnologyAdvisors/node...</a><br></p>
<p>Awesome tutorial! I'd like to be able to control multiple pins on the Arduino with multiple sliders on the Pi. I've tried tweaking the code but I'm not quite sure how to get it working. Anyone have an example using multiple pins?</p><p>Thanks</p><p>Rich</p>
<p>Hi,<br>When I run npm -v, node -v, n&uuml;m install express or anything else relating to this I got a &quot;Segmentation fault&quot;<br>Any idea why?</p>
<p>Hi,</p><p>I don't know what could cause this, your guess is as good as mine.<br><a href="https://github.com/nodesource/distributions/issues/44" rel="nofollow">https://github.com/nodesource/distributions/issues...</a> looks like you're not the only one with this error.</p><p>If you do get it to work, let us know in case someone else has the same issue</p>
<p>that has to do with the wheezy version of raspbian.</p><p>I manage to get nodejs through this link </p><p><a href="http://blog.blakesimpson.co.uk/read/41-install-node-js-on-debian-wheezy" rel="nofollow">http://blog.blakesimpson.co.uk/read/41-install-nod...</a></p><p>and for npm just a simple apt-get install npm </p><p>(and I had to remove the stuff unpacked before)</p><p>I still have to see if everything work fine, but at least I have nodejs and npm working. I will get a try tomorrow....</p>
<p>I still had some issues running npm install express, but I managed to solver it:</p><p>I had to execute the command below:</p><br><pre>npm config set registry http://registry.npmjs.org/</pre><p>However, that will make npm install packages over an insecure HTTP connection. If you can, you should stick with</p><br><pre>npm config set registry https://registry.npmjs.org/</pre><p>instead to install over HTTPS.</p>
<p>hi there thank you very much for the tutorial :D, i waned to ask if this be done with out an arduino direct from the GPIO pins?</p>
<p>Glad you like it! You can absolutely use a rpi-gpio library and do away with the arduino. I used the arduino as an additional abstraction layer because I'm using an odroid C1 (great board, but lacks the good library support that the raspberry pi offers)</p>
<p>hi martin thanks for the replay how will i add rpi-gpio to the java code what part of your code should i change with the gpio code ?</p>
<p>you only need to replace the serial write function with the set pwm (probably not the actual name) function</p>
<p>Dear,</p><p>Great project, but I'd like to expand this to an 8 channel ac dimmer. The hardware is no problem. I saw in one of your solutions something like:</p><ol><br><li>function showValue()<li>{<li>var state = {};<li>state.Servo1=document.getElementById(&quot;inputSlider1&quot;).value;<li>state.Servo2=document.getElementById(&quot;inputSlider2&quot;).value;<li>state.Servo3=document.getElementById(&quot;inputSlider3&quot;).value;<li>socket.emit('led', state);<li>}</ol><p>but i can't make anything out of it. Could you be more specific. I'm not that familiar with java nor html.</p><p>Grz</p><p>Bart</p>
<p>OK, its spring break so now I have time to work out this project. Now when I create the node_led folder, do I create it inside the /usr/local/ nodejs folder or just create it in the home folder then npm install everything inside node_led &lt;- I would rather install everything -g globally...</p>
<p>you can put the node_led folder whereeveryou want (and where you have write permissions :D)</p><p>if you want you can install all modules globally, it should run just as well.</p>
<p>where did you find a notepad++ that will install on raspberry pi 2? i tried installing three different versions and I get nothing but frustration.</p>
<p>N++ is not running on the Pi, it's running on my PC and accessing the files on the Pi via FTP with the NppFTP plugin</p>
<p>I swear this is my last time bugging you but I get this error when i start up server. I didn't realize how slow and buggy RPI 2 is (the gui). But what am i doing wrong here?</p><p>module.js:340</p><p> throw err;</p><p> ^</p><p>Error: Cannot find module '/home/pi/node_led/node_modules/serialport/build/Release/node-v11-linux-arm/serialport.node'</p><p> at Function.Module._resolveFilename (module.js:338:15)</p><p> at Function.Module._load (module.js:280:25)</p><p> at Module.require (module.js:364:17)</p><p> at require (module.js:380:17)</p><p> at Object.&lt;anonymous&gt; (/home/pi/node_led/node_modules/serialport/serialport.js:14:25)</p><p> at Module._compile (module.js:456:26)</p><p> at Object.Module._extensions..js (module.js:474:10)</p><p> at Module.load (module.js:356:32)</p><p> at Function.Module._load (module.js:312:12)</p><p> at Module.require (module.js:364:17)</p>
<p>looks like the serial port module didn't compile properly when you installed it with npm. try uninstalling it, then reinstall it and look for errors. Maybe also try installing it with elevated permissions (if you do this you'll need sudo again to delete the folder)</p>
<p>Hey thanks for the reply and yes I'm gonna try that when I get home (on my laptop &amp; RPI2). Just working with RPI2 is a hassle cause graphically it functions like I'm on a windows 95 platform (really irritating).</p>
<p>where does the RPI come in?</p><p>Also, I have an RPI3 coming to me this week - can I use that also?</p>
<p>The RPi acts as the webserver that handles all the static requests and the Socket.io traffic. All the command line commands are run on the Pi, though you can use pretty much any computer for that (even Desktops), since the Arduino handles all the I/O. I used an Odroid C1 for example.</p><p>The RPi3 is very well suited for the job and should work just fine.</p>
<p>how are you connecting the Arduino to the rpi or Odroid?</p>
<p>just using USB</p>
<p>cool thanks</p>
<p>thanks bruh</p>
<p>Hi Martin,</p><p>Thank for the files and instructable. i m very new to Raspberry Pi and Node.js. there are few question need advice. </p><p>1) is there any why we can make 'node main.js' to run automatically when we start the server (raspberry Pi).</p><p>2) Can we integrated it with webserver.</p><p>The project i plan to build is a light On/Off and dimmer. </p>
<p>Hi,</p><p>glad you liked it, thanks for the pictures of your project :)</p><p>1) you can run node on startup by adding a line to /etc/rc.local (before exit 0):<br>sudo node /home/pi/NodeJS/main.js&amp;</p><p>edit the path to your setup. The &amp; at the end tells the interpreter to run this command in the background so rc.local will finish executing while node keeps running.</p><p>2) yes you can. If you have a regular apache (or anything else) webserver, you can use it to host the static pages, you'll only have to edit the javascript inside index.html to connect socket.io to the ip/port that node is running on.</p>
<p>Hi Martin, Great instructable... :-), i would like to try it this weekend but can you provide my another link for the index.html file and full code. i can't access <a href="http://pastebin.com/jJ9L0RF8" rel="nofollow">http://pastebin.com/jJ9L0RF8</a> it give me gateway timeout. or email me at alankam58@yahoo.com. Thanks.</p>
<p>Hi I'm getting a &quot;Cannot GET /&quot; error when I try to access the webpage. Any idea why?</p>
<p>Sounds like either you're missing 'index.html' in the /public/ folder or express isn't set up correctly. Does your folder structure match the one in the screenshot?</p>
<p>Thank you! I had to rename my html file. to index.html. Works great now. <br><br>Similar to the idea of an RGB, I wish to make three sliders that can control three servos. I figured out how to make the three sliders, but am having trouble figuring out the rest of the code. Could you help me out with this? </p>
<p>As I pointed out in another comment, you could change the &quot;showvalue&quot; function to something like this:</p><ol><br><li>function showValue()<li> {<li>var state = {};<li>state.Servo1=document.getElementById(&quot;inputSlider1&quot;).value;<li>state.Servo2=document.getElementById(&quot;inputSlider2&quot;).value;<li>state.Servo3=document.getElementById(&quot;inputSlider3&quot;).value;<li> socket.emit('led', state);<li>}</ol><p>This should get you started</p>
<p>Thank you for this instructional. I built it with a Nano 3.0 and a RPi-2. Works fine! Very cool. ;-}</p><p>I did have to try more than one form of installation for Node-JS due to errors finding serialport.node. It appeared to be some kind of version tracking issue.</p>
<p>Happy you like it,</p><p>the installation process seems to change with every version. I guess the safest bet would be to compile it from source, but that takes half an eternity even on newer SBCs.</p>
<p>Hi Martin, thx for this perfect tutorial!</p><p>Everything is clear for RPi &lt;-&gt; USB &lt;-&gt; Arduino but I want to realize the websocket connection with node.js in the following way: RPi &lt;-&gt; LAN &lt;-&gt; Ethernetshield (w5100) &lt;-&gt; Arduino. This is caused by longer distance (10m). Do you or somebody know what should be modified instead of the USB/serial software?</p><p>greetings from Germany, Frank</p>
<p>Hi Frank, if you want to add ethernet to the arduino, you could do away with the Pi completely and use it to host the website. Another option would be to use a USB-Serial adapter (or even better use the UART pins on the GPIO-header) and cover the distance with UART, that way you could strip the arduino down to a much smaller and cheaper AtTiny and a few passives.</p><p>If you have further questions, let me know.</p><p>Beste Gr&uuml;&szlig;e von daheim,</p><p>Martin</p>
Thanks Martin,<br>after searching the net I want to use &quot;configurable firmata&quot; on a Mega2560 with w5100-LAN-shield. This should work as &quot;arm&quot; of the RPi2-server which run node.js. I hope, that the Mega will do a good job for IO.<br>Thanks for your perfect tutorial.<br>Gru&szlig; zur&uuml;ck :-)<br>Frank
<p>quick notes </p><p>install the latest node 4.2.4 as of 201612 hence the npm files do not compile </p><p>ensure you move the tar file to the /usr/local/ folder </p><p>unzip it when done run </p><p>sudo ./configure (takes around one hour to compile unless pi is overclocked)</p><p>sudo make</p><p>sudo make install </p><p>create the folder node_led and in it another one named public </p><p>cd into node_led</p><p>npm install express</p><p>npm install socket.io</p><p>npm install serialport </p><p>check the serial port u running on by ls /dev |grep ttyAcm</p><p>change the serial port# i the js file </p><p>if you get an error installing the npm serialport Run sudo npm install --unsafe-perm serialport </p>
<p>Hi,</p><p>Could you explain how I would add RGB control for a LED strip.</p><p>I have the ws2801 driver and was going to feed the values in to that but I cannot figure out what is needed to transfer the values of 3 sliders to the server.</p><p>Thanks</p>
<p>Hi Dean,</p><p>you can send objects with node.js, that way you can send all three values at once. the best solution would be to have the 'oninput' event of all three sliders fire the same function, which collects all three values and sends them as one object. likewise, have the server re-emit the same object.</p>
<p>What I meant to say: Thanks for this example. It finally got me started using node js.</p>
<p>You're welcome, glad I'm not the only one who needed a kickstart :)</p><p>(been wondering about that empty comment for a while :) )</p>
<p>I created an account for that reply. Apparently the first post after creating an account is discarded automatically. ;-)</p><p>The reason I wanted to use something like node is that I've created an app with Angular and phonegap which in its first version uses polling. The disadvantages of that are obvious. It eats mobile batteries and data limits for breakfast and it's not even really responsive to user input. Once I get the whole authentication thing figured out I'll bring the new node.js version online. </p><br>
<p>Dear Martin,</p><p>Here it is written that ./public contains static webpages, but I find no description about what are the static webpages to add in that directory. Or if it is there in this instructables please let me know where it is because I want to build similar project but thought of ./public is resisting me.</p><p>It would be great if you post the static webpages to put in that directory related to this instructable. Reply awaited! :)</p><p>Thanks.</p>
<p>Hi Jay,</p><p>You're looking for Step 4, you need index.html (<a href="http://pastebin.com/jJ9L0RF8" rel="nofollow">http://pastebin.com/jJ9L0RF8</a>) and (optionally) style.css.</p><p>You're welcome!</p><p>P.S. I'm curious about your project, what do you have in mind? :)</p>
<p>Hello Martin,</p><p>First of all I will be making it for small LED then will try to implement for home bulbs and tubelights. If you have already implemented for home bulbs and tubelight, you can share the videos and ideas here! ;) </p><p>Thanks for asking about my project! :)</p>

About This Instructable




More by martin2250:Easy Node.JS + WebSockets LED Controller for Raspberry Pi 
Add instructable to: