2013年1月27日日曜日

AVRの開発環境

基板を起こす前にAVRマイコンの基板と前回作成したXBeeのHA制御基板、AVRのwriterを組み合わせてテスト環境を作りました。
これで、IR-Bluster機能を実装してみて動作確認していきます。



2013年1月26日土曜日

エアコン用のXBee経由IR-Bluster&HA端子制御


エアコンに組み込むためのXBee経由のIR-Bluster&HA端子制御モジュールを設計し始めました。
エアコンの状態はHA端子経由で監視し、制御は発光強度を弱めにしたIR-Blusterでリモコンコードを送ることで制御するモジュールです。
RaspberryPiで組んでいる赤外線受発光+XBeeコントロールサーバーからコマンドを投げて赤外線を発光するものです。
FRISKケースに組み込むのでこんな感じの基板を想定しています。

回路は前回と違い細かな制御をするためと、IR-Blusterの赤外線コード生成のためにAVRマイコンを一緒に入れています。
まだ、基板設計を始めてないので入りきるか判りませんが、ざっと上の様に部品を並べて見た限りでは入りそうと判断しています。


ちょっと、部品が多くなってきたので2個入りの表面実装部品を多用しています。
あと、コネクタを前回のように個別に置いていては面積が足りなくなりそうなので22pの集合コネクタと電源用の3pコネクタに減らしました。



2013年1月20日日曜日

RaspberryPiの赤外線送受信基板

ユニバーサル基板で組んでいたRaspberryPi用のIR-Remocon回路を基板化しようかと思い、検討をはじめました。
ざっくりRaspberryPiの基板の寸法から上に載せる基板の寸法を拾い、おおまかな配置を検討しています。RaspberryPiの上に部品面を下にした基板を載せて、GPIOの26pinコネクタと、RPi RevBで追加になった8pinコネクタ(no mountですが)で接続します。
また、IR-LEDの側にRPiの電源コネクタがいるので、これを反対側に2.1mm電源コネクタを増設して、こちらから給電出来るようにします。



回路としては、XBeeとIR送受信、BuzzerとAVRマイコンを載せておき、空き領域を2mmピッチ、2.54mmピッチの蛇の目で追加回路を載せられるようにしておきます。


この基板は2通りの用途を考えています。
1つはRPiと組み合わせてXBee Cordinator + IR-Receiver + IR-Bluster機能付きのホームコントロールサーバー
2つ目はこの基板単独でAVR + XBee EndPoint + IR-BlusterのXbee経由のIR-Bluster子機
です。
これからいくつかの部分の回路の動作検証をしつつ基板の設計をしていくつもりです。

2013年1月7日月曜日

MacによるXBeeのFW書き込み

MacでXBeeのFW書き込みをするためにVirtualBox上でWindowsXPを動かしてX-CTUを使っていましたが、これだけのためにWindowsを起動するのが面倒くさくてOS-Xから直接やることができないか検討してみました。
X-CTUで書き込みをしているXBeeのDI/DOのピンからUSB-Serialの基板のRxDに信号を繋いで転送プロトコルの内容を見てみました。

どうもat%pコマンド(ATコマンドモード時)というマニュアルに存在していないコマンドを発行して通信しているようです。このコマンドを発行した後にデータを転送しているようなのですが、この転送しているデータの中身がXBeeのeblファイルやehxファイルの中身とは別物でどうやって生成しているのかが判りませんでした。

で、色々ググっていたのですがmoltosenso Network Manager IRONというFreeのツールがLinuxやMac OS-X用に存在していてFWの書き込みが出来るようなので試してみました。
ちょっとUIに癖がありますが、FWのファイルを用意して書き込みをするとちゃんと書き込み出来ました。書き込みはやはりat%pコマンドで書き込んでいるようです。

X-CTUの場合はModem XBEE, Function Set, Versionという項目を選択するところをファイルを直接指定して書き込みするのが判りにくいですが、ZBのCordinator APIの場合はXB24-ZB_21A7、ZBのRouterAPIの場合はXB24-ZB_23A7を選択することで書き込むことができます。
どのバージョン番号がどのFunction Setに相当するのかはmxiファイルを見ると先頭部分に書かれています。

これでいちいちWindowsを立ち上げる必要がなくなりました。


2013年1月6日日曜日

基板化に向けてユニバーサル基板で組み直し

そのうち基板を起こす準備として、ユニバーサル基板に部品を並べて回路を整理しました。パルス駆動なのでIR-LEDも高出力化しています。



ケースに収まるように部品面を下にしてRaspberryPiのGPIOコネクタに接続しています。


横から見たらこんな感じです。
左からIR-LED、IR-Receiver、XBeeモジュール、IR-LEDの順です。



その他のセンサーI/Fや電源コネクタetcを追加して、XBeeの子機側の回路と合わせて基板を起こそうかと考えています。


2012年12月31日月曜日

コントロールサーバーとリモコン制御

これまではiRemoconをsocket経由で制御してIRリモコン出力を出すようにコントロールサーバーを組んでいましたが、今回コントロールサーバーを置いているRaspberryPiにリモコンの記録、発光機能を追加したのでiRemoconをやめてRaspberryPiでリモコンも制御するようにコントロールサーバーを変更しました。




これで、すべての制御がRaspberryPiとその配下のXBeeコントローラで完結したのでシンプルになりました。




リモコン制御を追加したついでに外出時の一斉shutdownでTVも落とせるようにリモコンコードを追加しました。
普通のリモコンのTV電源はon/offのトグルボタンなのでリモコンコードを変更して電源offのコマンドをだせるようにして対応しています。

IR発光部を強化

回路が検討しているうちに汚くなってきたのと、いまいちLEDの指向性が強すぎるのか角度によっては反応しなかったりするので、LEDを2個に増やしました。
今までもLED点灯時の電流は90mA程度(LEDはMax100mA品)流れているので出力が弱いということはないはずですが。
短時間nパルス発光なので、LEDを2個+Trでほぼ3.3Vになるようにして抵抗を外しました。
デジカメで発光の様子を比べてみましたが、今度のほうが手元のリモコンと同程度の発光強度が有りそうです。リモコンの効きも良くなりました。
これでうまくケースに収まります。


--> RaspberryPiの赤外線リモコン

RaspberryPiにIRリモコンの送受信を追加

だんだん汚くなってきてしまいましたが、RaspberryPiにIRリモコンの送信部、受信部を追加した状態です。



回路図は書くほどのものではないですが、こんな感じになります。




リモコンフォーマットの解析と展開(その他)

家製協、NEC、SONYフォーマット以外のフォーマットは38kHzの倍の76kHzでサンプリングした後、0のclk数、1のclk数を16bitで表したフォーマットで記録します。
データが大きくなりますが、38kHz搬送波のものは記録できると思います。

-----

/*
 code[0],code[1]    = ID
 code[2],code[3]    = length
 code[4]            = FormatOther 0x04
 code[5] - code[31] = reserve
 code[32] -         = continuous length(16bit BE)
*/
int FormatAnalyzeOther(unsigned int *buf, int bufSize, unsigned char *code, int codeSize) {

  int last = -1;
  int n = 0;
  int c = 0;
  memset(code, 0, codeSize);
  for(int i = 0; i < bufSize * 32; i++) {
    int d = (buf[i / 32] >> (31 - (i % 32))) & 1;
    if((last < 0) && (d == 1)) continue; // skip nosignal

    if(c && (d != last)) { // data edge
      code[32 + n * 2] = c / 256;
      code[32 + n * 2 + 1] = c % 256;
      n++;
      c = 1;
    } else { // data continue
      c++;
    }
    last = d;
  }
  if(last == 0) return -1;
  
  code[2] = (32 + n * 2) / 256;
  code[3] = (32 + n * 2) % 256;
  code[4] = 4;
  return 32 + n * 2;
}

int CodeExpandOther(unsigned char *code, int codeSize, unsigned int *buf, int bufSize) {

  memset(buf, 0, bufSize * 4);
  int len = (((code[2] << 8) | code[3]) - 32) / 2;
  int p = 0;
  int o = 1;
  for(int i = 0; i < len; i++) {
    int c = (code[32 + i * 2] << 8) | code[32 + i * 2 + 1];
    for(int j = 0; j < c; j++) {
      if(o && !(p & 1)) buf[p / 32] |= (1 << (31 - (p % 32)));
      p++;
    }
    o ^= 1;
  }
  p += 31;
  return p / 32;
}

リモコンフォーマットの解析と展開(SONY)

次はSONYフォーマットの解析部分と展開部分です。

-----

/*
 code[0],code[1]    = ID
 code[2],code[3]    = length
 code[4]            = FormatSONY 0x03
 code[5]            = 1TCLK
 code[6]            = AddrLen (5/8/13)
 code[7] - code[31] = reserve
 code[32]           = Data
 code[33],code[34]  = Address
 */
int FormatAnalyzeSONY(unsigned int *buf, int bufSize, unsigned char *code, int codeSize) {

  int last = -1;
  int n = 0;
  int c = 0;
  int on = 0;
  int off = 0;
  int t = 1000;
  int r = 0;
  memset(code, 0, codeSize);
  for(int i = 0; i < bufSize * 32; i++) {
    int d = (buf[i / 32] >> (31 - (i % 32))) & 1;
    if((last < 0) && (d == 1)) continue; // skip nosignal

    if(d != last) { // data edge
      if(c && (last == 0)) {
        on = c;
        if(!n) { // leader
           t = on / 4;
        } else { // customer/data code
          if(((off + t / 2) / t) != 1) return -1;
          if(((on + t / 2) / t) == 2) {
            code[32 + (n-1)/8 + r * 4] |= 1 << ((n-1)%8);
          } else if(((on + t / 2) / t) != 1) {
            return -1;
          }
        }
        n++;
        if(n == 8) n++;
      } else if(c) {
        off = c;
      }
      c = 1;
    } else { // data continue
      c++;
      if(c > t * 20) { // trailer
        if(n) {
          code[32 + 3 + r * 4] = n;
          r++;
        }
        n = 0;
      }
    }
    last = d;
  }
  if(r < 3) return -1;
  n = code[32 + 3];
  if((n != (1 + 8 + 5)) && (n != (1 + 8 + 8)) && (n != (1 + 8 + 13))) return -1;
  if(memcmp(&code[32], &code[36], 3) || memcmp(&code[32], &code[40], 3)) return -1;

  code[3] = 32 + 3;
  code[4] = 3;
  code[5] = t;
  code[6] = n - 8 - 1;
  return 32 + 3;
}

int CodeExpandSONY(unsigned char *code, int codeSize, unsigned int *buf, int bufSize) {

  memset(buf, 0, bufSize * 4);
  int len = (((code[2] << 8) | code[3]) - 32) / 2;
  int t = code[5];
  int alen = code[6];

  int p = 0;
  for(int i = 0; i < 3; i++) {
    // leader
    for(int j = 0; j < 4 * t; j++) {
      if(!(p & 1)) buf[p / 32] |= (1 << (31 - (p % 32)));
      p++;
    }

    //Data
    for(int j = 0; j < 7; j++) {
      int d = (code[32] >> j) & 1;
      p += t;
      for(int k = 0; k < (d?t*2:t); k++) {
        if(!(p & 1)) buf[p / 32] |= (1 << (31 - (p % 32)));
        p++;
      }
    }

    //Addr
    for(int j = 0; j < alen; j++) {
      int d = (code[33 + (j / 8)] >> (j % 8)) & 1;
      p += t;
      for(int k = 0; k < (d?t*2:t); k++) {
        if(!(p & 1)) buf[p / 32] |= (1 << (31 - (p % 32)));
        p++;
      }
    }
    p = 3420 * i;
  }
  p += 31;
  return p / 32;
}