SSブログ

自作プロトシールド [Arduino]

Arduino用のユニバーサル基板ってだいたい600円くらいします。
最近は見かけませんが、以前にはヤフオクで300円くらいのものを見たこともあります。

サンハヤト株式会社 製品情報-Detail 「Arduino用ユニバーサル基板 UB-ARD01」
http://www.sunhayato.co.jp/products/details.php?u=1481&id=07017

なぜ自作できないかというと、デジタル7番と8番の間のピッチが1.27mmずれているからで、この問題を解決するため 1.27mmピッチの基板を使えばいいと思いました。
しかし、通常のピンヘッダーは太くて、この基板に刺さりません。
そこで、細ピンヘッダというものがあったので、これをつかってみました。

両面スルホール・ガラス・ユニバーサル基板Cタイプ1.27mmピッチ(72x48mm): パーツ一般 秋月電子通商 電子部品 ネット通販:
http://akizukidenshi.com/catalog/g/gP-00186/
細ピンヘッダ1x20: パーツ一般 秋月電子通商 電子部品 ネット通販:
http://akizukidenshi.com/catalog/g/gC-04398/

universalshield1.jpg
universalshield2.jpg
こんな感じです。

ただ、細ピンヘッダで、さらに短めです。
ぼくのArduino Duemilanove は、アナログピンのソケットがガバガバになっているので接触不良気味でした。

ピンソケット1x6(6P): パーツ一般 秋月電子通商 電子部品 ネット通販:
http://akizukidenshi.com/catalog/g/gC-04045/
ピンソケット1x8(8P): パーツ一般 秋月電子通商 電子部品 ネット通販:
http://akizukidenshi.com/catalog/g/gC-04046/
Arduinoスタック用コネクタ / ASC6P8Px2-SET ノーブランド / B1R111
http://eleshop.jp/shop/g/gB1R111/
Arduinoスタック用コネクタ / PRT-10007 SparkFunElecronics / AB5119
http://eleshop.jp/shop/g/gAB5119/

これらもので足の細いものがあれば、、、購入して試してみようと思います。




日本語であそぼう [Arduino]

シリアルモニターで漢字を入力すると、シフトJISでArduinoにコードが送られます。
そこで日本語を処理するためには、シフトJISを恵梨沙フォント上での位置、つまりJISコード順で未使用領域を除いたものに変換しなければなりません。
また、半角文字は恵梨沙フォントに収載されていないので、別にフォントを用意する必要がありますが、用意できなかったので半角英数記号を全角に変換することで対応しました。(半角カナは使用しないことにしました。)

#include <Wire.h> //I2C library

byte buf[8];

void setup() 
{
  Wire.begin(); // initialise the connection
  Serial.begin(9600);
  delay(10); //add a small delay
}

void loop() 
{
  unsigned int sjisH, sjisL;
  signed int elisa;
  while( !Serial.available() );   // なにか文字がくるまで待機
  sjisH = Serial.read();          // 1byte 読み込む
  elisa = asciiToElisa ( sjisH );  // ASCII文字 → ELISAでの位置
  if( elisa == -1 ) {              // シフトJIS上位1バイトなら漢字を表示
    while( !Serial.available() );   // 下位1バイトが届くのを待つ
    sjisL = Serial.read();
    elisa = sjisToElisa( (sjisH << 8) + sjisL );
    if( elisa == -1 ) return;        // 対応文字コードがなければ終了
  } else if( elisa < -1 ) return;    // 制御コード、半角カナなどなら終了
  i2c_eeprom_read_buffer( 0x50, elisa * 8, buf, 8 );
  for(int i=0; i<8; i++) {
    for(int j=7; j>=0; j--) {
      Serial.print( bitRead(buf[i], j) ? "**":"  " );
    }
    Serial.println("");
  }
  Serial.println("");             // 1文字毎に1行あける
}

  // maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress,length);
  int c = 0;
  for ( c = 0; c < length; c++ )
    if (Wire.available()) buffer[c] = Wire.receive();
}

signed int sjisToElisa(unsigned int sjiscode) {    // シフトJISからELISAフォント上の位置に
  byte jisH, jisL;
  int sp, jiscode;
    // まず、シフトJISをJISに変換
  jisH = sjiscode / 256;    // 上位バイト
  jisL = sjiscode % 256;    // 下位バイト
  jisH = (jisH - ((jisH <= 0x9F) ? 0x71:0xB1)) * 2 + 1;
  if( jisL > 0x7F ) jisL--;
  if( jisL >= 0x9E ) jisH++;
  jisL -= ((jisL >= 0x9E) ? 0x7D:0x1F);
  jiscode = (jisH << 8) + jisL;
    // JISから ELISAフォントでの文字位置を計算
  if     (jiscode>=0x2121 && jiscode<=0x222e) sp =   0;  // 一般記号
  else if(jiscode>=0x223a && jiscode<=0x2241) sp =  11;  // 集合記号
  else if(jiscode>=0x224a && jiscode<=0x2250) sp =  19;  // 論理記号
  else if(jiscode>=0x225c && jiscode<=0x226a) sp =  30;  // 幾何記号
  else if(jiscode>=0x2272 && jiscode<=0x2279) sp =  37;  // 音楽記号
  else if(jiscode==0x227e)                    sp =  41;  // 丸記号
  else if(jiscode>=0x2330 && jiscode<=0x2339) sp =  56;  // 数字
  else if(jiscode>=0x2341 && jiscode<=0x235a) sp =  63;  // 英大文字
  else if(jiscode>=0x2361 && jiscode<=0x237a) sp =  69;  // 英小文字 
  else if(jiscode>=0x2421 && jiscode<=0x2473) sp =  73;  // ひらがな
  else if(jiscode>=0x2521 && jiscode<=0x2576) sp =  84;  // カタカナ
  else if(jiscode>=0x2621 && jiscode<=0x2638) sp =  92;  // ギリシャ大文字
  else if(jiscode>=0x2641 && jiscode<=0x2658) sp = 100;  // ギリシャ小文字
  else if(jiscode>=0x2721 && jiscode<=0x2741) sp = 138;  // キリル大文字
  else if(jiscode>=0x2751 && jiscode<=0x2771) sp = 153;  // キリル小文字
  else if(jiscode>=0x2821 && jiscode<=0x2840) sp = 166;  // 罫線
  else if(jiscode>=0x3021 && jiscode<=0x4f53) sp = 886;  // 第一水準漢字
  else if(jiscode>=0x5021 && jiscode<=0x7426) sp = 929;  // 第二水準漢字
  else return -1;              // 対応文字コードがなければ -1
  return (jiscode / 256 - 0x21) * 94 + (jiscode % 256) - 0x21 - sp;
}

signed int asciiToElisa(unsigned int code) {  // 半角を全角ELISAに変換 (半角カナはごめんなさい)
  static byte ElisaMark[] = {  0,  9, 40, 83, 79, 82, 84, 38, 41, 42, 85, 59,
                               3, 60,  4, 30,  6,  7, 66, 64, 67,  8, 86, 45,
                              78, 46, 15, 17, 13, 47, 34, 48, 16};
  if((code>=0x81 && code<=0x9f) || (code>=0xe0 && code<=0xef))
                  return -1;                    // ★ シフトJIS上位1バイトは -1 ★
  if(code  <  32) return -2;                    // 制御文字
  if(code <=  47) return ElisaMark[code-32];    // 記号 SP ! " # $ % & ' ( ) * + , - . /
  if(code <=  57) return code + 99;             // 数字
  if(code <=  64) return ElisaMark[code-42];    // 記号 : ; < = > ?
  if(code <=  90) return code + 92;             // 英大文字
  if(code <=  96) return ElisaMark[code-68];    // 記号 [ \ ] ^ _
  if(code <= 122) return code + 86;             // 英小文字
  if(code <= 126) return ElisaMark[code-94];    // 記号 { | } ~
  if(code == 127) return -2;                    // 制御文字 DEL
  if(code >= 0xa1 && code <= 0x9f) return -4;   // 半角カタカナで
  return -3;                                    // 未使用またはシフトJIS下位1バイト
}

このスケッチをアップロードして、シリアルモニター(9600 baud)に「こんにちは世界」と入力すると
konnichiha.png
こんな感じで表示されます。
LED POV (Persistence of Vision) や、マトリックスLEDで使えそうになってきました。

I2C EEPROM にファイルを書き込む [Arduino]

準備するものは、恵梨沙フォントのファイルと、Tera Termというソフトです。

恵梨沙フォントのダウンロード : Vector ソフトを探す!:
http://www.vector.co.jp/soft/dl/data/writing/se013782.html
ダウンロード - Tera Term (テラターム) - SourceForge.JP
http://sourceforge.jp/projects/ttssh2/releases/

続いて、Arduino にシリアルから送られてきたデータをEEPROMに書き込むスケッチをUploadします。
EEPROMは書き込む段階で数msecほどかかるようなので、8バイト毎にデータを書き込むようにしました。
(8で割り切れるファイルサイズでないといけません)
4800 baud でデータを送信します。
Arduino playground のスケッチをまるパクりです。

#include <Wire.h> //I2C library

unsigned int addr=0; //first address
byte buf[8];

void setup() 
{
  Wire.begin(); // initialise the connection
  Serial.begin(4800);
  delay(10); //add a small delay
}

void loop() 
{
  if( Serial.available() >= 8 ){
    for(int i=0; i<8; i++) buf[i] = Serial.read();
    i2c_eeprom_write_page( 0x50, addr, buf, 8 );
    delay(10); //add a small delay
    addr += 8; 
  }
}

  // WARNING: address is a page address, 6-bit end will wrap around
  // also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddresspage >> 8)); // MSB
  Wire.send((int)(eeaddresspage & 0xFF)); // LSB
  byte c;
  for ( c = 0; c < length; c++)
    Wire.send(data[c]);
    Wire.endTransmission();
}

続いて、Tera Term を起動します。
Arduino がつながったシリアルポートを選択。
teraterm1.png

メニューから、 設定(S) → シリアルポート(E)... を開きます。
teraterm2.png
ここで、ボーレートを4800 にします。(スケッチで9600 baudとした場合、ボーレートは9600で送信遅延を 1ミリ秒/字 にすればOKでした。)

またメニューから、ファイル(F) → ファイル送信(S)... を開いて
teraterm3.png
忘れずに、バイナリ(B) にチェックをいれて、ELISA100.FNT を選択し、開く(O) で送信が完了します。

ちゃんと書き込めているかどうかのチェックをするスケッチです。
これも、Arduino playground のスケッチをほぼまるパクりです。
#include <Wire.h> //I2C library

unsigned int addr=0; //first address
byte buf[8];

void setup() 
{
  Wire.begin(); // initialise the connection
  Serial.begin(4800);
  delay(10); //add a small delay
}

void loop() 
{
  i2c_eeprom_read_buffer( 0x50, addr, buf, 8 );
  for(int i=0; i<8; i++) {
    for(int j=7; j>=0; j--) {
      Serial.print( bitRead(buf[i], j) ? "**":"  " );
    }
    Serial.println("");
  }    
  Serial.println("");
  addr += 8;
}

  // maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress,length);
  int c = 0;
  for ( c = 0; c < length; c++ )
    if (Wire.available()) buffer[c] = Wire.receive();
}

シリアルモニターで、JISコード順に文字がでてきたら成功です。
eeprom_read.png

JISコードは扱いにくいので、シフトJISにしたり、半角用のフォントがないので半角を全角にしたりと、日本語独自の問題があります。次回はこのあたりを解決したいと思います。

初めての I2C EEPROM [Arduino]

ArduinoでマトリックスLEDやグラフィックLCDを使ってみたいと思っています。
日本人としては平仮名、漢字を表示してみたいものですが、フォントの格納や日本語の処理が必要になってきます。
8x8マトリックスを目一杯使えるもので考えると「恵梨沙フォント」というのが良さそうです。

恵梨沙フォントの詳細情報 : Vector ソフトを探す!:
http://www.vector.co.jp/soft/data/writing/se013782.html

この ELISA100.FNT を調べてみると

 ・ファイルサイズ55,016 byte
 ・ヘッダーはなく、1文字あたり8byteで6877文字
 ・全角のみ
 ・JISコード順で、文字のない部分はつめてある
 ・最後の2文字「凜」「熙」は収載されていない

ということで、Arduinoで使えそうな感じがします。

これを格納するメモリが必要ですが、ATmega328では足りないので、外付けにします。8x8マトリックスLEDで16本、シリアル通信に2本となると、残りは2本しかないので、I2C EEPROMならなんとかなりそうです。
スケッチのサンプルもありました。

Arduino playground - I2CEEPROM:
http://arduino.cc/playground/Code/I2CEEPROM

容量は512kbit (= 64kbyte) で間に合います。

シリアルIスケアC EEPROM 24FC512-I/P マイコン関連 秋月電子通商 電子部品 ネット通販
http://akizukidenshi.com/catalog/g/gI-03569/

I2Cのプルアップ抵抗を使っているページもみかけましたが、

Arduino playground - I2C:
http://www.arduino.cc/playground/Learning/I2C

i2c_pullups.png

ここをみると、Wire libraryを使う限りはどうやら必要なさそうです。

EEPROMを使うのは初めてなのでどういうものか試してみました。
eeprom_test_pic.jpg
eeprom_test.png
そのまま、Arduinoに刺さります。(写真はライトプロテクトされている状態)
eeprom_test_on.jpg
さきほどの、スケッチ

Arduino playground - I2CEEPROM:
http://arduino.cc/playground/Code/I2CEEPROM

うまく動きました。
次回は、ファイルの書き込みをしてみたいと思います。
タグ:EEPROM I2C

赤外線リモコン (解析の友) [Arduino]

赤外線リモコンの信号を可視化してデータを抽出する手助けをするスケッチです。
初心者の汚いソースですみません。
ATmega328 でないとRAMが足りないみたいです。
霧ヶ峰のリモコンも家電協フォーマットで読み込めました。
なるべくリピート信号を読み込まないようにリモコンのボタンはポンッと瞬間的に押すといいみたいです。
irkirigamine.jpg
赤外線リモコン受信モジュールは写真のようにArduinoに直刺しとしました。
// IR解析スケッチ
#define IR_IN   2          // IR Receiver

#define NEC    1
#define AEHA   2
#define SONY   3

#define BUF_SIZE   (512)    // ATmega168だと 128くらいにしないと不具合

void setup()  { 
  pinMode( IR_IN , INPUT );                        // Vout  ->   digital 2
  pinMode( 3, OUTPUT );  digitalWrite( 3,  LOW );  // GND   ->   digital 3 LOW
  pinMode( 4, OUTPUT );  digitalWrite( 4, HIGH );  // Vcc   ->   digital 4 HIGH
  Serial.begin(115200);
}

void loop() {
  unsigned long usec, nec_data = 0;
  unsigned int i, n, irOffTime, minTime, aveCnt = 0, aveAdd = 0;
  unsigned int irdata[ BUF_SIZE ], timeunit, leaderH, leaderL, datalen;
  unsigned int format = 0, hex = 0, sony_data = 0, sony_adrs = 0,  sony_adrs_bit = 0;
  boolean isvalid = true;
  // ● 赤外線を感知するまで待つ
  while( digitalRead( IR_IN ) == HIGH );
  // ● 生データの取得
  for( i=0; i < BUF_SIZE; ) {
    usec = micros();
    while( digitalRead( IR_IN ) == LOW );           // IR信号がONの時間を測定
    irdata[  i] = micros() - usec;
    irdata[++i] = 0;
    usec = micros();
    while( digitalRead( IR_IN ) == HIGH ) {         // IR信号がOFFの時間を測定
      irOffTime = micros() - usec;
      if( irOffTime > 65000 ) goto ir_exit;         // 信号途絶なら終了
    }
    irdata[i++] = irOffTime;
  }
  ir_exit:
  Serial.println( "<< IR data analyser >>" );
  // ● 時間単位を調べる
  minTime = irdata[0];                    // まず最小値を確認
  for( i=0; irdata[i]; i++) minTime = min( minTime, irdata[i]);
  for( i=0; irdata[i]; i++) {             // 最小値の+50%までを時間単位とする
    if( minTime * 3 / 2 > irdata[i] ) { aveAdd += irdata[i] - minTime;  aveCnt++; }
  }
  timeunit = aveAdd / aveCnt + minTime;
  if( timeunit < 300 ) return;            // 時間単位が短すぎるときは異常と判断
  // ● 時間単位でのデータに変換
  for( i=0; irdata[i]; i++) irdata[i] = ( irdata[i] + timeunit / 2 ) / timeunit;
  // ● 赤外線の波形を表示
  for( i=0; irdata[i]; i++) for( n=0; n < irdata[i]; n++) Serial.print( (i % 2) ? "_":"|" );
  Serial.println( "." );  
  // ● 解析しやすくするためにデータの整理
  leaderH = irdata[0];                // リーダ部Highの長さ
  leaderL = irdata[1];                // リーダ部Low の長さ
  for( i=0;; i+=2) {                  // データ解析用にリーダ部とリピートを除去・整理
    irdata[i] = irdata[i+2];  irdata[i+1] = irdata[i+3];
    if( irdata[i+1] > 10 || irdata[i+1] == 0 ) { irdata[i+1] = 0;  datalen = i / 2 + 1;  break;  }
  }
  // ● 各種基本データを表示
  Serial.print( "Time Unit (usec) : ");  Serial.println( timeunit , DEC );
  Serial.print( "Leader  (On/Off) : ");
  Serial.print( leaderH, DEC );  Serial.print(  " / " );  Serial.println( leaderL, DEC );
  // ● リーダ部の長さでフォーマットを判断
  Serial.print( "Format           : ");
  if((leaderH > 14 || leaderH < 18) && leaderL == 8 ) { format = NEC;  Serial.println( "NEC"  ); }
  else if           ( leaderH == 8  && leaderL == 4 ) { format = AEHA; Serial.println( "AEHA" ); }
  else if           ( leaderH == 4  && leaderL == 1 ) { format = SONY; Serial.println( "SONY" ); }
  else  Serial.println( "???" );
  // ● フォーマット毎に結果を表示
  switch( format ) {                
    case 0:        // ■ 不明な形式の場合
      Serial.println( " !! Analysing as NEC format !!");
    case NEC:      // ■ NECフォーマット
      Serial.print( "Binary data      : (LSB)");
      for( n=0; n < datalen - 1; n++) {  // ストップビット手前まで繰り返し
        if( n % 8 == 0) Serial.print(" ");
        if( irdata[n*2]   != 1)                       { Serial.print("?"); isvalid = false; continue; }
        if( irdata[n*2+1] != 1 && irdata[n*2+1] != 3) { Serial.print("?"); isvalid = false; continue; }
        Serial.print((irdata[n*2+1]==1) ? "0" : "1" );
        nec_data |= ( (irdata[n*2+1]==1) ? 0UL : 1UL ) << n;
      }
      Serial.println(" (MSB)");
      if( !isvalid ) break;    // データに不具合があれば以降は表示しない
      Serial.print( "Custom code      : ");  printlnHexcode(  nec_data        % 256 );
      Serial.print( "Custom code '    : ");  printlnHexcode( (nec_data >>  8) % 256 );
      Serial.print( "Data code        : ");  printlnHexcode( (nec_data >> 16) % 256 );
      Serial.print( "Data code (nega) : ");  printlnHexcode( (nec_data >> 24) % 256 );
      break;
    case AEHA:     // ■ 家電協フォーマット
      Serial.print( "Binary data      : (LSB)");
      for( n=0; n < datalen - 1; n++) {  // ストップビット手前まで繰り返し
        if( n % 4 == 0) Serial.print(" ");
        if( irdata[n*2]   != 1)                       { Serial.print("?"); isvalid = false; continue; }
        if( irdata[n*2+1] != 1 && irdata[n*2+1] != 3) { Serial.print("?"); isvalid = false; continue; }
        Serial.print((irdata[n*2+1]==1) ? "0" : "1");
      }
      Serial.println(" (MSB)");
      if( !isvalid ) break;    // データに不具合があれば以降は表示しない
      Serial.print( "Hexadecimal data : (LSB) ");
      for( n=0; n < datalen; n++) {
        hex |= ((irdata[n*2+1]==1)? 0:1) << (n%4);
        if( n % 4 == 3) {  Serial.print(hex, HEX); Serial.print("    "); hex = 0; }
      }
      Serial.println("(MSB)");
      break;
    case SONY:     // ■ SONYフォーマット
      Serial.print( "Binary data      : (LSB) ");
      for( n=0; n < datalen; n++) {
        if( irdata[n*2+1] != 0 && irdata[n*2+1] != 1) { Serial.print("?"); isvalid = false; continue; }
        if( irdata[n*2]   != 1 && irdata[n*2]   != 2) { Serial.print("?"); isvalid = false; continue; }
        Serial.print( irdata[n*2]-1 );
        if( n == 6 ) Serial.print(" ");
        if( n <  7 ) sony_data |= (irdata[n*2]-1) << n;
        else         sony_adrs |= (irdata[n*2]-1) << sony_adrs_bit++;
      }
      Serial.println(" (MSB)");
      if( !isvalid ) break;    // データに不具合があれば以降は表示しない
      Serial.print( "Data (7bit)      : ");  printlnHexcode( sony_data );
      Serial.print( "Adress bits (bit): ");  Serial.print( sony_adrs_bit ); Serial.println( " bit" );
      Serial.print( "Adress           : ");  printlnHexcode( sony_adrs );
      break;
  }
  // ● 以上で終了
  Serial.println("");
}

void printlnHexcode(unsigned int d) {    // 16進数を見栄えよく表示、改行
  Serial.print( ( d < 0x10 ) ? "0x0" : "0x" );
  Serial.print( ( d < 0x1000 && d > 0xff ) ? "0" : "" );
  Serial.println( d, HEX);
}

シリアルモニターに結果を表示します。
kirigamine.png



赤外線リモコン (送信) [Arduino]

赤外線リモコン解析スケッチはもう少し練り直してブログに載せてみたいと思っています。
今回は、以前受信できた東芝REGZA(NECフォーマット)のデータを送信してみます。
赤外線出力フォーマットはわかっているので、このフォーマットに則って出力します。
38kHzのキャリアをどうするか、、。タイマーとかPWMの関係を操作する方法があるようですが、自分の頭では理解ができませんでした。そういうものは使わないわかりやすい方法がありました。

Arduino 日本語リファレンス millis()
http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=2352
または「Arduinoをはじめよう(日本語版)」の141ページ millis() のところにあるサンプルスケッチがそのままです。

このスケッチを実行すると、だいたい tdelay が 7 くらいで38kHzに一番近い値がでました。
[ON] 7 μsec / [OFF] 7 μsec ということになります。

これを踏まえて作った赤外線リモコン出力のサンプルスケッチは、5秒おきにテレビのチャンネルを変更していくというものです。
一応 duty比が1/3 となっているので、[ON] 5 μsec / [OFF] 9 μsec くらいにしてみました。

// リモコン出力サンプル (東芝REGZA チャンネル変更)
#define IR_IN   2               // IR Receiver
#define IR_OUT  3               // IR LED

void setup()  { 
  pinMode( IR_IN,  INPUT  );
  pinMode( IR_OUT, OUTPUT );
}

void loop() {
  sendIrNEC( 0xE41BBF40 );      // REGZA 0x40 0xBF, 0x1B (Ch.Up)
  delay( 5000 );
}

void sendIrNEC( unsigned long d ) {
  irPulse( 9000, 4500 );        // リーダ
  for(int i=0; i<32; i++) irPulse( 562, (d>>i)%2 ? 1687:562 );  // データ
  irPulse( 562, 40000 );        // トレーラ
}

void irPulse( unsigned long lenH, unsigned long lenL ) {
    unsigned long irExp = micros() + lenH;  // 赤外線ONの期限
    while ( irExp > micros() ) {            // 時間までパルス出力
      digitalWrite( IR_OUT, HIGH );  delayMicroseconds(5);
      digitalWrite( IR_OUT, LOW  );  delayMicroseconds(9);
    }
    delayMicroseconds( lenL );
}

携帯のカメラやデジカメで赤外線がでているのが確認できます。
irout.jpg

写真は高出力タイプですが、テレビのリモコンくらいならArduinoに抵抗をはさんで直接赤外線LEDをつなぐだけで反応してくれました。



赤外線リモコン (解析) [Arduino]

Panasonic の照明用リモコン HK9327K のデータを解析してみました。

明るさフリーリモコン HK9327K 商品概要 照明その他 Panasonic
http://ctlg.panasonic.jp/product/info.do?pg=04&hb=HK9327K

たとえば、チャンネル1で、全灯の場合は、
||||||||____|_|_|___|___|_|___|_|_|_|___|_|_|___|_|___|_|___|_|_|___|_|_|_|_|_|_|___|___|_|___|_|_|___|_|___|_|_|___|_|_|.
Time Unit (usec) : 435
Format           : AEHA
Leader  (On/Off) : 8 / 4
 (LSB) 0011 0100 0100 1010 1001 0000 0011 0100 1010 0100 (MSB)
 (LSB) C    2    2    5    9    0    C    2    5    2    (MSB)

こんな感じです。これを6個のボタンを3チャンネル分調べてみました。
リーダ部とトレーラ部を除いたデータは以下のとおりでした。

hk9327kdata.png

さらに、データの内容を推測してみました。
hk9327kformat.png

最初の16+4ビットは、家電協共通のフォーマットと思われます。次の4ビットの意味合いが不明です (未使用?)。そして次の3ビットでボタンの種類、次の2ビットでチャンネル、次の3ビット(001B)は不明。次の8ビット(5バイト目)はエラーチェック用で、はじめから数えて3バイト目と4バイト目のXORと思われました。

しかし、家電協フォーマットは最初の20ビットのみ形式が指定されていて、21ビット目以降はデータ内容の他、エラーチェックについても各社まかせのようです。シャープのAQUOSも家電協フォーマットのようですが、データの形式からエラーチェックの方法まで全く違うようです。


赤外線リモコン (フォーマット その2) [Arduino]

日本で使われている3大リモコンフォーマットをさらに調べてみました。
すべてネットの情報ですが、公式なものから非公式?推測?と思われるものまでできる範囲で集め、その中から比較的正しいと思われる情報をまとめてみました。
100%正確というわけではありませんが下記のとおりです。

irformattable.png.png
(SONYのデータ0/1が逆だったので修正しました)
これをもとに、赤外線リモコン解析プログラムを作成してみました。
SONYのコードはSONYの学習リモコンがあったので、これで確認しました。

iranalyser.png

この解析プログラムを使って Panasonic の照明リモコンを解析したので、次回その結果を載せる予定です。


SONY リモートコマンダー PLZ530D レッド RM-PLZ530D R

SONY リモートコマンダー PLZ530D レッド RM-PLZ530D R

  • 出版社/メーカー: ソニー
  • メディア: エレクトロニクス

赤外線リモコン (受信) [Arduino]

赤外線リモコンの受信モジュールは PL-IRM2161-C438 というものを使いました。
出力は負論理で、普段はHigh、リモコンの赤外線信号を受信するとLowになります。

pl-irm2161-c438.png
赤外線リモコン受信モジュール PL-IRM2161-C438 (2個入)
http://akizukidenshi.com/catalog/g/gI-03292/

ブレッドボードやワイヤーを使うのが面倒だったので、デジタル2番をデータ受信、3番をGND、4番をVcc扱いにしてみました。

irrcv.png

<NECフォーマットの受信サンプル>
できるだけ簡略にしてみました。
リーダ部以外は赤外線ONの時間は一定で、赤外線OFFの時間の長さで 0 / 1 が決まるので、赤外線OFFの時間を測定し1000μ秒より短ければ 0、長ければ1 としました。これを32ビット分行い、データのチェックは簡易的に3バイト目と4バイトのXORでのみ行いました。リーダの長さや、赤外線ONの時間や、ストップビット以降のチェックなどはしていません。
// NECフォーマットリモコン(東芝テレビ REGZAなど)用サンプルプログラム
#define IR_IN   2          // IR Receiver

void setup()  { 
  pinMode( IR_IN , INPUT );
  pinMode( 3, OUTPUT );  digitalWrite( 3, LOW );  // GND
  pinMode( 4, OUTPUT );  digitalWrite( 4, HIGH ); // Vcc
  Serial.begin( 115200 );
}

void loop() {
  unsigned long irData = 0x00000000, irOff;
  while( digitalRead( IR_IN ) == HIGH );      // 赤外線を感知するまで待つ(信号としてはLOWになるまで待つ)
  while( digitalRead( IR_IN ) == LOW );       // リーダ部をやりすごす
  for( int dig = 0; dig < 32; dig++ ) {       // データを32桁読み込む
    irOff = pulseIn( IR_IN, HIGH );             // 赤外線消灯時間(信号としてはHIGHの時間)
    if( irOff > 10000 ) return;                 // 信号途絶なら終了
    if( irOff >  1000 ) irData |= 1UL << dig;   // Data=1 とき
  }
  if( (byte)((irData>>16)^(irData>>24)) == 0xff )
    Serial.println( irData , HEX );          // データOKならシリアル出力
}

pulseInのパラメータにタイムアウトがあるのですが、これはパルスが開始するまでのタイムアウト時間で、パルスが終了しない時のタイムアウトの指定はできません。LEDのダイナミック点灯をしながら赤外線がきたら受信するというような場合に、赤外線が途切れてしまったら次の赤外線がくるまでまちぼうけとなってしまいます。
次のようにすると信号途絶の対応ができると思います。
    usec = micros();
    while( digitalRead( IR_IN ) == HIGH ) {   // IR信号がOFFの時間を測定
      irOff = micros() - usec;
      if( irOff > 10000 ) return;             // 信号途絶なら終了
    }

<赤外線リモコンの受信結果>
regzacode.png

REGZAの場合は、
カスタムコード 1バイト目は0x40
カスタムコード 2バイト目は0xBF (1バイト目の反転)
データコードは各社バラバラのようで、REGZAの場合は、チャンネルの番号とデータの値が同じでした。

家電協フォーマットは可変長のようです。うちの照明用リモコンで解析してみたいと思っています。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。