스마트 플랜트 커뮤니케이터: 아두이노 MKR IoT 번들로 자동 원예 구현
부품 및 소모품
앱 및 온라인 서비스
| 서비스 |
| Zapier |
| ThingSpeak API |
프로젝트 개요
주의: 이 튜토리얼은 구버전일 수 있습니다. 최신 버전은 여기를 참조하세요.
소개
영국 시인 윌리엄 워즈워스가 말했듯:
"당신의 마음은 정원, 생각은 씨앗, 수확은 꽃이 될 수도 잡초가 될 수도 있다."
식물을 살려두는 건 꽤 어려운 일입니다. 식물은 의사소통을 정말 못 하니까요.
식물을 데리고 다니는 방법도 있지만, 큰 선인장이나 양치식물을 겨울 재킷 주머니에 넣고 다니긴 힘들죠. 게다가 대부분 식물은 추위를 싫어합니다.
거미 식물(Spider Plant)과 몇 달간 대화 시도 끝에 포기하고, MKR IoT 번들 구성품으로 어떤 식물이든 원격으로 안부를 확인하는 장치를 만들었습니다.
핵심 요약
이 실험에서는 아두이노 매직을 활용해 식물을 보호하고 생존을 보장하는 방법을 배웁니다.
습도, 온도, 조도를 모니터링해 식물이 건강한지 확인할 수 있습니다. 매일 이메일과 그래프를 전송하고 식물의 필요를 알림으로 받아볼 수 있습니다.
사용 센서
- TMP36 온도 센서
- 포토트랜지스터 (조도 센서)
- DIY 습도 센서 (직접 제작)
학습 목표
- Zapier 인터넷 서비스 소개
- HTTP 요청 전송
- 실시간 시계(RTC) 및 알람 관리
- DIY 습도 센서 제작
- ThingSpeak으로 값 플로팅 #ProTips
- HTML/CSS로 이메일 스타일링 #ProTips
- PostMan으로 API 테스트 #ProTips
더 알고 싶다면?
이 튜토리얼은 MKR1000과 IoT에 익숙해지는 일련의 실험 중 하나입니다. 모든 실험은 MKR IoT 번들 구성품으로 제작 가능합니다.
- Love You Pillow
- Puzzle Box
- Pavlov's Cat
- The Nerd
- Plant Communicator
Zapier 설정
Zapier는 다른 앱들의 API를 쉽게 관리하는 온라인 자동화 도구입니다. 여러 작업을 결합하거나, 아두이노가 직접 관리하기 복잡한 API를 사용할 때 유용합니다.
우리 경우 아두이노에서 가져온 데이터로 이메일을 보내는 데 사용합니다.
다음 간단 단계를 따라 나만의 잽(Zap)을 만드세요!




주어진 URL을 복사해 Test and Review 버튼을 눌러 훅을 테스트합니다. URL에 다음 파라미터 추가:
custom_webhook_URL?temperature=0&moisture=0&light=0&warning=0
이 URL을 새 브라우저 탭에 붙여넣으면 아래 같은 응답이 나옵니다.


축하합니다! 방금 HTTP 요청을 보냈습니다!
이게 바로 아두이노 보드로 할 일입니다. 브라우저에 URL을 붙여넣는 대신 직접 Zapier 서버로 보내고, 파라미터 값(현재 0)을 실제 센서 값으로 대체합니다.
API 테스트와 작업을 위한 더 고급 방법은 하단의 #ProTip을 참조하세요.
요청 테스트 후 잽 생성을 계속 진행하세요.
Do This... 단계에서 편집을 진행합니다.








지메일 받은함을 확인해 Zapier가 생성한 이메일을 확인하세요. 1단계에서 설정한 지메일 계정에서 발송됩니다.
보드 설정
다음 라이브러리를 사용합니다:
- WiFi101 - 인터넷 연결 및 네트워크 스캔
- RTCZero - 시간 기반 이벤트 관리
라이브러리 매니저에서 다운로드 가능합니다 (가이드 참조).
Zapier API 사용을 위해 MKR1000에 인증서를 업로드해야 합니다. 대부분의 온라인 서비스와 API에 적용됩니다!
WiFi101 라이브러리의 Firmware Updater 예제를 업로드하고 도메인에 hooks.zapier.com을 추가하세요.
> examples > WiFi101 > FirmwareUpdater
아두이노 웹 에디터 사용 시 이 기능은 아직 미구현이므로 아두이노 데스크톱 애플리케이션으로 해야 합니다.



HTTP 요청 전송
이제 아두이노 보드에서 HTTP 요청을 보낼 준비가 되었습니다.
당분간 파라미터에 임의 값을 넣고, 나중엔 실제 센서 값으로 대체합니다.
float temperature = 22;
int moisture = 150;
int light = 40;
String warning = "This is a warning message";
warning.replace(" ", "%20");
문자열은 인코딩해야 합니다. HTTP 요청에 공백을 보낼 수 없으므로 모든 공백을 %20으로 치환합니다.
파라미터 설정 후 send_email() 함수를 호출해 모든 값을 문자열로 변환하고, 앞서 테스트한 것과 같은 URL을 재구성해 Zapier 서버에 POST합니다.
아래 스케치에 와이파이 자격증명과 Zapier에서 받은 URL 조각을 채워 업로드하세요.
#include <WiFi101.h>
#include <WiFiSSLClient.h>
const char* ssid = SECRET_SSID; // 네트워크 SSID
const char* password = SECRET_PSWD; // 네트워크 비밀번호
String httpsRequest = SECRET_REQUEST; // Zapier URL
const char* host = "hooks.zapier.com";
WiFiSSLClient client;
void setup() {
Serial.begin(9600);
while (!Serial);
delay(2000);
Serial.print("와이파이 연결 중: ");
Serial.println(ssid);
while (WiFi.begin(ssid, password) != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("와이파이 연결됨");
}
void loop() {
float temperature = 22;
int moisture = 150;
int light = 40;
String warning = "This is a warning message";
warning.replace(" ", "%20");
send_email(temperature, moisture, light, warning);
delay(20000);
}
void send_email(float temperature, int moisture, int light, String warning) {
String _temperature = String(temperature);
String _moisture = String(moisture);
String _light = String(light);
String _warning = warning;
if (client.connect(host, 443)) {
client.println("POST " + httpsRequest + "?temperature=" + _temperature + "&moisture=" + _moisture + "&light=" + _light + "&warning=" + _warning + " HTTP/1.1");
client.println("Host: " + String(host));
client.println("Connection: close");
client.println();
delay(1000);
while (client.available()) {
char c = client.read();
Serial.write(c);
}
client.stop();
} else {
Serial.println("클라이언트 연결 실패");
}
}
설정한 이메일 주소로 20초마다 메일이 발송됩니다.
주의: Zapier 무료 티어는 월 100회 요청만 허용합니다.
RTC와 알람
MKR1000의 실시간 시계(RTC)를 이용해 매일 특정 시간에 이메일을 보낼 수 있습니다.
아래 스케치는 2017년 12월 4일 16:00으로 시간/날짜 설정 후 매일 16:01에 알람을 트리거합니다.
알람이 인터럽트 함수에 연결되므로 딜레이를 포함할 수 없습니다. 대신 불리언 변수로 loop()에서 액션을 트리거하세요.
#include <RTCZero.h>
RTCZero rtc; // RTC 객체 생성
/* 초기 시간 설정 */
const byte seconds = 0;
const byte minutes = 0;
const byte hours = 16;
/* 초기 날짜 설정 */
const byte day = 4;
const byte month = 12;
const byte year = 17;
bool email_already_sent = false;
void setup() {
Serial.begin(9600);
while (!Serial);
delay(2000);
rtc.begin(); // RTC 초기화, 24시간 형식
rtc.setTime(hours, minutes, seconds);
rtc.setDate(day, month, year);
rtc.setAlarmTime(16, 1, 0); // 이메일 발송 알람 시간
rtc.enableAlarm(rtc.MATCH_HHMMSS);
rtc.attachInterrupt(alarmMatch);
}
void loop() {
if (!email_already_sent) {
// send_email();
email_already_sent = true;
}
}
void alarmMatch() {
Serial.println("알람 매치!");
email_already_sent = false;
}
DIY 토양 습도 센서
화분에 꽂은 두 전선은 가변 저항을 형성하며, 저항값이 토양 습도에 따라 변합니다.
이 가변 저항을 분압기 구성으로 연결하면 아두이노는 두 전선 사이 저항에 비례하는 전압을 읽습니다.
즉, 토양이 더 습할수록 아두이노가 측정하는 전압은 낮아집니다.
1메가옴 저항과 전선 두 개로 DIY 토양 습도 센서를 만들 수 있습니다!

다음 스케치 업로드 후 센서 값을 읽을 수 있습니다. 마른 흙에서 먼저 테스트해 값을 기록하세요.
이 값으로 임계값을 설정하면 아두이노가 식물 물 필요 시기를 알고 긴급 이메일을 보냅니다.
int moisturePin = A2;
// 사용한 저항에 맞춰 임계값 설정
// 마른 흙과 젖은 흙에서 테스트해 가장 쉽게 보정 가능
int threeshold = 800;
void setup() {
Serial.begin(9600);
while (!Serial);
delay(2000);
}
void loop() {
Serial.println(get_average_moisture());
delay(5000);
}
int get_average_moisture() {
// 10개 값 평균으로 정확도 향상
int tempValue = 0;
for (int a = 0; a < 10; a++) {
tempValue += analogRead(moisturePin);
delay(100);
}
return tempValue / 10;
}
온도 및 조도 센서 추가
아래 회로도대로 두 센서를 배선하세요. 센서 값 읽기 함수:
float get_temperature(){
int reading = analogRead(A1);
float voltage = reading * 3.3;
voltage /= 1024.0;
// 섭씨 온도
float temperatureC = (voltage - 0.5) * 100;
// 화씨 변환
float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
return temperatureC;
}
int get_light(){
int light_value = analogRead(A0);
return light_value;
}
temperatureF를 반환하면 화씨 단위 사용 가능.
#ProTip: ThingSpeak으로 값 플로팅
데이터를 예쁜 그래프로 시각화하려면 ThingSpeak 플랫폼을 사용하세요.




ThingSpeak 라이브러리 다운로드 후 시작. 아래 스케치는 센서 값을 매분 클라우드에 업로드합니다. 와이파이 자격증명과 채널 API 키를 채워 업로드하세요.
#include <WiFi101.h>
#include <WiFiSSLClient.h>
#include "ThingSpeak.h"
const char* ssid = SECRET_SSID;
const char* password = SECRET_PSWD;
WiFiClient ThingSpeakClient;
unsigned long myChannelNumber = 356392;
const char * myWriteAPIKey = SECRET_WRITE_API;
int lightPin = A0;
int tempPin = A1;
int moisturePin = A2;
void setup() {
Serial.begin(9600);
while (!Serial);
delay(2000);
Serial.print("와이파이 연결 중: ");
Serial.println(ssid);
while (WiFi.begin(ssid, password) != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("와이파이 연결됨");
ThingSpeak.begin(ThingSpeakClient);
}
void loop() {
ThingSpeak.setField(1, get_light());
ThingSpeak.setField(2, get_temperature());
ThingSpeak.setField(3, get_average_moisture());
ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
Serial.println("클라우드에 메시지 전송됨");
delay(60000); // 1분마다 전송
}
float get_temperature() {
int reading = analogRead(tempPin);
float voltage = reading * 3.3;
voltage /= 1024.0;
float temperatureC = (voltage - 0.5) * 100;
float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
return temperatureC;
}
int get_average_moisture() {
int tempValue = 0;
for (int a = 0; a < 10; a++) {
tempValue += analogRead(moisturePin);
delay(10);
}
return tempValue / 10;
}
int get_light() {
int light_value = analogRead(A0);
return light_value;
}
완전한 프로 스케치에서 이 업로드를 매분 트리거되는 RTC 알람에 연결하는 방법을 볼 수 있습니다.
안타깝게도 지메일은 이메일 본문에 그래프와 아이프레임 임베드를 허용하지 않습니다. 하지만 아래 팁처럼 예쁜 버튼으로 링크를 보낼 수 있습니다.
ThingSpeak을 선택했지만 Dweet.io, freeboard.io 등 대안도 많습니다.
#ProTip: 이메일 스타일링
Zapier에서 이메일 본문 타입을 HTML로 변경하고 HTML/CSS를 추가하면 예쁜 이메일을 보낼 수 있습니다.



아래 템플릿 복사해 사용하세요:
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.container{ margin-left: 10%; margin-right: 10%; background-color: #ECF1F1; min-height: 100%; padding-top: 5%; padding-bottom: 10%; font-family: monospace; letter-spacing: 2px; }
.title{ text-align: center; color: #0CA1A6; font-size: 1.5em; padding-top: 0.2vh; }
.subtitle{ text-align: center; color: #0CA1A6; font-size: 1.1em; padding-bottom: 4%; padding-left: 4%; padding-right: 4%; }
.data{ padding-bottom: 2%; padding-top: 2%; padding-left: 5%; margin-left: 15%; margin-right: 15%; background-color: #F7F9F9; text-align: left; color: #4E5B61; font-size: 1em; font-weight: bold; }
.bttn{ padding-bottom: 3%; padding-top: 3%; margin-left: 25%; margin-right: 25%; margin-top: 10%; background-color: #00979D; text-align: center; color: #F7F9F9; font-size: 1.4em; }
.disclaimer{ text-align:center; color:#DA5B4A; font-size: 1.1em; font-weight: bold; margin-top: 10%; }
.bttn:hover{ background-color: #008184; }
a{ text-decoration: none; }
</style>
</head>
<body>
<div class="container">
<h2 class="title">HELLO !</h2>
<h4 class="subtitle">Here's your daily update about your garden ☘</h4>
<div class="data"> ♨ Temperature: {{querystring__temperature}} C </div>
<div id="humidity" class="data"> ☔ Moisture: {{_querystring__moisture}}</div>
<div class="data"> ☀ Light: {{querystring__light}}</div>
<div class="disclaimer">{{querystring__warning}}</div>
<a href="your_link_to_thingSpeak">
<div class="bttn">SEE THE GRAPH</div>
</a>
</div>
</body>
</html>
#ProTip: PostMan으로 API 테스트
HTTP 요청 작업은 까다로울 수 있지만, URL 생성을 돕는 도구가 많습니다.
Postman이 좋은 예시입니다:

Zapier가 준 초기 URL을 붙여넣고 파라미터 추가 후 전송하면 서버 응답을 출력하고 URL을 완성해줍니다.
코드
완전한 스케치
#ProTips 완전한 스케치
(전체 통합 코드는 원본 참조)
회로도
