주사위 사용하기

01. 주사위 눈을 읽자

주사위를 던져 보드 게임을 해보자. 주사위를 사용하기 위해서는 그림 1과 같이 먼저 스마트 로봇 론처의 환경 설정 > 주변기기에서 주사위를 등록해야 한다. 각각의 주사위는 제품 번호로 구분되는데, 주사위의 제품 번호는 그림 1과 같이 등록할 때 표시된다(주사위 종류 #1).

그림 1. 주변기기 등록

주사위를 던져 나오는 눈을 읽는 프로그램을 작성하시오.

RobotActivity 클래스를 상속한 액티비티를 만들고, onInitialized(Robot) 메소드와 onExecute() 메소드를 상속받아 새로 구현하자. 프로젝트에 스마트 로봇 라이브러리를 포함하는 것을 잊지 않기를 바란다.

 public class SampleActivity extends RobotActivity
 {
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onInitialized(Robot robot)
     {
     }

     @Override
     public void onExecute()
     {
     }
 }

주사위의 눈에 해당하는 디바이스로 EVENT_VALUE가 있다. 주사위의 윗면이 수직으로 위를 향하고 있을 때는 1부터 6까지의 값을 가지고, 주사위가 기울어져 있는 경우에는 -1부터 -6까지의 값을 가진다.

펜과 마찬가지로 Robot 클래스의 findDeviceById(int productId, int deviceId) 메소드를 사용하여 필요한 디바이스의 객체를 얻을 수 있다. 디바이스 ID만으로는 어느 주사위의 디바이스인지 구분할 수가 없기 때문에 productId에 제품 번호를 넣어주어야 한다. 여기서는 제품 번호가 1인 경우라고 가정하자.

 public class SampleActivity extends RobotActivity
 {
     private Device mValueDevice;

     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onInitialized(Robot robot)
     {
         mValueDevice = robot.findDeviceById(1, Dice.EVENT_VALUE); // 1번 주사위의 눈 디바이스
     }

     @Override
     public void onExecute()
     {
         int value = 0;
         if(mValueDevice.e())
         {
             value = mValueDevice.read();
         }
     }
 }

주사위 눈 디바이스는 이벤트 디바이스라는 것에 주의해야 한다. 이벤트가 발생하였을 때만 데이터가 갱신되기 때문에 데이터를 읽기 전에 반드시 Device 클래스의 e() 메소드를 사용하여 이벤트가 발생하였는지 확인해야 한다.

주사위를 살짝 놓으면 반칙이다. 주사위를 던졌다는 것은 어떻게 알 수 있을까? 다행히 주사위에는 자유 낙하 디바이스가 있어서 주사위를 던졌다는 것을 알 수 있다. 자유 낙하 디바이스로 EVENT_FALL이 있는데, 주사위를 던질 때마다 값이 1씩 증가한다.

 public class SampleActivity extends RobotActivity
 {
     private Device mValueDevice;
     private Device mFallDevice;
     private boolean mFall;

     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onInitialized(Robot robot)
     {
         mValueDevice = robot.findDeviceById(1, Dice.EVENT_VALUE);
         mFallDevice = robot.findDeviceById(1, Dice.EVENT_FALL);
     }

     @Override
     public void onExecute()
     {
         int value = 0;
         if(mFallDevice.e())
         {
             mFall = true;
         }
         if(mFall)
         {
             if(mValueDevice.e())
             {
                 value = mValueDevice.read();
             }
         }
     }
 }

자유 낙하 디바이스도 이벤트 디바이스인데, 값 자체는 중요하지 않다. 자유 낙하 이벤트가 발생하였는지만 확인하면 된다.

그런데, 한 가지 문제가 있다. 주사위를 던지면 데구르르 굴러가기 때문에 주사위 눈이 계속 바뀌게 된다. 어떻게 해결해야 할까? 주사위가 굴러가다가 멈추면 더이상 주사위 눈이 바뀌지 않을 것이다. 따라서 일정 시간동안 주사위의 눈이 바뀌지 않는지 확인하면 된다. 여기서는 1초동안 확인해 보자. 1초는 onExecute 메소드가 50번 호출되는 시간이다.

 public class SampleActivity extends RobotActivity
 {
     private Device mValueDevice;
     private Device mFallDevice;
     private boolean mFall;
     private int mOldValue;
     private int mCount;

     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onInitialized(Robot robot)
     {
         mValueDevice = robot.findDeviceById(1, Dice.EVENT_VALUE);
         mFallDevice = robot.findDeviceById(1, Dice.EVENT_FALL);
     }

     @Override
     public void onExecute()
     {
         int value = 0;
         if(mFallDevice.e())
         {
             mFall = true;
             mCount = 0;
         }
         if(mFall)
         {
             if(mValueDevice.e())
             {
                 value = mValueDevice.read();
                 if(value != mOldValue)
                     mCount = 0;
                 mOldValue = value;
             }
             if(++ mCount > 50)
             {
                 // 주사위가 멈추었다.
                 mCount = 0;
                 mFall = false;
             }
         }
     }
 }

이번에는 onDeviceDataChanged 메소드를 상속받아 구현해 보자.

 public class SampleActivity extends RobotActivity
 {
     private boolean mFall;
     private int mOldValue;
     private int mCount;

     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onExecute()
     {
         if(mFall)
         {
             if(++ mCount > 50)
             {
                 // 주사위가 멈추었다.
                 mCount = 0;
                 mFall = false;
             }
         }
     }

     @Override
     public void onDeviceDataChanged(Device device, Object values, long timestamp)
     {
         int value = 0;
         if(device.getProductId() == 1)
         {
             switch(device.getId())
             {
             case Dice.EVENT_FALL:
                 mFall = true;
                 mCount = 0;
                 break;
             case Dice.EVENT_VALUE:
                 if(mFall)
                 {
                     value = ((int[])values)[0];
                     if(value != mOldValue)
                         mCount = 0;
                     mOldValue = value;
                 }
                 break;
             }
         }
     }
 }

Device 클래스의 getProductId() 메소드를 사용하여 제품 번호가 1인 경우인지 확인하였는데, 어느 주사위를 던져도 상관이 없다면 이 조건문은 없어도 된다.

02. 멀리 가면 안되요

주사위는 지그비 무선 통신으로 로봇에 데이터를 전달한다. 와이파이 존에서 멀어지면 통신이 안되듯이 주사위도 로봇에서 멀어지면 데이터를 전달하지 못한다.

지그비 신호 세기를 알아내는 프로그램을 작성하시오.

주사위에는 지그비 신호 세기에 해당하는 디바이스로 SENSOR_SIGNAL이 있다. 신호 세기는 0부터 255까지의 값을 가지는데 주사위가 로봇 가까이 있으면 신호 세기가 커져서 값이 증가하고, 멀어지면 신호 세기가 작아져서 값이 감소하게 된다.

 public class SampleActivity extends RobotActivity
 {
     private Device mSignalDevice;

     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onInitialized(Robot robot)
     {
         mSignalDevice = robot.findDeviceById(1, Dice.SENSOR_SIGNAL);
     }

     @Override
     public void onExecute()
     {
         int signal = mSignalDevice.read();
     }
 }

이번에는 onDeviceDataChanged 메소드를 상속받아 구현해 보자.

 public class SampleActivity extends RobotActivity
 {
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onDeviceDataChanged(Device device, Object values, long timestamp)
     {
         int signal;
         switch(device.getId())
         {
         case Dice.SENSOR_SIGNAL:
             signal = ((int[])values)[0];
             break;
         }
     }
 }

03. 다른 센서는 없나요?

주사위에는 알버트 로봇과 마찬가지로 가속도 센서와 배터리 센서, 온도 센서가 있다. 각 센서의 값을 읽는 프로그램을 작성해 보자.

주사위의 가속도 센서에 해당하는 디바이스는 SENSOR_ACCELERATION이다. 3축 가속도 센서이므로 가속도 데이터는 크기 3의 배열로 되어 있으며, 각각 -8192부터 8191까지의 값을 가진다. 그림 2와 같이 가속도 센서의 X축은 주사위의 눈 2의 방향이 양수 값이고 반대 방향인 눈 5의 방향이 음수 값이다. Y축은 눈 3의 방향이 양수 값, 눈 4의 방향이 음수 값이며, Z축은 눈 1의 방향이 양수 값, 눈 6의 방향이 음수 값이다.

그림 2. 주사위의 가속도 센서 방향

주사위에 내장된 배터리의 상태를 알려 주는 센서 디바이스는 SENSOR_BATTERY이며, 0부터 100까지의 값을 가지고 배터리의 잔량을 %로 나타낸다. 주사위 내부의 온도를 알려 주는 온도 센서 디바이스는 SENSOR_TEMPERATURE이며, -40부터 88까지의 값을 가지고 온도를 섭씨(oC)로 나타낸다.

 public class SampleActivity extends RobotActivity
 {
     private Device mAccelerationDevice;
     private Device mBatteryDevice;
     private Device mTemperatureDevice;

     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onInitialized(Robot robot)
     {
         mAccelerationDevice = robot.findDeviceById(1, Dice.SENSOR_ACCELERATION);
         mBatteryDevice = robot.findDeviceById(1, Dice.SENSOR_BATTERY);
         mTemperatureDevice = robot.findDeviceById(1, Dice.SENSOR_TEMPERATURE);
     }

     @Override
     public void onExecute()
     {
         int accelerationX = mAccelerationDevice.read(0);
         int accelerationY = mAccelerationDevice.read(1);
         int accelerationZ = mAccelerationDevice.read(2);
         int battery = mBatteryDevice.read();
         int temperature = mTemperatureDevice.read();
     }
 }

onDeviceDataChanged 메소드를 상속받아 구현하면 다음과 같다.

 public class SampleActivity extends RobotActivity
 {
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }

     @Override
     public void onDeviceDataChanged(Device device, Object values, long timestamp)
     {
         int[] acceleration = new int[3];
         int battery;
         int temperature;
         switch(device.getId())
         {
         case Dice.SENSOR_ACCELERATION:
             acceleration[0] = ((int[])values)[0];
             acceleration[1] = ((int[])values)[1];
             acceleration[2] = ((int[])values)[2];
             break;
         case Dice.SENSOR_BATTERY:
             battery = ((int[])values)[0];
             break;
         case Dice.SENSOR_TEMPERATURE:
             temperature = ((int[])values)[0];
             break;
         }
     }
 }