Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
FlashとArduinoで赤外線リモコン

概要

以前実験した赤外線リモコン(もどき)をFlash(ActionScript)から制御する実験です。NECフォーマットの赤外線リモコンをFlashから制御します。

目的

いろいろありますが、主なものとしては以下の通りです。

  • ArduinoとFlashとの連携方法をもう少し調べる
  • 最終的には、FlashからArduinoを制御し、赤外線リモコンのフレームワークを作成する

ArduinoとFlashとの連携

プロトコル

ArduinoとFlash(だけに限らずさまざまなホストコンピュータ)とを連携させるプロトコルとして、Firmataがあります。今回は、このプロトコルを利用することを考えました。

Firmataでは、Arduinoの操作を行うために、デジタルピンやアナログピンの操作用のコマンドが用意されています。しかし、今回作成するNECフォーマットの赤外線リモコンでは、38kHzのキャリア周波数で変調をかける必要があり、このためにはマイクロ秒単位の制御が必要となるため、FlashのコードからArduinoを直接制御するのは現実的ではないと考えました。

NECフォーマットの赤外線リモコンでは、2バイトのカスタムコードと1バイトのデータコードの合計3バイトのデータが必要です。このため、Flash側では、ボタンを押すたびに3バイトのバイナリデータをArduinoに送信することにしました。図にするほどでもありませんが、アプリケーションレベルのプロトコルとして一応図を書いておきます。

このプロトコルを実現するために、Firmataで用意されているSTRINGというメッセージを利用することにします。これは、任意の文字列を送信するためのメッセージで、SysExコマンドを利用して送信するメッセージです。具体的には、以下のデータフォーマットで送信されます(FirmataプロトコルはMIDIプロトコルを基に作成されたプロトコルです)。

Flash側のプログラム

Flash側のプログラムには、as3glueを利用しました。使い方については、こちらのページを参照してください。as3glueでは残念ながら、前述のSTRING_DATAを送信するためのコマンドはありませんでした。そこで、arduino.asに、以下のコードを追加しました。Firmataプロトコルでは、1回の送信で7ビット分送信するため、以下のようなコードになっています(下位7ビットを送信後、8ビット目を別途送信する)。

public function writeByteArray(byte:ByteArray):void
{
        writeByte(ARD_SYSEX_MESSAGE_START);
        writeByte(ARD_SYSEX_STRING);
        for (var i:Number = 0; i < byte.length; i++) {
                writeByte(byte[i] % 128);
                writeByte(byte[i] >> 7);
        }
        writeByte(ARD_SYSEX_MESSAGE_END);
                        
        flush();
}

Arduino側のプログラム

Arduinoでは、FlashからのSTRINGメッセージを受信するだけです。STRINGメッセージを受信した際のコールバック関数を用意し、それを登録(attach)するだけです。コールバック関数は、受信した文字列を引数にした関数です。具体的には、以下のようにして、コールバック関数を登録します。remoteContollerCallbackは、文字列を引数にする関数です。

Firmata.attach(STRING_DATA, remoteControllerCallback);

これにより、STRING_DATAを受信したときに、remoteControllerCallback関数が呼ばれます。このコールバック関数の中で、リモコンコードを送信します。

FlashからArduinoを制御する赤外線リモコンのフレームワーク

Flashプログラム

Flashのプログラムを以下に示します。下記以外に、as3glueが必要です。as3glueの使い方は、こちらを参照してください。また、as3glueに付属のArduino.asに、上記のコードを追加する必要があります。現状、東芝のDVDレコーダに対応して、カスタムコードを0x45、0xbcとしています。また、数字部分のキー(0~9と、+10、クリア)だけを実装しています。keyやcustom0、custom1を変えると、他のリモコンにも対応できると思います。リモコンのデータコードを調べるプログラムはこちらにおいてあります。

Flashは初心者なので、もっといいコーディング方法があれば教えてください。

プログラム 概要
Controller.as メインプログラム。ボタンの作成依頼、ボタンを押したときのイベントハンドラの登録を実施する。
ControllerButton.as ボタンを実際に作成する。

Controller.as

package  
{
        import flash.display.Sprite;
        import flash.events.Event;
        import flash.events.MouseEvent;
        import flash.utils.ByteArray;
        import net.eriksjodin.arduino.Arduino;

        public class Controller extends Sprite
        {
                //キーの定義:キーを押したときのデータコード、キーに表示する文字列、キーの座標を定義
                private var key:Object = [
                        {dataCode:0x01, text:"1", x:0, y:30 },
                        {dataCode:0x02, text:"2", x:60, y:30 },
                        {dataCode:0x03, text:"3", x:120, y:30 },
                        {dataCode:0x04, text:"4", x:0, y:60 },
                        {dataCode:0x05, text:"5", x:60, y:60 },
                        {dataCode:0x06, text:"6", x:120, y:60 },
                        {dataCode:0x07, text:"7", x:0, y:90 },
                        {dataCode:0x08, text:"8", x:60, y:90 },
                        {dataCode:0x09, text:"9", x:120, y:90 },
                        {dataCode:0x0a, text:"+10", x:0, y:120 },
                        {dataCode:0x00, text:"0", x:60, y:120 },
                        {dataCode:0x53, text:"クリア", x:120, y:120 },
                ];
                //カスタムコード
                private var customCode0:uint = 0x45;
                private var customCode1:uint = 0xbc;
                private var arduino:Arduino;

                public function Controller()
                {
                        var button:ControllerButton;
                        
                        arduino = new Arduino("127.0.0.1", 5335);

                        for each (var e:Object in key) {
                                button = new ControllerButton(e.text);
                                button.x = e.x;
                                button.y = e.y;
                                button.addEventListener(MouseEvent.CLICK, clickHandler(e.dataCode)); 
                                addChild(button);
                        }
                }

                public function clickHandler(dataCode:int):Function
                {
                        var data:ByteArray = new ByteArray();

                        return function(event:MouseEvent):void
                        {
                                data[0] = customCode0;
                                data[1] = customCode1;
                                data[2] = dataCode;
                                
                                arduino.writeByteArray(data);
                        }
                }
        }
}

ControllerButton.as

package
{
        import flash.display.DisplayObject;
        import flash.display.Sprite;
        import flash.display.SimpleButton;
        import flash.text.TextField;
        import flash.text.TextFormatAlign;
        import flash.text.TextFormat;
        import flash.text.TextLineMetrics;

        public class ControllerButton extends SimpleButton
        {
                private var buttonWidth:uint, buttonHeight:uint;
                private var title:String;
                
                public function ControllerButton(title:String = "title", width:uint = 50, height:uint = 20, 
                                                                                                upColor:uint = 0x0000ff, downColor:uint = 0x00ff00, overColor:uint = 0xff0000, hitStateColor:uint = 0x0000ff)
                {
                        this.title = title;
                        this.buttonWidth = width;
                        this.buttonHeight = height;
                
                        upState = drawRoundRectButton(upColor);
                        downState = drawRoundRectButton(downColor);
                        overState = drawRoundRectButton(overColor);
                        hitTestState = drawRoundRectButton(hitStateColor);
                
                        useHandCursor = true;
                }
        
                private function drawRoundRectButton(color:uint):Sprite
                {
                        var shape:Sprite = new Sprite();
                        
                        shape.graphics.beginFill(color);
                        shape.graphics.drawRoundRect(0, 0, buttonWidth, buttonHeight, 10, 10);
                        shape.graphics.endFill();
                        shape.addChild(makeTitle());
                        
                        return shape;
                }
                
                private function makeTitle():TextField
                {
                        var message : TextField = new TextField();
                        var format : TextFormat = new TextFormat();
                        
                        format.size = 18;
                        format.align = TextFormatAlign.CENTER;
                                                
                        message.text = title;
                        message.setTextFormat(format);
                        message.width = buttonWidth;
                        message.height = buttonHeight;
                        message.x = 0;
                        message.y = 0;
                        
                        return message;
                }
        }
}

Arduinoスケッチ

Arduinoのスケッチを以下に示します。以下の3つのファイルを、同一のフォルダに配置してコンパイルしてください。

プログラム 概要
rc_flash.pde メインプログラム。Flashから受信したリモコンコードを送信します。
irControllerNecFormat.h NECフォーマットのリモコンを制御するプログラムのヘッダファイル。
irControllerNecFormat.cpp NECフォーマットのリモコンを制御するプログラムのソースファイル。

rc_flash.pde

#include <Boards.h>
#include <Firmata.h>
#include "irControllerNecFormat.h"

//赤外線LEDを接続するポート(0~7)
irControllerNecFormat rc(6);

void remoteControllerCallback(char *buffer)
{
  rc.sendCommand(buffer[0], buffer[1], buffer[2]);
}

void setup()
{
  Firmata.setFirmwareVersion(0, 1);
  Firmata.attach(STRING_DATA, remoteControllerCallback);
  Firmata.begin(57600);
}

void loop()
{
  while(Firmata.available()) {
    Firmata.processInput();
  }
}

irControllerNecFormat.h

class irControllerNecFormat {
  public:
    // constructor
    irControllerNecFormat(byte ledPin, byte custom0, byte custom1);
    irControllerNecFormat(byte ledPin);
    void sendCommand(byte data);
    void sendCommand(byte custom0, byte custom1, byte data);
  private:
    byte _ledPin;   // the pin number which the LED is connected
    byte _custom0;  // custom code 
    byte _custom1;  // custom code'
    
    void sendData(byte date);
    void on(int num);
};

irControllerNecFormat.cpp

#include <WProgram.h>
#include "irControllerNecFormat.h"

irControllerNecFormat::irControllerNecFormat(byte ledPin) {
  _ledPin = 1 << ledPin;
  pinMode(ledPin, OUTPUT);  // set the ledPin as an OUTPUT  
}

irControllerNecFormat::irControllerNecFormat(byte ledPin, byte custom0, byte custom1) {
  _ledPin = 1 << ledPin;
  _custom0 = custom0;
  _custom1 = custom1;
  
  pinMode(ledPin, OUTPUT);  // set the ledPin as an OUTPUT
}

void irControllerNecFormat::sendCommand(byte custom0, byte custom1, byte data){
  on(346);                  // leader code(ON)
  delayMicroseconds(4500);  // leader code(OFF)
  
  sendData(custom0);       // custom code
  sendData(custom1);       // custom code
  
  sendData(data);           // data
  sendData(~data);          // ~data
  
  on(22);                   // stop bit
}

void irControllerNecFormat::sendCommand(byte data) {
  sendCommand(_custom0, _custom1, data);
}

void irControllerNecFormat::sendData(byte data) {
  for(int i = 0; i < 8; i++) {
    on(22);
    switch(data & 1) {
    case 0:
      delayMicroseconds(565);
      break;
    case 1:
      delayMicroseconds(1690);
      break;
    }
    data = data >> 1;
  }
}

void irControllerNecFormat::on(int num) {
  for(int i = 0; i < num; i++) {
    PORTD |= _ledPin;
    delayMicroseconds(9);
    PORTD &= ~_ledPin;
    delayMicroseconds(17);
  }
}

赤外線LEDの接続

rc_flash.pdeのrcをインスタンス化する際に指定したポートに、赤外線LEDを接続します。赤外線LEDは、このプログラムの都合上0番ピンから7番ピンに接続する必要があります。詳細は、こちらのページを参照してください。

最後に

このプログラムを動作させてDVDレコーダのチャンネルが変わることを確認しました。とりあえず、FlashとArduinoとを連携させて、赤外線リモコンを作ることができました。時間があれば、もう少しFlash側をブラッシュアップしていきたいと思います。

バージョン

Arduino 0022



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2011 garretlab all rights reserved.
inserted by FC2 system