Akıllı Bina IoT: Sensör Entegrasyonu ve Uç Bilgi İşlem
Avrupa akıllı bina pazarı yakalandı 2025'te 7,5 milyar euro, Enerji verimliliğine ilişkin AB mevzuatı ve AB mevzuatının etkisiyle 2024 yılında 6,3 milyar büyüme Gayrimenkul yatırımcıları üzerinde ESG baskısı. Yapay Zeka Odaklı Bina Yönetim Sistemlerine Sahip Pilot Uygulamalar enerji tüketiminde azalmalar olduğunu ortaya koymuştur %20-50, en yüksek kazançla HVAC yoğun binalarda.
Bu makalede akıllı binalara yönelik bir IoT sisteminin eksiksiz mimarisini oluşturuyoruz: Bina otomasyon protokollerinin (BACnet, Modbus, KNX) entegrasyonundan işlemeye kadar Gerçek zamanlı enerji analitiğinden doluluk tabanlı HVAC otomasyonuna kadar MQTT ile uç noktalara ulaşın ML ile tahmin.
Ne Öğreneceksiniz
- Bina otomasyonu protokolleri: BACnet, Modbus RTU/TCP, KNX, DALI
- IoT ağ geçidi ile uç bilişim: Node-RED'li Raspberry Pi/endüstriyel PC
- MQTT komisyoncusu: sensör verileri için konu tasarımı ve QoS
- InfluxDB ve Grafana ile zaman serisi alımı
- ML ile doluluk tahmini: otomatik enerji tasarrufu
- HVAC otomasyonu: PID kontrolörü ve talebe dayalı havalandırma
- Enerji kıyaslaması: ENERGY STAR Puanı ve ISO 50001
- Gerçek zamanlı güncelleme ile binanın dijital ikizi
Bina Otomasyonu protokolleri
Modern ticari ve konut binaları özel iletişim protokolleri kullanır sensörleri, aktüatörleri ve kontrol sistemlerini bağlamak için. Bu standartların bir arada bulunması tek bir binada (çoğunlukla eski + modern) ve entegrasyonun temel zorluğu.
Ana Protokoller
- BACnet/IP: Ticari binalara yönelik ASHRAE standartları; HVAC, erişim kontrolü, yangın güvenliği için kullanılır
- Modbus TCP/RTU: basit endüstriyel protokol; enerji sensörleri, invertörler, PLC
- KNX: Konut ve ticari binalar için Avrupa standardı; aydınlatma, panjurlar, ısıtma
- - DALI: aydınlatma kontrolüne özel; gelişmiş karartma ve gruplama
- Zigbee/Z-Dalga: Düşük güçlü sensörler için kablosuz ağ (sıcaklık, doluluk, CO2)
- MQTT: modern IoT protokolü; Eski ve bulut protokolleri arasında köprü
- OPC UA'sı: endüstriyel birlikte çalışabilirlik; akıllı binaların benimsenmesi artıyor
Uç Bilgi İşlem Mimarisi
Temel model e 3 katmanlı kenar: sensörler/aktüatörler (saha seviyesi) -> uç ağ geçidi (yerel işleme, protokol çevirisi) -> bulut (analitik, ML, kontrol paneli). Gecikmeyi, bant genişliği tüketimini azaltmak ve sürekliliği sağlamak için uç işleme kritik öneme sahiptir Bulut bağlantısı kesildiğinde bile çalışır durumda.
// Edge Gateway: integrazione BACnet e MQTT con Node.js
import * as bacnet from 'node-bacnet';
import Aedes from 'aedes';
import net from 'net';
interface SensorReading {
deviceId: string;
sensorType: 'temperature' | 'humidity' | 'co2' | 'occupancy' | 'energy' | 'illuminance';
value: number;
unit: string;
timestamp: string;
quality: 'good' | 'uncertain' | 'bad';
}
// Configurazione BACnet device scanner
export class BACnetGateway {
private client = new bacnet.Client();
private discoveredDevices = new Map<number, { address: string; objects: any[] }>();
constructor(private readonly mqttBroker: Aedes) {}
startDiscovery(): void {
// Who-Is broadcast: scopri tutti i device BACnet sulla rete
this.client.whoIs();
this.client.on('iAm', (device: any) => {
const deviceId = device.deviceId;
console.log(`Discovered BACnet device: ${deviceId} at ${device.address}`);
this.discoveredDevices.set(deviceId, { address: device.address, objects: [] });
this.readDeviceObjects(deviceId, device.address);
});
}
private async readDeviceObjects(deviceId: number, address: string): Promise<void> {
// Leggi Property List: scopri tutti gli oggetti del device
this.client.readProperty(
{ ip: address },
{ type: 8, instance: deviceId }, // 8 = Device Object
bacnet.enum.PropertyIdentifier.OBJECT_LIST,
(err: Error | null, value: any) => {
if (err) {
console.error(`Error reading objects for device ${deviceId}:`, err);
return;
}
// Schedula polling dei valori ogni 60 secondi
this.schedulePolling(deviceId, address, value.values);
}
);
}
private schedulePolling(deviceId: number, address: string, objects: any[]): void {
const POLL_INTERVAL_MS = 60_000;
const poll = async () => {
for (const obj of objects.slice(0, 50)) { // Max 50 oggetti per device
try {
await this.readAndPublishObject(deviceId, address, obj);
} catch (err) {
console.warn(`Failed to read ${obj.type}:${obj.instance}:`, err);
}
}
};
poll();
setInterval(poll, POLL_INTERVAL_MS);
}
private readAndPublishObject(deviceId: number, address: string, obj: any): Promise<void> {
return new Promise((resolve) => {
this.client.readProperty(
{ ip: address },
{ type: obj.type, instance: obj.instance },
bacnet.enum.PropertyIdentifier.PRESENT_VALUE,
(err: Error | null, data: any) => {
if (err) { resolve(); return; }
const reading: SensorReading = {
deviceId: `bacnet-${deviceId}-${obj.type}-${obj.instance}`,
sensorType: this.inferSensorType(obj.type, obj.description),
value: data.values[0].value,
unit: this.getUnit(obj.type),
timestamp: new Date().toISOString(),
quality: 'good',
};
// Pubblica su MQTT con topic strutturato
const topic = `building/${deviceId}/sensor/${reading.sensorType}/${obj.instance}`;
this.mqttBroker.publish({
cmd: 'publish',
topic,
payload: Buffer.from(JSON.stringify(reading)),
qos: 1, // At least once - ok per telemetria
retain: true, // Ultimo valore disponibile per nuovi subscriber
dup: false,
}, () => resolve());
}
);
});
}
private inferSensorType(bacnetType: number, description?: string): SensorReading['sensorType'] {
// BACnet object type 0 = Analog Input, 2 = Binary Input, etc.
// Usa description per distinguere (es. "Room Temp", "CO2 Level")
const desc = (description ?? '').toLowerCase();
if (desc.includes('temp')) return 'temperature';
if (desc.includes('co2') || desc.includes('carbon')) return 'co2';
if (desc.includes('humid')) return 'humidity';
if (desc.includes('occup') || desc.includes('presence')) return 'occupancy';
if (desc.includes('energy') || desc.includes('power') || desc.includes('kwh')) return 'energy';
if (desc.includes('lux') || desc.includes('illum')) return 'illuminance';
return 'temperature'; // default
}
private getUnit(bacnetType: number): string {
const units: Record<number, string> = {
62: '°C', 64: '°F', 55: 'ppm', 91: '%RH', 83: 'kWh', 37: 'lux'
};
return units[bacnetType] ?? 'unit';
}
}
// Setup MQTT broker edge-side
export function createEdgeMqttBroker(port = 1883): Aedes {
const broker = new Aedes();
const server = net.createServer(broker.handle);
server.listen(port, () => {
console.log(`MQTT broker listening on port ${port}`);
});
return broker;
}
Modbus Entegrasyonu: Enerji Sayaçları ve PLC
Modbus ve elektrik sayaçları, invertörler için baskın protokol
fotovoltaik sistemler ve endüstriyel PLC'ler. Entegrasyon belirli kayıtların okunmasını gerektirir
kütüphane ile modbus-serial.
import ModbusRTU from 'modbus-serial';
interface ModbusEnergyReading {
activeEnergyKwh: number;
activePowerW: number;
voltageV: number;
currentA: number;
powerFactor: number;
frequencyHz: number;
}
export class ModbusEnergyMeter {
private client = new ModbusRTU();
async connect(port: string, baudRate = 9600, slaveId = 1): Promise<void> {
await this.client.connectRTUBuffered(port, { baudRate });
this.client.setID(slaveId);
this.client.setTimeout(3000);
console.log(`Modbus connected: ${port} @ ${baudRate} baud, slave ${slaveId}`);
}
// Esempio per misuratore Carlo Gavazzi EM340 (Modbus Map comune)
async readEnergyData(): Promise<ModbusEnergyReading> {
// Registro 40001 (0x0000): Tensione L1-N (in 0.1V)
const voltageReg = await this.client.readHoldingRegisters(0x0000, 2);
// Registro 40007 (0x0006): Corrente L1 (in 0.001A)
const currentReg = await this.client.readHoldingRegisters(0x0006, 2);
// Registro 40013 (0x000C): Potenza Attiva Totale (in 0.1W)
const powerReg = await this.client.readHoldingRegisters(0x000C, 2);
// Registro 40013 (0x0034): Energia Attiva Importata (in 0.1Wh)
const energyReg = await this.client.readHoldingRegisters(0x0034, 4);
// Registro 40047 (0x002E): Power Factor (in 0.001)
const pfReg = await this.client.readHoldingRegisters(0x002E, 2);
const toFloat = (regs: number[]) => {
const buf = Buffer.alloc(4);
buf.writeUInt16BE(regs[0], 0);
buf.writeUInt16BE(regs[1], 2);
return buf.readFloatBE(0);
};
return {
voltageV: toFloat(voltageReg.data),
currentA: toFloat(currentReg.data),
activePowerW: toFloat(powerReg.data),
activeEnergyKwh: toFloat(energyReg.data.slice(0, 2)) / 10,
powerFactor: toFloat(pfReg.data),
frequencyHz: 50, // fisso per reti europee
};
}
async startContinuousReading(
intervalMs: number,
onReading: (reading: ModbusEnergyReading) => void
): Promise<void> {
const loop = async () => {
try {
const reading = await this.readEnergyData();
onReading(reading);
} catch (err) {
console.error('Modbus read error:', err);
// Tenta riconnessione
await new Promise(r => setTimeout(r, 5000));
}
};
setInterval(loop, intervalMs);
}
}
InfluxDB: Sensör Verileri için Zaman Serisi
InfluxDB v3 ve yüksek frekanslı IoT için referans zaman serisi veritabanı. Toplu yazma ve InfluxQL/Flux sorgularıyla milyarlarca veri noktasını mükemmel performansla işler.
import { InfluxDB, Point, WriteApi } from '@influxdata/influxdb-client';
const client = new InfluxDB({
url: process.env['INFLUXDB_URL']!,
token: process.env['INFLUXDB_TOKEN']!,
});
const writeApi = client.getWriteApi(
process.env['INFLUXDB_ORG']!,
process.env['INFLUXDB_BUCKET']!,
'ms' // precisione timestamp
);
// Configurazione batching per performance
writeApi.useDefaultTags({ environment: 'production' });
export async function writeSensorReading(reading: SensorReading, buildingId: string): Promise<void> {
const point = new Point('sensor_reading')
.tag('building_id', buildingId)
.tag('device_id', reading.deviceId)
.tag('sensor_type', reading.sensorType)
.tag('quality', reading.quality)
.floatField('value', reading.value)
.timestamp(new Date(reading.timestamp));
writeApi.writePoint(point);
// Flush ogni 30 punti o ogni 10 secondi (gestito internamente)
}
export async function writeEnergyReading(
energy: ModbusEnergyReading,
buildingId: string,
meterId: string
): Promise<void> {
const point = new Point('energy_meter')
.tag('building_id', buildingId)
.tag('meter_id', meterId)
.floatField('active_power_w', energy.activePowerW)
.floatField('energy_kwh', energy.activeEnergyKwh)
.floatField('voltage_v', energy.voltageV)
.floatField('current_a', energy.currentA)
.floatField('power_factor', energy.powerFactor)
.timestamp(new Date());
writeApi.writePoint(point);
}
// Query Flux: consumo energetico ultimi 7 giorni con rollup orario
export const ENERGY_QUERY_7D = `
from(bucket: "smart-building")
|> range(start: -7d)
|> filter(fn: (r) => r["_measurement"] == "energy_meter")
|> filter(fn: (r) => r["building_id"] == "building-001")
|> filter(fn: (r) => r["_field"] == "active_power_w")
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|> yield(name: "mean_power_hourly")
`;
// Query: alert quando potenza supera soglia
export const POWER_ALERT_QUERY = `
from(bucket: "smart-building")
|> range(start: -5m)
|> filter(fn: (r) => r["_measurement"] == "energy_meter")
|> filter(fn: (r) => r["_field"] == "active_power_w")
|> filter(fn: (r) => r["_value"] > 150000) // 150kW - soglia alert
|> yield(name: "power_alerts")
`;
HVAC Otomasyonu için ML ile Doluluk Tahmini
En etkili kullanım senaryosu: 30-60 dakika içinde binanın her bölgesinin doluluk durumunu tahmin edin Konforu en üst düzeye çıkaracak şekilde ilk önce HVAC sistemini ön koşullandırın (veya kapatın) ve enerji tüketimini en aza indirir.
import { RandomForestClassifier } from 'ml-random-forest';
interface OccupancyFeatures {
hourOfDay: number; // 0-23
dayOfWeek: number; // 0-6 (0=Lunedi)
monthOfYear: number; // 1-12
isHoliday: boolean;
currentCo2Ppm: number; // sensore CO2 (proxy occupancy)
currentPirTriggered: boolean; // sensore PIR motion
currentDoorState: 'open' | 'closed';
temperatureDelta: number; // temp stanza - setpoint
}
export class OccupancyPredictor {
private model: RandomForestClassifier | null = null;
async train(trainingData: Array<{ features: OccupancyFeatures; occupied: boolean }>): Promise<void> {
const X = trainingData.map(d => this.featuresToArray(d.features));
const y = trainingData.map(d => d.occupied ? 1 : 0);
this.model = new RandomForestClassifier({
nEstimators: 100,
maxDepth: 10,
seed: 42,
});
this.model.train(X, y);
console.log('Occupancy prediction model trained');
}
predict(features: OccupancyFeatures): { occupied: boolean; confidence: number } {
if (!this.model) throw new Error('Model not trained');
const X = [this.featuresToArray(features)];
const prediction = this.model.predict(X);
const probabilities = this.model.predictProbability(X);
return {
occupied: prediction[0] === 1,
confidence: Math.max(...probabilities[0]),
};
}
private featuresToArray(f: OccupancyFeatures): number[] {
return [
f.hourOfDay / 23,
f.dayOfWeek / 6,
f.monthOfYear / 12,
f.isHoliday ? 1 : 0,
Math.min(f.currentCo2Ppm / 2000, 1),
f.currentPirTriggered ? 1 : 0,
f.currentDoorState === 'open' ? 1 : 0,
Math.abs(f.temperatureDelta) / 10,
];
}
}
// HVAC Automation basata su occupancy prediction
interface HVACSetpoint {
zoneId: string;
heatingSetpoint: number; // gradi C
coolingSetpoint: number; // gradi C
fanMode: 'auto' | 'on' | 'off';
mode: 'heat' | 'cool' | 'auto' | 'off';
}
export function calculateHVACSetpoint(
zoneId: string,
occupancyPrediction: { occupied: boolean; confidence: number },
currentTemp: number,
currentHour: number
): HVACSetpoint {
// Comfort setpoints (orario lavorativo)
const COMFORT_HEATING = 21.5;
const COMFORT_COOLING = 24.0;
// Setback setpoints (non occupato)
const SETBACK_HEATING = 18.0;
const SETBACK_COOLING = 27.0;
const isOccupied = occupancyPrediction.occupied && occupancyPrediction.confidence > 0.65;
// Pre-conditioning: attiva 30min prima dell'occupancy prevista
const nextHourOccupied = occupancyPrediction.occupied;
if (isOccupied || (nextHourOccupied && occupancyPrediction.confidence > 0.80)) {
return {
zoneId,
heatingSetpoint: COMFORT_HEATING,
coolingSetpoint: COMFORT_COOLING,
fanMode: 'auto',
mode: 'auto',
};
}
// Setback mode - risparmio energetico
return {
zoneId,
heatingSetpoint: SETBACK_HEATING,
coolingSetpoint: SETBACK_COOLING,
fanMode: 'off',
mode: currentTemp < SETBACK_HEATING ? 'heat' : currentTemp > SETBACK_COOLING ? 'cool' : 'off',
};
}
Enerji Karşılaştırması ve KPI'lar
| KPI'lar | Formül | Verimli Binayı Hedefleyin |
|---|---|---|
| EUI (Enerji Kullanım Yoğunluğu) | kWh/yıl / m2 | <100 kWh/m2 (ofisler, Akdeniz iklimi) |
| PUE (Güç Kullanımı Etkinliği) | Toplam güç / BT gücü | Bina içi veri merkezleri için <1,5 |
| Termal Konfor İndeksi | Konfor aralığındaki saatlerin yüzdesi (20-24°C) | >%95 saat doluluk |
| CO2 Hava Kalitesi | PPM ortalama işgal edilen bölgeler | <800 ppm (ASHRAE 62.1) |
| Doluluk Bazlı Tasarruf | Temel değere kıyasla tasarruf edilen kWh | %20-35 HVAC tasarrufu |
OT/IT Güvenliği: Kritik Altyapı
BACnet ve Modbus sistemleri kapalı ağlar için tasarlanmış olup güvenlik özelliğine sahiptir. sınırlıdır (genellikle kimlik doğrulaması yoktur). Bina otomasyon sistemlerini bağlamadan önce IP ağına, şunları uygular: ağ bölümleme (OT adanmış VLAN), tek yönlü güvenlik duvarı OT -> BT iletişimi için (veri diyotu), ağ geçitlerine uzaktan erişim ve izleme için VPN trafik anormalliklerinden Bir binanın HVAC sistemlerine yapılacak bir saldırı hasara neden olabilir önemli fizikçiler
Sonuçlar
Akıllı binalarda IoT entegrasyonu önde gelen bir teknik ve iş fırsatıdır: %20-50 enerji tasarrufu, bina sakinlerinin konforunun iyileştirilmesi ve CO2 emisyonlarının azaltılması. Edge bilişim mimarisi, bulutla bağlantının kesilmesi durumunda bile operasyonel esneklik sağlar, Doluluk tahmini için makine öğrenimi HVAC kararlarını eskisinden daha otomatik hale getirirken sürekli insan müdahalesini gerektiriyorlardı.







