Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • danc/MicroCART
  • snawerdt/MicroCART_17-18
  • bbartels/MicroCART_17-18
  • jonahu/MicroCART
4 results
Show changes
Showing
with 0 additions and 3089 deletions
package eu.ensam.ii.vrpn;
/**
* This class must be used as an {@link android.app.Application Application} for applications that use the Vrpn widgets.
* <p>
* An application that wants to use the Vrpn widgets must define an
* <em>application</em> tag with the name of this class in its manifest :
* </p>
* <ul>
* <li>edit your application <em>AndroidManifext.xml</em> and open the <em>Application</em> tab</li>
* <li>in the <em>Application toggle</em> section , check
* <em>Define an &lt;application&gt; tag ...</em></li>
* <li>in the <em>Application Attributes</em> section, type <em>eu.ensam.ii.vrpn.VrpnApplication</em> as the value of the
* <em>Name</em> attribute or use the <em>Browse...<em>button</li>
* </ul>
*
*/
public class VrpnApplication extends android.app.Application {
/*
* The VrpnApplication object holds and initializes an instance of VrpnClient.
*/
private VrpnClient _vrpnClient;
@Override
public void onCreate() {
_vrpnClient = VrpnClient.getInstance();
_vrpnClient.init(getApplicationContext());
}
}
package eu.ensam.ii.vrpn;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.util.Log;
/**
* Send updates button, analog and tracker updates to the JsonNet Vrpn server.
*
* <p>
* A application that uses the Vrpn widgets must initialize the unique instance
* of this class by calling {@link #setUri(Context, Uri) setUri} before the
* widgets are used.
* </p>
* <p>
* This class is not thread-safe. The methods <code>sendButton</code> and
* <code>sendAnalog</code> must be called from the main UI thread
* </p>
* <p>
* Currently the messages neither include a timestamp nor a sequence number
* </p>
*
* TODO Add timestamps and sequence numbers Add a method to allow sending TODO
* tracker updates from the widgets
*/
public class VrpnClient implements SensorEventListener {
private static VrpnClient _instance;
private static String LOG_TAG = "VrpnClient";
private SensorManager _sensorManager;
private boolean _isTiltTrackerEnabled = false;
private boolean _isRawGyroscopeEnabled = false;
private boolean _isRawAccelerometerEnabled = false;
private int _tiltTrackerSequenceNumber = 0;
private static final Vec3f Zpos = new Vec3f(0.0f, 0.0f, 1.0f);
float _values[] = new float[3];
/*
* Create the objects once to avoid repeated allocations in frequently
* called functions. As long as the VrpnClient methods that update the
* object are called from the same thread, no problem. This is currently the
* case, since the sensor updates are call on the main and the other updates
* are called from UI widgets callbacks, also on the main UI thread..
*/
private DatagramSocket _socket;
private DatagramPacket _packet;
JSONArray _jsonArray = new JSONArray();
private JSONObject _sensorJsonObject = new JSONObject();
private JSONObject _buttonJsonObject = new JSONObject();
private JSONObject _analogJsonObject = new JSONObject();
private Quat _quat = new Quat();
private Vec3f _v = new Vec3f();
/*
* These Ids must match those declared in vrpn_Tracker_Android.cpp
*/
private static final String MSG_KEY_TYPE = "type";
private static final String MSG_KEY_SEQUENCE_NUMBER = "sn";
private static final String MSG_KEY_TIMESTAMP = "ts";
private static final String MSG_KEY_TRACKER_ID = "id";
private static final String MSG_KEY_TRACKER_QUAT = "quat";
@SuppressWarnings("unused")
private static final String MSG_KEY_TRACKER_POS = "pos";
// Values for MSG_KEY_TRACKER_ID
private static final int MSG_SENSOR_ID_TILT = 0;
@SuppressWarnings("unused")
private static final int MSG_SENSOR_ID_RAW_ACCELEROMETER = 1;
@SuppressWarnings("unused")
private static final int MSG_SENSOR_ID_RAW_GYROSCOPE = 2;
private static final String MSG_KEY_BUTTON_ID = "button";
private static final String MSG_KEY_BUTTON_STATUS = "state";
private static final String MSG_KEY_ANALOG_CHANNEL = "num";
private static final String MSG_KEY_ANALOG_DATA = "data";
private final int MSG_TYPE_TRACKER = 1;
private final int MSG_TYPE_BUTTON = 2;
private final int MSG_TYPE_ANALOG = 3;
/**************************************************************************
*
* Create / Destroy
*
**************************************************************************/
/**
* Returns the unique VrpnClient instance
*/
public static VrpnClient getInstance() {
if (_instance == null) {
_instance = new VrpnClient();
}
return _instance;
}
/**
* Initializes the address and port of the VRPN server.
*
* <p>
* This method must be called before sending any update to the server.
* </p>
* Note that the widgets trigger updates when they they are displayed for
* the first time. So this method must be called before calling
* {@link android.app.Activity#setContentView(int) setContentView} in the
* main {@link android.app.Activity activity}
* {@link android.app.Activity#onCreate setContentView}
* <code>onCreate</code> method
*
* @param host
* a string that is a valid host name or IP address
* @param port
* the port number of the VRPN server
*/
public void setupVrpnServer(InetAddress hostAddress, int port) {
Log.d(LOG_TAG, "server:host = " + hostAddress + ":" + port);
/*
* Initialize the packet destination
*/
_packet.setAddress(hostAddress);
_packet.setPort(port);
}
/**
* Load the target server address and port from the shared preferences
*
* @param context
*/
void init(Context context) {
_sensorManager = (SensorManager) context
.getSystemService(Context.SENSOR_SERVICE);
enableOrDisableSensors();
}
/**
* Send an update to a Vrpn Button.
*
* This update will trigger a button callback on the application side
*
* @param buttonId
* the button number. Invalid button numbers are ignored on this
* side, but may trigger an error message on the Vrpn server
* @param buttonState
* the new status of the button
*/
public void sendButton(int buttonId, boolean buttonState) {
try {
_buttonJsonObject.put(MSG_KEY_TYPE, MSG_TYPE_BUTTON);
_buttonJsonObject.put(MSG_KEY_BUTTON_ID, buttonId);
_buttonJsonObject.put(MSG_KEY_BUTTON_STATUS, buttonState);
} catch (JSONException e1) {
e1.printStackTrace();
}
_sendObject(_buttonJsonObject);
}
/**
* Send an update to a Vrpn Analog channel.
*
* This update will trigger an analog callback on the application side
*
* @param channelId
* The channel number. Invalid channel numbers are ignored on
* this side but, they may trigger an error message on the Vrpn
* server
* @param channelValue
* the data that will be sent on the analog channel
*/
public void sendAnalog(int channelId, double channelValue) {
try {
_analogJsonObject.put(MSG_KEY_TYPE, MSG_TYPE_ANALOG);
_analogJsonObject.put(MSG_KEY_ANALOG_CHANNEL, channelId);
_analogJsonObject.put(MSG_KEY_ANALOG_DATA, channelValue);
} catch (JSONException e1) {
e1.printStackTrace();
}
_sendObject(_analogJsonObject);
}
/**
* Enables or disable the tilt tracker.
*
* The tilt tracker send a tracker update with a quaternion that rotates the
* "earth vertical up" vector to the +Z vector in device coordinates. See
* {@link android.hardware.SensorEvent SensorEvent} for a description of the
* device coordinate system. This quaternion can be used to retrieve the
* roll and pitch of the device.
*
* @param enable
* <code>true</code> enables the tilt tracker updates and
* <code>false</code> disable the tilt tracker updates
*/
public void enableTiltTracker(boolean enable) {
_isTiltTrackerEnabled = enable;
enableOrDisableSensors();
}
/**************************************************************************
*
* Sensorlistener implementation
*
**************************************************************************/
/**
* Called by the system when the sensor accuracy changes
*
* This method is empty and should not be called by widgets
*
*/
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
/**
* Called by the system when a sensor data changes.
*
* This method retrieves the Android sensor object. If an vrpn Tracker is
* active that involves this sensor object this method sends a Vrpn tracker
* update to the appropriate tracker. packages the sensor data into a JSON
* object then send the JSON object onto an UDP socket. The target socket
* endpoint configuration is configured by calling setupVrpnServer
*
*/
@Override
public void onSensorChanged(SensorEvent event) {
final int sensorType = event.sensor.getType();
// retrieve and update the adequate sequence number.
switch (sensorType) {
case Sensor.TYPE_ACCELEROMETER:
if (_isTiltTrackerEnabled) {
buildTiltTrackerObject(event.values, event.timestamp);
_sendObject(_sensorJsonObject);
}
if (_isRawAccelerometerEnabled) {
// To do
}
break;
case Sensor.TYPE_GYROSCOPE:
if (_isRawGyroscopeEnabled) {
// To do
}
break;
default:
return;
}
// Add other trackers here
// Send the data onto the network
}
/****************************************************************************
* Private methods
****************************************************************************/
private VrpnClient() {
try {
_socket = new DatagramSocket();
} catch (SocketException e) {
// Check this device IP network configuration
e.printStackTrace();
}
/*
* There is a single _packet instance to avoid numerous object creation
* in onSensorChanged()
*/
byte dummy[] = { 0 };
_packet = new DatagramPacket(dummy, dummy.length);
// Set the address with anything since _sendObject will be called before
// setUri has been called // why
try {
_packet.setAddress(InetAddress.getLocalHost());
} catch (UnknownHostException e) {
}
}
/**
* Send the string representation of a Json Object.
*
* @param[in] o the json object to send
*/
private void _sendObject(final JSONObject o) {
Log.d(LOG_TAG, o.toString());
// Send the data onto the network
_packet.setData(o.toString().getBytes());
try {
_socket.send(_packet);
} catch (SocketException e) {
// Local network not ready, no route to host, ...
;
} catch (IOException e) {
// TODO Auto-generated catch block : send a notification ?
e.printStackTrace();
}
}
/**
* Build a tilt tracker JSON object
*
* This method updates the _sensorJsonObject object
*
* @param x
* @param y
* @param z
* @param timestamp
*/
private void buildTiltTrackerObject(final float[] values, long timestamp) {
_v.set(values[0], values[1], values[2]);
_v.normalize();
/*
* When the device is at rest, the acceleration vector is -g (earth
* vertical up) in device coordinate system (Z out of the screen, that
* is Z up when the device lays on a table). Compute the rotation +Z -->
* -g
*/
_quat.setRotate(Zpos, _v);
// Build a Json message
try {
_sensorJsonObject.put(MSG_KEY_TYPE, MSG_TYPE_TRACKER);
_sensorJsonObject.put(MSG_KEY_SEQUENCE_NUMBER,
_tiltTrackerSequenceNumber++);
_sensorJsonObject.put(MSG_KEY_TIMESTAMP, timestamp);
_sensorJsonObject.put(MSG_KEY_TRACKER_ID, MSG_SENSOR_ID_TILT);
_quat.fill(_jsonArray);
_sensorJsonObject.put(MSG_KEY_TRACKER_QUAT, _jsonArray);
} catch (JSONException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
}
/**
* Update the device sensor activation status.
*/
private void enableOrDisableSensors() {
final int rate = SensorManager.SENSOR_DELAY_GAME;
final Sensor accelerometer = _sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
_sensorManager.unregisterListener(this);
if (_isTiltTrackerEnabled) {
_sensorManager.registerListener(this, accelerometer, rate);
} else {
/*
* We are closing down the tilt tracker, Send an update with
* horizontal position
*/
buildTiltTrackerObject(new float[] { 0.0f, 0.0f, 1.0f },
System.nanoTime());
_sendObject(_sensorJsonObject);
}
}
}
package eu.ensam.ii.vrpn;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* A {@link android.widget.Button Button} connected to a VRPN Button.
*
* <p>
* This button is linked to a Vrpn button. It sends sends a <code>true</code>
* update when its status changes to checked and a <code>false</code> update
* when its status changes to unchecked. The status of the VrpnPressButton
* changes when it is pushed, and also when it is released, unlike a
* {@link VrpnToggleButton}.
* </p>
*
* <p>
* In your <code>res/layout.xml</code>, include the widget in
* its parent layout :
* </p>
* <pre>
* &lt;eu.ensam.ii.VrpnPressButton
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"
* app:vrpnButton="@id/MyButton"
* &#47&gt;
* </pre>
*
* <p>
* The root element of the layout must declare the following line just after the
* <code>xmlns:android ... </code>line :
*
* <pre>
* xmlns:app="http://schemas.android.com/apk/res/your.package.name.here"
* </pre>
*
* </p>
*
* <p>
* <strong>Custom XML attributes :</strong>
* </p>
* <ul>
* <li><code>app:vrpnButton</code> : the id of the Vrpn button that will receive
* updated from this widget. This value can be a literal or a resource Id that
* can be kept in a <a href="package-summary.html#vrpnids">separate Vrpn Id
* list</a></li>
* </ul>
*
*/
public class VrpnPressButton extends android.widget.Button implements
android.view.View.OnTouchListener {
private String TAG = "VrpnPressButton";
private int _vrpnButtonId = 0;
public VrpnPressButton(Context context) {
super(context);
init(null);
}
public VrpnPressButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public VrpnPressButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
/**
* Widget initialization
*
* @param attrs
* the AttributeSet that is required to access to the XML custom
* attributes
*/
private void init(AttributeSet attrs) {
// this.onTouch() will be called when this widget is touched
setOnTouchListener(this);
/*
* Get The Vrpn button id from the xml app:vrpnButton attribute The
* custom attribute is declared is in res/values/attrs.xml
*/
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.VrpnRadioButton);
_vrpnButtonId = a.getInt(R.styleable.VrpnRadioButton_vrpnButton, 0);
a.recycle();
// Send a "false" since a PressButton is always initially released
Log.d(TAG, "ctor init : button " + _vrpnButtonId + " released ");
VrpnClient.getInstance().sendButton(_vrpnButtonId, false);
}
/**
* Called when a touch event is dispatched to this VrpnPressButton.
*
* This method sends a <code>true</code> update when the button is pressed
* and a <code>false</code> update when the button is released. when it is
* released. No event is send while the button is pressed.
*
* @see android.view.View.OnTouchListener#onTouch(android.view.View,
* android.view.MotionEvent)
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
final int action = event.getAction();
/*
* Avoid unnecessary processing for irrelevant events Nothing is sent
* while the button is hold down (event=ACTION_MOVE)
*/
if (action != MotionEvent.ACTION_DOWN
&& action != MotionEvent.ACTION_UP
&& action != MotionEvent.ACTION_CANCEL) {
// Mark the event has handled, so it is not be handled by
// ToggleButton::onClick
return true;
}
/*
* Send true when the button is pressed and false when it is released.
*/
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, " button " + _vrpnButtonId + " pressed ");
VrpnClient.getInstance().sendButton(_vrpnButtonId, true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, " button " + _vrpnButtonId + " released ");
VrpnClient.getInstance().sendButton(_vrpnButtonId, false);
break;
default:
break;
}
// Mark the event has handled, so it is not be handled by
// ToggleButton::onClick
return false;
}
}
package eu.ensam.ii.vrpn;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.CompoundButton;
import android.widget.RadioGroup;
import android.widget.CompoundButton.OnCheckedChangeListener;
/**
* A {@link android.widget.RadioButton RadioButton} that updates a Vrpn Button channel.
*
* <p>
* The <code>VrpnRadioButton</code> is to be included inside a {@link RadioGroup}. The
* <code>VrpnRadioButton</code> will send a <code>true</code> when its state changes to
* checked and a <code> false</code> update when its state changes to unchecked.
* Checking another button of the <code>RadioGroup</code> will uncheck the <code>RadioButton</code>.</p>
* <p>in a <code>RadioGroup</code>, it is possible to include :
* <ul>
* <li>a <code>VrpnRadiobutton</code> and a <code>RadioButton</code>. Checking the <code>RadioButton</code> will uncheck the <code>VrpnRadioButton</code> that will send an update</li>
* <li>several <code>VrpnRadioButton</code> widgets. In this case the checked and the unchecked widgets will send updates</li>
* </ul>
* </p>
* <p>
* In your layout, declare the following :
* </p>
*
* <pre>
* &lt;RadioGroup
* android:id="@+id/RadioGroup01"
* &gt;
* &lt;eu.ensam.ii.vrpn.VrpnRadioButton
* android:text="This way"
* android:checked="true"
* app:vrpnButton="@id/VrpnButtonThisWay"
* /&gt;
* &lt;RadioButton
* android:text="other Way"
* android:checked="false"
* /&gt;
* &lt;/RadioGroup&gt;
* </pre>
*
* <p>
* The root element of the layout must declare the following line just after the
* <code>xmlns:android ... </code>line :
*
* <pre>
* xmlns:app="http://schemas.android.com/apk/res/your.package.name.here"
* </pre>
*
* </p>
*
* <p>
* <strong>Custom XML attributes :</strong>
* </p>
* <ul>
* <li><code>app:vrpnButton</code> : the id of the Vrpn button that will receive
* updated from this widget. This value can be a literal or a resource Id that
* can be kept in a <a href="package-summary.html#vrpnids">separate Vrpn Id
* list</a></li>
* </ul>
*/
public class VrpnRadioButton extends android.widget.RadioButton implements
OnCheckedChangeListener {
private static final String TAG = "VrpnRadioButton";
int _vrpnButtonId = 0;
public VrpnRadioButton(Context context) {
super(context);
init(null);
}
public VrpnRadioButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public VrpnRadioButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
setOnCheckedChangeListener(this);
// get The Vrpn button id from the xml app:vrpnButton attribute
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.VrpnRadioButton);
_vrpnButtonId = a.getInt(R.styleable.VrpnRadioButton_vrpnButton, 0);
a.recycle();
// Trigger an initial Vrpn update
Log.d(TAG, "ctor init " + _vrpnButtonId + " : " + isChecked());
VrpnClient.getInstance().sendButton(_vrpnButtonId, isChecked());
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.d(TAG, "onCheckChanged " + _vrpnButtonId + " : " + isChecked);
// Send the update through the Vrpn service
VrpnClient.getInstance().sendButton(_vrpnButtonId, isChecked());
}
}
package eu.ensam.ii.vrpn;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
/**
* A {@link android.widget.SeekBar SeekBar} that updates a Vrpn Analog channel.
*
* <p>
* The seekbar include a line with the min, max and current
* values of the seekbar, and the seekbar itself.
*
* The seekbar progress is converted into a value in the
* <code>app:minValue</code> .. <code>app:maxValue</code>
* range. This value is then sent out to the Vrpn Analog channel whose
* id is specified in the <code>app:vrpnAnalog</code> attribute. The <code>VrpnSeekBar</code>
* moves in increments that are one hundredth of the value range.
* </p>
* <p>
* In your layout, declare the following (note the app:attributes)
* </p>
*
* <pre>
* &lt;eu.ensam.ii.vrpn.VrpnSeekBar
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"
* app:minValue="-3"
* app:minValue="3"
* app:minValue="0"
* app:vrpnAnalog="@id/MyVrpnAnalogId"
* /&gt;
* </pre>
*
* <p>
* The root element of the layout must declare the following line just after the
* <code>xmlns:android ... </code>line :
*
* <pre>
* xmlns:app="http://schemas.android.com/apk/res/your.package.name.here"
* </pre>
*
* </p>
*
* <p>
* <strong>Custom XML attributes :</strong>
* </p>
* <ul>
* <li><code>app:vrpnAnalog</code> : the id of the Vrpn analog that will receive
* updates from this widget. This value can be a literal or a resource Id that
* can be kept in a <a href="package-summary.html#vrpnids">separate Vrpn Id
* list</a></li>
* <li><code>app:minValue</code> : the numeric value mapped onto the left
* position of the VrpnSeekbar</li>
* <li><code>app:maxValue</code> : the numeric value mapped onto the left
* position of the VrpnSeekbar</li>
* <li><code>app:defaultValue</code> : the initial numeric value of the VrpnSeekbar;</li>
* <li><code>app:hideValues</code> : <code>true</code> will cause the line with
* the minimum, maximum and current values to be hidden, <code>false</code> will cause
* the line to be displayed (default)</li>
* </ul>
*
* @author Philippe
*
*/
public class VrpnSeekBar extends android.widget.LinearLayout implements
OnSeekBarChangeListener {
// Partly adapted from
// http://www.codeproject.com/KB/android/seekbar_preference.aspx
private static final String TAG = "VrpnSeekBar";
private static final float DEFAULT_MIN_VALUE = 0.0f;
private static final float DEFAULT_MAX_VALUE = 100.0f;
private float _minValue;
private float _maxValue;
private float _currentValue;
private TextView _currentValueText;
private CharSequence _title;
private int _vrpnAnalogId;
private SeekBar _seekBar;
private boolean _hideValues;
public VrpnSeekBar(Context context) {
this(context, null);
}
public VrpnSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
// Get the custom attribute values
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.VrpnSeekBar);
_minValue = a
.getFloat(R.styleable.VrpnSeekBar_minValue, DEFAULT_MIN_VALUE);
_maxValue = a
.getFloat(R.styleable.VrpnSeekBar_maxValue, DEFAULT_MAX_VALUE);
_currentValue = a.getFloat(R.styleable.VrpnSeekBar_defaultValue,
(_maxValue + _minValue) / 2.0f);
_title = a.getText(R.styleable.VrpnSeekBar_title);
_vrpnAnalogId = a.getInt(R.styleable.VrpnSeekBar_vrpnAnalog, 0);
boolean hideTitle = a.getBoolean(R.styleable.VrpnSeekBar_hideTitle, false);
_hideValues = a.getBoolean(R.styleable.VrpnSeekBar_hideValues, false);
a.recycle();
// Load the view layout
LayoutInflater li = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
li.inflate(R.layout.vrpn_seekbar, this, true);
// Load or hide the title
TextView titleView = (TextView) findViewById(R.id.txtSeekBarTitle);
if (hideTitle) {
titleView.setVisibility(GONE);
} else {
titleView.setText(_title);
}
// Load or hide the layout with the values
if (_hideValues) {
((LinearLayout) findViewById(R.id.lytSeekBarValues))
.setVisibility(GONE);
} else {
((TextView) findViewById(R.id.txtSeekBarMinValue)).setText(Float
.toString(_minValue));
((TextView) findViewById(R.id.txtSeekBarMaxValue)).setText(Float
.toString(_maxValue));
_currentValueText = (TextView) findViewById(R.id.txtSeekBarCurrentValue);
}
// Setup the SeekBar
_seekBar = (SeekBar) findViewById(R.id.seekBarWidget);
_seekBar.setOnSeekBarChangeListener(this);
// This triggers OnProgressChanged() and a VrpnUpdate
int currentValue = (int) (100.0 * (_currentValue - _minValue) / (_maxValue - _minValue));
_seekBar.setProgress(currentValue);
}
/**
* Called when the progress of the SeekBar changes
*
* Compute the value from the progress and send a VrpnUpdate to the analog
* channel that is associated to the widget with the custom attribute
* app:vrpnAnalog
*/
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// progress is in range 0..100 : map to min..max
_currentValue = (_maxValue - _minValue) / 100 * progress + _minValue;
if (!_hideValues) {
_currentValueText.setText(String.format("%.2f", _currentValue));
}
Log.d(TAG, "onProgressChanged " + _vrpnAnalogId + " progress/value: "
+ progress + " " + _currentValue);
// Send the vrpn update
// Note : The line below throws an exception in the Eclipse GUI preview
VrpnClient.getInstance().sendAnalog(_vrpnAnalogId, _currentValue);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
package eu.ensam.ii.vrpn;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
/**
* A {@link android.widget.Spinner Spinner} that updates a Vrpn Analog channel.
*
* <p>
* Picking a value from the spinner will update a Vrpn Analog channel with the
* position of the item that was picked in the item list. On the application side, the value will be delivered as a <em>double</em>.
* </p>
* <p>
* In your layout, declare the following :
* </p>
*
* <pre>
* &lt;eu.ensam.ii.vrpn.VrpnSpinner
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"
* android:prompt="@string/the_prompt"
* android:entries="@array/the_array"
* app:vrpnAnalog="@id/VrpnAnalogMySpinner"
* /&gt;
* </pre>
*
* In a resource XML file located in <em>res/values</em>, define the prompt that
* will be displayed in the choice dialog, as well as the list of values to choose from.</p><
*
* <pre>
* &lt;string name="the_prompt"&gt;Message displayed when the choice dialog is opened&lt;/string&gt;
* &lt;string-array name="the_array"&gt;
* &lt;item&gt;"Mars"&lt;/item&gt;
* &lt;item&gt;"Venus"&lt;/item&gt;
* &lt;/string-array&gt;
* </pre>
* <p>
* The root element of the layout must declare the following line just after the
* <code>xmlns:android ... </code>line :</p>
* <pre>
* xmlns:app="http://schemas.android.com/apk/res/your.package.name.here"
* </pre>
* <p>
* <strong>Custom XML attributes :</strong>
* </p>
* <ul>
* <li><code>app:vrpnAnalog</code> : the id of the Vrpn analog that will receive
* updated from this widget. This value can be a literal or a resource Id that
* can be kept in a <a href="package-summary.html#vrpnids">separate Vrpn Id
* list</a></li>
* </ul>
*/
public class VrpnSpinner extends android.widget.Spinner implements OnItemSelectedListener {
private static final String TAG = "VrpnSpinner";
private int _vrpnAnalogId;
public VrpnSpinner(Context context) {
super(context);
init(null);
}
public VrpnSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
private void init(AttributeSet attrs) {
// Get the custom attribute values
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.VrpnSpinner);
_vrpnAnalogId = a.getInt(R.styleable.VrpnSpinner_vrpnAnalog, 0);
a.recycle();
setOnItemSelectedListener(this);
setSelection(0);
// Trigger a Vrpn update to get a known initial state
Log.d(TAG, "ctor " + _vrpnAnalogId + " position " + 0);
VrpnClient.getInstance().sendAnalog(_vrpnAnalogId, 0);
}
/**
*
* @see android.widget.AdapterView.OnItemSelectedListener#onItemSelected(android.widget.AdapterView, android.view.View, int, long)
*/
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
Log.d(TAG, "onItemSelected " + _vrpnAnalogId + " position" + position);
// Send the vrpn update
// Note : The line below throws an exception in the Eclipse GUI preview
VrpnClient.getInstance().sendAnalog(_vrpnAnalogId, position);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
}
package eu.ensam.ii.vrpn;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
/**
* This {@link android.view.SurfaceView SurfaceView} updates two Vrpn Analog channels with the coordinates of the
* Surface point that is touched.
*
* <p>
* Updates are sent as long astouch events occur within the surface. The
* X channel range from
* <code>app:leftX</code> to <code>app:rightX</code>,
* and the Y channel values range from <code>app:topY</code> to
* <code>app:bottomY</code>.
* </p>
*
* <p>
* In your layout, declare the following (note the <code>app:</code> attributes)
* </p>
*
* <pre>
* &lt;eu.ensam.ii.vrpn.VrpnSurface
* android:layout_width="fill_parent"
* android:layout_height="fill_parent"
* app:leftX="-1"
* app:rightX="1"
* app:topY="0"
* app:bottomY="1"
* app:vrpnAnalogX="@id/MyVrpnAnalogX"
* app:vrpnAnalogY="@id/MyVrpnAnalogY"
* /&gt;
* </pre>
*
* <p>
*
* <p>
* The root element of the layout must declare the following line just after the
* <code>xmlns:android ... </code>line :
* <pre>
* xmlns:app="http://schemas.android.com/apk/res/your.package.name.here"
* </pre>
* </p>
* <p>
* <strong>Custom XML attributes :</strong>
* </p>
* <ul>
* <li><code>app:vrpnAnalogX</code> : the id of the Vrpn analog that will
* receive the X position of the touch event from this widget. The update value
* sent to the Vrpn analog ranges from the <code>app:leftX</code> value to the
* <code>app:rightX</code> value.</li>
* <li><code>app:vrpnAnalogY</code> : the id of the Vrpn analog that will
* receive the Y position of the touch event from this widget. The update value
* sent to the Vrpn analog ranges from the <code>app:bottomY</code> value to the
* <code>app:topX</code> value.</li>
* <li><code>app:leftX</code> : the numeric value mapped onto the left border of
* the surface.</li>
* <li><code>app:rightX</code> : the numeric value mapped onto the right border
* of the surface. May be greater or lower than <code>app:leftX</code>.</li>
* <li><code>app:bottomY</code> : the numeric value mapped onto the top border
* of the surface</li>
* <li><code>app:topY</code> : the numeric value mapped onto the top border of
* the surface. May be greater or lower than <code>app:bottomY</code></li>
* </ul>
*
* The <code>app:vrpnAnalogX</code> and <code>app:vrpnAnalogY</code> ids can be
* literals or a resource Ids. These resource ids may be kept in a <a
* href="package-summary.html#vrpnids">separate Vrpn Id list</a>
*
*/
public class VrpnSurface extends android.view.SurfaceView implements
OnTouchListener {
protected String TAG = "VrpnSurface";
int _vrpnAnalogXId = 0;
int _vrpnAnalogYId = 0;
float _leftX, _rightX, _topY, _bottomY;
public VrpnSurface(Context context) {
super(context);
init(null);
}
public VrpnSurface(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public VrpnSurface(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
/**
* Perform initialization that is common to the constructors
*
* @param attrs
*/
private void init(AttributeSet attrs) {
setOnTouchListener(this);
// get The Vrpn button id from the xml app:vrpnButton attribute
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.VrpnSurface);
_vrpnAnalogXId = a.getInt(R.styleable.VrpnSurface_vrpnAnalogX, 0);
_vrpnAnalogYId = a.getInt(R.styleable.VrpnSurface_vrpnAnalogY, 0);
_leftX = a.getFloat(R.styleable.VrpnSurface_leftX, 0.0f);
_rightX = a.getFloat(R.styleable.VrpnSurface_rightX, 1.0f);
_topY = a.getFloat(R.styleable.VrpnSurface_topY, 0.0f);
_bottomY = a.getFloat(R.styleable.VrpnSurface_bottomY, 1.0f);
a.recycle();
// Start at origin
// TODO start at the middle
Log.d(TAG, "ctor init : " + _vrpnAnalogXId + " " + _vrpnAnalogYId);
VrpnClient.getInstance().sendAnalog(_vrpnAnalogXId, 0);
VrpnClient.getInstance().sendAnalog(_vrpnAnalogYId, 0);
}
/**
* Called when a touch event is dispatched to this widget.
*
* <p>
* If the touch event occurs within the surface of the view, two analog Vrpn
* updates are send for the X and y position. Updates are sent when
* ACTION_DOWN, ACTION_MOVE, and ACTION_UP are received. the event history
* is ignored.
* </p>
*
* @see android.view.View.OnTouchListener#onTouch(View, MotionEvent)
* @see android.view.MotionEvent
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_OUTSIDE) {
return false;
}
final float x = event.getX();
final float y = event.getY();
final float w = getWidth();
final float h = getHeight();
if (x < 0 || x > w || y < 0 || y > h) {
// dragging the touch point may trigger values outside the view
// dimensions
return false;
}
/*
* event reports absolute View X/Y coordinates, convert to the
* appropriate bounds then send the updates
*/
final float vrpnX = (x / w) * (_rightX - _leftX) + _leftX;
final float vrpnY = (y / h) * (_bottomY - _topY) + _topY;
Log.d(TAG, "touch action x/y = " + action + " " + vrpnX + "/" + vrpnY);
VrpnClient.getInstance().sendAnalog(_vrpnAnalogXId, vrpnX);
VrpnClient.getInstance().sendAnalog(_vrpnAnalogYId, vrpnY);
return true;
}
}
package eu.ensam.ii.vrpn;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
/**
* A {@link android.view.ToggleButton ToggleButton} connected to a Vrpn Button.
*
* <p>
* This button sends a true update when its status changes to checked and false
* when its status changes to unchecked. Unlike a
* {@link VrpnPressButton}, the status of this widget changes when it is
* pressed, but not when it is released,
* </p>
*
* <p>
* In your layout XML file, include the following into a container widget :
* <pre>
* &lt;eu.ensam.ii.vrpn.VrpnButton
* app:vrpnButton="@id/MyButton"
* /&gt;
* </pre>
* <p>
* The root element of the layout must declare the following line just after the
* <code>xmlns:android ... </code>line :
* <pre>
* xmlns:app="http://schemas.android.com/apk/res/your.package.name.here"
* </pre>
*
* </p>
*
* <p>
* <strong>Custom XML attributes :</strong>
* </p>
* <ul>
* <li><code>app:vrpnButton</code> : the id of the Vrpn button that will receive
* updated from this widget. This value can be a literal or a resource Id that
* can be kept in a <a href="package-summary.html#vrpnids">separate Vrpn Id
* list</a></li>
* </ul>
*/
public class VrpnToggleButton extends android.widget.ToggleButton implements
OnCheckedChangeListener {
private static final String TAG = "VrpnToggleButton";
private int _vrpnButtonId;
public VrpnToggleButton(Context context) {
super(context);
init(null);
}
public VrpnToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public VrpnToggleButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
setOnCheckedChangeListener(this);
// get The Vrpn button id from the xml app:vrpnButton attribute
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.VrpnRadioButton);
_vrpnButtonId = a.getInt(R.styleable.VrpnToggleButton_vrpnButton, 0);
a.recycle();
// Trigger an initial Vrpn update
Log.d(TAG, "ctor init " + _vrpnButtonId + " : " + isChecked());
VrpnClient.getInstance().sendButton(_vrpnButtonId, isChecked());
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.d(TAG, "onCheckedChanged " + _vrpnButtonId + " : " + isChecked);
VrpnClient.getInstance().sendButton(_vrpnButtonId,
buttonView.isChecked());
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<!--
Edit the documentation here, then copy the contents of the <body> tag into package-info.java (without the tags) -->
<body>
This package include a set of widgets that are bound to a Vrpn server
buttons, analogs and trackers.
<h3>Create a simple application that uses the widgets</h3>
<p>
The following chapters describe how to create a simple application
with a {@link eu.ensam.ii.vrpn.VrpnToggleButton VrpnToggleButton} and
a {@link eu.ensam.ii.vrpn.VrpnSeekBar VrpnSeekBar}. An example
application project that contains these widgets and more can be found
in the <em>example_app</em> directory.
</p>
<h4>Create and configure the project</h4>
<p>
Refer to any Android documentation for instructions about how to
set-up the Android SDK and the Eclipse plugin. Do not forget to setup
the Android SDK in any new workspace (<em>Window</em> &gt; <em>Preferences</em>,
then go to the <em>Android</em> tab and configure the SDK location).
</p>
<p>
Import the <em>Vrpn_library</em> project into your Eclipse workspace.
Open the Android section of the project property page and verify that
<em>Is library</em> checked. If the build complains about "Android
requires .class compatibility set to 5.0", you need first to select
the project, right-click > <em>Properties</em> &gt; <em>Android
tools</em> &gt; <em>Fix project properties</em>, then open the project
properties, go to the <em>Java compiler</em> section, then deselect
<em>enable project specific settings</em>. You may need to clean and
rebuild (<em>Project</em> &gt; <em>Clean</em>) several times before
the project shows no errors.
</p>
<p>
Create a new Android project : <em>File</em> > <em>New</em> > <em>Other
...</em> , <em>Android</em> &gt; <em>Android Project</em>. On the first
page of the wizard, check <em>Create activity</em> and enter any name
you like for the main activity (for instance <em>MainActivity</em>).
</p>
<p>
Add the library to your project : open the properties of the project,
go to the <em>Android</em> page. In the <em>Library</em> pane, click <em>Add</em>
then select <em>vrpn_library</em>, apply the changes and close the
window. At this point, the Eclipse <em>Package Explorer</em> view of
your project should contain a <em>vrpn_library_src</em> folder and the
<em>gen</em> folder should contain a package entry named <em>eu.ensam.ii.vrpn</em>.
</p>
<p>
Open the
<code>AndroidManifest.xml</code>
file. On the <em>Application</em> tab, check <em>Define an
&lt;Application&gt; tag in the AndroidManifest.xml</em>. Then click on the
<em>Browse</em> button that is on the right of <em>Name</em> and
select <em>VrpnApplication</em>.
</p>
<h4>Modify the main activity</h4>
<p>Edit the main activity to define the binding to the Vrpn server
when the activity is created. The method {@link
android.app.Activity#onCreate onCreate} is called when the Activity is
created. The default version of this method contain only the first and
last lines :</p>
<pre>
super.onCreate(savedInstanceState);
....
setContentView(R.layout.main);
</pre>
<p>We need to define the binding with the Vrpn server. The method
becomes :</p>
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InetAddress vrpnServerAddress = null;
try {
// Replace the IP address
vrpnServerAddress = InetAddress.getByName("192.168.173.1");
} catch (UnknownHostException e) {
}
final int vrpnServerPort = 7777;
VrpnClient.getInstance().setupVrpnServer(vrpnServerAddress, vrpnServerPort);
setContentView(R.layout.main);
}
</pre>
<a name="vrpnids"></a>
<h4>Create a Vrpn Ids list</h4>
<p>
Before laying out your Vrpn widgets in your application, you should
create an XML configuration file that contains the IDs of the Vrpn
buttons and analog that you will be using. This allows you to keep a
unique list of Vrpn IDs, making it easier to avoid duplicate values
and reference this list from your application. Create this file with <em>File</em>
> <em>New</em> > <em>Other</em>, then <em>Android </em> > <em>Android
XML File</em>. In XML file dialog, type any file name you like, for
instance <em>vrpn.xml</em>, then select <em>Values</em> as the type of
resource to create.
</p>
<p>
Edit the resulting file, found at <em>res/values/vrpn.xml</em> to add
the numbers of the Vrpn Buttons and Analogs that you will be using.
</p>
<pre>
&lt;resources&gt;
&lt;item type="id" format="integer" name="VrpnAnalogLightIntensity"&gt;0&lt;/item&gt;
&lt;item type="id" format="integer" name="VrpnButtonLightsOn"&gt;0&lt;/item&gt;
&lt;/resources&gt;
</pre>
<p>
Note that <em>type</em> must be <em>id</em> and <em>format</em> must
be <em>integer</em>, but <em>name</em> can be anything you like.
</p>
<h4>Add your widgets to the main layout</h4>
<p>Say we want an Android application to control the lighting in
our VR application : turn the light on or off and also control the
light intensity. We will create an application with a {@link
eu.ensam.ii.vrpn.VrpnToggleButton VrpnToggleButton} as the light
switch and a {@link eu.ensam.ii.vrpn.VrpnSeekBar VrpnSeekBar} as a
light intensity controller.</p>
<p>
By default, the layout of the main Activity is described in <em>res/layout/main.xml</em>
and just displays <em>Hello</em>. Edit this file. The XML code looks
is something like :
</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
&gt;
&lt;TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/&gt;
&lt;/LinearLayout&gt;
</pre>
<p>
The root element,
<code>LinearLayout</code>
, is a container for the other widgets, currently contains a
<code>TextView</code>
.
<p>
The basics about XML layouts are presented in <a
href="http://developer.android.com/guide/topics/ui/declaring-layout.html">Declaring
layout</a> SDK page.
</p>
<p>
Before the end tag
<code>&lt;/LinearLayout&gt;</code>
, include a {@link eu.ensam.ii.vrpn.VrpnToggleButton VrpnToggleButton}
to turn the lights on or off :
<pre>
&lt;eu.ensam.ii.vrpn.VrpnToggleButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:vrpnButton="@id/VrpnButtonLightsOn"
/&gt;
</pre>
The Vrpn widgets use so-called
<em>custom XML properties</em> , such as
<em>app:vrpnButton</em> . This custom property allows you to specify
the id of the target Vrpn button in the XML layout file rather than in
Java code. However, the use of this custom property requires the
addition of an XML namespace tag so that the build system can find
then. After the
<em>xmlns:android</em> line, add the following line :
<pre>
xmlns:app="http://schemas.android.com/apk/res/your.application.package.name.here"
</pre>
Different attributes exist for each widgets and the whole list is shown
when you unfold
<em>gen/eu/ensam.ii.vrpn/R.java/R/attr</em> in the Eclipse Package
View. Your layout is now ready for your to include the Vrpn widgets.
Now save the file to build your project. If there are build errors,
check the following :
<ul>
<li>you have added the <code>xmlns::app</code> line in the root
layout</li>
<li>the spelling of the <code>app:</code> attribute</li>
<li>the value of the Vrpn attributes</li>
<li>the spelling of the widget name</li>
</ul>
<p>
In most cases, the <em>Preview</em> tab of the XML layout is not
displayed properly as soon as you include one of the Vrpn widgets.
This is a known problem for which not solution has currently been
found. It prevents you from previewing the layout it wil be displayed
correctly in the application, either on a device or in the emulator.
</p>
<p>Now add a {@link eu.ensam.ii.vrpn.VrpnSeekBar VrpnSeekBar} to
control the light intensity :</p>
<pre>
&lt;eu.ensam.ii.vrpn.VrpnSeekBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:minValue="0"
app:maxValue="255"
app:defaultValue="128"
app:vrpnAnalog="@id/VrpnAnalogLightIntensity"
/&gt;
</pre>
Your Android application is now ready to run. Upload on a device and
run. There is not much to seen for the time being. However, if you run
a network sniffer on a machine connected to the same network as the
Android device, you should see UDP packets flowing out of the device.
If you are using the emulator instead of an actual device, see the
<a
href="
http://developer.android.com/guide/developing/devices/emulator.html#emulatornetworking">Emulator
networking</a> SDK page for the correct network setup.
<h4>Test your application</h4>
In order to test your application, you need a VRPN server built with
VRPN_USE_JSONNET. See the
<code>README.jsoncpp</code>
file for instructions to build this server. When you get this server
running, the easiest way to test the Android application is to use the
<code>vrpn_print_devices</code>
application provided with Vrpn. Type the command :
<pre>
vrpn_print_devices Jsonnet@localhost
</pre>
The command should then report the values of the Button and SeekBar as
they are changed.
<h3>Beyond the basics</h3>
<h4>Application with multiple tabs</h4>
If you want to create an application with more widgets that can be
placed on a single screen, using a {@link android.app.TabActivity
TabActivity} instead of a regular Activity may be the way to go. A
{@link android.app.TabActivity TabActivity} can display several
activities of view, each inside a tab. The Android SDK documentation
explains how to create a tabbed application with activities. However,
using activities require careful state management since the activities
may be terminated when the use switches from one tab to another. It is
therefore easier to manage a {@link android.app.TabActivity
TabActivity} that display
<em>views</em> intead of activities.
<p></p>
<p>First of all, your main activity must extend {@link
android.app.TabActivity TabActivity} intead of {@link
android.app.Activity Activity}.</p>
<p>
Then, create a layout for each tab in <em>res/layout</em> with <em>File</em>
> <em>New</em> > <em>Other...</em> , then <em>Android</em> > <em>New
XML file</em>. In the <em>New Android XML file</em> dialog, name the file
<em>tab_one.xml</em>, select <em>Layout</em> as the type of resource,
then select <em>LinearLayout</em> as the root element. Repeat the
process with a second tab layout named <em>tab_two.xml</em>. Update
each tab layout as described in <em>Add your widgets to the main
layout</em> above, paying attention not to forget the <em>xmlns:app</em>
line.
</p>
<p>
The next step is to create a new layout names <em>res/layout/tab_group.xml</em>for
the whole activity. This layout looks like :
</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent"&gt;
&lt;TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/&gt;
&lt;FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"&gt;
&lt;/FrameLayout&gt;
&lt;include layout="@layout/tab_one"&gt;&lt;/include&gt;
&lt;include layout="@layout/tab_two"&gt;&lt;/include&gt;
&lt;/TabHost&gt;
</pre>
<p>
Then edit the <em>onCreate</em> method of your activity. After the
line <em>super.onCreate()</em> add the <em>VrpnClient.getInstance().setUri(....)</em>
line, then add :
<pre>
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.tab_group, getTabHost().getTabContentView(), true);
TabHost.TabSpec spec;
spec = getTabHost().newTabSpec("1").setContent(R.id.tab_one);
getTabHost().addTab(spec)
spec = getTabHost().newTabSpec("2").setContent(R.id.tab_two);
getTabHost().addTab(spec);
getTabHost().setCurrentTab(0);
</pre>
<p>See in the SDK documentation how to use the {@link
android.widget.TabHost.TabSpec#setIndicator setIndicator} method in
order to display an icon in the tab headers.</p>
<h3>Updating the widget library</h3>
<h4>Create a new Widget</h4>
<p>To create a new Widget type you should look at the source code
of VrpnSeekBar, which is the more elaborate. The main steps are as
follows :</p>
<ul>
<li>Decide which Android widget class you want to extend and
which events will trigger Vrpn updates.</li>
<li>Decide whether your widget will send Vrpn button,analog or
tracker updates.</li>
<li>Decide whether your widget needs new custom XML attributes.
If this is the case, look at <code>res/values/attrs.xml</code> for
examples.</li>
<li>Implement a private <em>init</em> method that each
constructor will call. In this method, you must obtain the XML custom
attributes of the widget. You should also send a Vrpn update with the
initial value of the widget</li>
<li>Implement the appropriate change listener, according to which
event you want your widget to respond to. In the change listener,
call the appropriate method : {@link
eu.ensam.ii.vrpn.VrpnClient#sendAnalog(int,double) sendAnalog} or
{@link eu.ensam.ii.vrpn.VrpnClient#sendButton(int,boolean)
sendButton}. Note that a single widget can s end several updates,
like the {@link eu.ensam.ii.vrpn.VrpnSurface VrpnSurface} does or
like a <a
href="http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/ColorPickerDialog.html">color
picker</a>would do. There is currently no <em>sendTracker</em> method
although this may be useful to implement an Arcball for instance.</li>
</ul>
<p>Note that using the full path of the parent class such as in :
<pre>public class VrpnRadioButton extends android.widget.RadioButton</pre>
allows Javadoc to generate a link to the parent class.
</p>
<h4>Generate the documentation</h4>
<p>
Select the <em>vrpn_library/src</em> folder, then select the menu item
<em>Project</em> &gt; <em>Generate Javadoc</em>.
<p>
On the first page of the wizard, select the path of the <em>javadoc</em>
tool. Check that <em>vrpn_library/src</em> is selected, but not <em>vrpn_library/gen</em>,
then select an appropriate destination for the documentation.
</p>
<p>
On the second page of the wizard select <em>android.jar</em> in the <em>Referenced
archives</em> list and take note of the <em>android.jar</em> doc path
(like
<code>file:/E:/android/docs/reference</code>
).
</p>
<p>
On the third page of the wizard add the following line to the <em>Extra
javadoc options</em> text box :
<pre>
-linkoffline http://d.android.com/reference file:/E:/android/docs/reference
</pre>
but replace the
<code>file:/</code>
URL by your local Android SDK URL noted from the page two of the
wizard. This allows Javadoc to include full references to the on-line
Android documentation.
</body>
</html>
\ No newline at end of file
//
//
// Please edit package-info.html, then paste here
//
/**
*
This package include a set of widgets that are bound to a Vrpn server
buttons, analogs and trackers.
<h3>Create a simple application that uses the widgets</h3>
<p>
The following chapters describe how to create a simple application
with a {@link eu.ensam.ii.vrpn.VrpnToggleButton VrpnToggleButton} and
a {@link eu.ensam.ii.vrpn.VrpnSeekBar VrpnSeekBar}. An example
application project that contains these widgets and more can be found
in the <em>example_app</em> directory.
</p>
<h4>Create and configure the project</h4>
<p>
Refer to any Android documentation for instructions about how to
set-up the Android SDK and the Eclipse plugin. Do not forget to setup
the Android SDK in any new workspace (<em>Window</em> &gt; <em>Preferences</em>,
then go to the <em>Android</em> tab and configure the SDK location).
</p>
<p>
Import the <em>Vrpn_library</em> project into your Eclipse workspace.
Open the Android section of the project property page and verify that
<em>Is library</em> checked. If the build complains about "Android
requires .class compatibility set to 5.0", you need first to select
the project, right-click > <em>Properties</em> &gt; <em>Android
tools</em> &gt; <em>Fix project properties</em>, then open the project
properties, go to the <em>Java compiler</em> section, then deselect
<em>enable project specific settings</em>. You may need to clean and
rebuild (<em>Project</em> &gt; <em>Clean</em>) several times before
the project shows no errors.
</p>
<p>
Create a new Android project : <em>File</em> > <em>New</em> > <em>Other
...</em> , <em>Android</em> &gt; <em>Android Project</em>. On the first
page of the wizard, check <em>Create activity</em> and enter any name
you like for the main activity (for instance <em>MainActivity</em>).
</p>
<p>
Add the library to your project : open the properties of the project,
go to the <em>Android</em> page. In the <em>Library</em> pane, click <em>Add</em>
then select <em>vrpn_library</em>, apply the changes and close the
window. At this point, the Eclipse <em>Package Explorer</em> view of
your project should contain a <em>vrpn_library_src</em> folder and the
<em>gen</em> folder should contain a package entry named <em>eu.ensam.ii.vrpn</em>.
</p>
<p>
Open the
<code>AndroidManifest.xml</code>
file. On the <em>Application</em> tab, check <em>Define an
&lt;Application&gt; tag in the AndroidManifest.xml</em>. Then click on the
<em>Browse</em> button that is on the right of <em>Name</em> and
select <em>VrpnApplication</em>.
</p>
<h4>Modify the main activity</h4>
<p>Edit the main activity to define the binding to the Vrpn server
when the activity is created. The method {@link
android.app.Activity#onCreate onCreate} is called when the Activity is
created. The default version of this method contain only the first and
last lines :</p>
<pre>
super.onCreate(savedInstanceState);
....
setContentView(R.layout.main);
</pre>
<p>We need to define the binding with the Vrpn server. The method
becomes :</p>
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InetAddress vrpnServerAddress = null;
try {
// Replace the IP address
vrpnServerAddress = InetAddress.getByName("192.168.173.1");
} catch (UnknownHostException e) {
}
final int vrpnServerPort = 7777;
VrpnClient.getInstance().setupVrpnServer(vrpnServerAddress, vrpnServerPort);
setContentView(R.layout.main);
}
</pre>
<a name="vrpnids"></a>
<h4>Create a Vrpn Ids list</h4>
<p>
Before laying out your Vrpn widgets in your application, you should
create an XML configuration file that contains the IDs of the Vrpn
buttons and analog that you will be using. This allows you to keep a
unique list of Vrpn IDs, making it easier to avoid duplicate values
and reference this list from your application. Create this file with <em>File</em>
> <em>New</em> > <em>Other</em>, then <em>Android </em> > <em>Android
XML File</em>. In XML file dialog, type any file name you like, for
instance <em>vrpn.xml</em>, then select <em>Values</em> as the type of
resource to create.
</p>
<p>
Edit the resulting file, found at <em>res/values/vrpn.xml</em> to add
the numbers of the Vrpn Buttons and Analogs that you will be using.
</p>
<pre>
&lt;resources&gt;
&lt;item type="id" format="integer" name="VrpnAnalogLightIntensity"&gt;0&lt;/item&gt;
&lt;item type="id" format="integer" name="VrpnButtonLightsOn"&gt;0&lt;/item&gt;
&lt;/resources&gt;
</pre>
<p>
Note that <em>type</em> must be <em>id</em> and <em>format</em> must
be <em>integer</em>, but <em>name</em> can be anything you like.
</p>
<h4>Add your widgets to the main layout</h4>
<p>Say we want an Android application to control the lighting in
our VR application : turn the light on or off and also control the
light intensity. We will create an application with a {@link
eu.ensam.ii.vrpn.VrpnToggleButton VrpnToggleButton} as the light
switch and a {@link eu.ensam.ii.vrpn.VrpnSeekBar VrpnSeekBar} as a
light intensity controller.</p>
<p>
By default, the layout of the main Activity is described in <em>res/layout/main.xml</em>
and just displays <em>Hello</em>. Edit this file. The XML code looks
is something like :
</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
&gt;
&lt;TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/&gt;
&lt;/LinearLayout&gt;
</pre>
<p>
The root element,
<code>LinearLayout</code>
, is a container for the other widgets, currently contains a
<code>TextView</code>
.
<p>
The basics about XML layouts are presented in <a
href="http://developer.android.com/guide/topics/ui/declaring-layout.html">Declaring
layout</a> SDK page.
</p>
<p>
Before the end tag
<code>&lt;/LinearLayout&gt;</code>
, include a {@link eu.ensam.ii.vrpn.VrpnToggleButton VrpnToggleButton}
to turn the lights on or off :
<pre>
&lt;eu.ensam.ii.vrpn.VrpnToggleButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:vrpnButton="@id/VrpnButtonLightsOn"
/&gt;
</pre>
The Vrpn widgets use so-called
<em>custom XML properties</em> , such as
<em>app:vrpnButton</em> . This custom property allows you to specify
the id of the target Vrpn button in the XML layout file rather than in
Java code. However, the use of this custom property requires the
addition of an XML namespace tag so that the build system can find
then. After the
<em>xmlns:android</em> line, add the following line :
<pre>
xmlns:app="http://schemas.android.com/apk/res/your.application.package.name.here"
</pre>
Different attributes exist for each widgets and the whole list is shown
when you unfold
<em>gen/eu/ensam.ii.vrpn/R.java/R/attr</em> in the Eclipse Package
View. Your layout is now ready for your to include the Vrpn widgets.
Now save the file to build your project. If there are build errors,
check the following :
<ul>
<li>you have added the <code>xmlns::app</code> line in the root
layout</li>
<li>the spelling of the <code>app:</code> attribute</li>
<li>the value of the Vrpn attributes</li>
<li>the spelling of the widget name</li>
</ul>
<p>
In most cases, the <em>Preview</em> tab of the XML layout is not
displayed properly as soon as you include one of the Vrpn widgets.
This is a known problem for which not solution has currently been
found. It prevents you from previewing the layout it wil be displayed
correctly in the application, either on a device or in the emulator.
</p>
<p>Now add a {@link eu.ensam.ii.vrpn.VrpnSeekBar VrpnSeekBar} to
control the light intensity :</p>
<pre>
&lt;eu.ensam.ii.vrpn.VrpnSeekBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:minValue="0"
app:maxValue="255"
app:defaultValue="128"
app:vrpnAnalog="@id/VrpnAnalogLightIntensity"
/&gt;
</pre>
Your Android application is now ready to run. Upload on a device and
run. There is not much to seen for the time being. However, if you run
a network sniffer on a machine connected to the same network as the
Android device, you should see UDP packets flowing out of the device.
If you are using the emulator instead of an actual device, see the
<a
href="
http://developer.android.com/guide/developing/devices/emulator.html#emulatornetworking">Emulator
networking</a> SDK page for the correct network setup.
<h4>Test your application</h4>
In order to test your application, you need a VRPN server built with
VRPN_USE_JSONNET. See the
<code>README.jsoncpp</code>
file for instructions to build this server. When you get this server
running, the easiest way to test the Android application is to use the
<code>vrpn_print_devices</code>
application provided with Vrpn. Type the command :
<pre>
vrpn_print_devices Jsonnet@localhost
</pre>
The command should then report the values of the Button and SeekBar as
they are changed.
<h3>Beyond the basics</h3>
<h4>Application with multiple tabs</h4>
If you want to create an application with more widgets that can be
placed on a single screen, using a {@link android.app.TabActivity
TabActivity} instead of a regular Activity may be the way to go. A
{@link android.app.TabActivity TabActivity} can display several
activities of view, each inside a tab. The Android SDK documentation
explains how to create a tabbed application with activities. However,
using activities require careful state management since the activities
may be terminated when the use switches from one tab to another. It is
therefore easier to manage a {@link android.app.TabActivity
TabActivity} that display
<em>views</em> intead of activities.
<p></p>
<p>First of all, your main activity must extend {@link
android.app.TabActivity TabActivity} intead of {@link
android.app.Activity Activity}.</p>
<p>
Then, create a layout for each tab in <em>res/layout</em> with <em>File</em>
> <em>New</em> > <em>Other...</em> , then <em>Android</em> > <em>New
XML file</em>. In the <em>New Android XML file</em> dialog, name the file
<em>tab_one.xml</em>, select <em>Layout</em> as the type of resource,
then select <em>LinearLayout</em> as the root element. Repeat the
process with a second tab layout named <em>tab_two.xml</em>. Update
each tab layout as described in <em>Add your widgets to the main
layout</em> above, paying attention not to forget the <em>xmlns:app</em>
line.
</p>
<p>
The next step is to create a new layout names <em>res/layout/tab_group.xml</em>for
the whole activity. This layout looks like :
</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent"&gt;
&lt;TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/&gt;
&lt;FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"&gt;
&lt;/FrameLayout&gt;
&lt;include layout="@layout/tab_one"&gt;&lt;/include&gt;
&lt;include layout="@layout/tab_two"&gt;&lt;/include&gt;
&lt;/TabHost&gt;
</pre>
<p>
Then edit the <em>onCreate</em> method of your activity. After the
line <em>super.onCreate()</em> add the <em>VrpnClient.getInstance().setUri(....)</em>
line, then add :
<pre>
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.tab_group, getTabHost().getTabContentView(), true);
TabHost.TabSpec spec;
spec = getTabHost().newTabSpec("1").setContent(R.id.tab_one);
getTabHost().addTab(spec)
spec = getTabHost().newTabSpec("2").setContent(R.id.tab_two);
getTabHost().addTab(spec);
getTabHost().setCurrentTab(0);
</pre>
<p>See in the SDK documentation how to use the {@link
android.widget.TabHost.TabSpec#setIndicator setIndicator} method in
order to display an icon in the tab headers.</p>
<h3>Updating the widget library</h3>
<h4>Create a new Widget</h4>
<p>To create a new Widget type you should look at the source code
of VrpnSeekBar, which is the more elaborate. The main steps are as
follows :</p>
<ul>
<li>Decide which Android widget class you want to extend and
which events will trigger Vrpn updates.</li>
<li>Decide whether your widget will send Vrpn button,analog or
tracker updates.</li>
<li>Decide whether your widget needs new custom XML attributes.
If this is the case, look at <code>res/values/attrs.xml</code> for
examples.</li>
<li>Implement a private <em>init</em> method that each
constructor will call. In this method, you must obtain the XML custom
attributes of the widget. You should also send a Vrpn update with the
initial value of the widget</li>
<li>Implement the appropriate change listener, according to which
event you want your widget to respond to. In the change listener,
call the appropriate method : {@link
eu.ensam.ii.vrpn.VrpnClient#sendAnalog(int,double) sendAnalog} or
{@link eu.ensam.ii.vrpn.VrpnClient#sendButton(int,boolean)
sendButton}. Note that a single widget can s end several updates,
like the {@link eu.ensam.ii.vrpn.VrpnSurface VrpnSurface} does or
like a <a
href="http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/ColorPickerDialog.html">color
picker</a>would do. There is currently no <em>sendTracker</em> method
although this may be useful to implement an Arcball for instance.</li>
</ul>
<p>Note that using the full path of the parent class such as in :
<pre>public class VrpnRadioButton extends android.widget.RadioButton</pre>
allows Javadoc to generate a link to the parent class.
</p>
<h4>Generate the documentation</h4>
<p>
Select the <em>vrpn_library/src</em> folder, then select the menu item
<em>Project</em> &gt; <em>Generate Javadoc</em>.
<p>
On the first page of the wizard, select the path of the <em>javadoc</em>
tool. Check that <em>vrpn_library/src</em> is selected, but not <em>vrpn_library/gen</em>,
then select an appropriate destination for the documentation.
</p>
<p>
On the second page of the wizard select <em>android.jar</em> in the <em>Referenced
archives</em> list and take note of the <em>android.jar</em> doc path
(like
<code>file:/E:/android/docs/reference</code>
).
</p>
<p>
On the third page of the wizard add the following line to the <em>Extra
javadoc options</em> text box :
<pre>
-linkoffline http://d.android.com/reference file:/E:/android/docs/reference
</pre>
but replace the
<code>file:/</code>
URL by your local Android SDK URL noted from the page two of the
wizard. This allows Javadoc to include full references to the on-line
Android documentation.
/*
********************************* End of paste Here
*/
package eu.ensam.ii.vrpn;
\ No newline at end of file
set(ATMELLIB_PUBLIC_HEADERS
vrpn_atmellib_errno.h
vrpn_atmellib.h)
set(ATMELLIB_SOURCES
vrpn_atmellib_helper.h
vrpn_atmellib_helper.C
vrpn_atmellib_iobasic.C
vrpn_atmellib_openclose.C
vrpn_atmellib_register.C
vrpn_atmellib_tester.C)
add_library(vrpn_atmel ${ATMELLIB_PUBLIC_HEADERS} ${ATMELLIB_SOURCES})
set_property(TARGET
vrpn_atmel
PROPERTY
PUBLIC_HEADER
${ATMELLIB_PUBLIC_HEADERS})
set_property(TARGET
vrpn_atmel
PROPERTY
FOLDER
"Library")
if(VRPN_INSTALL)
install(TARGETS
vrpn_atmel
ARCHIVE
DESTINATION
lib
COMPONENT
serversdk
PUBLIC_HEADER
DESTINATION
include
COMPONENT
serversdk)
endif()
/* -*- Mode:C -*- */
/***************************************************************************************************/
/* */
/* Copyright (C) 2003 Bauhaus University Weimar */
/* Released into the public domain on 6/23/2007 as part of the VRPN project */
/* by Jan P. Springer. */
/* */
/***************************************************************************************************/
/* */
/* module : vrpn_atmellib.h */
/* project : vrpn_avango */
/* description: part of vrpn_avango_atmel_server */
/* */
/***************************************************************************************************/
/* */
/* */
/***************************************************************************************************/
#if !defined(VRPN_AVANGO_ATMELLIB_SERIAL_H)
#define VRPN_AVANGO_ATMELLIB_SERIAL_H
/* includes, system */
/* #include <> */
/* includes, project */
#include "vrpn_atmellib_errno.h"
#if defined(__cplusplus)
extern "C" {
#endif
/*************************************************************************************************/
/* constants you have to remember */
/* maiximum time to wait for a new byte from the atmel */
#define VRPN_ATMELLIB_SELECT_WAIT_SEC 0
#define VRPN_ATMELLIB_SELECT_WAIT_USEC 150000
/*************************************************************************************************/
/* types, exported (enum, struct, union, typedef) */
/*************************************************************************************************/
/* the 2 bytes which defines a message from/to the microcontroller */
struct command_t {
unsigned char addr;
unsigned char value;
};
/*************************************************************************************************/
typedef int error_t;
typedef int handle_t;
/*************************************************************************************************/
/* for development purpose mainly
displays a value as Bits on the screen */
extern void outBit( unsigned char );
/*************************************************************************************************/
/* BASICS */
/*************************************************************************************************/
/* opens the the specified port (whole path needed) and sets the parameters
for the communication with the microcontroller
CAUTION: the return value is a program specific file handler and not the
system file handler */
extern handle_t openPort(const char*i, const int baud, struct termios * init_param);
/* close the specified port and reset all parameters */
extern error_t closePort (handle_t, struct termios * init_param);
/* checks if the given file handler is valid: returns 1 (true) if the handle is invalid otherwise
return 0 (false) */
extern error_t handle_invalid (int);
/* checks if the register is valid */
extern error_t register_invalid( unsigned char );
/*************************************************************************************************/
/* LOW LEVEL INTERFACE */
/*************************************************************************************************/
/* send one/more command(s) to the microcontroller which don't change the state of it
the return value of the command - i.e. the value of a register - is returned in the
second calling variable */
extern int getCmd (handle_t, struct command_t*);
/* send one/more command(s) to the microcontroller which change the state of the microcontrolle */
extern int setCmd (int, struct command_t*);
/* set the value of the specified register */
extern error_t
setRegister( handle_t Hd , const unsigned char Register , const unsigned char Val);
/* get the value of the specified register */
extern int getRegister( handle_t Hd , const unsigned char Register);
#if defined(__cplusplus)
} /* extern "C" { */
#endif
#endif /* #if !defined(VRPN_AVANGO_ATMELLIB_SERIAL_H) */
/* -*- Mode:C -*- */
/***************************************************************************************************/
/* */
/* Copyright (C) 2003 Bauhaus University Weimar */
/* Released into the public domain on 6/23/2007 as part of the VRPN project */
/* by Jan P. Springer. */
/* */
/***************************************************************************************************/
/* */
/* module : atmel_serial_errno.h */
/* project : atmellib */
/* description: error numbers of the lib */
/* */
/***************************************************************************************************/
#if !defined(ATMELLIB_SERIAL_ERRNO_H)
#define ATMELLIB_SERIAL_ERRNO_H
/* program specific error codes */
#define ATMELLIB_NOERROR 1 /* no error */
#define ATMELLIB_ERROR_GENERAL -1 /* general error */
#define ATMELLIB_ERROR_SEQTO -5 /* sequence to small */
#define ATMELLIB_ERROR_NORESPVAL -6 /* the mc didn't confirm the command */
#define ATMELLIB_ERROR_NOCOMVAL -7 /* no valid command (must be bigger than 127 ) */
#define ATMELLIB_ERROR_REGINV -10 /* the register is invalid */
#define ATMELLIB_ERROR_VALINV -11 /* the value register is invalid */
#define ATMELLIB_ERROR_RESINV -12 /* hardware ressource invalid */
#define ATMELLIB_ERROR_NOTINIT -13 /* the prequists are not ok: i.e. to put a
light on the pin must be in output mode */
#define ATMELLIB_ERROR_PARAMINV -14 /* functioncall with invalid parameter*/
#define ATMELLIB_ERROR_NOSTATEVAL -15 /* no valid state or not able to read it out */
#endif /* #if !defined(ATMELLIB_SERIAL_ERRNO_H) */
/***************************************************************************************************/
/* */
/* Copyright (C) 2004 Bauhaus University Weimar */
/* Released into the public domain on 6/23/2007 as part of the VRPN project */
/* by Jan P. Springer. */
/* */
/***************************************************************************************************/
/* */
/* module : vrpn_atmellib_helper.C */
/* project : atmel-avango */
/* description: part of the serial lib for the Atmel */
/* miscellaneous functions */
/* */
/***************************************************************************************************/
/* include system headers */
#include <errno.h> // for error_t
#include <stdio.h> // for printf
/* include i/f header */
#include "vrpn_atmellib.h" // for command_t, getCmd, setCmd, etc
#include "vrpn_atmellib_errno.h" // for ATMELLIB_NOERROR, etc
#include "vrpn_atmellib_helper.h"
/***************************************************************************************************/
/* set the defined bit in the command to the expected value and leave the rest like it is */
/***************************************************************************************************/
error_t
setBitCmd( struct command_t * Cmd , const unsigned char Pos , unsigned char Val )
{
/* check if the parameter are valid */
if ((Val != 0) && (Val != 1))
return ATMELLIB_ERROR_VALINV;
if (Pos > 7)
return ATMELLIB_ERROR_VALINV;
/* check if the value for "our" pin is already correct else change it */
if (Pos == 7) {
/* bit seven is in byte one */
if (((*Cmd).addr) & 0x01) {
if (Val == 0)
(*Cmd).addr -= 1; /* it's 1 and it has to be 0*/
}
else {
if (Val == 1) /* it's 0 and it has to be 1 */
(*Cmd).addr += 1;
}
}
else { /* the bits in the second byte */
unsigned char Reference = 0x01 << Pos;
if ( ((*Cmd).value) & Reference ) {
if (Val == 0)
(*Cmd).value -= PowToTwo(Pos);
}
else { /* change necessary */
if (Val == 1)
(*Cmd).value += PowToTwo(Pos);
}
}
return ATMELLIB_NOERROR;
}
/***************************************************************************************************/
/* change only one bit of the value of the specified register */
/***************************************************************************************************/
error_t
setBitReg( const handle_t Hd , const unsigned char Reg , \
const unsigned char Pos , const unsigned char Val )
{
struct command_t Cmd;
error_t R;
unsigned char i;
/* check if the parameters are valid */
if ((Val != 0) && (Val != 1))
return ATMELLIB_ERROR_VALINV;
/* set the correct address for the register */
/* check if the first bit is already set or if it has to be done here */
if (Reg & 0x80)
Cmd.addr = Reg;
else {
Cmd.addr = Reg + 64;
Cmd.addr <<= 1;
}
Cmd.value = 0x00;
/* get the current setting of the register on the microcontroller */
/* if you get sth invalid try again */
for (i=0 ; i<10 ; ++i) {
if ( (R = getCmd( Hd , &Cmd )) != ATMELLIB_NOERROR )
return R;
if ( Cmd.value<128)
break;
}
if (i==10)
return ATMELLIB_ERROR_NOSTATEVAL;
if ( (R = setBitCmd( &Cmd , Pos , Val )) != ATMELLIB_NOERROR)
return R;
/* write the corrected value back to the microcontroller */
if ( (R = setCmd( Hd , &Cmd )) != ATMELLIB_NOERROR )
return R;
return ATMELLIB_NOERROR;
}
/***************************************************************************************************/
/* 2^Exponent */
/***************************************************************************************************/
unsigned int
PowToTwo( unsigned int Exponent )
{
unsigned int i;
unsigned int R = 1;
for( i=0 ; i<Exponent ; i++)
R *= 2;
return R;
}
/***************************************************************************************************/
/* valid address byte to the specified register */
/* the MSB data bit is set to zero */
/***************************************************************************************************/
unsigned char
getAddress( unsigned char Reg )
{
Reg += 64;
Reg <<= 1;
return Reg;
}
/***************************************************************************************************/
/* set the value in a two byte command */
/***************************************************************************************************/
void
setValue( struct command_t * Cmd , unsigned char Val)
{
if (Val>127) {
Val -= 128;
if ( ! ((*Cmd).addr & 0x01))
(*Cmd).addr +=1;
}
else
if ((*Cmd).addr & 0x01)
(*Cmd).addr -=1;
(*Cmd).value = Val;
}
/***************************************************************************************************/
/* helper function */
/***************************************************************************************************/
/* extern */ void
outBit( unsigned char Arg )
{
unsigned char Ref;
for ( Ref=0x80 ; Ref != 0x00 ; Ref >>= 1 ) {
if (Ref & Arg)
printf("1");
else
printf("0");
}
printf("\n");
}
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma set woff 1174
#endif
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma reset woff 1174
#endif
/* -*- Mode:C -*- */
/***************************************************************************************************/
/* */
/* Copyright (C) 2003 Bauhaus University Weimar */
/* Released into the public domain on 6/23/2007 as part of the VRPN project */
/* by Jan P. Springer. */
/* */
/***************************************************************************************************/
/* */
/* module : lib */
/* project : atmellib */
/* description: private part of the header for internal use */
/* */
/***************************************************************************************************/
/* $Id: vrpn_atmellib_helper.h,v 1.1 2007/06/25 19:56:16 taylorr Exp $ */
#if !defined(ATMELLIB_SERIAL_HELPER_H)
#define ATMELLIB_SERIAL_HELPER_H
/* includes, system */
/* #include <> */
#include <errno.h> // for error_t
/* includes, project */
#include "vrpn_atmellib.h" // for handle_t
#if defined(__cplusplus)
extern "C" {
#endif
/*****************************************************************/
/* types, variables NOT exported, internal (static) */
/* internal functions */
/* set the specified bit in the given Command without interfering the other bits*/
error_t setBitCmd( struct command_t * Command , \
const unsigned char Position , unsigned char Value );
/* set the specified bit in the Register without interfering the other bits */
error_t setBitReg( const handle_t Handle , const unsigned char Register , \
const unsigned char Position , const unsigned char Value );
/* gives you the 2^Exponent */
unsigned int PowToTwo( unsigned int Exponent);
/* valid address byte for the specified Register
the MSB data bit is set to zero */
unsigned char getAddress( unsigned char Register );
/* set a value in an two byte command*/
void setValue( struct command_t * Command , unsigned char Value);
#if defined(__cplusplus)
} /* extern "C" { */
#endif
#endif /* #if !defined(ATMELLIB_SERIAL_HELPER_H) */
/***************************************************************************************************/
/* */
/* Copyright (C) 2004 Bauhaus University Weimar */
/* Released into the public domain on 6/23/2007 as part of the VRPN project */
/* by Jan P. Springer. */
/* */
/***************************************************************************************************/
/* */
/* module : vrpn_atmel_iobasic.C */
/* project : atmel-avango */
/* description: basic functions for serial io access on the tty */
/* */
/***************************************************************************************************/
//#define VRPN_ATMELLIB_VERBOSE_OUTBIT
//#define VRPN_ATMELLIB_VERBOSE
/* include system headers */
#include <errno.h> // for errno, ENXIO, error_t
#include <stddef.h> // for size_t
#include <stdio.h> // for fprintf, stderr, NULL, etc
#include <string.h> // for strerror
#include <sys/select.h> // for select, FD_SET, FD_ZERO, etc
#include <termios.h> // for tcflush, TCIOFLUSH
#include <unistd.h> // for read, write
/* include i/f header */
#include "vrpn_Shared.h" // for timeval
#include "vrpn_atmellib.h" // for command_t, handle_t, etc
#include "vrpn_atmellib_errno.h" // for ATMELLIB_ERROR_NORESPVAL, etc
/***************************************************************************************************/
/* read data from the tty */
/* returns the bytes read or one of the system wide error codes returned by read */
/***************************************************************************************************/
static int
read_wrapper (handle_t fd, void* buf, size_t len)
{
return read (fd, buf, len);
}
/***************************************************************************************************/
/* write data to the tty: return the byte written or one of the system
wide error codes returned by write */
/***************************************************************************************************/
static int
write_wrapper ( handle_t fd , const void* buf , size_t len )
{
return write (fd , buf , len );
}
/***************************************************************************************************/
/* make a select for lib-wide time and if this is successfully read one byte */
/***************************************************************************************************/
static int
select_read_wrapper(handle_t fd , struct timeval * time)
{
fd_set rdfs;
unsigned char byte;
int ret;
FD_ZERO( &rdfs );
FD_SET(fd , &rdfs);
ret = select( fd+1, &rdfs, NULL, NULL, time);
if (ret<0) {
fprintf(stderr, "select_read_wrapper:: error during select: %s (%i)\n",
strerror(errno) ,errno);
return ATMELLIB_ERROR_NORESPVAL;
}
else if (ret==0) {
// time expired
fprintf(stderr, "vrpn_atmellib::select_read_wrapper: select timed out\n" );
return ATMELLIB_ERROR_NORESPVAL;
}
// successful select -> read out one command
if ((ret = read_wrapper(fd , (void *) (&byte), 1)) <=0 )
return ret;
#ifdef VRPN_ATMELLIB_VERBOSE_OUTBIT
outBit(byte);
#endif
return byte;
}
/***************************************************************************************************/
/* make a select for lib-wide time and if this is successfully read one byte */
/***************************************************************************************************/
static int
select_write_wrapper(handle_t fd , struct timeval * time, unsigned char * data , int len)
{
fd_set wrfs;
int ret;
FD_ZERO( &wrfs );
FD_SET(fd , &wrfs);
ret = select( fd+1, NULL, &wrfs, NULL, time);
if (ret<0) {
fprintf(stderr,
"vrpn_atmellib::select_write_wrapper::error during waiting for writing permission: %s (%i)\n",
strerror(errno) ,errno);
return ATMELLIB_ERROR_NORESPVAL;
}
else if (ret==0) {
// time expired
fprintf(stderr, "vrpn_atmellib::select_write_wrapper: timed out in wrapper\n" );
return ATMELLIB_ERROR_NORESPVAL;
}
// successful select -> write down command
// write twice to ensure that the atmel receives it
//write_wrapper(fd , (void *) data, len);
return write_wrapper(fd , (void *) data, len);
}
/***************************************************************************************************/
/* PART OF THE BASIC INTERFACE: for explainations to the functions see the header file */
/***************************************************************************************************/
/***************************************************************************************************/
/* get the value of one register */
/***************************************************************************************************/
/* extern */ error_t
getCmd (handle_t fd, struct command_t* Cmd)
{
struct timeval time_wait;
int ret;
#ifdef VRPN_ATMEL_SERIAL_VRPN
unsigned char read_val;
#endif
// some adds to measure time for reading out
#ifdef VRPN_ATMELLIB_TIME_MEASURE
struct timeval start;
struct timeval end;
int sec, usec;
vrpn_gettimeofday( &start , 0);
#endif
unsigned char PossibilityOne;
unsigned char PossibilityTwo;
unsigned char Reference;
/* check if the given parameters are valid */
if (handle_invalid(fd))
return ENXIO;
if ((*Cmd).addr < 128)
return ATMELLIB_ERROR_NOCOMVAL;
PossibilityOne = (*Cmd).addr;
PossibilityTwo = (*Cmd).addr;
Reference = 0x01;
if (Reference & PossibilityTwo)
/* the LSB of the Address is 1 -> make 0 -> use XOR operator */
PossibilityTwo ^= Reference;
else
/* the LSB of the address is 0 -> make 1 -> use OR operator */
PossibilityTwo |= Reference;
#ifdef VRPN_ATMEL_SERIAL_VRPN
vrpn_flush_input_buffer(fd);
vrpn_flush_output_buffer(fd);
#else
tcflush( fd , TCIOFLUSH );
#endif
time_wait.tv_sec = VRPN_ATMELLIB_SELECT_WAIT_SEC;
time_wait.tv_usec = VRPN_ATMELLIB_SELECT_WAIT_USEC;
#ifdef VRPN_ATMEL_SERIAL_VRPN
vrpn_write_characters(fd, (&((*Cmd).addr)) , 1);
#else
/* you have to send the address first */
if ( (ret = select_write_wrapper( fd ,
&(time_wait),
(&((*Cmd).addr)) ,
1 ))
!= 1 ) {
fprintf(stderr, "\n vrpn_atmellib::getCmd: Error while writing down. error=%i\n",
ret);
return ret;
}
#endif
while (time_wait.tv_usec!=0) {
#ifdef VRPN_ATMEL_SERIAL_VRPN
if (( vrpn_read_available_characters(fd, &(read_val), 1, &time_wait)) != 1) {
fprintf(stderr, "vrpn_atmellib::getCmd: Error vrpn_read_available_characters for first byte\n");
break;
}
// else
ret = read_val;
#else
if ((ret = select_read_wrapper(fd, &time_wait)) < 0) {
fprintf(stderr, "vrpn_atmellib::getCmd:\
Error select_read_wrapper for first byte: %i\n" , ret);
break;
}
#endif
// found expected first byte
if ((ret==PossibilityOne) || (ret==PossibilityTwo )) {
(*Cmd).addr = ret;
#ifdef VRPN_ATMEL_SERIAL_VRPN
if (( vrpn_read_available_characters(fd, &(read_val), 1, &time_wait)) != 1) {
fprintf(stderr, "vrpn_atmellib::getCmd: Error vrpn_read_available_characters.\n");;
break;
}
//else
ret = read_val;
#else
ret = select_read_wrapper(fd, &time_wait);
#endif
if ((ret < 0) || (ret > 128)) {
fprintf(stderr, "vrpn_atmellib::getCmd: Error reading second byte: %i\n\n" , ret);
break;
}
(*Cmd).value = ret;
#ifdef VRPN_ATMELLIB_TIME_MEASURE
// display time for
vrpn_gettimeofday( &end , 0);
sec=end.tv_sec-start.tv_sec;
usec=end.tv_usec-start.tv_usec;
printf("Time for reading out: sec=%i , usec=%i\n", sec, usec);
#endif
return ATMELLIB_NOERROR;
}
}
return ATMELLIB_ERROR_NORESPVAL;
}
/***************************************************************************************************/
/* write one command to the mc and wait for confirmation */
/***************************************************************************************************/
/* extern */ error_t
setCmd (handle_t fd , struct command_t * Cmd)
{
struct timeval time_wait;
int ret;
#ifdef VRPN_ATMEL_SERIAL_VRPN
unsigned char read_val;
#endif
// some adds to measure time for reading out
#ifdef VRPN_ATMELLIB_TIME_MEASURE
struct timeval start;
struct timeval end;
int sec, usec;
vrpn_gettimeofday( &start , 0);
#endif
/* check if the given parameters are valid */
if (handle_invalid(fd))
return ENXIO;
if ((*Cmd).addr < 128)
return ATMELLIB_ERROR_NOCOMVAL;
time_wait.tv_sec = VRPN_ATMELLIB_SELECT_WAIT_SEC;
time_wait.tv_usec = VRPN_ATMELLIB_SELECT_WAIT_USEC;
#ifdef VRPN_ATMEL_SERIAL_VRPN
vrpn_write_characters(fd, (unsigned char*) Cmd, 2);
#else
if ( (ret = select_write_wrapper( fd ,
&(time_wait),
(unsigned char*) Cmd ,
2 ))
!= 2 ) {
fprintf(stderr, "\n vrpn_atmellib::setCmd: Error while writing down. error=%i\n",
ret);
return ret;
}
#endif
#ifdef VRPN_ATMEL_SERIAL_VRPN
vrpn_flush_input_buffer(fd);
vrpn_flush_output_buffer(fd);
#else
tcflush( fd , TCIOFLUSH );
#endif
while (time_wait.tv_usec!=0) {
#ifdef VRPN_ATMEL_SERIAL_VRPN
if (( vrpn_read_available_characters(fd, &(read_val), 1, &time_wait)) != 1) {
fprintf(stderr, "vrpn_atmellib::setCmd: Error vrpn_read_available_characters.\n");;
break;
}
//else
ret = read_val;
#else
if ((ret = select_read_wrapper(fd, &time_wait)) < 0) {
fprintf(stderr, "vrpn_atmellib::setCmd: Error select_read_wrapper for first byte: %i\n" , ret);
break;
}
#endif
// found expected first byte
if (ret==(*Cmd).addr) {
#ifdef VRPN_ATMEL_SERIAL_VRPN
if (( vrpn_read_available_characters(fd, &(read_val), 1, &time_wait)) != 1) {
printf("Error vrpn_read_available_characters.\n");;
break;
}
//else
ret = read_val;
#else
ret = select_read_wrapper(fd, &time_wait);
#endif
if (ret!=(*Cmd).value) {
printf("vrpn_atmellib::setCmd: Error select_read_wrapper for second byte: %i\n" , ret);
break;
}
#ifdef ATMELLIB_TIME_MEASURE
// display time for
vrpn_gettimeofday( &end , 0);
sec=end.tv_sec-start.tv_sec;
usec=end.tv_usec-start.tv_usec;
printf("Time for writing down: sec=%i , usec=%i\n", sec, usec);
#endif
return ATMELLIB_NOERROR;
}
}
return ATMELLIB_ERROR_NORESPVAL;
}
/* file static for <ident> or <what> */
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma set woff 1174
#endif
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma reset woff 1174
#endif
/***************************************************************************************************/
/* */
/* Copyright (C) 2004 Bauhaus University Weimar */
/* Released into the public domain on 6/23/2007 as part of the VRPN project */
/* by Jan P. Springer. */
/* */
/***************************************************************************************************/
/* */
/* module : vrpn_atmellib_openclose.C */
/* project : atmel-avango */
/* description: handle open, close and initalization of the tty */
/* */
/***************************************************************************************************/
/* include system headers */
#include <errno.h> // for ENXIO, error_t
#include <fcntl.h> // for open, O_NOCTTY, O_RDWR
#include <termios.h> // for termios, cfsetispeed, etc
#include <unistd.h> // for close, isatty
/* include i/f header */
#include "vrpn_atmellib.h" // for handle_t, handle_invalid
#include "vrpn_atmellib_errno.h" // for ATMELLIB_NOERROR
/***************************************************************************************************/
/* internal (static) functions */
/*****************************************************************/
/* set the parameter to communicate with the microcontroller */
/*****************************************************************/
static int
Set_Params( handle_t fd , const int baud, struct termios * init_param)
{
struct termios Params;
/* get the actual parameters of the tty */
tcgetattr( fd , &Params );
/* save the init values of the tty */
(*init_param) = Params;
/* wait time for new data */
Params.c_cc[VTIME] = 0;
Params.c_cc[VMIN] = 1;
if (baud==38400) {
Params.c_cflag = B38400 | CSIZE | CS8 | CREAD | CLOCAL | HUPCL;
cfsetospeed( &Params , B38400 );
cfsetispeed( &Params , B38400 );
}
else {
// 9600 baud is default
Params.c_cflag = B9600 | CSIZE | CS8 | CREAD | CLOCAL | HUPCL;
cfsetospeed( &Params , B9600 );
cfsetispeed( &Params , B9600 );
}
Params.c_iflag = (IGNBRK | IGNPAR);
Params.c_lflag = 0;
Params.c_oflag = 0;
/* activate the new settings */
tcsetattr( fd , TCSANOW , &Params );
return ATMELLIB_NOERROR;
}
/***************************************************************************************************/
/* PART OF THE BASIC INTERFACE: for explainations to the functions see the header file */
/***************************************************************************************************/
/***************************************************************************************************/
/* open the specified serial port and set the parameter that it's ready to communicate with the mc*/
/***************************************************************************************************/
/* extern */ handle_t
openPort (const char* tty , const int baud, struct termios * init_param)
{
handle_t fd;
/* try to open thespecified device */
if ( (fd = open( tty , O_RDWR | O_NOCTTY )) < 0)
return fd;
/* check if the open file is a tty */
if (!isatty( fd )) {
close (fd);
return -1;
}
/* set the correct serial parameter to communicate with the microcontroller */
if ( Set_Params( fd , baud, init_param ) < 0 )
{
close (fd);
return -1;
}
return fd;
}
/***************************************************************************************************/
/* close the specified port and reset the parameter to the initial values */
/***************************************************************************************************/
/* extern */ error_t
closePort (handle_t fd , struct termios * init_param)
{
if (handle_invalid(fd))
return ENXIO;
/* first reset the parameters and then close the port */
tcsetattr( fd , TCSADRAIN, init_param );
close( fd );
return ATMELLIB_NOERROR;
}
/* file static for <ident> or <what> */
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma set woff 1174
#endif
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma reset woff 1174
#endif
/***************************************************************************************************/
/* */
/* Copyright (C) 2003 Bauhaus University Weimar */
/* Released into the public domain on 6/23/2007 as part of the VRPN project */
/* by Jan P. Springer. */
/* */
/***************************************************************************************************/
/* */
/* module : vrpn_atmellib_register.C */
/* project : atmel-avango */
/* description: function of an easy high level interface for access to the registers */
/* */
/***************************************************************************************************/
/* include system headers */
#include <errno.h> // for ENXIO, error_t
/* include i/f header */
#include "vrpn_atmellib.h" // for command_t, handle_invalid, etc
#include "vrpn_atmellib_errno.h" // for ATMELLIB_ERROR_REGINV, etc
#include "vrpn_atmellib_helper.h" // for getAddress, setValue
/***************************************************************************************************/
/* HIGH LEVEL INTERFACE */
/***************************************************************************************************/
/***************************************************************************************************/
/* write the value of the specified register and wait for confirmation */
/***************************************************************************************************/
/* extern */ error_t
setRegister( handle_t Handle , const unsigned char Register , const unsigned char Value)
{
struct command_t Cmd;
/* check if the arguments are valid */
if (handle_invalid(Handle))
return ENXIO;
if (register_invalid( Register ) != ATMELLIB_NOERROR)
return ATMELLIB_ERROR_REGINV;
/* combine the parameter to a valid commande */
Cmd.addr = getAddress( Register );
setValue( &Cmd , Value );
/* send the command to the microcontroller */
return ( setCmd( Handle , &Cmd ) );
}
/***************************************************************************************************/
/* read out the value of the specified register */
/***************************************************************************************************/
/*extern*/ int
getRegister( handle_t Handle , const unsigned char Register )
{
error_t R;
struct command_t Cmd;
/* check if the arguments are valid */
if (handle_invalid(Handle))
return ENXIO;
if (register_invalid( Register ) == ATMELLIB_ERROR_GENERAL)
return ATMELLIB_ERROR_REGINV;
/* make a valid command */
Cmd.addr = getAddress( Register );
/* Send the command to the microcontroller */
if ( (R = getCmd( Handle , &Cmd )) != ATMELLIB_NOERROR)
return R;
/* get the value */
if ( Cmd.addr & 0x01 ) Cmd.value += 128;
return (Cmd.value);
}
/* file static for <ident> or <what> */
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma set woff 1174
#endif
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma reset woff 1174
#endif
/***************************************************************************************************/
/* */
/* Copyright (C) 2004 Bauhaus University Weimar */
/* Released into the public domain on 6/23/2007 as part of the VRPN project */
/* by Jan P. Springer. */
/* */
/***************************************************************************************************/
/* */
/* module : vrpn_atmellib_tester.C */
/* project : atmel-avango */
/* description: functions to test the state */
/* */
/***************************************************************************************************/
/* include system headers */
#include <errno.h> // for error_t
/* include i/f header */
#include "vrpn_atmellib.h" // for handle_t
#include "vrpn_atmellib_errno.h" // for ATMELLIB_ERROR_GENERAL, etc
/***************************************************************************************************/
/* internal (static) variables */
/* define a invalid handle */
const static int invalid_handle = -1;
/***************************************************************************************************/
/* test if the given handler is a valid handle */
/***************************************************************************************************/
/* extern */ error_t
handle_invalid (handle_t handle)
{
return (invalid_handle == handle);
}
/***************************************************************************************************/
/* test if the register is valid - if it exists on the mc */
/***************************************************************************************************/
/* extern */ error_t
register_invalid (unsigned char Register)
{
if (Register > 63) {
return ATMELLIB_ERROR_GENERAL;
}
return ATMELLIB_NOERROR;
}
/* file static for <ident> or <what> */
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma set woff 1174
#endif
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
# pragma reset woff 1174
#endif
.depend
make.out
makefile
pc_cygwin
pc_linux
sgi_irix
sparc_solaris
powerpc_aix
*.vcproj.*.user