ESP-WROOM-02 を使った リモート温度・湿度・気圧計の製作

ESP-WROOM-02-1

今年はほんとに毎日暑い日が続きました。汗をかきながらWebを閲覧していると偶然ikesato氏の「ESP-WROOM-02 の Arduino 環境で I2C 制御」にたどりつきました。

初めて聞く名前なので早速検索をかけ調べると「技適OKな中華IoTモジュールを使いこなす」こちらのサイトに詳しい内容が書かれていました。

ESP-WROOM-02についてわかった事は

(1)ESP8266EXを搭載したWiFiモジュール。WiFi接続機能とCPU・フラッシュROMが一体になっていて日本の技適マークも取得済みである。3.3V動作。

(2)デフォルトではATコマンドベースの無線モジュールだがArduino IDEを使ってプログラムすることも出来る。

要はセンサー又は表示器と電源だけ用意すれば簡単に動作するWiFiに繋がるチップだということですね。以前私が実験したNRF24L01+と違い日本の法律に抵触せずに工作することができ、しかも単価が安い!私は無意識のうちに通販サイトの購入ボタンをポッチとしていました。

とりあえず2.54mmピッチに変換されている完成品、SWITCH SCIENCE社のESP-WROOM-02ピッチ変換済みモジュール《フル版》 1,080円を1個購入してしまいました。

センサーは手持ちにあった気圧センサー 秋月電子通商 SCP1000モジュール(SPI接続)と何処から購入したか分からなくなってしまった気温・湿度センサー SENSIRION SHT10を繋ぎます。電源は前回も使用した秋月電子製StepUP 3.3v DC/DCコンバータによる単三乾電池×2本による動作としました。

前回通りDebian自宅サーバにてHIGHCHARTS社製 非商用利用無料のHIGHSTOKライブラリを使用しwebから過去データを含め時系列に表示します。

結局、以前の記事の焼き直しです。オリジナリティが全く無い製作記事です。(わざわざご訪問いただき誠に申し訳ありません。<(_ _)> )

まずESP-WROOM-02のArduino IDEによる開発のセッティングですがこちらのサイト https://github.com/esp8266/Arduino に載っている通りに行います。

・Arduno Wevsite http://www.arduino.cc/en/main/software よりArduino 1.6.5をダウンロードしてインストール
・Arduino 1.6.5を起動して ファイル – 環境設定 と進みAdditional Board Manager URLs欄にhttp://arduino.esp8266.com/stable/package_esp8266com_index.json を入力
・再起動後 ツール – ボード – ボードマネージャー より下のほうのにあるesp8266 by ESP8266 Comunity をクリックしてインストール (※ 手順が抜けていました 10/25追記)
・ツール – ボード よりGeneric ESP2866 Moduleを選択

詳しくはこちらのサイト「技適済み格安高性能Wi-FiモジュールESP8266をArduinoIDEを使ってIoT開発する為の環境準備を10分でやる方法 」に書かれています。

スケッチの書き込みには秋月電子 FT232RL USBシリアル変換モジュール(AE-UM232R)を使用しました。全体の電圧が3.3VなのでVCCIOも3.3Vにします。

ここで注意すべきことは、AduinoIDEでスケッチを書き込んでしまうとデフォルトのATコマンドベースの動作はしなくなってしまうことです。元に戻すことは無いかもしれませんが、念のためにその方法を押えて置きましょう。「ねむいさんのぶろぐ」こちらに詳しく書かれています。

「ねむいさんのぶろぐ」に書かれていますが、スケッチを書き込む前にESP-WROOM-02本体のMACアドレスやSSIDなどをATコマンドを使い調べて置く方が良いと思います。AduinoIDEのシリアルモニタでボーレートを115200bps,改行コードをCRおよびLFに設定して通信します。

[ATコマンドの例]
AT+CIFSR
+CIFSR:APIP,"192.168.4.1"
+CIFSR:APMAC,"1a:fe:34:xx:xx:xx"
OK
AT+CWSAP?
+CWSAP:"ESP_xxxxxx","",1,0
OK
AT+GMR
AT version:0.25.0.0(Jun 24 2015 18:02:27)
SDK version:1.1.2
compile time:Jun 24 2015 18:15:29

ねむいさん謹製のESP-WROOM-02ピン配置表 https://dl.dropboxusercontent.com/u/34019941/images/2015/20150623/20150623_006.pngはとても役にたちました。ありがとうございました。(ただしGND PINのアサインがSWITCH SCIENCE社のブレークアウトボードでは違っていますので注意が必要です。)

回路は上記の「ESP-WROOM-02 の Arduino 環境で I2C 制御」を参考にさせていただきました。

回路図

回路図

SW1を押しながらSW2を押してリセットをかけ、SW2,SW1の順に離すとファームウエアの書き込みモードになるのでスケッチを書き込むことができます。

スケッチの書き込み

スケッチの書き込み

ESP-WROOM-02をArduino IDEで使うことが始めてでしたので、まずsht10センサーの値の読み込みが動作するか確認しました。こちらからダウンロードして解凍したたファイルSHTx1-masterをArduinoをインストールしたディレクトリ下にあるlibariesのなかにコピーしますとReadSHT1xValuesをスケッチの例から読み出すことができるようになります。

ここは良く分からなかったのですが、このようなパラメータで設定しました

良く分からなかったのですが、とりあえずこんな感じで

ArduinoIDE にセットすべきパラメータに分からない項目がありましたが、とりあえず図のようにして書き込みました。

今回はSHT10のdataPin がESP-WROOM-02の IO2に、同じくclockPinが IO4に繋がりますのでReadSHT1xValuesスケッチのその部分を変更します。
測定結果がシリアルモニタに表示されました。よかったです。

/**
* ReadSHT1xValues
*
* Read temperature and humidity values from an SHT1x-series (SHT10,
* SHT11, SHT15) sensor.
*
* Copyright 2009 Jonathan Oxer &lt;jon@oxer.com.au&gt;
* www.practicalarduino.com
*/


#include <SHT1x.h>

// Specify data and clock connections and instantiate SHT1x object
#define dataPin 2    //オリジナルから変更
#define clockPin 4   //オリジナルから変更
SHT1x sht1x(dataPin, clockPin);

void setup()
{
Serial.begin(38400); // Open serial connection to report values to host
Serial.println("Starting up");
}

void loop()
{
float temp_c;
float temp_f;
float humidity;

// Read values from the sensor
temp_c = sht1x.readTemperatureC();
temp_f = sht1x.readTemperatureF();
humidity = sht1x.readHumidity();

// Print the values to the serial port
Serial.print("Temperature: ");
Serial.print(temp_c, DEC);
Serial.print("C / ");
Serial.print(temp_f, DEC);
Serial.print("F. Humidity: ");
Serial.print(humidity);
Serial.println("%");

delay(2000);
}

SCP1000 気圧センサー部分も同様に
https://github.com/PaulStoffregen/Arduino-1.6.1-Teensyduino/blob/master/hardware/arduino/avr/libraries/SPI/examples/BarometricPressureSensor/BarometricPressureSensor.inoを参考にさせていただきました。

SPI通信のPIN結線ですが今回は

DRDY— NC (使用しない)
CSB — 5
MOSI — 13
MISO — 12
SCK — 14

と結線しています。スケッチ中の該当する値を下記の通り変更します。

//const int dataReadyPin = 6; //オリジナルから変更
const int chipSelectPin = 5;

dataReadyPinを使わないのでその部分を修正して変わりにディレイをいれています。必要な時間の長さが分からなかったのでとりあえず 1000mmSとしてしまいました。(お気付きになられたかと思いますが「とりあえず」を多用するのが私の特徴です、さらに大抵の場合その後、変化はありません。そう、予想通りです。)

上記のスケッチをまるまる使用させていただき、さらにArduino IDE のスケッチの例からはESP8266WiFi-WiFiClientを修正して目的の下記スケッチが完成いたしました。(ほとんど全て copy & pasteです。作者の皆様ありがとうございました。)

#include <SPI.h>
#include <SHT1x.h>
#include <ESP8266WiFi.h>

//SCP1000 Sensor's memory register addresses:
const int PRESSURE = 0x1F;      //3 most significant bits of pressure
const int PRESSURE_LSB = 0x20;  //16 least significant bits of pressure
const int TEMPERATURE = 0x21;   //16 bit temperature reading
const byte READ = 0b11111100;     // SCP1000's read command
const byte WRITE = 0b00000010;   // SCP1000's write command

//SCP1000 pins used for the connection with the sensor
// the other you need are controlled by the SPI library):
//const int dataReadyPin = 6;
const int chipSelectPin = 5;

// Specify data and clock connections and instantiate SHT1x object
#define dataPin  2
#define clockPin 4
SHT1x sht1x(dataPin, clockPin);  

// wifi
const char* ssid     = "エスエスid";
const char* password = "パスword";
 
const char* host = "自宅server IP-address";

void setup()
{

 delay(10);
 
  // We start by connecting to a WiFi network
 
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

//SCP100 start the SPI library:
  SPI.begin();
// initalize the  data ready and chip select pins:
//  pinMode(dataReadyPin, INPUT);
  pinMode(chipSelectPin, OUTPUT);

  //Configure SCP1000 for low noise configuration:
  writeRegister(0x02, 0x2D);
  writeRegister(0x01, 0x03);
  writeRegister(0x03, 0x02);
  // give the sensor time to set up:
  delay(100);

   Serial.begin(115200); // Open serial connection to report values to host
   Serial.println("Starting up");
}

int value = 0;

void loop()
{
  delay(5000);
  ++value;

//SHT10
  float temp_c;
  float temp_f;
  float humidity;

  // Read values from the sensor
  temp_c = sht1x.readTemperatureC();
  temp_f = sht1x.readTemperatureF();
  humidity = sht1x.readHumidity();

  // Print the values to the serial port
  Serial.print("Temperature: ");
  Serial.print(temp_c, DEC);
  Serial.print("C / ");
  Serial.print(temp_f, DEC);
  Serial.print("F. Humidity: ");
  Serial.print(humidity);
  Serial.println("%");


//SCP1000

 //Select High Resolution Mode
  writeRegister(0x03, 0x0A);

    delay(1000);
    //Read the temperature data
    int tempData = readRegister(0x21, 2);

    // convert the temperature to celsius and display it:
    float realTemp = (float)tempData / 20.0;
    Serial.print("Temp[℃]=");
    Serial.print(realTemp);


    //Read the pressure data highest 3 bits:
    byte  pressure_data_high = readRegister(0x1F, 1);
    pressure_data_high &= 0b00000111; //you only needs bits 2 to 0

    //Read the pressure data lower 16 bits:
    unsigned int pressure_data_low = readRegister(0x20, 2);
    //combine the two parts into one 19-bit number:
    long pressure = ((pressure_data_high << 16) | pressure_data_low) / 4;

    // display the temperature:
    Serial.println("\tPressure [Pa]=" + String(pressure));
    
  // Use WiFiClient class to create TCP connections
  Serial.print("connecting to ");
  Serial.println(host);
 
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }
 
  // We now create a URI for the request
  String url = "/tempera/tempera_log.php";
         url = url + "?tempera=" +String( temp_c);
         url = url + "&humidi=" + String(humidity);
         url = url + "&puress=" + String(pressure);
  Serial.print("Requesting URL: ");
  Serial.println(url);
 
  // This will send the request to the server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
  delay(10);
 
  // Read all the lines of the reply from server and print them to Serial
  while(client.available()){
    String line = client.readStringUntil('\r');
    Serial.print(line);
  }
 
  Serial.println();
  Serial.println("closing connection");

 //SCP1000 Select Powere Down Mode
  writeRegister(0x03, 0x00);
 //ESP8266 Deep Sleap mode
 //1:μ秒での復帰までのタイマー時間設定 (30分)  2:復帰するきっかけの設定(モード設定)
  ESP.deepSleep(30 * 60 * 1000 * 1000 , WAKE_RF_DEFAULT);  

  //deepsleepモード移行までのダミー命令
  delay(1000);
}

//Read from or write to register from the SCP1000:
unsigned int readRegister(byte thisRegister, int bytesToRead ) {
  byte inByte = 0;           // incoming byte from the SPI
  unsigned int result = 0;   // result to return
//  Serial.print(thisRegister, BIN);
//  Serial.print("\t");
  // SCP1000 expects the register name in the upper 6 bits
  // of the byte. So shift the bits left by two bits:
  thisRegister = thisRegister << 2;
  // now combine the address and the command into one byte
  byte dataToSend = thisRegister & READ;
//  Serial.println(thisRegister, BIN);
  // take the chip select low to select the device:
  digitalWrite(chipSelectPin, LOW);
  // send the device the register you want to read:
  SPI.transfer(dataToSend);
  // send a value of 0 to read the first byte returned:
  result = SPI.transfer(0x00);
  // decrement the number of bytes left to read:
  bytesToRead--;
  // if you still have another byte to read:
  if (bytesToRead > 0) {
    // shift the first byte left, then get the second byte:
    result = result << 8;
    inByte = SPI.transfer(0x00);
    // combine the byte you just got with the previous one:
    result = result | inByte;
    // decrement the number of bytes left to read:
    bytesToRead--;
  }
  // take the chip select high to de-select:
  digitalWrite(chipSelectPin, HIGH);
  // return the result:
  return(result);
}
//Sends a write command to SCP1000

void writeRegister(byte thisRegister, byte thisValue) {

  // SCP1000 expects the register address in the upper 6 bits
  // of the byte. So shift the bits left by two bits:
  thisRegister = thisRegister << 2;
  // now combine the register address and the command into one byte:
  byte dataToSend = thisRegister | WRITE;

  // take the chip select low to select the device:
  digitalWrite(chipSelectPin, LOW);

  SPI.transfer(dataToSend); //Send register location
  SPI.transfer(thisValue);  //Send value to record into register

  // take the chip select high to de-select:
  digitalWrite(chipSelectPin, HIGH);
}

約30分に1回自宅サーバにアクセスしてデータを渡します。ディープスリープモードに関しては

ESP8266の真骨頂Deep-Sleepモードの使い方こちらを参考にさせていただきました。ありがとうございました。

サーバーサイドのスクリプトはESP-WROOM-02からデータを受け取ってファイルに書き込むphpファイルとデータをグラフにして表示させるhtmlファイル2個です。当然ですがwebサーバが稼動していてPHPが使えなければなりません。

PHPファイルですが、 NRF24L01+ と DS18B20+と Arduinoでリモート温度計 3 で書かれている物を手直ししました。

tempera_log.php

<?php
//   ini_set( 'display_errors', 1 );
   $strGetTempera = time();
   $strGetTempera .= ",";
   $strGetTempera .= $_GET['tempera'];
   $strGetTempera .= ",";
   $strGetTempera .= $_GET['humidi'];
   $strGetTempera .= ",";
   $strGetTempera .= $_GET['puress'];
   $strGetTempera .= "\n";
   $strDataFilePath = '/ファイルのpath/atmosphere_log.csv';  //ログを記録するファイルの指定です
   $fp = fopen($strDataFilePath, "ab");
   fwrite($fp, $strGetTempera);
   fclose($fp);
?>

動作確認のためにブラウザから

http://サーバアドレス/パス/tempera_log.php?tempera=1234.567&humidi=12&puress=34
とアドレス欄に入力して atmosphere_log.csvが作成され、中身が下記の様になるか確かめます。

1439546219,1234.567,12,34 //左から アクセスした時のUniX時間,temperaの値,humidiの値,puressの値

グラフを表示させるhtmlファイルも前回作成したものをベースにしています。


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "xhtml11.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Highstocks temperature-humidity-pressure</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
<script src="http://code.highcharts.com/stock/highstock.js"></script>
<script type="text/javascript"> 
var chart1; // globally available
$(document).ready(function() {
$.get('atmosphere_log.csv', function(data) {
    // Split the lines
        var lines = data.split('\n');
        var temperature = [];
        var humidity = [];
        var pressure = [];
        var js_time 
    // Iterate over the lines and add categories or series
    $.each(lines, function(lineNo, line) {
        var items = line.split(',');
	if(parseInt(items[0],10)){
         js_time = parseInt(items[0])*1000+ (9*60*60*1000) ;
	temperature.push([js_time,parseFloat(items[1])]);
	humidity.push([js_time,parseFloat(items[2])]);
	pressure.push([js_time,parseInt(items[3]/100)]);
        }
    });
    var seconds = (js_time /1000)%(24 * 60 * 60);
    var hour  =  Math.floor(seconds / (60 * 60));
    var minit =  Math.floor((seconds % (60 * 60))/60);
    var options = {
     title : {
       text : '[ ' + hour +'時'+ minit +'分 現在] ' + '気温: ' + Math.floor(temperature[temperature.length -1][1]*10)/10 + "℃ "
                       + '湿度: ' + Math.round(humidity[humidity.length -1][1]) + "% "
                     + '気圧: ' + pressure[pressure.length -1][1] + "hPa" ,
       style : {
            color: '#6Faa6F',
            fontSize: '20px'
            }
        },
     navigator: {
         baseSeries: 2
         },
     legend: {
         enabled: true,
         align: 'left',
         backgroundColor: '#EFFFC5',
         borderColor: 'black',
         borderWidth: 2,
        layout: 'vertical',
        verticalAlign: 'top',
        y: 100,
        shadow: true
        }, 
    chart: {
        renderTo: 'container' ,
        zoomType: 'x'
    },
    xAxis: {
        type: 'datetime',
        range:  24*3600 * 1000 // 24 hour
    },
     yAxis: [{
       title: {
         text: '湿度 (%)'
       },            
       opposite: true
    },{
       title: {
              text: '気圧 (hPa)'
              },
       offset : 10,
       opposite: true
    },{
       title: {
         text: '気温 (°C)'
       },
      opposite: false
	}],
    series:[{
             yAxis: 0,
	     data: humidity,
         name: '湿度',
         type: 'area',
	     color: '#ccccff',
             tooltip: {
                    valueDecimals: 1,
                    valueSuffix: '%'
                      }
           },{
             yAxis: 1,
	     data: pressure,
         name: '気圧',
         type: 'spline',
	     color: '#000000',
         tooltip: {
                    valueDecimals: 1,
                    valueSuffix: 'hPa'
                      },
         dashStyle: 'shortdot',
           },{
             yAxis: 2,
	         data: temperature,
             name: '気温',
             color: '#00bbff',
             lineWidth:3,
	         tooltip: {
                    valueDecimals: 1,
                    valueSuffix: '°C'
                   }
	      }]
};
    var chart1 = new Highcharts.StockChart(options);
});
});
</script>
</head>
<body>
<div id="container" style="width: 100%; height: 400px"></div>
</body>
</html>

いままで大気圧などは気にしたことがなかったのですが、今回の件で少しだけ学習しました。大気圧は海抜が低い所では標高差8mで1hPa変化するそうです。私の住んでいるところは東京都下ですが標高約200m弱のところにあります。その割合で計算すると確かに千代田区にある気象庁の発表する値と大体一致します。また、しばし観測した結果、同じ場所では気圧の変動幅はあまり広くないことがわかりました。気圧に関しては大気の状態による変動よりも、標高差による変動がとても大きくなるようです。蛇足ですが血圧計の単位 mmHgはトリチェリの水銀柱由来だそうです。知りませんでした。

何ヶ所かのディレイの値を調整することとWiFiに接続を固定IPにすることにより消費電力をもう少し抑えることができると思います。今回はこの設定で百円ショップで購入した単三電池がどれだけ長持ちするか観察いたします。(2015/8/17より稼動)
※ 2015/10/14 追記
数日前に異常な湿度の値が記録されていたので怪しげに思っていたのですが、2015/10/13午前6時過ぎに電池が切れてしまったようです。上記の設定で単三乾電池2本により動作させ約57日間稼動しました。
※ 2016/04/03 追記
後日3端子レギュレターを追加して電池からAC電源に変更した際分ったのですが、ESP-WROOM-02は連続動作させると結構発熱します。ESP-WROOM-02のそばにセンサーがあると正確に温度を測れない可能性があります。この記事では、Sleepモードを使った間欠動作だったので気付きませんでした。ご注意ください。

こちらに拡大したグラフ

Highstocks temperature-humidity-pressure


 

GeForce GTX 460 ディスプレイドライバの応答停止と回復エラー

zotac_gtx460
私はゲーマーではないのでVideo Card にはあまり興味がなく、写れば良い程度に考えていました。Windows 7 Pro 64bit Pentium E6500 Memory 4G に nvida GT430 Video Card を載せた構成で2画面のディスプレイを表示させて満足です。実際、ワープロや表計算、動画再生では全然問題が生じません。

しかし、動画編集を行う時は、さすがにPCのパワー不足を感じます。私が使っているソフトはadobe premiere pro CS6 なのですが、ある時 adobe認定外のNvidia ビデオカードでもGPU アクセラレーションができるという情報をつかみました。adobe After Effectsでも可能とのことです。

こちらのサイト
http://www.studio1productions.com/Articles/PremiereCS5.htm
にすごく詳しく記述されています。(秀逸です)

早速書いてあるとおりにしてみました。しかし詳しく計測したわけではないのですが、GT430では体感的にはあまり変わりないような気がしました。

ならば「上位のビデオカード」で、ということなのですが、新しいビデオカードを買う予算が捻出できません。そんな折ヤフーオークションをみると「動作確認無し」という商品説明で、ZOTAC GTX460中古品 が出品されていました。少々迷いましたが、格安(2300円也)でしたのでポチとクリックして落札してしまいました。

<gtx 460 をヤフオクで>

GTX460はCUDAコア数がGT430と比較して3.5倍の336個、メモリータイプもGT430のDDR3に対してDDR5です、メモリーインターフェースのBit数も2倍です。最上級クラスの GTX Titan やGTX780,GTX680と比較するとかなり見劣りしますが、軽くお試しというならGTX460でも十分期待が持てます。

商品が届いて早速PCにとりつけます。緊張しながらスイッチをONにしました。VGAの画面が写し出されて一安心です。どうやら当たりだったようです。nVidiaのサイトから最新ドライバをダウンロードしてインストールしました。ところが…です。

しばらく使っていると表記の「ディスプレイ ドライバーの応答停止と回復」エラーが頻発します。出現するのは高負荷の時に限らず発生して、再現性がありません。通常は数秒後回復するのですが、画面が真っ黒になり戻って来ない時もあります。これでは使い物になりません。

googleで検索をかけて調べますと、皆さん苦労しているようで多くの報告がありました。安く仕入れた物ではありますがこのまま断念では気持ちが治まりません。最大限努力することにしました。

  • 電源を400Wから600Wに変更 — (推奨値以下でしたので必須でした、が) …NG
  • グラフィックドライバをバージョンダウン — (あるwebサイトに書かれていた対策でしたが) …NG
  • ケースの放熱対策 — (CPUID HWMonitorで調べるとGPUの温度が高い時で75℃ほど、高すぎるわけではなさそうですが) …NG
  • メインメモリー交換 — (あまりその可能性が考えられないですがメモリーの不具合説を発見、しかし) …NG
  • ディスプレイドライバの残骸が悪さをしている説 — (しばらく前の情報でしたが、藁にもすがる気持ちで) …NG
  • Fermiアーキテクチャに配慮したベータ版ドライバ 331.93 — (5分で) …NG

何度ビデオカードを付け直した事か。もう対策はないとあきらめていたところに次の記事を発見しました。

  • コアクロックを25MHz、メモリークロックを 50MHz落とす。それで解決できないときはコア電圧を0.012~0.013V上げてみること。

http://answers.yahoo.com/question/index?qid=20121216001252AATsP0W

ヤッホー!これはいままで見たことの無かった新説でした。すごく説得力がある文章です。私はまるで芥川龍之介が描いたカンダダのようになってしまいました。ひょっとしたら、ひょっとします。さっそく記事の通り GPU-Z と MSI afterburnerをダウンロードして、期待を込めて設定しました。が … 撃沈。

何をやってもうまく動いてくれません。もうあきらめました。本当です。あーぁ。ガッデム。

いつものごとく沈んだ気分でディスプレイカードを元通りに付け替えます。「処分」の2文字が頭に浮かびました、が、どうせなら中を分解して構造を眺めてからと。

このビデオカードは8本のネジをはずすと簡単に分解できました。ここで大発見、GPUと放熱板の間に塗ってある熱伝導グリス(サーマルコンパウンド)が少し変です。固形化していて、まるで劣化したゴムのようです。しかも随分厚塗りです。厚く塗れば良いって物でもないでしょう。

分解

かすかな希望がわいてきました。マイナスのドライバーで固まったグリスを剥ぎ取り、アルコールの染みたウエットティッシュで綺麗に掃除しました。手元にあった安物熱伝導グリスを薄く塗布してから元に戻し、再度PCにセッテイングです。

20時間連続運転後にこの文章を書いていますが、いまのところきわめて順調です。動画のエンコーディングも同時に行っていますが、かなりの時間短縮になりそうです。

※追記 (お詫び) ※
その後しばらくは順調でしたが、最終的には調子が悪くなり使用に耐えなくなってしまいました。上記は根本的な解決方法にはなりませんでした。

Raspberry Pi USB webcamera 動画ストリーミング配信(オマケで音声も配信)

mjpg-streamer寝ている赤ちゃんやラヴリィな愛玩動物、さらには徘徊する御老人等々を見守るために、ラズベリーパイとUSB webcamを使いWifiで画像と音声のストリーミング配信をする実験を行いました。
いろいろ試してみたのですが、なかなか動作が安定せず現在も試行錯誤の段階です。その過程でまた新たに512MB RAMのRPiを買ってしまいました。

スペックです。

Raspberry Pi Model B
Downloaded image: 2012-12-16-wheezy-raspbian.zip
uname -a output: 3.2.27+ #250 PREEMPT Thu Oct 18 19:03:02 BST 2012 armv6l GNU/Linux

Webcam : Logitech C270
Wireless adapter: BUFFALO WLI-UC-GNM2

lsusb output:
Bus 001 Device 004: ID 0411:01ee BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]
Bus 001 Device 005: ID 046d:0825 Logitech, Inc. Webcam C270

cat /proc/cpuinfo

Hardware : BCM2708
Revision : 000f

電源は 5V 2A

まず動画の配信ですが、ffmpeg ffserverを使う方法やMotion, MJPG-Streamerなど何種類かを色々なサイトを参考にして実験してみました。

Motion(http://www.lavrsen.dk/foswiki/bin/view/Motion/WebHome) は apt-get で簡単にインストールでき、コンフィグ設定が簡単、しかも多機能なのでとても良いのですが、Raspberry Piの処理速度に起因してか最大で352×288 pixel の画像しか見ることができません。さらに問題なのは、私の環境では起動から数時間経過するとuvcvideo関係のエラーで動作が止まってしまう事でした。

しかしMotionは、玄箱HG(KURO-BOX HG) + C270 webcam + Debian squeeze + 640×480(画像)に設定して安定して動いていた経験があります。RPiでのこのエラー原因は、現versionのRaspbianの実装の問題か、またはwebcamとの相性なのか、良くわかりません。(ちなみに archlinux-hf-2012-09-18.zip でも試してみましたが、結果は同じでした)

結局、現在はディレイが少なく画像も640×480 pixelを軽快に再生できるMjpg-streamerを使っています。webcamは家電量販店で安く売っていたELECOM UCAM-DLD200BAも試してみたのですが、私の場合Logitech C270との方が相性が良いみたいです。ちなみにこの組み合わせで1280×960 pixelの再生も可能でした。でも、動画再生の様子ははカクカクという感じです。これは無線LANの通信速度にも大きく依存すると思います。

Mjpg-streamerのインストールについては多くの方が書かれているのですが、私はSLB Labs さんのサイト(http://www.slblabs.com/2012/09/26/rpi-webcam-stream/)を参考にさせていただきました。

作業手順は上記サイトに記載されている通りです。Raspbianインストール後、初期設定を行った後に、まずアップデートしておきました

# apt-get update
# apt-get upgrade

そして、こちらからmjpg-streamer-r63.tar.gzをダウンロード

任意のディレクトリで解凍
# tar xvzf mjpg-streamer-r63.tar.gz

コンパイルするためにlibjpeg8-devをインストール
# apt-get install libjpeg8-dev

シンボリックリンクを作成
# ln -s /usr/include/linux/videodev2.h /usr/include/linux/videodev.h

コンパイル
# cd mjpg-streamer-r63
# CFLAGS+=”-O2 -march=armv6 -mfpu=vfp -mfloat-abi=hard” make

/etc/rc.localファイルの# By default this script does nothing.の下あたりに下記を追記するとブート時に自動起動します。

cd /'ファイル パス'/mjpg-streamer-r63/
export LD_LIBRARY_PATH=.
./mjpg_streamer -i './input_uvc.so -d /dev/video0 -r 320x240 -f 3' -o './output_http.so -w www -p 8080'

(2013/02/24 -r -f の値を変更しました。 thanks D.K )

クライアントPCのブラウザで
http://ラズベリーパイのip address:8080
にアクセスすると、FirefoxやChomeさらに IEでもjavascriptを利用して動画を見ることができます。

また、スカイの社長日記さんのサイト(http://skysh.kyo2.jp/e391120.html) を参考にさせていただき、

/boot/cmdline.txt に smsc95xx.turbo_mode=N を追加 ===> (2013/02/03追記へ)

# raspi-config にて

overclock –> Turbo 1000MHz ARM, 500MHz core, 600MHz SDRAM, 6 overvolt
memory_split –> 16

にしてあります。また無線LANの設定は前回のエントリー(修正アリ)の通りに設定しました。

さて、C270 webcamにはマイクも付いていますので音声も同時ストリーミングに挑戦してみました。

こちら pruperさんのサイト (http://prupert.wordpress.com/2010/08/02/stream-live-audio-from-a-microphone-in-near-real-time-in-ubuntu/)や

こちらのサイト (http://ffmpeg.org/trac/ffmpeg/wiki/Capturing%20audio%20with%20FFmpeg%20and%20ALSA) を参考にしました。

まずffmpeg と lame をインストールします

# apt-get install ffmpeg lame

# arecord -l で Audio Input device を表示させます

**** List of CAPTURE Hardware Devices ****
card 1: U0x46d0x825 [USB Device 0x46d:0x825], device 0: USB Audio [USB Audio]
Subdevices: 0/1
Subdevice #0: subdevice #0

C270 webcamの音声(Microphone on the USB web cam)は上記の通り card 1 ですので ffmpeg の -i オプションで指示する値を hw:1 にします。

# ffmpeg -f alsa -ac 1 -i hw:1 -acodec libmp3lame -ab 32k -re -f rtp rtp://239.5.5.5:1234 >/dev/null 2>&1 &

上記を実行した後にクライアントPCから VLC mdedia player を起動して

メディア (Media)- ネットワークストリームを開く(Open Network Stream) – ネットワークURL欄に rtp://239.5.5.5:1234 – 再生 (Play)

の順にすすめば音声も聞くことができます。

【映像・音声の遅延について】
ネットワークの通信速度にもよるのでしょうが、私の実験では映像に1秒-3秒くらいのディレイがあります。さらに遅れて音声が届くという感じです。

【CPU等の使用率: topの抜粋です】

画像のみの時 (Mjpg-streamer)

top - 21:03:22 up 2 min,  1 user,  load average: 0.37, 0.26, 0.11
Tasks:  60 total,   1 running,  59 sleeping,   0 stopped,   0 zombie
%Cpu(s):  4.1 us, 10.7 sy,  0.0 ni, 85.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:    497764 total,    68868 used,   428896 free,     8976 buffers
KiB Swap:   102396 total,        0 used,   102396 free,    39144 cached

  PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND
 2187 root      20   0 45760 1652 1468 S   5.2  0.3   0:02.35 mjpg_streamer

画像+音声 (Mjpg-streamer + ffmpeg)

top - 20:59:27 up 33 min,  1 user,  load average: 1.04, 1.12, 0.82
Tasks:  63 total,   2 running,  61 sleeping,   0 stopped,   0 zombie
%Cpu(s): 59.2 us, 15.4 sy,  0.0 ni, 25.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:    497764 total,    99988 used,   397776 free,    12920 buffers
KiB Swap:   102396 total,        0 used,   102396 free,    57880 cached

  PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND
15352 root      20   0 37268  10m 4380 R  56.7  2.1   7:56.70 ffmpeg
 2170 root      20   0 53952 1840 1468 S   5.5  0.4   1:43.38 mjpg_streamer

ffmpeg はCPUの使用率が高いです。

調子良く運用できそうだったのですが、やはりエラーメッセージを出して止まってしまいます。 (涙)

Jan 30 17:51:35 raspberrypi MJPG-streamer [2209]: Error grabbing frames
Jan 30 17:51:35 raspberrypi kernel: [12810.889315] uvcvideo: Non-zero status (-5) in video completion handler.

この方法で音声も送るのには無理があるのでしょうか?今後の課題です。

キャプチャ

(2013/02/03 追記)

残念ながらしばらく動かしていると止まってしまい安定しません。どうやらUSB webcamだけでなくUSB無線アダプタの方も安定しないみたいです。smsc95xx問題関係かと思い対策方法を検索してみたところ、次の記事が目に留まりました。

http://www.raspberrypi.org/phpBB3/viewtopic.php?t=21963&p=259518

bgirardot さんがdevelopment firmwarenへのアップデートについて記載されていたので、カーネルパラメータに追記する方法に変えて試してみました。参照先にもかかれているように一度開発版のファームウエアにアップデートすると、元に戻すのはとても難しいので注意が必要です。(bgirardotさんはSDメモリカードのコピーを勧めています)

https://github.com/Hexxeh/rpi-update へアクセスしてこちらに書かれている通りに作業しました。(スパーユーザーの権限で行います)

■ [preparaitons(準備作業)]
私にはこの作業は必要なかったですが、行うとすれば

# apt-get update

# apt-get install ntpdate

# ntpdate -u ntp.nict.jp

# apt-get install ca-certificates

■ [rpi-update コマンドのインストール]

# wget http://goo.gl/1BOfJ -O /usr/bin/rpi-update && sudo chmod +x /usr/bin/rpi-update

# apt-get install git-core

■ [rpi-update の実行]

# rpi-update

# reboot

■ [上記コマンド実行の結果]

# uname -a の表示です。

Linux raspberrypi 3.6.11+ #366 PREEMPT Wed Jan 30 12:59:10 GMT 2013 armv6l GNU/Linux

まだ、長時間テストしているわけではないのですが安定性は良いみたいです。ただ、mjpg-streamerを実行した時の再生スピードが遅くなってしまったような感じがします(バラツキがある)。

(2013/02/05 さらに追記) 残念ながら上記設定でも長時間運用していると下記のエラーを出して動画配信が止まってしまいました。安定的に連続で動かせるようになるには、しばらくかかりそうです。

Feb 5 05:19:23 raspberrypi kernel: [78477.340097] uvcvideo: Non-zero status (-5) in video completion handler.

Raspberry Pi 無線LAN (wifi) SSH接続ができない

WLI-UC-GNM2
Raspberry pi を有線LAN経由でSSH接続してHeadless運用(モニタとかキーボードを接続しない)していたのですが、無線LANでSSHが使えれば離れたところにも設置できて便利です。そこでwifiで使うために、BUFFALOの無線アダプタを購入いたしました。型番はWLI-UC-GNM2。Amazon.co.jpで891円でした。無線LANに関してはWeb上で多くの方が報告されていますので問題ないハズと思ったのですが、なかなかうまくいきませんでした。

まず私のRPi環境のスペックです

Raspberry Pi Model B
Downloaded image: 2012-10-28-wheezy-raspbian.zip
Kernel version: 3.2.27+
Wireless adapter: BUFFALO WLI-UC-GNM2
lsusb output: Bus 001 Device 004: ID 0411:01ee BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]

board revision
root@raspberrypi:~# cat /proc/cpuinfo

Hardware : BCM2708
Revision : 0003

電源は 5V 2A
無線アダプタはセルフパワードハブ( powered hub)に接続
(電源に問題があって通信できない事例をWebで見ていましたので、この構成で試してみました。が、結果的には直接RPiに無線アダプタを挿しても同じでした。)

アクセスポイント側 BUFFALO WHR-HP-GN

無線アダプタは接続するとすぐ認識できました。

root@raspberrypi:~# lsusb
Bus 001 Device 004: ID 0411:01ee BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]

root@raspberrypi:~# dmesg
[    3.263009] usb 1-1.2: new high-speed USB device number 4 using dwc_otg
[    3.409389] usb 1-1.2: New USB device found, idVendor=0411, idProduct=01ee
[    3.423267] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[    3.437499] usb 1-1.2: Product: 802.11 n WLAN
[    3.448902] usb 1-1.2: Manufacturer: Ralink
[    3.460167] usb 1-1.2: SerialNumber: 1.0

IP address ,SSID, 事前共有Key等をセットします。(IPアドレス等は実際とは異なる便宜上のものです)LANケーブルが差し込まれている時は eth0 から通信して、また反対に無線アダプタが差し込まれている時はwlan0から通信する事を目的としています。 (1/29 追記: 後日、新しいSD-CARDに再構築してみたところ、下記の設定だけではデフォルトゲートウエイがwlan0に切り替わりませんでした。何の設定をしたのか思い出せないので、しかたなくeth0の設定をコメントアウトしました。また、アクセスポイントはWPA-PSK 暗号化はAESで試してみました。 )

</etc/network/interfaces>

auto lo
iface lo inet loopback

# auto eth0
# allow-hotplug eth0
# iface eth0 inet static   
# address 192.168.11.200
# network 192.168.11.0
# netmask 255.255.255.0
# broadcast 192.168.11.255
# gateway 192.168.11.1


auto wlan0
allow-hotplug wlan0
iface wlan0 inet static
address 192.168.11.201
network 192.168.11.0
netmask 255.255.255.0
broadcast 192.168.11.255
gateway 192.168.11.1

wpa-ssid "エスエスID"
wpa-psk "事前共有key"

</etc/resolv.conf>

nameserver 192.168.11.1
root@raspberrypi:~# shutdown -r now

無線アダプタをセットして、LANケーブルは抜いた状態でリブートします。

root@raspberrypi:~# iwconfig
lo        no wireless extensions.

wlan0     IEEE 802.11bgn  ESSID:"エスエスID"
          Mode:Managed  Frequency:2.412 GHz  Access Point: 00:24:A5:xx:xx:xx
          Bit Rate=48 Mb/s   Tx-Power=20 dBm
          Retry  long limit:7   RTS thr:off   Fragment thr:off
          Encryption key:off
          Power Management:on
          Link Quality=70/70  Signal level=-19 dBm
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:1  Invalid misc:16   Missed beacon:0

eth0      no wireless extensions.

リンクはOKみたいです

root@raspberrypi:~# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.11.1      0.0.0.0         UG    0      0        0 wlan0
192.168.11.0      *               255.255.255.0   U     0      0        0 wlan0

ルーティングテーブルもOKみたいです。おや?簡単に繋がりました。インターネットもOK。Raspberry Pi側から通信する事は全然問題は無いですね。ですが・・・
クライアントのWindowsPCから有線LANでは繋がったSSHが無線LANでは繋がりません。(涙)

かなりの時間を費やしてようやく原因がわかりました。

(1)Windows PC   --- ping ---> Raspberry Pi  <NG>
(2)Raspberry Pi --- ping ---> Windows PC    <OK>
(3)Windows PC   --- ping ---> Raspberry Pi  <OK> (しばらくの間だけ)

上記のような状況です。Raspberry PiからクライアントPCに向けてPingを打った直後のみ、クライアントPCからRaspberry Piに通信できます。クライアント側からの ARP Request に Raspberry Pi の wlan0 が応答を返さない事が原因みたいです。

この対策を探すために必死にラズベリーパイのフォーラム等を探しまくりましたが、一番有力な解決策と思われるものは定期的にRaspberry Pi側からPingを打つ方法でした。簡単と言えばそうですが、これはスマートとはいえない解決策ですね。たいした成果も得られず、しかも夜遅くまで作業していたので、私はくたくたになってしまいました。(意識もうろう)

そこで後日、状況を整理するために、普段使っていなかった無線ルータ(PCI GW-MF54G2)で実験環境を作ってみましたところ、驚いたことに、何の問題もなく無線LANでクライアントマシンからSSH通信ができました。Raspberry Pi側ではなく、アクセスポイント側 BUFFALO WHR-HP-GNの仕様に問題があったということが結論でした。

バッファロのサイトを見たら新しいファームウエア【Ver.1.85 (R1.18/B1.00)】があって、「broadcast通信が出来なくなる場合がある問題を修正しました。」と書かれていましたので早速アップデートしたところ、現在使用している無線LANアクセスポイント (WHR-HP-GN)でも使えるようになりました。めでたしめでたしです。

Raspberry PiとArduino 間 nRF24L01+ 通信実験

一月の末に RS Compornets社に注文予約して待つこと8ヶ月、やっと Raspberry Piが先日我が家に届きました。私はケースも一緒に頼んだので送料込みで合計金額が$49.49 USD 、日本円で¥4,082でした。

早速 Debian派生のLinux、Raspbianをインストールしてみました。イントールにあたってはJunさんのホームページがとても参考になります。raspbianのバージョンですが、私は2012-09-18-wheezy-raspbianをインストールしました。蛇足になりますが、「ラズビアン」っては、日本語では少々危ない響きですね。わたしは滑舌(かつぜつ)が良くないのでとても心配です。

GUIで操作することもできて、ネットワークにも繋がる。それでいてこの価格は、すごいの一言ですね。色々と応用ができそうです。

その後、Raspbery Piの GPIO で nRF24L01 を動かす記事を発見しました。

Raspberry Pi View topic – NRF24L01 RF Transceiver

Beaglebone 用に書かれたnRF24L01 Libraryを Raspberry Pi で動かせるように書き直したと書かれています。( Thanks Mr. Purinda Gunasekara & Mr./Ms. direk! ) この記事を参考に、Raspbery Pi と Arduinoとの通信実験を行いました。

※ 2.4GHz帯 ISMバンドを使用する小電力機器でも日本国内で使用する為には、電波法により技術基準に適合していることを、特定の認証機関で証明してもらう必要があります。違反しますと、1年以下の懲役または100万円以下の罰金という刑事罰の対象になり得ますので、くれぐれもご注意ください。

こちらより nrf.tar.gz をダウンロードします。(Click here to start download from sendspace をクリックするとダウンロードできます)

以下、私はRaspberry PiにSSHで接続して作業を行いました。

$ tar -xzvf nrf.tar.gz
$ cd nrf3

解凍してできた nrf3ディレクトリにある main.cpp がRaspberry Pi側のサンプルコードです。pingを打ってArduino側から戻ってくるreplayを待ちます。ArduinoのRF24ライブラリのサンプルプログラムを元に作られているようですね。roleピンの機能は省かれているみたいです。

また、pingpair_dyn_arduino_pongbackディレクトリにあるコードはArduino側のものになります。Raspberry Pi からのping を待ち、受け取ったら応答を返す動作をします。ちなみにArduinoにはRF24ライブラリが必要です。

$ make

コンパイルすると dist/Debug/GNU_Arm-Linux-x86/ ディレクトリに rf24bb が作成されます。これが実行するプログラムになります。

次に上記プログラムを実行するために、Raspberry PiでSPIを使えるようにします。「竹本 浩氏のページ」を参考にさせていただきました。

/etc/modules に spidev の1行を追加して、/etc/modprobe.d/raspi-blacklist.confのblacklist spi-bcm2708を#でコメントアウトします。

</etc/modules>

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.

snd-bcm2835
spidev

</etc/modprobe.d/raspi-blacklist.conf>

# blacklist spi and i2c by default (many users don't need them)

#blacklist spi-bcm2708
blacklist i2c-bcm2708

リブート後、

# ls -l /dev/spidev* を実行して /dev/spidev0.0 and 0.1 が表示されることを確認します。これでソフトウエアの準備は完了です。


ハードウエアの結線ですが、raspberry pi 側のmain.cppの中に RF24 radio(8, 25); の記述があるのでnRF24L01のCEをGPIO 8,CSNをGPIO 25に繋ぎます。

同様に Arduino側 pingpair_dyn_arduino_pongback.ino 中に RF24 radio(4,3); の記述があるのでnRF24L01のCEをdigital 4,CSNをdigital 3に結線しました。必要であれば数値を変更してピン・アサインを変えることができます。

$ sudo ./nrf3/dist/Debug/GNU_Arm-Linux-x86/rf24bb

全てをセットした後に、Raspberry Pi 側ではルート権限で rf24bbを走らせます。同じくArduino側もスタートさせておきます。

Raspberry Pi のターミナルに以下のように表示され、Raspberry Pi と Arduino 間の通信を確認することができました。

STATUS = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1 = 0xf0f0f0f0e1 0xf0f0f0f0d2
RX_ADDR_P2-5 = 0xc3 0xc4 0xc5 0xc6
TX_ADDR = 0xf0f0f0f0e1
RX_PW_P0-6 = 0x20 0x20 0x00 0x00 0x00 0x00
EN_AA = 0x3f
EN_RXADDR = 0x03
RF_CH = 0x78
RF_SETUP = 0x25
CONFIG = 0x0f
DYNPD/FEATURE = 0x3f 0x04
Data Rate = Model = CRC Length = PA Power = Now sending length 4...Got response size=4 value=ABCD
Now sending length 6...Got response size=6 value=ABCDEF
Now sending length 8...Got response size=8 value=ABCDEFGH
Now sending length 10...Got response size=10 value=ABCDEFGHIJ
Now sending length 12...Got response size=12 value=ABCDEFGHIJKL
Now sending length 14...Got response size=14 value=ABCDEFGHIJKLMN

・・・

Xbox 360 有線コントローラ ジョイスティック(アナログスティック)修理

Xbox360有線コントローラ


久々の投稿になってしまいました。今回はXbox360のコントローラの修理にチャレンジしました。

私はあまりコンピュータゲームはしないのですが、唯一の例外がXbox360上のCALL OF DUTY というタイトル。FPS(一人称シューティングゲーム)というカテゴリに属する、戦争もののテレビゲームです。

各シーンの描写がとてもリアルにできていて、オンライン対戦ではいつも顔が真っ赤になる程熱中しています。残念なことに歳とともに反射神経もにぶくなってしまい。成績がぜんぜんよくなりません。【 くやしぃ~~ 】

ハード、ソフトとも子供から借りて使っているのですが、ついつい力が入ってコントローラがすぐだめになってしまいます。ここ何年かの間にコントローラを何個か買いなおさせられてしまいました。私の場合壊れる箇所が決まっていて、左アナログスティックの押し込みボタンが壊れてしまいます。このジョイスティックの部品交換をします。

googleで調べて

【ebay (seller waxdvd)】「XBOX 360 REPLACEMENT CONTROLLER JOYSTICK POTENTIOMETERS」 2個で $8.25
+ $4.5 shipping

【http://www.7daysget.com/】「Replacement Handle Joystick 3D for Xbox 360」 1個 $3.69
+ Registerd Air Mail $1.99 (送料無料も選べます)

左はhttp://www.7daysget.com/ 右はebay (seller waxdvd)から購入。

上記2箇所からジョイステック部品を購入しました。後でわかったのですが、写真左側の品物とよく似た部品が楽天でも売りに出ていました。

ネジをはずし分解したところ

無線コントローラのトルクスネジと違い、有線コントローラは普通のプラスネジ7個で止められています。1箇所はシールの下になっていて見つけるのに手間取りました。また、このシールをはがすと保証が無効になるようです。

開けてみてびっくりしたのですが、長年使っていたコントローラは思いのほか手垢やほこりで汚れています。ケースやボタン等、プラスチックやゴムでできている部材は水洗いをして、きれいに乾かしてあげました。一つ一つの部品は簡単にはずすことができますが、位置や取り付け方向などを覚えておかないと、後で組み立てるときにパズルの様になってしまいます。できるだけ詳細に覚えておきましょう。デジカメで写真に撮っておく事も良い方法だと思います。私はこれを怠って、かなり苦労してしまいました。

左トリガとジョイスティック部品

振動用のモータ2個のコネクタをはずします。おもりの大きさが違うところに注意します。基盤から伸びるUSBケーブルのコネクタは、はずすのが難しそうだったのでテープを貼り付けて養生をすることにしました。

左のジョイスティック部品はハンダ付けされているので、そのハンダを除去しなければならないのですが、そのためには、ちょうど干渉している左トリガをはずさなくてはなりません。写真親指のところに見えている3箇所のハンダをハンダコテで温めながら、吸い取り器で取り除きます。このトリガは再利用しますので注意深く取りはずさなくてはなりません。

取り外し後の基盤とハンダ吸取り器

ジョイスティク部分は14箇所のハンダを吸取ります。何かコツが必要なのか、私には完全には吸取ることが出来なかったようです。作業後もかすかに付着しているみたいで、素手では簡単に基盤から外すことができません。気を取り直して、ペンチでつまんで引き抜くとあっけなく外れました。トリガと違い代替部品があるので気が楽です。

ここまでくれば終わったも同然です。新規購入のジョイスティック部品やトリガをハンダ付けした後、元の通りにケースに組み立て直します。(注… 私の場合、終わったも同然の後が長かった)

修理したコントローラの操作感ですが、残念ながら純正品と同じという訳ではありませんでした。7daysget.comから購入した部品は押し込みボタンの遊びが少なく、前後左右の動きも少し柔らかいような感触です。まぁ、ぎりぎり許容範囲内と言ったところでしょうか。ebayから購入した部品は、ほぼ純正品に近い感触でした。(それでも少し違和感があります)

修理した2ヶのコントローラは子供から無償でもらうことに成功したので、マイコントローラとして活躍しています。これで当分新しいコントローラを買わずにすみそうです。

NRF24L01+ と DS18B20+と Arduinoでリモート温度計 3

さて、シリアルモニタに温度を表示させるだけではつまらないので、現在動いている自宅サーバにログを取って、その結果をweb上からグラフにして表示させてみることにします。

Kenji.Yさんが構築された素晴らしいシステムには遠く及びませんが、とにかく頑張りましょう。Ethernetシールドを使用してHTTP Getメソッドでサーバにデータをcsv形式で記録し、それを基にグラフ作成です。グラフの描写はhighstockで行うことにしました。

サーバ側のコードはphpで書きました。これは、Mapo堂さんの記事を参考にさせていただきました、ありがとうございます。現在私のところのPHPは Version 5.3です。

tempera_log.php
[PHP]

[/PHP]
動作確認のためにブラウザから

http://サーバアドレス/パス/tempera_log.php?tempera=1234.5

などとアドレス欄に入力してtempera_log.csvが作成され、数値が記録されるか確かめます。私の場合データの投稿はLAN内からのアクセスですので、外部からこのファイルにアクセスできないようにアクセス制限を掛けておきました。もし将来仮に、インターネット側からデータを送らなければいけないときは、安全性を考慮して別の手立てをしなければならないでしょう。

ログファイルの置かれているディレクトリはwebサーバを動かしているユーザーから書き込む事ができるようにアクセス権を設定します。私はいつもこのことで失敗してしまいます。(学習能力がまるで無い)

続いて、先日作成したRx_data.inoにwebサーバアクセス用のコードを付け加えます。 Arduino 1.0 の Examplesにあるwebclientスケッチを元に作成しました。

Rx_data_log.ino ( Rx_log_data.zip )

Rx_data.ino スケッチの冒頭に追加します

[C]
#include

// Ethernet configration

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //ほとんどの場合macアドレスはこのままで大丈夫です
byte ip[] = { 192, 168, 1, 201 }; //イーサネットシールドのIPアドレス:LAN環境に合わせて設定変更が必要です
byte gateway[] = { 192,168,1,1 }; //デフォルトゲートウエイアドレス: 同上
byte subnet[] = { 255, 255, 255, 0 }; //サブネットマスク: 同上

IPAddress Logging_server(192,168,1,100); //logを記録するwebサーバのIPアドレス

EthernetClient client;   // イーサーネットクライアントライブラリ をイニシャライズ
[/C]

void loop(void)内の最後に追加します

[C]
// start the Ethernet connection:
Ethernet.begin(mac, ip ,subnet,gateway ); //subnet,gatewayの順番はArduino1.0のためのものです(バグ?)
delay(1000); // イニシャライズの為のディレイです

if (client.connect(Logging_server, 80)) {
Serial.println(“connected to Logging server”);

client.print(“GET /cgi-bin/tempera_log.php?tempera=”); // /cgi-bin/tempera_log.phpへ HTTP GETリクエストです:
client.print( got_data);
client.println(” HTTP/1.0″);
client.println();
}
else {
Serial.println(“Logging server connection failed”);
}
client.stop();
[/C]

さて最後に一番手こずったのがグラフ描写です。今回試したhighstockは以前使ったRRDtoolsとは違いJavaScriptで書かれていて、クライアント側でグラフを描くものです。

Highsoft社が開発元で、個人利用であればフリーで使うことができます。また、姉妹版のhighchartsというライブラリもあって、豊富な種類かつ表現豊かなグラフを描くことができます。highstockは時系列のデータに対して相性が良いようです。

こちらからライブラリをダウンロードして適切なディレクトリに解凍して設置します。

ドキュメントを参考にしてプログラムを組んだのですが、CSVファイルの取り込みがどうしてもうまくいかなくて苦労しました。サポートフォーラムの中を必死に探してようやく動かすことができました。でも、ひょっとすると、もっと根本的な恥ずかしい間違いを犯しているかもしれません。

このソフトに関しては日本語の資料があまりなく、へっぽこメモさんのwebに助けていただきました。ありがとうございます。

highstock.jsとtempera_log.csvのファイルのパスに注意します。文字コードセットをUTF8で保存します。

temperature.html





Highstocks Example






こちらにグラフサンプル


NRF24L01+ と DS18B20+と Arduinoでリモート温度計2

無事DS18B20+センサを使って温度を計測することができましたので、NRF24L01+無線モジュールをつかって無線でデータを飛ばすことにします。 送り手側は電池駆動で試してみました。きっと、将来他の事例にも応用が利くと思います。

今回の実験のためにもう一台Arduino互換機を作る事にしました。秋月電子からATMEGA168/328用マイコンボード(1枚150円)、水晶振動子、抵抗、コンデンサ、ピンソケット等、必要最小限の部品を購入して組み立てました。USBインターフェースと電源レギュレーターは省略します。

ATmegaマイコンボードとAVRISP MkII

この基盤にはICSP端子がついていますので、電源とAVRプログラマAVRISPmkII を繋ぐとArduino1.0 IDEからブートローダとスケッチ双方が書き込めますので大変便利です。

さらに秋月電子からは 昇圧型DC-DCコンバーター(3.3Vタイプ)を仕入れました。入力電圧が0.7~3.3V出力電圧が3.3V最大出力電流が200mAという性能です。広告には電解コンデンサを1個追加するだけで使えると書いてありました。

センサー、無線モジュールの他にMCUの電源も DC-DCコンバータから供給するように配線します。

送信側ハードです

ハードは整いました。ソフトウエアはJ. Colizさんが書かれた RF24ライブラリにバンドルされている「pingpair_sleepy」スケッチを基にして作りました。この「pingpair_sleepy」は電池駆動のために消費電力を抑えるAtmegaのスリープモードとウオッチドッグタイマを利用しています。

ATmegaのスリープモードは

SLEEP_MODE_IDLE  アイドルモード
SLEEP_MODE_ADC   ADCノイズ低減モード
SLEEP_MODE_PW_SAVE パワーセーブモード
SLEEP_MODE_STANDBY スタンバイモード
SLEEP_MODE_PWR_DWN パワーダウンモード

5種類あって、パワーダウンモードが一番消費電力が少ないモードになっています。また、スリープモードから復帰するためにWatchdog(WDT)タイマーを使って割り込みを発生させます。

ウオッチドッグタイマーは、スリープモード中でも動いている内蔵128KHz発信器のクロックをカウントして指定のタイムアウト時に割り込み又はシステムリセットを行う機能を持ちます。

タイムアウト時間は16ms,32ms,64ms,128ms,250ms,500ms,1s,2s,4s,8sの中から選ぶことができまして、またそれをループさせることにより任意の間隔で一連の処理を行うことができます。

「pingpair_sleepy」スケッチでは setup_watchdog()のパラメータでタイムアウト時間を指定し、sleep_cycles_per_transmissionの値の倍数の期間だけスリープモードを繰り返すように組んであります。ただ、内蔵発信器のクロックなので時間の精度はあまり良くないようです。ATmega MCU内部動作の詳細はデータシートの訳 こちら を参考にさせていただきました。awawaさんありがとうございました。

スリープモードの時間はいろいろな値を設定することができますが、現在私は setup_watchdog(wdt_4s)そして sleep_cycles_per_transmission = 75として、5分に一回データを送るように設定しました。

以下にarduino1.0用に作成したスケッチを記述します。オリジナルから変更点は、温度センサーの値を読み込む処理、送受信役割分担(ロールピン)の部分を削除した所です。私のハードがNRF24L01+無線モジュールのCE,CSNがそれぞれデジタル8,9ピン、DS18B20+温度センサのDQがデジタル6ピンにつががっていますので、スケッチの該当する部分もそれに合わせてあります。温度センサの解像度は10bitに設定してあります。

送信側スケッチ Tx_data.zip
受信側スケッチ Rx_data.zip

このスケッチを動かすためには前述のOneWireライブラリRF24ライブラリをインストールする必要があります。

送信側と受信側双方をセットして動かすとシリアルモニターに温度が無事表示されました。シリアルモニタの通信速度はNRF24L01+無線モジュールの関係で57600bpsになっています。

実際に回路の電流を測ってみると、待機時に流れる電流が約0.2mAでした。どのくらい電池が持つか計算してみましょう。こちらの記事を参考にさせてもらいました。

スケッチが動いているときの消費電流は

 ATmega328P-PU         20mA
 NRF24L01+ モジュール   11mA
 DS18B20+  センサ        2mA
 DC-DCコンバータ         6mA (変換効率が85%と言うことで)
 
 計      おおむね 40mA
 

スケッチが動いている実働時間は2秒程度、待機している時間が約300秒(5分)なので平均の消費電流は

(2s/300s)*40mA+0.2mA ≒ 0.5mA

単三電池の容量が2000mAh程度なので

2000mAh÷0.5mA = 4000h(時間) ≒ 166日

計算では結構長く電池が持ちそうです。いずれこの期間についても検証してみたいと思っています。

※ 上記のハード・ソフトで 2012年3月3日 から 2012年8月17日まで、電池交換なしで5.5ヶ月間動き続けました。8月17日に落雷が原因で受け取り側のPC(サーバ)が不調にならなければ、もっと長い期間の記録になったはずでした。

NRF24L01+ と DS18B20+と Arduinoでリモート温度計 1

NRF24L01+を使って無線実験が成功したので、実用的な用途に応用です。本当は家の中の消費電力を計測したいのですが、他の方の製作記事を拝見すると結構大変そうです。今の私には少々荷が重すぎるようですね。なので、すかさず方針変更です。手始めに気温を測る温度計に挑戦です。これなら何とかいけそうです。

でも、一旦冷静になって考えると”気温を測ってどうするの?”という素朴な疑問がわいてきました。「実用的な用途」と言えるかどうかも論議が必要です….結局、結論がなかなか出てきそうにないので、先に進むために今そのことは考えないことにします。

温度を測るセンサーもいろいろあって迷いますが、秋月電子の広告を見て「1wireデジタル温度センサー」という文字が目にとまりました。値段も1つ300円とお手頃です。

Maximの1wireデジタル温度センサー DS18B20+
=特長=
・1wireインターフェースのシンプルなデジタル温度センサー
・すべてのデバイスに対してユニークな64ビットのシリアルコードを付与(内部ROMに書込済)
・データ線から電源を供給可能
・電源電圧:3.0~5.5V
・測定温度範囲:-55℃~+125℃
・精度:±0.5℃(-10℃~;85℃)
・ドリフト:±0.2℃

・・・

他の温度センサーと比べて精度が良いみたいですね。ネットで検索するとO-Famiry 電子工作の部屋さんのWeb中にDS18B20+の説明が書かれていました。またマニュアルの日本語訳もされています。すごいですね、頭がさがります。ありがとうございます。調べて行くとArduinoとの相性も良さそうな事がわかりました。これに決めます。

ArduinoでDS18B20+を使うためにPJRCさんのサイトから
OneWire Library:Download: OneWire.zip (Version 2.1)
このライブラリをダウンロードします。

zipファイルを解凍してできる「OneWire」フォルダをフォルダごとAduino1.0の「libraries」ディレクトリにコピーすると使えるようになります。

まずArduino単体で温度が読み取れるか試してみましょう。ブレッドボードにDS18B20+を配線します。このOnWireデバイスは、一つ一つのデバイスに64ビットのアドレスが割り振られているとのことです。ワンワイヤーと言っても現実には通信に2本の線が必要(パラサイトモード時)なのですが、カスケードにして複数つなぐことができるそうです。トランジスタ風のパッケージの中に、機能がぎっしり詰まっていますね。感心してしまいます。

今回私は、DS18B20+の2番ピン(DQ)はArduinoのデジタル6番ピンにつなぎました。ブレッドボードに配線をすませて、ライブラリ付属のサンプルスケッチ「DS18x20_Temperature」を実行します。

スケッチ中の
OneWire ds(10); // on pin 10
この行は、自分の環境に合わせ、6に変更しておきました。これでシリアルモニターに現在の気温が表示されるはずです。

スケッチを走らせると瞬時に気温が計測され、表示されます。センサーを指で触ると体温に反応して表示される温度が刻々と変化します。応答性能もよさそうです。

ちなみに配線を間違えると85℃の表示がされ、そこから微動だにしません。(なぜこのことを知っているかは秘密です)資料を見ると85℃はパワーオンリセット時にセットされるデフォルト値みたいです。配線には気をつけましょう。壊れなくて良かったです。

先ほどのPJRCさんのwebに、このスケッチには氷点下以下が測れないというBugがあると書かれていましたので、このスケッチを修正してみました。とりあえず接続するDS18B20+は1つなので、アドレス指定の部分は削除してしまいました。
[C]
#include
// OneWire DS18B20 Temperature Reading
// only slightly modified DS18x20_Temperature sketch
// from OneWire library examples bundle.
// http://www.pjrc.com/teensy/td_libs_OneWire.html
//

OneWire ds(6); // on digital pin 6 DS18B20+ (DQ)
const short Resolution = 12; //DS18B20 Resolution 9 – 12 Bit

void setup(void) {
Serial.begin(9600);

// Thermometer Resolution set (default 12 BITS)

short Sw_value ;
switch (Resolution){
case 9:
Sw_value = 0x1F;
break;
case 10:
Sw_value = 0x3F;
break;
case 11:
Sw_value = 0x5F;
break;
default:
Sw_value = 0x7F;
break;
}
ds.reset();
ds.skip();
ds.write(0x4E); // Write Scratchpad
ds.write(0x00); // User Byte 1 (not in use)
ds.write(0x00); // User Byte 2 (not in use)
ds.write(Sw_value); // set Thermometer Resolution

}

void loop(void) {
byte i;
byte data[12];
float celsius;

ds.reset();
ds.skip();
ds.write(0x44,1); // start conversion, with parasite power on at the end

int Conv_time = 1000;
switch (Resolution ){
case 9 :
Conv_time = 100;
break;;
case 10 :
Conv_time = 200;
break;
case 11 :
Conv_time = 400;
break;
}
delay(Conv_time); // Resolution to 9 BITS conversion time 93.75ms
// 200 : Resolution to 10 BITS conversion time 187.5 ms
// 400 : Resolution to 11 BITS conversion time 375 ms
// 1000 : Resolution to 12 BITS conversion time 750 ms
// we might do a ds.depower() here, but the reset will take care of it.

ds.reset();
ds.skip();
ds.write(0xBE); // Read Scratchpad

for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } if (OneWire::crc8(data,8) != data[8]) { Serial.println("CRC is not valid!"); return; } // convert the data to actual temperature int raw = (data[1] <<8) | data[0]; switch ( Resolution){ case 9: raw = raw & 0xFFF8; // 9 bit resolution break; case 10: raw = raw & 0xFFFC; // 10 bit resolution break; case 11: raw = raw & 0xFFFE; // 11 bit resolution break; } celsius = (float)raw / 16.0 ; Serial.print(" Temperature = "); Serial.print(celsius); Serial.println(" C "); } [/C] ちゃんと動くか否か心配なのでテストをすることにしました。昨日まで冷え込んでいたので夜明け前なら外気温は氷点下なると見込んで測ってみました。が、残念ながらあとわずかの所で氷点下になりません。2~3日繰り返しましたが徒労に終わりました。このまま待っていると来冬になってしまいそうなので、DS18B20+を簡単なプローブにして氷と塩を使い測ってみました。まるで理科の実験です。小学校3年生以来ですね。 解像度( Resolution)は9,10,11,12ビットのいずれかで設定することができ、それぞれ0.5℃,0.25℃,0.125℃,0.0625℃単位で測ることができます。設定をしないと12ビットになります。解像度が大きくなるほど温度をデジタルデータに変換する時間がかかりますので、電池駆動で消費電力を少なくしたい時は解像度を低く抑えた方が良いかもしれません。

nRF24L01+無線モジュール SCANNER

nRF24L01L+には電波の強度を計測する機能(RPD)があって、RF24ライブラリ作者のMr. J.Colizさんが2.4GHz帯(ISMバンド)の電波利用状況を計測するスケッチを書いてくれました。RF24ライブラリをインストールした後、[Examples]-[RF24]-[scanner]でロードできます。

ArduinoにnRF24L01L+無線モジュールをセットし、スケッチを走らせるとシリアルモニタには下記のように表示されます。

RF24/examples/scanner/に続く2行はチャンネル(RF_CH)を表示しています。このチャンネルは縦に読むのがミソで、十六進数で00~7fの間、2.4GHz帯の1MHzごと、128CH分が表示されています。次の行からはスキャンした時点で使われている電波の強度を表示しています。

チャンネルと周波数の関係式 : F = 2400 + RF_CH [MHz] (中心周波数)

WindowsPC用のソフトウエアで無線LANの電波利用状況を調べるNetwork Stumblerというものがあり、同時に計測してみました。無線LANも802.11 b/g/n規格の物は2.4GHz帯を使用します。

私の所は郊外で、家も少ない訳なのですが、それでも結構電波が飛んでいますね。一番下の行が私の使っている無線ルータです。無線LANの11CHを使っていて、この中では一番シグナルが強いです。ややっこしいのですが、無線LANのチャンネルは上記のチャンネルとは異なります。

2.4GHz帯の無線LANの電波の割り当ては、第1CHの中心周波数が2412MHzで、それ以降は5MHzずつ中心周波数が高くなり13CHまで使われています。ただ、使う帯域は22Mhzですので隣どおしのchはかなり重なってしまいます。確か、全く周波数が重ならないように802.11 b/gの無線LANを使うには、3つの組み合わせしかなかったと思います。

上記無線LANの11chは中心周波数が2462MHzですので、2451MHz~2473Mhzを使っている事になります。これは、ちょうどシリアルモニタに表示されたピンクのアンダーラインの所に該当します。双方を比較してみると、Arduinoで計測した方が明らかに詳しく測ることができますね。

nRF24L01+のデータシートを見るとnRF24L01+の占有する周波数の帯域は通信速度によりますが、250kbps と 1Mbpsで1MHz、2Mbpsで2MHz 程度だそうです。設定できる範囲は1Mhz刻みで2.400GHz から 2.525GHzと書いてあります。

このスケッチを使って無線LANや他の電波と干渉しないようにnRF24L+をセットする事ができます。ちなみにこのRF24ライブラリはデフォルトで76CH(0x4C)、2476MHzを使うようになっています。