다들 이정도 재료는 집에 있잖아요.
(11월 달에 만들었는데 그동안 공기질이 좋아 안쓰고 있다가 오늘 써봅니다.)
(미세먼지센서는 샀습니다. 중국 사이트에서 2만원에 팝니다. SDS011 검색)
이 측정기는 크게 3부분으로 나뉩니다.
O LCD 창 - 센서에서 읽은 값을 표시합니다.
sunfounder사의 LCD1602 라는 제품입니다.
https://github.com/sunfounder/SunFounder_SensorKit_for_RPi2/blob/master/Python/LCD1602.py
O 미세먼지 센서 - 미세먼지를 측정합니다.
중국 알리에서 구매한 SDS011 센서입니다. 의외로 신뢰도가 좋아 유럽에서 대기질 측정 프로젝트에서 쓰이고 있다고 합니다.
https://openschoolsolutions.org/measure-particulate-matter-with-a-raspberry-pi/
O 버튼 - 누르면 기기가 꺼집니다.
그냥 일반적인 스위치 버튼입니다.
https://raspberrypihq.com/use-a-push-button-with-raspberry-pi-gpio/
추가로 배터리 - 이걸로 들고 다니면서 측정할 수 있습니다. 샤오미 보조배터리입니다. 휴대폰 충전(5V)되는 거면 뭐든지 다 됩니다.
그리고 이것들을 라즈베리 파이에 꽃아줍니다.
인터넷에 각부분에 대한 파이썬 코드를 찾을 수 있었습니다. 위 링크 가면 코드를 찾아볼 수 있습니다.
저것들은 모아서 파일 하나로 만듭니다.
#!/usr/bin/python
# coding=utf-8
# "DATASHEET": http://cl.ly/ekot
# https://gist.github.com/kadamski/92653913a53baf9dd1a8
from __future__ import print_function
import serial, struct, sys, time, json
#LCD ==============================================
import time
import smbus
#button ==========
import RPi.GPIO as GPIO # Import Raspberry Pi GPIO library
import os
def button_callback(channel):
print("button was pushed bye!")
os.system("sudo halt")
time.sleep(1)
GPIO.setwarnings(False) # Ignore warning for now
GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
GPIO.setup(10, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Set pin 10 to be an input$
GPIO.add_event_detect(10,GPIO.RISING,callback=button_callback) # Setup event on$
#button end =======
BUS = smbus.SMBus(1)
def write_word(addr, data):
global BLEN
temp = data
if BLEN == 1:
temp |= 0x08
else:
temp &= 0xF7
BUS.write_byte(addr ,temp)
def send_command(comm):
# Send bit7-4 firstly
buf = comm & 0xF0
buf |= 0x04 # RS = 0, RW = 0, EN = 1
write_word(LCD_ADDR ,buf)
time.sleep(0.002)
buf &= 0xFB # Make EN = 0
write_word(LCD_ADDR ,buf)
# Send bit3-0 secondly
buf = (comm & 0x0F) << 4
buf |= 0x04 # RS = 0, RW = 0, EN = 1
write_word(LCD_ADDR ,buf)
time.sleep(0.002)
buf &= 0xFB # Make EN = 0
write_word(LCD_ADDR ,buf)
def send_data(data):
# Send bit7-4 firstly
buf = data & 0xF0
buf |= 0x05 # RS = 1, RW = 0, EN = 1
write_word(LCD_ADDR ,buf)
time.sleep(0.002)
buf &= 0xFB # Make EN = 0
write_word(LCD_ADDR ,buf)
# Send bit3-0 secondly
buf = (data & 0x0F) << 4
buf |= 0x05 # RS = 1, RW = 0, EN = 1
write_word(LCD_ADDR ,buf)
time.sleep(0.002)
buf &= 0xFB # Make EN = 0
write_word(LCD_ADDR ,buf)
def init(addr, bl):
# global BUS
# BUS = smbus.SMBus(1)
global LCD_ADDR
global BLEN
LCD_ADDR = addr
BLEN = bl
try:
send_command(0x33) # Must initialize to 8-line mode at first
time.sleep(0.005)
send_command(0x32) # Then initialize to 4-line mode
time.sleep(0.005)
send_command(0x28) # 2 Lines & 5*7 dots
time.sleep(0.005)
send_command(0x0C) # Enable display without cursor
time.sleep(0.005)
send_command(0x01) # Clear Screen
BUS.write_byte(LCD_ADDR, 0x08)
except:
return False
else:
return True
def clear():
send_command(0x01) # Clear Screen
def openlight(): # Enable the backlight
BUS.write_byte(0x27,0x08)
BUS.close()
def write(x, y, str):
if x < 0:
x = 0
if x > 15:
x = 15
if y <0:
y = 0
if y > 1:
y = 1
# Move cursor
addr = 0x80 + 0x40 * y + x
send_command(addr)
for chr in str:
send_data(ord(chr))
# end of LCD ======================================================
DEBUG = 0
CMD_MODE = 2
CMD_QUERY_DATA = 4
CMD_DEVICE_ID = 5
CMD_SLEEP = 6
CMD_FIRMWARE = 7
CMD_WORKING_PERIOD = 8
MODE_ACTIVE = 0
MODE_QUERY = 1
ser = serial.Serial()
ser.port = "/dev/ttyUSB0"
ser.baudrate = 9600
ser.open()
ser.flushInput()
byte, data = 0, ""
def dump(d, prefix=''):
print(prefix + ' '.join(x.encode('hex') for x in d))
def construct_command(cmd, data=[]):
assert len(data) <= 12
data += [0,]*(12-len(data))
checksum = (sum(data)+cmd-2)%256
ret = "\xaa\xb4" + chr(cmd)
ret += ''.join(chr(x) for x in data)
ret += "\xff\xff" + chr(checksum) + "\xab"
if DEBUG:
dump(ret, '> ')
return ret
def process_data(d):
r = struct.unpack('<HHxxBB', d[2:])
pm25 = r[0]/10.0
pm10 = r[1]/10.0
checksum = sum(ord(v) for v in d[2:8])%256
return [pm25, pm10]
#print("PM 2.5: {} μg/m^3 PM 10: {} μg/m^3 CRC={}".format(pm25, pm10, "OK"$
def process_version(d):
r = struct.unpack('<BBBHBB', d[3:])
checksum = sum(ord(v) for v in d[2:8])%256
print("Y: {}, M: {}, D: {}, ID: {}, CRC={}".format(r[0], r[1], r[2], hex(r[$
def read_response():
byte = 0
while byte != "\xaa":
byte = ser.read(size=1)
d = ser.read(size=9)
if DEBUG:
dump(d, '< ')
return byte + d
def cmd_set_mode(mode=MODE_QUERY):
ser.write(construct_command(CMD_MODE, [0x1, mode]))
read_response()
def cmd_query_data():
ser.write(construct_command(CMD_QUERY_DATA))
d = read_response()
values = []
if d[1] == "\xc0":
values = process_data(d)
return values
def cmd_set_sleep(sleep=1):
mode = 0 if sleep else 1
ser.write(construct_command(CMD_SLEEP, [0x1, mode]))
read_response()
def cmd_set_working_period(period):
ser.write(construct_command(CMD_WORKING_PERIOD, [0x1, period]))
read_response()
def cmd_firmware_ver():
ser.write(construct_command(CMD_FIRMWARE))
d = read_response()
process_version(d)
def cmd_set_id(id):
id_h = (id>>8) % 256
id_l = id % 256
ser.write(construct_command(CMD_DEVICE_ID, [0]*10+[id_l, id_h]))
read_response()
PM25 = 'hello'
PM10 = 'world'
if __name__ == "__main__":
while True:
cmd_set_sleep(0)
cmd_set_mode(1);
for t in range(15):
values = cmd_query_data();
if values is not None:
# print("PM2.5: ", values[0], ", PM10: ", values[1])
PM25 = 'PM2.5:' + str(values[0])
PM10 = 'PM10:' + str(values[1])
init(0x27, 1)
write(0, 0, PM25)
write(0, 1, PM10)
time.sleep(2)
라즈베리파이가 기동할때 자동으로 프로그램이 실행되도록 /etc/rc.local 에 등록해줍니다.
fi와 exit 0 사이에 (sleep 10 && python /home/pi/AQIandLCD.py) & 이걸 추가해줍니다.
sleep 10 은 부팅이 끝날때 까지 기다리는 시간입니다. 부팅중에 프로그램이 실행되면 곤란하니까요.
&& 은 이작업이 끝나고 실행하라는 것입니다.
python /home/pi/AQIandLCD.py 파일 경로입니다.
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi
(sleep 10 && python /home/pi/AQIandLCD.py) &
exit 0
이런식으로 쌓아줄 겁니다.
휴대할 수 있도록 쌓아줍니다.
흔들흔들 거려 뭔가 위태롭네요.
종이 받침대를 만들어 중간에 끼워 넣겠습니다.
뭔가 균형이 안맞은게 2차대전 항공모함 같네요.
MP가 부족하니 포션하나 빨고 합니다.
뭔가 사제 폭탄 같이 생겼지만 성능은 무시못합니다.
밖에 나가 측정을 해봅니다.
베란다에서 본 우리 도시 풍경.jpg
스모그 무시무시 합니다.
집은 그런데로 깨끗합니다.
어플로 보니 밖은 상태가 좋지 않습니다.
WHO 기준으로
연간 PM2.5 10 μg/m3 PM10 20 μg/m3 이하가 나와야 합니다.
이것보다 안좋으면 평균수명 깎이는 중.
높으신 분들은 그걸 몰라요.
https://www.who.int/news-room/fact-sheets/detail/ambient-(outdoor)-air-quality-and-health
아침 8시에 스모그 보고 신나서 측정하러 나갑니다.
영 좋지 않은 듯
샤오미 배터리가 알루미늄이라 영하의 날씨에 손가락이 달라 붙음...
바람의 방향을 보니 우리 도시는 북풍임. 중국발은 아닌 듯
오후 1시에 먹을거 구하느라 다시 나옴 여전히 상태 않좋음.
그런데 상태 좋음.
마스크 벗음.

여전히 우리 도시 북풍
오후 2시 쯤에 베란다에서 찍음. 스모그 날라갔음.
기온 6도 정도 찍음. 복사로 인한 상승기류 로 인해 대기가 섞인거 같습니다.
추가로 공기청정기 소개합니다. 샤오미 공기 청정기 필터에 환풍기 얹어 놓았을 뿐입니다.
그런데로 쓸만합니다.
환풍기 팔천원. 필터 2.5만원.
한 2시간 정도 켜 놓으면 실내 농도는 어느정도 잡습니다.
http://www.itnamu.com/1228
이 사이트 가시면 그대로 따라할 수 있습니다.
밖에 나가 측정을 해봅니다.
베란다에서 본 우리 도시 풍경.jpg
스모그 무시무시 합니다.
집은 그런데로 깨끗합니다.
어플로 보니 밖은 상태가 좋지 않습니다.
WHO 기준으로
WHO Air Quality Guideline values
Particulate Matter (PM)
Guideline values
Fine Particulate Matter (PM2.5)
10 μg/m3 annual mean
25 μg/m3 24-hour mean
25 μg/m3 24-hour mean
Coarse Particulate Matter (PM10)
20 μg/m3 annual mean
50 μg/m3 24-hour mean
이정도는 나와야 정상입니다.50 μg/m3 24-hour mean
연간 PM2.5 10 μg/m3 PM10 20 μg/m3 이하가 나와야 합니다.
이것보다 안좋으면 평균수명 깎이는 중.
높으신 분들은 그걸 몰라요.
아침 8시에 스모그 보고 신나서 측정하러 나갑니다.
영 좋지 않은 듯
샤오미 배터리가 알루미늄이라 영하의 날씨에 손가락이 달라 붙음...
바람의 방향을 보니 우리 도시는 북풍임. 중국발은 아닌 듯
오후 1시에 먹을거 구하느라 다시 나옴 여전히 상태 않좋음.
그런데 상태 좋음.
마스크 벗음.
여전히 우리 도시 북풍
오후 2시 쯤에 베란다에서 찍음. 스모그 날라갔음.
기온 6도 정도 찍음. 복사로 인한 상승기류 로 인해 대기가 섞인거 같습니다.
추가로 공기청정기 소개합니다. 샤오미 공기 청정기 필터에 환풍기 얹어 놓았을 뿐입니다.
그런데로 쓸만합니다.
환풍기 팔천원. 필터 2.5만원.
한 2시간 정도 켜 놓으면 실내 농도는 어느정도 잡습니다.
http://www.itnamu.com/1228
이 사이트 가시면 그대로 따라할 수 있습니다.
No comments:
Post a Comment