照度センサを使ってみた

今回は照度センサを使ってみます。

明るさを測る方法のひとつにフォトレジスタを使うものがあります。フォトレジスタは光を入射すると電気抵抗が変化する素子です。フォトレジスタには様々な種類がありますが、以前はCdS(硫化カドミウムセル)といわれるものがよく使われていました。CdSは「抵抗値の変化の幅が大きい」、「反応する周波数が広い」などの利点がありましたが、カドミウムがRoHS指令の規制対象元素になったため、あまり使われなくなりました。

現在フォトダイオードやフォトICなどの代替品が各社から販売されていますが、今回はCdSの代わりとして使いやすいパナソニック製NaPiCa AMS302を使いました。千石電商で入手できます。
NaPiCaはフォトダイオードのパッケージに増幅回路を内蔵しており、人間の視感度に近い特性を持っているのが特徴です。印加電圧は1.5~6V、ピーク波長は580nmです。周囲の明るさに比例した光電流がリニアに出力されます。

DSC01510s
回路を組んでみました。写真のブレッドボード上の透明なLEDのような形をした素子がAMS302です。AMS302は上がアノード、下の切り欠きのある側がカソードです。アノードのほうが足が長くなっています。抵抗をアノードとGNDの間につなぐことにより抵抗とアノードの間から出力(電圧)が得られます。今回は330Ωの抵抗をつなぎました。RaspberryHabuBasic側はアナログ入力0ピンを使っています。

 // napica.c sample program for RaspberryHabu
 // Licence CC 0 ( Public Domain )
 
 #include "stdio.h"
 #include "stdlib.h"
 #include "wiringPiSPI.h"
 #include "unistd.h"
 #define SPIBUFFSIZE 256
 unsigned char spidata[SPIBUFFSIZE];
 int MCP3008Read( int channel ){
   int spireturn,data;
   spidata[0]= 1; 
   spidata[1]= ( 0x80 | channel << 4 )  ;
   spidata[2]= 0;
   spireturn = wiringPiSPIDataRW( 0, spidata , SPIBUFFSIZE );
   data = ((spidata[1]&3) << 8) + spidata[2];
 return(data);
 }

int main()
{
  int spiresult;
  if (-1 == wiringPiSPISetup(0,1000000))
    {
      printf("SPI setup error!");
      exit(1);
    }
  while(1){
    spiresult=MCP3008Read(0);
    printf("channnel 0 : %d\n", spiresult);
    sleep (1);
  }
}

AD変換された10bitデータがそのまま表示されるコードです。1から1024の数字が出ます。暗いと小さくて明るいと大きい数字になります。

DSC01511通常時です。すこし暗めですが、数値は110付近ですね。

DSC01512センサを手で覆ってみました。数値は15付近。だいぶ暗いです。

DSC01514白いLEDで照らしてみました。数値は250付近でした。

このセンサを使えば明るさの変化に対応したシステムが組めます。
今後、いろいろ使って紹介していきたいと思います。

温度計作ってみた

今回は熱電対を使って温度計を作ってみます。

異なる2種類の金属線をつないで、二つの接した部分に温度差を与えると電圧が発生します。この現象をゼーベック効果といいます。熱電対とはこの現象を利用したセンサで、一端の温度が分かれば発生する電圧と既知の金属の特性からもう一方の接点の温度を求めることができます。

絶対的な温度を計る場合は熱電対の測定に使う接点を温接点、端子側を基準接点とし、その温度(基準接点温度)を測り測定点の温度を求める必要があります。基準接点温度を測定するときはサーミスタや測温抵抗体などを使います。

試作には秋月電子で購入したK型熱電対ステンレス管タイプを使いました。この熱電対は-200℃~+600℃の広範囲の温度を測定することができます。温度差1℃当たり約40.7μVの起電力が発生します。

RaspberryHabuのアナログポートに入力するには電圧変化が小さすぎるのでオペアンプで増幅します。一般的に信号を増幅するためによく使われるLM358では誤差が大きすぎるので今回は精度の高いオペアンプNJM2119Dの1回路分を使い、1kΩと100kΩの抵抗で100倍に増幅し、RaspberryHabuのアナログ1番ピンに入力しました。NJM2119Dは秋月電子で手に入ります。

2V2A6612

熱電対の端子は商品では樹脂の端子がついていますが、ブレッドボードに指すために分解してあります。ケーブルは単芯なので被膜を取るだけでブレッドボードにさせます。
写真は2か所の温度を計るため熱電対を2つ使い、2チャンネル分の回路を作っています。

熱電対の出力はアナログなのでRaspberryHabuBasicのアナログ入力ピンからMCP3008を通してSPIでRaspberryPiに入力します。
以下のコードはサーミスタも使い室温を出したうえで熱電対で測定した結果の温度差を足して計測対象の温度を出しています。

 // thermocouple.c sample program for RaspberryHABU
 // Licence CC 0 ( Public Domain )
 
 
 #include "stdio.h"
 #include "stdlib.h"
 #include "wiringPi.h"
 #include "unistd.h"
 #define SPIBUFFSIZE 256
 #define THERMISTORCHANNEL 7
 #define THERMISTOR 10000 // 10k ohm
 #define B 3380 // 
 #define DIVR 10000 // 10k ohm
 
 unsigned char spidata[SPIBUFFSIZE];
 int MCP3008Read( int channel ){
   int spireturn,data;
   spidata[0]= 1; 
   spidata[1]= ( 0x80 | channel << 4 )  ;
   spidata[2]= 0;
   spireturn = wiringPiSPIDataRW( 0, spidata , SPIBUFFSIZE );
   data = ((spidata[1]&3) << 8) + spidata[2];
 return(data);
 }
 
 int main()
 {
   int spiresult,spiresult7;
   double thermistor_r,baseTemp,TCTemp,t1;
   if ( -1 ==  wiringPiSPISetup(0,1000000) )
   {
     printf("SPI setup error!");
     exit(1);
   }
   while (1){
     spiresult7=MCP3008Read(THERMISTORCHANNEL);
     thermistor_r = ( DIVR * spiresult7 )/ (1024 -spiresult7 );  // thermistor is lower
     t1 =   log( thermistor_r / THERMISTOR ) ;
     baseTemp = ((298*B) / ( B + ( 298 * t1 ))) - 273;
   
     spiresult=MCP3008Read(0);
     TCTemp = (3.3/1024.0*(double)spiresult*1.010)/0.00407;
 
     printf("temp: %3.f c  (room temp: %3.f c : A0 value0 %d)\n",TCTemp+baseTemp,baseTemp,spiresult);
     sleep(10);
   }
 
 }

入力した値はAD変換され10bitのデータとしてRaspberryPiに入力されます。その値は3.3Vを10bit(1024)分割された値なので1Vあたりの数値を出し、それを40.7μVで割ることで温度差を出しています。NJM2119Dは5V電源を試用していますが、出力は4V程度までしか出ないため、1.01をかけて補正してあります。

上記のプログラムとは出力が違いますが、別の実験で熱電対と基準接点温度を測定するためにサーミスタを使ったときの出力結果は以下のようになりました。この実験ではコップのお湯の温度を測ってみました。

IMG_20140326_141717

今回使ったK型熱電対は対温度性が1250℃(樹脂の端子カバーを残して使う場合は600℃)と高いのでオーブン作ったりリフロー機作ったりできそうですね。

キャラクタ液晶使ってみた

Arduinoでも良く使われるキャラクタ液晶モジュールをRaspberryPiで使ってみました。
今回使ったのは台湾サンライク社(SUNLIKE)液晶モジュールSD1602HULB-XA-G-Gです。若松通商でGR-SAKURA用として販売されています。秋月電子でも販売されています。
16文字x2行の液晶でLEDバックライトも内蔵しています。

この液晶モジュールに内蔵されているインターフェースICはHD44780互換になっています。HD44780は、80年代前半に日立製作所(現ルネサス エレクトロニクス)から発売されたキャラクタ液晶用のコントローラICです。現在、ほとんどのキャラクタ液晶モジュールでHD44780またはその互換ICが使用されています。HD44780は「MPUにバス接続してコマンドによる制御が可能」「80文字(40桁×2行)分の表示データ・バッファを内蔵」「キャラクタ・ジェネレータを内蔵」「LCDドライバを内蔵しLCDパネルを直結駆動可能」などの特徴があり、カナや外字の表示も可能です。

この液晶インターフェースを動かすにはシンク電流が流れるようにしておく必要がありますが、RaspberryHabuのTD62783AGPを通すとシンク電流が流れないため、基板上のRaspberryPiのピンから直結されているポートにコネクタをつけます。
DSC01441s
この液晶にデータを書き込む方法は4bitモードと8bitモードがありますが、今回はピン数を節約するため4bitモードで使います。4bitモードでは8bitの信号を上位4bit下位4bitの2回に分けて書き込みます。
ピンを液晶にはんだづけしてブレッドボードに差し、以下のように繋ぎます。
DSC01446s

左下の半固定抵抗は10kΩでコントラスト調整用です。だいたい3kΩくらいが見やすい値でした。液晶側15、16ピンはバックライト用で15ピンはVcc、16ピンは330Ωの抵抗を通してGNDにつなぎます。

/*
lcdout.c
Lisence CC0.
*/
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "wiringPi.h"

#define RS 4
#define E 6
#define ESETUPTIME 10
#define NEXTTIME 50
#define EPULSETIME 60

int WriteLCDLCommand( int command ){
  int i,pair=0x80;
  usleep(NEXTTIME);
  digitalWrite(RS,LOW);
  for ( i = 3; i >= 0 ; i--){
    if ( 0 < ( pair & command ) )
    {
      digitalWrite(i,HIGH);
    } else {
      digitalWrite(i,LOW);
      }
    pair = pair / 2;
    }
  usleep(ESETUPTIME);
  digitalWrite(E,HIGH);
  usleep(EPULSETIME);
  digitalWrite(E,LOW);
  usleep(ESETUPTIME);
  for ( i = 3; i >= 0 ; i--){
    if ( 0 < ( pair & command ) )
    {
  digitalWrite(i,HIGH);
    } else {
      digitalWrite(i,LOW);
      }
    pair = pair / 2;
    }
  usleep(ESETUPTIME);
  digitalWrite(E,HIGH);
  usleep(EPULSETIME);
  digitalWrite(E,LOW);
  usleep(ESETUPTIME);
  }

int WriteLCDLData( int data ){
  int i,pair=0x80;
  usleep(NEXTTIME);
  digitalWrite(RS,HIGH);
  for ( i = 3; i >= 0 ; i--){
    printf("%d %d %d:",data,pair,i);
    if ( 0 < ( pair & data ) )
      {
      digitalWrite(i,HIGH);
      printf("1\n");
      } else {
        digitalWrite(i,LOW);
        printf("0\n");
        }
    pair = pair / 2;
    }
  usleep(ESETUPTIME);
  digitalWrite(E,HIGH);
  usleep(EPULSETIME);
  digitalWrite(E,LOW);
  usleep(ESETUPTIME);
  for ( i = 3; i >= 0 ; i--){
    printf("%d %d %d:",data,pair,i);
    if ( 0 < ( pair & data ) )
      {
      digitalWrite(i,HIGH);
      printf("1\n");
      } else {
      digitalWrite(i,LOW);
      printf("0\n");
      }
    pair = pair / 2;
    }
  usleep(ESETUPTIME);
  digitalWrite(E,HIGH);
  usleep(EPULSETIME);
  digitalWrite(E,LOW);
  usleep(ESETUPTIME);

  }

  int WriteLCDCommand( int command ){
  int i;
  digitalWrite(RS,LOW);
  for ( i = 0; i < 4 ; i++){
    if ( 0 > command % 2 )
      {
      digitalWrite(i,HIGH);
      } else {
      digitalWrite(i,LOW);
      }
    command = command / 2;
    }
  usleep(ESETUPTIME);
  digitalWrite(E,HIGH);
  usleep(EPULSETIME);
  digitalWrite(E,LOW);
  usleep(ESETUPTIME);

  }

  int WriteLCDData( int data ){
  int i;
  digitalWrite(RS,HIGH);
  for ( i = 0; i < 4 ; i++){
  if ( 0 > data % 2 )
    {
    digitalWrite(i,HIGH);
    } else {
    digitalWrite(i,LOW);
    }
  data = data / 2;
  }
  usleep(ESETUPTIME);
  digitalWrite(E,HIGH);
  usleep(EPULSETIME);
  digitalWrite(E,LOW);
  usleep(ESETUPTIME);

  }

int main()
  {
  wiringPiSetup();
  pinMode(0,OUTPUT);
  pinMode(1,OUTPUT);
  pinMode(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  /*
  WriteLCDCommand( 0x02 );
  WriteLCDCommand( 0x02 );

  WriteLCDCommand( 0x00 );
  WriteLCDCommand( 0x08 );

  WriteLCDCommand( 0x00 );
  WriteLCDCommand( 0x01 );

  WriteLCDCommand( 0x00 );
  WriteLCDCommand( 0x04 );

  WriteLCDCommand( 0x07 );
  WriteLCDCommand( 0x00 );

  WriteLCDData( 0x08 );
  WriteLCDData( 0x00 );

  WriteLCDData( 0x03 );
  WriteLCDData( 0x03 );

  usleep(NEXTTIME);

  WriteLCDCommand( 0x03 );
  WriteLCDCommand( 0x03 );

  usleep(NEXTTIME);

  WriteLCDCommand( 0x03 );
  WriteLCDCommand( 0x02 );

  usleep(NEXTTIME);

  WriteLCDCommand( 0x02 );
  WriteLCDCommand( 0x08 );

  usleep(NEXTTIME);
  WriteLCDCommand( 0x00 );
  WriteLCDCommand( 0x0C );

  usleep(NEXTTIME);
  WriteLCDCommand( 0x00 );
  WriteLCDCommand( 0x06 );

  usleep(NEXTTIME);
  WriteLCDCommand( 0x00 );
  WriteLCDCommand( 0x01 );

  usleep(NEXTTIME);
  usleep(2000);

  WriteLCDCommand( 0x08 );
  WriteLCDCommand( 0x00 );

  usleep(NEXTTIME);
  WriteLCDData( 0x03 );
  WriteLCDData( 0x03 );

  usleep(NEXTTIME);
  WriteLCDData( 0x03 );
  WriteLCDData( 0x04 );

  usleep(NEXTTIME);
  WriteLCDData( 0x03 );
  WriteLCDData( 0x05 );

  usleep(NEXTTIME);
  WriteLCDData( 0x03 );
  WriteLCDData( 0x06 );

  usleep(NEXTTIME);
  WriteLCDData( 0x03 );
  WriteLCDData( 0x07 );

  usleep(NEXTTIME);
  WriteLCDData( 0x03 );
  WriteLCDData( 0x08 );

  usleep(NEXTTIME);
  WriteLCDData( 0x03 );
  WriteLCDData( 0x09 );

  usleep(NEXTTIME);
  WriteLCDData( 0x03 );
  WriteLCDData( 0x0A );

  WriteLCDLCommand( 0x33 );
  WriteLCDLCommand( 0x32 );
  WriteLCDLCommand( 0x28 );
  WriteLCDLCommand( 0x0C );
  WriteLCDLCommand( 0x06 );
  WriteLCDLCommand( 0x01 );
  WriteLCDLCommand( 0x80 );

  WriteLCDLData( 0x32 );
  WriteLCDLData( 0x33 );
  WriteLCDLData( 0x34 );
  WriteLCDLData( 0x35 );
  WriteLCDLData( 0x33 );
}

WriteLCDLCommandでLCDの初期設定やモードなどを書き込み、WriteLCDLDataで表示する文字のデータを2回に分けて書き込みます。

DSC01437ss
出力結果です。WriteLCDLDataで書き込まれた数字が表示されています。

LCD表示ができるとセンサで計測した数値をリアルタイムで確認したり本体の動作の確認に使えたりいろいろ便利そうですね。

ジャイロセンサを使ってみた

今回は村田製作所のジャイロセンサを使います。
秋月電子で販売されているアンプ内蔵のモジュールを使います。

このモジュールは電源、接地、アナログ出力2本の計4本の線をつなぎます。アナログ信号はRaspberryHabu上のアナログ入力3,4につなぎました。

基本はSPIからMC2008を通して入力します。以下のようなコードを書きました。

無題

実行するとブザーが鳴ります。このブザーはジャイロの数値によって音が変わるようにプログラムしました。下の出力例の一番右の数字がブザーの出力値です。

出力例

これで傾きの動きを検知しながらその情報をもとに音を出す装置ができます。オリジナル楽器などが作れそうですね。

RaspberryHabu + PIRセンサモジュール(その2)

前回の続きでPIRセンサをいじっていきます。

まずはWiringPiをRaspberryPiに入れましょう。RaspberryHabuの組み立てTogetterにて動作確認のために入れていれば問題ないです。
そして簡単なデジタルINのプログラムを書いてみます。ピンは1番です。

pirtest-c

特に目立ったことはしてないです。digitalRead(pin番号)という関数がWiringPiでデジタル入力信号を取ってくる関数ですね。1秒ごとにHighで1、LOWで0が返ってきます。

cc -o プログラム名 ファイル名 -lwiringPi
でコンパイルして実行すると

pirtest-offpirtest-on

こうなります。人が止まっているときやいないときはセンサからLOWが返ってくるのでOFFと表示されます。動いたらセンサからHIGHが返ってきてONと表示されています。写真ではわかりにくいですが、僕はOFFになるまでじっと待っていました。センサの調整ねじを回せば同じ信号を出し続ける時間が長くなります。

このプログラムはHIGHやLOWだけを出すようなほかのセンサでも応用できるので便利です。
スイッチにも使えますね。すでに実装されてますが。