arduinoでリモコン #02

 前回書いた通り、赤外線信号をコンピュータに保存、ついでに保存した信号を使って操作することに成功しました。

ハードウェアは

  • 07pin: スイッチ
  • 08pin: 赤外LED(+)
  • 12pin: 赤外受信モジュール(Vout)
  • 13pin: LED(arduinoオンボード)

だけ。スイッチのもう一方と赤外受信モジュールのGNDはGNDに、赤外受信モジュールのVccは5Vに。

そふとうぇあ

 UARTでの通信方法の仕様を少し厳密にしたので、arduino側のソフトウェアが変更しました。
 arduino→コンピュータへの転送では、L→0, H→1として、ヘッダをS(start)、フッタをE(end)としました。ここはコンピュータに持ってきたときにデータをいじりやすいように、このままで。

ここではヘッダとフッタはいらないような感じなのですが、念のため?

 逆にコンピュータ→arduinoでは、ちゃんとビットとして投げるようにしました。こちらではヘッダを0xAA, フッタを0x55とかにしてます。ちなみに上のデータはarduinoに投げる時はこのような状態になってます。

0000000: aa00 0000 001f fff1 f8cf c623 3f98 cc7e  ...........#?..~
0000010: 7f3f 1f8c fc7e 3f19 8fcc 6633 1f9f cc7e  .?...~?...f3...~
0000020: 7f3f 9f55                                .?.U

ちゃんとヘッダ0xAAとフッタ0x55が付加しております。ここではソフトウェア的にヘッダとフッタを認識するので必要です。


arduinoソフトウェア

#define SIZE 1536
#define WAIT 250
bool data[SIZE];
bool switch_prev = 1;
bool switch_tmp;
int len;

void setup()
{
  pinMode(13, OUTPUT);   // LED
  pinMode(12, INPUT);    // recv LED
  pinMode(8, OUTPUT);    // send LED
  pinMode(7, INPUT);     // switch
  digitalWrite(7, HIGH); // pull up
  Serial.begin(9600);
}

void loop()
{
  switch_tmp = digitalRead(7);
  if(switch_tmp == LOW && switch_prev == HIGH) { // recv state
    digitalWrite(13, HIGH);

    recv_data_led(data);
    send_data_uart(data);

  } else { // send state
    digitalWrite(13, LOW);

    if(Serial.available() > 0) {
      // pc command mode
      len = recv_data_uart(data);
      send_data_led(data, len);
    } else {
      // through mode
      through_led_data();
    }
  }
  switch_prev = switch_tmp;
}

void recv_data_led(bool buf[])
{
  bool f = 0;
  // wait until get signal
  while((f = digitalRead(12)) == 1);
  buf[0] = f;
  delayMicroseconds(WAIT);
  for(int i=1; i<SIZE; i++) {
    buf[i] = digitalRead(12);
    delayMicroseconds(WAIT);
  }
}

void send_data_led(bool buf[], int length)
{
  for(int i=0; i<length; i++) {
    if(buf[i] == 0) { // H
      for(int j=0; j<(WAIT/26); j++) {
        digitalWrite(8, HIGH);
        delayMicroseconds(13);
        digitalWrite(8, LOW);
        delayMicroseconds(13);
      }
    } else { // L
      delayMicroseconds(WAIT);
    }
  }
}


int recv_data_uart(bool bool_buf[])
{
  unsigned char bin_buf[64];
  int bin_index = 0;
  int tmp;
  int bool_index = 0;
  // header: 0xAA
  // footer: 0x55
  while(Serial.read() != 0xAA);
  while((tmp = Serial.read()) != 0x55) {
    if(tmp != -1) bin_buf[bin_index++] = (unsigned char)tmp;
  }

  // convert 8bits to 1bit
  for(int i=0; i<bin_index; i++) {
    bool_buf[bool_index++] = ((bin_buf[i]>>7)&0x01);
    bool_buf[bool_index++] = ((bin_buf[i]>>6)&0x01);
    bool_buf[bool_index++] = ((bin_buf[i]>>5)&0x01);
    bool_buf[bool_index++] = ((bin_buf[i]>>4)&0x01);
    bool_buf[bool_index++] = ((bin_buf[i]>>3)&0x01);
    bool_buf[bool_index++] = ((bin_buf[i]>>2)&0x01);
    bool_buf[bool_index++] = ((bin_buf[i]>>1)&0x01);
    bool_buf[bool_index++] = ((bin_buf[i]>>0)&0x01);
  }

  // return array size
  return bool_index;
}


void send_data_uart(bool buf[])
{
  // header: S
  // footer: E
  Serial.print('S');
  for(int i=1; i<SIZE; i++)
    Serial.print(buf[i]);
  Serial.print('E');
  Serial.print("\r\n");
}


void through_led_data()
{
  if(digitalRead(12) == 0) { // H
    for(int j=0; j<(WAIT/26); j++) {
      digitalWrite(8, HIGH);
      delayMicroseconds(13);
      digitalWrite(8, LOW);
      delayMicroseconds(13);
    }
  } else { // L
    delayMicroseconds(WAIT);
  }
}

信号保存プログラム

#!/usr/bin/env ruby
# coding: utf-8
require 'serialport'

sp = SerialPort.new "/dev/tty.usbserial-A600eAHN", 9600

loop do
  data = sp.readline.strip!
  if data =~ /^S([01]+)E$/
    tmp = $1.split('').map {|i| i.to_i }
    arr = []
    index = 0

    # convert 1bit to 8bits
    while index < tmp.length
      t  = (tmp[index+0]<<7) unless tmp[index+0] == nil
      t |= (tmp[index+1]<<6) unless tmp[index+1] == nil
      t |= (tmp[index+2]<<5) unless tmp[index+2] == nil
      t |= (tmp[index+3]<<4) unless tmp[index+3] == nil
      t |= (tmp[index+4]<<3) unless tmp[index+4] == nil
      t |= (tmp[index+5]<<2) unless tmp[index+5] == nil
      t |= (tmp[index+6]<<1) unless tmp[index+6] == nil
      t |= (tmp[index+7]<<0) unless tmp[index+7] == nil
      arr << t
      index += 8
    end

    arr.pop # last is not 255
    arr.pop while arr[-1] == 255 # delete bottom null data

    # set header and footer
    arr.unshift 170 # 0xAA
    arr.push 85     # 0x55

    #write data
    open('hoge.bin', 'wb') do |f|
      f.write arr.pack("C*")
    end
  end
end

信号送信プログラム

#!/usr/bin/env ruby
# coding: utf-8
require 'serialport'

sp = SerialPort.new "/dev/tty.usbserial-A600eAHN", 9600


sleep 3
sp.write File.read('hoge.bin')

 こんな感じでしょうかね。arduino側はスイッチを押すと信号をコンピュータに流す状態になって、流し終わると通常モードにもどります。通常モードは普段は受信モジュールからの信号をそのままLEDに流し込む事をやっていて、コンピュータからデータが流れてきたときには自動的にそれを流すようになります。ちょっと便利です。
 arduinoで赤外線リモコンの延長?をやってみたいなーって思う人がいたら、ソースのthrough_led_data()あたりを参考にすると良いと思います。
 信号受信プログラムはちょっと面倒な事してますが、arduinoから送信された信号を、aruduinoに送信できるようにコンバートして、保存するだけ。送信プログラムはもっと簡単で、単に保存した信号をarduinoに流し込むようになってます。

 と言う具合で本日はこれくらいにします。次はWebから送受信できるようにする事をやります。Webサーバを立ててそこからというより、何かAPI的なモノで操作する方向にします。それと信号の保存方法をもう少し厳密に決める必要がありそうです。yamlでやるかsqlを使うかなど、考える余地がありそうですね。

TODO

  • 済:arduinoで赤外線の送受信
  • 済:コンピュータに信号保存・コンピュータから信号送信
  • 未:Webからリモコン操作