Shuichi Machida's Weblog

« 前のページ | メイン | 次のページ »

http://blogs.sun.com/machida/date/20080516 2008年 5月 16日 金曜日

[Sun SPOT Demo(7)] Sound Effects KIT を使って効果音を出してみる。

昨日家に帰った後、無性に効果音を出してみたくなったので、家に転がっていたキットを使って試してみました。

# ELEKIT の8色バトルサウンド。プラモデルやジオラマセットに組み込めば効果抜群(何の?)のようです。。


このキットはジャンパを差し替えることで8種類の効果音を再生可能で、DC2.5~5.0Vの電源とスピーカーを接続して、スイッチを押したときに音が出ます。今回は、Sun SPOTで効果音の再生と再生時間を制御するためにいくつか改造します。改造点は以下の1~3です:

 

改造したサウンド発生回路をSun SPOTに接続します。

  • 電池ボックスの5Vを Sun SPOTのVH、GNDを Sun SPOTのGNDに
  • サウンド発生回路の +5V を Sun SPOTのH0、GNDを Sun SPOTのGNDに
  • スピーカーをサウンド発生回路のスピーカ接続用ソケットに
# 完成図

 

プログラムでは、大電流出力ポート H0 をOn, Of することで、サウンドの発生と再生時間を制御します。また、今回はサウンド発生のトリガーとして Sun SPOTの照度センサーを使用し、周りが暗くなったら効果音を発生させるようにします。

 

package org.sunspotworld;

import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ILightSensor;
import com.sun.spot.sensorboard.peripheral.ILightSensorThresholdListener;
import com.sun.spot.util.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class SoundEffectsDemoSpot extends MIDlet {
    private final int LOW_VALUE = 20;   // 暗い
    private final int HIGH_VALUE = 60; // 明るい
            
    private SoundEffects sounds;
    private ILightSensor lightSensor;
    
    private boolean lastHigh = false;

    /** ここからスタート */
    protected void startApp() throws MIDletStateChangeException {
        new BootloaderListener().start();   // monitor the USB (if connected) and recognize commands from host
        Spot.getInstance().getSleepManager().disableDeepSleep();
        
        sounds = new SoundEffects(EDemoBoard.H0);

        initLightSensor();
    }

    private void initLightSensor() {
        lightSensor = EDemoBoard.getInstance().getLightSensor();
        
        /* 照度センサーの値がある一定の閾値(low, high)を超えたときに
           呼び出されるイベントリスナーを登録 */
        lightSensor.addILightSensorThresholdListener(new ILightSensorThresholdListener() {
            public void thresholdChanged(ILightSensor light, int low, int high) {
                System.out.println("thresholdChanged( low=" + low + ", high=" + high + " )" );
            }
            
            public void thresholdExceeded(ILightSensor light, int val) {
                if (val <= LOW_VALUE) {
                    // 暗くなった
                    System.out.println("It's dark now!: " + val);
                    if (isLastHigh()) {
                        play();
                        setLastHigh(false);
                    }                        
                } else {
                    // 明るくなった
                    System.out.println("It's bright now!: " + val);     
                    setLastHigh(true);
                }
                lightSensor.enableThresholdEvents(true);
            }
        });
        
        // 照度センサーに閾値を設定し、イベント処理を有効にする
        lightSensor.setThresholds(LOW_VALUE, HIGH_VALUE);  // low, high(明るさ)   
        lightSensor.enableThresholdEvents(true);
    }

    
    private boolean isLastHigh() {
        return this.lastHigh;
    }
    
    private void setLastHigh(boolean lastHigh) {
        this.lastHigh = lastHigh;
    }
    
    /** 効果音を生成する */
    private void play() {
        sounds.play();
    }

    
    protected void pauseApp() {
        // This will never be called by the Squawk VM
    }

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
        // Only called if startApp throws any exception other than MIDletStateChangeException
    }
}

 

package org.sunspotworld;

import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.io.IOutputPin;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.sensorboard.peripheral.LEDColor;
import com.sun.spot.util.Utils;

public class SoundEffects {
    private static final long DEFAULT_DURATION = 5000L;
    private long duration;
    
    private IOutputPin out;
    private ITriColorLED[] leds;
    
    public SoundEffects(int pinId) {
        this(pinId, DEFAULT_DURATION);
    }
    
    public SoundEffects(int pinId, long duration) {
        out = EDemoBoard.getInstance().getOutputPins()[pinId];
        initLeds();
        
        if (duration > 0) {
            this.duration = duration;
        } else {
            this.duration = DEFAULT_DURATION;
        }
    }
    
    /** 効果音を生成する */
    public void play() {
        // 効果音スタート
        updateLeds(true);
        out.setHigh();
        
        Utils.sleep(duration);
        
        // 効果音ストップ
        updateLeds(false);        
        out.setLow();            
    }

    
    private void initLeds() {
        leds = EDemoBoard.getInstance().getLEDs();
        for (int i = 0; i < leds.length; i++) {
            leds[i].setOff();
            leds[i].setColor(LEDColor.BLUE);
        }
    }
    
    private void updateLeds(boolean isOn) {
        for (int i = 0; i < leds.length; i++) {
            leds[i].setOn(isOn);
        }
    }
}

それでは実行してみます。

。。。。

# ♪☆×○★!! 

 

動画はこちらです↓ (注:音が出ます)




http://blogs.sun.com/machida/date/20080514 2008年 5月 14日 水曜日

# [Sun SPOT Demo(1)-2] 距離センサー(Distance Sensor) Demo: 動画を撮ってみた。

もうひとつ、[Sun SPOT Demo(1)]でご紹介した距離センサーのデモの動画も撮ってみました。


[Sun SPOT Demo(4)-2] Isolate migration: 動画を撮ってみた。

[Sun SPOT Demo(4)] で実行中の音楽をSun SPOT間で移動させる、Isolate migration のデモをご紹介しましたが、動画を撮ってみました。


JASRACが怖いのでメロディーがショボいですが orz

http://blogs.sun.com/machida/date/20080512 2008年 5月 12日 月曜日

[Sun SPOTトラブルシューティング(2)] Sun SPOTが暴走して操作を受け付けなくなったときの対処法

うまく動かないアプリケーションをSun SPOTにインストールしてしまったり、起動時に致命的なエラーが発生した場合など、Sun SPOTが暴走してステータスLEDが点滅を繰り返し、以後のインストールやファームウェアのアップグレードを受け付けなくなってしまう(ようにみえる)ことがあります。

このような場合は、コマンドラインから以下のコマンド:

 # ant upgrade -Dport=<portnumber>

または

 # ant upgrade -Dspotport=<portnumber>

をお試しください。<portnumber> はSun SPOTが使用しているシリアル(COM)ポートの番号です。

おそらく、大抵の場合は復旧すると思います。

# 例

C:\> cd Sun\SunSPOT\sdk    (SDKのホームに移動)

C:\Sun\SunSPOT\sdk> ant upgrade -Dport=COM29


フォーラムの以下のポストも参考になると思います:

- SPOT recovery problem

http://blogs.sun.com/machida/date/20080507 2008年 5月 07日 水曜日

[Sun SPOTで"デューク神棚(Duke Temple)"を動かそう(4)] 動画を撮ってみた。

動画を撮ってみました。扉が開く時の音に、何となく趣が。。。 ないですね。

 

# Duke "model" temple



 

http://blogs.sun.com/machida/date/20080501 2008年 5月 01日 木曜日

[Sun SPOT API Tips(2)] Sun SPOTのバッテリー残量や電流消費量を調べたい時は?

よく

  • 「Sun SPOTのバッテリー残量を取得できますか?」
  • 「Sun SPOTの電流消費量を調べられますか?」

といった質問を受けることあります。 Solarium (a.k.a. SPOT World)という管理ツールを使うと、ホストPC上からリモートにある Sun SPOTのステータス情報を取得することができるのですが、Sun SPOTアプリケーションではSun SPOT API V3.0 IPowerController インタフェースが使えます。このAPIを使うとバッテリー残量や電流消費量などの電源関連の情報がいろいろ入手できます。

コード例:

package org.sunspotworld;

import com.sun.spot.peripheral.IPowerController;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.util.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {

    protected void startApp() throws MIDletStateChangeException {
        new BootloaderListener().start();   // monitor the USB (if connected) and recognize commands from host
        Spot.getInstance().getSleepManager().disableDeepSleep();
        
        IPowerController powerController =
                Spot.getInstance().getPowerController();

        
        while (true) {
            // Vbatt の値で、大体のバッテリー残量を推定可能
            // APIドキュメントによると、2700~4700mVの範囲
            System.out.println("battery supply(mV): " +
                    powerController.getVbatt());
            
            // 現在の電流消費量を取得
            System.out.println("current discharge(mA): " +
                    powerController.getIdischarge());        
            
            // 現在のチャージ電流の量を取得
            System.out.println("current charge(mA): " +
                    powerController.getIcharge());                        

            Utils.sleep(10000L);
        }
    }

    protected void pauseApp() {
    }

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
    }
}

 

# 実行結果


# 裏で無線通信しているので、その時だけ電流消費量が数十mA 上がっています。

なお、ホストPCにUSB接続している場合はバスパワーで動作して充電します。USBつないでいないのにchargeの方も値が出てる(ようにみえる)のは謎です。。

[Sun SPOT API Tips(1)] Sun SPOTのバッテリー残量少/リセット/パワーオフのタイミングで処理を実行したい時は?

Sun SPOT アプリケーションで

  • Sun SPOTのバッテリ残量が少なくなった
  • Sun SPOTをリセットした
  • Sun SPOTをパワーオフした
タイミングで処理を実行したい場合、FiqInterruptDaemon クラスが使えます。

o Sun SPOT API V3.0

FiqInterruptDaemon のインスタンスを Spot から取得して、イベントを処理するハンドラを登録します。例えばこんな感じです↓

package org.sunspotworld;

import com.sun.spot.peripheral.FiqInterruptDaemon;
import com.sun.spot.peripheral.IEventHandler;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.util.*;
import com.sun.squawk.VM;

import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {
    
    protected void startApp() throws MIDletStateChangeException {
        new BootloaderListener().start();   // monitor the USB (if connected) and recognize commands from host

        // FiqInterruptDaemon は、eSPOT(メインボード)の電源コントローラからの
        // イベントを処理するハンドラを扱うためのクラス
        FiqInterruptDaemon fiqDaemon =
                Spot.getInstance().getFiqInterruptDaemon();


        // リセットボタンを押した時の処理を実行するハンドラを登録する
        fiqDaemon.setButtonHandler(new IEventHandler() {
            public void signalEvent() {
                System.out.println("Resetting..." );
                // TODO: ここに何か処理を実装                                
                VM.stopVM(0);
            }
        });

        
        // 電源をオフにした時の処理を実行するハンドラを登録する        
        fiqDaemon.setPowerOffHandler(new IEventHandler() {
            public void signalEvent() {
                System.out.println("Powering off..." );
                // TODO: ここに何か処理を実装                
                // APIドキュメントによると、パワーオフまでにこのハンドラで
                // 処理できる時間は 400ms 程度。
            }
        });

        
        // バッテリー残量が少なくなった時の処理を実行するハンドラを登録する
        fiqDaemon.setLowBatteryHandler(new IEventHandler() {
            public void signalEvent() {
                System.out.println("Battery is Low!!" );
                // TODO: ここに何か処理を実装
            }
        });

        
        while (true) {
            // TODO: ここに何か処理を実装
            Utils.sleep(2000L);
        }
//        notifyDestroyed();                      // cause the MIDlet to exit
    }

    protected void pauseApp() {
    }

    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {
    }
}

http://blogs.sun.com/machida/date/20080428 2008年 4月 28日 月曜日

[Sun SPOT Demo(6)] OLED Display (μOLED-96-G1) を使ってみる。

 ストロベリー・リナックスで超小型のフルカラーOLED(有機EL)ディスプレイモジュールを見つけたので、Sun SPOTのeDemoボードに取り付けてみました。

# OLEDモジュール。かなり小さいです!

# 裏。microSDカードも使えます。

 以前 LCDをつないだ時にも書きましたが、Sun SPOTの eDemoボード上の汎用I/Oピン D0, D1を使うと簡単に非同期シリアル通信(UART)ができます (最大 baud rate は 38400, data 8bits, no parity, 1or2 stop bits)。

# UART で使用するI/Oピン

今回のOLEDモジュールもシリアル接続できるので、Sun SPOTで制御できそうです。ただ、OLEDモジュールは TTLレベル(0-3.3V)とのことで、そのまま Sun SPOTのRx と OLEDモジュールの Tx をつなぐとD0ピンの許容最大電圧 3.0Vを超えてしまいます。このような場合、レベルシフトバッファなどを使って電圧を調整するそうなのですが、抵抗で分圧しても良いみたいですので、今回は抵抗を使います。

# 1KΩと100Ωの抵抗で分圧。電圧計で実際に測ったところ 2.8V程度でしたが、ロジックレベル 'H' として十分認識される範囲なのでOKでしょうか。。

 

早速電子回路の作成です。 OLEDモジュールは最大で100mA以上消費するので、Sun SPOTの5Vでは危なそうなので外部電源を使います。

 # ちょっと拡大。相変わらず半田付けがてきとうです。目が悪すぎて手元が良く見えないせいということにしておきます ;-)

 作成した回路基板がSun SPOTにマウントできるように、裏にはピンヘッダを取り付けています。

 装着。

# 横から見るとこんな感じです。ArshanがUSから持ってきていたディスプレイボードとは違い、不恰好です orz

# まぁ、eProto ボードが手元にないのでしょうがないのですが。。

 。。。

ハードウェアは完成です!ここまでくれば、後は パパッと Javaでプログラムを作成してインストールするだけです。

。。。

電源を入れてみました。

# OLEDのロゴに続いて、、、 

# お約束の?メッセージが表示されました ;-) それにしても、このディスプレイ結構表示が綺麗です。

プログラムはこんな感じです。

#  メイン部分のプログラム(MIDlet)

package org.sunspotworld;

import com.sun.spot.peripheral.ISleepManager;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.util.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet implements MicroOLEDConstants {
    private IOled oled;  // OLED
    
    protected void startApp() throws MIDletStateChangeException {
        new BootloaderListener().start();
        
        // USBケーブルでPCにつないでいない状態で電源が切れないように、
        // 自動電源管理によるdeep sleepをオフにする
        ISleepManager sleepManager = Spot.getInstance().getSleepManager();
        sleepManager.disableDeepSleep();
        
        // uOLED-96-G1 にアクセスするためのクラスのインスタンスを生成
        oled = new MicroOLED96G1();

        
        // OLED にメッセージを表示
        try {
            oled.clear();
            oled.drawString("Hi!", 0, 0, FONT_8x12, RED);
            oled.drawString("This is", 0, 2, FONT_8x12, GREEN);
            oled.drawString("a Sun SPOT!!", 0, 3, FONT_8x12, BLUE);
        } catch (Exception ex) {
            ex.printStackTrace();
            Utils.sleep(2000L);
        }

        
        while (true) {
            // TODO: ここに処理を追加
            Utils.sleep(2000L);
        }
    }
    
    protected void pauseApp() {
    }
    
    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
    }
}

IOledは、OLEDモジュールにアクセスするためのインタフェースを定義します。

package org.sunspotworld;

public interface IOled {
    /** OLEDの画面をクリア */
    void clear() throws CommandExecutionException;
    /** column, row で指定された位置に引数で指定された文字列を表示する */
    void drawString(String text, int column, int row, int font, byte[] color) throws CommandExecutionException;
}

package org.sunspotworld;

public interface MicroOLEDConstants {
    // コマンド
    // ここでは、今回実際に使用するもののみ定義
    public static final byte ACK = (byte)0x06;
    public static final byte NAK = (byte)0x15;
    
    public static final byte COMMAND_INIT = (byte)'U';
    public static final byte COMMAND_CLS = (byte)'E';
    public static final byte COMMAND_DRAW_STRING = (byte)'s';
    
    // フォント関連
    public static final byte FONT_5x7 = (byte)0x00;
    public static final byte FONT_8x8 = (byte)0x01;
    public static final byte FONT_8x12 = (byte)0x02;
    
    // 文字列関連
    public static final byte STRING_TERMINATOR = (byte)0x00;

    // 色関連
    public static final byte[] BLACK = {(byte)0x00, (byte)0x00};
    public static final byte[] RED = {(byte)0xF8, (byte)0x00};
    public static final byte[] GREEN = {(byte)0x07, (byte)0xE0};    
    public static final byte[] BLUE = {(byte)0x00, (byte)0x1F};    
    public static final byte[] WHITE = {(byte)0xFF, (byte)0xFF};    
}

 

package org.sunspotworld;

public class CommandExecutionException extends java.lang.Exception {
    public CommandExecutionException() {
    }

    public CommandExecutionException(String message) {
        super(message);
    }
}

MicroOLED96G1 は IOled インタフェースの実装クラスで、OLEDにアクセスするための機能を実装します。なおここでは最小限の機能のみ実装しています(実際、このデバイスはかなり高機能です)。

package org.sunspotworld;

import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.util.Utils;
import java.io.IOException;

public class MicroOLED96G1 implements IOled, MicroOLEDConstants {
    private EDemoBoard demo;

    public MicroOLED96G1() {
        demo = EDemoBoard.getInstance();
        // baud: 38400bps, 8bits, no parity, 1 stop bit
        demo.initUART(EDemoBoard.SERIAL_SPEED_9600, false);
        initialize();
    }
   
    private void initialize() {
        send(COMMAND_INIT);
        try {
            receiveAck();
        } catch (CommandExecutionException ex) {
            ex.printStackTrace();
        }
    }

    public void clear() throws CommandExecutionException {
        send(COMMAND_CLS);
        receiveAck();
    }

   
    public void drawString(String text, int column, int row, int font, byte[] color) throws CommandExecutionException {
        byte[] data = text.getBytes();
        if (data.length > 255)
            throw new IllegalArgumentException("text length out of range: " + data.length);
       
        send(COMMAND_DRAW_STRING);
        send((byte)column);
        send((byte)row);
        send((byte)font);
        for (int i = 0; i < color.length; i++) {
            send(color[i]);
        }
        for (int i = 0; i < data.length; i++) {
            send(data[i]);
        }
        send(STRING_TERMINATOR);
       
        receiveAck();
    }

   
    private void send(final byte data) {
        demo.sendUART(data);
    }

    private byte receive(final long timeout) throws IOException {
        long startTime = System.currentTimeMillis();
        while (true) {
            try {
                byte data = demo.receiveUART();
                return data;
            } catch (IOException ex) {
                if ((timeout > 0) & (System.currentTimeMillis() - startTime) > timeout) {
                    throw ex;
                }
            }
            Utils.sleep(10L);
        }
    }

    private byte receiveAck() throws CommandExecutionException {
        int retry = 0;
        while (retry < 3) {
            Utils.sleep(20L);
            try {
                byte status = demo.receiveUART();
                if (!(status == ACK)) {
                    throw new CommandExecutionException("response -> " + Integer.toHexString(status));
                }
                return status;
            } catch (IOException ex) {
                System.out.println("Error in receiveAck: " + ex);
                retry++;
            }
        }
        throw new CommandExecutionException("No response" );
    }
}

とりあえず動かすことはできましたが、まだこのデバイスの持つ機能をほとんど使っていないので、時間があるときにもう少しいじってみようと思います。


http://blogs.sun.com/machida/date/20080425 2008年 4月 25日 金曜日

[Sun SPOT(23)] Sun SPOT SDK の'nightly builds' と SPOT Manager Tool v3.0 が公開されました。

前回のエントリで、「Sun SPOTのEmulator を試したい場合は SDK Beta プログラムに参加してみてください」と書いたのですが、その2日後ぐらいにフォーラムBlog SPOT Blog でビッグニュースが :-)

  • Sun SPOT Manager Tool v3.0 が公開されました
  • SDKの'nightly builds' がリリースされるようになりました
'nightly builds'  は2~3週間に1度リリースされるみたいですね。

Sun SPOT Manager ToolのURL:

  • http://www.sunspotworld.com/SPOTManager/


是非アクセスしてみてください。

http://blogs.sun.com/machida/date/20080422 2008年 4月 22日 火曜日

[Sun SPOT(22)] Q: 「まだ購入していないけど Emulator を使って Sun SPOTプログラムを試してみたい!」

以前、こちらのエントリ等でSun SPOT SDK のインストールや SPOT Managerツール、エミュレータの使用方法について紹介したのですが、書いた時点ではこれらのツールは Sun SPOT Java Development Kit (つまり実機)を購入頂いた方のみ使用可能ということで、途中からツールの配布URLを削除していました。

フォーラムDavidさんのブログでアナウンスされていますが、最新版の Software-only Beta の配布が始まっております。まだ購入していないけどエミュレータを使ってSun SPOTプログラムを試してみたい!という方は、是非 Beta プログラムに参加してみてください!

参加方法は David のブログを参照してください。ブログにあるアドレスにメールを送ればOKです。メールのSubject(件名)に "SDK Beta" と書くのを忘れずに、とのことです。

ツールに対する改善点やご要望などのフィードバックも、是非よろしくお願いいたします!

ツールのインストールや設定方法は  David のこのエントリでも詳しく紹介されています。


# Sun SPOTアプリケーションをエミュレータで実行中。。。


[Sun SPOT で"デューク神棚(Duke Temple)" を動かそう(3)] 距離センサー(Distance Sensor)で人を検出して扉を開閉する。

前回のエントリで扉の開閉までできましたので、次は神棚に距離センサーを取り付けて、

  • 前に人が立った時に自動的に扉が開く
  • 人が立ち去った時に扉が閉まる
ような動作を実装してみます。

# 距離センサーを取り付けたところ↓

前回作成した回路に、距離センサーを追加します。Sun SPOT の 5V、GNDに距離センサーの5V、GNDを、またアナログ入力ピンA0に距離センサーの出力を接続します。

# 接続の図。

 # ちょっと拡大。

ちょっと長いですが、プログラムはこんなかんじです↓

扉の開閉を安定させるために、3回以上連続して人を検出した/しなかった時に扉を開閉しています。

 package org.sunspotworld;

import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.io.IOutputPin;
import com.sun.spot.util.*;
import java.io.IOException;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class TempleDemoSpot extends MIDlet {
    private DoorController doorController;  // 扉開閉制御スレッド
    
    protected void startApp() throws MIDletStateChangeException {
        try {
            initAndRun();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (doorController != null) {
                doorController.terminate();
            }
        }
    }
    
    private void initAndRun() throws IOException {
        new BootloaderListener().start();
        Spot.getInstance().getSleepManager().disableDeepSleep();
        
        doorController = new DoorController();
        doorController.start();
        
        while (true) {
            // TODO:
            // 後でここに処理を追加する
            Utils.sleep(2000L);
        }
    }
    
    /**
     * DoorController クラスは、アナログ入力ピンA0に接続した距離センサーから
     * 定期的(DETECTION_INTERVAL間隔)に値を取得して、以下の動作を実行します:
     *  o COUNT_THRESHOLD 回連続して10cm~80cmの間に物体を検出した場合に扉を開きます
     *  o COUNT_THRESHOLD 回連続して物体を検出しなかった場合に扉を閉じます
     */
    class DoorController extends Thread {
        private final long DETECTION_INTERVAL = 1000L;
        private final int COUNT_THRESHOLD = 3;
        private TempleManager templeManager;    // 神棚の扉の開閉を制御するクラス
        private DistanceSensor distanceSensor;  // 距離計測用クラス

        private int countInRange;      // 連続して物体を検出した回数
        private int countOutOfRange;   // 連続して物体を検出しなかった回数
        
        private volatile boolean  running = true;
        private boolean doorOpened = false;   // 扉の状態(開: true, 閉: false)
        
        DoorController() throws IOException {
            // 大電流出力ピンH0、H1を使用する
            IOutputPin[] pins = EDemoBoard.getInstance().getOutputPins();
            templeManager = new TempleManager(
                    pins[EDemoBoard.H0],pins[EDemoBoard.H1]);
            
            // アナログ入力ピンA0を使用する
            distanceSensor = new DistanceSensor(EDemoBoard.A0);
        }
        
        public void run() {
            while (running) {
                try {
                    doDetect();    // 物体の検出を実行
                    updateDoorStatus();    // 扉の状態(開/閉)を更新
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                Utils.sleep(DETECTION_INTERVAL);
            }
        }
        
        /*
         * 物体の検出を実行する
         */
        private void doDetect() throws IOException {
            // 距離センサーから値を取得
            float distance = distanceSensor.getValue();
            if (distance >= 10.0f && distance <= 80.0f) {
                // 物体を検出した
                updateCountInRange();
            } else {
                // 物体を検出しなかった
                updateCountOutOfRange();
            }
        }
        
        /*
         * 扉の状態(開/閉)を更新する
         */
        private void updateDoorStatus() {
            if (countInRange >= COUNT_THRESHOLD) {
                // 連続COUNT_THRESHOLD回以上物体を検出した
                if (!isDoorOpened()) {
                    // 扉が閉じているので開く
                    templeManager.openSesame();
                    setDoorOpened(true);
                }
                countInRange = 0;
            } else if (countOutOfRange >= COUNT_THRESHOLD) {
                // 連続COUNT_THRESHOLD回以上物体を検出しなかった
                if (isDoorOpened()) {
                    // 扉が開いているので閉じる
                    templeManager.shutSesame();
                    setDoorOpened(false);
                }
                countOutOfRange = 0;
            }
        }
        
        private void updateCountInRange() {
            countInRange++;
            countOutOfRange = 0;
            System.out.println("updateCountInRange(countInRange=" + countInRange);
        }
        
        private void updateCountOutOfRange() {
            countOutOfRange++;
            countInRange = 0;
            System.out.println("updateCountOutOfRange(countInRange=" + countOutOfRange);
        }
        
        private boolean isDoorOpened() {
            return doorOpened;
        }
        
        private void setDoorOpened(boolean doorOpened) {
            this.doorOpened = doorOpened;
        }
        
        public void terminate() {
            running = false;
        }
        
        public void destroy() {
            templeManager.destroy();
        }
    }
    
    protected void pauseApp() {
    }
    
    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
        if (doorController != null) {
            doorController.destroy();
        }
    }
}

 package org.sunspotworld;

import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.io.IScalarInput;
import java.io.IOException;

public class DistanceSensor {
    private static final float MAX_VOLTAGE = 3.0f;
    
    private IScalarInput input;
    private int range;
    
    public DistanceSensor(int pinId) throws IOException {
        input = EDemoBoard.getInstance().getScalarInputs()[pinId];
        range = input.getRange();
        System.out.println("DistanceSensor init: range=" + range);
    }
    
    public float getValue() throws IOException {
        int value = input.getValue();
        float voltage = (float)value / range * MAX_VOLTAGE;
        System.out.println("A/D value -> " + value + ", voltage -> " + voltage);

        // calculates the distance
        float distance = 23.333f / (voltage - 0.236f) - 0.420f;
        System.out.println("Distance=" + distance);
        return distance;
    }
}

 

 package org.sunspotworld;

import com.sun.spot.sensorboard.io.IOutputPin;
import com.sun.spot.util.Utils;

public class TempleManager {
    private final long DURATION = 6000L;
    
    private IOutputPin out1;
    private IOutputPin out2;
    
    public TempleManager(IOutputPin out1, IOutputPin out2) {
        this.out1 = out1;
        this.out2 = out2;
        
        stop(1L);
    }
    
    /**
     * 扉を開く
     */
    public void openSesame() {
        System.out.println("openSesame()" );
        stop(1L);
        out1.setHigh();
        Utils.sleep(DURATION);
        out1.setLow();
    }
    
    /**
     * 扉を閉じる
     */
    public void shutSesame() {
        System.out.println("shutSesame()" );        
        stop(1L);
        out2.setHigh();
        Utils.sleep(DURATION);
        out2.setLow();
    }
    
    /**
     * ストップ
     */
    public void stop(long duration) {
        out1.setLow();
        out2.setLow();
        Utils.sleep(duration);
    }
    
    public void destroy() {
        out1.setLow();
        out2.setLow();
    }
}

 

それでは実行してみます。

。。。

# デューク登場 ;-)


http://blogs.sun.com/machida/date/20080421 2008年 4月 21日 月曜日

[Sun SPOT で"デューク神棚(Duke Temple)" を動かそう(2)] Hello Crystal Duke!!

 前回のエントリでDCモータの制御回路の簡単な動作確認まで行ったので、いよいよ神棚 の作成に取り掛かります。もともと工作セットのパーツで組み立て説明書が付いているのできっと簡単でしょう。

。。。

二時間経過。。

。。。 

く、結構めんどい。。。

。。。

か、完成! ;-)

# 神棚 

前回と同じように回路を接続します。

 # ちょっと拡大

さて、扉を開閉するプログラムはどうしましょう。元々付いていた音センサーを利用してもよいのですが、そういえば以前距離センサーを使ったことがあったので、これは使えそうです。 かしわ手で反応するのではなく、人が10cm~80cmの距離に近づいた時に扉が開いているように制御する、というのは良いかもです。ただ、そろそろ昼時も近づいてきたのでこれは後日の課題として、今日のところは単純に、扉の開閉を繰り返すSun SPOTプログラムを作成して動作確認だけすることにします。

こんな感じです↓

package org.sunspotworld;

import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.io.IOutputPin;
import com.sun.spot.util.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {
    private TempleManager templeManager;    // 神棚の扉の開閉を制御するクラス
    
    protected void startApp() throws MIDletStateChangeException {
        new BootloaderListener().start();
        Spot.getInstance().getSleepManager().disableDeepSleep();
        
        // 大電流出力ピンH0、H1を使用する
        IOutputPin[] pins = EDemoBoard.getInstance().getOutputPins();
        templeManager = new TempleManager(
                pins[EDemoBoard.H0],pins[EDemoBoard.H1]);

        while (true) {
            templeManager.openSesame();    // 扉を開く
            templeManager.stop(1000L);      // ストップ
            templeManager.shutSesame();    // 扉を閉じる
            templeManager.stop(1000L);      // ストップ
        }
    }
    protected void pauseApp() {
    }

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
        if (templeManager != null) {
            templeManager.destroy();
        }
    }
}

package org.sunspotworld;

import com.sun.spot.sensorboard.io.IOutputPin;
import com.sun.spot.util.Utils;

/**
 * 神棚の扉の開閉を制御するクラス
 */
public class TempleManager {
    private final long DURATION = 6000L;
    
    private IOutputPin out1;
    private IOutputPin out2;
    
    public TempleManager(IOutputPin out1, IOutputPin out2) {
        this.out1 = out1;
        this.out2 = out2;
        
        stop(1L);
    }
    
    /**
     * 扉を開く
     */
    public void openSesame() {
        stop(1L);
        out1.setHigh();
        Utils.sleep(DURATION);
        out1.setLow();
    }
    
    /**
     * 扉を閉じる
     */
    public void shutSesame() {
        stop(1L);
        out2.setHigh();
        Utils.sleep(DURATION);
        out2.setLow();
    }    

    /**
     * ストップ
     */
    public void stop(long duration) {
        out1.setLow();
        out2.setLow();
        Utils.sleep(duration);
    }    
    
    public void destroy() {
        out1.setLow();
        out2.setLow();
    }
}

Sun SPOT にプログラムをインストールして実行してみましょう。

。。。

# 扉が開き始めました!;-) なにやら透明な物体が。。。

 # クリスタルデューク登場!

 # 拡大。


http://blogs.sun.com/machida/date/20080420 2008年 4月 20日 日曜日

[Sun SPOT で"デューク神棚(Duke Temple)" を動かそう(1)] DC Motor を駆動してみる。

今朝は休日だというのになぜか6時に目が覚め、また寝るのも勿体無いので午前中を使って Sun SPOTで遊ぶことにしました。昼ぐらいまでに何か動くものを作れるでしょうか。ちょうど手近に神だな工作セット(TAMIYA製)が転がっていたので、これを改造して Duke 神棚 を作ることにします ;-)

# sound activated temple (TAMIYA)

この工作セットには元々音センサースイッチ回路がついており、かしわ手を打つと扉が開いて神さまが出てくる、という動作をします。扉の開閉の制御には FA-130 というDCモータを1つ使用しています。

# 付属している音センサースイッチ回路とDCモータ。今回は音センサー回路は使わず、Sun SPOTを使って扉の開閉を制御します。

DCモータでは数100mAの大電流が流れるため、Sun SPOTの出力ポートでは直接駆動することができません。またDCモータを1つの電源で正逆回転させるためには Hブリッジなどの回路を組む必要があります。そこで、今回はモータドライバICを使って制御回路を作成して、この回路を Sun SPOTの出力ポートで制御することにします。

モータドライバICとしては TA7291P などが書籍などでよく紹介されていますが、今回はそれより少しだけスペックが下(で安価: 100円ぐらい)なTA7291SG というICがちょうど転がっていたので、これを使用します。

# TA7291SG の外観

 

1時間半ぐらい半田ごてを動かして、以下のようなモータ制御回路を作成しました。

データシートや書籍、Webサイトを参考にすれば、比較的容易だと思います)

# モータ制御回路の外観 (パスコンが抜けてる。。4/22追記)

 # 裏。半田付けが下手で恥ずかしいので縮小。まぁ、動けば。。。

 

IN1, IN2 に、Sun SPOTの出力ポートを2つ接続して制御します。また、OUT1, OUT2 にモータを接続します。

さらに、モータ動作用の電源として1.5Vの単三電池x2、ドライバICとSun SPOTのVH用としてACアダプタ5V電源を使用します。ACアダプタのジャックは、CK-23 という電源コネクタ変換基板を使って2.54mm に変換してケーブルを引き出せるようにします。

# CK-23

 DCモータを動かすための準備が整ったので、ちょっとテストしてみることにします。

# ケーブルを接続したところ

Sun SPOTのVH ピンに5V電源、GNDにドライバ制御回路のGND、そして H0、H1 に制御回路の IN1、IN2をそれぞれ接続しています。

# 拡大

 あ、もちろんモータ制御用のSun SPOTアプリケーションも必要ですね。こんな感じで作成しました↓

package org.sunspotworld;

import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.io.IOutputPin;
import com.sun.spot.util.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {
    private IOutputPin h0;
    private IOutputPin h1;
    
    protected void startApp() throws MIDletStateChangeException {
        new BootloaderListener().start();
        Spot.getInstance().getSleepManager().disableDeepSleep();
        
        // 大電流出力ピンH0、H1を使用する
        IOutputPin[] pins = EDemoBoard.getInstance().getOutputPins();
        h0 = pins[EDemoBoard.H0];
        h1 = pins[EDemoBoard.H1];

        stop(1L);
        while (true) {
            forward(6000L);    // 6秒間正転
            stop(1000L);       // 1秒間ストップ
            reverse(6000L);    // 6秒間逆転
            stop(1000L);       // 1秒間ストップ
        }
    }

    /**
     * 正転
     */
    private void forward(long duration) {
        stop(1L);
        h0.setHigh();
        Utils.sleep(duration);
        h0.setLow();
    }
    
    /**
     * 逆転
     */
    private void reverse(long duration) {
        stop(1L);
        h1.setHigh();
        Utils.sleep(duration);
        h1.setLow();
    }    

    /**
     * ストップ
     */
    private void stop(long duration) {
        h0.setLow();
        h1.setLow();
        Utils.sleep(duration);
    }
        
    protected void pauseApp() {
    }

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
    }
}


最後に、制御回路のOUT1,OUT2にモータ(と羽根)を取り付けて早速実行してみます。。。

 

 

 。。。

回転しました ;-)


http://blogs.sun.com/machida/date/20080416 2008年 4月 16日 水曜日

[Sun SPOT(21)] SDC SQUARE 4月号 Sun SPOT連載第3回: 今回のテーマはSun SPOTアプリケーション開発です。

SDC SQUARE 4月号に、大野さんNetBeans 6 (Mobility)寺田さんGlassFish の記事とともに

が掲載されています。

。。。。

Sun SPOT :無線センサーデバイスの新潮流

第3回:Sun SPOT で Hello World!

シリーズ第3回となる今回のテーマは「Sun SPOTアプリケーション開発」です。
NetBeans IDEでプログラムを作成して、Sun SPOT上で動かしてみましょう。
センサーや入出力ピン、無線通信などの機能を使うためのAPIライブラリに
ついても見ていきます。最後に、これらの機能を使った簡単なアプリケーション
を紹介します。

。。。。

今回は、ちょっとしたソースコードも紹介しています。是非ご覧下さい! m(_ _)m

http://blogs.sun.com/machida/date/20080415 2008年 4月 15日 火曜日

[Sun SPOT(20)] 4/18(金)の Sun Business .Next 2008 & Japan ERC で Sun SPOT展示します。

2008年4月18日 (金) に同時開催されるサン・マイクロシステムズ株式会社主催のイベント:

o Sun Business .Next 2008 ~ビジネスフィールドに革新的なIT環境を~

o Japan Education & Research Conference

で Sun SPOT も展示予定になっております。

場所: 東京ミッドタウン


どちらのイベントも、サンが提供する最先端のテクノロジやソリューションをご覧いただけます(Japan ERCは教育研究機関向けのイベントです)。

参加費は無料ですが、事前登録制となっております。是非、御参加下さい!

 



Valid HTML! Valid CSS!

This is a personal weblog, I do not speak for my employer.