Arduinoでリモコン

こちらの方のページを参考にArduinoでリモコン作ってみた。
参考にしたページでは受信したリモコンデータを一旦テキストエディタ等で加工しなければ送信データとして使えなかったので、コピペで送信できるようにソースを都合よく改変。
使った部品は秋月電子で買える受光部赤外線LED
LEDは+の方をリモコン出力ピンにもう片方をGNDに接続。
受光部はOUTPUTをリモコン入力ピンに、あとGND、5Vにそれぞれ接続。
こんな感じ。
2013-01-18 01.43.17
これに向かってリモコンのボタンを押してやるとシリアルモニタにデータが表示されるのでコロン以降の文字列をそのままコピーしてArduinoに送信してやるとそのままリモコンとして動作する。
スクリーンショット 2013-01-19 22.58.24
※ただし、80bytesを超えるような長さの文字列を送信すると取りこぼすっぽいので分割して送信すること。

ソースはこんな感じ。

#define DLEN 512
// リモコン入出力ピン
#define IR_IN  A1
#define IR_OUT A0

// リモコンデータ保存配列
unsigned char data[DLEN];

// リモコン受信状態制御 0:Leader受診前 1:Leader受信後受信完了まで -1:赤外線受信停止(未実装)
char recvStatus = 0;

// リモコン入力ピンの値保持
char last = 0;   

// -1,0,1しか使わない。
char onOff = -1; 

// 赤外線基本の時間 (μsec) リモコンで違うため可変
int tlen;        

// 0:点灯時間 1:消灯時間保持
unsigned int prevMem[2]; 

unsigned long us = micros();


// 消灯、点灯時間比率テーブル
// NECテーブル
unsigned char nec[5][2] = 
{
  {1,1},  // '0':数字の並びはoff, on の時間の並び
  {3,1},  // '1'
  {4,16}, // repeat
  {8,16}, // Leader
  {0,1}, // END
};

// 家製協テーブル
unsigned char aeha[5][2] = 
{
  {1,1},  // '0'
  {3,1},  // '1'
  {8,8}, // repeat
  {4,8}, // Leader
  {0,1}, // END
};


// セットアップ
void setup() {
  Serial.begin(57600);    // シリアル通信速度の設定
  pinMode(IR_IN, INPUT);  // 入出力ピンの設定
  pinMode(IR_OUT, OUTPUT);
}

// dataからリモコン信号を送信
void sendSignal() {
  char onoff = 1; // 必ず点灯から始まるため。 
  unsigned char state = 0;
  unsigned int len;
  unsigned char (*p)[2]; // necとaehaのtの長さテーブル参照用ポインタ。
  
  for (int cnt = 0; cnt < DLEN; cnt++) {
    // len に必要な点灯または消灯時間をセット
    if(cnt == 0){
      // 点灯、消灯時間比率テーブルの決定
      p = (data[cnt] == 'A') ? aeha : nec ;
      state = 3;
    }else{ // 0 or 1 or 2 or 4(END)
      state = data[cnt] - '0';
    }
    for(onoff = 1; onoff >= 0; onoff--){
      // テーブルから、点灯/消灯時間を決定
      len = tlen * p[state][onoff];
  
      // 長さ0のデータを見つけたら、リモコン信号送信終了
      if(len == 0)
        break;
      unsigned long us = micros();
      do {
        digitalWrite(IR_OUT, onoff); 
        delayMicroseconds(8);  // キャリア周波数38kHzでON/OFFするよう時間調整
        digitalWrite(IR_OUT, 0);
        delayMicroseconds(7);
      } 
      while (long(us + len - micros()) > 0); // 送信時間に達するまでループ
    } 
    // 長さ0のデータを見つけたら、リモコン信号送信終了
    if(len == 0)
      break;
  }
  Serial.print("OK\n");
}

// シリアルからの受信をチェック
// 入力エラーチェックなど全くしていないので注意。
void checkSerial() {
  unsigned char state = 0;
  unsigned int datacnt = 0;
  unsigned char c;
  if (Serial.available() > 0) {
    c = Serial.read();
    if(c == 'R'){
      sendSignal();
    }else if (c == 'P') {  // 入力開始
      // 念のため受信バッファ初期化
      for(datacnt = 0; datacnt < DLEN; datacnt++)
        data[datacnt] = 0;
      tlen = 0;
      datacnt = 0;
      Serial.print(">");
      while (1) {
        while (Serial.available() == 0) {
        } // 次のバイトが来るまで無限ループで待つ
        int a = Serial.read();
        switch(state){
          case 0: // N(EC) or A(AEHA)
            data[datacnt] = a;
            datacnt++;
            state++;
            break;
          case 1: // tlen
            if('0' <= a && a <= '9'){
              tlen = tlen * 10 + (a - '0');
            }else{
              state++; // ’S'受信
            }
            break;
          case 2: // 0 or 1 or 2 or 4
            if('0' <= a && a <= '2'){
              data[datacnt] = a;
              datacnt++;
            }else{
              // '4' データ終端
              data[datacnt] = a;
              datacnt++;
              state++;
            }
            break;
        }
        if(state == 3){
          Serial.print("data read end.[t=");
          Serial.print(tlen,DEC);
          Serial.print(",len=");
          Serial.print(datacnt,DEC);
          Serial.print("]\n");
          Serial.write(data, datacnt);
          Serial.print("\n");
          sendSignal();
          state = 0;
          break;
        }
      }
    }
  }
}

// 点灯、消灯時間からどのデータ(CustomerCode or Data or Leader or Repeat)であるか判断する。
// 送信開始から終了までに複数回Leaderを送るものには対応していない。
void chkData(){
  unsigned int t;
  if(prevMem[0] < 900){
    // Customer Code or Data
    if(recvStatus == 1){
      if(prevMem[0] * 2 < prevMem[1]){
        Serial.print("1");
      }
      else{
        Serial.print("0");
      }
    }
  }
  // 最初の点灯の長さでNEC/AEHAどっちのフォーマットかいい加減な感じで判断。
  else{
    if(recvStatus == 0){
      if(prevMem[0] > 8000){
        t = prevMem[0] / 16;
        Serial.print("NEC Leader:[t=");
        Serial.print(t, DEC);
        Serial.print("]:PN");
      }else{
        t = prevMem[0] / 8;
        Serial.print("AEHA Leader:[t=");
        Serial.print(t, DEC);
        Serial.print("]:PA");
      }
      Serial.print(t, DEC);
      Serial.print("S");
      recvStatus = 1;
    }else{
      if(recvStatus == 1)
        Serial.print("2");
    }
  }
}

void loop() {
  unsigned int val;
  unsigned int cnt = 0;

  // Wait for incoming IR signal
  while ((val = digitalRead(IR_IN)) == last) {  // パルスが切り替わるまで待機
    if (++cnt >= 30000) {  // 30000回ループで信号が終了したとみなす
      if (cnt == 30000 ){
        if(flames > 0 && recvStatus == 1){
          Serial.print("4\n");
        }
        recvStatus = 0;
        onOff = -1; // 最初に赤外線ONを受信した時に赤外線off時間の値を無視するため
        flames = 0;
      }
      else{
        checkSerial();   // シリアル通信が来ているかチェック
      }
      cnt = 30000;
    }
  }

  unsigned long us2 = micros();
  unsigned int len = (us2-us);
  if(onOff >= 0){ // 最初に赤外線ONを受信した時に赤外線off時間の値を無視するため
    prevMem[onOff] = len;
    if(onOff == 1){
      chkData();
      flames++;
      onOff = -1;
    }
  }

  last = val;
  us = us2;
  onOff++;
}

アクリルレーザーカット

Emerge+ 様のところに依頼したアクリルレーザーカット先日届いたので組み上げてみる。

A4のアクリル素材1枚から写真のDACエンクロージャが2個取れるように図面を作成。

初めてレーザーカットというのを試してみたけど、FAQに合った通り、切断する線から±0.1mm広がるということを考慮して図面を作成すればあとは難しいこと考えなくてもサイズ通りになるいうことがわかったので、これから色々活用してみたい。

アクリルカットで作成したDACエンクローシャ

C#でSQLite3を使う

MySQL使うほどでもない場合にSQLite使いたかったので調べてみた。

  1. ここ から System.Data.SQlite ライブラリを入手。
  2. サクっとインストール
  3. C#プロジェクト設定を .net framework 3.5にする。
  4. 参照設定に以下DLLを追加する。
    C:\Program Files (x86)\SQLite.NET\bin\System.Data.SQLite.dll
  5. 使う。
    using (SQLiteConnection cnn = new SQLiteConnection("Data Source=mydatabase.db"))
    using (SQLiteCommand cmd = cnn.CreateCommand())
    {
      cnn.Open();
    
      // CREATE文の実行
      cmd.CommandText = "CREATE TABLE FOO (ID INTEGER PRIMARY KEY, MyValue NVARCHAR(256))";
      cmd.ExecuteNonQuery(); // Create the table, don't expect returned data
    
      // INSERT文の実行
      cmd.CommandText = "INSERT INTO FOO (MyValue) VALUES('Hello World')";
      cmd.ExecuteNonQuery();
    
      // SELECT文の実行
      cmd.CommandText = "SELECT * FROM FOO";
      using (SQLiteDataReader reader = cmd.ExecuteReader())
      {
        while (reader.Read())
        {
          Console.WriteLine(String.Format("ID = {0}, MyValue = {1}", reader[0], reader[1]));
        }
      }
    }