Getting Started with Node.js on Raspberry Pi

The Raspberry Pi's versatility combined with Node.js's efficiency makes for a powerful combination, perfect for IoT projects, home automation, web servers, and more. This comprehensive guide will walk you through everything you need to know about running Node.js on your Raspberry Pi, from installation to creating and deploying real-world applications.
Why Use Node.js on Raspberry Pi?
Node.js offers several advantages that make it particularly well-suited for Raspberry Pi development:
- Lightweight runtime: Runs efficiently even on the Pi's limited resources
- Asynchronous, non-blocking I/O: Perfect for handling multiple connections simultaneously
- Vast package ecosystem: Thousands of npm packages available for virtually any functionality
- JavaScript: Use the same language for frontend and backend development
-
GPIO library support: Easy hardware interfacing with libraries like
rpi-gpio
oronoff
- Real-time capabilities: Ideal for IoT applications requiring immediate responses
Prerequisites
Before we begin, make sure you have:
- A Raspberry Pi (any model, though Pi 3 or newer is recommended for better performance)
- Raspberry Pi OS installed and updated (formerly Raspbian)
- Internet connection
- Basic familiarity with Linux commands
- Optional: SSH access to your Raspberry Pi for headless setup
Installing Node.js on Raspberry Pi
There are several ways to install Node.js on your Raspberry Pi. We'll cover the most reliable methods.
Method 1: Using the Node Version Manager (NVM) (Recommended)
NVM allows you to install and manage multiple Node.js versions easily:
# Install dependencies
sudo apt update
sudo apt install -y curl build-essential libssl-dev
# Download and install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
# Load NVM in the current shell
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
# Install the latest LTS version of Node.js
nvm install --lts
# Set the LTS version as default
nvm use --lts
# Verify installation
node -v
npm -v
Method 2: Using NodeSource Repository
NodeSource provides up-to-date Node.js versions via APT:
# Install dependencies
sudo apt update
sudo apt install -y curl
# Download and execute the NodeSource installation script (for Node.js 18 LTS)
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
# Install Node.js
sudo apt install -y nodejs
# Verify installation
node -v
npm -v
Method 3: Using the Default Package Manager (Simplest but may not be latest version)
sudo apt update
sudo apt install -y nodejs npm
# Verify installation
node -v
npm -v
Setting Up Your First Node.js Project
Now that Node.js is installed, let's create a simple project:
# Create a project directory
mkdir my-pi-node-project
cd my-pi-node-project
# Initialize a new Node.js project
npm init -y
# Create a basic application file
touch app.js
Open app.js
in your favorite text editor and add:
const http = require('http');
const hostname = '0.0.0.0'; // Listen on all network interfaces
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello from Raspberry Pi!\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
Run the application:
node app.js
Access your application by opening a browser and navigating to http://[your-raspberry-pi-ip]:3000
.
Working with GPIO Pins
One of the most powerful aspects of using a Raspberry Pi is the ability to interact with physical hardware through GPIO pins.
First, install the GPIO library:
npm install onoff
Here's a simple example that toggles an LED connected to GPIO pin 17:
const { Gpio } = require('onoff');
// Initialize GPIO pin 17 as output
const led = new Gpio(17, 'out');
// Toggle LED every second
let ledState = 0;
setInterval(() => {
ledState = ledState === 0 ? 1 : 0;
led.writeSync(ledState);
console.log(`LED state: ${ledState}`);
}, 1000);
// Clean up on program exit
process.on('SIGINT', () => {
led.unexport();
console.log('Exiting application and freeing GPIO resources');
process.exit(0);
});
Save this as led-blink.js
and run it with:
sudo node led-blink.js
Note: sudo
is required for GPIO access unless you've configured your system to allow non-root GPIO access.
Reading Sensor Data
Here's how to read data from a DHT22 temperature and humidity sensor:
npm install node-dht-sensor
const sensor = require('node-dht-sensor');
// Initialize sensor (11 for DHT11, 22 for DHT22)
const sensorType = 22;
const sensorPin = 4; // GPIO pin number
setInterval(() => {
if (sensor.read(sensorType, sensorPin)) {
console.log(`Temperature: ${sensor.read(sensorType, sensorPin).temperature.toFixed(1)}°C`);
console.log(`Humidity: ${sensor.read(sensorType, sensorPin).humidity.toFixed(1)}%`);
} else {
console.warn('Failed to read sensor data');
}
}, 2000);
Creating a Web Server to Control Hardware
Let's combine web capabilities with hardware control by creating a server to control an LED:
npm install express
const express = require('express');
const { Gpio } = require('onoff');
const app = express();
const port = 3000;
// Initialize GPIO pin 17 as output
const led = new Gpio(17, 'out');
// Serve static files
app.use(express.static('public'));
// API endpoints
app.get('/api/led/on', (req, res) => {
led.writeSync(1);
res.json({ status: 'LED turned ON' });
});
app.get('/api/led/off', (req, res) => {
led.writeSync(0);
res.json({ status: 'LED turned OFF' });
});
app.get('/api/led/status', (req, res) => {
const status = led.readSync();
res.json({ status: status === 1 ? 'ON' : 'OFF' });
});
// Start server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
// Clean up on exit
process.on('SIGINT', () => {
led.unexport();
console.log('Exiting application and freeing GPIO resources');
process.exit(0);
});
Create a public
directory with an index.html
file:
mkdir public
touch public/index.html
Add this code to index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Raspberry Pi LED Control</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
.button {
display: inline-block;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
}
.button.off {
background-color: #f44336;
}
#status {
font-size: 18px;
margin: 20px 0;
}
</style>
</head>
<body>
<h1>Raspberry Pi LED Control</h1>
<div id="status">LED Status: Unknown</div>
<button class="button" id="on">Turn ON</button>
<button class="button off" id="off">Turn OFF</button>
<script>
const statusElement = document.getElementById('status');
// Update status function
function updateStatus() {
fetch('/api/led/status')
.then(response => response.json())
.then(data => {
statusElement.textContent = `LED Status: ${data.status}`;
})
.catch(error => {
console.error('Error fetching status:', error);
});
}
// Initial status update
updateStatus();
// Button event listeners
document.getElementById('on').addEventListener('click', () => {
fetch('/api/led/on')
.then(response => response.json())
.then(data => {
console.log(data);
updateStatus();
});
});
document.getElementById('off').addEventListener('click', () => {
fetch('/api/led/off')
.then(response => response.json())
.then(data => {
console.log(data);
updateStatus();
});
});
</script>
</body>
</html>
Run the server:
sudo node app.js
Access the control panel at http://[your-raspberry-pi-ip]:3000
.
Creating a Real-time IoT Dashboard with WebSockets
For real-time applications, WebSockets are ideal. Let's create a dashboard that displays temperature data in real-time:
npm install express socket.io node-dht-sensor
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const sensor = require('node-dht-sensor');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
const port = 3000;
// Sensor configuration
const sensorType = 22; // DHT22
const sensorPin = 4; // GPIO pin number
// Serve static files
app.use(express.static('public'));
// WebSocket connection
io.on('connection', (socket) => {
console.log('A client connected');
// Function to read sensor data
function readSensor() {
if (sensor.read(sensorType, sensorPin)) {
const temperature = sensor.read(sensorType, sensorPin).temperature.toFixed(1);
const humidity = sensor.read(sensorType, sensorPin).humidity.toFixed(1);
// Send data to client
socket.emit('sensorData', { temperature, humidity, timestamp: new Date() });
} else {
socket.emit('sensorError', { error: 'Failed to read sensor data' });
}
}
// Send data every 2 seconds
const interval = setInterval(readSensor, 2000);
// Handle client disconnect
socket.on('disconnect', () => {
console.log('A client disconnected');
clearInterval(interval);
});
});
// Start server
server.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
Create public/dashboard.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IoT Dashboard</title>
<script src="/socket.io/socket.io.js"></script>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.dashboard {
display: flex;
justify-content: space-around;
margin-top: 30px;
}
.card {
background-color: #f8f9fa;
border-radius: 10px;
padding: 20px;
width: 45%;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
text-align: center;
}
.value {
font-size: 3rem;
font-weight: bold;
margin: 15px 0;
}
.label {
color: #666;
font-size: 1.2rem;
}
#temperature { color: #e63946; }
#humidity { color: #457b9d; }
#timestamp {
text-align: center;
color: #666;
margin-top: 20px;
}
h1 {
text-align: center;
color: #1d3557;
}
</style>
</head>
<body>
<h1>Raspberry Pi IoT Dashboard</h1>
<div class="dashboard">
<div class="card">
<div class="label">Temperature</div>
<div class="value" id="temperature">--°C</div>
</div>
<div class="card">
<div class="label">Humidity</div>
<div class="value" id="humidity">--%</div>
</div>
</div>
<div id="timestamp">Last updated: --</div>
<script>
const socket = io();
socket.on('sensorData', (data) => {
document.getElementById('temperature').textContent = `${data.temperature}°C`;
document.getElementById('humidity').textContent = `${data.humidity}%`;
document.getElementById('timestamp').textContent = `Last updated: ${new Date(data.timestamp).toLocaleTimeString()}`;
});
socket.on('sensorError', (data) => {
console.error(data.error);
document.getElementById('timestamp').textContent = `Error: ${data.error}`;
});
</script>
</body>
</html>
Run this with:
sudo node app.js
Access the dashboard at http://[your-raspberry-pi-ip]:3000/dashboard.html
.
Running Node.js Applications as Services
To ensure your Node.js applications run at startup and restart after crashes, you can configure them as system services using systemd:
sudo nano /etc/systemd/system/my-node-app.service
Add the following content:
[Unit]
Description=My Node.js Application
After=network.target
[Service]
ExecStart=/usr/local/bin/node /home/pi/my-pi-node-project/app.js
WorkingDirectory=/home/pi/my-pi-node-project
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi
[Install]
WantedBy=multi-user.target
Enable and start the service:
sudo systemctl enable my-node-app
sudo systemctl start my-node-app
Check the status:
sudo systemctl status my-node-app
Performance Optimization for Raspberry Pi
Node.js can run well on Raspberry Pi with some optimizations:
-
Use the latest Node.js LTS version: Newer versions typically have better performance.
-
Minimize dependencies: Each npm package adds overhead. Use lightweight alternatives when possible.
-
Implement caching: Cache results of expensive operations or external API calls.
-
Use worker threads for CPU-intensive tasks:
const { Worker, isMainThread, parentPort } = require('worker_threads'); if (isMainThread) { const worker = new Worker(__filename); worker.on('message', (result) => { console.log(`Result: ${result}`); }); worker.postMessage(30); // Calculate fibonacci(30) } else { // This code runs in the worker thread parentPort.once('message', (n) => { function fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); } const result = fibonacci(n); parentPort.postMessage(result); }); }
-
Reduce logging: Excessive console output affects performance.
-
Monitor memory usage:
setInterval(() => { const memoryUsage = process.memoryUsage(); console.log(`Memory usage: ${Math.round(memoryUsage.heapUsed / 1024 / 1024 * 100) / 100} MB`); }, 30000);
-
Use PM2 for process management:
npm install -g pm2 pm2 start app.js --name "my-app" pm2 startup pm2 save
Security Considerations
When deploying Node.js applications on Raspberry Pi, consider these security measures:
-
Keep software updated:
sudo apt update && sudo apt upgrade -y nvm install --lts
-
Use environment variables for secrets:
require('dotenv').config(); const apiKey = process.env.API_KEY;
-
Implement proper authentication and authorization:
npm install passport passport-local
-
Set up a firewall:
sudo apt install ufw sudo ufw allow 22 sudo ufw allow 3000 sudo ufw enable
-
Use HTTPS:
const https = require('https'); const fs = require('fs'); const options = { key: fs.readFileSync('privkey.pem'), cert: fs.readFileSync('cert.pem') }; https.createServer(options, app).listen(443);
-
Input validation and sanitization:
npm install express-validator
Debugging Node.js Applications on Raspberry Pi
Debugging in a headless environment:
-
Remote debugging:
node --inspect=0.0.0.0:9229 app.js
Connect from Chrome on your development machine by going to
chrome://inspect
-
Using debug logs:
DEBUG=app:* node app.js
-
Tracking file operations:
strace -e trace=file node app.js
Common Raspberry Pi Node.js Projects
Here are some popular projects you might want to build:
- Home Automation Hub: Control lights, thermostats, and other smart devices.
- Weather Station: Collect and display temperature, humidity, and pressure data.
- Media Server: Stream videos and music to devices on your network.
- Security Camera: Monitor your home with motion detection.
- Voice Assistant: Create your own Alexa/Google Home alternative.
- Digital Signage: Display information on screens around your home or office.
- Retro Gaming Server: Host multiplayer classic games.
- Network Monitor: Track devices on your network and monitor bandwidth.
Conclusion
Node.js on Raspberry Pi opens up a world of possibilities for developers. Its lightweight nature, powerful asynchronous capabilities, and excellent GPIO libraries make it a perfect match for IoT projects, home automation, and much more.
By following this guide, you've learned how to:
- Install Node.js on your Raspberry Pi
- Create basic web servers and applications
- Interact with GPIO pins and sensors
- Build web interfaces to control hardware
- Create real-time applications with WebSockets
- Run Node.js applications as system services
- Optimize performance and improve security
Now it's time to build your own projects and explore the amazing potential of Node.js on the Raspberry Pi platform!