안디움(Andium)에서는 드디어 새 사무실로 이사하게 되어 매우 기쁩니다. 화이트보드로 뒤덮인 벽, 새롭고 반짝이는 기술, 집 같은 편안함으로 꾸몄습니다. 특히 공간 곳곳에 이 녹색 잎사귀 친구들을 배치하면서 진짜 집처럼 느껴지기 시작했습니다!
하지만 물을 주는 걸 잊어버렸고, 일주일 만에 우리 잎사귀 친구들은 이렇게 되었습니다...
실패. 우리는 IoT 회사인데, 이보다 나아야죠. 우리 식물들이 왜 인터넷에 연결되지 않았죠! 늦기 전에 안두이노 와이파이 쉴드와 IFTTT로 해결책을 만들었습니다. 식물들에게 목소리를 주자!
시작하기
초록 엄지(가드닝 재능)가 있다면 이미 읽기를 멈췄겠지만, 저처럼 식물에게 디지털로 자비를 구하고 물을 외칠 능력을 주고 싶다면 제대로 오셨습니다.
식물에게 목소리를 주려면 아두이노 듀 또는 제로, 안두이노 와이파이 쉴드(또는 아다프루트 IO와 통신 가능한 와이파이 연결 아두이노), 그리고 '정전 용량식 토양 수분 센서'가 필요합니다. 아, 물 한 잔도요. (다 끝나면 식물에 주세요. 이제 이메일로 고마워할 거예요).
수분 센서
토양 수분 센서는 꽤 멋집니다. 젖은 흙에 담그라고 권장하는 센서는 드물거든요, 그 기분을 즐기세요. 너무 깊진 않게요, 이 선을 주의하세요...
무슨 일이 있어도 흰 선 아래까지 센서를 담그지 마세요. 그 위 부분은 습기 노출을 잘 견디지 못하니, 이 센서가 있는 식물에 물 줄 때 조심하세요.
위 사진을 클릭해 다음 사진을 보면 센서를 물컵에 담근 이유가 궁금할 겁니다. 더 이상 궁금해하지 마세요!
정전 용량식 토양 수분 센서 보정 방법
우리 정전 용량 센서는 3.3V(빨강), 그라운드(검정), (파랑-신호) 아날로그 핀 A1에 연결할 세 개의 와이어가 있습니다. 센서 보정 시 완전히 마른 상태의 정전 용량 읽기와, 물컵에 담갔을 때(흰 선까지!)의 읽기를 기록합니다. 이게 0-100% 상대 습도 극한 임계값이 됩니다.
센서를 배선하고 이 스케치를 실행해 결과를 기록하세요. 참고로 제 dryVal은 807, wetVal은 415였습니다.
int inputPin = A1;
void setup() {
Serial.begin(115200);
delay(3000);
}
void loop() {
Serial.print("Raw val: ");
Serial.println(readHumidity());
delay(3000);
}
int readHumidity() {
int sensorVal = analogRead(inputPin);
return sensorVal;
}
이 두 숫자를 얻으면 상대 습도 매핑 설정이 가능합니다. 보정 완료!
스케치
#include <WiFi101.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "AnduinoLCD.h"
// 와이파이 파라미터
#define WLAN_SSID "Andium"
#define WLAN_PASS "maybeitsjustpassword"
// 아다프루트 IO
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883
#define AIO_USERNAME "yourusernname"
#define AIO_KEY "dosifje0j230fj32lksdfj230ff23"
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
/****************************** 피드 ***************************************/
// 온도 피드 설정
Adafruit_MQTT_Publish humidityVal = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/officePlants");
/* AnduinoLCD 인스턴스 생성 */
AnduinoLCD LCD = AnduinoLCD(ST7735_CS_PIN, ST7735_DC_PIN, ST7735_RST_PIN);
int inputPin = A1;
const int dryVal = 807;
const int wetVal = 415;
int humidity = 0;
int humidityPrev = 0;
int relHumidity = 0;
void setup() {
Serial.begin(115200);
delay(3000);
// 와이파이 & 아다프루트.IO 연결
connectToWiFi();
connectToAdafruit();
// LCD 초기화
LCD.begin();
LCDinit();
}
void loop() {
// 아다프루트 IO에 몇 번 핑해서 연결 유지 확인
if (!mqtt.ping(3)) {
// 아다프루트 IO 재연결
if (!mqtt.connected()) connect();
}
humidity = readHumidity();
// int를 char 배열로 변환
char b[4];
String str;
str = String(humidity);
for (int i = 0; i < str.length(); i++) {
b[i] = str.charAt(i);
}
b[(str.length()) + 1] = 0;
// 데이터 발행
if (!humidityVal.publish((char*)b)) {
Serial.println(F("Failed to publish temp"));
} else {
Serial.print(F("Humidity published: "));
Serial.println(humidity);
displayHumidity(humidity, humidityPrev);
}
Serial.print("Humidity: ");
Serial.print(humidity);
// 이전 습도 저장 (LCD용)
humidityPrev = humidity;
// 10분마다 반복
delay(600000);
}
// 아다프루트 IO에 MQTT로 연결
void connect() {
Serial.print(F("Connecting to Adafruit IO... "));
int8_t ret;
while ((ret = mqtt.connect()) != 0) {
switch (ret) {
case 1: Serial.println(F("Wrong protocol")); break;
case 2: Serial.println(F("ID rejected")); break;
case 3: Serial.println(F("Server unavail")); break;
case 4: Serial.println(F("Bad user/pass")); break;
case 5: Serial.println(F("Not authed")); break;
case 6: Serial.println(F("Failed to subscribe")); break;
default: Serial.println(F("Connection failed")); break;
}
if (ret >= 0) mqtt.disconnect();
Serial.println(F("Retrying connection..."));
delay(1000);
}
Serial.println(F("Adafruit IO Connected!"));
}
void displayHumidity(int humidity, int humidtyPrev) {
// 이전 값 지우기
LCD.setTextColor(ST7735_BLACK);
LCD.setTextSize(2);
LCD.setTextWrap(true);
LCD.setCursor(40, 60);
LCD.setTextSize(5);
LCD.print(humidityPrev);
LCD.setTextSize(2);
LCD.print("%");
// 새 값 출력
LCD.setTextColor(ST7735_WHITE);
LCD.setTextSize(2);
LCD.setTextWrap(true);
LCD.setCursor(40, 60);
LCD.setTextSize(5);
LCD.print(humidity);
LCD.setTextSize(2);
LCD.print("%");
}
void connectToWiFi() {
// 와이파이 액세스 포인트 연결
delay(10);
Serial.print(F("Connecting to "));
Serial.println(WLAN_SSID);
WiFi.begin(WLAN_SSID, WLAN_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println(F("WiFi connected!"));
}
void connectToAdafruit() {
// 아다프루트 IO 연결
connect();
}
void LCDinit() {
LCD.setBacklight(ON);
LCD.fillScreen(ST7735_BLACK); // 화면 지우기
LCD.showBanner(); // 안디움 배너 로드
LCD.setTextColor(ST7735_WHITE);
LCD.setTextSize(2);
LCD.setTextWrap(true);
LCD.setCursor(0, 40);
LCD.print("Humidity: ");
}
int readHumidity() {
int sensorVal = analogRead(inputPin);
relHumidity = map(sensorVal, dryVal, wetVal, 0, 100);
Serial.println("relHumidity");
Serial.print(relHumidity);
return relHumidity;
}
이 스케치를 IDE에 복사 붙여넣기 하고 다음 라이브러리들이 설치되어 있는지 확인하세요.
WiFi101
Adafruit_MQTT
AnduinoLCD
그리고 개인적으로 보정한...
const int dryVal = 807;
const int wetVal = 415;
건조/습윤 값을 사용하세요. 스케치 하단 readHumidity()에서 이 값들로 0-100% 상대 습도를 매핑합니다. 이제 값은 마르면 0%, 완전히 젖으면 100%로 읽힙니다.