1 min read
AutomatisierungRaspberry Pi IoT Projekte mit Node.js
IoT-Projekte mit Raspberry Pi und Node.js. GPIO-Steuerung, Sensor-Integration, MQTT und Web Dashboards.
Raspberry PiNode.jsGPIOIoTSensorsHome Automation

Raspberry Pi IoT Projekte mit Node.js
Meta-Description: IoT-Projekte mit Raspberry Pi und Node.js. GPIO-Steuerung, Sensor-Integration, MQTT und Web Dashboards.
Keywords: Raspberry Pi, Node.js, GPIO, IoT, Sensors, Home Automation, MQTT, Embedded Linux
Einführung
Der Raspberry Pi ist der perfekte Single-Board Computer für IoT-Projekte. Mit Node.js kombiniert man die Hardware-Nähe mit dem mächtigen npm-Ökosystem für Web-basierte IoT-Anwendungen.
Raspberry Pi Setup
┌─────────────────────────────────────────────────────────────┐
│ RASPBERRY PI 5 SPECIFICATIONS │
├─────────────────────────────────────────────────────────────┤
│ │
│ CPU: Broadcom BCM2712 quad-core Arm Cortex-A76 @ 2.4GHz │
│ RAM: 4GB / 8GB LPDDR4X-4267 │
│ │
│ Connectivity: │
│ ├── Gigabit Ethernet │
│ ├── Dual-band WiFi 802.11ac │
│ ├── Bluetooth 5.0 / BLE │
│ ├── 2× USB 3.0 + 2× USB 2.0 │
│ └── PCIe 2.0 x1 │
│ │
│ GPIO: │
│ ├── 40-pin GPIO Header │
│ ├── 26 GPIO Pins │
│ ├── I2C, SPI, UART │
│ └── PWM (Hardware) │
│ │
│ Storage: microSD / NVMe (via HAT) │
│ Power: 5V/5A USB-C │
│ │
│ GPIO Pinout (Subset): │
│ ┌────┬────┬────────────────────────┐ │
│ │ 1 │ 2 │ 3.3V | 5V │ │
│ │ 3 │ 4 │ GPIO2 | 5V │ │
│ │ 5 │ 6 │ GPIO3 | GND │ │
│ │ 7 │ 8 │ GPIO4 | GPIO14(TX) │ │
│ │ 9 │ 10 │ GND | GPIO15(RX) │ │
│ └────┴────┴────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘Node.js Installation
# Node.js via NodeSource (empfohlen)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
# Alternativ: NVM (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
# Verify
node --version
npm --version
# Build Tools für native Addons
sudo apt-get install -y build-essential python3
# GPIO Berechtigungen (ohne sudo)
sudo usermod -aG gpio $USER
sudo usermod -aG i2c $USER
sudo usermod -aG spi $USERGPIO Control mit Node.js
// lib/gpio.ts
import { Gpio } from 'onoff';
// LED Control
const LED_PIN = 17;
const led = new Gpio(LED_PIN, 'out');
// LED einschalten
led.writeSync(1);
// LED ausschalten
led.writeSync(0);
// Asynchrone Steuerung
async function blinkLED(times: number, intervalMs: number) {
for (let i = 0; i < times; i++) {
await led.write(1);
await sleep(intervalMs);
await led.write(0);
await sleep(intervalMs);
}
}
// Button mit Interrupt
const BUTTON_PIN = 18;
const button = new Gpio(BUTTON_PIN, 'in', 'both', { debounceTimeout: 50 });
button.watch((err, value) => {
if (err) {
console.error('Button error:', err);
return;
}
console.log(`Button: ${value === 1 ? 'pressed' : 'released'}`);
});
// Cleanup bei Programmende
process.on('SIGINT', () => {
led.unexport();
button.unexport();
process.exit();
});
// Helper
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}// Für Raspberry Pi 5: gpiod verwenden
// lib/gpio-pi5.ts
import { Chip, Line } from '@iiot2k/gpiox';
const chip = new Chip(4); // Pi5 verwendet Chip 4
// LED Setup
const ledLine = chip.getLine(17);
ledLine.requestOutput('led');
// LED steuern
ledLine.setValue(1); // On
ledLine.setValue(0); // Off
// Button Setup mit Events
const buttonLine = chip.getLine(18);
buttonLine.requestInput('button');
buttonLine.requestBothEdges();
buttonLine.addEventListener('value', (event) => {
console.log(`Button value: ${event.value}`);
});Sensor-Integration
// lib/sensors/dht22.ts
import sensor from 'node-dht-sensor';
const DHT_TYPE = 22; // DHT22
const DHT_PIN = 4;
interface Reading {
temperature: number;
humidity: number;
timestamp: Date;
}
export async function readDHT22(): Promise<Reading> {
return new Promise((resolve, reject) => {
sensor.read(DHT_TYPE, DHT_PIN, (err, temperature, humidity) => {
if (err) {
reject(err);
return;
}
resolve({
temperature: Math.round(temperature * 10) / 10,
humidity: Math.round(humidity * 10) / 10,
timestamp: new Date()
});
});
});
}
// Retry Logic für zuverlässigere Messungen
export async function readDHT22WithRetry(maxRetries = 3): Promise<Reading> {
let lastError: Error | null = null;
for (let i = 0; i < maxRetries; i++) {
try {
const reading = await readDHT22();
// Plausibilitätsprüfung
if (reading.temperature > -40 && reading.temperature < 80 &&
reading.humidity >= 0 && reading.humidity <= 100) {
return reading;
}
} catch (err) {
lastError = err as Error;
await sleep(2000); // DHT braucht Zeit zwischen Messungen
}
}
throw lastError || new Error('Failed to read DHT22');
}// lib/sensors/bme280.ts
import BME280 from 'bme280-sensor';
const bme280 = new BME280({ i2cBusNo: 1, i2cAddress: 0x76 });
interface EnvironmentData {
temperature: number;
humidity: number;
pressure: number;
}
export async function initBME280(): Promise<void> {
await bme280.init();
console.log('BME280 initialized');
}
export async function readBME280(): Promise<EnvironmentData> {
const data = await bme280.readSensorData();
return {
temperature: Math.round(data.temperature_C * 10) / 10,
humidity: Math.round(data.humidity * 10) / 10,
pressure: Math.round(data.pressure_hPa * 10) / 10
};
}// lib/sensors/ultrasonic.ts
import { Gpio } from 'onoff';
const TRIG_PIN = 23;
const ECHO_PIN = 24;
const trigger = new Gpio(TRIG_PIN, 'out');
const echo = new Gpio(ECHO_PIN, 'in', 'both');
export function measureDistance(): Promise<number> {
return new Promise((resolve, reject) => {
let startTime: bigint;
let endTime: bigint;
let timeout: NodeJS.Timeout;
// Timeout nach 1 Sekunde
timeout = setTimeout(() => {
reject(new Error('Measurement timeout'));
}, 1000);
echo.watch((err, value) => {
if (err) {
clearTimeout(timeout);
reject(err);
return;
}
if (value === 1) {
startTime = process.hrtime.bigint();
} else {
endTime = process.hrtime.bigint();
clearTimeout(timeout);
// Distanz berechnen
const duration = Number(endTime - startTime) / 1e9; // Sekunden
const distance = (duration * 34300) / 2; // cm
resolve(Math.round(distance * 10) / 10);
}
});
// Trigger Puls
trigger.writeSync(1);
setTimeout(() => trigger.writeSync(0), 10); // 10µs Puls
});
}MQTT Sensor Node
// apps/sensor-node.ts
import mqtt from 'mqtt';
import { readDHT22WithRetry } from './lib/sensors/dht22';
import { measureDistance } from './lib/sensors/ultrasonic';
import os from 'os';
interface SensorNodeConfig {
brokerUrl: string;
deviceId: string;
readIntervalMs: number;
}
class SensorNode {
private client: mqtt.MqttClient;
private config: SensorNodeConfig;
private baseTopic: string;
private running = false;
constructor(config: SensorNodeConfig) {
this.config = config;
this.baseTopic = `sensors/${config.deviceId}`;
this.client = mqtt.connect(config.brokerUrl, {
clientId: config.deviceId,
will: {
topic: `${this.baseTopic}/status`,
payload: Buffer.from('offline'),
qos: 1,
retain: true
}
});
}
async start(): Promise<void> {
await this.waitForConnection();
// Online Status
this.client.publish(
`${this.baseTopic}/status`,
'online',
{ retain: true }
);
// Device Info
this.client.publish(
`${this.baseTopic}/info`,
JSON.stringify({
device_id: this.config.deviceId,
hostname: os.hostname(),
platform: os.platform(),
arch: os.arch(),
uptime: os.uptime(),
memory: {
total: os.totalmem(),
free: os.freemem()
}
}),
{ retain: true }
);
// Commands abonnieren
this.client.subscribe(`${this.baseTopic}/command`);
this.client.on('message', (topic, payload) => {
this.handleCommand(payload.toString());
});
this.running = true;
this.sensorLoop();
}
private async sensorLoop(): Promise<void> {
while (this.running) {
try {
// Alle Sensoren lesen
const [dht, distance] = await Promise.allSettled([
readDHT22WithRetry(),
measureDistance()
]);
const data: Record<string, any> = {
device_id: this.config.deviceId,
timestamp: new Date().toISOString()
};
if (dht.status === 'fulfilled') {
data.temperature = dht.value.temperature;
data.humidity = dht.value.humidity;
}
if (distance.status === 'fulfilled') {
data.distance = distance.value;
}
// System Metrics
data.system = {
cpu_temp: await this.getCPUTemperature(),
memory_usage: (1 - os.freemem() / os.totalmem()) * 100,
load_average: os.loadavg()[0]
};
// Publish
this.client.publish(
`${this.baseTopic}/data`,
JSON.stringify(data),
{ qos: 1 }
);
console.log('Published:', data);
} catch (error) {
console.error('Sensor read error:', error);
}
await this.sleep(this.config.readIntervalMs);
}
}
private handleCommand(payload: string): void {
try {
const command = JSON.parse(payload);
switch (command.action) {
case 'restart':
console.log('Restart command received');
process.exit(0); // Systemd wird neustarten
break;
case 'setInterval':
this.config.readIntervalMs = command.value;
console.log(`Interval set to ${command.value}ms`);
break;
case 'status':
this.publishStatus();
break;
}
} catch (error) {
console.error('Command parse error:', error);
}
}
private async getCPUTemperature(): Promise<number> {
try {
const { readFile } = await import('fs/promises');
const temp = await readFile('/sys/class/thermal/thermal_zone0/temp', 'utf8');
return parseInt(temp) / 1000;
} catch {
return 0;
}
}
private publishStatus(): void {
this.client.publish(
`${this.baseTopic}/status/detailed`,
JSON.stringify({
running: this.running,
uptime: process.uptime(),
memory: process.memoryUsage(),
interval: this.config.readIntervalMs
})
);
}
private waitForConnection(): Promise<void> {
return new Promise((resolve, reject) => {
this.client.on('connect', () => {
console.log('Connected to MQTT broker');
resolve();
});
this.client.on('error', reject);
setTimeout(() => reject(new Error('Connection timeout')), 30000);
});
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
stop(): void {
this.running = false;
this.client.publish(
`${this.baseTopic}/status`,
'offline',
{ retain: true }
);
this.client.end();
}
}
// Verwendung
const node = new SensorNode({
brokerUrl: process.env.MQTT_URL || 'mqtt://localhost:1883',
deviceId: process.env.DEVICE_ID || `rpi-${os.hostname()}`,
readIntervalMs: 30000
});
node.start();
// Graceful Shutdown
process.on('SIGINT', () => {
console.log('Shutting down...');
node.stop();
process.exit(0);
});Web Dashboard
// apps/dashboard/server.ts
import express from 'express';
import { createServer } from 'http';
import { Server as SocketServer } from 'socket.io';
import mqtt from 'mqtt';
const app = express();
const server = createServer(app);
const io = new SocketServer(server);
// MQTT Client
const mqttClient = mqtt.connect(process.env.MQTT_URL || 'mqtt://localhost:1883');
// Sensor Data speichern
const sensorData: Map<string, any[]> = new Map();
const MAX_HISTORY = 100;
mqttClient.on('connect', () => {
console.log('MQTT connected');
mqttClient.subscribe('sensors/+/data');
mqttClient.subscribe('sensors/+/status');
});
mqttClient.on('message', (topic, payload) => {
const parts = topic.split('/');
const deviceId = parts[1];
const type = parts[2];
if (type === 'data') {
const data = JSON.parse(payload.toString());
// History speichern
if (!sensorData.has(deviceId)) {
sensorData.set(deviceId, []);
}
const history = sensorData.get(deviceId)!;
history.push(data);
if (history.length > MAX_HISTORY) {
history.shift();
}
// An WebSocket Clients senden
io.emit('sensorData', { deviceId, data });
}
if (type === 'status') {
io.emit('deviceStatus', {
deviceId,
status: payload.toString()
});
}
});
// Static Files
app.use(express.static('public'));
// API Endpoints
app.get('/api/devices', (req, res) => {
const devices = Array.from(sensorData.keys()).map(id => ({
id,
lastReading: sensorData.get(id)?.slice(-1)[0]
}));
res.json(devices);
});
app.get('/api/devices/:id/history', (req, res) => {
const history = sensorData.get(req.params.id) || [];
res.json(history);
});
// WebSocket Events
io.on('connection', (socket) => {
console.log('Client connected');
// Initial Data senden
sensorData.forEach((history, deviceId) => {
socket.emit('initialData', {
deviceId,
history: history.slice(-20)
});
});
// Command an Gerät senden
socket.on('command', ({ deviceId, action, payload }) => {
mqttClient.publish(
`sensors/${deviceId}/command`,
JSON.stringify({ action, ...payload })
);
});
});
server.listen(3000, () => {
console.log('Dashboard running on http://localhost:3000');
});<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Pi Sensor Dashboard</title>
<script src="https://cdn.socket.io/4.7.4/socket.io.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: sans-serif; padding: 20px; background: #1a1a2e; color: #eee; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
.card { background: #16213e; border-radius: 12px; padding: 20px; }
.value { font-size: 48px; font-weight: bold; color: #00d9ff; }
.label { color: #888; margin-top: 5px; }
canvas { max-height: 200px; }
</style>
</head>
<body>
<h1>Pi Sensor Dashboard</h1>
<div class="grid">
<div class="card">
<div class="value" id="temperature">--</div>
<div class="label">Temperature (°C)</div>
</div>
<div class="card">
<div class="value" id="humidity">--</div>
<div class="label">Humidity (%)</div>
</div>
<div class="card">
<canvas id="chart"></canvas>
</div>
</div>
<script>
const socket = io();
const ctx = document.getElementById('chart').getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Temperature',
data: [],
borderColor: '#00d9ff',
tension: 0.4
}, {
label: 'Humidity',
data: [],
borderColor: '#ff6b6b',
tension: 0.4
}]
},
options: {
responsive: true,
scales: { y: { beginAtZero: false } }
}
});
socket.on('sensorData', ({ deviceId, data }) => {
document.getElementById('temperature').textContent = data.temperature?.toFixed(1) || '--';
document.getElementById('humidity').textContent = data.humidity?.toFixed(1) || '--';
// Chart Update
const time = new Date(data.timestamp).toLocaleTimeString();
chart.data.labels.push(time);
chart.data.datasets[0].data.push(data.temperature);
chart.data.datasets[1].data.push(data.humidity);
if (chart.data.labels.length > 20) {
chart.data.labels.shift();
chart.data.datasets.forEach(ds => ds.data.shift());
}
chart.update('none');
});
socket.on('initialData', ({ deviceId, history }) => {
history.forEach(data => {
const time = new Date(data.timestamp).toLocaleTimeString();
chart.data.labels.push(time);
chart.data.datasets[0].data.push(data.temperature);
chart.data.datasets[1].data.push(data.humidity);
});
chart.update();
});
</script>
</body>
</html>Systemd Service
# /etc/systemd/system/sensor-node.service
[Unit]
Description=Raspberry Pi Sensor Node
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/sensor-node
ExecStart=/usr/bin/node dist/apps/sensor-node.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
Environment=MQTT_URL=mqtt://localhost:1883
[Install]
WantedBy=multi-user.target# Service aktivieren
sudo systemctl daemon-reload
sudo systemctl enable sensor-node
sudo systemctl start sensor-node
# Status prüfen
sudo systemctl status sensor-node
journalctl -u sensor-node -fFazit
Raspberry Pi mit Node.js bietet:
- Full Linux: Komplettes OS mit npm-Ökosystem
- GPIO Access: Direkte Hardware-Steuerung
- Networking: WiFi, Ethernet, Bluetooth
- Web Stack: Express, Socket.io, React
Perfekt für IoT-Gateways und Edge Computing.
Bildprompts
- "Raspberry Pi with connected sensors and wires, IoT prototype"
- "Web dashboard showing sensor data graphs, real-time monitoring"
- "Smart home hub with Pi and multiple sensor nodes, IoT network"