Shuichi Machida's Weblog

« [Sun SPOT で"デューク神棚(D... | メイン | [Sun SPOT(22)] Q:... »

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

[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();
    }
}

 

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

。。。

# デューク登場 ;-)


投稿されたコメント:

コメント
  • HTML文法 不許可

Valid HTML! Valid CSS!

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