לקוח מחברת אגריקולטורה התלונן שעד 15% מהפקודות אבדו עקב עיכובי רשת בניהול ההשקיה הענני. יבול היבול נפגע ובזבוז המים הגיע ל-20%. הצענו ארכיטקטורה היברידית—בקר MQTT מקומי עם גיבוי ענן. כעת אמינות הפקודות היא 99.9%, וחיסכון במים עד 40%.
בקר השקיה בשטח הוא מכשיר עם ממסרים הפותחים וסוגרים שסתומי סולנואיד באזורים. Rain Bird, Hunter, Orbit הם השמות המוכרים ביותר. רובם מתקשרים עם "רכזות חכמות" באמצעות פרוטוקולים קנייניים. לשילוב מותאם אישית באפליקציה ניידת אנו משתמשים ב-API של היצרן (אם קיים) או מחליפים את הבקר בבקר תואם IoT (ESP32 עם MQTT, Raspberry Pi עם GPIO) או בשער ביניים. לשליטה מרחוק בהשקיה, דיוק ביצוע הפקודות וניטור מצב הם קריטיים. השקיה שהוחמצה עקב שגיאת רשת עלולה להרוס יבול, ודליפה עלולה לגרום לבזבוז מים. לכן, ארכיטקטורת האפליקציה חייבת להבטיח מסירת פקודות וטיפול בתקלות. בקר MQTT אמין פי 5 ברשת מקומית מאשר API ענני, ומספק שליטה מלאה על הציוד.
כיצד לנהל מערכת השקיה דרך אפליקציה ניידת?
אינטגרציה דרך Rachio API
Rachio הוא אחד היצרנים הבודדים עם REST API ציבורי. הרשאת OAuth 2.0, היקף full_control. לפי תיעוד Rachio, "ה-API מספק שליטה מלאה על אזורים, לוחות זמנים והגדרות מכשיר."
// iOS, async/await
class RachioClient {
let baseURL = "https://api.rach.io/1/public"
var accessToken: String
func getPersonInfo() async throws -> PersonInfo {
return try await get("/person/info")
}
func startZone(zoneId: String, duration: Int) async throws {
// duration в секундах
try await put("/zone/start", body: [
"id": zoneId,
"duration": duration
])
}
func stopDevice(deviceId: String) async throws {
try await put("/device/stop_water", body: ["id": deviceId])
}
func createScheduleRule(deviceId: String, zones: [ZoneSchedule]) async throws -> ScheduleRule {
return try await post("/schedulerule", body: [
"device": ["id": deviceId],
"name": zones.first?.name ?? "Schedule",
"zones": zones.map { ["id": $0.id, "duration": $0.durationSeconds] },
"startTime": 21600, // секунды от полуночи = 6:00
"type": "FIXED_SCHEDULE"
])
}
}Rachio Webhook מאפשר קבלת אירועים בזמן אמת: התחלת/סיום השקיה, שגיאות שסתומים, זיהוי גשם. רישום Webhook דרך API, אירועים מגיעים כבקשות POST לשרת המפתח, שמעביר אותם לאפליקציה הניידת דרך WebSocket או FCM.
| מאפיין | Rachio API | בקר MQTT |
|---|---|---|
| פרוטוקול | REST + Webhook | MQTT + WebSocket |
| שליטה באזורים | דרך הענן | מקומית, ללא השהיה |
| תלות באינטרנט | תמיד נדרש | פעולה מקומית אפשרית |
| עלות | עמלות קריאות ענן | נמוכה (רק עלות רכיבים) |
| גמישות | מוגבלת ליכולות ה-API | שליטה מלאה בלוגיקה |
למה לבחור בבקר MQTT מותאם אישית?
יישום על ESP32
להתקנות מותאמות אישית—בקר מבוסס ESP32 עם MQTT. נושאים:
irrigation/zone/1/command → {"action": "open", "duration": 300}
irrigation/zone/1/state → {"isOpen": true, "openedAt": "2024-07-15T06:00:00Z"}
irrigation/system/status → {"activeZones": [1,3], "waterFlow": 12.5, "pressure": 2.8}האפליקציה הניידת נרשמת ל-// iOS, async/await class RachioClient { let baseURL = "https://api.rach.io/1/public" var accessToken: String func getPersonInfo() async throws -> PersonInfo { return try await get("/person/info") } func startZone(zoneId: String, duration: Int) async throws { // duration в секундах try await put("/zone/start", body: [ "id": zoneId, "duration": duration ]) } func stopDevice(deviceId: String) async throws { try await put("/device/stop_water", body: ["id": deviceId]) } func createScheduleRule(deviceId: String, zones: [ZoneSchedule]) async throws -> ScheduleRule { return try await post("/schedulerule", body: [ "device": ["id": deviceId], "name": zones.first?.name ?? "Schedule", "zones": zones.map { ["id": $0.id, "duration": $0.durationSeconds] }, "startTime": 21600, // секунды от полуночи = 6:00 "type": "FIXED_SCHEDULE" ]) } } ו-irrigation/zone/1/command → {"action": "open", "duration": 300} irrigation/zone/1/state → {"isOpen": true, "openedAt": "2024-07-15T06:00:00Z"} irrigation/system/status → {"activeZones": [1,3], "waterFlow": 12.5, "pressure": 2.8} . שליטה—פרסום ל-irrigation/+/state.
עבור Flutter:
class IrrigationRepository {
late MqttServerClient _client;
final StreamController<ZoneState> _zoneStateController = StreamController.broadcast();
Stream<ZoneState> get zoneStates => _zoneStateController.stream;
Future<void> connect(MqttConfig config) async {
_client = MqttServerClient(config.host, config.clientId)
..port = config.port
..secure = true
..securityContext = config.sslContext
..keepAlivePeriod = 30
..onDisconnected = _onDisconnected;
await _client.connect(config.username, config.password);
_client.subscribe('irrigation/+/state', MqttQos.atLeastOnce);
_client.updates!.listen((messages) {
for (final message in messages) {
final payload = MqttPublishPayload.bytesToStringAsString(
(message.payload as MqttPublishMessage).payload.message,
);
final zoneId = _extractZoneId(message.topic);
_zoneStateController.add(ZoneState.fromJson(zoneId, jsonDecode(payload)));
}
});
}
Future<void> startZone(int zoneId, Duration duration) async {
final builder = MqttClientPayloadBuilder();
builder.addString(jsonEncode({
'action': 'open',
'duration': duration.inSeconds,
}));
_client.publishMessage(
'irrigation/zone/$zoneId/command',
MqttQos.atLeastOnce,
builder.payload!,
);
}
} מה מספקת אוטומציית השקיה מבוססת מזג אוויר?
לוח הזמנים של ההשקיה הוא מסך נפרד שבו ניתן להגדיר אזורים, משך וחזרתיות. מורכבות: לוח הזמנים חייב להתחשב בתחזיות מזג האוויר (דילוג על השקיה אם צפוי גשם) ובנתוני חיישני קרקע. על ידי אינטגרציה עם Open-Meteo אנו נמנעים מהשקיה מיותרת בימים גשומים, וחוסכים עד 40% מים. שליטה מדויקת מניבה חיסכון של עד 30% בחשמל למשאבות ומאריכה את חיי השסתומים.
אינטגרציה עם תחזית מזג האוויר דרך Open-Meteo API (חינמי, ללא צורך במפתח):
Future<bool> shouldSkipIrrigation(double lat, double lon) async {
final url = Uri.parse(
'https://api.open-meteo.com/v1/forecast'
'?latitude=$lat&longitude=$lon'
'&daily=precipitation_sum'
'&forecast_days=2'
'&timezone=auto'
);
final response = await http.get(url);
final data = jsonDecode(response.body);
final todayRain = data['daily']['precipitation_sum'][0] as double;
final tomorrowRain = data['daily']['precipitation_sum'][1] as double;
// Пропустить если сегодня или завтра более 5мм осадков
return todayRain > 5.0 || tomorrowRain > 5.0;
} סקירת תהליך
אנו משלימים פרויקט תוך 3-5 שבועות. שלבים:
| שלב | משך | תוצאה |
|---|---|---|
| ניתוח ועיצוב | 1-2 ימים | מפרט טכני, בחירת ציוד |
| פיתוח אפליקציה ניידת ושרת אחורי | 2-3 שבועות | קוד מקור, ברוקר MQTT מוגדר |
| אינטגרציה ובדיקות | 3-5 ימים | ניפוי פקודות, אימות על בקר אמיתי |
| פריסה והדרכה | 1-2 ימים | העלאה ל-App Store/Google Play, מדריך למפעיל |
מה כלול בעבודה
- קוד מקור של האפליקציה הניידת ל-iOS ואנדרואיד
- שרת אחורי (API, ברוקר MQTT, אינטגרציית מזג אוויר)
- הגדרת בקר וחיישנים
- תיעוד תפעולי
- הדרכת צוות (שעתיים אונליין)
- תמיכה טכנית ל-30 יום לאחר ההשקה
דרישות טכניות לשרת
- VPS עם 2 vCPU, 4 GB RAM, 50 GB SSD
- מערכת הפעלה: Ubuntu 22.04 LTS
- Docker ו-docker-compose מותקנים
- גישה ל-IP ציבורי עבור MQTT (פורט 8883) ו-HTTPS
למה לבחור בפתרון שלנו?
למעלה מ-10 שנות ניסיון בפיתוח נייד, 45 פרויקטי IoT שיושמו למגזר החקלאי. מומחים מוסמכים ב-Swift ו-Kotlin מבטיחים ביצועים יציבים של האפליקציה בשטח. השוואה: Rachio API פשוט יותר ליישום, אך בקר MQTT נותן שליטה מלאה על הציוד וחוסך עד 30% בעלויות רישוי שירותי ענן.
צרו קשר לייעוץ בנושא בחירת ארכיטקטורה. הזמינו פיתוח סוהר וקבלו ייעוץ מהנדס.
מדריך שלב אחר שלב: חיבור בקר MQTT לאפליקציית Flutter
- הגדירו ESP32: הבהבו סקיצה עם לקוח MQTT, ציינו כתובת ברוקר ונושאים.
- פרסו ברוקר MQTT (לדוגמה, Mosquitto) על שרת עם תעודת SSL.
- באפליקציית Flutter, השתמשו בחבילת
irrigation/system/status, התחברו עם שם משתמש/סיסמה. - הירשמו לנושאי מצב אזור (
irrigation/zone/+/command). - יישמו שליחת פקודות דרך פרסום ל-
class IrrigationRepository { late MqttServerClient _client; final StreamController<ZoneState> _zoneStateController = StreamController.broadcast(); Stream<ZoneState> get zoneStates => _zoneStateController.stream; Future<void> connect(MqttConfig config) async { _client = MqttServerClient(config.host, config.clientId) ..port = config.port ..secure = true ..securityContext = config.sslContext ..keepAlivePeriod = 30 ..onDisconnected = _onDisconnected; await _client.connect(config.username, config.password); _client.subscribe('irrigation/+/state', MqttQos.atLeastOnce); _client.updates!.listen((messages) { for (final message in messages) { final payload = MqttPublishPayload.bytesToStringAsString( (message.payload as MqttPublishMessage).payload.message, ); final zoneId = _extractZoneId(message.topic); _zoneStateController.add(ZoneState.fromJson(zoneId, jsonDecode(payload))); } }); } Future<void> startZone(int zoneId, Duration duration) async { final builder = MqttClientPayloadBuilder(); builder.addString(jsonEncode({ 'action': 'open', 'duration': duration.inSeconds, })); _client.publishMessage( 'irrigation/zone/$zoneId/command', MqttQos.atLeastOnce, builder.payload!, ); } }. - בדקו את המחזור: פתיחת שסתום → קבלת מצב → הצגה על המסך.







