├── 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 | 20 | 42 | 46 | 50 | 54 | 58 | 62 | 66 | 70 | 74 | 78 | 82 | 86 | 90 | 94 | 98 | 102 | 106 | 110 | 114 | 115 | 117 | wemos_d1_mini.svg 118 | 119 | 121 | 123 | 124 | 126 | image/svg+xml 127 | 129 | 130 | 131 | 132 | 133 | 138 | 141 | 143 | 146 | 149 | 151 | 154 | 157 | 159 | 162 | 165 | 167 | 170 | 173 | 175 | 178 | 184 | 192 | 200 | 208 | 216 | 224 | 232 | 240 | 248 | 256 | 264 | 272 | 280 | 288 | 296 | 304 | 312 | 315 | 317 | TX 330 | 331 | 332 | 335 | 337 | RX 350 | 351 | 352 | 355 | 357 | D1 370 | 371 | 372 | 375 | 377 | D2 390 | 391 | 392 | 395 | 397 | D3 410 | 411 | 412 | 415 | 417 | D4 430 | 431 | 432 | 435 | 437 | G 450 | 451 | 452 | 455 | 457 | 5V 470 | 471 | 472 | 475 | 477 | 3V3 490 | 491 | 492 | 495 | 497 | D8 510 | 511 | 512 | 515 | 517 | D7 530 | 531 | 532 | 535 | 537 | D6 550 | 551 | 552 | 555 | 557 | D5 570 | 571 | 572 | 575 | 577 | D0 590 | 591 | 592 | 595 | 597 | A0 610 | 611 | 612 | 615 | 617 | RST 630 | 631 | 632 | 641 | 650 | 658 | 667 | 668 | 669 | 670 | 673 | 675 | WeMos D1 mini 688 | 689 | 690 | 696 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 1006 | 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 | --------------------------------------------------------------------------------