From 7ebf206688af2d3e193d8c2e6b409c40edc00bdd Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 9 Jul 2018 13:40:54 +0200 Subject: [PATCH] add ANT --- .idea/gradle.xml | 1 + .idea/misc.xml | 2 +- .idea/modules.xml | 1 + Application/Application.iml | 13 +- Application/build.gradle | 2 + Application/src/main/AndroidManifest.xml | 1 + .../dsi/ant/channel/PredefinedNetwork.java | 40 +++ .../iconsole/BluetoothChatFragment.java | 69 ++++ .../org/surfsite/iconsole/ChannelService.java | 262 +++++++++++++++ .../org/surfsite/iconsole/MainActivity.java | 12 +- .../iconsole/PowerChannelController.java | 292 +++++++++++++++++ .../iconsole/SpeedChannelController.java | 306 ++++++++++++++++++ android_antlib_4-14/android_antlib_4-14.iml | 25 ++ android_antlib_4-14/build.gradle | 2 + settings.gradle | 5 +- 15 files changed, 1026 insertions(+), 7 deletions(-) create mode 100644 Application/src/main/java/com/dsi/ant/channel/PredefinedNetwork.java create mode 100644 Application/src/main/java/org/surfsite/iconsole/ChannelService.java create mode 100644 Application/src/main/java/org/surfsite/iconsole/PowerChannelController.java create mode 100644 Application/src/main/java/org/surfsite/iconsole/SpeedChannelController.java create mode 100644 android_antlib_4-14/android_antlib_4-14.iml create mode 100644 android_antlib_4-14/build.gradle diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 773d366..1c53797 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -9,6 +9,7 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml index c8421df..14a85c1 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + diff --git a/Application/Application.iml b/Application/Application.iml index bb90e68..153210c 100644 --- a/Application/Application.iml +++ b/Application/Application.iml @@ -90,17 +90,27 @@ + + + + + + + - + + + + @@ -130,5 +140,6 @@ + \ No newline at end of file diff --git a/Application/build.gradle b/Application/build.gradle index 232ebea..8f87031 100644 --- a/Application/build.gradle +++ b/Application/build.gradle @@ -29,8 +29,10 @@ dependencies { implementation "com.android.support:appcompat-v7:27.1.1" implementation 'com.android.support:support-v13:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.2' + implementation project(':android_antlib_4-14') } + // The sample build uses multiple directories to // keep boilerplate and common code separate from // the main sample code. diff --git a/Application/src/main/AndroidManifest.xml b/Application/src/main/AndroidManifest.xml index 01cfa2b..2b01c64 100644 --- a/Application/src/main/AndroidManifest.xml +++ b/Application/src/main/AndroidManifest.xml @@ -43,6 +43,7 @@ + 0; + // Initial check for if legacy interface is in use. If the + // legacy interface is in use, applications can free the ANT + // radio by attempting to acquire a channel. + boolean legacyInterfaceInUse = mAntChannelProvider.isLegacyInterfaceInUse(); + + // If there are channels OR legacy interface in use, allow adding channels + if (mChannelAvailable || legacyInterfaceInUse) { + mAllowAddChannel = true; + } else { + // If no channels available AND legacy interface is not in use, disallow adding channels + mAllowAddChannel = false; + } + + + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + die("Binder Died"); + + mAntChannelProvider = null; + mAntRadioService = null; + + mAllowAddChannel = false; + } + + }; + + /** + * The interface used to communicate with the ChannelService + */ + public class ChannelServiceComm extends Binder { + + void setSpeed(double speed) { + if (null != speedChannelController) { + speedChannelController.speed = speed; + } + } + + void setPower(int power) { + if (null != powerChannelController) { + powerChannelController.power = power; + } + } + + void setCadence(int cadence) { + if (null != powerChannelController) { + powerChannelController.cadence = cadence; + } + } + + /** + * Closes all channels currently added. + */ + void clearAllChannels() { + closeAllChannels(); + } + } + + public void openAllChannels() throws ChannelNotAvailableException { + powerChannelController = new PowerChannelController(acquireChannel()); + speedChannelController = new SpeedChannelController(acquireChannel()); + } + + private void closeAllChannels() { + powerChannelController.close(); + speedChannelController.close(); + powerChannelController = null; + speedChannelController = null; + } + + AntChannel acquireChannel() throws ChannelNotAvailableException { + AntChannel mAntChannel = null; + if (null != mAntChannelProvider) { + try { + /* + * If applications require a channel with specific capabilities + * (event buffering, background scanning etc.), a Capabilities + * object should be created and then the specific capabilities + * required set to true. Applications can specify both required + * and desired Capabilities with both being passed in + * acquireChannel(context, PredefinedNetwork, + * requiredCapabilities, desiredCapabilities). + */ + mAntChannel = mAntChannelProvider.acquireChannel(this, PredefinedNetwork.ANT_PLUS_1); + /* + NetworkKey mNK = new NetworkKey(new byte[] { (byte)0xb9, (byte)0xa5, (byte)0x21, (byte)0xfb, + (byte)0xbd, (byte)0x72, (byte)0xc3, (byte)0x45 }); + Log.v(TAG, mNK.toString()); + mAntChannel = mAntChannelProvider.acquireChannelOnPrivateNetwork(this, mNK); + */ + } catch (RemoteException e) { + die("ACP Remote Ex"); + } + } + return mAntChannel; + } + + @Override + public IBinder onBind(Intent arg0) { + return new ChannelServiceComm(); + } + + /** + * Receives AntChannelProvider state changes being sent from ANT Radio Service + */ + private final BroadcastReceiver mChannelProviderStateChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (AntChannelProvider.ACTION_CHANNEL_PROVIDER_STATE_CHANGED.equals(intent.getAction())) { + boolean update = false; + // Retrieving the data contained in the intent + int numChannels = intent.getIntExtra(AntChannelProvider.NUM_CHANNELS_AVAILABLE, 0); + boolean legacyInterfaceInUse = intent.getBooleanExtra(AntChannelProvider.LEGACY_INTERFACE_IN_USE, false); + + if (mAllowAddChannel) { + // Was a acquire channel allowed + // If no channels available AND legacy interface is not in use, disallow acquiring of channels + if (0 == numChannels && !legacyInterfaceInUse) { + mAllowAddChannel = false; + update = true; + closeAllChannels(); + } + } else { + // Acquire channels not allowed + // If there are channels OR legacy interface in use, allow acquiring of channels + if (numChannels > 0 || legacyInterfaceInUse) { + mAllowAddChannel = true; + update = true; + try { + openAllChannels(); + } catch (ChannelNotAvailableException exception) { + Log.e(TAG, "Channel not available!!"); + } + } + } + } + } + }; + + private void doBindAntRadioService() { + if (BuildConfig.DEBUG) Log.v(TAG, "doBindAntRadioService"); + + // Start listing for channel available intents + registerReceiver(mChannelProviderStateChangedReceiver, new IntentFilter(AntChannelProvider.ACTION_CHANNEL_PROVIDER_STATE_CHANGED)); + + // Creating the intent and calling context.bindService() is handled by + // the static bindService() method in AntService + mAntRadioServiceBound = AntService.bindService(this, mAntRadioServiceConnection); + } + + private void doUnbindAntRadioService() { + if (BuildConfig.DEBUG) Log.v(TAG, "doUnbindAntRadioService"); + + // Stop listing for channel available intents + try { + unregisterReceiver(mChannelProviderStateChangedReceiver); + } catch (IllegalArgumentException exception) { + if (BuildConfig.DEBUG) + Log.d(TAG, "Attempting to unregister a never registered Channel Provider State Changed receiver."); + } + + if (mAntRadioServiceBound) { + try { + unbindService(mAntRadioServiceConnection); + } catch (IllegalArgumentException e) { + // Not bound, that's what we want anyway + } + + mAntRadioServiceBound = false; + } + } + + @Override + public void onCreate() { + super.onCreate(); + + mAntRadioServiceBound = false; + + doBindAntRadioService(); + + } + + @Override + public void onDestroy() { + closeAllChannels(); + + doUnbindAntRadioService(); + mAntChannelProvider = null; + + super.onDestroy(); + } + + static void die(String error) { + Log.e(TAG, "DIE: " + error); + } + +} diff --git a/Application/src/main/java/org/surfsite/iconsole/MainActivity.java b/Application/src/main/java/org/surfsite/iconsole/MainActivity.java index 6e26949..b917357 100644 --- a/Application/src/main/java/org/surfsite/iconsole/MainActivity.java +++ b/Application/src/main/java/org/surfsite/iconsole/MainActivity.java @@ -30,8 +30,10 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; +import android.widget.Button; import android.widget.ViewAnimator; +import org.surfsite.iconsole.ChannelService.ChannelServiceComm; /** @@ -70,8 +72,16 @@ public class MainActivity extends FragmentActivity { @Override protected void onDestroy() { - stopService(new Intent(this, BluetoothChatService.class)); + + if(isFinishing()) + { + stopService(new Intent(this, BluetoothChatService.class)); + stopService(new Intent(this, ChannelService.class)); + } super.onDestroy(); } + + + } diff --git a/Application/src/main/java/org/surfsite/iconsole/PowerChannelController.java b/Application/src/main/java/org/surfsite/iconsole/PowerChannelController.java new file mode 100644 index 0000000..d678426 --- /dev/null +++ b/Application/src/main/java/org/surfsite/iconsole/PowerChannelController.java @@ -0,0 +1,292 @@ +/* + * Copyright 2012 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.surfsite.iconsole; + +import android.os.RemoteException; +import android.util.Log; + +import com.dsi.ant.channel.AntChannel; +import com.dsi.ant.channel.AntCommandFailedException; +import com.dsi.ant.channel.IAntChannelEventHandler; +import com.dsi.ant.message.ChannelId; +import com.dsi.ant.message.ChannelType; +import com.dsi.ant.message.EventCode; +import com.dsi.ant.message.fromant.AcknowledgedDataMessage; +import com.dsi.ant.message.fromant.ChannelEventMessage; +import com.dsi.ant.message.fromant.MessageFromAntType; +import com.dsi.ant.message.ipc.AntMessageParcel; + +import java.util.Random; + +public class PowerChannelController { + public static final int POWER_SENSOR_ID = 0x9e3d4b66; + // The device type and transmission type to be part of the channel ID message + private static final int CHANNEL_POWER_DEVICE_TYPE = 0x0B; + private static final int CHANNEL_POWER_TRANSMISSION_TYPE = 5; + // The period and frequency values the channel will be configured to + private static final int CHANNEL_POWER_PERIOD = 8182; // 1 Hz + private static final int CHANNEL_POWER_FREQUENCY = 57; + private static final String TAG = PowerChannelController.class.getSimpleName(); + private static Random randGen = new Random(); + int power = 0; + int cadence = 0; + private AntChannel mAntChannel; + private ChannelEventCallback mChannelEventCallback = new ChannelEventCallback(); + private boolean mIsOpen; + + public PowerChannelController(AntChannel antChannel) { + mAntChannel = antChannel; + openChannel(); + } + + boolean openChannel() { + if (null != mAntChannel) { + if (mIsOpen) { + Log.w(TAG, "Channel was already open"); + } else { + // Channel ID message contains device number, type and transmission type. In + // order for master (TX) channels and slave (RX) channels to connect, they + // must have the same channel ID, or wildcard (0) is used. + ChannelId channelId = new ChannelId(POWER_SENSOR_ID & 0xFFFF, + CHANNEL_POWER_DEVICE_TYPE, CHANNEL_POWER_TRANSMISSION_TYPE); + + try { + // Setting the channel event handler so that we can receive messages from ANT + mAntChannel.setChannelEventHandler(mChannelEventCallback); + + // Performs channel assignment by assigning the type to the channel. Additional + // features (such as, background scanning and frequency agility) can be enabled + // by passing an ExtendedAssignment object to assign(ChannelType, ExtendedAssignment). + mAntChannel.assign(ChannelType.BIDIRECTIONAL_MASTER); + + /* + * Configures the channel ID, messaging period and rf frequency after assigning, + * then opening the channel. + * + * For any additional ANT features such as proximity search or background scanning, refer to + * the ANT Protocol Doc found at: + * http://www.thisisant.com/resources/ant-message-protocol-and-usage/ + */ + mAntChannel.setChannelId(channelId); + mAntChannel.setPeriod(CHANNEL_POWER_PERIOD); + mAntChannel.setRfFrequency(CHANNEL_POWER_FREQUENCY); + mAntChannel.open(); + mIsOpen = true; + + Log.d(TAG, "Opened channel with device number: " + POWER_SENSOR_ID); + } catch (RemoteException e) { + channelError(e); + } catch (AntCommandFailedException e) { + // This will release, and therefore unassign if required + channelError("Open failed", e); + } + } + } else { + Log.w(TAG, "No channel available"); + } + + return mIsOpen; + } + + + void channelError(RemoteException e) { + String logString = "Remote service communication failed."; + + Log.e(TAG, logString); + + } + + void channelError(String error, AntCommandFailedException e) { + StringBuilder logString; + + if (e.getResponseMessage() != null) { + String initiatingMessageId = "0x" + Integer.toHexString( + e.getResponseMessage().getInitiatingMessageId()); + String rawResponseCode = "0x" + Integer.toHexString( + e.getResponseMessage().getRawResponseCode()); + + logString = new StringBuilder(error) + .append(". Command ") + .append(initiatingMessageId) + .append(" failed with code ") + .append(rawResponseCode); + } else { + String attemptedMessageId = "0x" + Integer.toHexString( + e.getAttemptedMessageType().getMessageId()); + String failureReason = e.getFailureReason().toString(); + + logString = new StringBuilder(error) + .append(". Command ") + .append(attemptedMessageId) + .append(" failed with reason ") + .append(failureReason); + } + + Log.e(TAG, logString.toString()); + + mAntChannel.release(); + } + + public void close() { + // TODO kill all our resources + if (null != mAntChannel) { + mIsOpen = false; + + // Releasing the channel to make it available for others. + // After releasing, the AntChannel instance cannot be reused. + mAntChannel.release(); + mAntChannel = null; + } + + Log.e(TAG, "Channel Closed"); + } + + /** + * Implements the Channel Event Handler Interface so that messages can be + * received and channel death events can be handled. + */ + public class ChannelEventCallback implements IAntChannelEventHandler { + + int cnt = 0; + int eventCount = 0; + int cumulativePower = 0; + + @Override + public void onChannelDeath() { + // Display channel death message when channel dies + Log.e(TAG, "Channel Death"); + } + + @Override + public void onReceiveMessage(MessageFromAntType messageType, AntMessageParcel antParcel) { + Log.d(TAG, "Rx: " + antParcel); + Log.d(TAG, "Message Type: " + messageType); + byte[] payload = new byte[8]; + + // Switching on message type to handle different types of messages + switch (messageType) { + // If data message, construct from parcel and update channel data + case BROADCAST_DATA: + // Rx Data + //updateData(new BroadcastDataMessage(antParcel).getPayload()); + break; + case ACKNOWLEDGED_DATA: + // Rx Data + //updateData(new AcknowledgedDataMessage(antParcel).getPayload()); + payload = new AcknowledgedDataMessage(antParcel).getPayload(); + Log.d(TAG, "AcknowledgedDataMessage: " + payload); + + if (payload[0] == 0 && payload[1] == 1 && payload[2] == 0xAA) { + payload[0] = (byte) 0x01; + payload[1] = (byte) 0xAC; + payload[2] = (byte) 0xFF; + payload[3] = (byte) 0xFF; + payload[4] = (byte) 0xFF; + payload[5] = (byte) 0xFF; + payload[6] = (byte) 0x00; + payload[7] = (byte) 0x00; + try { + // Setting the data to be broadcast on the next channel period + mAntChannel.setBroadcastData(payload); + } catch (RemoteException e) { + channelError(e); + } + } + break; + case CHANNEL_EVENT: + // Constructing channel event message from parcel + ChannelEventMessage eventMessage = new ChannelEventMessage(antParcel); + EventCode code = eventMessage.getEventCode(); + Log.d(TAG, "Event Code: " + code); + + // Switching on event code to handle the different types of channel events + switch (code) { + case TX: + cnt += 1; + + if (cnt % 61 == 15) { + payload[0] = (byte) 0x50; + payload[1] = (byte) 0xFF; + payload[2] = (byte) 0xFF; + payload[3] = (byte) 0x01; + payload[4] = (byte) 0xFF; + payload[5] = (byte) 0x00; + payload[6] = (byte) 0x01; + payload[7] = (byte) 0x00; + } else if (cnt % 61 == 30) { + payload[0] = (byte) 0x51; + payload[1] = (byte) 0xFF; + payload[2] = (byte) 0xFF; + payload[3] = (byte) 0x01; + payload[4] = (byte) ((POWER_SENSOR_ID) & 0xFF); + payload[5] = (byte) ((POWER_SENSOR_ID >> 8) & 0xFF); + payload[6] = (byte) ((POWER_SENSOR_ID >> 16) & 0xFF); + payload[7] = (byte) ((POWER_SENSOR_ID >> 24) & 0xFF); + } else { + eventCount = (eventCount + 1) & 0xFF; + cumulativePower = (cumulativePower + power) & 0xFFFF; + payload[0] = (byte) 0x10; + payload[1] = (byte) eventCount; + payload[2] = (byte) 0xFF; + payload[3] = (byte) cadence; + payload[4] = (byte) ((cumulativePower) & 0xFF); + payload[5] = (byte) ((cumulativePower >> 8) & 0xFF); + payload[6] = (byte) ((power) & 0xFF); + payload[7] = (byte) ((power >> 8) & 0xFF); + } + + if (mIsOpen) { + try { + // Setting the data to be broadcast on the next channel period + mAntChannel.setBroadcastData(payload); + } catch (RemoteException e) { + channelError(e); + } + } + break; + case CHANNEL_COLLISION: + cnt += 1; + break; + case RX_SEARCH_TIMEOUT: + // TODO May want to keep searching + Log.e(TAG, "No Device Found"); + break; + case CHANNEL_CLOSED: + case RX_FAIL: + case RX_FAIL_GO_TO_SEARCH: + case TRANSFER_RX_FAILED: + case TRANSFER_TX_COMPLETED: + case TRANSFER_TX_FAILED: + case TRANSFER_TX_START: + case UNKNOWN: + // TODO More complex communication will need to handle these events + break; + } + break; + case ANT_VERSION: + case BURST_TRANSFER_DATA: + case CAPABILITIES: + case CHANNEL_ID: + case CHANNEL_RESPONSE: + case CHANNEL_STATUS: + case SERIAL_NUMBER: + case OTHER: + // TODO More complex communication will need to handle these message types + break; + } + } + } +} diff --git a/Application/src/main/java/org/surfsite/iconsole/SpeedChannelController.java b/Application/src/main/java/org/surfsite/iconsole/SpeedChannelController.java new file mode 100644 index 0000000..7a83b33 --- /dev/null +++ b/Application/src/main/java/org/surfsite/iconsole/SpeedChannelController.java @@ -0,0 +1,306 @@ +/* + * Copyright 2012 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.surfsite.iconsole; + +import android.os.RemoteException; +import android.util.Log; + +import com.dsi.ant.channel.AntChannel; +import com.dsi.ant.channel.AntCommandFailedException; +import com.dsi.ant.channel.IAntChannelEventHandler; +import com.dsi.ant.message.ChannelId; +import com.dsi.ant.message.ChannelType; +import com.dsi.ant.message.EventCode; +import com.dsi.ant.message.fromant.ChannelEventMessage; +import com.dsi.ant.message.fromant.MessageFromAntType; +import com.dsi.ant.message.ipc.AntMessageParcel; + +import java.util.Random; + +public class SpeedChannelController { + // The device type and transmission type to be part of the channel ID message + private static final int CHANNEL_SPEED_DEVICE_TYPE = 0x7B; + private static final int CHANNEL_SPEED_TRANSMISSION_TYPE = 1; + + // The period and frequency values the channel will be configured to + private static final int CHANNEL_SPEED_PERIOD = 8118; // 1 Hz + private static final int CHANNEL_SPEED_FREQUENCY = 57; + + private static final String TAG = SpeedChannelController.class.getSimpleName(); + public static final int SPEED_SENSOR_ID = 0x9e3d4b65; + + private static Random randGen = new Random(); + + private AntChannel mAntChannel; + + private ChannelEventCallback mChannelEventCallback = new ChannelEventCallback(); + + + private boolean mIsOpen; + double speed = 0.0; + + public SpeedChannelController(AntChannel antChannel) { + mAntChannel = antChannel; + openChannel(); + } + + boolean openChannel() { + if (null != mAntChannel) { + if (mIsOpen) { + Log.w(TAG, "Channel was already open"); + } else { + // Channel ID message contains device number, type and transmission type. In + // order for master (TX) channels and slave (RX) channels to connect, they + // must have the same channel ID, or wildcard (0) is used. + ChannelId channelId = new ChannelId(SPEED_SENSOR_ID & 0xFFFF, + CHANNEL_SPEED_DEVICE_TYPE, CHANNEL_SPEED_TRANSMISSION_TYPE); + + try { + // Setting the channel event handler so that we can receive messages from ANT + mAntChannel.setChannelEventHandler(mChannelEventCallback); + + // Performs channel assignment by assigning the type to the channel. Additional + // features (such as, background scanning and frequency agility) can be enabled + // by passing an ExtendedAssignment object to assign(ChannelType, ExtendedAssignment). + mAntChannel.assign(ChannelType.BIDIRECTIONAL_MASTER); + + /* + * Configures the channel ID, messaging period and rf frequency after assigning, + * then opening the channel. + * + * For any additional ANT features such as proximity search or background scanning, refer to + * the ANT Protocol Doc found at: + * http://www.thisisant.com/resources/ant-message-protocol-and-usage/ + */ + mAntChannel.setChannelId(channelId); + mAntChannel.setPeriod(CHANNEL_SPEED_PERIOD); + mAntChannel.setRfFrequency(CHANNEL_SPEED_FREQUENCY); + mAntChannel.open(); + mIsOpen = true; + + Log.d(TAG, "Opened channel with device number: " + SPEED_SENSOR_ID); + } catch (RemoteException e) { + channelError(e); + } catch (AntCommandFailedException e) { + // This will release, and therefore unassign if required + channelError("Open failed", e); + } + } + } else { + Log.w(TAG, "No channel available"); + } + + return mIsOpen; + } + + void channelError(RemoteException e) { + String logString = "Remote service communication failed."; + + Log.e(TAG, logString); + } + + void channelError(String error, AntCommandFailedException e) { + StringBuilder logString; + + if (e.getResponseMessage() != null) { + String initiatingMessageId = "0x" + Integer.toHexString( + e.getResponseMessage().getInitiatingMessageId()); + String rawResponseCode = "0x" + Integer.toHexString( + e.getResponseMessage().getRawResponseCode()); + + logString = new StringBuilder(error) + .append(". Command ") + .append(initiatingMessageId) + .append(" failed with code ") + .append(rawResponseCode); + } else { + String attemptedMessageId = "0x" + Integer.toHexString( + e.getAttemptedMessageType().getMessageId()); + String failureReason = e.getFailureReason().toString(); + + logString = new StringBuilder(error) + .append(". Command ") + .append(attemptedMessageId) + .append(" failed with reason ") + .append(failureReason); + } + + Log.e(TAG, logString.toString()); + + mAntChannel.release(); + + Log.e(TAG, "ANT Command Failed"); + } + + public void close() { + // TODO kill all our resources + if (null != mAntChannel) { + mIsOpen = false; + + // Releasing the channel to make it available for others. + // After releasing, the AntChannel instance cannot be reused. + mAntChannel.release(); + mAntChannel = null; + } + + Log.e(TAG, "Channel Closed"); + } + + /** + * Implements the Channel Event Handler Interface so that messages can be + * received and channel death events can be handled. + */ + public class ChannelEventCallback implements IAntChannelEventHandler { + int revCounts = 0; + int ucMessageCount = 0; + byte ucPageChange = 0; + byte ucExtMesgType = 1; + long lastTime = 0; + double way; + int rev; + double remWay; + double wheel = 0.1; + + @Override + public void onChannelDeath() { + // Display channel death message when channel dies + Log.e(TAG, "Channel Death"); + } + + @Override + public void onReceiveMessage(MessageFromAntType messageType, AntMessageParcel antParcel) { + Log.d(TAG, "Rx: " + antParcel); + Log.d(TAG, "Message Type: " + messageType); + + // Switching on message type to handle different types of messages + switch (messageType) { + // If data message, construct from parcel and update channel data + case BROADCAST_DATA: + // Rx Data + //updateData(new BroadcastDataMessage(antParcel).getPayload()); + break; + case ACKNOWLEDGED_DATA: + // Rx Data + //updateData(new AcknowledgedDataMessage(antParcel).getPayload()); + break; + case CHANNEL_EVENT: + // Constructing channel event message from parcel + ChannelEventMessage eventMessage = new ChannelEventMessage(antParcel); + EventCode code = eventMessage.getEventCode(); + Log.d(TAG, "Event Code: " + code); + + // Switching on event code to handle the different types of channel events + switch (code) { + case TX: + long unixTime = System.currentTimeMillis() / 1000L; + + if (lastTime != 0) { + way = speed * (unixTime - lastTime) / 3.6 + remWay; + rev = (int)(way / wheel + 0.5); + remWay = way - rev * wheel; + revCounts += rev; + } + lastTime = unixTime; + + ucPageChange += 0x20; + ucPageChange &= 0xF0; + ucMessageCount += 1; + byte[] payload = new byte[8]; + + if (ucMessageCount >= 65) { + if (ucExtMesgType >= 4) + ucExtMesgType = 1; + + if (ucExtMesgType == 1) { + int halfunixTime = (int) (unixTime / 2L); + payload[0] = (byte) ((byte) 0x01 | (byte) (ucPageChange & (byte) 0x80)); + payload[1] = (byte) (halfunixTime & 0xFF); + payload[2] = (byte) ((halfunixTime >> 8) & 0xFF); + payload[3] = (byte) ((halfunixTime >> 16) & 0xFF); + } + else if (ucExtMesgType == 2) { + payload[0] = (byte) ((byte) 0x02 | (byte) (ucPageChange & (byte) 0x80)); + payload[1] = (byte) 0xFF; + payload[2] = (byte) ((SPEED_SENSOR_ID >> 16) & 0xFF); + payload[3] = (byte) ((SPEED_SENSOR_ID >> 24) & 0xFF); + } + else if (ucExtMesgType == 3) { + payload[0] = (byte) ((byte) 0x03 | (byte) (ucPageChange & (byte) 0x80)); + payload[1] = (byte) 0x01; + payload[2] = (byte) 0x01; + payload[3] = (byte) 0x01; + } + if (ucMessageCount >= 68) { + ucMessageCount = 0; + ucExtMesgType += 1; + } + } else { + payload[0] = (byte) (ucPageChange & 0x80); + payload[1] = (byte) 0xFF; + payload[2] = (byte) 0xFF; + payload[3] = (byte) 0xFF; + } + + int unixTime1024 = (int) (unixTime * 1024); + payload[4] = (byte) (unixTime1024 & 0xFF); + payload[5] = (byte) ((unixTime1024 >> 8) & 0xFF); + payload[6] = (byte) (revCounts & 0xFF); + payload[7] = (byte) ((revCounts >> 8) & 0xFF); + + if (mIsOpen) { + try { + // Setting the data to be broadcast on the next channel period + mAntChannel.setBroadcastData(payload); + } catch (RemoteException e) { + channelError(e); + } + } + break; + case CHANNEL_COLLISION: + ucPageChange += 0x20; + ucPageChange &= 0xF0; + ucMessageCount += 1; + break; + case RX_SEARCH_TIMEOUT: + // TODO May want to keep searching + Log.e(TAG, "No Device Found"); + break; + case CHANNEL_CLOSED: + case RX_FAIL: + case RX_FAIL_GO_TO_SEARCH: + case TRANSFER_RX_FAILED: + case TRANSFER_TX_COMPLETED: + case TRANSFER_TX_FAILED: + case TRANSFER_TX_START: + case UNKNOWN: + // TODO More complex communication will need to handle these events + break; + } + break; + case ANT_VERSION: + case BURST_TRANSFER_DATA: + case CAPABILITIES: + case CHANNEL_ID: + case CHANNEL_RESPONSE: + case CHANNEL_STATUS: + case SERIAL_NUMBER: + case OTHER: + // TODO More complex communication will need to handle these message types + break; + } + } + } +} diff --git a/android_antlib_4-14/android_antlib_4-14.iml b/android_antlib_4-14/android_antlib_4-14.iml new file mode 100644 index 0000000..18306c8 --- /dev/null +++ b/android_antlib_4-14/android_antlib_4-14.iml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android_antlib_4-14/build.gradle b/android_antlib_4-14/build.gradle new file mode 100644 index 0000000..04de0a9 --- /dev/null +++ b/android_antlib_4-14/build.gradle @@ -0,0 +1,2 @@ +configurations.maybeCreate("default") +artifacts.add("default", file('android_antlib_4-14-0.jar')) \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 68c02fb..7f179be 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1 @@ - - - -include 'Application' +include 'Application', ':android_antlib_4-14'