UnifiedWater v1 - 아두이노 MKR GSM 1400 기반 완전 수질 모니터링 센서 키트
부품 및 소모품
| 아두이노 MKR GSM 1400 |
×1 |
| Adafruit 네오픽셀 링: WS2812 5050 RGB LED |
×1 |
| GY-21 온습도 센서 |
×1 |
| 탁도 센서, 포토트랜지스터 출력 |
×1 |
| Adafruit 방수 DS18B20 디지털 온도 센서 |
×1 |
| DFRobot Gravity: 아날로그 창끝형 pH 센서/미터 키트 |
×1 |
| 스파크펀 푸시버튼 스위치 12mm |
×1 |
| 점퍼 와이어 (일반) |
×1 |
| 저항 220Ω |
×1 |
필요 도구 및 장비
앱 및 온라인 서비스
- 아두이노 웹 에디터
- SORACOM Air IoT 연결 플랫폼
- SORACOM Harvest (데이터 수집)
- SORACOM Lagoon (대시보드 시각화)
프로젝트 소개
개요
강은 우리 생존과 교역 등에 필수적입니다. 지난 10년간 세계 수자원 오염을 줄이려 큰 노력을 기울여 왔습니다. 강이 오염원입니다. 사람들은 바다에 직접 쓰레기를 버리지 않고 강에 버립니다.
이 강물이 바다로 흘러들어가며, 한때 강에 버려진 칫솔이 세계를 돌아 반대편 해안에 닿게 됩니다.
오염과 싸우는 세상에선 데이터가 핵심입니다. 기업과 사업체가 전 지구적 수질 오염 저감 프로젝트에 쉽게 협업할 수 있어야 합니다. UnifiedWater가 그 해답입니다.
데이터를 쉽게 수집·분석할 수 있을 만큼 저렴하고 확장 가능한 장치. 기업용 모드와 개인용 모드 2가지를 제공합니다.
여러 장치를 강의 서로 다른 지점이나 다른 수역에 배치해 동일 데이터베이스로 일정 간격 데이터를 전송하면, 버튼 하나로 강/호수 상태를 모니터링할 수 있습니다.
휴대용 버전도 있습니다. 개인이 장치를 휴대하다 수질 샘플링 원할 때 버튼을 누르고 30초간 물에 담그면 데이터가 온라인 대시보드에 나타납니다.
수온, pH, 습도뿐 아니라 기온/습도까지 측정해 수질 오염 모니터링에 필요한 모든 센서를 탑재했습니다.
영상
이미지
기능
UnifiedWater는 사용자(기업 또는 개인)가 데이터를 안전하고 정확하게 수집하고, Soracom 클라우드에서 한곳에 시각화할 수 있게 합니다. 프론트엔드(물리 장치)와 백엔드로 구성됩니다.
프론트엔드
물리 장치가 데이터를 수집해 클라우드로 전송합니다. 모드 1(버튼 눌러 일회성 측정, 수시 모니터링용)과 모드 2(정해진 간격으로 자동 측정 및 전송) 두 가지 모드 지원.
MKR GSM을 프론트엔드로 사용: 사용 쉽고 신뢰성 높으며 GSM으로 Soracom 접속 가능. 데이터 수집 시 장치 동작 순서:
배터리
장치 전원 공급 방식 다양: 보드 내장 LiPo 배터리 포트, 파워뱅크, VIN 포트 직결 배터리. 배터리 용량에 따라 수명 결정. 측정 간격엔 슬립 모드로 진입해 에너지 절약.
LED 링
장치 상태 피드백용 LED 링 탑재. 3가지 모드:
- 다색 점멸: 장치 설정 중이거나 데이터 처리 중
- 빨간색 깜빡임: 경고 - 보통 물에 담그라는 뜻, 에러일 수도 있음
- 파란색/초록색 진행: 현재 샘플링 중, 물에 담가야 함
샘플 채취
샘플 채취 시 모든 센서가 물에 잠겨야 함. 센서 예열용 6초 대기 후 측정. 예열 동안에도 센서가 물에 잠겨 있는 게 최적.
데이터 파싱
데이터는 JSON 문자열 형태로 Soracom에 전송. 키-값 쌍으로 구성돼 백엔드에서 쉽게 해석 가능. 페이로드 예시:
{
"Latitude":53.3474617,
"Longitude":-6.2514529,
"waterpH":7.30,
"waterTurbidity":94,
"waterTemp":12.10,
"atmoTemp":14.50,
"atmoHum":82,
"deviceID":1,
"deviceName":"device1",
"epoch":1559392636,
"mode":2
}
백엔드
백엔드는 Soracom. 데이터 수집·시각화는 Soracom Lagoon 대시보드로 수행.
대시보드
대시보드는 장치 수집 데이터 종합. 지도에 측정 위치 표시(색상으로 오염도 표현), 선 그래프로 시계열 표시, 표로 전체 데이터 요약. pH나 탁도 값이 비정상이면 이메일 알림 발송.
확장성
장치 쉽게 확장 가능. 모든 데이터 동일 대시보드에 수집·표시. 여러 장치가 Soracom으로 데이터 스트리밍. 장치 가격 저렴하고 제작/프로그래밍 쉬워 다수 운용 용이. Soracom Krypton 같은 도구로 손쉽게 등록 가능.
각 기업/개인별 맞춤 대시보드 제공. 향후 동일 대시보드에서 협업·데이터 공유 기능 추가 희망.
장점
- 장치가 자급자족에 가까워 운영 비용 절감
- 단독 또는 수십 대 규모 플릿으로 쉽게 확장 가능
- 실시간에 가까운 빠른 데이터 수집·클라우드 전송·시각화
- 온라인 대시보드로 언제 어디서나 데이터 시각화 가능
프로젝트 제작
1단계: 필요 장비
수질/대기 파라미터 모니터링용 다수 센서 필요. 전체 목록:
- 아두이노 MKR GSM 1개
- 16개 LED RGB 링 1개
- GY-21 온습도 모듈 1개
- 탁도 센서 1개
- 방수 온도 센서 1개
- pH 센서 및 모듈 1개
- 푸시버튼 1개
- 저항 220Ω 1개
- 점퍼 와이어
2단계: 회로 연결
부품들을 납땜으로 연결. 브레드보드로 회로도 이해 돕기. 회로도 아래 참조.
MKR GSM 준비
센서 납땜 후 SIM 카드, GSM 안테나, 배터리 장착. 저는 VIN 포트에 AA 배터리 2개로 전원 공급. 단계별 사진:
3단계: 코드 이해
코드는 4개 주요 섹션으로 구성:
- 센서 데이터 수집
- 시간/날짜 획득
- 데이터 처리
- 데이터 전송
센서 데이터 수집
Serial.println("Taking Sample");
Serial.println("________________________________________");
Serial.println("Taking Sample");
Serial.println(" OK - Warming Up");
delay(6000); // 센서 보정 대기
colourLED(50);
Serial.println(" OK - Taking Sample");
Serial.print(" ");
for (int i = 0; i < 16; i++) {
if (mode == 1) {
strip.setPixelColor(i, strip.Color(0, 255, 0));
strip.show();
} else {
strip.setPixelColor(i, strip.Color(0, 0, 255));
strip.show();
}
// 변동 큰 센서들 16회 측정 후 평균
waterTurbidity += getWaterTurbidity();
waterPh += getWaterPh();
if (i > 14) {
// 고정밀 센서는 마지막 루프에서 1회만
waterTemperature = getWaterTemp();
atmoTemperature = getAtmoTemp();
atmoHumidity = getAtmoHumidity();
}
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println(" Success - Samples Taken");
for (int i = 0; i <= 16; i++) {
strip.setPixelColor(i, strip.Color(0, 0, 0));
strip.show();
delay(30);
}
Serial.println("________________________________________");
Serial.println("");
delay(500);
6초간 센서 보정 대기 후 16회 루프 돌며 매번 LED 링에 불이 들어옴. 변동 큰 센서(탁도, pH)는 16회 측정해 평균, 고정밀 센서(수온, 기온/습도)는 마지막 루프에서 1회만 측정.
시간 및 위치 획득
void getCredentials() {
Serial.println(" [1/2] Time");
Serial.println(" OK - Getting Time from RTC");
currentEpoch = processTime();
colourLED(50);
Serial.println(" [2/2] Geolocation");
Serial.println(" OK - Getting Geolocation from GPRS");
while (!getLocation());
Serial.print(" Success - Geolocation is "); Serial.print(latitude, 7); Serial.print(", "); Serial.println(longitude, 7);
colourLED(50);
}
bool getLocation() {
if (location.available()) {
latitude = location.latitude();
longitude = location.longitude();
delay(500);
return true;
} else {
delay(500);
return false;
}
}
첫 루프에서 자격 증명 처리. 시간은 설정 시 GSM 네트워크와 동기화된 온보드 RTC에서 가져오고, 지리 위치는 GPRS에서 획득.
데이터 처리
void processData() {
Serial.println(" OK - Getting Mean of Water pH and Turbidity");
waterPh = (waterPh / 16);
waterTurbidity = (waterTurbidity / 16);
Serial.println(" OK - Dumping Data to Serial");
Serial.println("");
Serial.print(" [Water] pH "); Serial.println(waterPh);
Serial.print(" [Water] Turbidity "); Serial.println(waterTurbidity);
Serial.print(" [Water] Temperature "); Serial.println(waterTemperature);
Serial.print(" [Atmo] Temperature "); Serial.println(atmoTemperature);
Serial.print(" [Atmo] Humidity "); Serial.println(atmoHumidity);
Serial.println("");
Serial.println(" Success - Data Processed");
colourLED(50);
}
String makeLine() {
Serial.println(" OK - Making String");
String dataReturned; dataReturned += "{"; dataReturned += " \n";
dataReturned += "\"Latitude\":" + String(latitude, 7); dataReturned += ", \n";
dataReturned += "\"Longitude\":" + String(longitude, 7); dataReturned += ", \n";
dataReturned += "\"waterpH\":" + String(waterPh); dataReturned += ", \n";
dataReturned += "\"waterTurbidity\":" + String(waterTurbidity); dataReturned += ", \n";
dataReturned += "\"waterTemp\":" + String(waterTemperature); dataReturned += ", \n";
dataReturned += "\"atmoTemp\":" + String(atmoTemperature); dataReturned += ", \n";
dataReturned += "\"atmoHum\":" + String(atmoHumidity); dataReturned += ", \n";
dataReturned += "\"deviceID\":" + String(deviceID); dataReturned += ", \n";
dataReturned += "\"deviceName\":\""; dataReturned += String("\""); dataReturned += String(deviceName); dataReturned += String("\""); dataReturned += ", \n";
dataReturned += "\"epoch\":" + String(currentEpoch); dataReturned += ", \n";
dataReturned += "\"mode\":" + String(mode); dataReturned += " \n";
dataReturned += "}";
Serial.println(" OK - Data is below");
Serial.println("");
Serial.println(dataReturned);
Serial.println("");
Serial.println(" Success - String is Ready");
colourLED(50);
return dataReturned;
}
processData(): 변동 센서 데이터 평균 내고 시리얼 모니터에 전체 데이터 출력.
makeLine(): 모든 데이터를 Soracom 전송용 JSON 문자열로 컴파일.
데이터 전송
void parseData(String dataToSend) {
Serial.println(" OK - Setting Up Connection");
if(client.connect(url, 80)) {
Serial.println(" OK - Connection Established, Parsing Data");
client.println("POST / HTTP/1.1");
client.println("Host: harvest.soracom.io");
client.println("User-Agent: Arduino/1.0");
client.println("Connection: close");
client.print("Content-Length: ");
client.println(dataToSend.length());
client.println("");
client.println(dataToSend);
Serial.println(" OK - Data Parsed");
}
Serial.println(" OK - Getting Responce");
Serial.println("");
while(1) {
if(client.available()) {
char c = client.read();
Serial.print(c);
}
if(!client.connected()) {
break;
}
}
Serial.println(" Success - Data is Parsed");
}
서버와 연결 수립 후 자격 증명 준비, 데이터 전송, 응답을 시리얼 모니터에 출력. 이후 트리거될 때까지 슬립 모드 진입 후 반복.
4단계: 변수 설정
사용 전 반드시 설정해야 할 변수들:
mode: 1=버튼 작동 모드(일회성), 2=주기적 자동 측정 모드
deviceID, deviceName: 플릿 운용 시 장치 식별용 고유 ID/이름
sleepTime: 측정 간 슬립 시간. 코드상 5초(테스트용), 실전엔 15분 등 적절히 변경
proDebug: 디버깅용. false 기본값. true면 시리얼 모니터 켜져 있어야 작동
5단계: 코드 업로드
백엔드 설정 전 데이터 전송 테스트. MKR GSM을 컴퓨터에 연결해 코드 업로드, 모드 1로 설정. 모든 센서 물에 담그고 버튼 눌러 데이터 수집/전송. 몇 번 반복해 Soracom Air에 데이터 쌓이게 하기.
6단계: Soracom 설정
2개 섹션: 계정 생성/SIM 등록, Soracom Harvest로 데이터 수집 설정. 기존 계정 있으면 첫 섹션 생략.
섹션 1: 계정 생성
섹션 2: 그룹 및 Harvest
7단계: Lagoon 설정
마지막으로 Soracom Lagoon으로 데이터 시각화 및 이메일 알림 설정. 아래 단계 따라 하기.
보정 문제
탁도·pH 센서는 정밀 사용 위해 보정 필요. 코드 실행 시 탁도 105%나 pH 3 등 비정상 값 나오면 보정하세요.
- pH 센서: 모듈의 포텐셔미터로 보정. pH 7 정수에 프로브 담그고 5분 대기. 센서 pH 값을 시리얼 모니터에 출력하는 코드 작성해 포텐셔미터 돌려 pH 7 맞추기.
- 탁도 센서: 정밀도 낮아 순수 물 대비 상대 백분율로 측정. 읽기 값이 100% 초과하면 순수 물에 센서 담그고 아날로그 핀 전압을 시리얼 모니터에 출력하는 코드로 전압 확인.
sensors.h의 calibration 변수 값을 이 전압으로 변경.
사용 라이브러리
- ArduinoLowPower (c) 2016 Arduino LLC LGPL
- Adafruit_Neopixel (c) Phil Burges LGPL
- MKRGSM (c) 2016 Arduino AG LGPL
- Wire (c) 2006 Nicholas Zambetti LGPL
- OneWire (c) 2007 Jim Studt GPL
- DallasTemperature GPL
- RTCZero (c) 2015 Arduino LLC LGPL
마무리
휴대용이면서 모드 1/2 모두 지원하는 인클로저 완성. 단계별 사진:
모드 설정 확인 후 현장 투입. 근처 강/호수 수질 확인하고 대시보드 위젯들 활용해보세요.
배경
오늘날 데이터는 새로운 화폐. 쉽고 효율적으로 수집하는 게 더 나은 환경의 열쇠. 강·호수 오염도를 집단 측정해 물이 더러워지고 있음을 알리고 행동 촉구.
Soracom 콘테스트 아이디어 구상 중 환경에 도움 되는 것 만들고 싶었고, 전 세계 사람들이 공동 대시보드에서 강·호수 상태를 시각화하며 협업하는 아이디어에 영감받아 이 프로젝트 시작.
당신은 수질 오염 막기 위해 뭘 할 건가요? 행동은 오늘 해야 합니다. 내일은 너무 늦어요.