├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml └── vcs.xml ├── Adafruit_BLE_UART.iml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── adafruit │ │ └── bleuart │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── adafruit │ │ └── bleuart │ │ ├── BluetoothLeUart.java │ │ └── MainActivity.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── layout │ └── activity_main.xml │ ├── menu │ └── main.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | .DS_Store 5 | /build 6 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | BLE UART -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Adafruit_BLE_UART.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Adafruit Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Adafruit Android BLE UART 2 | ========================= 3 | 4 | Example code for interacting with a Bluefruit Bluetooth LE UART in an Android application. Run the application and it will connect to the first available Bluetooth LE UART found to provide a simple console for sending and receiving data. Try testing with a Bluefruit LE configured to run the echoDemo! 5 | 6 | **This code requires the latest version of [Android Studio](https://developer.android.com/sdk/installing/studio.html) (0.8 beta) to build.** Download the repository and use the 'Import project...' option to load the directory as a project in Android Studio. The code will not work with the legacy Eclipse-based ADT build environment! 7 | 8 | **Warning:** This is alpha/beta quality software and only serves as an example of how to use a Bluetooth LE UART in an Android application! Bluetooth LE support is somewhat flakey in Android so it is _highly recommended_ that you upgrade to the absolute latest version of Android possible (ideally 4.4.4+) and use hardware that is known to be somewhat reliable like a Nexus device. 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 19 5 | buildToolsVersion "19.1.0" 6 | 7 | defaultConfig { 8 | applicationId "com.adafruit.bleuart" 9 | minSdkVersion 18 10 | targetSdkVersion 19 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | runProguard false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | } 25 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/tony/adt-bundle-linux-x86_64-20131030/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/adafruit/bleuart/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.adafruit.bleuart; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/adafruit/bleuart/BluetoothLeUart.java: -------------------------------------------------------------------------------- 1 | package com.adafruit.bleuart; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.bluetooth.BluetoothGatt; 6 | import android.bluetooth.BluetoothGattCallback; 7 | import android.bluetooth.BluetoothGattCharacteristic; 8 | import android.bluetooth.BluetoothGattDescriptor; 9 | import android.content.Context; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.nio.ByteOrder; 13 | import java.nio.charset.Charset; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Queue; 17 | import java.util.UUID; 18 | import java.util.WeakHashMap; 19 | import java.lang.String; 20 | import java.util.concurrent.ConcurrentLinkedQueue; 21 | 22 | public class BluetoothLeUart extends BluetoothGattCallback implements BluetoothAdapter.LeScanCallback { 23 | 24 | // UUIDs for UART service and associated characteristics. 25 | public static UUID UART_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"); 26 | public static UUID TX_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"); 27 | public static UUID RX_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"); 28 | 29 | // UUID for the UART BTLE client characteristic which is necessary for notifications. 30 | public static UUID CLIENT_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); 31 | 32 | // UUIDs for the Device Information service and associated characeristics. 33 | public static UUID DIS_UUID = UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb"); 34 | public static UUID DIS_MANUF_UUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb"); 35 | public static UUID DIS_MODEL_UUID = UUID.fromString("00002a24-0000-1000-8000-00805f9b34fb"); 36 | public static UUID DIS_HWREV_UUID = UUID.fromString("00002a26-0000-1000-8000-00805f9b34fb"); 37 | public static UUID DIS_SWREV_UUID = UUID.fromString("00002a28-0000-1000-8000-00805f9b34fb"); 38 | 39 | // Internal UART state. 40 | private Context context; 41 | private WeakHashMap callbacks; 42 | private BluetoothAdapter adapter; 43 | private BluetoothGatt gatt; 44 | private BluetoothGattCharacteristic tx; 45 | private BluetoothGattCharacteristic rx; 46 | private boolean connectFirst; 47 | private boolean writeInProgress; // Flag to indicate a write is currently in progress 48 | 49 | // Device Information state. 50 | private BluetoothGattCharacteristic disManuf; 51 | private BluetoothGattCharacteristic disModel; 52 | private BluetoothGattCharacteristic disHWRev; 53 | private BluetoothGattCharacteristic disSWRev; 54 | private boolean disAvailable; 55 | 56 | // Queues for characteristic read (synchronous) 57 | private Queue readQueue; 58 | 59 | // Interface for a BluetoothLeUart client to be notified of UART actions. 60 | public interface Callback { 61 | public void onConnected(BluetoothLeUart uart); 62 | public void onConnectFailed(BluetoothLeUart uart); 63 | public void onDisconnected(BluetoothLeUart uart); 64 | public void onReceive(BluetoothLeUart uart, BluetoothGattCharacteristic rx); 65 | public void onDeviceFound(BluetoothDevice device); 66 | public void onDeviceInfoAvailable(); 67 | } 68 | 69 | public BluetoothLeUart(Context context) { 70 | super(); 71 | this.context = context; 72 | this.callbacks = new WeakHashMap(); 73 | this.adapter = BluetoothAdapter.getDefaultAdapter(); 74 | this.gatt = null; 75 | this.tx = null; 76 | this.rx = null; 77 | this.disManuf = null; 78 | this.disModel = null; 79 | this.disHWRev = null; 80 | this.disSWRev = null; 81 | this.disAvailable = false; 82 | this.connectFirst = false; 83 | this.writeInProgress = false; 84 | this.readQueue = new ConcurrentLinkedQueue(); 85 | } 86 | 87 | // Return instance of BluetoothGatt. 88 | public BluetoothGatt getGatt() { 89 | return gatt; 90 | } 91 | 92 | // Return true if connected to UART device, false otherwise. 93 | public boolean isConnected() { 94 | return (tx != null && rx != null); 95 | } 96 | 97 | public String getDeviceInfo() { 98 | if (tx == null || !disAvailable ) { 99 | // Do nothing if there is no connection. 100 | return ""; 101 | } 102 | StringBuilder sb = new StringBuilder(); 103 | sb.append("Manufacturer : " + disManuf.getStringValue(0) + "\n"); 104 | sb.append("Model : " + disModel.getStringValue(0) + "\n"); 105 | sb.append("Firmware : " + disSWRev.getStringValue(0) + "\n"); 106 | return sb.toString(); 107 | }; 108 | 109 | public boolean deviceInfoAvailable() { return disAvailable; } 110 | 111 | // Send data to connected UART device. 112 | public void send(byte[] data) { 113 | if (tx == null || data == null || data.length == 0) { 114 | // Do nothing if there is no connection or message to send. 115 | return; 116 | } 117 | // Update TX characteristic value. Note the setValue overload that takes a byte array must be used. 118 | tx.setValue(data); 119 | writeInProgress = true; // Set the write in progress flag 120 | gatt.writeCharacteristic(tx); 121 | // ToDo: Update to include a timeout in case this goes into the weeds 122 | while (writeInProgress); // Wait for the flag to clear in onCharacteristicWrite 123 | } 124 | 125 | // Send data to connected UART device. 126 | public void send(String data) { 127 | if (data != null && !data.isEmpty()) { 128 | send(data.getBytes(Charset.forName("UTF-8"))); 129 | } 130 | } 131 | 132 | // Register the specified callback to receive UART callbacks. 133 | public void registerCallback(Callback callback) { 134 | callbacks.put(callback, null); 135 | } 136 | 137 | // Unregister the specified callback. 138 | public void unregisterCallback(Callback callback) { 139 | callbacks.remove(callback); 140 | } 141 | 142 | // Disconnect to a device if currently connected. 143 | public void disconnect() { 144 | if (gatt != null) { 145 | gatt.disconnect(); 146 | } 147 | gatt = null; 148 | tx = null; 149 | rx = null; 150 | } 151 | 152 | // Stop any in progress UART device scan. 153 | public void stopScan() { 154 | if (adapter != null) { 155 | adapter.stopLeScan(this); 156 | } 157 | } 158 | 159 | // Start scanning for BLE UART devices. Registered callback's onDeviceFound method will be called 160 | // when devices are found during scanning. 161 | public void startScan() { 162 | if (adapter != null) { 163 | adapter.startLeScan(this); 164 | } 165 | } 166 | 167 | // Connect to the first available UART device. 168 | public void connectFirstAvailable() { 169 | // Disconnect to any connected device. 170 | disconnect(); 171 | // Stop any in progress device scan. 172 | stopScan(); 173 | // Start scan and connect to first available device. 174 | connectFirst = true; 175 | startScan(); 176 | } 177 | 178 | // Handlers for BluetoothGatt and LeScan events. 179 | @Override 180 | public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 181 | super.onConnectionStateChange(gatt, status, newState); 182 | if (newState == BluetoothGatt.STATE_CONNECTED) { 183 | if (status == BluetoothGatt.GATT_SUCCESS) { 184 | // Connected to device, start discovering services. 185 | if (!gatt.discoverServices()) { 186 | // Error starting service discovery. 187 | connectFailure(); 188 | } 189 | } 190 | else { 191 | // Error connecting to device. 192 | connectFailure(); 193 | } 194 | } 195 | else if (newState == BluetoothGatt.STATE_DISCONNECTED) { 196 | // Disconnected, notify callbacks of disconnection. 197 | rx = null; 198 | tx = null; 199 | notifyOnDisconnected(this); 200 | } 201 | } 202 | 203 | @Override 204 | public void onServicesDiscovered(BluetoothGatt gatt, int status) { 205 | super.onServicesDiscovered(gatt, status); 206 | // Notify connection failure if service discovery failed. 207 | if (status == BluetoothGatt.GATT_FAILURE) { 208 | connectFailure(); 209 | return; 210 | } 211 | 212 | // Save reference to each UART characteristic. 213 | tx = gatt.getService(UART_UUID).getCharacteristic(TX_UUID); 214 | rx = gatt.getService(UART_UUID).getCharacteristic(RX_UUID); 215 | 216 | // Save reference to each DIS characteristic. 217 | disManuf = gatt.getService(DIS_UUID).getCharacteristic(DIS_MANUF_UUID); 218 | disModel = gatt.getService(DIS_UUID).getCharacteristic(DIS_MODEL_UUID); 219 | disHWRev = gatt.getService(DIS_UUID).getCharacteristic(DIS_HWREV_UUID); 220 | disSWRev = gatt.getService(DIS_UUID).getCharacteristic(DIS_SWREV_UUID); 221 | 222 | // Add device information characteristics to the read queue 223 | // These need to be queued because we have to wait for the response to the first 224 | // read request before a second one can be processed (which makes you wonder why they 225 | // implemented this with async logic to begin with???) 226 | readQueue.offer(disManuf); 227 | readQueue.offer(disModel); 228 | readQueue.offer(disHWRev); 229 | readQueue.offer(disSWRev); 230 | 231 | // Request a dummy read to get the device information queue going 232 | gatt.readCharacteristic(disManuf); 233 | 234 | // Setup notifications on RX characteristic changes (i.e. data received). 235 | // First call setCharacteristicNotification to enable notification. 236 | if (!gatt.setCharacteristicNotification(rx, true)) { 237 | // Stop if the characteristic notification setup failed. 238 | connectFailure(); 239 | return; 240 | } 241 | // Next update the RX characteristic's client descriptor to enable notifications. 242 | BluetoothGattDescriptor desc = rx.getDescriptor(CLIENT_UUID); 243 | if (desc == null) { 244 | // Stop if the RX characteristic has no client descriptor. 245 | connectFailure(); 246 | return; 247 | } 248 | desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 249 | if (!gatt.writeDescriptor(desc)) { 250 | // Stop if the client descriptor could not be written. 251 | connectFailure(); 252 | return; 253 | } 254 | // Notify of connection completion. 255 | notifyOnConnected(this); 256 | } 257 | 258 | @Override 259 | public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { 260 | super.onCharacteristicChanged(gatt, characteristic); 261 | notifyOnReceive(this, characteristic); 262 | } 263 | 264 | @Override 265 | public void onCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 266 | super.onCharacteristicRead(gatt, characteristic, status); 267 | 268 | if (status == BluetoothGatt.GATT_SUCCESS) { 269 | //Log.w("DIS", characteristic.getStringValue(0)); 270 | // Check if there is anything left in the queue 271 | BluetoothGattCharacteristic nextRequest = readQueue.poll(); 272 | if(nextRequest != null){ 273 | // Send a read request for the next item in the queue 274 | gatt.readCharacteristic(nextRequest); 275 | } 276 | else { 277 | // We've reached the end of the queue 278 | disAvailable = true; 279 | notifyOnDeviceInfoAvailable(); 280 | } 281 | } 282 | else { 283 | //Log.w("DIS", "Failed reading characteristic " + characteristic.getUuid().toString()); 284 | } 285 | } 286 | 287 | @Override 288 | public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 289 | super.onCharacteristicWrite(gatt, characteristic, status); 290 | 291 | if (status == BluetoothGatt.GATT_SUCCESS) { 292 | // Log.d(TAG,"Characteristic write successful"); 293 | } 294 | writeInProgress = false; 295 | } 296 | 297 | @Override 298 | public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { 299 | // Stop if the device doesn't have the UART service. 300 | if (!parseUUIDs(scanRecord).contains(UART_UUID)) { 301 | return; 302 | } 303 | // Notify registered callbacks of found device. 304 | notifyOnDeviceFound(device); 305 | // Connect to first found device if required. 306 | if (connectFirst) { 307 | // Stop scanning for devices. 308 | stopScan(); 309 | // Prevent connections to future found devices. 310 | connectFirst = false; 311 | // Connect to device. 312 | gatt = device.connectGatt(context, true, this); 313 | } 314 | } 315 | 316 | // Private functions to simplify the notification of all callbacks of a certain event. 317 | private void notifyOnConnected(BluetoothLeUart uart) { 318 | for (Callback cb : callbacks.keySet()) { 319 | if (cb != null) { 320 | cb.onConnected(uart); 321 | } 322 | } 323 | } 324 | 325 | private void notifyOnConnectFailed(BluetoothLeUart uart) { 326 | for (Callback cb : callbacks.keySet()) { 327 | if (cb != null) { 328 | cb.onConnectFailed(uart); 329 | } 330 | } 331 | } 332 | 333 | private void notifyOnDisconnected(BluetoothLeUart uart) { 334 | for (Callback cb : callbacks.keySet()) { 335 | if (cb != null) { 336 | cb.onDisconnected(uart); 337 | } 338 | } 339 | } 340 | 341 | private void notifyOnReceive(BluetoothLeUart uart, BluetoothGattCharacteristic rx) { 342 | for (Callback cb : callbacks.keySet()) { 343 | if (cb != null ) { 344 | cb.onReceive(uart, rx); 345 | } 346 | } 347 | } 348 | 349 | private void notifyOnDeviceFound(BluetoothDevice device) { 350 | for (Callback cb : callbacks.keySet()) { 351 | if (cb != null) { 352 | cb.onDeviceFound(device); 353 | } 354 | } 355 | } 356 | 357 | private void notifyOnDeviceInfoAvailable() { 358 | for (Callback cb : callbacks.keySet()) { 359 | if (cb != null) { 360 | cb.onDeviceInfoAvailable(); 361 | } 362 | } 363 | } 364 | 365 | // Notify callbacks of connection failure, and reset connection state. 366 | private void connectFailure() { 367 | rx = null; 368 | tx = null; 369 | notifyOnConnectFailed(this); 370 | } 371 | 372 | // Filtering by custom UUID is broken in Android 4.3 and 4.4, see: 373 | // http://stackoverflow.com/questions/18019161/startlescan-with-128-bit-uuids-doesnt-work-on-native-android-ble-implementation?noredirect=1#comment27879874_18019161 374 | // This is a workaround function from the SO thread to manually parse advertisement data. 375 | private List parseUUIDs(final byte[] advertisedData) { 376 | List uuids = new ArrayList(); 377 | 378 | int offset = 0; 379 | while (offset < (advertisedData.length - 2)) { 380 | int len = advertisedData[offset++]; 381 | if (len == 0) 382 | break; 383 | 384 | int type = advertisedData[offset++]; 385 | switch (type) { 386 | case 0x02: // Partial list of 16-bit UUIDs 387 | case 0x03: // Complete list of 16-bit UUIDs 388 | while (len > 1) { 389 | int uuid16 = advertisedData[offset++]; 390 | uuid16 += (advertisedData[offset++] << 8); 391 | len -= 2; 392 | uuids.add(UUID.fromString(String.format("%08x-0000-1000-8000-00805f9b34fb", uuid16))); 393 | } 394 | break; 395 | case 0x06:// Partial list of 128-bit UUIDs 396 | case 0x07:// Complete list of 128-bit UUIDs 397 | // Loop through the advertised 128-bit UUID's. 398 | while (len >= 16) { 399 | try { 400 | // Wrap the advertised bits and order them. 401 | ByteBuffer buffer = ByteBuffer.wrap(advertisedData, offset++, 16).order(ByteOrder.LITTLE_ENDIAN); 402 | long mostSignificantBit = buffer.getLong(); 403 | long leastSignificantBit = buffer.getLong(); 404 | uuids.add(new UUID(leastSignificantBit, 405 | mostSignificantBit)); 406 | } catch (IndexOutOfBoundsException e) { 407 | // Defensive programming. 408 | //Log.e(LOG_TAG, e.toString()); 409 | continue; 410 | } finally { 411 | // Move the offset to read the next uuid. 412 | offset += 15; 413 | len -= 16; 414 | } 415 | } 416 | break; 417 | default: 418 | offset += (len - 1); 419 | break; 420 | } 421 | } 422 | return uuids; 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /app/src/main/java/com/adafruit/bleuart/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.adafruit.bleuart; 2 | 3 | import android.app.Activity; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.bluetooth.BluetoothGattCharacteristic; 6 | import android.os.Bundle; 7 | import android.text.method.ScrollingMovementMethod; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.Button; 12 | import android.widget.CheckBox; 13 | import android.widget.EditText; 14 | import android.widget.TextView; 15 | 16 | import java.lang.Thread; 17 | 18 | public class MainActivity extends Activity implements BluetoothLeUart.Callback { 19 | 20 | // UI elements 21 | private TextView messages; 22 | private EditText input; 23 | private Button send; 24 | private CheckBox newline; 25 | 26 | // Bluetooth LE UART instance. This is defined in BluetoothLeUart.java. 27 | private BluetoothLeUart uart; 28 | 29 | // Write some text to the messages text view. 30 | // Care is taken to do this on the main UI thread so writeLine can be called from any thread 31 | // (like the BTLE callback). 32 | private void writeLine(final CharSequence text) { 33 | runOnUiThread(new Runnable() { 34 | @Override 35 | public void run() { 36 | messages.append(text); 37 | messages.append("\n"); 38 | } 39 | }); 40 | } 41 | 42 | // Handler for mouse click on the send button. 43 | public void sendClick(View view) { 44 | StringBuilder stringBuilder = new StringBuilder(); 45 | String message = input.getText().toString(); 46 | 47 | // We can only send 20 bytes per packet, so break longer messages 48 | // up into 20 byte payloads 49 | int len = message.length(); 50 | int pos = 0; 51 | while(len != 0) { 52 | stringBuilder.setLength(0); 53 | if (len>=20) { 54 | stringBuilder.append(message.toCharArray(), pos, 20 ); 55 | len-=20; 56 | pos+=20; 57 | } 58 | else { 59 | stringBuilder.append(message.toCharArray(), pos, len); 60 | len = 0; 61 | } 62 | uart.send(stringBuilder.toString()); 63 | } 64 | // Terminate with a newline character if requests 65 | newline = (CheckBox) findViewById(R.id.newline); 66 | if (newline.isChecked()) { 67 | stringBuilder.setLength(0); 68 | stringBuilder.append("\n"); 69 | uart.send(stringBuilder.toString()); 70 | } 71 | } 72 | 73 | @Override 74 | protected void onCreate(Bundle savedInstanceState) { 75 | super.onCreate(savedInstanceState); 76 | setContentView(R.layout.activity_main); 77 | 78 | // Grab references to UI elements. 79 | messages = (TextView) findViewById(R.id.messages); 80 | input = (EditText) findViewById(R.id.input); 81 | 82 | // Initialize UART. 83 | uart = new BluetoothLeUart(getApplicationContext()); 84 | 85 | // Disable the send button until we're connected. 86 | send = (Button)findViewById(R.id.send); 87 | send.setClickable(false); 88 | send.setEnabled(false); 89 | 90 | // Enable auto-scroll in the TextView 91 | messages.setMovementMethod(new ScrollingMovementMethod()); 92 | } 93 | 94 | // OnCreate, called once to initialize the activity. 95 | @Override 96 | public boolean onCreateOptionsMenu(Menu menu) { 97 | // Inflate the menu; this adds items to the action bar if it is present. 98 | getMenuInflater().inflate(R.menu.main, menu); 99 | return true; 100 | } 101 | 102 | // OnResume, called right before UI is displayed. Connect to the bluetooth device. 103 | @Override 104 | protected void onResume() { 105 | super.onResume(); 106 | writeLine("Scanning for devices ..."); 107 | uart.registerCallback(this); 108 | uart.connectFirstAvailable(); 109 | } 110 | 111 | // OnStop, called right before the activity loses foreground focus. Close the BTLE connection. 112 | @Override 113 | protected void onStop() { 114 | super.onStop(); 115 | uart.unregisterCallback(this); 116 | uart.disconnect(); 117 | } 118 | 119 | @Override 120 | public boolean onOptionsItemSelected(MenuItem item) { 121 | // Handle action bar item clicks here. The action bar will 122 | // automatically handle clicks on the Home/Up button, so long 123 | // as you specify a parent activity in AndroidManifest.xml. 124 | int id = item.getItemId(); 125 | if (id == R.id.action_settings) { 126 | return true; 127 | } 128 | return super.onOptionsItemSelected(item); 129 | } 130 | 131 | // UART Callback event handlers. 132 | @Override 133 | public void onConnected(BluetoothLeUart uart) { 134 | // Called when UART device is connected and ready to send/receive data. 135 | writeLine("Connected!"); 136 | // Enable the send button 137 | runOnUiThread(new Runnable() { 138 | @Override 139 | public void run() { 140 | send = (Button)findViewById(R.id.send); 141 | send.setClickable(true); 142 | send.setEnabled(true); 143 | } 144 | }); 145 | } 146 | 147 | @Override 148 | public void onConnectFailed(BluetoothLeUart uart) { 149 | // Called when some error occured which prevented UART connection from completing. 150 | writeLine("Error connecting to device!"); 151 | runOnUiThread(new Runnable() { 152 | @Override 153 | public void run() { 154 | send = (Button)findViewById(R.id.send); 155 | send.setClickable(false); 156 | send.setEnabled(false); 157 | } 158 | }); 159 | } 160 | 161 | @Override 162 | public void onDisconnected(BluetoothLeUart uart) { 163 | // Called when the UART device disconnected. 164 | writeLine("Disconnected!"); 165 | // Disable the send button. 166 | runOnUiThread(new Runnable() { 167 | @Override 168 | public void run() { 169 | send = (Button)findViewById(R.id.send); 170 | send.setClickable(false); 171 | send.setEnabled(false); 172 | } 173 | }); 174 | } 175 | 176 | @Override 177 | public void onReceive(BluetoothLeUart uart, BluetoothGattCharacteristic rx) { 178 | // Called when data is received by the UART. 179 | writeLine("Received: " + rx.getStringValue(0)); 180 | } 181 | 182 | @Override 183 | public void onDeviceFound(BluetoothDevice device) { 184 | // Called when a UART device is discovered (after calling startScan). 185 | writeLine("Found device : " + device.getAddress()); 186 | writeLine("Waiting for a connection ..."); 187 | } 188 | 189 | @Override 190 | public void onDeviceInfoAvailable() { 191 | writeLine(uart.getDeviceInfo()); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_Android_BLE_UART/68c686575a307eaaa88ce76886f304779bd29690/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_Android_BLE_UART/68c686575a307eaaa88ce76886f304779bd29690/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_Android_BLE_UART/68c686575a307eaaa88ce76886f304779bd29690/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_Android_BLE_UART/68c686575a307eaaa88ce76886f304779bd29690/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 |