Moverio Controller Function SDK
Developer's Guide for Android
This section describes required procedure for the application development of the Moverio controller.
The description explains how to install Android SDK using Windows 10 PC as an example.
Android Studio will be downloaded from following Android developer's site.
https://developer.android.com/studio/
Follow an installer instruction and install Android Studio on your PC.
The later explanation is based on the SDK is installed in the following folder
C:\Users\<user name>\AppData\Local\Android\Sdk
When developing an application in a network environment that requires proxy settings, set the Android Studio proxy settings. Please check the detailed procedure on the site below.
https://developer.android.com/studio/intro/studio-config#gradle-plugin
If you do not know the proxy settings, ask your network administrator how to connect to the external network using the proxy.
Use the Android SDK Manager for the tools required for application development. Please check the detailed procedure on the site below.
https://developer.android.com/studio/intro/update#sdk-manager
Use the ADB connection confirmation command to check if the computer and host device are connected.
Start a command prompt and run;
"cd C:\Users\<user name>\AppData\Local\Android\sdk\platform-tools"
*It is convenient to set the above path as an environment variable.
If you execute "adb devices" and the device name is displayed in the list, ADB connection is established.
If it is not displayed, reconnect the host device with USB and execute "adb devices" again.
Moverio Controller Function SDK has dedicated API for controlling the Moverio controller. Describe the method for using dedicated API.
Following description is based on using Android Studio.
Display the Project View of Android Studio and create a "libs" folder from [File]-[New]-[Directory].
After C:¥Users¥<user name>¥AndroidStudioProjects¥<application name>¥app¥libs is created.
Put MoverioControllerFunctionSDK_1.0.0.aar there.
Then define your application's minimum API level at 28 and add MoverioControllerFunctionSDK_1.0.0.aar to your application's dependencies.
apply plugin: 'com.android.application'
android {
:
// Add API level 28
minSdkVersion 28
:
}
dependencies {
:
// Add Moverio Controller Function SDK.aar
implementation files('libs/MoverioControllerFunctionSDK_1.0.0.aar')
:
:
}
Press the Sync Project with Gradle Files button at the top of Android Studio to reflect Gradle changes to the project.
When developing an application for a headset model (USB connection type Moverio headset), confirm the application behavior in the state of connecting Moverio headset and the Moverio controller via USB. However, the PC and the Moverio controller are not connected by USB, so wired debugging cannot be performed at this time.
This section describes how to develop and debug application software for headset models and the Moverio controller that are connected via USB by connecting to the Moverio controller via a network. Please check the following sites as well.
https://developer.android.com/studio/command-line/adb#wireless
# adb tcpip 5555
# adb connect ip_address (e.g. adb connect 192.168.1.10#5555)
Firstly, please import com.epson.moverio.btcontrol.BtCustomKey such as below.
import com.epson.moverio.btcontrol.BtCustomKey;
Next, please create an instance of BtCustomKey class and specify the key constants in the table below in the first argument of the setKeyEnable() method, and specify a boolean type lock (false) and unlock (true) in the second argument and call it.
Constants | Description |
---|---|
BtCustomKey.VOLUME_UP | Volume up key |
BtCustomKey.VOLUME_DOWN | Volume down key |
BtCustomKey.TRIGGER | Function key |
BtCustomKey.HOME | Home key |
BtCustomKey.BACK | Back key |
BtCustomKey.APP_SWITCH | History key |
BtCustomKey.POWER | Power button |
BtCustomKey.ALLKEY | Used to specify all physical keys |
public class UIControlActivity extends Activity {
private BtCustomKey mBtCustomKey = null;
private Button mButton_powerKeyLock = null;
@Override
public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBtCustomKey = new BtCustomKey(this);
mButton_powerKeyLock = (Button) findViewById(R.id.button_powerKeyLock);
mButton_powerKeyLock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mBtCustomKey.setKeyEnable(BtCustomKey.POWER, false); /* Disable power key. */
}
});
}
:
:
}
This API can change the key code assigned to the physical key of the Moverio controller. However, it is impossible to change the key code of the power button.
Firstly, please import com.epson.moverio.btcontrol.BtCustomKey such as below.
import com.epson.moverio.btcontrol.BtCustomKey;
Next, please create an instance of the BtCustomKey class and specify the target physical key in the table below as the first argument of the setKeyAssign() method, and specify the key code as the second argument.
Target Physical Key | Key Code |
---|---|
BtCustomKey.VOLUME_UP | KeyEvent.KEYCODE_VOLUME_UP |
BtCustomKey.VOLUME_DOWN | KeyEvent.KEYCODE_VOLUME_DOW |
BtCustomKey.TRIGGER | KeyEvent.KEYCODE_FUNCTION |
BtCustomKey.HOME | KeyEvent.KEYCODE_HOME |
BtCustomKey.BACK | KeyEvent.KEYCODE_BACK |
BtCustomKey.APP_SWITCH | KeyEvent.KEYCODE_APP_SWITCH KeyEvent.KEYCODE_F1 |
KeyEvent.KEYCODE_F2 | |
KeyEvent.KEYCODE_F3 | |
KeyEvent.KEYCODE_F4 |
public class UIControlActivity extends Activity {
private BtCustomKey mBtCustomKey = null;
private Button mButton_appSwitch2Home = null;
@Override public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBtCustomKey = new BtCustomKey(this);
mButton_appSwitch2Home = (Button) findViewById(R.id.button_appSwitch2Home);
mButton_appSwitch2Home.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
mBtCustomKey.setKeyAssign(BtCustomKey.APP_SWITCH, KeyEvent.KEYCODE_HOME);
}
});
}
:
:
}
This API can initialize key status that are changed by "Key operation lock" and "Key code customization".
Please execute resetToDefault() on instance that was generated for "Key operation lock" for "Key code customization".
mBtCustomKey.resetToDefault();
Note : Initializing key status by key operations: You can initialize the key status by holding down the Function key, Home key, and Volume Up key at the same time for three seconds. Use this to force the key status to initialize when an application closes abnormally while the key status has been changed.
This API can control LED indicator of the Moverio controller (Lighted mode/Not lighted mode) .
Firstly, please import com.epson.moverio.btcontrol.BtControllerLedMode.
import com.epson.moverio.btcontrol.BtControllerLedMode;
Next, please create an instance of BtControllerLedMode class and specify constants in the table below as the first argument of the setControllerLedMode() method.
Constants | Description |
---|---|
BtControllerLedMode.MODE_OFF | Not lighted mode |
BtControllerLedMode.LED_MODE_NORMAL | Lighted mode |
public class UIControlActivity extends Activity {
private BtControllerLedMode mBtLed = null;
private Button mButton_ledOff = null;
@Override public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBtLed = new BtControllerLedMode (this);
mButton_ledOff = (Button) findViewById(R.id.button_appSwitch2Home);
mButton_ledOff.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
mBtLed.setControllerLedMode(BtControllerLedMode.MODE_OFF);
}
});
}
:
:
}
The Moverio controller has Mirror mode (Same picture is shown between headset and controller), Trackpad mode(Usable trackpad on controller screen) and Assist mode (Usable arrow key on controller screen).
This API can switch these modes.
Firstly, please import com.epson.moverio.btcontrol.UIControl.
import com.epson.moverio.btcontrol.UIControl;
Next, please create an instance of UIControl class and specify constants in the table below as the first argument of the setUiMode() method.
Constants | Description |
---|---|
UIControl.UI_MODE_MIRROR | Switch to Mirror mode |
UIControl.UI_MODE_TRACK | Switch to Trackpad mode |
UIControl.UI_MODE_ASSIST | Switch to Assist mode |
public class UIControlActivity extends Activity {
private UIControl mUiMode = null;
private Button mButton_modeTrack = null;
@Override public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUiMode = new UIControl (this);
mButton_modeTrack = (Button) findViewById(R.id.button_appSwitch2Home);
mButton_modeTrack.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
mUiMode.setUiMode(UIControl. UI_MODE_TRACK);
}
});
}
:
:
}
Note : When the modes are switched by this AP, the application is closed automatically to application screen reconstruction. After completed to switch mode, the application screen (Activity) before closed is shown again automatically.
Assist mode is added from version 2.1.00. This mode is usable arrow key on controller screen.
When mode is switched to Assist mode, arrow key and enter key are shown on controller screen. Application can get following key events by dispatchKeyEvent() of Android API depending on key operation.
Key name | Key event |
---|---|
Up key | KeyEvent.KEYCODE_DPAD_UP |
Down key | KeyEvent.KEYCODE_DPAD_DOWN |
Left key | KeyEvent.KEYCODE_DPAD_LEFT |
Right key | KeyEvent.KEYCODE_DPAD_RIGHT |
Enter key | KeyEvent.KEYCODE_DPAD_CENTER |
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP && e.getAction() == KeyEvent.ACTION_DOWN) {
// Press Up key
} else if(e.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN && e.getAction() == KeyEvent.ACTION_DOWN) {
// Press Down key
} else if(e.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT && e.getAction() == KeyEvent.ACTION_DOWN) {
// Press Left key
} else if(e.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT && e.getAction() == KeyEvent.ACTION_DOWN) {
// Press Right key
} else if(e.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER && e.getAction() == KeyEvent.ACTION_DOWN) {
// Press Enter key
}
return true;
}
If the bar of upper part of controller screen is pulled down, Assist mode is finished and mode transfer to Trackpad mode.
If the toggle button of "Settings->Accessibility->Arrow Key" is ON, Assist mode is usable without setUiMode(). (Default setting is OFF)
If above setting is on, the bar is shown on lower part of controller screen. If this bar is pulled up on Trackpad mode, mode transfer to Assist mode. If this bar is pulled down on Assist mode, mode transfer to Trackpad mode.
Note : Assist mode is the feature for new application development. Please note that existing applications of including pre-install applications usually don't not correspond screen operation (can't operate screen) by arrow key and enter key.
This API can switch enable/disable of the Key Lock feature that executed at the time of holding down function key.
Firstly, please import com.epson.moverio.btcontrol.KeyLock.
import com.epson.moverio.btcontrol.KeyLock;
Next, please create an instance of KeyLock class and specify constants in the table below as the first argument of the setKeyLock() method.
Constants | Description |
---|---|
KeyLock.KEY_LOCK_ENABLE | Enable Key Lock feature |
KeyLock.KEY_LOCK_DISABLE | Disable Key Lock feature |
public class UIControlActivity extends Activity {
private KeyLock mKeyLock = null;
private Button mButton_keyLock = null;
@Override public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mKeyLock = new KeyLock (this);
mButton_keyLock = (Button) findViewById(R.id.button_keyLock);
mButton_keyLock.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
mKeyLock.setKeyLock(KeyLock.KEY_LOCK_ENABLE);
Handler handler = new Handler(getMainLooper());
handler.postDelayed(new Runnable(){
public void run() {
if (keyLock.getKeyLock() == KeyLock.KEY_LOCK_ENABLE) {
keyLock.setKeyLock(KeyLock.KEY_LOCK_DISABLE);
}
}
}, 10000);
}
});
}
:
:
}
*When Key Lock feature is enabled, all operations of the Moverio controller (Physical key operation, Touch operation) are disabled. If Key Lock feature doesn't need to enable, please disable this feature promptly. Following code enable Key Lock feature and will be disabled automatically after 10 second. In addition, Key Lock feature is disabled automatically when the Moverio controller enter to sleep mode.
BT-35E and BT-45C have front facing headset camera. The Moverio controller can get camera data (picture, video) by Android Camera API or Moverio SDK. In addition, the camera data can be applied to various uses (e.g. marker recognition, etc.).
This section describes how to control BT-35E camera and BT-45C camera with the Moverio controller.
*Note : This document is for OS version 2.1.00 of the Moverio controller.
The Moverio controller can control BT-45C/BT-35E camera using Android CameraAPI or Moverio Basic Function SDK. Please refer to developer guide of Moverio Basic Function SDK for way of controlling camera by SDK. However, camera control by Android CameraAPI and Moverio Basic Function SDK cannot use at the same time. After controlling the camera with the Moverio Basic Function SDK, Android CameraAPI cannot detect the camera. When using Android CameraAPI after using camera control by Moverio Basic Function SDK, please reconnect BT-45C / BT-35E.
There are CameraAPI1 and CameraAPI2 available for Android CameraAPI. Please refer to following information for the usages of each API.
https://developer.android.com/training/camera
*Note:CameraAPI1 is deprecated
https://developer.android.com/training/camera2
https://medium.com/google-developers/detecting-camera-features-with-camera2-61675bb7d1bf
The following information is sample code for a camera application using CameraAPI2. Build and run app to the Moverio controller and launch the application with BT-45C/BT-35E connected, BT-45C/BT-35E camera can now be used. Please refer to this for implementing application.
https://github.com/googlearchive/android-Camera2Basic
It is possible to control the BT-45C/BT-35E camera in the Moverio controller by Android CameraAPI2. However, it is impossible to change some settings from Android CameraAPI2. Please use Moverio Basic Function SDK if want to change these settings. The spec of BT-45C/BT-35E camera and controllability of the settings by each API / SDK are described in "The correspondence table of setting controllability by Android CameraAPI2 / Moverio SDK".
The correspondence table of setting controllability by Android CameraAPI2 / Moverio Basic Function SDK
Settings | BT-35E | BT-45C | Android CameraAPI | Moverio Basic Function SDK |
---|---|---|---|---|
Resolution | 640x480 | 640x480 | ||
1280x720 | 1280x720 | |||
1600x1200 | ||||
1920x1080 | 1920x1080 | |||
2560x1440 | ||||
2592x1944 | ||||
3264x2448 | ||||
Exposure compensation | auto/manual | auto/manual | auto only | |
The step of manual exposure compensation |
-5 ~ +5 | -7 ~ +5 | ||
Focus mode | Off (Pan) | auto/manual | auto only | |
Manual focus | +50 ~ +3000[mm] | |||
Sharpness | 0 ~ +128 | |||
Brightness | -127 ~ +127 | 0 ~ +255 | ||
Gain | 0 ~ +255 | +1024 ~ +16383 | ||
White balance | auto/cloudy_daylight(6000K)/ daylght(5500K)/fluorescent(4200K)/ incandescent(3200K)/twilight(3500K) |
auto/cloudy_daylight(6000K)/ daylight(5500K)/fluorescent(4200K)/ incandescent(3200K)/twilight(3500K) |
auto only | |
Power frequency | 50Hz/60Hz | 50Hz/60Hz/Disable | ||
Indicator mode | auto | Auto/on/off |
The Moverio controller has the camera (Back camera). Normally, Android CameraAPI user this camera. On the other hand, the Moverio controller has the feature that Android CameraAPI give priority to use BT-45C/BT-35E camera (hereinafter called "headset camera"). This feature is called "the feature of priority using headset camera". Please refer to "Camera control specification for the Moverio controller using Android CameraAPI" for this feature of software specification. In addition, the setting of "Camera Custom State" is added from OS version 2.1.00 in order that the Moverio controller select to priority use headset camera or not.
The feature of priority using headset camera is activated under the following conditions at OS version 2.1.00.
The Moverio controller also had the feature of priority using headset camera on OS version 2.0.00. However, activate conditions of the feature of priority using headset camera are different in part with OS version 2.1.00. Please note it. "Active conditions of the feature of priority using headset camera" is described for the details. "-" of this table mean that standard behavior of Android CameraAPI.
Activate condition of the feature of priority using headset camera
OS version | Version2.0.00 | Version2.1.00 | |||||
---|---|---|---|---|---|---|---|
Mode of the Moverio controller | Other conditions | Camera Custom State does not exist | Camera Custom State is ON | Camera Custom State is OFF | |||
Connect H camera* |
Disconnect H camera |
Connect H camera |
Disconnect H camera |
Connect H camera |
Disconnect H camera |
||
Mirror mode | - | - | - | - | - | ||
Trackpad mode | - | - | - | - | |||
Assist mode |
Camera Custom State is OFF on default. When BT-45C or BT-35E is connected at first time, this setting become ON automatically. If you want to change this setting, please operate the toggle button of "Setting -> Accessibility-> Camera Custom State". This setting will not become ON automatically if when BT-45C or BT-35E is connected from the second time.
When the feature of priority using headset camera is activated, the specification of some Android CameraAPI of the Moverio controller are different with standard Android CameraAPI. This topic explains about this specification on CameraAPI2.
The code snippet for getting camera characteristics
CameraManager manager = (CameraManager)getSystemService(CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
// Do something with the characteristics
} catch (CameraAccessException e) {
e.printStackTrace();
}
"The code snippet for getting camera characteristics" specify the cameraId and get camera characteristics. The Moverio controller has two USB port (Bottom port, Side port) and it is possible to control headset camera and USB camera by connecting these. However, headset camera is supported on Bottom port only.
When headset camera is connected to Bottom port and the feature of priority using headset camera is activated, the cameraId is assigned such as "The cameraId at connecting headset camera".
The cameraId at connecting headset camera
Camera API | The Moverio controller Back Camera | Bottom port | Side port * |
---|---|---|---|
Camera API1 | 1 | 0 | 2 |
Camera API2 | /dev/video3 | 0 | /dev/video4 |
* In case of connecting USB camera to Side port
The Moverio controller gets camera characteristics from CameraCharacteristics and can select intended camera.
The code snippet for getting camera lends direction
CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
Integer facing = chars.get(CameraCharacteristics.LENS_FACING);
"The code snippet for getting camera lens facing" specify cameraId and get the lens facing from camera characteristics.
When headset camera is connected to Bottom port and the feature of priority using headset camera is activated, the lens facing is assigned such as "The lens facing at connecting headset camera".
The lens facing at connecting headset camera
Camera API | The Moverio controller Back Camera | Bottom port | Side port * |
---|---|---|---|
Camera API1 | FRONT | BACK | FRONT |
Camera API2 | EXTERNAL | BACK | EXTERNAL |
* In case of connecting USB camera to Side port
"The code snippet for using headset camera" is that code snippet in case of using headset camera.
The code snippet for using headset camera
CameraManager manager = (CameraManager)getSystemService(CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
Integer facing = chars.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
// Open BT-45C/BT-35E camera (Please refer to upper link)
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
The information of cameraId is used for not only opening camera but setTorchMode API that uses camera flash light. The table "The cameraId at connecting headset camera" is applied for setTorchMode too. When want to use back flash light of the Moverio controller with connecting headset camera and the feature of priority using headset camera is activated, it is necessary to specify "/dev/video3" as cameraId.
When USB camera is connected to USB port (Bottom or Side), the cameraId is assigned such as "The cameraId at connecting USB camera" regardless of status of the feature of priority using headset camera.
The cameraId at connecting USB camera
Camera API | The Moverio controller Back Camera | Bottom Port * | Side Port * |
---|---|---|---|
Camera API1 | 0 | 1 or 2 | 1 or 2 |
Camera API2 | 0 | /dev/video3 or 4 | /dev/video3 or 4 |
* cameraId can be assigned a lower number Id from the first recognized device
The lens facing is assigned such as "The lens facing at connecting USB camera".
The lens facing at connecting USB camera
Camera API | The Moverio controller Back Camera | Bottom Port | Side Port |
---|---|---|---|
Camera API1 | BACK | FRONT | FRONT |
Camera API2 | BACK | EXTERNAL | EXTERNAL |
If the Moverio controller use USB camera, please specify the parameters of "The cameraId at connecting USB camera" and "The lens facing at connecting USB camera" to the source code of "The code snippet for using BT-45C/BT-35E camera".
At Android CameraAPI2 sample source code, the screen rotation process depending on the Moverio controller rotate state is executed in order to do appropriate camera preview.
However, when the same process is executed using headset camera, the Moverio controller rotate state and headset rotate state don't synchronize. As a result, the mismatch of camera preview is occurred. Therefore, when the Moverio controller use headset camera, recommend to not include the screen rotation process such as "The source code of not including screen rotation process" (Comment out "matrix.postRoatate()").
The source code of not including screen rotation process
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(), (float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
// matrix.postRotate(90 * (rotation - 2), centerX, centerY);
}
In addition, the return values of AndroidAPI regarding screen rotation are fixed value on Trackpad mode like "The AndroidAPI regarding screen rotation".
The AndroidAPI regarding screen rotation
Android API | Return value |
---|---|
Display#getRotation() | ROTATION_0 |
Configuration#orientation | ORIENTATION_LANDSCAPE |
The mounting angle differs between the camera mounted on the Moverio controller and headset camera. Please refer to the table below for details.
The return value of CameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)
Type of Camera | Return value |
---|---|
The Moverio controller Back Camera | 90 |
BT-45C/BT-35E Camera | 0 |
herefore, if use headset camera and preview the image assuming 90-degree mounting angle, the preview screen may be distorted. For example, a square object will appear as a rectangle. It is recommended that pay attention to the mounting angle and preview the image appropriately for the resolution of the acquired image.
The Android CameraAPI2 sample source code assumes a 90-degree mounting angle for previewing. With the following modification such as "The code of changing appropriate aspect ratio", headset camera can preview with the proper aspect ratio.
The code of changing appropriate aspect ratio
if(mSensorOrientation != 0){
// When the mounting angle is not 0-degree, it is dedicated controller back camera, so it should be the same as the original source code.
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
mTextureView.setAspectRatio(mPreviewSize.getWidth(),mPreviewSize.getHeight());
} else {
mTextureView.setAspectRatio(
mPreviewSize.getHeight(), mPreviewSize.getWidth());
}
} else {
// When the mounting angle is 0-degree, it is determined to be BT-35E camera (USB camera).
//BT-35E camera doesn't rotate regardless of the device state, so the aspect ratio is always assumed to be horizontal preview.
mTextureView.setAspectRatio(
mPreviewSize.getWidth(), mPreviewSize.getHeight());
}
if(mSensorOrientation !=0) {
// For BT-35E camera, the following process is not necessary because BT-35E camera doesn't rotate regardless of the device state.
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(), (float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
}
BT-45C, BT-40 and BT-35E have various sensors (hereinafter called "headset sensor") in the headset that detect movement, direction, and ambient illuminance. By using these sensor data, it is possible to guess the movement of the wearer's head and the surrounding brightness.
This section describes how to control headset sensor with the Moverio controller.
*Note : This document is for OS version 2.1.00 of the Moverio controller.
The Moverio controller can control headset sensor using Android Sensor API or Moverio Basic Function SDK. Please refer to developer guide of Moverio Basic Function SDK for way of controlling headset sensor by SDK. However, headset sensor control by Android Sensor API and Moverio Basic Function SDK cannot use at the same time. After controlling headset sensor with the Moverio Basic Function SDK, Android Sensor API cannot detect headset sensor. When using Android Sensor API after using headset sensor control by Moverio Basic Function SDK, please reconnect headset sensor.
Please refer to following information for specification of Android Sensor API.
https://developer.android.com/reference/android/hardware/Sensor
The Moverio controller has same type sensor with headset sensor. Normally, Android Sensor API user this sensor. On the other hand, the Moverio controller has the feature that Android Sensor API give priority to use headset sensor. This feature is called "the feature of priority using headset sensor". Please refer to "Sensor control specification for the Moverio controller using Android Sensor API" for software specification of this feature. In addition, the Moverio controller has "Sensor Custom State" in order that the Moverio controller select to priority use headset sensor or not.
The feature of priority using headset sensor is activated under the following conditions at OS version 2.1.00.
"Active conditions of the feature of priority using headset sensor" is described for behavior of the feature of priority using headset senso. "-" of this table mean that standard behavior of Android Sensor API.
Active conditions of the feature of priority using headset sensor
Mode of the Moverio controller | Other condition | Sensor custom state is ON | Sensor custom state is OFF | ||
---|---|---|---|---|---|
Connect H sensor* |
Disconnect H sensor |
Connect H sensor |
Disconnect H sensor |
||
Mirror mode | - | - | - | - | |
Trackpad mode | - | - | - | ||
Assist mode |
*H sensor : Headset sensor
Sensor Custom State is OFF on default. When BT-45C, BT-40 or BT-35E is connected at first time, this setting become ON automatically. If you want to change this setting, please operate the toggle button of "Setting -> Accessibility-> Sensor Custom State". This setting will not become ON automatically if when BT-45C, BT-40 or BT-35E is connected from the second time.
Headset has various sensors. Supported sensor types (Sensor data are available) are described in "Supported sensor types on headset".
Supported sensor types on headset
Sensor type | Sensor ID (Hex) | Description | Android standard sensor type | Event frequency | Supported sensor type | |
---|---|---|---|---|---|---|
BT-45C BT-40 |
BT-35E | |||||
TYPE_ACCELEROMETER | 0x00000001 | Measure the acceleration of headset including gravity along the three axes (X, Y, and Z). | 100Hz | |||
TYPE_MAGNETIC_FIELD | 0x00000002 | Measure the surrounding magnetic field of headset along the three axes (X, Y, and Z). | 100Hz | |||
TYPE_GYROSCOPE | 0x00000004 | Measure the angular velocity of headset along the three axes (X, Y, and Z). | 100Hz | |||
TYPE_LIGHT | 0x00000005 | Measure the surrounding ambient light of headset. | 5Hz | |||
TYPE_GRAVITY | 0x00000009 | Measure the acceleration of gravity of headset along the three axes (X, Y, and Z). | 100Hz | |||
TYPE_LINEAR_ACCELERATION | 0x0000000a | Measure the acceleration of headset along the three axes (X, Y, and Z) with gravity removed. | 100Hz | |||
TYPE_ROTATION_VECTOR | 0x0000000b | Measure headset orientation in rotation vectors from 9-axis sensor (Accelerometer, Gyroscope, Magnetic). | 100Hz | |||
TYPE_MAGNETIC_FIELD_UNCALIBRATED | 0x0000000e | Measure the surrounding uncalibrated magnetic field (Raw data) of headset along the three axes (X, Y, and Z). | 100Hz | |||
TYPE_GAME_ROTATION_VECTOR | 0x0000000f | Measure headset orientation in rotation vectors from 6-axis sensor (Accelerometer, Gyroscope). | 100Hz | |||
TYPE_GYROSCOPE_UNCALIBRATED | 0x00000010 | Measure the uncalibrated angular velocity (Raw data) of the headset along the three axes (X, Y, and Z). | 100Hz | |||
TYPE_STATIONARY_DETECT | 0x0000001d | Detect stationary state of headset. When headset is stationary 1 minute, "1" of data is outputted. | At occurring event | |||
TYPE_MOTION_DETECT | 0x0000001e | Detect motion state of headset. When headset is moved after detected stationary state of headset, "1" of data is outputted. | At occurring event | |||
TYPE_ACCELEROMETER_UNCALIBRATED | 0x00000023 | Measure the uncalibrated acceleration (Raw data) of headset including gravity along the three axes (X, Y, and Z) | 100Hz | |||
TYPE_HEADSET_TAP | 0x00102001 | Detect the stationary state of headset. When headset is tapped twice, "1" of data is outputted. | - | At occurring event |
When the feature of priority using headset sensor is activated, application can get headset sensor data by specified any Sensor ID to Android Sensor API.
Sensor types except for TYPE_HEADSET_TAP are Android standard sensor type. Therefore, constant values of Sensor class are available as Sensor ID.
However, TYPE_HEADSET_TAP is not Android standard sensor type. Therefore, please specify Sensor ID of "Supported sensor types on headset" (0x00102001) to Android Sensor API directly.
Headset sensor axis is same with Android standard sensor coordinate system. When user wear headset, X axis show right direction and Y axis show upper direction and Z axis show wearer direction.
Headset sensor axis
By the way, please note about below If the feature of priority using headset sensor is activated and you use headset sensor.
When the feature of priority using headset sensor is activated, sensor types of "Supported sensor types on headset" are not available from controller even if you specify Sensor ID of Android standard sensor type.
If you want to use controller sensor data at this time, please specify Sensor ID of custom sensor type for the Moverio controller to Android Sensor API.
Sensor ID of custom sensor type are described in "Custom sensor type for the Moverio controller". By the way, these sensor types aren't not Android standard sensor type. Therefore, please specify Sensor ID of table to Android Sensor API directly.
Custom sensor type for the Moverio controller
Custom sensor type | Sensor ID (Hex) | Corresponded Android standard sensor type |
---|---|---|
TYPE_CONTROLLER_ACCELEROMETER | 0x00100001 | TYPE_ACCELEROMETER |
TYPE_CONTROLLER_MAGNETIC_FIELD | 0x00100002 | TYPE_MAGNETIC_FIELD |
TYPE_CONTROLLER_GYROSCOPE | 0x00100004 | TYPE_GYROSCOPE |
TYPE_CONTROLLER_LIGHT | 0x00100005 | TYPE_LIGHT |
TYPE_CONTROLLER_GRAVITY | 0x00100009 | TYPE_GRAVITY |
TYPE_CONTROLLER_LINEAR_ACCELERATION | 0x0010000a | TYPE_LINEAR_ACCELERATION |
TYPE_CONTROLLER_ROTATION_VECTOR | 0x0010000b | TYPE_ROTATION_VECTOR |
TYPE_CONTROLLER_MAGNETIC_FIELD_UNCALIBRATED | 0x0010000e | TYPE_MAGNETIC_FIELD_UNCALIBRATED |
TYPE_CONTROLLER_GAME_ROTATION_VECTOR | 0x0010000f | TYPE_GAME_ROTATION_VECTOR |
TYPE_CONTROLLER_GYROSCOPE_UNCALIBRATED | 0x00100010 | TYPE_GYROSCOPE_UNCALIBRATED |
TYPE_CONTROLLER_STATIONARY_DETECT | 0x0010001d | TYPE_STATIONARY_DETECT |
TYPE_CONTROLLER_MOTION_DETECT | 0x0010001e | TYPE_MOTION_DETECT |
TYPE_CONTROLLER_ACCELEROMETER_UNCALIBRATED | 0x00100023 | TYPE_ACCELEROMETER_UNCALIBRATED |
Supported sensor types on the feature of priority using headset sensor are categorized as normal sensor and trigger sensor. Following sensor types are trigger sensor and other sensor types are normal sensor.
Describe about how to use normal sensor and trigger sensor from application using Android Sensor API.
Import following modules for using normal sensor.
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
Implements SensorEventListener to Activity or Service for getting sensor data.
public class SampleActivity extends Activity implements SensorEventListener {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
}
}
Get SENSOR_SERVICE from system and register any sensor to SensorEventListener.
By the way, there is no real limit to the number of sensors that can be used at the same time, but we recommend to only register necessary sensors to prevent a decline in performance.
e.g., Register TYPE_ACCELEROMETER and TYPE_CONTROLLER_ACCELEROMETER (Sensor ID : 0x00100001)
SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE);
// Register accelerometer
Sensor acc = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sm.registerListene(this, acc, SensorManager.SENSOR_DELAY_NORMAL);
// Register controller accelerometer
Sensor cacc = sm.getDefaultSensor(0x00100001);
sm.registerListener(this, cacc, SensorManager.SENSOR_DELAY_NORMAL);
Get sensor data on onSensorChanged().
e.g., Get sensor data of TYPE_ACCELEROMETER and TYPE_CONTROLLER_ACCELEROMETER (Sensor ID : 0x00100001)
@Override
public void onSensorChanged(SensorEvent event) {
switch(event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
// Get accelerometer event (Show log)
Log.d("Sample", "x="+event.values[0]+",y="+event.values[1]+",z="+event.values[2]);
break;
case 0x00102001:
// Get controller accelerometer event (Show log)
Log.d("Sample", "x="+event.values[0]+",y="+event.values[1]+",z="+event.values[2]);
break;
}
}
When application do to stop sensor, unregister it.
e.g., Unregister TYPE_ACCELEROMETER and TYPE_CONTROLLER_ACCELEROMETER
// Unregister accelerometer
sm.unregisterListener(this, acc);
// Unregister controller accelerometer
sm.unregisterListener(this, cacc);
Import following modules for using trigger sensor.
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
Create any class in Activity or Service and extends TriggerEventLisnter to this class.
public class SampleActivity extends Activity {
public class TriggerListener extends TriggerEventListener {
@Override
public void onTrigger(TriggerEvent e) {
}
}
}
Get SENSOR_SERVICE from system and request any sensor to SensorEventListener.
e.g., Request TYPE_STATIONARY_DETECT
SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE);
TriggerListener triggerEventListener = new TriggerListener();
// Register stationary detect
sta = mSensorManager.getDefaultSensor(Sensor.TYPE_STATIONARY_DETECT);
sm.requestTriggerSensor(triggerEventListener, sta);
Sensor data (sensor event) is raised on onTrigger(). If sensor event is raised, this sensor type's request is released. If you want to use sensor ID, please request sensor again.
e.g., Get sensor data of TYPE_STATIONARY_DETECT
public class TriggerListener extends TriggerEventListener {
@Override
public void onTrigger(TriggerEvent e) {
switch(e.sensor.getType()) {
case Sensor.TYPE_STATIONARY_DETECT:
mTextView_staData.setText(String.format("%f", e.values[0]));
Log.d("Sample", "Get stationary event : " + e.values[0]);
break;
}
}
}
This sample code is that application use TYPE_ACCELEROMETER, TYPE_CONTROLLER_ACCELEROMETER, TYPE_STATIONARY_DETECT, TYPE_HEADSET_TAP.
Package ***;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
public class SampleActivity extends Activity implements SensorEventListener {
private SensorEventListener mSensorEventListener = null;
private TriggerEventListener mTriggerEventListener = null;
private SensorManager mSensorManager = null;
// Defined custom sensor type
private static final int TYPE_CONTROLLER_ACCELEROMETER = 0x00100001;
private static final int TYPE_HEADSET_TAP = 0x00102001;
private Sensor mAccSensor = null;
private CheckBox mCheckBox_acc = null;
private TextView mTextView_accData = null;
private Sensor mCAccSensor = null;
private CheckBox mCheckBox_cacc = null;
private TextView mTextView_caccData = null;
private Sensor mTapSensor = null;
private CheckBox mCheckBox_tap = null;
private TextView mTextView_tapData = null;
private TextView mTextView_tapEventTime = null;
private Sensor mStaSensor = null;
private CheckBox mCheckBox_sta = null;
private TextView mTextView_staData = null;
private TextView mTextView_staEventTime = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mSensorEventListener = this;
mTriggerEventListener = new TriggerListener();
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
// TYPE_ACCELEROMETER
mTextView_accData = (TextView) this.findViewById(R.id.textView_accData);
mCheckBox_acc = (CheckBox) this.findViewById(R.id.checkBox_acc);
mCheckBox_acc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mAccSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(mSensorEventListener, mAccSensor, SensorManager.SENSOR_DELAY_NORMAL);
} else {
mSensorManager.unregisterListener(mSensorEventListener, mAccSensor);
}
}
});
// TYPE_CONTROLLER_ACCELEROMETER
mTextView_caccData = (TextView) this.findViewById(R.id.textView_caccData);
mCheckBox_cacc = (CheckBox) this.findViewById(R.id.checkBox_cacc);
mCheckBox_cacc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mCAccSensor = mSensorManager.getDefaultSensor(TYPE_CONTROLLER_ACCELEROMETER);
mSensorManager.registerListener(mSensorEventListener, mCAccSensor, SensorManager.SENSOR_DELAY_NORMAL);
} else {
mSensorManager.unregisterListener(mSensorEventListener, mCAccSensor);
}
}
});
// TYPE_STATIONARY_DETECT
mTextView_staData = (TextView) this.findViewById(R.id.textView_staData);
mCheckBox_sta = (CheckBox) this.findViewById(R.id.checkBox_sta);
mTextView_staEventTime = (TextView) this.findViewById(R.id.textView_staTime);
mCheckBox_sta.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mStaSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STATIONARY_DETECT);
mSensorManager.requestTriggerSensor(mTriggerEventListener, mStaSensor);
}
}
});
// TYPE_HEADSET_TAP
mTextView_tapData = (TextView) this.findViewById(R.id.textView_tapData);
mCheckBox_tap = (CheckBox) this.findViewById(R.id.checkBox_tap);
mTextView_tapEventTime = (TextView) this.findViewById(R.id.textView_tapTime);
mCheckBox_tap.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mTapSensor = mSensorManager.getDefaultSensor(TYPE_HEADSET_TAP);
mSensorManager.registerListener(mSensorEventListener, mTapSensor, SensorManager.SENSOR_DELAY_NORMAL);
} else {
mSensorManager.unregisterListener(mSensorEventListener, mTapSensor);
}
}
});
}
@Override
protected void onPause() {
super.onPause();
if (mSensorManager != null) {
mSensorManager.unregisterListener(this);
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
String data = "";
for(int i = 0; i < event.values.length; i++) {
data += String.format("%.3f", event.values[i]) + ", ";
}
switch(event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
// Show sensor data
mTextView_accData.setText(data);
break;
case TYPE_CONTROLLER_ACCELEROMETER:
// Show sensor data and
mTextView_caccData.setText(data);
break;
case TYPE_HEADSET_TAP:
// Show sensor data and time of occurring event
mTextView_tapData.setText(data);
mTextView_tapEventTime.setText(getNowTime());
break;
}
}
public class TriggerListener extends TriggerEventListener {
@Override
public void onTrigger(TriggerEvent e) {
switch(e.sensor.getType()) {
case Sensor.TYPE_STATIONARY_DETECT:
// Show sensor data and time of occurring event
mTextView_staData.setText(String.format("%.3f", e.values[0]));
mTextView_staEventTime.setText(getNowTime());
if(mCheckBox_sta.isChecked()) mCheckBox_sta.setChecked(false);
break;
}
}
}
private String getNowTime() {
LocalDateTime nowDate = LocalDateTime.now();
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy/MM/dd/HH:mm:ss.SSS");
String formatNowDate = dtf1.format(nowDate);
return formatNowDate;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:isScrollContainer="false"
android:id="@+id/scrollView">
<LinearLayout
android:id="@+id/LinearLayout"
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent">
<!-- TYPE_ACCELEROMETER -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/checkBox_acc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="TYPE_ACCELEROMETER" />
<Space
android:layout_width="5dip"
android:layout_height="match_parent" />
<TextView
android:id="@+id/textView_accData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="data" />
</LinearLayout>
<!-- TYPE_CONTROLLER_ACCELEROMETER -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/checkBox_cacc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="TYPE_CONTROLLER_ACCELEROMETER" />
<Space
android:layout_width="5dip"
android:layout_height="match_parent" />
<TextView
android:id="@+id/textView_caccData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="data" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="10dip" />
<!-- TYPE_STATIONARY_DETECT -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/checkBox_sta"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="TYPE_STATIONARY_DETECT" />
<Space
android:layout_width="5dip"
android:layout_height="match_parent" />
<TextView
android:id="@+id/textView_staData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="data" />
<Space
android:layout_width="5dip"
android:layout_height="match_parent" />
<TextView
android:id="@+id/textView_staTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="event time" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="10dip" />
<!-- TYPE_HEADSET_TAP -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/checkBox_tap"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="TYPE_HEADSET_TAP" />
<Space
android:layout_width="5dip"
android:layout_height="match_parent" />
<TextView
android:id="@+id/textView_tapData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="data" />
<Space
android:layout_width="5dip"
android:layout_height="match_parent" />
<TextView
android:id="@+id/textView_tapTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="event time" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="10dip" />
</LinearLayout>
</ScrollView>
</LinearLayout>