אנו נתקלים באופן קבוע בפרויקטים שבהם בקרי HVAC (Danfoss, Honeywell, Daikin VRV) משתמשים בפרוטוקולים שונים. אתר אחד — Modbus RTU על RS-485 ליחידות טיפול אוויר, BACnet/IP לצ'ילרים, פרוטוקול קנייני ליחידות פנימיות של Daikin. האפליקציה הניידת שלנו עובדת עם נתונים מנורמלים דרך שער API, ולכן אנו מקדישים תשומת לב מיוחדת לפרשנות נכונה של נתוני המקור. שגיאה בשלב הפענוח עלולה להוביל למצבי חירום: לדוגמה, אם טמפרטורת האספקה נקראת כ-unsigned במקום signed, המערכת עלולה לפרש 10°C- כ-65436°C ולכבות את החימום. במהלך השנים צברנו מאגר נתונים של תצורות אופייניות לרוב הבקרים הנפוצים.
פרמטרים מרכזיים ומקורותיהם
קבוצה מינימלית לניטור אקלים:
| פרמטר | מקור | יחידה | תדירות |
|---|---|---|---|
| טמפרטורת אספקה/החזרה | חיישני PT1000/NTC על הצנרת | °C | 30 שניות |
| טמפרטורת אוויר באזור | חיישן חדר או תרמוסטט | °C | דקה |
| לחות | SHT31 או HIH6130 באזור | %RH | דקה |
| נקודת יעד (Setpoint) | בקר | °C | בעת שינוי |
| מצב מדחס | קלט דיגיטלי של הבקר | פעיל/כבוי | בעת שינוי |
| COP (מקדם ביצועים) | מחושב בשרת | — | 5 דקות |
ניואנס חשוב: טמפרטורה ב-Modbus Holding Registers מגיעה לעיתים קרובות כ-signed int16 ביחידות של 0.1°C. אם הבקר מוציא 0xFF9C, זה לא 65436°C — זה 100-, כלומר 10.0°C-. פרשנות שגויה היא מקור קלאסי לשגיאת "החיישן מציג 3200°C-".
fun parseModbusTemperature(rawValue: Int): Double { // Конвертируем unsigned 16-bit в signed
val signed = if (rawValue > 32767) rawValue - 65536 else rawValue
return signed / 10.0
}לפי מפרט פרוטוקול היישום של Modbus v1.1b, כתובות הרשם מתחילות ב-40001, אך ב-API של השער הן עשויות להיות מוזזות. לכן אנו תמיד מאמתים את מיפוי הרשמים מול תיעוד הבקר.
יישום באנדרואיד: שאילתות באמצעות Retrofit + coroutines
השער (Node-RED או שירות Go מותאם) מספק REST API. שאילתות במרווח אדפטיבי — תכופות כשהאפליקציה בחזית, דלילות ברקע:
class HvacPollingService(
private val api: HvacApi,
private val repository: HvacRepository,
) {
private var pollingJob: Job? = null
fun startPolling(scope: CoroutineScope, foreground: Boolean) {
pollingJob?.cancel()
val interval = if (foreground) 15_000L else 60_000L
pollingJob = scope.launch {
while (isActive) {
try {
val data = api.getHvacStatus()
repository.update(data)
} catch (e: IOException) {
// Логируем, не крэшим — потеря связи с шлюзом штатная ситуация
}
delay(interval)
}
}
}
}לאתרים עם שער MQTT (Eclipse Mosquitto), אנו משתמשים ב-fun parseModbusTemperature(rawValue: Int): Double { // Конвертируем unsigned 16-bit в signed val signed = if (rawValue > 32767) rawValue - 65536 else rawValue return signed / 10.0 } . נושאים לפי אזור: class HvacPollingService( private val api: HvacApi, private val repository: HvacRepository, ) { private var pollingJob: Job? = null fun startPolling(scope: CoroutineScope, foreground: Boolean) { pollingJob?.cancel() val interval = if (foreground) 15_000L else 60_000L pollingJob = scope.launch { while (isActive) { try { val data = api.getHvacStatus() repository.update(data) } catch (e: IOException) { // Логируем, не крэшим — потеря связи с шлюзом штатная ситуация } delay(interval) } } } } , org.eclipse.paho.client.mqttv3. MQTT מעביר שינויים כמעט מיידית — פי 10 מהר יותר משאילתות עם אותו עומס רשת.
| פרמטר | שאילתות (REST) | MQTT |
|---|---|---|
| השהיית מסירה | 1–15 שניות | < 0.5 שניות |
| עומס רשת | גבוה | נמוך |
| אמינות | תלוי במרווח | הודעות QoS 1/2 |
| מורכבות יישום | נמוכה | בינונית |
מגמות והיסטוריה
גרף טמפרטורה לשבוע הוא אלמנט חובה. עבור נתונים לטווח ארוך, אנו מבקשים צבירה בשרת (ממוצע/מינימום/מקסימום לשעה), ומונעים רשומות גולמיות של 30 שניות. באפליקציה אנו מציגים באמצעות hvac/{buildingId}/{unitId}/temperature (אנדרואיד) או hvac/{buildingId}/{unitId}/setpoint (Flutter):
LineChartData buildTemperatureChart(List<TemperatureReading> history) {
return LineChartData(
lineBarsData: [
LineChartBarData(
spots: history.asMap().entries.map((e) => FlSpot(e.key.toDouble(), e.value.temperature)).toList(),
isCurved: true,
color: Colors.blue,
dotData: FlDotData(show: false),
),
],
titlesData: FlTitlesData(
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) => Text(formatHour(history[value.toInt()].timestamp)),
),
),
),
);
} התראות על ערכים מחוץ לטווח
טמפרטורה חורגת מהגבולות — נדרשת הודעה. אנו מגדירים כללים בשרת (לדוגמה, באמצעות Node-RED או צבירות רציפות של TimescaleDB), ושולחים התראות דרך FCM. באפליקציה, אנו שומרים היסטוריית התראות מקומית ב-Room/SQLite — המשתמש חייב לראות מתי ומה קרה, גם אם ההודעה נסגרה.
מדוע חשוב לפרש נכון נתוני פרוטוקול?
גם עם קריאת רשם נכונה, עלולים להיווצר פערים: בקרים שונים משתמשים בסדר בתים שונה (little-endian לעומת big-endian) ובקידוד נתונים שונה. לדוגמה, חלק מהבקרים מעבירים טמפרטורה במעלות פרנהייט עם מכפיל של 100, לא 10. הניסיון שלנו מראה שב-30% מהפרויקטים יש אי-התאמות בין התיעוד לפרוטוקול בפועל. לכן, אנו תמיד מבצעים שאילתת בדיקה של כל הרשמים ומצלבים מול חיישני ייחוס. זה מבטיח שהאפליקציה מציגה נתונים נכונים מהיום הראשון.
כיצד אנו מבטיחים ניטור רציף?
הפתרון המרכזי הוא יתירות בערוצי התקשורת. אם השער הראשי דרך Modbus אינו זמין, האפליקציה עוברת אוטומטית ל-BACnet/IP או ל-Cloud API גיבוי. אנו משתמשים בתבנית Chain of Responsibility עם timeouts. בנוסף, אנו מגדירים מטמון נתונים מקומי במכשיר למקרה של אובדן רשת. באחד הפרויקטים שלנו לקניון עם 200 נקודות ניטור, הבטחנו מסירת התראות תוך 30 שניות עם זמינות שרת של 99.9%.
מה כולל הפיתוח?
העבודה שלנו כוללת:
- תיעוד פרוטוקולים ונקודות נתונים (כתובות רשם, מקדמי המרה).
- קוד מקור של האפליקציה עם הערות ובדיקות.
- אינטגרציה עם מערכות קיימות (BMS, SCADA) דרך API.
- הדרכת צוות הלקוח לשימוש באפליקציה.
- תמיכה טכנית למשך 3 חודשים לאחר ההשקה.
תהליך: ביקורת בקרים קיימים → עיצוב ארכיטקטורת אינטגרציה → פיתוח אפליקציה ניידת (iOS/Android) → בדיקות ספסל → פריסה באתר. לוח זמנים: 4 עד 6 שבועות לפרויקט טיפוסי. העלות מחושבת באופן אישי לאחר ניתוח הפרוטוקולים ומספר נקודות הניטור. צרו קשר כדי לדון בפרטים ולקבל ייעוץ — ננתח את התשתית שלכם ונציע פתרון סוהר מיטבי.
לפיכך, אנו מבטיחים ניטור HVAC אמין עם דיוק נתונים עד 0.1°C וזמן תגובה לחירום מתחת ל-30 שניות. הפקידו את בקרת האקלים בידי אנשי מקצוע עם ניסיון של 5+ שנים ולמעלה מ-20 פרויקטים שהושלמו בהצלחה. קבלו ייעוץ לפרויקט שלכם עוד היום.







