제조공정
| 토양 수분 센서 | ×2 | 아날로그 출력이 있는 모든 토양 수분 센서 사용 가능. 두 전극과 전압 분배기로도 대체 가능. 선택 사항. |
| 빗물 감지 센서 | ×1 | |
| 우유 팩 (물통용) | ×1 | 물을 담을 수 있고 자르고 고정할 수 있는 모든 용기 가능. 높은 곳에 설치해야 함. |
| 연질 플라스틱 호스 | ×1 | 어떤 호스든 가능. 줄넘기 줄의 양끝을 잘라 속이 빈 부분을 사용함. |
| 점퍼 와이어 (일반) | ×37 | 다소 여유있게 준비 |
| 아두이노 101용 SparkFun Inventor's Kit | ×1 | 아두이노 101, 서보, 브레드보드, 포텐셔미터, LCD 포함 |
| 모델링 클레이 | ×1 | |
| 짧은 플라스틱 막대 | ×1 | 또는 휘지 않는 막대. 물통 출수관 지지용. |
| 330옴 저항 | ×1 |
이 장치는 도시 환경에서 식물 관수를 개선합니다. 아두이노 101로 구동되며, 내장 도구와 몇 가지 외부 센서를 사용하여 해당 환경에서 식물에 물을 주기 위한 최적의 조건을 계산하고, 계산된 시간에 자동으로 물을 줍니다.
이 장치는 우유 팩, 플라스틱 호스, 클레이(밀봉용), 서보를 사용하여 만든 저수조에서 물을 공급받습니다. 이 용기는 장치보다 높은 곳에 위치하여 장치 위로 내리는 빗물을 모을 수 있도록 설계되었습니다.
저수조는 주로 가정용 수도로 채워지지만 빗물로 보충됩니다. 팩 바닥에 구멍을 내고 출수관을 삽입하면 중력이 물을 관을 통해 끌어당깁니다. 서보가 물이 나가는 시점을 제어합니다. 평소에는 서보 암이 직립하여 물 흐름을 차단하고, 급수 시 135도 회전하여 물을 흘려보냅니다. 물의 압력은 저수조 안정성과 물 배출을 돕습니다.
이 조합은 식물 급수 최적 시간을 계산하는 데 사용됩니다. TMP36은 전자식 아날로그 출력을 가진 온도 센서로, 마이크로컨트롤러가 읽어 온도로 변환할 수 있습니다. 이 프로젝트에서는 식물 급수에 최적인 25°C에 가장 가까운 시간을 찾습니다. 시간당 30회, 2분 간격으로 기록하고, 각 기간이 끝날 때 29개 데이터(첫 번째 제외)의 평균을 계산합니다. 여기서 패턴 매칭 엔진이 작동합니다.
인텔 퀴리 PME는 아두이노 101에 내장된 인공 신경망으로, 128개 뉴런으로 구성되어 데이터를 학습하고 분류합니다. GitHub에서 라이브러리를 제공합니다.
이 프로젝트에서는 PME가 하루 동안의 온도 데이터를 기록하고 25°C에 해당하는 최적 조건을 분류합니다. 결과가 다음 날 급수 시간이 됩니다.
데이터는 오전 8시부터 오후 9시까지 매시간 기록됩니다. 첫 학습 시 데이터는 온보드 시리얼 플래시에 저장되어 전원이 꺼져도 데이터를 유지합니다. 데이터셋 확보 후 최적 조건을 분류 시도하며, 성공 시 해당 시간이 다음 라운드의 급수 시간이 됩니다. 실패 시 월별 상수(해당 월의 최고 기온 시간)를 사용합니다. 이 월별 상수가 항상 식물 급수에 최적인 것은 아니므로 PME를 활용했습니다.
첫 학습 후 데이터는 초기화되고 다음 날 다시 학습하며, 선택된 시간에 식물에 물을 줍니다. 이 주기가 무한 반복되거나 전원이 차단될 때까지 계속되며, 재부팅 시 저장된 파라미터를 급수 시간으로 사용합니다.
인텔 퀴리 RTC는 장치의 모든 동작 시점을 제어하는 핵심 부품입니다. 특히 급수 시간과 데이터 기록 시간을 위한 '시간', 백업 피크 온도 상수를 위한 '월'을 추적하는 데 필수적입니다. 하지만 정확한 날짜는 코드나 사용자 입력으로 수동 설정해야 합니다. 이를 BLE로 해결했습니다.
블루투스 저에너지는 저전력 장치를 위해 설계된 새로운 블루투스 버전으로, 센트럴-페리페럴 시스템으로 동작합니다. 센트럴(입력)이 페리페럴(출력)에 데이터를 쓰는 방식으로, 게시판 시스템처럼 센트럴이 데이터 게시판에 데이터를 올리면 모든 페리페럴이 읽습니다. 여기서는 모바일 기기의 nRF Connect를 센트럴로, 아두이노 101을 페리페럴로 사용했습니다. 모바일 기기가 아두이노에 연결하여 데이터를 전송합니다. 필요한 입력 필드 4개(시, 분, 일, 월)를 위해 모바일 기기에서 4번 데이터를 전송해야 합니다.
모바일 기기 입력은 16진수로 이루어지며, 10진수에서 변환하기 쉽지만 온라인 변환기를 사용해도 됩니다.
이 관수 솔루션 제작에는 약간의 회로 지식이 필요하지만 어렵지 않습니다. 몇 가지 비전기 부품도 필요합니다.
첨부된 코드를 아두이노 101에 업로드합니다. 아두이노 IDE와 퀴리 코어 2.0.2 이상 사용 (가능한 경우). 코드에 유용한 주석이 많이 포함되어 있습니다.
장치를 처음 켜면 모바일 기기 연결을 기다립니다. nRF Connect로 기기가 연결되면 시간 입력을 기다립니다. 아래 다이어그램과 같이 10진수 시, 분, 일, 월을 16진수 코드로 순차적으로 nRF Connect에 입력합니다.
시간 입력 전에 ID 입력(임의의 숫자)을 먼저 전송해야 합니다.
시간 입력 후 101은 모바일 기기 연결 해제를 기다립니다. 해제 후 오전 8시(당일 또는 다음 날)를 기다립니다.
오전 8시가 되면 보드는 플래시 메모리에 저장된 데이터가 있는지 확인합니다. 없으면 앞서 설명한 14시간 수집 과정을 거쳐 데이터를 분류하고 최적 시간을 결정한 후 수집 주기를 반복합니다. 데이터가 저장되어 있으면 그 데이터를 시간 상수로 사용하고 정상적으로 주기가 계속됩니다.
급수 시간에 비가 오거나 토양 수분이 과다(선택 사항)하면 당일 급수를 건너뛰고 다음 날을 기다립니다.
이 솔루션은 자동 설정을 통해 도시 식물 관수를 더 간단하고 최적화되게 만듭니다. 저수조를 사용하여 식물에 직접 내리지 않는 빗물과 기존 수도를 함께 활용함으로써 빗물을 절약합니다.
이 프로젝트가 끊임없이 변화하는 세상을 조금 더 나은 곳으로 만드는 데 도움이 되기를 바랍니다!
아두이노 101 관수 시스템 작동 코드입니다. 아두이노 IDE와 퀴리 코어 2.0.2로 업로드하세요. CuriePME는 GitHub에서 찾을 수 있습니다.
/* 도시 관수 솔루션 스케치. CuriePME를 사용해 온도 데이터를 학습하고
최적 식물 급수 조건(25°C)을 분류합니다. 해당 시간에 급수하고 데이터를
다시 학습하며 무한 반복합니다. 이 스케치는 대상 날짜 오전 8시 전에
실행해야 시작됩니다.
데이터 출처: WeatherSpark.com, Just 4 Growers */
#include "CuriePME.h"
#include <CurieBLE.h>
#include <CurieTime.h>
#include <SerialFlash.h>
#include <SPI.h>
#include <LiquidCrystal.h>
#include <Servo.h>
// 서보 핀 설정
Servo waterPipe;
// LCD 핀 설정
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// 전역 상수/변수
#define thermometer A3
#define rainSensor 1
#define humidity1 A4
#define humidity2 A1
int tm1, tm2, tm3, tm4, tm5, tm6, tm7, tm8, tm9, tm10;
int tm11, tm12, tm13, tm14, tm15, tm16, tm17, tm18, tm19, tm20;
int tm21, tm22, tm23, tm24, tm25, tm26, tm27, tm28, tm29, tm30;
int average, progav;
float voltage, temperatureC;
int tm, hourTime = -1, minuteTime = -1, dayTime = -1, monthTime = -1, confirmTime = -1;
// BLE 서비스 데이터
BLEService plantService("19B10000-E8F2-537E-4F6C-D104768A1214");
// BLE 특성, 센트럴에서 읽기/쓰기 가능
BLEUnsignedCharCharacteristic timeCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
void setup() {
waterPipe.attach(9);
waterPipe.write(0);
pinMode(rainSensor, INPUT);
lcd.begin(16, 2);
lcd.clear();
if (!SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN));
lcd.setCursor(0, 0);
lcd.print("시간 입력: BLE");
// BLE 서비스 초기화
BLE.begin();
BLE.setLocalName("Arduino 101");
BLE.setAdvertisedService(plantService);
plantService.addCharacteristic(timeCharacteristic);
BLE.addService(plantService);
timeCharacteristic.setValue(0);
BLE.advertise();
lcd.setCursor(0, 1);
lcd.print("대기 중");
// BLE 연결 대기
x: BLEDevice central = BLE.central();
if (central) {
lcd.setCursor(8, 1);
lcd.print(" 완료");
delay(3000);
while (central.connected()) {
if (timeCharacteristic.written()) {
// 순차적으로 시간 변수 채우기 (확인용 1회 + 시/분/일/월 4회)
if (confirmTime == -1) { confirmTime = timeCharacteristic.value(); }
else if (hourTime == -1) { hourTime = timeCharacteristic.value(); }
else if (minuteTime == -1) { minuteTime = timeCharacteristic.value(); }
else if (dayTime == -1) { dayTime = timeCharacteristic.value(); }
else if (monthTime == -1) {
monthTime = timeCharacteristic.value();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("시간 설정됨.");
lcd.setCursor(0, 1);
lcd.print("기기 연결 해제.");
}
}
}
} else {
goto x; // 연결 안 되면 루프
}
// 수집된 변수로 시간 설정 (초와 연도는 이 시스템에서 중요하지 않음)
setTime(hourTime, minuteTime, 00, dayTime, monthTime, 2017);
// PME 초기화
lcd.clear();
CuriePME.begin();
lcd.setCursor(0, 0);
lcd.print("저장 데이터 확인");
delay(3000);
const char *filename = "NeurData.dat";
if (check_if_exists(filename) == true) {
restoreNetworkKnowledge();
lcd.setCursor(0, 1);
lcd.print("발견!");
delay(3000);
lcd.clear();
goto z;
} else {
lcd.setCursor(0, 1);
lcd.print("없음!");
delay(3000);
lcd.clear();
}
/* setup의 나머지 부분: 하루 동안 온도 데이터 수집.
시간당 30회 체크, 2분 간격. 각 시간 끝에서 첫 스캔 제외
29개 데이터의 산술 평균을 구해 PME에 학습시킴.
오전 8시부터 오후 9시까지 14회 반복, 각기 다른 카테고리에 저장.
완료 후 전체 데이터셋을 퀴리 플래시 메모리에 기본값으로 저장. */
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("학습 중");
for (int i = 8; i < 22; i++) { // 14회 반복, 각기 다른 카테고리 저장
// 30개 온도 데이터 수집 (0-255 사이 int로 매핑)
voltage = analogRead(thermometer) * 3.3;
voltage /= 1024.0;
temperatureC = (voltage - 0.5) * 100;
tm = map(temperatureC, -40, 125, 0, 255);
tm1 = constrain(tm, 0, 255);
delay(114000);
// tm2 ~ tm30 동일하게 수집 (코드 생략 - 반복 구조)
// ...
// 첫 학습 기간에는 월별 피크 온도 시간에 급수
if ((month() == 1 || month() == 2 || month() == 11 || month() == 12) && hour() == 12) {
// 급수 로직 (비 오지 않고 토양 건조할 때)
}
// ... 월별 조건들 생략 ...
delay(110000);
// tm16 ~ tm30 수집
// ...
// 데이터를 온도 센서 사양으로 재변환
tm1 = map(tm1, 0, 255, -40, 125); tm1 = constrain(tm1, -40, 125);
// ... tm2 ~ tm30 동일 ...
// 산술 평균 계산 및 메모리 커밋
average = (tm2 + tm3 + ... + tm30) / 29;
commitSample(i, average);
}
saveNetworkKnowledge(); // 새 데이터를 플래시 메모리에 저장
z: delay(1);
}
void commitSample(int category, uint8_t s1) {
uint8_t vector[1];
vector[0] = s1;
CuriePME.learn(vector, 1, category);
}
void loop() {
// 무한 반복
/* PME가 학습한 데이터 중 25°C(식물 급수 최적 온도)를 분류 시도.
분류 성공 시 반환된 카테고리가 급수 시간이 됨.
데이터 삭제 후 재학습, 무한 반복.
분류 실패 시 앞서 정의한 월별 기본값 사용. */
uint8_t vector[1];
vector[0] = 25;
int answer = CuriePME.classify(vector, 1);
if (answer == CuriePME.noMatch) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("매칭 없음!");
// 월별 기본값 사용
if (month() == 1 || month() == 2 || month() == 11 || month() == 12) { answer = 12; }
else if (month() == 3 || month() == 4 || month() == 10) { answer = 14; }
else if (month() == 5 || month() == 6 || month() == 9) { answer = 15; }
else if (month() == 7 || month() == 8) { answer = 16; }
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("카테고리:");
lcd.setCursor(0, 1);
lcd.print(answer);
}
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("최적화 완료");
delay(3000);
CuriePME.forget(); // 삭제 후 재학습 (플래시 메모리는 지우지 않음)
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("대기 중");
while (hour() != 8); // 오전 8시까지 대기
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("학습 중");
// 데이터 재학습 (setup과 유사한 루프)
for (int i = 8; i < 22; i++) {
// 온도 데이터 30개 수집 및 평균 계산
// ...
// PME가 결정한 시간에 급수
if (hour() == answer) {
// 급수 로직 (비 오지 않고 토양 건조할 때)
}
// 평균 계산 및 커밋
average = (tm2 + tm3 + ... + tm30) / 29;
progav = (tm30 - tm2) / 2;
commitSample(i, average);
}
}
/* 플래시 메모리 데이터 참고: 데이터는 한 번만 저장되며 이 코드로는 변경 불가.
전원 손실 시 최초 사용 시 설정으로 기본값 복원됨.
완전히 지우려면 IDE의 "CurieSerialFlash"에서 "EraseEverything" 업로드 후
이 스케치 재업로드하여 새 평균값 저장 필요. */
void saveNetworkKnowledge() {
// 플래시 메모리 저장 코드 (전체 코드는 다운로드 참조)
}
회로 프리칭 다이어그램. 배터리는 왼쪽에 부착됩니다. 토양 수분 센서는 선택 사항입니다.
rainpoweredsmartirrigationsystem_OHtd4bVfb3.fzz
제조공정
부품 및 소모품 아두이노 나노 R3 × 1 Adafruit 표준 LCD - 16x2 백색/청색 × 1 미니 워터 펌프 × 1 로터리 인코더 (푸시버튼 포함) × 1 L298N IC × 1 포토 레지스터 (조도 센서) × 1 범용 NPN 트랜지스터 × 1 저항 10kΩ × 1 단회전 포텐셔미터 10kΩ × 1 커패시터 10µF × 1 커패시터 1000µF × 1 커패시터 470µF × 1 커패시터 100nF × 7 1N4001 다이오드 × 8 RCA 소켓 × 1 토양 수분 센
부품 및 소모품 아두이노 듀에 (ARM 기반, 3.3V 로직) 1개 라즈베리 파이 2 모델 B 1개 Adafruit BMP180 기압/온도/고도 센서 (중국산 호환품 사용) 1개 Adafruit ADS1115 16비트 ADC (중국산 호환품 사용) 1개 SparkFun 토양 수분 센서 (나사 단자 포함, 비교기 모듈 달린 중국산 사용) 4개 서보 Tower Pro MG996R 2개 릴레이 (일반) 2개 브레드보드 (일반) 1개 앱 및 온라인 서비스 마이크로소프트 비주얼 스튜디오 2015 아두이노