2015年5月17日日曜日

arduinoとraspberry pi間のbluetooth接続



参考文献

Raspberry Pi関係



Arduino関係

参考文献というか,ソースそのもの.

Arduino

Bluetoothアダプタ

むか~し買った,planexのbluetoothアダプタ(古すぎて機種名など不明)

実験用本体

  • Arduino ADK (古いから3.0じゃないよ)
最終的には,実験などで使わないようなものを実用系システムに回したいので,ADKではなく,一番使われていないものと後で変更(予定)

最終予定本体

  • Arduino Mega 2560 (こちらも結構古い機体)

USBホストシールド


本当に使えるか否か,未だ未確認.


Raspberry pi

本体

piは自宅で遊び用にしていたが,pi2が出たためそちらも購入.piをセンサ用に流用しようと仕込み中.先日(2015年5月)に秋月の店頭ではmodel B+とPi2の両方が売っていた.

Bluetoothアダプタ

家のごく近所(徒歩3分ぐらい?)のところのヤマダ電機でバッファローのBLEアダプタを購入.箱とか捨てたので,こちらも機種名などは不明.今のところ問題なく動作している.

準備

Raspberry Pi

ソフトのインストール

使っているのはraspbianなんで,普通に「apt-get」する.GUI不要なのでインストールするのは最小限にする.


# apt-get install bluetooth bluez-utils


不具合修正


参考文献「http://d.hatena.ne.jp/yunoya/20140330/1396114048」と同じ問題が出たので以下の修正を実施.bluez-simple-agentの


capability = "KeyboardDisplay"



capability = "DisplayYesNo"

に変更.

Arduino

USBシールド用ライブラリをダウンロードして,添付されているBluetooth用のsppプロファイル向けのサンプルプログラムを入れれば,動作の確認まで可能.

USBシールドのライブラリ(https://github.com/felis/USB_Host_Shield_2.0)をarduinoのIDEに登録すると,サンプルプログラムが利用できる.

USB_Host_Shield_2.0/examples/Bluetooth/SPPが今回使うサンプルプログラム.

このプログラムでは接続確立後に,PCとarduinoからデータを送り合って,相手から届いたデータを画面に出すことができる.arduinoの場合,シリアルモニタ画面を開いて,入力窓からテキストを入れると,Raspberry Piのアプリからそのデータを拾うことができる.

接続

まずは,Bluetoothのデバイスの接続


# bluez-simple-agent hci0 <MACアドレス>
DisplayPasskey (/org/bluez/XXXX/hci0/dev_<MACアドレス>, YYYY)
Release
New device (/org/bluez/XXXX/hci0/dev_<MACアドレス>)
# 

次はBluetoothのシリアルプロファイルを使えるようにする


# rfcomm bind 1 <<MACアドレス>>
 

動作の確認

次のコマンドでarduinoから送られるデータが表示される.

# cat /dev/rfcomm1


Arduinoで使える温度・湿度センサについての情報整理



以前の記事では,温度センサにLM35DZ,湿度センサはDHT11を使いました.そこでわかったこと.DHT11の温度出力は常に2度かそれ以上高くて測定値として使うのは問題あるレベルだった.

そのため,前回の記事ではLM35DZの温度とDHT11の湿度から体感温度を計算してます.

ただし,この記事で書いたようなシステムでセンサを積んだエンドノードはあまり大きいと置き場所に困る.ちなみに,前回の試作の記事のうち暫定版の記事には本体の写真が入れてありますが,センサ関係の回路を収める関係上,箱はこちらの製品になってます.


この箱のサイズはL126.8×W89.2×H30.0mmなんでかなり大きいのはわかってもらえると思います.

今回は,センサを積んだエンドノードをばらまくことができるようにしたいため,部品点数を減らしたいというのが意図です.

そのため,LM35DZを使わず,DHT11の代わりにもう少し精度がマシなセンサ1台でなんとかしようということで,他のセンサの情報のまとめです.

とりあえず,いろいろ以下の参考文献を見た結果,価格面もあり,DHT-22 (温湿度センサ モジュール AM2302)を購入しました.まだ実装とかはしていないので,後ほど報告しまふ.

参考文献



精度が重要な場合





オペアンプとかを使って,精度を上げるやりかた.今回は部品点数を減らすため採用しない.

この記事で使っているセンサは「HIS-02」と「HS15P」


ちょっとお金持ちさま向け(SHT11)





精度が「温度±0.4°、相対湿度±3%」と非常に良い.ただし,秋月のSHT11の販売ページを見ると,現時点(2015年5月)で『4,300円』と非常に高いのでこちらもパス.お金持ちさまにはおすすめかも.


いろんなセンサの比較記事

5種類のセンサの比較記事になってます.




SHT11とDHT22の利用事例




SHT11とDHT22の利用方法を紹介してます.


DHT22の利用事例





2015年5月時点のセンサの秋月での価格



参考情報

秋月のwebからのコピペですが,両方のセンサの仕様を入れておきます.具体的に検討したい方は,秋月のwebにデータシートがありますんで,そちらを参考にしてください.

LM35DZの仕様


  • 測定温度範囲:0~100℃
  • 精度:±1℃
  • 温度係数:10.0mV/℃
  • 電源電圧:DC4V~20V
  • 低消費電流:60μA
  • 低出力インピーダンス:0.1Ω
  • パッケージ:TO-92


DHT11の仕様


◆主な仕様


  • 電源電圧:3.5V~5.5V
  • 消費電流:0.3mA(測定時)、60μA(スタンバイ時)
  • サンプリング間隔:2秒以上
  • 内部ADコンバータ:各16bit
  • サイズ:12×15.5×5.5mm(ピン含まず)


◆湿度センサ部


  • センサ:有機ポリマー
  • 精度:±5% RH(@25℃)
  • 繰り返し精度:±1% RH
  • レスポンス:6秒以内(1/e (63%)、@25℃、風速1m/s)


◆温度センサ部


  • センサ:NTCサーミスタ
  • 精度:±2℃(@25℃)
  • 繰り返し精度:±0.2℃
  • レスポンス:10秒以内(1/e (63%))


◆シリアル通信部


  • 形式:単線バス(双方向)、シリアル40bit構成
  • 出力データ:湿度8bit(分解能:1%RH)、温度8bit(分解能:1℃)
  • 出力:オープンドレイン
  • 通信距離:20m max(@5.1kΩプルアップ)




arduinoの温度・湿度センサとraspberry pi を Bluetoothでつなぐ時に役立つ情報源

今回は,前回の記事に書いたようなシステムを作るため,いろいろ調べたことのメモ.特に,参考文献のリストになってます.

Raspberry Piとbluetooth



参考文献


読んだ感想では3番目のリンクの記事が単純にやろうとしていることに近い.特に,raspberry piのbluetoothのプログラムの不具合修正まで書いてくれてあるのがありがたい.

4番目の記事はsppプロファイルで周辺機器とつなぐ際は参考になる.


Arduinoとbluetooth


参考文献



Arduino上のRTC


参考文献



上記のRTCの回路では,I2C通信のために抵抗をつけている例が多いですが,そのプルアップ抵抗が本当にいるかいらないかについては以下の記事が参考になるはず.


Arduinoの低電力状態

参考文献


無線を使った温度・湿度センサネットワーク

今日の記事は新しい試作の決意表明というか,イントロだけをカキコ.


以前の記事では,Ethernet付きのArduinoを使って,温度や湿度を取得できるようにした.部屋に1台だけ設置するなら,これでも十分なんだが,大きな部屋(会社の事務所等)でセンサをばら撒きたい場合,ネットワークの配線やIPアドレス大量消費の問題が出てくる.


そのため,下の図のようにarduinoが測定したデータをBLEやzigbeeで中継サーバ(今回はRaspberry piを予定)に集めて,有線イーサネットでどこかのサーバに投げ込む形式のシステムを作ろうとしてます.



2015年5月9日土曜日

Arduinoを利用した温度・湿度測定webサーバ(改版)


はじめに


以前の記事「Arduinoを利用した温度・湿度測定webサーバ(暫定版)」の「暫定」の度合いがひどかったので,週末などなどの空き時間を使ってソフトをバージョンアップ.


変更点

将来的に,24時間運転のサーバでデータを定期的に取得して,グラフ化したかったので,コンピュータから読み取って,解析が容易なフォーマットと,人間が読むフォーマットを分けたかった.

Arduinoに届いた,httpのリクエストの「GET なんとか HTTP/1.1」の『なんとか』の文字列を解析して分けられるようにした.下は,実際にアクセスした場合のスクリーンショット.

「http://ホスト名(もしくはIPアドレス)/」でアクセスした場合



「http://ホスト名(もしくはIPアドレス)/ゴミ文字列」でアクセスした場合




「http://ホスト名(もしくはIPアドレス)/ゴミ文字列」でアクセスした場合は,『:』区切りの1行データになるので,sedやawkでも簡単に切り出せる.


ソースコード

プログラムにHTMLを吐き出す機能があるせいで,ソースの中のHTMLのタグ類を,ソースを表示するためのGoogle Code Prettifyが正しく扱えないので一部表示がおかしくなってます.

このページのソースは参考用で,きちんと読みたい方はソースを「ここ」に置いておきますのでダウンロードしてください.

/*
  LM35DZを用いた温度測定(0℃~100℃)
  シリアルモニターに摂氏で表示させるのと,httpでのアクセスで答える
  参考URL
  httpサーバおよび,温度センサ関連
  http://openhardware.gridshield.net/home/arduino-snmp-temperature-sensor
  http://d.hatena.ne.jp/a10i/20110607/1307453448
  http://homepage3.nifty.com/sudamiyako/zk/AVR_ADC/AVR_ADC.html
  http://makining.com/okuzawats/dht11-temperature-humidity-20150218
  https://learn.adafruit.com/dht/connecting-to-a-dhtxx-sensor
  http://mpu.seesaa.net/category/22909809-2.html
  http://blog.livedoor.jp/hardyboy/tag/LM35DZ
  http://makining.com/okuzawats/dht11-temperature-humidity-20150218
  http://qiita.com/masato/items/99e5dac91d13650e90a2
  pingのライブラリ
  http://playground.arduino.cc/Code/ICMPPing
  DHTセンサのライブラリ
  https://github.com/adafruit/DHT-sensor-library
*/

/* 固定IP設定の時に,pingで設定を確認する機能を有効にする場合に定義する */
#define USE_PING

/* ネットワーク関係のインクルード */
#include 
//#include 
//#include 
#include 
//#include 
//#include 
//#include 
#ifdef USE_PING
#include 
#endif /* USE_PING */

/* DHTセンサ関係のインクルード */
#include "DHT.h"

/*
 * DHT11関係の定義
 */
#define DHTPIN 2     // DHTが接続されているデジタルのピン番号

// 使っているセンサの種類の定義
#define DHTTYPE DHT11   // DHT 11 
//#define DHTTYPE DHT22   // DHT 22  (AM2302)
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor

// Initialize DHT sensor for normal 16mhz Arduino
DHT dht(DHTPIN, DHTTYPE);
// NOTE: For working with a faster chip, like an Arduino Due or Teensy, you
// might need to increase the threshold for cycle counts considered a 1 or 0.
// You can do this by passing a 3rd parameter for this threshold.  It's a bit
// of fiddling to find the right value, but in general the faster the CPU the
// higher the value.  The default for a 16mhz AVR is a value of 6.  For an
// Arduino Due that runs at 84mhz a value of 30 works.
// Example to initialize DHT sensor for Arduino Due:
//DHT dht(DHTPIN, DHTTYPE, 30);

/*
 * LM35(温度センサ)関連の定数の定義
 */
float v=5.0;            // MEGA2560やEthernet R3は5.0V駆動
float tempC   = 0;      // 摂氏値( ℃ )
float offset = 0;       // センサの温度ズレを修正する変数

#define LOOPNUMBER 100
#define MEDIAN 50

/*
 * 配線関連の定数
 */
int TempSensorPin = 0;  // センサ用のアナログI/Fは0番を利用

/*
 * ネットワーク関係の定数
 */
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0x92, 0x1A }; //アドレスは手持ちのarduinoのものに変更すること
boolean useDhcp = false; // DHCPで運用する場合は true に変更
                   // DHCPでのアドレス取得失敗時の対策や,長時間経過後のアドレス再割当て等は対応していない
/* 以下は固定IP運用の場合の変数なので適宜変更して使用すること */
IPAddress ip(192,168,0,201);
IPAddress dnsServer(192,168,0,1);
IPAddress gatewayAddress(192,168,0,1);
IPAddress netMask(255,255,255,0);

#ifdef USE_PING
SOCKET pingSocket = 0;
char buffer [256];
#endif /* USE_PING */

/*
 * デバッグ関連
 */
//#define DEBUG // デバッグ目的で,各種情報をシリアルに出力する場合は有効化

/*
 * プログラム本体
 */
#ifdef USE_PING
ICMPPing ping(pingSocket, (uint16_t)random(0, 255));
#endif /* USE_PING */

EthernetServer server(80); // サーバオブジェクトの定義. 引数はポート番号

void setup() {
#ifdef DEBUG
  // シリアル通信速度
  Serial.begin(9600);
#endif
  // DHT11センサの初期化
  dht.begin();
  // MACアドレスとIPアドレスの設定
  // 参考URL http://arduino.cc/en/Reference/EthernetBegin
  if (useDhcp) {
#ifdef DEBUG
    Serial.println("configure Ethernet using DHCP");
#endif
    if (Ethernet.begin(mac)==0) {
#ifdef DEBUG
      Serial.println("Failed.");
#endif
    } else {
#ifdef DEBUG
      Serial.println("Success.");
#endif
    }
  } else {
#ifdef DEBUG
    Serial.println("Manual ip confuguration of ethernet");
#endif
    Ethernet.begin(mac, ip, dnsServer, gatewayAddress, netMask);
#ifdef DEBUG
    Serial.println("done.");
#endif
#ifdef USE_PING
    // ルータのアドレスにping
    // 参考URL http://playground.arduino.cc/Code/ICMPPing
    ICMPEchoReply echoReply = ping(gatewayAddress, 4);
#ifdef DEBUG
    if (echoReply.status == SUCCESS) {
      Serial.println("Ping to Gateway address : success");
    } else {
      Serial.println("Ping to Gateway address : fail");
    }
#endif
#endif /* USE_PING */
  }
#ifdef DEBUG
  // IPアドレスのシリアルへの出力
  Serial.println("IP address of Ethernet Interface");
  Serial.println(Ethernet.localIP());
  //
  float temperature = getLMTemperatureEX(TempSensorPin);
  float h = getDHTHumidity();
  float t = getDHTCTemperature();
  float f = getDHTFTemperature();
#endif
  // サーバの実行
  server.begin();
}

void loop() {
  /*
   * httpリクエスト解析用
   */
  String httpRequestString="";
  String httpBuffer="";
  // 接続してきたクライアントを示すオブジェクトを生成
  EthernetClient client = server.available();
  if (client) {
#ifdef DEBUG
    Serial.println("Connection established by a client");
#endif
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read(); //一文字読み取り
#ifdef DEBUG
        Serial.print(c);
#endif
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
#ifdef DEBUG
          Serial.println("");
          Serial.println("http request end.");
          Serial.print("http request = ");
          Serial.println(httpRequestString);
#endif
          boolean httpRequestClass = matchRequestString(httpRequestString);
          httpRequestString = "";
          // センサから値を取得して体感温度を計算
          // LM35温度読み取り  
          //float temperature = getLMTemperature(TempSensorPin);
          float temperatureEX = getLMTemperatureEX(TempSensorPin);
          // DHT11の温度湿度
          float h = getDHTHumidity();
          // 摂氏の温度取得
          float t = getDHTCTemperature();
          // 華氏の温度取得
          float f = getDHTFTemperature();
          // LM35の温度とDHT11の湿度から,体感温度計算
          float hi = dht.convertFtoC(dht.computeHeatIndex(dht.convertCtoF(temperatureEX), h));
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          //返答する文字コードの定義
          //client.println("Content-Type: text/html; charset=iso-2022-jp"); // JIS
          //client.println("Content-Type: text/html; charset=shift_jis"); // SJIS
          //client.println("Content-Type: text/html; charset=euc-jp"); // EUC
          client.println("Content-Type: text/html; charset=UTF-8"); // UTF-8
          //client.println("Content-Type: text/html");  // 定義なし
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("");
          client.println("");
          // httpRequestが人手によるものか否かでHTMLの内容を切り替え
          if (httpRequestClass) {
            // 人が入力した場合
            if (isnan(h) || isnan(t) || isnan(f)) {
#ifdef DEBUG
              Serial.println("Failed to read from DHT sensor!");
#endif
              client.println("LM35の測定値 : ");
              client.println("
");
              client.print("摂氏 ");
              client.print(temperatureEX);
              client.println(" 度");
              client.println("
");
              client.println("
");
              client.println("DHT11はエラーで測定できませんでした");
              client.println("
");
            } else {
              client.println("室内の温度・湿度環境");
              client.println("");
              client.println("

室内の温度・湿度環境

"); client.println(" "); client.println(" "); client.println(""); client.println(""); client.println(""); client.println(" "); client.println(""); client.println(""); client.println(""); client.println(" "); client.println(" "); client.println(""); client.println(""); client.println(""); client.println(""); client.println(" "); client.println(" "); client.print(""); client.print(""); client.print(""); client.print(""); client.print(""); client.println(" "); client.println("
arduinoの出力
LM35測定結果DHT11測定結果体感温度(摂氏)
温度(摂氏)温度(摂氏)温度(華氏)湿度(\%)
");client.print(temperatureEX);client.println("");client.print(t);client.println("");client.print(f);client.println("");client.print(h);client.println("");client.print(hi);client.println("
"); client.println(""); } } else { // 人でない場合 // LM35の測定結果を出力 client.print("LM35:C:"); client.print(temperatureEX); client.print(":"); // DHTの測定結果を出力 client.print("DHT11:"); // DHTの出力がエラーか否かで切り替え if (isnan(h) || isnan(t) || isnan(f)) { #ifdef DEBUG Serial.println("Failed to read from DHT sensor!"); #endif client.println("C:0:F:0:H:0:HI:C:0"); } else { client.print("C:"); client.print(t); client.print(":F:"); client.print(f); client.print(":H:"); client.print(h); client.print(":HI:C:"); client.println(hi); } client.println(" "); } client.println(""); break; } if (c == '\n') { // you're starting a new line currentLineIsBlank = true; if (httpBuffer.startsWith("GET ")) { // GET xxx HTTP/1.1 の行だけを処理対象にする. // 「xxx」の部分だけを取り出す. httpRequestString = httpBuffer; httpRequestString.replace("GET ",""); httpRequestString.replace("HTTP/1.1",""); httpRequestString.replace(" ",""); httpRequestString.replace("\t",""); } httpBuffer = ""; } else if (c != '\r') { // you've gotten a character on the current line currentLineIsBlank = false; httpBuffer = httpBuffer + c; } } } // give the web browser time to receive the data delay(1); // close the connection: client.stop(); #ifdef DEBUG Serial.println("client disconnected"); #endif } } // // DHT11の温度と湿度読み取り関数 float getDHTCTemperature(){ float t = dht.readTemperature(false); #ifdef DEBUG // 温度のシリアルへの出力 Serial.println("DHT11 celcious temperature is"); Serial.println(t); #endif return t; } float getDHTFTemperature(){ float t = dht.readTemperature(true); #ifdef DEBUG // 温度のシリアルへの出力 Serial.println("DHT11 Faren temperature is"); Serial.println(t); #endif return t; } float getDHTHumidity(){ float h = dht.readHumidity(); #ifdef DEBUG // 湿度のシリアルへの出力 Serial.println("DHT11 Humidity is"); Serial.println(h); #endif return h; } // // LM35の温度読み取り関数 // float getLMTemperatureEX(int pin){ int buff[LOOPNUMBER]; float tmp; #ifdef DEBUG Serial.print("Data = "); #endif for (int i=0 ; i < LOOPNUMBER ; i++){ buff[i] = analogRead( pin ); #ifdef DEBUG Serial.print(buff[i]); Serial.print(" , "); #endif } #ifdef DEBUG Serial.println(""); #endif sort(buff,LOOPNUMBER); #ifdef DEBUG Serial.print("SortData = "); for (int i=0 ; i < LOOPNUMBER ; i++){ Serial.print(buff[i]); Serial.print(" , "); } #endif tmp = ((v * buff[MEDIAN]) / 1023) * 100; tmp = tmp + offset; #ifdef DEBUG // 温度のシリアルへの出力 Serial.println("temperature is"); Serial.println(tmp); Serial.println("Pin number = "); Serial.println(pin); #endif return tmp; } void sort(int a[], int size) { for(int i=0; i<(size-1); i++) { for(int o=0; o<(size-(i+1)); o++) { if(a[o] > a[o+1]) { int t = a[o]; a[o] = a[o+1]; a[o+1] = t; } } } } /* * httpリクエストの解析関数 * * 意図: * 人がブラウザでアクセスした時だけtrueにしたい * http://ホスト名(もしくはIPアドレス/ でアクセスした時は人がブラウザを使ったと見なす. * http://ホスト名(もしくはIPアドレス/の後ろにゴミがついていた場合は,マシンが自動で読み出しに来たと見なす. * * 仕様 : * httpクリエスとの文字列がある文字列("/"か"/favicon.ico")に一致したらtrue,それ以外はfalse */ #define HTTP_REQUEST_STRING "/" boolean matchRequestString(String httpRequestString) { String request_url = HTTP_REQUEST_STRING; request_url = request_url + "favicon.ico"; int comp_result1 = httpRequestString.compareTo(HTTP_REQUEST_STRING); int comp_result2 = httpRequestString.compareTo(request_url); #ifdef DEBUG Serial.print("defined string = "); Serial.println(HTTP_REQUEST_STRING); Serial.print("other string = "); Serial.println(request_url); Serial.print("argument of match request string = "); Serial.println(httpRequestString); Serial.print("result of compare 1 = "); Serial.println(comp_result1); Serial.print("result of compare 2 = "); Serial.println(comp_result2); #endif if ((comp_result1 == 0) || (comp_result2 == 0)) { // どちらかの結果が1なら,定義したURL文字列と一致した return true; } else { // それ以外の場合は,一致していない return false; } }

2015年5月4日月曜日

ゴールデンウィーク期間中の秋葉原

Raspberry PiとArduino関係のお買い物のために,このGWの前半に2回ほど秋葉原に出かけました.時々出かけていた時も思っていたことでもある上に,テレビのニュースなどでも良く出ていますが,とにかく「中国語」が良く聞こえます.

電気街口から出て,高架横のSEGAのゲーセンのあたりは観光客がたくさん.中国語が良く聞こえる以外には,見た感じ東南アジア系と思える濃い顔の人も多数.時々白人がパラパラっていうところでしょうか.

SEGAのところから,千石までの間はメイド喫茶などが多数あるため,観光客とピラ配りのメイドさんをかき分けて行かないと通ることができません.

表通り側は余計に観光客が多いので,表通りから,ヒロセテクニカルの横にでるのも面倒くさい.

昨日は,駅に着いたのが早くて,千石の開店時間前になりそうだったので,秋葉原ワシントンホテル側の高架下の喫煙所に寄ってから,LAOX本店の前を通って行きましたが,店の前に人だかり.微妙に聞こえる(周りがうるさいので)言語はどうも中国語のよう.

昨日は千石電商だけで用事が済んだのですが,その前にRaspberry Pi 2 本体を買うために秋月にも寄った日は秋月の店内の人だかりにも閉口.

休みの日の秋月(特に開店直後から午後早い時間)ってなんであんなに混んでるのか.以前,夕方以降に寄った時はそれほどでもなかったので,秋月は開店を店の前で待つか,夕方以降に出かけるのをオススメします.

Arduinoを利用した温度・湿度測定webサーバ(暫定版)


以前の投稿「arduinoを使った温度測定webサーバ」とか「arduinoの気温測定で問題発生」でArduinoを使って,室温を図るwebサーバの作成について書いてきましたが,一冬過ぎて,そろそろオフィスの室温が上がり始めたこと,湿度に関する意見も部署内であることから,湿度センサを増設し,ケースに入れたバージョンを作成しました.


各種参考URL

まずは,参考URLの類.わからないことがあった場合,他の記事と比較したい場合のスタートポイントにしてください.

httpサーバおよび,温度センサ関連


  • http://openhardware.gridshield.net/home/arduino-snmp-temperature-sensor
  • http://d.hatena.ne.jp/a10i/20110607/1307453448
  • http://homepage3.nifty.com/sudamiyako/zk/AVR_ADC/AVR_ADC.html
  • http://makining.com/okuzawats/dht11-temperature-humidity-20150218
  • https://learn.adafruit.com/dht/connecting-to-a-dhtxx-sensor
  • http://mpu.seesaa.net/category/22909809-2.html
  • http://blog.livedoor.jp/hardyboy/tag/LM35DZ
  • http://makining.com/okuzawats/dht11-temperature-humidity-20150218
  • http://qiita.com/masato/items/99e5dac91d13650e90a2


pingのライブラリ


  • http://playground.arduino.cc/Code/ICMPPing

DHTセンサのライブラリ


  • https://github.com/adafruit/DHT-sensor-library

使った部品

リンクは製品販売元じゃなくて,私が買った店や有名代理店の場合もあります.
抵抗は,買った時の情報が無いので,ワット数は違っているかも.コンデンサは転がっていたやつを使ったので,同一容量の製品のwebページです.

ハードウェア

一応,Fritzingの図面も添付しますが,ユニバーサル基板の配線は,Fritzingの練習不足のため,ご勘弁ください.

回路図





現物写真


手元に適当なサイズのユニバーサル基板がなかったので,微妙に大きい.

  • 表側



  • 裏側




ソフトウェア


とりあえず,ソースもここに置いときます.

実装上の工夫

LM35DZの温度を読むと,非常にふらつく上に,時々とんでもない値が出てくるため以下の工夫を入れた.

  • 100回読む
  • ソートする
  • 統計的な中央値を選んでそれを結果とする

現在の問題

わかっている問題は以下の2点

  • 精度
    温度や湿度の校正はしていない(そんな設備がない)ので精度は不明.
  • DHT11とLM35の出力の違い
    DHT11の測定値とLM35の測定値が約2度程度異なり,DHTが常に高い温度を返す.


ソースコード


/*
  LM35DZを用いた温度測定(0℃~100℃)
  シリアルモニターに摂氏で表示させるのと,httpでのアクセスで答える
  参考URL
  httpサーバおよび,温度センサ関連
  http://openhardware.gridshield.net/home/arduino-snmp-temperature-sensor
  http://d.hatena.ne.jp/a10i/20110607/1307453448
  http://homepage3.nifty.com/sudamiyako/zk/AVR_ADC/AVR_ADC.html
  http://makining.com/okuzawats/dht11-temperature-humidity-20150218
  https://learn.adafruit.com/dht/connecting-to-a-dhtxx-sensor
  http://mpu.seesaa.net/category/22909809-2.html
  http://blog.livedoor.jp/hardyboy/tag/LM35DZ
  http://makining.com/okuzawats/dht11-temperature-humidity-20150218
  http://qiita.com/masato/items/99e5dac91d13650e90a2
  pingのライブラリ
  http://playground.arduino.cc/Code/ICMPPing
  DHTセンサのライブラリ
  https://github.com/adafruit/DHT-sensor-library
*/


/* 固定IP設定の時に,pingで設定を確認する機能を有効にする場合に定義する */
#define USE_PING

/* ネットワーク関係のインクルード */
#include 
//#include 
//#include 
#include 
//#include 
//#include 
//#include 
#ifdef USE_PING
#include 
#endif /* USE_PING */

/* DHTセンサ関係のインクルード */
#include "DHT.h"

/*
 * DHT11関係の定義
 */
#define DHTPIN 2     // what pin we're connected to

// Uncomment whatever type you're using!
#define DHTTYPE DHT11   // DHT 11 
//#define DHTTYPE DHT22   // DHT 22  (AM2302)
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor

// Initialize DHT sensor for normal 16mhz Arduino
DHT dht(DHTPIN, DHTTYPE);
// NOTE: For working with a faster chip, like an Arduino Due or Teensy, you
// might need to increase the threshold for cycle counts considered a 1 or 0.
// You can do this by passing a 3rd parameter for this threshold.  It's a bit
// of fiddling to find the right value, but in general the faster the CPU the
// higher the value.  The default for a 16mhz AVR is a value of 6.  For an
// Arduino Due that runs at 84mhz a value of 30 works.
// Example to initialize DHT sensor for Arduino Due:
//DHT dht(DHTPIN, DHTTYPE, 30);

/*
 * LM35(温度センサ)関連の定数の定義
 */
float v=5.0;            // MEGA2560やEthernet R3は5.0V駆動
float tempC   = 0;      // 摂氏値( ℃ )
float Threshold=28.0;   // 警告LEDをつける閾値(気温)
float offset = 0;       // センサの温度ズレを修正する変数

#define LOOPNUMBER 100
#define MEDIAN 50

/*
 * 配線関連の定数
 */
int TempSensorPin = 0;  // センサ用のアナログI/Fは0番を利用

/*
 * ネットワーク関係の定数
 */
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0x92, 0x1A }; //アドレスは手持ちのarduinoのものに変更すること
boolean useDhcp = false; // DHCPで運用する場合は true に変更
                   // DHCPでのアドレス取得失敗時の対策や,長時間経過後のアドレス再割当て等は対応していない
/* 以下は固定IP運用の場合の変数なので適宜変更して使用すること */
IPAddress ip(192,168,0,201);
IPAddress dnsServer(192,168,0,1);
IPAddress gatewayAddress(192,168,0,1);
IPAddress netMask(255,255,255,0);

#ifdef USE_PING
SOCKET pingSocket = 0;
char buffer [256];
#endif /* USE_PING */



/*
 * デバッグ関連
 */
boolean debugFlag=false; // デバッグ目的で,各種情報をシリアルに出力する場合は true

/*
 * プログラム本体
 */

#ifdef USE_PING
ICMPPing ping(pingSocket, (uint16_t)random(0, 255));
#endif /* USE_PING */

EthernetServer server(80); // サーバオブジェクトの定義. 引数はポート番号

void setup() {    
  if (debugFlag) {
    // シリアル通信速度
    Serial.begin(9600);
  }
  // DHT11センサの初期化
  dht.begin();
    // MACアドレスとIPアドレスの設定
  // 参考URL http://arduino.cc/en/Reference/EthernetBegin
  if (useDhcp) {
    Serial.println("configure Ethernet using DHCP");
    if ((Ethernet.begin(mac)==0) && (debugFlag)) {
      Serial.println("Failed.");
    } else {
      Serial.println("Success.");
    }
  } else {
    Serial.println("Manual ip confuguration of ethernet");
    Ethernet.begin(mac, ip, dnsServer, gatewayAddress, netMask);
    Serial.println("done.");
#ifdef USE_PING
    // ルータのアドレスにping
    // 参考URL http://playground.arduino.cc/Code/ICMPPing
    ICMPEchoReply echoReply = ping(gatewayAddress, 4);
    if (debugFlag) {
      if (echoReply.status == SUCCESS) {
        Serial.println("Ping to Gateway address : success");
      } else {
        Serial.println("Ping to Gateway address : fail");
      }
    }
#endif /* USE_PING */
  }
  if (debugFlag) {
    // IPアドレスのシリアルへの出力
    Serial.println("IP address of Ethernet Interface");
    Serial.println(Ethernet.localIP());
    //
    float temperature = getLMTemperature(TempSensorPin);
    float h = getDHTHumidity();
    float t = getDHTCTemperature();
    float f = getDHTFTemperature();
  }
  // サーバの実行
  server.begin();
}

void loop() {
  // 接続してきたクライアントを示すオブジェクトを生成
  EthernetClient client = server.available();
  if (client) {
    if (debugFlag) {
      Serial.println("Connection established by a client");
    }
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read(); //一文字読み取り
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("");
          client.println("");
          // LM35温度読み取り  
          float temperature = getLMTemperature(TempSensorPin);
          client.println("LM35 output");
          client.println("
");
          client.print("Temperature:  ");
          //client.print(temperature);
          //client.println("  degrees Celsius");
          //client.println("
");
          float temperatureEX = getLMTemperatureEX(TempSensorPin);
          //client.print("Fixed Temperature:  ");
          client.print(temperatureEX);
          client.println("  degrees Celsius");
          client.println("
");
          client.println("
");
          //
          // DHT11の温度湿度
          float h = getDHTHumidity();
          // Read temperature as Celsius
          float t = getDHTCTemperature();
          // Read temperature as Fahrenheit
          float f = getDHTFTemperature();
          float hi = dht.computeHeatIndex(f, h);
          // Check if any reads failed and exit early (to try again).
          if (isnan(h) || isnan(t) || isnan(f)) {
            if (debugFlag) {
              Serial.println("Failed to read from DHT sensor!");
            }
            client.println("Failed to read from DHT sensor!");
            client.println("
");
          } else {
            client.println("DHT11 output");
            client.println("
");
            client.print("Temperature: ");
            client.print(t);
            client.println(" degree Celsius");
            client.println("
");
            /*
            client.print(f);
            client.println(" degree Fahrenheit");
            client.println("
");
            */
            client.print("Humidity:  ");
            client.print(h);
            client.println("  \%");
            client.println("
");
            /*
            client.print("Heat index: ");
            client.print(hi);
            client.println(" degree Fahrenheit");
            client.println("
");
            */
            if (debugFlag) {
              Serial.println("Heat index: ");
              Serial.println(hi);
            }
          }
          //
          //
          client.println("");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    if (debugFlag) {
      Serial.println("client disconnected");
    }
  }
}

//
// DHT11の温度と湿度読み取り関数
float getDHTCTemperature(){
  float t = dht.readTemperature();
  if (debugFlag) {
    // 温度のシリアルへの出力
    Serial.println("DHT11 celcious temperature is");
    Serial.println(t);
  }
  return t;
}
float getDHTFTemperature(){
  float t = dht.readTemperature(true);
  if (debugFlag) {
    // 温度のシリアルへの出力
    Serial.println("DHT11 Faren temperature is");
    Serial.println(t);
  }
  return t;
}
float getDHTHumidity(){
  float h = dht.readHumidity();
  if (debugFlag) {
    // 湿度のシリアルへの出力
    Serial.println("DHT11 Humidity is");
    Serial.println(h);
  }
  return h;
}

//
// LM35の温度読み取り関数
//
float getLMTemperature(int pin){
  float t;
  t = analogRead( pin );
  t = ((v * t) / 1024) * 100;
  t = t + offset;
  if (debugFlag) {
    // 温度のシリアルへの出力
    Serial.println("temperature is");
    Serial.println(t);
  }
  return t;
}

float getLMTemperatureEX(int pin){
  int buff[LOOPNUMBER];
  float tmp;
  if (debugFlag){
    Serial.print("Data = ");
  }
  for (int i=0 ; i < LOOPNUMBER ; i++){
    buff[i] = analogRead( pin );
    if (debugFlag) {
      Serial.print(buff[i]);
      Serial.print(" , ");
    }
  }
  if (debugFlag) {
    Serial.println("");
  }
  sort(buff,LOOPNUMBER);
  if (debugFlag){
    Serial.print("SortData = ");
    for (int i=0 ; i < LOOPNUMBER ; i++){
      if (debugFlag) {
        Serial.print(buff[i]);
        Serial.print(" , ");
      }
    }
  }
  tmp = ((v * buff[MEDIAN]) / 1023) * 100;
  tmp = tmp + offset;
  if (debugFlag) {
    // 温度のシリアルへの出力
    Serial.println("temperature is");
    Serial.println(tmp);
    Serial.println("Pin number = ");
    Serial.println(pin);
  }
  return tmp;
}
void sort(int a[], int size) {
    for(int i=0; i<(size-1); i++) {
        for(int o=0; o<(size-(i+1)); o++) {
                if(a[o] > a[o+1]) {
                    int t = a[o];
                    a[o] = a[o+1];
                    a[o+1] = t;
                }
        }
    }
}

2015年5月3日日曜日

Raspberry Pi 2 もヒートシンクとファンとUSBシリアルをつける (3)

今回はソフトウェア編です.


ハードとの関係


ファンのON/OFFを行うためには,下の回路図の「トランジスタ(2SC1815)」のベースに入る線を制御する必要があります.下の図を見てもらうとわかるように,GPIOの19番ピンにつないでいます.







ピン位置


GPIOの19番の場所は以下の図の通りで,USBポートの近い位置の内側.

微妙に中途半端な場所なのは,ユニバーサル基板上に回路を作った関係上,トランジスタのベースの位置が一番落ち着く場所だったからです.

というわけで,ファンを制御するプログラムは,GPIOの19番ポートを制御すれば良いことになります.







ファンのテストプログラム


ファンをONするプログラム


#!/bin/sh


echo 19 >/sys/class/gpio/export
echo out >/sys/class/gpio/gpio19/direction
echo 1 > /sys/class/gpio/gpio19/value

ファンをOFFするプログラム


#!/bin/sh


echo 19 >/sys/class/gpio/export
echo out >/sys/class/gpio/gpio19/direction
echo 0 > /sys/class/gpio/gpio19/value

制御プログラム本体


#!/usr/bin/perl

use Switch;

open(IN, "/sys/class/thermal/thermal_zone0/temp");
$cputemp = /1000.0; #numbers
close(IN);
$cputempavg = $cputemp;

system("echo 19 > /sys/class/gpio/export");
system("echo out > /sys/class/gpio/gpio19/direction");
system("echo 1 > /sys/class/gpio/gpio19/value");
$fan_run = 1;

while(1)
{
        open(IN, "/sys/class/thermal/thermal_zone0/temp");
        $cputemp = /1000.0; #numbers
        close(IN);

        $cputempavg = $cputempavg*0.9 + $cputemp*0.1;

        if($cputempavg > 45)
        {
                $fan_run = 1;
        }
        if($cputempavg < 35)
        {
                $fan_run = 0;
        }
        if($cputempavg > 65)
        {
                $fan_run = 2;
        }
        switch ($fan_run) {
                case 1 { system("echo 1 > /sys/class/gpio/gpio19/value");}
                case 0 { system("echo 0 > /sys/class/gpio/gpio19/value");}
                else   { system("shutdown -h now");}
        }
        sleep(10);
}

Raspberry Pi 2 もヒートシンクとファンとUSBシリアルをつける (2)

今回は製作内容ですが,詳細は省きます(もうしわけなし).

ただし,回路図さえあれば,だいたい誰でも作れると思うので,Fritzingの回路図を添付しておきます.利用した部品に関しては,前回の記事にも書いてあるので,そちらも参照してください.ユニバーサル基板上の配線はご勘弁.もう少し,Fritzingに精通しないと無理っぽいです.

最近,Eagleで戦うのに疲れてしまったので,最近は「fritzing」を使っています.ブレッドボードを使って試作するならEagleよりfritzingの方が楽だと思います.それにEagleは部品を探すのがあまりにも面倒くさい.


とりあえず,Fritzingの図面も置いておきます.

回路図




ブレッドボードでの試作図






Raspberry PiのGPIOの配置図





注意事項

ファンの位置とユニバーサル基板の関係

ファン付きのケースですが,ファンの位置がちょうどCPUの真上にあるため,シリアルコンソールを外に取り出すためのコネクタの基板(下の写真の右側の基板)と当たるため,基板の使ってない部分を切断しました.

さらに,ファンの制御を行う基板(下の写真の左側の基板)につけたトランジスタが本体基板に接触すると嫌だったので,トランジスタを折り曲げています.トランジスタがイーサのチップの真上にくるのが少し嫌ですが,ちょっと我慢.

まぁ,ファンが付いているので,あまりCPUの温度が上がらなくても早めに回す方向で検討します.



Raspberry Pi 2 もヒートシンクとファンとUSBシリアルをつける (1)

マイクロソフトがRaspberry Pi 2ユーザにwindows10 (Raspberry Pi 2用)をただでくれるというので,Raspberry Pi 2を買った.
買うだけでは,まともに運用できないので,冷却関係の回路をつけた.

以前,Raspberry Piでもやったが,今回は,千石でファン付きの箱を売っていたので,そちらを使ってやってみた.以前はファン付きの箱がなかったので,箱から加工したが,今回はそれがなくて楽ちん.ただ,以前使ったファンと比べて,今回箱についてくるファンは小さくて弱いのでそこだけが心配.

以前の記事




完成品


お買い物

購入した店

皆さんお馴染みの...かもしれない有名店


自宅から1時間弱で到着できるものうれしい.

秋月で買ったもの

以前,普通のPiは直接RSコンポーネンツから購入したけど,今は個人向けのPi2は「株式会社ケイエスワイ」から買えとRSコンポーネンツのwebに書いてあるので,秋月で買いました.


本体だけ秋月で買いましたが,後日千石秋葉原本店の2Fで,ファン付きのケースに入れられたRaspberry Piを発見.Model B+かPi2かまでは確認してませんが,もし,Pi2だったら,別々に買うより良いかも.

千石で買ったもの

  • Seeed Studio114990129/141107004
    【FAN付き】Raspberry Pi B+専用ケース(クリア/ロゴなし/組立式)
    http://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=EEHD-4N4C
  • Seeed Studio 114990130 (上のケースのファン無し)
    Raspberry Pi B+専用ケース(クリア/ロゴなし/組立式)
    http://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=EEHD-4R3F
    なぜ2個買ったかは,後(「ケースの問題」のところ)で説明します.
  • タカチ電機工業TNF11-16
    鉛フリーユニバーサル基板(SW-20用)
    http://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=3AUA-KMJL#
    今回は,一番小さいものだけを購入しましたが,工作のしやすさなどを考慮すると,「TNF15-25」とか「TNF25-35」でもいいかもしれません.

手元にあったもの

以前の記事の制作に使った部品を今回も使用.ただし,ショットキーダイオードの型番はすっかり忘れてしまいました(申し訳なし).

  • 3kΩのカーボン抵抗
  • ショットキーダイオード
  • トランジスタ (2SC1815)


ケースの問題

ファン付きのケースは40pinの横に大きな切り込みがあり,最初,組み立てようとしたら,そこで「パッキリ」折れてしまいました.それに対して,ファン無しのケースは同じ部分に切り込みがないため,少々力がかかっても壊れることはないため,追加でファン無しを購入し,壊れた部分を差し替えました.




ヒートシンク

Raspberry Pi用のヒートシンクは千石本店の2Fレジで店員さんに言うと出してもらえますが,
今回購入した「Seeed Studio 800035001 Raspberry Pi用アルミニウム・ヒートシンク」の他に,「Seeed Studio 114990125 Raspberry Pi B+用ヒートシンク」があり,本来,Pi2用はPi B+用となります.

ただし,今回旧型Pi用を購入したのは,旧型用は小型のヒートシンクが余分についている代わりに,熱伝導両面テープが付いているので,特別な材料とかなしに,チップに貼り付けることができるためです.

多少値段が違うため,熱伝導両面テープなどの取り付け材料を持っている人は,本来のPi B+用を購入したほうが良いでしょう.

次回

次回は製作内容になります.