├── LICENSE
├── README.md
├── fritzing
├── WeMos D1 mini.fzpz
└── wemos_d1_mini.layers.svg
├── monitor
├── .gitignore
├── keys.h
└── monitor.ino
└── sensor
├── .gitignore
├── keys.h
└── sensor.ino
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Nathan Heskew
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AWS IoT Soil Moisture Monitor
2 |
3 | Simple remote soil moisture monitor using a [WeMos D1 mini (based on based on the ESP-8266EX)](http://www.wemos.cc/wiki/doku.php?id=en:d1_mini) and [SparkFun Soil Moister Sensor](https://www.sparkfun.com/products/13322) on the sensor side and an [Adafruit HUZZAH ESP8266 Feather](https://www.adafruit.com/products/2821) and [Adafruit NeoPixel FeatherWing](https://www.adafruit.com/products/2945) for a remote display.
4 |
5 | ## Code
6 |
7 | - [WeMos D1 mini based sensor](sensor/)
8 | - [Adafruit HUZZAH ESP8266 and NeoPixel monitor](monitor/)
9 |
10 |
--------------------------------------------------------------------------------
/fritzing/WeMos D1 mini.fzpz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heskew/things-aws-iot-soil-monitor/5131a85650b0fd0b492c61e90e7ef9cef40f9a68/fritzing/WeMos D1 mini.fzpz
--------------------------------------------------------------------------------
/fritzing/wemos_d1_mini.layers.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
1007 |
--------------------------------------------------------------------------------
/monitor/.gitignore:
--------------------------------------------------------------------------------
1 | keys.cpp
--------------------------------------------------------------------------------
/monitor/keys.h:
--------------------------------------------------------------------------------
1 | #ifndef KEYS_H_
2 | #define KEYS_H_
3 |
4 | // aws
5 | extern const char* awsKeyID;
6 | extern const char* awsSecKey;
7 |
8 | // aws iot
9 | extern const char* awsIotRegion;
10 | extern const char* awsIotEndpoint;
11 | extern const char* awsIotDomain;
12 |
13 | // wifi
14 | extern const char* wifiSsid;
15 | extern const char* wifiPwd;
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/monitor/monitor.ino:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Nathan Heskew
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | #include
24 | #include
25 | #include
26 | #include "keys.h"
27 |
28 | #include
29 |
30 | #include
31 |
32 | #define PIN 15
33 |
34 | // Parameter 1 = number of pixels in strip
35 | // Parameter 2 = pin number (most are valid)
36 | // Parameter 3 = pixel type flags, add together as needed:
37 | // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
38 | // NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
39 | // NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
40 | // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
41 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(32, PIN, NEO_GRB + NEO_KHZ800);
42 |
43 | void setColor(uint8_t r, uint8_t g, uint8_t b, bool immediate = false);
44 | uint8_t getSoilMoistureValue();
45 |
46 | void setup() {
47 |
48 | Serial.begin(115200);
49 |
50 | strip.begin();
51 | strip.show(); // Initialize all pixels to 'off'
52 |
53 | setColor(1, 1, 1, true);
54 |
55 | Serial.println("Started!");
56 | }
57 |
58 | void loop() {
59 |
60 | uint8_t value = getSoilMoistureValue();
61 | if (value <= 10) {
62 | // too little water
63 | setColor(1, 0, 0);
64 | }
65 | else if (value > 10 && value <= 30) {
66 | // should probably water
67 | setColor(1, 1, 0);
68 | }
69 | else if (value > 30 && value <= 60) {
70 | // just enough water
71 | setColor(0, 1, 0);
72 | }
73 | else {
74 | // too much water
75 | setColor(0, 0, 1);
76 | }
77 |
78 | delay(5000);
79 | }
80 |
81 | void setColor(uint8_t r, uint8_t g, uint8_t b, bool immediate) {
82 |
83 | for (uint8_t i = 0; i < 32; ++i) {
84 | strip.setPixelColor(i, r, g, b);
85 | if (immediate == false && (i+1)%8 == 0) {
86 | strip.show();
87 | delay(50);
88 | }
89 | }
90 |
91 | if (immediate == true) {
92 | strip.show();
93 | }
94 | }
95 |
96 | void printWiFiData() {
97 |
98 | // IP address
99 | Serial.print("IP Address: ");
100 | Serial.println(WiFi.localIP());
101 |
102 | // MAC address
103 | byte mac[6];
104 | WiFi.macAddress(mac);
105 | Serial.print("MAC address: ");
106 | Serial.print(mac[5], HEX);
107 | Serial.print(":");
108 | Serial.print(mac[4], HEX);
109 | Serial.print(":");
110 | Serial.print(mac[3], HEX);
111 | Serial.print(":");
112 | Serial.print(mac[2], HEX);
113 | Serial.print(":");
114 | Serial.print(mac[1], HEX);
115 | Serial.print(":");
116 | Serial.println(mac[0], HEX);
117 | }
118 |
119 | void printCurrentNetwork() {
120 |
121 | // SSID
122 | Serial.print("SSID: ");
123 | Serial.println(WiFi.SSID());
124 |
125 | // signal strength:
126 | Serial.print("signal strength (RSSI): ");
127 | Serial.println( WiFi.RSSI());
128 | }
129 |
130 | uint8_t getSoilMoistureValue() {
131 |
132 | AmazonIOTClient iotClient;
133 | ActionError actionError;
134 |
135 | Esp8266HttpClient httpClient;
136 | Esp8266DateTimeProvider dateTimeProvider;
137 |
138 | Serial.println();
139 | Serial.print("Connecting to ");
140 | Serial.print(wifiSsid);
141 | Serial.println("...");
142 |
143 | WiFi.begin(wifiSsid, wifiPwd);
144 | while (WiFi.status() != WL_CONNECTED) {
145 | Serial.print(".");
146 | delay(50);
147 | }
148 |
149 | Serial.println();
150 | Serial.println("WiFi connected");
151 |
152 | printCurrentNetwork();
153 | printWiFiData();
154 |
155 | delay(50);
156 |
157 | Serial.println("Initializing IoT client...");
158 |
159 | iotClient.setAWSRegion(awsIotRegion);
160 | iotClient.setAWSEndpoint(awsIotEndpoint);
161 | iotClient.setAWSDomain(awsIotDomain);
162 | iotClient.setAWSPath("/things/soil-sensor-one/shadow");
163 | iotClient.setAWSKeyID(awsKeyID);
164 | iotClient.setAWSSecretKey(awsSecKey);
165 | iotClient.setHttpClient(&httpClient);
166 | iotClient.setDateTimeProvider(&dateTimeProvider);
167 |
168 | Serial.println("Getting soil moisture value...");
169 | char* shadow = iotClient.get_shadow(actionError);
170 |
171 | int i = 0, l = strlen(shadow);
172 | char* body;
173 |
174 | while (i++ < l) {
175 |
176 | if (i > 0 && shadow[i] == '\n' && shadow[i-1] == '\n') {
177 |
178 | body = new char[l-1-i]();
179 |
180 | int j = 0, il = l + 1 - i;
181 | while (j < il) {
182 | body[j++] = shadow[i+j];
183 | }
184 |
185 | break;
186 | }
187 | }
188 |
189 | delete[] shadow;
190 |
191 | if (strlen(body) < 1) {
192 | Serial.println("There is no body");
193 | return 0;
194 | }
195 |
196 | StaticJsonBuffer<500> jsonBuffer;
197 |
198 | JsonObject& root = jsonBuffer.parseObject(body);
199 | delete[] body;
200 |
201 | Serial.println();
202 | Serial.print("Shadow JSON: ");
203 | root.printTo(Serial);
204 | Serial.println();
205 |
206 | uint8_t level = root["state"]["reported"]["moisture"];
207 | Serial.print("Moisture level: ");
208 | Serial.println(level);
209 |
210 | //Serial.println(ESP.getFreeHeap());
211 | Serial.println("Done!");
212 |
213 | return level;
214 | }
215 |
216 |
--------------------------------------------------------------------------------
/sensor/.gitignore:
--------------------------------------------------------------------------------
1 | keys.cpp
--------------------------------------------------------------------------------
/sensor/keys.h:
--------------------------------------------------------------------------------
1 | #ifndef KEYS_H_
2 | #define KEYS_H_
3 |
4 | // aws
5 | extern const char* awsKeyID;
6 | extern const char* awsSecKey;
7 |
8 | // aws iot
9 | extern const char* awsIotRegion;
10 | extern const char* awsIotEndpoint;
11 | extern const char* awsIotDomain;
12 |
13 | // wifi
14 | extern const char* wifiSsid;
15 | extern const char* wifiPwd;
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/sensor/sensor.ino:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Nathan Heskew
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | #include
24 | #include
25 | #include
26 | #include "keys.h"
27 |
28 | uint8_t sensorEnable = D8;
29 | uint8_t sensor = A0;
30 | uint8_t sensorSwitch = D1;
31 |
32 | const int sleepTimeS = 30;
33 |
34 | void printWiFiData();
35 | void printCurrentNetwork();
36 | bool switchIsDisabled();
37 | void publish(const char *topic, uint16_t data);
38 | void checkSoilMoisture();
39 |
40 | void setup() {
41 |
42 | Serial.begin(9600);
43 |
44 | // Pin configuration
45 | pinMode(sensorEnable, OUTPUT);
46 | pinMode(sensorSwitch, INPUT);
47 |
48 | Serial.println("Started!");
49 |
50 | checkSoilMoisture();
51 | ESP.deepSleep(sleepTimeS * 1000000);
52 | }
53 |
54 | void loop() {
55 | }
56 |
57 | void printWiFiData() {
58 |
59 | // IP address
60 | Serial.print("IP Address: ");
61 | Serial.println(WiFi.localIP());
62 |
63 | // MAC address
64 | byte mac[6];
65 | WiFi.macAddress(mac);
66 | Serial.print("MAC address: ");
67 | Serial.print(mac[5], HEX);
68 | Serial.print(":");
69 | Serial.print(mac[4], HEX);
70 | Serial.print(":");
71 | Serial.print(mac[3], HEX);
72 | Serial.print(":");
73 | Serial.print(mac[2], HEX);
74 | Serial.print(":");
75 | Serial.print(mac[1], HEX);
76 | Serial.print(":");
77 | Serial.println(mac[0], HEX);
78 | }
79 |
80 | void printCurrentNetwork() {
81 |
82 | // SSID
83 | Serial.print("SSID: ");
84 | Serial.println(WiFi.SSID());
85 |
86 | // signal strength:
87 | Serial.print("signal strength (RSSI): ");
88 | Serial.println(WiFi.RSSI());
89 | }
90 |
91 | bool sensorIsDisabled() {
92 |
93 | return digitalRead(sensorSwitch) == LOW;
94 | }
95 |
96 | void publish(const char *topic, uint8_t data) {
97 |
98 | AmazonIOTClient iotClient;
99 | ActionError actionError;
100 |
101 | Esp8266HttpClient httpClient;
102 | Esp8266DateTimeProvider dateTimeProvider;
103 |
104 | Serial.println();
105 | Serial.print("Connecting to ");
106 | Serial.print(wifiSsid);
107 | Serial.println("...");
108 |
109 | WiFi.begin(wifiSsid, wifiPwd);
110 | while (WiFi.status() != WL_CONNECTED) {
111 | Serial.print(".");
112 | delay(50);
113 | }
114 |
115 | Serial.println("");
116 | Serial.println("WiFi connected");
117 |
118 | printCurrentNetwork();
119 | printWiFiData();
120 |
121 | delay(50);
122 |
123 | Serial.println("Initializing IoT client...");
124 |
125 | iotClient.setAWSRegion(awsIotRegion);
126 | iotClient.setAWSEndpoint(awsIotEndpoint);
127 | iotClient.setAWSDomain(awsIotDomain);
128 | iotClient.setAWSPath("/things/soil-sensor-one/shadow");
129 | iotClient.setAWSKeyID(awsKeyID);
130 | iotClient.setAWSSecretKey(awsSecKey);
131 | iotClient.setHttpClient(&httpClient);
132 | iotClient.setDateTimeProvider(&dateTimeProvider);
133 |
134 | delay(50);
135 |
136 | Serial.println("Updating thing shadow...");
137 |
138 | MinimalString shadow = ("{\"state\":{\"reported\":{\"moisture\":" + String(data, DEC) + "}}}").c_str();
139 | char* result = iotClient.update_shadow(shadow, actionError);
140 |
141 | Serial.print("result: ");
142 | Serial.println(result);
143 | }
144 |
145 | void checkSoilMoisture() {
146 |
147 | Serial.println("Checking soil moisture level");
148 |
149 | if (sensorIsDisabled()) {
150 | Serial.println("Soil check is disabled..");
151 | return;
152 | }
153 |
154 | digitalWrite(sensorEnable, HIGH);
155 | delay(100);
156 |
157 | int sensorValue = analogRead(sensor);
158 | digitalWrite(sensorEnable, LOW);
159 |
160 | Serial.print("Sensor value: ");
161 | Serial.println(sensorValue);
162 |
163 | uint8_t moistureLevel = sensorValue / 1023.0f * 100;
164 |
165 | Serial.print("Moisture level: ");
166 | Serial.println(moistureLevel);
167 |
168 | publish("soil/moisture", moistureLevel);
169 | }
170 |
171 |
--------------------------------------------------------------------------------