완벽하게 작동하는 Arduino 기상 관측소를 만든 방법
게시 됨: 2022-03-11업데이트: 이 기사가 게시된 후에도 Arduino 기상 관측소에 대한 작업이 계속되어 OWS(Open Weather Station)가 출시되었습니다. 코드 및 새 자습서와 함께 추가 업데이트, 리소스를 확인하십시오.
이게 다 뭐야?
카이트서핑은 세계에서 가장 중독성이 강한 스포츠 중 하나입니다. 필요한 것은 카이트보드, 수역 및 몇 가지 액세서리뿐입니다. 자연과 교감하고 마음을 비우고 운동을 할 수 있는 좋은 방법입니다. 게다가, 당신은 그것으로 정말 미쳐버릴 수 있습니다.
그래서 문제가 무엇입니까?
아, 한 가지 필수 요건인 바람을 잊었습니다. 바로 여기에서 문제가 발생합니다. 가장 좋아하는 카이트서핑 장소 바로 옆에 살지 않는 한 바람이 불지 않을지 알 수 없습니다.
저는 카이트서핑을 하는 호수에서 약 130km(~80마일) 떨어진 아르헨티나 코르도바에 살고 있습니다. 차로 2시간 정도 걸리는 거리인데 감당할 수 있습니다. 하지만 일기예보가 정확하지 않다는 사실을 감당할 수 없습니다. 그리고 내가 사는 곳에서는 좋은 바람 조건이 단 두 시간 동안 지속됩니다. 마지막으로 하고 싶은 일은 월요일 일정을 정리하여 카이트서핑을 하고 2시간의 운전 후에 바람 없는 호수에서 신을 저주하는 자신을 발견하는 것입니다.
내가 가장 좋아하는 카이트서핑 장소의 바람 상태를 실시간으로 알아야 했습니다. 그래서 나는 나만의 기상 관측소를 만들기로 결정했다.
적대적인 환경에서 실시간으로 날씨 측정
목표는 실시간 날씨 데이터를 집의 브라우저에 전달하는 것이었습니다.
세부 사항에 들어가기 전에 잠시 시간을 내어 다음과 같은 프로젝트와 관련된 주요 질문과 주의 사항을 살펴보겠습니다.
- 도둑에게 귀중하지도 매력적이지도 않은 기상 관측소를 어떻게 만들 수 있습니까?
- 하드웨어 비용과 개발 시간을 최소화하려면 어떻게 해야 합니까?
- 날씨 데이터를 실시간으로 측정 및 액세스하여 유용하게 표시하려면 어떻게 해야 합니까?
- 필수 측정: 바람과 돌풍, 풍향, 비, 기압, 온도, 습도
- 스테이션을 인터넷에 연결
- 지역 날씨 데이터 저장 및 검색
- 기상 관측소와 서버 간 통신
- 유지 관리를 (거의) 0으로 줄이려면 어떻게 해야 합니까?
- 소프트웨어 중단 관리
- 연결 손실 관리
- 에너지 공급 손실 관리
내 작은 친구에게 인사해!
장갑이 스테이션을 더 친근하게 보이게 하기 위해 존재한다고 생각할 수도 있습니다. 그러나 실제로 기압 센서를 테스트하는 데 사용됩니다(장갑 압력이 팽창된 장갑 내부에서 증가함). 오른쪽에는 인근 타워 꼭대기에 위치한 최종 위치의 역이 보입니다.
나는 또한 카이트서핑 커뮤니티를 돕기 위해 스테이션 측정의 실시간 플롯을 포함하는 카이트서핑에 관한 웹사이트를 디자인하고 프로그래밍했습니다. 마지막으로 Facebook에서 카이트서핑 그룹을 만들었습니다.
굉장해! 그래서 어떻게 했어?
글쎄, 나는 차례로 각 요점을 다룰 것입니다 :
"도둑이 보기에 가치도 없고 매력도 없는 기상 관측소를 어떻게 만들 수 있습니까?"
이것은 중요한 요소였으며 여러 면에서 나머지 설계 프로세스를 주도했습니다. 2000달러 미만의 사전 제작된 스테이션은 대부분 컴퓨터에 USB 연결이 필요했습니다. 도둑이 스테이션 옆에 PC가 있다는 것을 알아차린다면 컴퓨터와 스테이션을 교체하는 데 드는 비용이 내 개인 예산을 초과할 것이기 때문에 모든 것이 끝납니다. 따라서 스테이션을 처음부터 저렴한 비용으로 구현하기 위해 여러 하드웨어 플랫폼을 테스트하기로 결정했습니다.
"어떻게 하면 하드웨어 비용과 개발 시간을 최소화할 수 있습니까?"
나 혼자 이 사이드 프로젝트의 비용을 지원하고 여가 시간에 모든 작업을 수행하고 있었기 때문에 당연히 이것이 큰 관심사였습니다. 나는 인기 있는 PIC32 와 일부 사전 조립된 마이크로칩 이더넷 모듈로 시작했지만, 비용은 예상만큼 낮지 않았고 하드웨어 조립 및 확장과 관련된 오버헤드가 너무 많았습니다. 그런 다음 C 언어를 사용하여 전자 제품 프로토타이핑을 위한 오픈 소스 하드웨어 및 소프트웨어인 Arduino를 살펴보기 시작했습니다. 이것이 바로 제가 원했던 것이었고 DealeXtreme에서 모듈을 구입할 수 있었습니다. 나는 단 15달러의 지출과 이틀의 시간으로 놀기 시작할 수 있었습니다.
물론 Arduino에도 한계가 있습니다. 컴파일된 소프트웨어의 경우 RAM이 2KBytes이고 컴파일된 소프트웨어의 경우 32Kbytes 뿐이므로 멋진 문자열이나 쓸모없는 변수를 위한 공간을 많이 남기지 않습니다.
"날씨 데이터를 실시간으로 측정 및 액세스하여 유용하게 표시하려면 어떻게 해야 하나요?"
현재 내 스테이션은 풍속, 돌풍, 풍향, 온도, 습도, 비 및 기압을 측정할 수 있습니다. 온도, 습도 및 압력은 두 개의 라이브러리에서 처리되어 삶이 훨씬 쉬워졌습니다.
풍속과 비를 측정하는 것은 약간 지저분했습니다. 스위치(리드 스위치)를 열고 닫음으로써 작동하는 센서. 따라서 입력을 트리거하는 즉시 센서를 포착하기 위해 하드웨어 인터럽트를 구현해야 했습니다. 즉, 몇 가지 메서드를 호출해야 했습니다.
attachInterrupt(RAINGAUGE_PIN, countRainCycles, FALLING);
이 인터럽트는 스위치가 회로를 닫거나 열어 생성되는 하강 에지가 발생하는 즉시 정상적인 코드 실행을 중단하고 countAnemometerCycles 또는 countRainCycles 함수를 호출합니다. 스위치의 각 트리거에서 몇 가지 변수가 증가합니다. (나중에 이러한 변수에 가중치를 주어 단위 변환을 고려합니다.)
void countRainCycles() { rainCyclesCounter++; // This is easy! And it actually works. }
그러나 그렇게 빠르지는 않습니다! 이 프로세스는 모든 하드웨어 스위치에 고유한 스위치 바운싱 효과의 결과로 수백 개의 잘못된 트리거를 생성합니다. 다행히도 이 문제에 대한 하드웨어 및 소프트웨어 솔루션이 모두 있습니다.
바운싱 효과에 대해
바운싱 효과는 스위치가 회로의 나머지 부분과 접촉을 설정하는 '접점'을 물리적으로 열거나 닫음으로써 발생합니다. 접점이 분리(스위치 열기) 또는 결합(스위치 닫기) 시작하면 몇 밀리초 동안 회로를 켜고 끄는 회로의 기계적 탄성뿐만 아니라 약간의 작은 전기 아크가 생성될 수 있습니다. 전등 스위치를 뒤집으면 이 효과가 뚜렷하지 않습니다. 그러나 신호의 하강 에지에 인터럽트를 연결하면 이 바운싱 효과로 인해 수많은 인터럽트가 발생합니다. 여기 더.
하드웨어 디바운스 회로와 유사한 버전의 소프트웨어를 모두 구현했습니다. 그러나 소프트웨어 디바운스를 정확히 어떻게 구현합니까? 쉬운! 첫 번째 예상 트리거가 발생한 후 새로운 인터럽트 수신을 시작하기 전에 바운스가 안정될 때까지 충분한 시간을 "기다립니다". 이것은 C:
void countRainCycles() { if (nextTimeRainIterrupt == 0 || nextTimeRainIterrupt < millis()) { rainCyclesCounter++; // The interrupts counter nextTimeRainIterrupt = millis() + 100; // Wait 100msecs before next trigger } }
millis() 함수는 Arduino가 켜진 이후 현재 실행 시간을 밀리초 단위로 반환합니다. 컴파일러가 실행을 최적화하지 않도록 지시하여 하드웨어 인터럽트 동안 부정확한 값을 피하도록 지시하려면 이러한 변수를 휘발성으로 정의해야 합니다.

여하튼 축적된 데이터를 저장하고 주기적으로 이 측정값을 MySQL 데이터베이스로 보낼 스테이션이 필요했습니다. 그래서 SD 슬롯이 있는 이더넷 모듈을 추가하여 값을 기록하고 사용자(서버)가 스테이션에 연결할 때마다 값을 검색합니다. 집에서 ADSL 연결을 사용하여 테스트하는 동안 이것은 놀라울 정도로 잘 작동했습니다. 그러나 3G 인터넷(3G 모뎀 사용)으로 "현장에서" 테스트했을 때 스테이션이 검색을 시도하면 스테이션이 무작위로 자체적으로 재설정되기 때문에 거의 머리카락을 잃을 뻔했습니다. 측정! 상당한 테스트를 거친 후 마침내 인터넷을 통해 연결된 클라이언트에 데이터 "제공"을 설명하는 예제가 연결이 너무 나빠서 패킷 전송 도중 클라이언트에 대한 연결이 손실될 수 있다는 점을 고려하지 않고 출력 버퍼가 오버플로됩니다. 그러나 연결이 끊어지면 버퍼 오버플로가 발생하는 이유는 무엇입니까? 음, 전송 세션이 시작되고 스테이션이 출력 버퍼를 데이터로 채우기 시작한다고 가정해 보겠습니다. 이상적으로는 클라이언트가 채워지는 것보다 더 빨리 이 버퍼를 사용합니다. 그러나 3G 모뎀으로 연결할 때는 그렇지 않았습니다! 클라이언트에 대한 연결이 너무 좋지 않아 버퍼가 소모되는 것보다 더 빨리 채워져 버퍼 오버플로와 스테이션의 갑작스러운 재부팅이 모두 발생했습니다.
이 문제를 해결하기 위해 Arduino와 함께 제공되는 이더넷 라이브러리에 다음과 같은 기능을 추가해야 했습니다.
int EthernetClient::free() { if (_sock != MAX_SOCK_NUM) return W5100.getTXFreeSize(_sock); return 0; }
그런 다음 더 많은 데이터로 채우기 전에 클라이언트에 버퍼에 공간이 있는지 확인할 수 있었습니다.
while (file.available() > 0) { if (client.free() > 0) { // This was key to solving the issue c = file.read(); client.print((char)c); } else { // No free buffer? Ok, I'll wait a couple of millis... delay(50); } } file.close();
그건 그렇고, Arduino 프로그래밍에 관심이 있다면 여기 훌륭한 가이드가 있습니다.
또 다른 흥미로운 작업은 LIFO 로그의 구현이었습니다. 이것이 왜 필요했습니까? 음, 일반적으로 측정값을 주어진 파일에 저장할 때 접근 방식은 간단합니다. 파일을 열고 끝에 새 샘플을 추가하고 파일을 닫습니다. 하지만 시간순으로 정렬된 최신 1000개의 측정값을 가져오고 싶다고 가정해 보겠습니다. 이러한 측정값은 파일 끝에 있습니다. 따라서 파일을 열고 커서를 끝으로 이동하고 최신 측정값을 출력한 다음 파일 커서를 이전 측정값으로 되돌려 놓고 샘플 구분 기호를 찾아 시작 및 중지 위치를 감지하여 출력해야 합니다. 아두이노는 이 프로세스를 빠르게 실행할 수 있는 RAM이나 프로세서 성능이 부족하여 다른 접근 방식이 필요했습니다. 대신 파일을 역순으로 서버에 출력한 다음 서버 측에서 문자열 리터럴을 되돌리기로 결정했습니다.
unsigned long filePosition = file.size(); file.seek(filePosition); while (filePosition >= 0) { if (client.free() > 0){ file.seek(filePosition); c = file.peek(); if (c != -1) { client.print((char)c); } if (filePosition <= 0) { break; } filePosition--; } }
PHP 개발자로서의 경험이 있으면 올바른 순서로 문자가 포함된 최신 샘플을 쉽게 얻을 수 있습니다.
// $output has the reversed string measures, each sample is delimited by ; $rows = split(";", trim($output)); array_walk_recursive($rows, 'reverseString'); if (strlen($rows[0]) == 0) { array_shift($rows); // Remove the first line if empty } function reverseString(&$row, $key) { $row = trim(strrev($row)); } // $rows is now the array of the latest samples :)
그런 다음 서버 측에서 cron 프로세스를 설정하여 2분마다 최신 측정값을 가져오고 데이터를 MySQL 엔진에 삽입합니다. 데이터를 표시하기 위해 www.kitesurfcordoba.com.ar를 만들고 jQuery를 사용하여 그래프를 자동 업데이트했습니다(훌륭한 오픈 소스 라이브러리인 pChart v2.0을 사용하여 자체적으로 생성됨).
소프트웨어 및 하드웨어 엔지니어링과 관련하여 작업을 수행하는 데 필요한 다른 트릭이 많이 있었지만 충분히 오래 끌었으므로 유지 관리 최소화에 대해 이야기하겠습니다.
"유지 보수를 (거의) 0으로 줄이려면 어떻게 해야 합니까?"
이것은 내가 역에 도달하는 것이 확실히 쉽지 않기 때문에 큰 걱정거리였습니다. 경미한 고장을 수리하기 위해 두 시간 거리를 운전할 의향이 있었다면 애초부터 역까지 데려다 줄 필요도 없었을 것입니다. 전에 이것을 언급하지 않았지만 우리가 겪은 모든 후에 역은 실제로 "그녀"이고 그녀의 이름은 도로시입니다).
그렇다면 여기서 어떤 종류의 오류에 대해 이야기하고 있습니까? 예를 들어, 소프트웨어가 중단될 수 있고, 네트워크 연결이 끊길 수 있으며, 에너지 공급이 실패할 수 있습니다.
기본적으로 스테이션은 가능한 한 많은 자가 복구를 수행해야 합니다. 그래서 소프트 워치독과 하드 워치독을 모두 사용했습니다. 익숙하지 않은 사람들에게 워치독은 시스템이 올바르게 작동하는지 확인하고 그렇지 않은 경우 다시 작동시키려고 시도하는 소프트웨어 또는 하드웨어입니다. Arduino에는 사용할 수 있는 내장 워치독이 있습니다. 8초 동안 기다리도록 설정했습니다. 호출이 해당 시간 제한보다 오래 걸리면 소프트웨어 워치독이 보드를 재설정합니다.
wdt_enable(WDTO_8S); // "wdt" stands for "watchdog timer"
나는 이 기능을 좋아한다. 그러나 보드가 재설정되고 이더넷 모듈이 재설정되지 않는 경우가 있습니다. 왜요? 글쎄요, 이것은 비교적 저렴한 프로토타이핑 보드이며, 고가의 고장 방지 장치가 아닙니다. 이 단점을 극복하기 위해 하드웨어 재설정 입력을 보드 자체의 디지털 출력에 교차 배선하여 Arduino를 해킹해야 했습니다. 재설정 루프를 방지하려면 몇 줄의 코드도 추가해야 합니다.
void setup() { digitalWrite(RESET_ARDUINO_PIN, HIGH); // Set it to HIGH immediately on boot pinMode(RESET_ARDUINO_PIN, OUTPUT); // We declare it an output ONLY AFTER it's HIGH digitalWrite(RESET_ARDUINO_PIN, HIGH); // Default to HIGH, set to LOW to HARD RESET ...
그 후, 나는 단순히 digitalWrite(RESET_ARDUINO_PIN, LOW)
를 호출하여 Arduino와 그 위에 있는 모든 모듈(이더넷 모듈 포함)에 대한 하드웨어 재설정을 실행할 수 있었고, 이는 몇 초 후에 Dorothy를 다시 살아나게 했습니다.
또한 보드는 에너지 손실 후 자동 재부팅됩니다. 인터넷 연결에 실패하면 SD 카드의 저장 기능을 활용합니다(데이터는 카드에 일주일 이상 저장할 수 있으며 서버는 누락된 샘플을 복구하기 위해 오래된 데이터를 가져올 수 있음). 이러한 모든 기능을 결합하면 모니터링을 위해 구축된 가혹한 조건에서 살아남을 수 있는 매우 강력한 기상 관측소가 제공됩니다. 총 비용은 약 300달러에 불과했습니다.
그리고 결국
스테이션은 2012년 12월부터 작동되었습니다. 현재까지 고장이 나지 않았습니다(또는 고장이 난 경우 스테이션이 카이트서핑 커뮤니티와 제가 눈치채지 못할 정도로 빠르게 복구됨). 약 500명의 카이트서퍼들이 그 장소로 여행하기 전에 기상 관측소를 정기적으로 확인합니다. 따라서 어려운 기술적 문제를 해결한 보상 외에도 많은 사람들에게 보다 즐거운 카이트서핑 경험을 제공할 기회가 있었습니다.
1 처음에는 Arduino Uno를 사용했습니다. 나중에 RAM과 플래시 메모리를 늘려야 하기 때문에 Arduino Mega로 전환했습니다.
관련 항목: ESP32 오디오 샘플링 작업