- Add 100 blog posts covering AI, development, and tech topics - Add .env.example for environment configuration - Add accessibility and lighthouse audit scripts - Remove obsolete SEO reports and temporary files - Remove dev-dist build artifacts and backup files - Remove unused portrait images (moved/consolidated elsewhere) - Update contact form and component improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
767 lines
20 KiB
Markdown
767 lines
20 KiB
Markdown
# 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<void> {
|
||
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<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');
|
||
}
|
||
```
|
||
|
||
```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<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
|
||
};
|
||
}
|
||
```
|
||
|
||
```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<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
|
||
|
||
```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<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
|
||
|
||
```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<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');
|
||
});
|
||
```
|
||
|
||
```html
|
||
<!-- 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
|
||
|
||
```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/)
|