Python으로 기상 센서 데이터 읽기
Source: Dev.to
날씨 관측소를 구축하는 것 외에도, 오래전부터 하고 싶었던 또 다른 일은 파이썬을 배우는 것이었습니다.
이 날씨 관측소 프로젝트를 통해 두 가지를 모두 만족시킬 수 있었습니다 :-)
이 프로젝트의 소스 코드를 Codeberg에서 확인하세요.
Pimoroni의 WeatherHAT 라이브러리 학습
모든 것을 설치한 후 처음으로 한 일은 Pimoroni의 WeatherHAT 라이브러리가 어떻게 작동하는지 배우는 것이었습니다:
- 센서에서 날씨 데이터를 읽기
- WeatherHAT 화면에 날씨 데이터를 표시하기
그래서 SSH 연결을 열고 실험을 시작했습니다!
라이브러리에서 배운 점
- 날씨 값을 얻기 전에 센서의
update메서드를 호출해야 합니다. - 첫 번째 측정값은 “쓰레기”와 같습니다; 신뢰할 수 있는 값을 얻기 위해 초기 업데이트와 짧은 대기 시간이 필요합니다.
- 온도 속성은 두 개가 있습니다:
device_temperature와temperature.temperature는device_temperature측정값에 기본 오프셋 7.5 °C를 적용하여 계산됩니다.
온도 보정
제가 가장 먼저 집중한 것은 온도를 정확히 보정하는 것이었습니다. 이를 위해 저는 며칠 동안 WeatherHAT 센서와 인근 기상 관측소에서 온도 데이터를 수집했으며, 매시간 측정값을 기록했습니다.
제가 발견한 점은 두 센서 간의 차이가 고정된 값이 아니라는 것이었습니다. WeatherHAT 측정값은 인근 관측소보다 항상 12 °C–16 °C 더 높았습니다.
고정 오프셋을 적용하는 대신 선형 회귀를 시도했습니다. 주어진 device_temperature 값에 따라 오프셋을 계산하는 클래스를 만들었습니다. 앞으로 오프셋을 계산하는 더 좋은 방법을 찾을 수 있을 것이며, 그래서 TemperatureOffsetInterface를 도입했습니다.
class LinearRegressionOffset(TemperatureOffsetInterface):
def __init__(self, config):
self.a = 0
self.b = 0
self.config = config
self.doLinearRegression()
def getOffset(self, x):
"""Return the offset for a given device temperature."""
offset = (self.b * x) + self.a
return offset
def doLinearRegression(self):
"""Calculate the linear regression coefficients a (intercept) and b (slope)."""
x = self.config['data']['x'].split(",")
y = self.config['data']['y'].split(",")
x = list(map(float, x))
y = list(map(float, y))
medianX = statistics.median(x)
medianY = statistics.median(y)
numSum = 0
denSum = 0
for index, xValue in enumerate(x):
numSum += (xValue - medianX) * (y[index] - medianY)
denSum += (xValue - medianX) ** 2
self.b = numSum / denSum
self.a = medianY - (self.b * medianX)
회귀 분석을 수행하기 위해 Python 라이브러리를 사용할 수도 있었지만, 기억하세요—이 프로젝트의 목표는 Python을 배우는 것이었습니다.
Source: …
하드웨어와의 분리
온도 보정 문제를 해결했으니 이제 읽기 시스템을 코딩할 차례였다.
이 프로세스는 주기적으로(예: 5분마다) 실행되어야 한다. 각 반복에서 시스템은:
- 센서에서 데이터를 읽는다.
- 온도 오프셋을 적용한다.
- 화면에 데이터를 표시한다.
- 데이터를 외부 서비스에 전송한다.
나는 라즈베리 파이에서 직접 코딩하는 것보다 내 개인 PC에서 개발하는 것이 더 편리하기 때문에 그렇게 한다. 이를 가능하게 하기 위해 모크(mock) 를 만들어 WeatherHAT 하드웨어에 대한 접근을 시뮬레이션했다.

# Example of using the abstract factory to obtain concrete implementations
sensor = factory.createSensor()
display = factory.createDisplay()
주 알고리즘을 완성한 뒤에는 하드웨어‑특정 부분만 구현하면 되었다. Pimoroni에서 제공하는 예제를 사용하면 센서에서 데이터를 가져오고 WeatherHAT 화면에 표시하는 것이 간단했다.
외부 서비스로 데이터 전송
어디서든 현재 온도를 확인하고, 나중에 통계 분석을 위해 측정값을 저장하고 싶었습니다.
다음 단계는 데이터를 외부 서비스로 전송하는 것이었습니다. 아직 내 자체 Weather Data Webapp이 준비되지 않아 Adafruit IO에 데이터를 보내는 것으로 시작했습니다. 현재 데이터를 표시하는 대시보드와 WeatherHAT 측정값을 인근 관측소 데이터와 비교할 수 있는 그래프를 만들었으며, 이는 선형 회귀의 품질을 확인하는 데 유용했습니다.
나중에 Adafruit 커넥터를 내 웹앱으로 데이터를 전송하는 구현으로 교체할 수 있도록 ConnectorInterface를 정의했습니다.
class ConnectorInterface:
def send(self, weather_data):
"""Send a WeatherData instance to the configured endpoint."""
pass
알고리즘의 주요 흐름을 작업하는 동안 잘못된 데이터를 전송하지 않도록 MockConnector도 만들었습니다.
추상 팩토리 구현
모든 것이 잘 동작했지만, 구현체(모크 vs. 실제) 간 전환이 복잡해졌습니다. 컴포넌트 선택을 단순화하기 위해 추상 팩토리 패턴을 적용했습니다. 하나의 설정만으로 이제 적절한 컴포넌트 집합(Sensor, Display, Connector)을 로드할 수 있습니다.
class SystemFactoryInterface:
def createDisplay(self) -> DisplayInterface:
pass
def createConnector(self, config) -> ConnectorInterface:
pass
def createSensor(self) -> SensorInterface:
pass
이제 나머지 코드는 개발 머신에서 모크를 사용하든 라즈베리 Pi에서 실제 하드웨어를 사용하든 전혀 신경 쓸 필요가 없습니다.
sensor = factory.createSensor()
display = factory.createDisplay()
connector = factory.createConnector(config)
결론
이 부분을 정말 즐겼습니다. 파이썬을 배우고, 센서에서 데이터를 읽고, 온도를 보정하고, 몇 가지 디자인 패턴을 적용했습니다… 프로그래밍을 처음 배우기 시작했을 때의 느낌이 다시 떠올랐습니다.
아마도 제가 평소에 개발하던 프로젝트와 정말 다르기 때문일 수도 있고, 여러 가지를 섞어놓았기 때문일 수도 있으며, 혹은 둘 다일 수도 있습니다 :-)
다음 글에서는 Weather Data Webapp의 구현을 설명하려고 합니다. 이번에는 제가 더 익숙하게 다루는 언어와 프레임워크인 PHP와 Symfony를 사용할 것입니다.
예시 인터페이스 정의 (Python)
class ConnectorInterface:
def connect(self) -> bool:
...
def disconnect(self) -> bool:
...
class SensorInterface:
def read(self) -> float:
...
class Config:
...
class MyFactory:
def createConnector(self, config: Config) -> ConnectorInterface:
pass
def createSensor(self, config: Config) -> SensorInterface:
pass