# 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 ```bash # 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 $USER ``` --- ## GPIO Control mit Node.js ```typescript // 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 { return new Promise(resolve => setTimeout(resolve, ms)); } ``` ```typescript // 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 ```typescript // 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 { 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 { 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'); } ``` ```typescript // 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 { await bme280.init(); console.log('BME280 initialized'); } export async function readBME280(): Promise { 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 }; } ``` ```typescript // 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 { 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 ```typescript // 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 { 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 { while (this.running) { try { // Alle Sensoren lesen const [dht, distance] = await Promise.allSettled([ readDHT22WithRetry(), measureDistance() ]); const data: Record = { 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 { 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 { 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 { 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 ```typescript // 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 = 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'); }); ``` ```html Pi Sensor Dashboard

Pi Sensor Dashboard

--
Temperature (°C)
--
Humidity (%)
``` --- ## Systemd Service ```ini # /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 ``` ```bash # 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 -f ``` --- ## Fazit Raspberry Pi mit Node.js bietet: 1. **Full Linux**: Komplettes OS mit npm-Ökosystem 2. **GPIO Access**: Direkte Hardware-Steuerung 3. **Networking**: WiFi, Ethernet, Bluetooth 4. **Web Stack**: Express, Socket.io, React Perfekt für IoT-Gateways und Edge Computing. --- ## Bildprompts 1. "Raspberry Pi with connected sensors and wires, IoT prototype" 2. "Web dashboard showing sensor data graphs, real-time monitoring" 3. "Smart home hub with Pi and multiple sensor nodes, IoT network" --- ## Quellen - [Node.js on Raspberry Pi Guide](https://fleetstack.io/blog/nodejs-raspberry-pi-complete-guide) - [InfluxDB + Node-RED + Raspberry Pi](https://www.influxdata.com/blog/node-red-influxdb-raspberry-pi-iot-sensor-tutorial/) - [onoff GPIO Library](https://www.npmjs.com/package/onoff) - [SitePoint GPIO Tutorial](https://www.sitepoint.com/getting-started-with-the-raspberry-pi-gpio-pins-in-node-js/)