-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgrowdammit.ino
332 lines (272 loc) · 7.38 KB
/
growdammit.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// comment out this next line to enable debug mode
// this sets the reporting period to 10 seconds instead of one minute
// and outputs on the serial port
//#define DEBUG
// ssid info goes in here
#include "config.h"
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <WiFiUdp.h>
#include <SparkFunHTU21D.h>
#include <Wire.h>
// =INDIRECT("R[0]C[-6]",false)/86400+"1-Jan-1970"-(5/24)
#define DATETIME_JAZZ "%3DINDIRECT%28%22R%5B0%5DC%5B-6%5D%22%2Cfalse%29%2F86400%2B%221-Jan-1970%22-%285%2F24%29"
// =INDIRECT("R[0]C[-1]", false)
#define TIME_JAZZ "%3DINDIRECT%28%22R%5B0%5DC%5B-1%5D%22%2Cfalse%29"
// MUX LOW = light, HIGH = soil
#define MUX 0
#define SOIL_ENABLE 4
#define LIGHT_ENABLE 5
#define WEATHER_DA 12
#define WEATHER_CL 14
// humidity and temp sensor library
HTU21D weather;
// HTTPS client
WiFiClientSecure https;
// for NTP client
WiFiUDP udp;
unsigned long unix_time;
unsigned long reset_time;
unsigned long inline ntp_unix_time();
String urlencode(String str);
unsigned long chip_id;
// Jan++4+2017+11%3A11%3A11
char build_time_cstr[25];
// 100.00
char humidity_cstr[7];
char temperature_cstr[7];
String build_time;
int ambient_light, soil_moisture;
float humidity, temperature;
unsigned int i, len;
char https_url[512];
void setup() {
// disables soil and light sensors on boot
pinMode(SOIL_ENABLE, OUTPUT);
digitalWrite(SOIL_ENABLE, LOW);
pinMode(LIGHT_ENABLE, OUTPUT);
digitalWrite(LIGHT_ENABLE, LOW);
pinMode(MUX, OUTPUT);
Wire.begin(WEATHER_DA, WEATHER_CL);
weather.begin();
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
// figure out roughly when our reset time was
reset_time = 0;
while (reset_time == 0)
reset_time = ntp_unix_time();
reset_time -= (millis() / 1000);
chip_id = ESP.getChipId();
sprintf(build_time_cstr, "%s %s", __DATE__, __TIME__);
build_time = urlencode(String(build_time_cstr));
pinMode(LED_BUILTIN, OUTPUT);
#ifdef DEBUG
Serial.begin(115200);
Serial.println();
Serial.println("GROW DAMMIT!");
#endif
}
unsigned int sample_ambient_light()
{
unsigned int ambient_light;
digitalWrite(LIGHT_ENABLE, HIGH);
digitalWrite(MUX, LOW);
delay(10);
ambient_light = analogSample();
digitalWrite(LIGHT_ENABLE, LOW);
return ambient_light;
}
unsigned int sample_soil_moisture()
{
unsigned int soil_moisture;
digitalWrite(SOIL_ENABLE, HIGH);
digitalWrite(MUX, HIGH);
delay(10);
soil_moisture = analogSample();
digitalWrite(SOIL_ENABLE, LOW);
return soil_moisture;
}
void loop() {
digitalWrite(LED_BUILTIN, LOW);
ambient_light = sample_ambient_light();
soil_moisture = sample_soil_moisture();
// read humidity and temperature
humidity = humiditySample();
dtostrf(humidity, 5, 2, humidity_cstr);
temperature = temperatureSample();
dtostrf(temperature, 5, 2, temperature_cstr);
unix_time = reset_time + (millis() / 1000);
len = sprintf(https_url, "%s", logging_url);
len += sprintf(https_url+len, "?unix_time=%d", unix_time);
len += sprintf(https_url+len, "&temperature=%s", temperature_cstr);
len += sprintf(https_url+len, "&humidity=%s", humidity_cstr);
len += sprintf(https_url+len, "&ambient_light=%d", ambient_light);
len += sprintf(https_url+len, "&soil_moisture=%d", soil_moisture);
len += sprintf(https_url+len, "&chip_id=%d", chip_id);
len += sprintf(https_url+len, "&build_time=%s", build_time.c_str());
len += sprintf(https_url+len, "&reset_time=%d", reset_time);
len += sprintf(https_url+len, "&datetime=%s", DATETIME_JAZZ);
//len += sprintf(https_url+len, DATETIME_JAZZ);
len += sprintf(https_url+len, "&time=%s", TIME_JAZZ);
//len += sprintf(https_url+len, TIME_JAZZ);
https.connect(logging_host, 443);
https.print(String("GET ") + https_url + " HTTP/1.1\r\n" +
"Host: " + logging_host + "\r\n" +
"User-Agent: growdammit\r\n" +
"Connection: close\r\n\r\n");
digitalWrite(LED_BUILTIN, HIGH);
#ifdef DEBUG
Serial.println(https_url);
delay(10000);
#else
delay(LOG_PERIOD);
#endif
}
unsigned long ntp_unix_time()
{
unsigned long unix_time = 0;
const byte NTP_HEADER[4] = { 0xEC, 0x06, 0x00, 0xE3 };
const byte NULL_DATA[1] = { 0x00 };
if (!udp.begin(ntp_local_port))
return 0;
// send our NTP packet
udp.flush();
udp.beginPacket(ntp_server, ntp_port);
udp.write(NTP_HEADER, 4);
for (i = 0; i < 44; i++)
udp.write(NULL_DATA, 1);
udp.endPacket();
// read response
for (i = 0; i < 20; i++)
{
if ((len = udp.parsePacket()) == 48)
break;
delay(200);
}
if (len != 48)
return 0;
for (int i = 0; i < 32; i++)
udp.read();
unix_time = udp.read();
for (int i = 0; i < 3; i++)
unix_time = unix_time << 8 | udp.read();
udp.flush();
return unix_time - 2208988800ul;
}
String urlencode(String str)
{
String encodedString = "";
char c;
char code0;
char code1;
char code2;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (c == ' ') {
encodedString += '+';
} else if (isalnum(c)) {
encodedString += c;
} else {
code1 = (c & 0xf) + '0';
if ((c & 0xf) > 9) {
code1 = (c & 0xf) - 10 + 'A';
}
c = (c >> 4) & 0xf;
code0 = c + '0';
if (c > 9) {
code0 = c - 10 + 'A';
}
code2 = '\0';
encodedString += '%';
encodedString += code0;
encodedString += code1;
//encodedString+=code2;
}
yield();
}
return encodedString;
}
#define NUM_SAMPLES 100
#define NUM_DISCARD 10
unsigned int samples[NUM_SAMPLES];
void bubble_sort(unsigned int samples[], unsigned int len) {
for (int i = 0; i < (len-1); i++) {
for (int j = 0; j < (len-(i+1)); j++) {
if(samples[j] > samples[j+1]) {
unsigned int t = samples[j];
samples[j] = samples[j+1];
samples[j+1] = t;
}
}
}
}
// read in 100 values, sort them, and
// discard the largest and smallest 10
// then take the average of the rest
unsigned int analogSample()
{
unsigned int i;
unsigned long average;
for (i = 0; i < NUM_SAMPLES; i++)
{
samples[i] = analogRead(A0);
delay(1);
}
bubble_sort(samples, NUM_SAMPLES);
for (i = NUM_DISCARD; i < (NUM_SAMPLES-NUM_DISCARD); i++)
{
average += samples[i];
}
average = average / (NUM_SAMPLES - (2*NUM_DISCARD));
return (unsigned int)average;
}
void bubble_sort(float samples[], unsigned int len) {
for (int i = 0; i < (len-1); i++) {
for (int j = 0; j < (len-(i+1)); j++) {
if(samples[j] > samples[j+1]) {
float t = samples[j];
samples[j] = samples[j+1];
samples[j+1] = t;
}
}
}
}
#define TEMP_SAMPLES 20
#define TEMP_DISCARD 2
float tsamples[TEMP_SAMPLES];
float temperatureSample()
{
unsigned int i;
float average;
for (i = 0; i < TEMP_SAMPLES; i++)
{
tsamples[i] = weather.readTemperature();
delay(1);
}
bubble_sort(tsamples, TEMP_SAMPLES);
for (i = TEMP_DISCARD; i < (TEMP_SAMPLES-TEMP_DISCARD); i++)
{
average += tsamples[i];
}
average = average / (TEMP_SAMPLES - (2*TEMP_DISCARD));
return average;
}
float humiditySample()
{
unsigned int i;
float average;
for (i = 0; i < TEMP_SAMPLES; i++)
{
tsamples[i] = weather.readHumidity();
delay(1);
}
bubble_sort(tsamples, TEMP_SAMPLES);
for (i = TEMP_DISCARD; i < (TEMP_SAMPLES-TEMP_DISCARD); i++)
{
average += tsamples[i];
}
average = average / (TEMP_SAMPLES - (2*TEMP_DISCARD));
return average;
}