ENTS-tock
Environmental NeTworked Sensor firmware on Tock OS
Loading...
Searching...
No Matches
irrigation.cpp
Go to the documentation of this file.
2
3#include <ArduinoJson.h>
4#include <ArduinoLog.h>
5#include <HTTPClient.h>
6#include <sys/time.h> // For gettimeofday()
7#include <time.h> // For gmtime() and strftime()
8
9#include "webserver.hpp"
10
11// Soil moisture thresholds
12static float moisture_min_threshold = 50.0f;
13static float moisture_max_threshold = 75.0f;
14static unsigned long check_interval = 10000; // 10 seconds
15static unsigned long last_check_time = 0;
17
18// Current moisture reading
19static float current_moisture_value = -1.0f;
20
22 type = Esp32Command_irrigation_command_tag;
23}
24
26 // nothing to do
27}
28
29void ModuleIrrigation::OnReceive(const Esp32Command& cmd) {
30 if (cmd.which_command != Esp32Command_irrigation_command_tag) {
31 return;
32 }
33
34 switch (cmd.command.irrigation_command.type) {
35 case IrrigationCommand_Type_CHECK:
36 Check(cmd);
37 break;
38 default:
39 break;
40 }
41}
42
44 memcpy(buffer, request_buffer, request_buffer_len);
45 return request_buffer_len;
46}
47
50 return;
51 }
52
53 unsigned long current_time = millis();
54 if ((current_time - last_check_time) < check_interval) {
55 return;
56 }
57
58 last_check_time = current_time;
59
60 Log.noticeln("Fetching soil moisture from API...");
61 float new_moisture = GetSimpleSoilMoisture();
62
63 if (new_moisture >= 0) {
64 current_moisture_value = new_moisture;
65 Log.noticeln("Moisture reading: %.1f%%", new_moisture);
67 } else {
68 Log.errorln("Failed to get moisture from API");
69 current_moisture_value = -1.0f;
70 }
71}
72
73void ModuleIrrigation::Check(const Esp32Command& cmd) {
74 IrrigationCommand resp = IrrigationCommand_init_zero;
75 resp.state = GetSolenoidState();
76 request_buffer_len =
77 EncodeIrrigationCommand(&resp, request_buffer, sizeof(request_buffer));
78}
79
81 if (!auto_irrigation_enabled || current_moisture_value < 0) {
82 return;
83 }
84
85 IrrigationCommand_State current_state = GetSolenoidState();
86
87 if (current_moisture_value < moisture_min_threshold) {
88 Log.noticeln("Moisture low (%.1f%% < %.1f%%) - Opening valve",
89 current_moisture_value, moisture_min_threshold);
90 SetSolenoidState(IrrigationCommand_State_OPEN);
91 } else if (current_moisture_value > moisture_max_threshold) {
92 Log.noticeln("Moisture adequate (%.1f%% > %.1f%%) - Closing valve",
93 current_moisture_value, moisture_max_threshold);
94 SetSolenoidState(IrrigationCommand_State_CLOSE);
95 }
96}
97
98// Helper function to format time as RFC 1123
99String formatTimeRFC1123(time_t timestamp) {
100 struct tm* tm_info = gmtime(&timestamp);
101 char buf[30];
102 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
103 return String(buf);
104}
105
106// Simple API call with proper time formatting
108 HTTPClient http;
109 float moisture_value = -1.0f;
110
111 // Get current UTC time using gettimeofday
112 struct timeval tv;
113 if (gettimeofday(&tv, NULL) == 0) {
114 time_t now = tv.tv_sec;
115 time_t start_time = now - 30; // 30 seconds ago
116
117 // Format timestamps in RFC 1123 format
118 String start_time_str = formatTimeRFC1123(start_time);
119 String end_time_str = formatTimeRFC1123(now);
120
121 String url = "http://dirtviz.jlab.ucsc.edu/api/sensor/";
122 url += "?name=sen0308";
123 url += "&measurement=humidity";
124 url += "&cellId=1448";
125 url += "&startTime=" + start_time_str;
126 url += "&endTime=" + end_time_str;
127 url += "&stream=true";
128
129 Log.traceln("API URL: %s", url.c_str());
130
131 if (http.begin(url)) {
132 Log.traceln("Requesting data from last 30 seconds...");
133 int httpCode = http.GET();
134 Log.traceln("HTTP response code: %d", httpCode);
135
136 if (httpCode == HTTP_CODE_OK) {
137 String payload = http.getString();
138 Log.traceln("Payload: %s", payload.c_str());
139
140 DynamicJsonDocument doc(2048);
141 DeserializationError error = deserializeJson(doc, payload);
142
143 if (!error) {
144 // The response is a JSON object with a "data" array
145 if (doc.containsKey("data") && doc["data"].is<JsonArray>()) {
146 JsonArray data_array = doc["data"];
147
148 if (data_array.size() > 0) {
149 // Get the most recent reading (last in array)
150 moisture_value = data_array[data_array.size() - 1].as<float>();
151 } else {
152 Log.errorln("Data array is empty");
153 }
154 } else {
155 Log.errorln("No 'data' array in API response");
156 // Debug: print all keys and their types
157 for (JsonPair kv : doc.as<JsonObject>()) {
158 if (kv.value().is<JsonArray>()) {
159 Log.traceln("Key: %s (Array)", kv.key().c_str());
160 } else {
161 Log.traceln("Key: %s, Value: %s", kv.key().c_str(),
162 kv.value().as<String>().c_str());
163 }
164 }
165 }
166 } else {
167 Log.errorln("JSON parse error: %s", error.c_str());
168 }
169 } else {
170 Log.errorln("HTTP request failed: %d", httpCode);
171 }
172 http.end();
173 } else {
174 Log.errorln("Failed to begin HTTP connection");
175 }
176 } else {
177 Log.errorln("Failed to get current time");
178 }
179
180 return moisture_value;
181}
182
183// Auto irrigation control functions
184void EnableAutoIrrigation(float min_thresh, float max_thresh) {
186 moisture_min_threshold = min_thresh;
187 moisture_max_threshold = max_thresh;
188 Log.noticeln("Auto irrigation enabled: Min=%.1f%%, Max=%.1f%%", min_thresh,
189 max_thresh);
190}
191
194 Log.noticeln("Auto irrigation disabled");
195}
196
198
200
202
203unsigned long GetCheckInterval() { return check_interval; }
204
205void SetCheckInterval(unsigned long interval_ms) {
206 check_interval = interval_ms;
207}
208
209float GetCurrentMoistureFromCache() { return current_moisture_value; }
size_t OnRequest(uint8_t *buffer)
void OnReceive(const Esp32Command &cmd)
void CheckAutoIrrigation()
uint8_t buffer[32]
size_t EncodeIrrigationCommand(const IrrigationCommand *irrigation_cmd, uint8_t *buffer, size_t size)
Encodes an IrrigationCommand.
Definition transcoder.c:322
void SetCheckInterval(unsigned long interval_ms)
bool auto_irrigation_enabled
void EnableAutoIrrigation(float min_thresh, float max_thresh)
bool IsAutoIrrigationEnabled()
float GetMaxThreshold()
float GetCurrentMoistureFromCache()
float GetSimpleSoilMoisture()
unsigned long GetCheckInterval()
void CheckIrrigationConditions()
String formatTimeRFC1123(time_t timestamp)
float GetMinThreshold()
void DisableAutoIrrigation()
Module for controling irrigation valve.
bool Check(void)
Check API health.
Definition wifi.c:353
unsigned long check_interval
bool auto_irrigation_enabled
float moisture_min_threshold
float moisture_max_threshold
IrrigationCommand_State GetSolenoidState()
Get the current solenoid state.
Definition webserver.cpp:30
void SetSolenoidState(IrrigationCommand_State newState)
Set the solenoid state.
Definition webserver.cpp:33
float GetSimpleSoilMoisture()
void CheckIrrigationConditions()