Avete la necessità di accendere un led o in generale leggere/impostare un qualsiasi pin del GPIO del Raspberry Pi via web? La soluzione più semplice e comoda è probabilmente utilizzare un server REST in Javascript con Node.js.
Esistono sicuramente anche altre valide alternative, io ho scelto questa perché negli ultimi mesi mi sono trovato diverse volte a lavorare con Node.Js e l’ho apprezzato veramente tanto per la sua semplicità e al contempo grossa potenza modulare, con un’infinità di pacchetti ed estensioni NPM.
Lo scopo di questa guida è quindi quello di accendere un led attaccato al gpio (con una resistenza e un mosfet, che non sono oggetto di questo post ma di cui potete trovare già tantissima documentazione) attraverso una semplice chiamata web http://indirizzo.ip/led/on.

Due led incastonati nel case del Raspberry, comandati da NodeJs
Parto dal presupposto che il Raspberry sia già stato configurato con la Raspbian installata ed attiva e che quindi abbiate già una conoscenza base generale del sistema.
Installare i pacchetti necessari con
curl -sLS https://apt.adafruit.com/add | sudo bash sudo apt-get install node
Verificate che node sia installato correttamente:
pi@raspberrypi node -v v0.12.0
creiamo un nuovo progetto ed installiamo le librerie necessarie
mkdir /home/pi/raspiledjs cd /home/pi/raspiledjs npm init
E rispondere alle domande per creare il package del progetto
Adesso possiamo installare le dipendenze necessarie: Express (per creare il server REST), rpi-gpio (per gestire i GPIO del Rpi)
npm install express rpi-gpio --save
Io ho inoltre voluto creare una pagina html semplice con dei bottoni per accendere e spegnere il led. Anche questa pagina sarà servita da node che funzionerà da web server.
Per creare l’interfaccia velocemente ho utilizzato boostrap e jquery, anch’esse installate con NPM:
npm install bootstrap jquery cors --save
Adesso che abbiamo installato il necessario, passiamo al codice dell’applicazione. Io ho creato un “index.js” con questo contenuto:
var http = require('http'); var express = require('express'); var gpio = require('rpi-gpio'); var exec = require('child_process').exec; var cors = require('cors'); var app = express(); app.use(express.static(__dirname + '/public')); app.use('/node_modules/bootstrap', express.static(__dirname + '/node_modules/bootstrap')); app.use('/node_modules/jquery', express.static(__dirname + '/node_modules/jquery')); app.use(cors()); var LEDPIN = 8; var SERVERPORT = 80; app.get('/led/:on', function (req, res) { console.log('led request = ' + req.params.on); if (req.params.on == 'on') { setLed(LEDPIN,1, function(err){ if (err) { res.status(500).send('Oops, Something went wrong! ' + err); } else { res.status(200).send("LED 1 ON"); } }); } else if (req.params.on == 'off') { setLed(LEDPIN,0, function(err){ if (err) { res.status(500).send('Oops, Something went wrong! ' + err); } else { res.status(200).send("LED 1 OFF"); } }); } else if (req.params.on == 'read') { readStatus(LEDPIN, function(err, value){ if (err) { res.status(500).send('Oops, Something went wrong! ' + err); } else { res.status(200).send(value); } }); } else { res.status(404).send("Bad Request. Use /led1/on or /led1/off or /led1/read"); } }); app.post('/shutdown', function (req, res) { res.status(200).send("Raspberry is shutting down..."); unexportPins(); execute("halt"); }); app.post('/restart', function (req, res) { res.status(200).send("Raspberry is restarting..."); unexportPins(); execute("restart"); }); // Express route for any other unrecognised incoming requests app.get('*', function (req, res) { res.status(404).send('Unrecognised API call.'); }); // Express route to handle errors app.use(function (err, req, res, next) { if (req.xhr) { res.status(500).send('Oops, Something went wrong!'); } else { next(err); } }); function setLed(PIN, value, callback) { console.log("Setting pin "+PIN+" to " + value); gpio.write(PIN, value, function(err) { if (err) { console.log("error writing " + err); callback("error writing " + err); return; } callback(); }); } function readStatus(PIN, callback) { console.log("reading pin "+PIN); gpio.read(PIN, function(err,value) { if (err) { console.log("error reading pin " + err, null); callback("error reading pin " + err, null); return; } callback(null,value); }); } //catches ctrl+c event process.on('SIGINT', function(){ unexportPins(); }); function unexportPins() { gpio.destroy(function() { console.log('All pins unexported'); process.exit(); }); } function execute(command) { exec(command, function (error, stdout, stderr) { console.log('exec: ' + stdout); console.log('stderr: ' + stderr); if (error !== null) { console.log('exec error: ' + error); } }); } //init pin and app gpio.setup(LEDPIN, gpio.DIR_OUT, function(err){ if (err) { console.log("Error opening pin " + err); return; } app.listen(SERVERPORT); console.log('GPIO setup completed and server listening on port ' + SERVERPORT); });
L’unica configurazione da fare sono le variabili LEDPIN e SERVERPORT che trovate alle righe 12 e 13 dello script:
- LEDPIN è il numero del GPIO che volete pilotare (segue la numerazione standard del Raspberry Pi, con il pin 1 in alto a sinistra, il secondo in alto a destra e così via. Riferimento completo qui.)
- SERVERPORT è la porta sulla quale starà in ascolto il server. La porta 80 permette di accedere agli indirizzi semplicemente con http://ipraspberry/led/on
Una volta salvato il file, possiamo testarlo con un’unica accortezza: eseguirlo con i permessi di root, altrimenti il raspberry non riuscirà a gestire i GPIO
sudo node index.js
se tutto è andato bene dovreste leggere una frase del tipo
GPIO setup completed and server listening on port 80
a questo punto potete provare ad attivare il vostro GPIO semplicemente da un qualsiasi browser in rete con il raspberry (anche da smartphone), aprendo l’indirizzo
http://192.168.1.10/led/on
dove 192.168.1.10 è l’ip del Raspberry in questo caso. (vi consiglio di impostarlo statico…)
Nella consolle di Node dovreste leggere contemporaneamente
led request = on Setting pin 8 to
Gli endpoint che questo script rende disponibili sono i seguenti:
- [GET] http://192.168.1.10/led/on – Attiva il pin
- [GET] http://192.168.1.10/led/off – Disattiva il pin
- [GET] http://192.168.1.10/led/read – Legge lo stato del pin
- [POST] http://192.168.1.10/shutdown – Spegne il Raspberry
- [POST] http://192.168.1.10/reboot – Riavvia il Raspberry
Ovviamente al pin comandato potrete collegare qualsiasi cosa, non solo un led !
Se infine volete eseguire uno script node in automatico all’avvio, vi consiglio l’ottimo Forever.
Interfaccia web
Lascio qua anche la paginetta web che ho creato per testare i servizi. Sia il file index.html che functions.js vanno salvati nella cartella “public” dell’applicazione node appena creata (ovvero /home/pi/raspiledjs/public ) e saranno raggiungibili semplicemente all’indirizzo http://ipdelraspberry/
nota: all’inizio del file functions.js inserire l’ip del raspberry nella variabile url.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>RASPiLedJs</title> <!-- Bootstrap --> <link href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="./functions.js"></script> </head> <body> <h1>RASPiLedJs</h1> <label>Last response: </label> <input type="text" id="response"> <h2>LED</h2> <div class="btn-group" role="group" aria-label="..."> <button type="button" class="btn btn-default" onclick="setLed('on')">LED ON</button> <button type="button" class="btn btn-default" onclick="setLed('off')">LED OFF</button> </div> <h2>SYSTEM</h2> <div class="btn-group" role="group" aria-label="..."> <button type="button" class="btn btn-default" onclick="restart()">RESTART</button> <button type="button" class="btn btn-default" onclick="shutdown()">SHUTDOWN</button> </div> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="../node_modules/jquery/dist/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script> </body> </html>
var url = "http://192.168.1.125/"; function setLed (status) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open( "GET", url+"led/"+status , true ); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState === 4) { var serverResponse = xmlHttp.responseText; output(serverResponse); } }; xmlHttp.send( null ); } function shutdown () { var xmlHttp = new XMLHttpRequest(); xmlHttp.open( "POST", url+"shutdown" , true ); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState === 4) { var serverResponse = xmlHttp.responseText; output(serverResponse); } }; xmlHttp.send( null ); } function restart () { var xmlHttp = new XMLHttpRequest(); xmlHttp.open( "POST", url+"restart" , true ); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState === 4) { var serverResponse = xmlHttp.responseText; output(serverResponse); } }; xmlHttp.send( null ); } function output(text) { var elem = document.getElementById("response"); if (elem!= null) elem.value = text; }