Saturday, December 15, 2018

미세먼지 측정 센서를 만들었으나 보여줄 사람이 없다.

제가 비염과 아토피가 좀 있어 공기질에 민감합니다.

그래서 집에 남는 재료로 미세먼지 측정기를 만들어 보았습니다.

다들 이정도 재료는 집에 있잖아요.

(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 기준으로

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
Coarse Particulate Matter (PM10)
20 μg/m3 annual mean
50 μg/m3 24-hour mean
이정도는 나와야 정상입니다.
연간 PM2.5 10 μg/mPM10 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
이 사이트 가시면 그대로 따라할 수 있습니다.





Friday, June 1, 2018

소똥을 소개합니다




비록

키도 작고

수동 이고

느리지만

제가 아주 좋아하는 작품입니다.

이제 여러 가지 해볼 수 있게 

바퀴도 바꿔달고

모자도 씌우고

팔도 달고

볼때마다 놀래서 설레게 만들어 드릴겁니다!



 LTE 통신을 이용한 rover/로봇/UGV 입니다. 아직 이게 뭔지 저도 모르겠습니다. 캠을 이용하여 원격으로 화면을 보면서 제어할 수 있습니다. 서보모터 2개로 상하좌우로 캠을 조정할 수 있습니다. 캠에는 녹화 기능이 있습니다. 전화기를 내장하여 파일럿과 대화 할 수 있습니다. 9v 모터 2개로 기동합니다. 대략 0.6km/h 정도 속도로 2~3시간 정도는 움직일 수 있습니다. 앞에 LED 를 켜고 끌수 있습니다. 비상시를 대비하여 경보음을 울릴 수 있습니다.... 무게는 약5kg 이며 가로세로 약40CM 정도의 크기입니다. 내부에 라즈베리 파이, 인텔 컴퓨트 스틱, 모뎀, 스마트폰 등이 내장되어 있습니다. 재질은 하판은 알루미늄이며 상판은 종이에 테이프로 강화했습니다. 종이박스이지만 가랑비 정도는 막을 수 있습니다. 통풍을 위해 아래가 뻥 뚫려 있습니다. 철사는 손잡이 입니다. 


메일 lenda.industry@gmail.com

로봇으로 슈퍼가서 콜라사기


세계 최초 라즈베리 파이와 LTE 통신망을 이용한 로봇을 이용하여 슈퍼에 가서 콜라를 삼.

역사적 순간을 함께 하세요~!

개인정보 보호를 위해 모든 사람은 모자이크 처리 하였으며 불가피한 경우 편집 하였습니다.