Raspberry Pi 備忘録 / Mbedもあるよ!

Raspberry Pi であれこれやった事の記録

Raspberry pi Zero で オフィス環境の可視化 その1

f:id:pongsuke:20161206135653j:plain

Raspberry Pi Zero にセンサーを付けて、各種データを集める。

登場するセンサー

  1. 温湿度センサー AM2302 > AM2320 に変更
  2. 気圧センサー SCP1000-D01
  3. CO2センサー K30

Raspberry pi Zero セットアップ

  1. Micor SDカードを SDformatter でフォーマット

  2. 公式サイトからダウンロードしてきた 2016-11-25-raspbian-jessie-lite を Win32DiskImager で焼く

  3. 挿してBoot

OS 設定

  1. 色々入れる

rpi-update, vim, build-essential, python-dev

alias vi='vim'

  1. international

locale, timezone, keybord [Generic 105-Key(Intel) PC] [japanese]

  1. 日本語フォントとターミナル

apt-get install -y fonts-takao ibus-mozc jfbterm

  1. SSH ON

systemctl enable ssh

  1. 新規ユーザーの作成とグループへの追加

piからsudo剥奪 sudo gpasswd -d pi sudo

# usermod -G pi,adm,dialout,cdrom,sudo,audio,video,plugdev,games,users,input,netdev,spi,i2c,gpio _USERNAME_
# visudo
  1. IP固定

/etc/dhcpcd.conf

interface eth0
static ip_address=192.168.100.88/24
static routers=192.168.100.1
static domain_name_servers=192.168.100.1

GPIOピン付

はんだ付けする。

40PIN メスを付けました。

AM2302

f:id:pongsuke:20161205161003p:plain

f:id:pongsuke:20161205161009p:plain

Special instructions of the single-bus communication:
1.Typical application circuit recommended in the short cable length of 30 meters on the 5.1K pull-up resistor pullup resistor according to the actual situation of lower than 30 m.
2.With 3.3V supply voltage, cable length shall not be greater than 100cm. Otherwise, the line voltage drop will lead to the sensor power supply, resulting in measurement error.
3.Read the sensor minimum time interval for the 2S; read interval is less than 2S, may cause the temperature and humidity are not allowed or communication is unsuccessful, etc..
4.Temperature and humidity values are each read out the results of the last measurement For real-time data that need continuous read twice, we recommend repeatedly to read sensors, and each read sensor interval is greater than 2 seconds to obtain accuratethe data.

5.1kΩ のプルアップ抵抗が必要ですが、手元にないので、4.7kΩ を入れました。

動作テスト

adafruit さんのスクリプトをありがたく使わせて頂く。

$ git clone https://github.com/adafruit/Adafruit_Python_DHT.git
$ git clone https://github.com/adafruit/Adafruit_Python_DHT.git
$ cd Adafruit_Python_DHT
$ sudo python setup.py install
$
$ cd examples/
$ sudo python ./AdafruitDHT.py 2302 4
Temp=24.9*  Humidity=46.0%

動いた。

コーディング

#!/usr/bin/python
import sys
import time
import Adafruit_DHT

sensor = Adafruit_DHT.AM2302
pin = 4

if __name__ == '__main__':
        try:
                while 1:
# Try to grab a sensor reading.  Use the read_retry method which will retry up
# to 15 times to get a sensor reading (waiting 2 seconds between each retry).
                        humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

# Un-comment the line below to convert the temperature to Fahrenheit.
                        # temperature = temperature * 9/5.0 + 32

# Note that sometimes you won't get a reading and
# the results will be null (because Linux can't
# guarantee the timing of calls to read the sensor).
# If this happens try again!

                        if humidity is not None and temperature is not None:
                                print( 'Temp={0:0.1f}dc'.format(temperature) )
                                print( 'Humidity={0:0.1f}%'.format(humidity) )
                                # print('Temp={0:0.1f}D  Humidity={1:0.1f}%'.format(temperature, humidity))
                        else:
                                print('Failed to get reading. Try again!')
                        time.sleep(3)

        except KeyboardInterrupt:
                sys.exit(0)

動かしてみて(追記)

数日運用して気がついたのですが、2つ問題が発生。

 1. 結構簡単に止まる
 2. 変に低い値が出る

電圧低下が原因とも思えないし、、、調査中です。

3.3v でも動作するので、
Vcc を GPIO にして、定期的に 通電/遮断 を繰り返せば、何とかごまかせますが、おかしいと思う。

AM2320

f:id:pongsuke:20161227114104p:plain

上記問題点をクリアできなかったので、AM2320 に乗り換えてみます。

配線

f:id:pongsuke:20161227114319p:plain

f:id:pongsuke:20161227114321p:plain

AM2302 とは異なり、I2C でつなぎます。

動作チェック

$ lsmod | grep i2c
i2c_bcm2708             5740  0 
i2c_dev                 6578  2
$ ls /dev/i2c-*  
/dev/i2c-1  
$ sudo i2cdetect -y 1  
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --   
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
70: -- -- -- -- -- -- -- --                         
$ sudo i2cdetect -y 1 
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  
00:          -- -- -- -- -- -- -- -- -- -- -- -- --  
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
50: -- -- -- -- -- -- -- -- -- -- -- -- 5c -- -- --  
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  
70: -- -- -- -- -- -- -- --  

コーディング

こちらを基に、コーディング。

温湿度センサAM2320をRaspberry Pi 3で使用する|wizqro.net

データシートにもには、まだ目を通して居ませんが、
i2cdetect -y 1 を繰り返すたびに、出てきたり出てこなかったりを繰り返していのは、先にreadする必要があるのかな?

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import time
import smbus

i2c = smbus.SMBus(1)
address = 0x5c
 
if __name__ == '__main__':
        try:
                while 1:
                        # センサsleep解除
                        try:
                            i2c.write_i2c_block_data(address,0x00,[])
                        except:
                            pass

                        # 読み取り命令
                        time.sleep(0.003)
                        i2c.write_i2c_block_data(address,0x03,[0x00,0x04])

                        # データ受取
                        time.sleep(0.015)
                        block = i2c.read_i2c_block_data(address,0,6)
                        humidity = float(block[2] << 8 | block[3])/10
                        temperature = float(block[4] << 8 | block[5])/10
 
                        print('Temp={0:0.1f}D  Humidity={1:0.1f}%'.format(temperature, humidity))
                        time.sleep(10)

# SQL にデータを投げます
# ...

        except KeyboardInterrupt:
                sys.exit(0)

実際には、SQLにデータを投げる処理も行っています。

注意点 Repeated Start Condition

Raspberry Pi で I2C の Repeated Start Condition を有効化 - Rabbit Note

/etc/modprobe.d/i2c.conf

options i2c_bcm2708 combined=1

データシートにも Repeated Start condition setup time 4.7 と表記がある。

SCP1000-D01

f:id:pongsuke:20161205170305p:plain f:id:pongsuke:20161205170232p:plain

SPI通信なので、まず有効にします。

raspi-config > advanced > spi

Wiringpi をインストール
$ git clone git://git.drogon.net/wiringPi
$ cd wiringPi/
$ ./build

以下のサイトを参考にさせていただいております。

ArduinoにSPI通信を行う機器を接続する(6) - フィジカル・コンピューティング

意伝子発信器

特に、コードは「意伝子発信器」さんのコードを改変して作りました。

レジスタ

f:id:pongsuke:20161206105032p:plain

f:id:pongsuke:20161206102916p:plain

サンプルを見ると、

レジスタ + 1/0 + 0 がひと固まりだとわかる。
B1 は常に 0
B2 は Read=0,Write=1
だ。

Address を、bite にする計算。

例) 0x06 RSTR "ASIC software reset" W Direct 8bits

の場合、

16進数 0x06
2進数 b110
Writeと 0 をお尻に足す b11010
16進数 0x1A

リセット命令 0x06,W,0 b11010, 0x1A
ステータス取得 0x07,R,0, b11100, 0x1C
オペレーションモード設定 0x03,R,0 0x0E

コーディング

とりあえず動くコード

#include<wiringPiSPI.h>
#include<wiringPi.h>
#include<stdio.h>
#include<stdlib.h>

#define SPI_CHANNEL 0
#define SPI_CLK 500000

int main(void){
        double temp, pressure = 0;
        unsigned char buf[3];
        int i,flag1=0, flag2=0;


        //wiringPiライブラリの初期化
        if (wiringPiSetup () == -1){
                return (1) ;
        }

        //SPIデバイスの初期化
        if((wiringPiSPISetup(SPI_CHANNEL, SPI_CLK))<0){
                printf("error SPI\n");
        }

        //ソフトリセットコード
        buf[0] = 0x1a;
        buf[1] = 0x01;
        wiringPiSPIDataRW(SPI_CHANNEL, buf, 2);
        printf("delay...\n");
        delay(60);

        //STATUS確認
        buf[0] = 0x1c;
        buf[1] = 0xff;
        wiringPiSPIDataRW(SPI_CHANNEL, buf, 2);
        printf("STATUS:%02x\n", buf[1]);
        i= buf[1]&0x01;
        if(i == 0){
                flag1 = 1;
                printf("STATUS OK!\n");
        }

        //DATARD8確認
        buf[0] = 0x7c;
        buf[1] = 0xff;
        wiringPiSPIDataRW(SPI_CHANNEL, buf, 2);
        printf("DATARD8:%02x\n", buf[1]);
        i= buf[1]&0x01;
        if(i == 1){
                flag2 = 1;
                printf("DATARD8 OK!\n");
        }


        if(flag1!=1 || flag2!=1){
                printf("Error...\n");
                exit(1);
        }


        //オペレーションレジスタに高精度測定モードを設定
        buf[0] = 0x0e;
        buf[1] = 0x0a;
        wiringPiSPIDataRW(SPI_CHANNEL, buf, 2);

        //メインループ
        while(1){
                //温度の測定
                buf[0] = 0x84;
                buf[1] = 0x00;
                buf[2] = 0x00;
                wiringPiSPIDataRW(SPI_CHANNEL, buf, 3);
                temp = (double)((buf[1] << (8+2)) + (buf[2] << 2)) / 20 / 4;

                //気圧の測定
                buf[0] = 0x7c;
                buf[1] = 0x00;
                wiringPiSPIDataRW(SPI_CHANNEL, buf, 2);
                pressure = buf[1] << 16;
                buf[0] = 0x80;
                buf[1] = 0x00;
                buf[2] = 0x01;
                wiringPiSPIDataRW(SPI_CHANNEL, buf, 3);
                pressure += (buf[1] << 8) + buf[2];
                pressure = pressure / 4 / 100;

                printf("temp %2.2f\n", temp);
                printf("press %4.4f\n", pressure);

                delay(1000);
        }
}