├── BLEAndroidExampleProject ├── .gitignore ├── .idea │ ├── .name │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── jarRepositories.xml │ ├── misc.xml │ └── runConfigurations.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── blearduinoexampleproject │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── blearduinoexampleproject │ │ │ │ ├── AccelerometerData.java │ │ │ │ ├── BatteryData.java │ │ │ │ ├── BleRecyclerAdapter.java │ │ │ │ ├── BluetoothLeService.java │ │ │ │ ├── GattAttributes.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── RecyclerBleDeviceActivity.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ ├── baseline_autorenew_white_48dp.xml │ │ │ ├── baseline_bluetooth_connected_white_48dp.xml │ │ │ ├── baseline_bluetooth_disabled_white_48dp.xml │ │ │ ├── baseline_bluetooth_searching_white_48dp.xml │ │ │ ├── baseline_bluetooth_white_48dp.xml │ │ │ ├── baseline_close_white_48dp.xml │ │ │ ├── baseline_directions_run_white_48dp.xml │ │ │ ├── baseline_donut_large_black_24dp.xml │ │ │ ├── baseline_donut_large_white_24dp.xml │ │ │ ├── baseline_donut_small_white_48dp.xml │ │ │ ├── baseline_more_vert_white_48dp.xml │ │ │ ├── baseline_opacity_white_48dp.xml │ │ │ ├── baseline_settings_bluetooth_white_48dp.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── ic_launcher_foreground.xml │ │ │ ├── progress_circle.xml │ │ │ ├── progress_circle_background.xml │ │ │ ├── progress_circle_blue.xml │ │ │ ├── progress_circle_error.xml │ │ │ ├── progress_circle_green.xml │ │ │ ├── progress_circle_orange.xml │ │ │ ├── progress_circle_primary.xml │ │ │ ├── progress_circle_purple.xml │ │ │ ├── progress_circle_warn.xml │ │ │ └── progress_circle_yellow.xml │ │ │ ├── layout │ │ │ ├── actionbar.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_recycler_ble_device.xml │ │ │ └── listitem_device.xml │ │ │ ├── menu │ │ │ └── gatt_services.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values-v21 │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── blearduinoexampleproject │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── README.md └── androidAdafruitBleExample ├── .DS_Store ├── .idea ├── androidAdafruitBleExample.iml ├── codeStyles │ └── Project.xml ├── modules.xml └── workspace.xml └── ble_example └── ble_example.ino /BLEAndroidExampleProject/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/.idea/.name: -------------------------------------------------------------------------------- 1 | BLE Android Example Project -------------------------------------------------------------------------------- /BLEAndroidExampleProject/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 21 | 22 | 23 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | xmlns:android 32 | 33 | ^$ 34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | xmlns:.* 43 | 44 | ^$ 45 | 46 | 47 | BY_NAME 48 | 49 |
50 |
51 | 52 | 53 | 54 | .*:id 55 | 56 | http://schemas.android.com/apk/res/android 57 | 58 | 59 | 60 |
61 |
62 | 63 | 64 | 65 | .*:name 66 | 67 | http://schemas.android.com/apk/res/android 68 | 69 | 70 | 71 |
72 |
73 | 74 | 75 | 76 | name 77 | 78 | ^$ 79 | 80 | 81 | 82 |
83 |
84 | 85 | 86 | 87 | style 88 | 89 | ^$ 90 | 91 | 92 | 93 |
94 |
95 | 96 | 97 | 98 | .* 99 | 100 | ^$ 101 | 102 | 103 | BY_NAME 104 | 105 |
106 |
107 | 108 | 109 | 110 | .* 111 | 112 | http://schemas.android.com/apk/res/android 113 | 114 | 115 | ANDROID_ATTRIBUTE_ORDER 116 | 117 |
118 |
119 | 120 | 121 | 122 | .* 123 | 124 | .* 125 | 126 | 127 | BY_NAME 128 | 129 |
130 |
131 |
132 |
133 |
134 |
-------------------------------------------------------------------------------- /BLEAndroidExampleProject/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 30 5 | buildToolsVersion "30.0.0" 6 | 7 | defaultConfig { 8 | applicationId "com.example.blearduinoexampleproject" 9 | minSdkVersion 16 10 | targetSdkVersion 30 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: "libs", include: ["*.jar"]) 27 | implementation 'androidx.appcompat:appcompat:1.2.0' 28 | implementation 'androidx.constraintlayout:constraintlayout:2.0.2' 29 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 30 | testImplementation 'junit:junit:4.12' 31 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 32 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 33 | 34 | } -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/androidTest/java/com/example/blearduinoexampleproject/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.example.blearduinoexampleproject", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/java/com/example/blearduinoexampleproject/AccelerometerData.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.ArrayList; 5 | import java.util.Date; 6 | 7 | /* 8 | * This is a class for the accelerometer data captured from your BLE connection. 9 | * 10 | * */ 11 | public class AccelerometerData { 12 | 13 | private ArrayList xAcceleration; 14 | private ArrayList yAcceleration; 15 | private ArrayList zAcceleration; 16 | private ArrayList xGyroscope; 17 | private ArrayList yGyroscope; 18 | private ArrayList zGyroscope; 19 | private ArrayList readingTimes; 20 | private ArrayList readingTimesFormatted; 21 | private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); 22 | 23 | public AccelerometerData(ArrayList xAcc, ArrayList yAcc, ArrayList zAcc, ArrayList xGyro, ArrayList yGyro, ArrayList zGyro, ArrayList times) { 24 | this.xAcceleration = xAcc; 25 | this.yAcceleration = yAcc; 26 | this.zAcceleration = zAcc; 27 | this.xGyroscope = xGyro; 28 | this.yGyroscope = yGyro; 29 | this.zGyroscope = zGyro; 30 | this.readingTimes = times; 31 | formatReadTimes(times); 32 | } 33 | 34 | public ArrayList getXAcceleration() { 35 | return this.xAcceleration; 36 | } 37 | public void setXAcceleration(ArrayList xAcc) { 38 | this.xAcceleration = xAcc; 39 | } 40 | 41 | public ArrayList getYAcceleration() { 42 | return this.yAcceleration; 43 | } 44 | public void setYAcceleration(ArrayList yAcc) { 45 | this.yAcceleration = yAcc; 46 | } 47 | 48 | public ArrayList getZAcceleration() { 49 | return this.zAcceleration; 50 | } 51 | public void setZAcceleration(ArrayList zAcc) { 52 | this.zAcceleration = zAcc; 53 | } 54 | 55 | public int getXAccelerationAvg() { 56 | return calculateAverage(this.xAcceleration); 57 | } 58 | 59 | public int getYAccelerationAvg() { 60 | return calculateAverage(this.yAcceleration); 61 | } 62 | 63 | public int getZAccelerationAvg() { 64 | return calculateAverage(this.zAcceleration); 65 | } 66 | 67 | public ArrayList getXGyroscope() { 68 | return this.xGyroscope; 69 | } 70 | public void setXGyroscope(ArrayList xGyro) { 71 | this.xGyroscope = xGyro; 72 | } 73 | 74 | public ArrayList getYGyroscope() { 75 | return this.yGyroscope; 76 | } 77 | public void setYGyroscope(ArrayList yGyro) { 78 | this.yGyroscope = yGyro; 79 | } 80 | 81 | public ArrayList getZGyroscope() { 82 | return this.zGyroscope; 83 | } 84 | public void setZGyroscope(ArrayList zGyro) { 85 | this.zGyroscope = zGyro; 86 | } 87 | 88 | public ArrayList getReadingTimes() { 89 | return this.readingTimes; 90 | } 91 | public void setReadingTimes(ArrayList time) { 92 | this.readingTimes = time; 93 | } 94 | 95 | private void formatReadTimes(ArrayList times) { 96 | readingTimesFormatted = new ArrayList(); 97 | for (int i=0; i < 5; i++) { 98 | readingTimesFormatted.add(dateFormatter.format(readingTimes.get(i))); 99 | } 100 | } 101 | 102 | private int calculateAverage(ArrayList data) { 103 | int sum = 0; 104 | if(!data.isEmpty()) { 105 | for (Integer num : data) { 106 | sum += num; 107 | } 108 | return sum / data.size(); 109 | } 110 | System.out.println(sum); 111 | return sum; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/java/com/example/blearduinoexampleproject/BatteryData.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | /* 4 | * This is a class for the battery data captured from your BLE connection. 5 | * 6 | * */ 7 | public class BatteryData { 8 | private int batteryLevel; 9 | 10 | public BatteryData(int batteryLevel) { 11 | this.batteryLevel = batteryLevel; 12 | } 13 | 14 | public int getBatteryLevel() { 15 | return batteryLevel; 16 | } 17 | 18 | public void setBatteryLevel(int batteryLevel) { 19 | this.batteryLevel = batteryLevel; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/java/com/example/blearduinoexampleproject/BleRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.content.Context; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import java.util.ArrayList; 13 | 14 | /* 15 | * This is an adapter that populates the list of BLE devices found in the sweep. This list is then 16 | * used to populate the UI the user sees so they can select the device they are working with. 17 | * 18 | * */ 19 | public class BleRecyclerAdapter extends RecyclerView.Adapter { 20 | 21 | private ArrayList bleDevices; 22 | private LayoutInflater inflater; 23 | private ItemClickListener clickListener; 24 | 25 | // data is passed into the constructor 26 | BleRecyclerAdapter(Context context) { 27 | this.bleDevices = new ArrayList<>(); 28 | this.inflater = LayoutInflater.from(context); 29 | } 30 | 31 | // inflates a row containing a device's info as needed 32 | @Override 33 | public BleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 34 | View view = inflater.inflate(R.layout.listitem_device, parent, false); 35 | return new BleViewHolder(view); 36 | } 37 | 38 | // binds the data to the TextViews in each row 39 | @Override 40 | public void onBindViewHolder(BleViewHolder holder, int position) { 41 | String name = ""; 42 | if (bleDevices.get(position).getName() == null) { 43 | name = "Unknown Device"; 44 | } else { 45 | name = bleDevices.get(position).getName(); 46 | } 47 | String address = bleDevices.get(position).getAddress(); 48 | 49 | holder.deviceAddressView.setText(address); 50 | holder.deviceNameView.setText(name); 51 | } 52 | 53 | // total number of rows 54 | @Override 55 | public int getItemCount() { 56 | return bleDevices.size(); 57 | } 58 | 59 | public class BleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 60 | public TextView deviceAddressView; 61 | public TextView deviceNameView; 62 | 63 | public BleViewHolder(View deviceView) { 64 | super(deviceView); 65 | deviceAddressView = deviceView.findViewById(R.id.device_address); 66 | deviceNameView = deviceView.findViewById(R.id.device_name); 67 | deviceView.setOnClickListener(this); 68 | } 69 | 70 | @Override 71 | public void onClick(View view) { 72 | if (clickListener != null) clickListener.onItemClick(view, getAdapterPosition()); 73 | } 74 | } 75 | 76 | // convenience method for getting data at click position 77 | public BluetoothDevice getDevice(int id) { 78 | return bleDevices.get(id); 79 | } 80 | 81 | // parent activity will implement this method to respond to click events 82 | public interface ItemClickListener { 83 | void onItemClick(View view, int position); 84 | } 85 | 86 | // allows clicks events to be caught 87 | public void setClickListener(ItemClickListener itemClickListener) { 88 | this.clickListener = itemClickListener; 89 | } 90 | 91 | // add a new device to the list 92 | public void addDevice(BluetoothDevice device) { 93 | if(!bleDevices.contains(device)) { 94 | bleDevices.add(device); 95 | } 96 | } 97 | // clear the list 98 | public void clear() { 99 | bleDevices.clear(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/java/com/example/blearduinoexampleproject/BluetoothLeService.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | import android.app.Service; 4 | import android.bluetooth.BluetoothAdapter; 5 | import android.bluetooth.BluetoothDevice; 6 | import android.bluetooth.BluetoothGatt; 7 | import android.bluetooth.BluetoothGattCallback; 8 | import android.bluetooth.BluetoothGattCharacteristic; 9 | import android.bluetooth.BluetoothGattDescriptor; 10 | import android.bluetooth.BluetoothGattService; 11 | import android.bluetooth.BluetoothManager; 12 | import android.bluetooth.BluetoothProfile; 13 | import android.content.Context; 14 | import android.content.Intent; 15 | import android.content.SharedPreferences; 16 | import android.os.Binder; 17 | import android.os.Build; 18 | import android.os.IBinder; 19 | import android.util.Log; 20 | 21 | import androidx.annotation.RequiresApi; 22 | 23 | import java.util.ArrayList; 24 | import java.util.Date; 25 | import java.util.List; 26 | import java.util.UUID; 27 | 28 | import static com.example.blearduinoexampleproject.GattAttributes.ACCELEROMETER_TIME_READ; 29 | import static com.example.blearduinoexampleproject.GattAttributes.BATTERY_LEVEL_READ; 30 | import static com.example.blearduinoexampleproject.GattAttributes.X_ACCELERATION_READ; 31 | import static com.example.blearduinoexampleproject.GattAttributes.X_GYROSCOPE_READ; 32 | import static com.example.blearduinoexampleproject.GattAttributes.Y_ACCELERATION_READ; 33 | import static com.example.blearduinoexampleproject.GattAttributes.Y_GYROSCOPE_READ; 34 | import static com.example.blearduinoexampleproject.GattAttributes.Z_ACCELERATION_READ; 35 | import static com.example.blearduinoexampleproject.GattAttributes.Z_GYROSCOPE_READ; 36 | 37 | /* 38 | * This is a service to handle the BLE interactions. 39 | * 40 | * */ 41 | @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 42 | public class BluetoothLeService extends Service { 43 | private final static String TAG = BluetoothLeService.class.getSimpleName(); 44 | 45 | private BluetoothManager bleManager; 46 | private BluetoothAdapter bleAdapter; 47 | private String bleDeviceAddress; 48 | private BluetoothGatt bleGatt; 49 | private BluetoothGattCharacteristic notifyCharacteristics; 50 | private int connectionState = STATE_DISCONNECTED; 51 | List chars = new ArrayList<>(); 52 | private AccelerometerData accelerometerData; 53 | private ArrayList xAcc = new ArrayList(); 54 | private ArrayList yAcc = new ArrayList(); 55 | private ArrayList zAcc = new ArrayList(); 56 | private ArrayList xGyro = new ArrayList(); 57 | private ArrayList yGyro = new ArrayList(); 58 | private ArrayList zGyro = new ArrayList(); 59 | private ArrayList accTime = new ArrayList(); 60 | 61 | private BatteryData batteryData; 62 | private int batteryLevel; 63 | private boolean sweepComplete = false; 64 | 65 | private static final int STATE_DISCONNECTED = 0; 66 | private static final int STATE_CONNECTING = 1; 67 | private static final int STATE_CONNECTED = 2; 68 | 69 | public final static String ACTION_GATT_CONNECTED = "ACTION_GATT_CONNECTED"; 70 | public final static String ACTION_GATT_DISCONNECTED = "ACTION_GATT_DISCONNECTED"; 71 | public final static String ACTION_GATT_SERVICES_DISCOVERED = "ACTION_GATT_SERVICES_DISCOVERED"; 72 | public final static String ACTION_DATA_READ_COMPLETED = "ACTION_DATA_READ_COMPLETED"; 73 | public final static String ACTION_BATTERY_LEVEL = "ACTION_BATTERY_LEVEL"; 74 | public final static String ACTION_DATA_AVAILABLE = "ACTION_DATA_AVAILABLE"; 75 | 76 | public final static UUID UUID_X_ACCELERATION = UUID.fromString(GattAttributes.X_ACCELERATION_MEASUREMENT); 77 | public final static UUID UUID_Y_ACCELERATION = UUID.fromString(GattAttributes.Y_ACCELERATION_MEASUREMENT); 78 | public final static UUID UUID_Z_ACCELERATION = UUID.fromString(GattAttributes.Z_ACCELERATION_MEASUREMENT); 79 | public final static UUID UUID_X_GYROSCOPE = UUID.fromString(GattAttributes.X_GYROSCOPE_MEASUREMENT); 80 | public final static UUID UUID_Y_GYROSCOPE = UUID.fromString(GattAttributes.Y_GYROSCOPE_MEASUREMENT); 81 | public final static UUID UUID_Z_GYROSCOPE = UUID.fromString(GattAttributes.Z_GYROSCOPE_MEASUREMENT); 82 | public final static UUID UUID_ACCELERATION_TIME = UUID.fromString(GattAttributes.ACCELEROMETER_TIME_MEASUREMENT); 83 | 84 | public final static UUID UUID_BATTERY_LEVEL = UUID.fromString(GattAttributes.BATTERY_LEVEL); 85 | public final static UUID UUID_BATTERY_STATUS = UUID.fromString(GattAttributes.BATTERY_STATUS); 86 | 87 | // Implements callback methods for GATT events that the app cares about. For example, 88 | // connection change and services discovered. 89 | private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() { 90 | @Override 91 | public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 92 | if (newState == BluetoothProfile.STATE_CONNECTED) { 93 | connectionState = STATE_CONNECTED; 94 | 95 | broadcastUpdate(ACTION_GATT_CONNECTED); 96 | Log.i(TAG, "Attempting to start service discovery."); 97 | gatt.discoverServices(); 98 | Log.i(TAG, "Connected to GATT server."); 99 | 100 | } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { 101 | connectionState = STATE_DISCONNECTED; 102 | Log.i(TAG, "Disconnected from GATT server."); 103 | broadcastUpdate(ACTION_GATT_DISCONNECTED); 104 | 105 | } else { 106 | Log.i(TAG, "Other State"); 107 | } 108 | } 109 | 110 | @Override 111 | public void onServicesDiscovered(BluetoothGatt gatt, int status) { 112 | if (status == BluetoothGatt.GATT_SUCCESS) { 113 | broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); 114 | 115 | clearDataArrays(); 116 | 117 | List services = gatt.getServices(); 118 | 119 | // Loops through available GATT Services. 120 | for (BluetoothGattService gattService : services) { 121 | List gattCharacteristicsList = gattService.getCharacteristics(); 122 | 123 | // Loops through available Characteristics. 124 | for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristicsList) { 125 | if (isDataCharacteristic(gattCharacteristic) != 0) { 126 | chars.add(gattCharacteristic); 127 | } 128 | } 129 | } 130 | 131 | requestCharacteristics(gatt); 132 | 133 | } else { 134 | Log.w(TAG, "onServicesDiscovered received: " + status); 135 | } 136 | } 137 | 138 | public void requestCharacteristics(BluetoothGatt gatt) { 139 | gatt.readCharacteristic(chars.get(chars.size()-1)); 140 | } 141 | 142 | @Override 143 | public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 144 | if (status == BluetoothGatt.GATT_SUCCESS) { 145 | broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); 146 | 147 | switch (isDataCharacteristic(characteristic)) { 148 | case X_ACCELERATION_READ: 149 | case Y_ACCELERATION_READ: 150 | case Z_ACCELERATION_READ: 151 | case X_GYROSCOPE_READ: 152 | case Y_GYROSCOPE_READ: 153 | case Z_GYROSCOPE_READ: 154 | case ACCELEROMETER_TIME_READ: 155 | if (sweepComplete) { 156 | chars.remove(chars.get(chars.size() - 1)); 157 | sweepComplete = false; 158 | } 159 | 160 | break; 161 | 162 | default: 163 | chars.remove(chars.get(chars.size() - 1)); 164 | break; 165 | } 166 | 167 | if (chars.size() > 0) { 168 | requestCharacteristics(gatt); 169 | 170 | } else { 171 | Log.i(TAG, "Gatt server data read completed."); 172 | saveAgmData(); 173 | broadcastUpdate(ACTION_DATA_READ_COMPLETED); 174 | disconnect(); 175 | } 176 | } 177 | } 178 | }; 179 | 180 | public int isDataCharacteristic(BluetoothGattCharacteristic characteristic) { 181 | if (UUID_BATTERY_LEVEL.equals(characteristic.getUuid())) { 182 | return BATTERY_LEVEL_READ; 183 | 184 | } else if (UUID_X_ACCELERATION.equals(characteristic.getUuid())) { 185 | return X_ACCELERATION_READ; 186 | 187 | } else if (UUID_Y_ACCELERATION.equals(characteristic.getUuid())) { 188 | return Y_ACCELERATION_READ; 189 | 190 | } else if (UUID_Z_ACCELERATION.equals(characteristic.getUuid())) { 191 | return Z_ACCELERATION_READ; 192 | 193 | } else if (UUID_X_GYROSCOPE.equals(characteristic.getUuid())) { 194 | return X_GYROSCOPE_READ; 195 | 196 | } else if (UUID_Y_GYROSCOPE.equals(characteristic.getUuid())) { 197 | return Y_GYROSCOPE_READ; 198 | 199 | } else if (UUID_Z_GYROSCOPE.equals(characteristic.getUuid())) { 200 | return Z_GYROSCOPE_READ; 201 | 202 | } else if (UUID_ACCELERATION_TIME.equals(characteristic.getUuid())) { 203 | return ACCELEROMETER_TIME_READ; 204 | 205 | } else { 206 | return 0; 207 | } 208 | } 209 | 210 | private void broadcastUpdate(final String action) { 211 | final Intent intent = new Intent(action); 212 | sendBroadcast(intent); 213 | } 214 | 215 | private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { 216 | 217 | final Intent intent = new Intent(action); 218 | int charWhat = isDataCharacteristic(characteristic); 219 | int count; 220 | 221 | switch (charWhat) { 222 | case BATTERY_LEVEL_READ: 223 | batteryLevel = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8,0); 224 | Log.d(TAG, String.format("Received battery level: %d", batteryLevel)); 225 | intent.putExtra(ACTION_BATTERY_LEVEL, String.valueOf(batteryLevel)); 226 | 227 | break; 228 | case X_ACCELERATION_READ: 229 | count = Integer.parseInt(characteristic.getStringValue(0).split(",")[1]); 230 | xAcc.set(count,Integer.parseInt(characteristic.getStringValue(0).split(",")[0])); 231 | 232 | if (!xAcc.contains(null)) { 233 | sweepComplete = true; 234 | } 235 | Log.d(TAG, String.format("Received x acceleration level: %d", xAcc.get(count))); 236 | 237 | break; 238 | case Y_ACCELERATION_READ: 239 | count = Integer.parseInt(characteristic.getStringValue(0).split(",")[1]); 240 | yAcc.set(count,Integer.parseInt(characteristic.getStringValue(0).split(",")[0])); 241 | 242 | if (!yAcc.contains(null)) { 243 | sweepComplete = true; 244 | } 245 | Log.d(TAG, String.format("Received y acceleration level: %d", yAcc.get(count))); 246 | 247 | break; 248 | case Z_ACCELERATION_READ: 249 | count = Integer.parseInt(characteristic.getStringValue(0).split(",")[1]); 250 | zAcc.set(count,Integer.parseInt(characteristic.getStringValue(0).split(",")[0])); 251 | 252 | if (!zAcc.contains(null)) { 253 | sweepComplete = true; 254 | } 255 | Log.d(TAG, String.format("Received z acceleration level: %d", zAcc.get(count))); 256 | 257 | break; 258 | case X_GYROSCOPE_READ: 259 | count = Integer.parseInt(characteristic.getStringValue(0).split(",")[1]); 260 | xGyro.set(count,Integer.parseInt(characteristic.getStringValue(0).split(",")[0])); 261 | 262 | if (!xGyro.contains(null)) { 263 | sweepComplete = true; 264 | } 265 | Log.d(TAG, String.format("Received x gyroscope level: %d", xGyro.get(count))); 266 | 267 | break; 268 | case Y_GYROSCOPE_READ: 269 | count = Integer.parseInt(characteristic.getStringValue(0).split(",")[1]); 270 | yGyro.set(count,Integer.parseInt(characteristic.getStringValue(0).split(",")[0])); 271 | 272 | if (!yGyro.contains(null)) { 273 | sweepComplete = true; 274 | } 275 | Log.d(TAG, String.format("Received y gyroscope level: %d", yGyro.get(count))); 276 | 277 | break; 278 | case Z_GYROSCOPE_READ: 279 | count = Integer.parseInt(characteristic.getStringValue(0).split(",")[1]); 280 | zGyro.set(count,Integer.parseInt(characteristic.getStringValue(0).split(",")[0])); 281 | 282 | if (!zGyro.contains(null)) { 283 | sweepComplete = true; 284 | } 285 | Log.d(TAG, String.format("Received z gyroscope level: %d", zGyro.get(count))); 286 | 287 | break; 288 | case ACCELEROMETER_TIME_READ: 289 | count = Integer.parseInt(characteristic.getStringValue(0).split(",")[1]); 290 | // accTime.set(count,Integer.parseInt(characteristic.getStringValue(0).split(",")[0])); 291 | 292 | accTime.set(count,new Date()); 293 | 294 | if (!accTime.contains(null)) { 295 | sweepComplete = true; 296 | } 297 | 298 | 299 | break; 300 | 301 | default: 302 | // For all other profiles, writes the data formatted in HEX. 303 | final byte[] data = characteristic.getValue(); 304 | 305 | if (data != null && data.length > 0) { 306 | final StringBuilder stringBuilder = new StringBuilder(data.length); 307 | 308 | for (byte byteChar : data) 309 | stringBuilder.append(String.format("%02X ", byteChar)); 310 | } 311 | break; 312 | } 313 | sendBroadcast(intent); 314 | } 315 | 316 | public class LocalBinder extends Binder { 317 | public BluetoothLeService getService() { 318 | return BluetoothLeService.this; 319 | } 320 | } 321 | 322 | private void saveAgmData() { 323 | accelerometerData = new AccelerometerData(xAcc, yAcc, zAcc, xGyro, yGyro, zGyro, accTime); 324 | 325 | SharedPreferences sharedPref = getSharedPreferences(getString(R.string.agm_key),Context.MODE_PRIVATE); 326 | SharedPreferences.Editor prefBleDeviceEditor = sharedPref.edit(); 327 | 328 | prefBleDeviceEditor.putString("x_acc_avg", String.valueOf(accelerometerData.getXAccelerationAvg())); 329 | prefBleDeviceEditor.putString("y_acc_avg", String.valueOf(accelerometerData.getYAccelerationAvg())); 330 | prefBleDeviceEditor.putString("z_acc_avg", String.valueOf(accelerometerData.getZAccelerationAvg())); 331 | 332 | prefBleDeviceEditor.apply(); 333 | 334 | } 335 | 336 | private void clearDataArrays() { 337 | xAcc = new ArrayList(); 338 | yAcc = new ArrayList(); 339 | zAcc = new ArrayList(); 340 | xGyro = new ArrayList(); 341 | yGyro = new ArrayList(); 342 | zGyro = new ArrayList(); 343 | accTime = new ArrayList(); 344 | 345 | for (int i=0; i < 5; i++) { 346 | xAcc.add(i,null); 347 | yAcc.add(i,null); 348 | zAcc.add(i,null); 349 | xGyro.add(i,null); 350 | yGyro.add(i,null); 351 | zGyro.add(i,null); 352 | accTime.add(i,null); 353 | } 354 | } 355 | 356 | @Override 357 | public IBinder onBind(Intent intent) { 358 | return binder; 359 | } 360 | 361 | @Override 362 | public boolean onUnbind(Intent intent) { 363 | // After using a given device, you should make sure that BluetoothGatt.close() is called 364 | // such that resources are cleaned up properly. In this particular example, close() is 365 | // invoked when the UI is disconnected from the Service. 366 | close(); 367 | return super.onUnbind(intent); 368 | } 369 | 370 | private final IBinder binder = new LocalBinder(); 371 | /** 372 | * Initializes a reference to the local Bluetooth adapter. 373 | * 374 | * @return Return true if the initialization is successful. 375 | */ 376 | public boolean initialize() { 377 | // For API level 18 and above, get a reference to BluetoothAdapter through 378 | // BluetoothManager. 379 | if (bleManager == null) { 380 | bleManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 381 | 382 | if (bleManager == null) { 383 | Log.e(TAG, "Unable to initialize BluetoothManager."); 384 | return false; 385 | } 386 | } 387 | 388 | bleAdapter = bleManager.getAdapter(); 389 | 390 | if (bleAdapter == null) { 391 | Log.e(TAG, "Unable to obtain a BluetoothAdapter."); 392 | return false; 393 | } 394 | 395 | return true; 396 | } 397 | /** 398 | * Connects to the GATT server hosted on the Bluetooth LE device. 399 | * 400 | * @param address The device address of the destination device. 401 | * 402 | * @return Return true if the connection is initiated successfully. The connection result 403 | * is reported asynchronously through the 404 | * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} 405 | * callback. 406 | */ 407 | public boolean connect(final String address) { 408 | if (bleAdapter == null || address == null) { 409 | Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); 410 | return false; 411 | } 412 | 413 | // Previously connected device. Try to reconnect. 414 | if (bleDeviceAddress != null && address.equals(bleDeviceAddress) 415 | && bleGatt != null) { 416 | Log.d(TAG, "Trying to use an existing bleGatt for connection."); 417 | 418 | if (bleGatt.connect()) { 419 | connectionState = STATE_CONNECTING; 420 | Log.d(TAG, "Connecting..."); 421 | return true; 422 | 423 | } else { 424 | Log.d(TAG, "Can't connect..."); 425 | return false; 426 | } 427 | } 428 | 429 | final BluetoothDevice device = bleAdapter.getRemoteDevice(address); 430 | 431 | if (device == null) { 432 | Log.w(TAG, "Device not found. Unable to connect."); 433 | return false; 434 | } 435 | 436 | // We want to directly connect to the device, so we are setting the autoConnect 437 | // parameter to false. 438 | bleGatt = device.connectGatt(this, false, gattCallback); 439 | Log.d(TAG, "Trying to create a new connection."); 440 | bleDeviceAddress = address; 441 | connectionState = STATE_CONNECTING; 442 | return true; 443 | } 444 | /** 445 | * Disconnects an existing connection or cancel a pending connection. The disconnection result 446 | * is reported asynchronously through the 447 | * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} 448 | * callback. 449 | */ 450 | public void disconnect() { 451 | if (bleAdapter == null || bleGatt == null) { 452 | Log.w(TAG, "BluetoothAdapter not initialized"); 453 | return; 454 | } 455 | bleGatt.disconnect(); 456 | } 457 | /** 458 | * After using a given BLE device, the app must call this method to ensure resources are 459 | * released properly. 460 | */ 461 | public void close() { 462 | if (bleGatt == null) { 463 | return; 464 | } 465 | bleGatt.close(); 466 | bleGatt = null; 467 | } 468 | /** 469 | * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported 470 | * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} 471 | * callback. 472 | * 473 | * @param characteristic The characteristic to read from. 474 | */ 475 | public void readCharacteristic(final BluetoothGattCharacteristic characteristic) { 476 | if (bleAdapter == null || bleGatt == null) { 477 | Log.w(TAG, "BluetoothAdapter not initialized"); 478 | return; 479 | } 480 | 481 | bleGatt.readCharacteristic(characteristic); 482 | } 483 | /** 484 | * Enables or disables notification on a give characteristic. 485 | * 486 | * @param characteristic Characteristic to act on. 487 | * @param enabled If true, enable notification. False otherwise. 488 | */ 489 | public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { 490 | if (bleAdapter == null || bleGatt == null) { 491 | Log.w(TAG, "BluetoothAdapter not initialized"); 492 | return; 493 | } 494 | 495 | bleGatt.setCharacteristicNotification(characteristic, enabled); 496 | 497 | // For only characteristics that are meant to notify 498 | if (UUID_BATTERY_LEVEL.equals(characteristic.getUuid()) 499 | || UUID_BATTERY_STATUS.equals(characteristic.getUuid())) 500 | { 501 | BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); 502 | descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 503 | bleGatt.writeDescriptor(descriptor); 504 | } 505 | } 506 | /** 507 | * Retrieves a list of supported GATT services on the connected device. This should be 508 | * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully. 509 | * 510 | * @return A {@code List} of supported services. 511 | */ 512 | public List getSupportedGattServices() { 513 | if (bleGatt == null) return null; 514 | return bleGatt.getServices(); 515 | } 516 | } -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/java/com/example/blearduinoexampleproject/GattAttributes.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | import java.util.HashMap; 4 | 5 | /* 6 | * This is where you keep all your UUIDs and some other items to differentiate BLE services 7 | * and characteristics. 8 | * 9 | * */ 10 | public class GattAttributes { 11 | private static HashMap attributes = new HashMap(); 12 | 13 | public static String GENERIC_ACCESS = "00001800-0000-1000-8000-00805f9b34fb"; 14 | public static String GENERIC_ATTRIBUTE = "00001801-0000-1000-8000-00805f9b34fb"; 15 | public static String DEVICE_INFORMATION_SERVICE = "0000180a-0000-1000-8000-00805f9b34fb"; 16 | 17 | public static String BATTERY_SERVICE = "0000180f-0000-1000-8000-00805f9b34fb"; 18 | public static String BATTERY_LEVEL = "00002a19-0000-1000-8000-00805f9b34fb"; 19 | public static String BATTERY_STATUS = "00002a1b-0000-1000-8000-00805f9b34fb"; 20 | 21 | public static String ACCELEROMETER_SERVICE = "00000001-627e-47e5-a3fc-ddabd97aa966"; 22 | public static String X_ACCELERATION_MEASUREMENT = "00000002-627e-47e5-a3fc-ddabd97aa966"; 23 | public static String Y_ACCELERATION_MEASUREMENT = "00000003-627e-47e5-a3fc-ddabd97aa966"; 24 | public static String Z_ACCELERATION_MEASUREMENT = "00000004-627e-47e5-a3fc-ddabd97aa966"; 25 | public static String X_GYROSCOPE_MEASUREMENT = "00000005-627e-47e5-a3fc-ddabd97aa966"; 26 | public static String Y_GYROSCOPE_MEASUREMENT = "00000006-627e-47e5-a3fc-ddabd97aa966"; 27 | public static String Z_GYROSCOPE_MEASUREMENT = "00000007-627e-47e5-a3fc-ddabd97aa966"; 28 | public static String ACCELEROMETER_TIME_MEASUREMENT = "00000008-627e-47e5-a3fc-ddabd97aa966"; 29 | 30 | public static String DEVICE_NAME = "00002a00-0000-1000-8000-00805f9b34fb"; 31 | public static String APPEARANCE = "00002a01-0000-1000-8000-00805f9b34fb"; 32 | public static String PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS = "00002a04-0000-1000-8000-00805f9b34fb"; 33 | 34 | public static String MODEL_NUMBER_STRING = "00002a24-0000-1000-8000-00805f9b34fb"; 35 | public static String SERIAL_NUMBER_STRING = "00002a25-0000-1000-8000-00805f9b34fb"; 36 | public static String FIRMWARE_REVISION_STRING = "00002a26-0000-1000-8000-00805f9b34fb"; 37 | public static String HARDWARE_REVISION_STRING = "00002a27-0000-1000-8000-00805f9b34fb"; 38 | public static String SOFTWARE_REVISION_STRING = "00002a28-0000-1000-8000-00805f9b34fb"; 39 | public static String MANUFACTURER_NAME_STRING = "00002a29-0000-1000-8000-00805f9b34fb"; 40 | 41 | public static String SERVICE_CHANGED = "00002a05-0000-1000-8000-00805f9b34fb"; 42 | 43 | public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; 44 | 45 | public static final int X_ACCELERATION_READ = 1; 46 | public static final int Y_ACCELERATION_READ = 2; 47 | public static final int Z_ACCELERATION_READ = 3; 48 | public static final int X_GYROSCOPE_READ = 4; 49 | public static final int Y_GYROSCOPE_READ = 5; 50 | public static final int Z_GYROSCOPE_READ = 6; 51 | public static final int ACCELEROMETER_TIME_READ = 7; 52 | public static final int BATTERY_LEVEL_READ = 9; 53 | 54 | static { 55 | // Services 56 | attributes.put(ACCELEROMETER_SERVICE, "Accelerometer Service"); 57 | attributes.put(BATTERY_SERVICE, "Battery Service"); 58 | attributes.put(GENERIC_ACCESS, "Generic Access"); 59 | attributes.put(DEVICE_INFORMATION_SERVICE, "Device Information Service"); 60 | attributes.put(GENERIC_ATTRIBUTE, "Generic Attribute"); 61 | 62 | // Characteristics 63 | attributes.put(X_ACCELERATION_MEASUREMENT, "X Accelerometer Type"); 64 | attributes.put(Y_ACCELERATION_MEASUREMENT, "Y Accelerometer Measurement"); 65 | attributes.put(Z_ACCELERATION_MEASUREMENT, "Z Accelerometer Measurement"); 66 | attributes.put(X_GYROSCOPE_MEASUREMENT, "X Gyroscope Impedance Measurement"); 67 | attributes.put(Y_GYROSCOPE_MEASUREMENT, "Y Gyroscope Measurement"); 68 | attributes.put(Z_GYROSCOPE_MEASUREMENT, "Z Gyroscope Measurement"); 69 | attributes.put(ACCELEROMETER_TIME_MEASUREMENT, "Accelerometer Time Measurement"); 70 | 71 | attributes.put(BATTERY_LEVEL, "Battery Level"); 72 | attributes.put(BATTERY_STATUS, "Battery Status"); 73 | 74 | attributes.put(DEVICE_NAME, "Device Name"); 75 | attributes.put(APPEARANCE, "Appearance"); 76 | attributes.put(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS, "Peripheral Preferred Connection Parameters"); 77 | 78 | attributes.put(MANUFACTURER_NAME_STRING, "Manufacturer Name String"); 79 | attributes.put(MODEL_NUMBER_STRING, "Model Number String"); 80 | attributes.put(SERIAL_NUMBER_STRING, "Serial Number String"); 81 | attributes.put(HARDWARE_REVISION_STRING, "Hardware Revision String"); 82 | attributes.put(FIRMWARE_REVISION_STRING, "Firmware Revision String"); 83 | attributes.put(SOFTWARE_REVISION_STRING, "Software Revision String"); 84 | 85 | attributes.put(SERVICE_CHANGED, "Service Changed"); 86 | } 87 | 88 | public static String lookup(String uuid, String defaultName) { 89 | String name = attributes.get(uuid); 90 | return name == null ? defaultName : name; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/java/com/example/blearduinoexampleproject/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.bluetooth.BluetoothAdapter; 5 | import android.bluetooth.BluetoothManager; 6 | import android.content.BroadcastReceiver; 7 | import android.content.ComponentName; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.IntentFilter; 11 | import android.content.ServiceConnection; 12 | import android.content.SharedPreferences; 13 | import android.content.pm.PackageManager; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.os.IBinder; 17 | import android.util.Log; 18 | import android.view.View; 19 | import android.widget.ImageView; 20 | import android.widget.ProgressBar; 21 | import android.widget.TextView; 22 | import android.widget.Toast; 23 | 24 | import androidx.annotation.RequiresApi; 25 | import androidx.appcompat.app.AppCompatActivity; 26 | import androidx.core.app.ActivityCompat; 27 | 28 | import static android.Manifest.permission.ACCESS_FINE_LOCATION; 29 | import static android.Manifest.permission.BLUETOOTH; 30 | import static android.Manifest.permission.BLUETOOTH_ADMIN; 31 | import static android.app.ActionBar.DISPLAY_SHOW_CUSTOM; 32 | 33 | @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 34 | public class MainActivity extends AppCompatActivity { 35 | private final static String TAG = MainActivity.class.getSimpleName(); 36 | 37 | private static final int REQUEST_BLUETOOTH_ADMIN_ID = 1; 38 | private static final int REQUEST_LOCATION_ID = 2; 39 | private static final int REQUEST_BLUETOOTH_ID = 3; 40 | private BluetoothAdapter bleAdapter; 41 | 42 | private String deviceName; 43 | private String deviceAddress; 44 | private BluetoothLeService bleService; 45 | private boolean connected = false; 46 | private ImageView reconnectView; 47 | private ProgressBar batteryStatus; 48 | private ProgressBar scanProgressBar; 49 | private TextView scanView; 50 | private SharedPreferences sharedPrefBLE; 51 | 52 | private ProgressBar xAccProgressView; 53 | private ProgressBar yAccProgressView; 54 | private ProgressBar zAccProgressView; 55 | private TextView xAccView; 56 | private TextView yAccView; 57 | private TextView zAccView; 58 | private TextView activityView; 59 | 60 | @SuppressLint("WrongConstant") 61 | @Override 62 | protected void onCreate(Bundle savedInstanceState) { 63 | super.onCreate(savedInstanceState); 64 | setContentView(R.layout.activity_main); 65 | 66 | sharedPrefBLE = getSharedPreferences(getString(R.string.ble_device_key),Context.MODE_PRIVATE); 67 | deviceName = sharedPrefBLE.getString("name",null); 68 | deviceAddress = sharedPrefBLE.getString("address",null); 69 | 70 | bleCheck(); 71 | locationCheck(); 72 | 73 | // Use this check to determine whether BLE is supported on the device. 74 | if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { 75 | Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); 76 | finish(); 77 | } 78 | 79 | // Initializes a Bluetooth adapter. 80 | final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 81 | bleAdapter = bluetoothManager.getAdapter(); 82 | // Checks if Bluetooth is supported on the device. 83 | if (bleAdapter == null) { 84 | Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); 85 | finish(); 86 | return; 87 | } 88 | 89 | initializeLayout(); 90 | establishServiceConnection(); 91 | } 92 | 93 | @Override 94 | protected void onDestroy() { 95 | super.onDestroy(); 96 | if (bleService != null) { 97 | unbindService(serviceConnection); 98 | bleService = null; 99 | } 100 | } 101 | 102 | // Code to manage Service lifecycle. 103 | private final ServiceConnection serviceConnection = new ServiceConnection() { 104 | @Override 105 | public void onServiceConnected(ComponentName componentName, IBinder service) { 106 | bleService = ((BluetoothLeService.LocalBinder) service).getService(); 107 | if (!bleService.initialize()) { 108 | Log.e(TAG, "Unable to initialize Bluetooth"); 109 | finish(); 110 | } 111 | // Automatically connects to the device upon successful start-up initialization. 112 | bleService.connect(deviceAddress); 113 | } 114 | @Override 115 | public void onServiceDisconnected(ComponentName componentName) { 116 | bleService = null; 117 | } 118 | }; 119 | 120 | // Handles various events fired by the Service. 121 | private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { 122 | @Override 123 | public void onReceive(Context context, Intent intent) { 124 | final String action = intent.getAction(); 125 | if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { 126 | connected = true; 127 | scanProgressBar.setVisibility(View.VISIBLE); 128 | scanView.setVisibility(View.GONE); 129 | reconnectView.setVisibility((View.GONE)); 130 | 131 | } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { 132 | connected = false; 133 | scanProgressBar.setVisibility(View.GONE); 134 | scanView.setVisibility(View.GONE); 135 | reconnectView.setVisibility((View.VISIBLE)); 136 | 137 | } 138 | 139 | // else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { 140 | // } 141 | else if (BluetoothLeService.ACTION_DATA_READ_COMPLETED.equals(action)) { 142 | Log.d(TAG, "Data Read Completed"); 143 | 144 | updateUI(); 145 | 146 | } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { 147 | if (intent.getStringExtra(BluetoothLeService.ACTION_BATTERY_LEVEL) != null) { 148 | Log.d(TAG, "Battery level on main activity: " + intent.getStringExtra(BluetoothLeService.ACTION_BATTERY_LEVEL)); 149 | } 150 | } 151 | } 152 | }; 153 | 154 | @Override 155 | protected void onResume() { 156 | super.onResume(); 157 | registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter()); 158 | if (bleService != null) { 159 | final boolean result = bleService.connect(deviceAddress); 160 | Log.d(TAG, "Connect request result=" + result); 161 | } 162 | } 163 | 164 | @Override 165 | protected void onPause() { 166 | super.onPause(); 167 | unregisterReceiver(mGattUpdateReceiver); 168 | } 169 | 170 | private static IntentFilter makeGattUpdateIntentFilter() { 171 | final IntentFilter intentFilter = new IntentFilter(); 172 | intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); 173 | intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); 174 | intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); 175 | intentFilter.addAction(BluetoothLeService.ACTION_DATA_READ_COMPLETED); 176 | intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); 177 | return intentFilter; 178 | } 179 | 180 | private void establishServiceConnection() { 181 | if (deviceName != null && deviceAddress != null) { 182 | scanProgressBar.setVisibility(View.VISIBLE); 183 | scanView.setVisibility(View.GONE); 184 | Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); 185 | bindService(gattServiceIntent, serviceConnection, BIND_AUTO_CREATE); 186 | } 187 | } 188 | 189 | private void bleCheck() { 190 | if (ActivityCompat.checkSelfPermission(this, BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { 191 | // Bluetooth permission has not been granted. 192 | ActivityCompat.requestPermissions(this,new String[]{BLUETOOTH},REQUEST_BLUETOOTH_ID); 193 | } 194 | if (ActivityCompat.checkSelfPermission(this, BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) { 195 | // Bluetooth admin permission has not been granted. 196 | ActivityCompat.requestPermissions(this, new String[]{BLUETOOTH_ADMIN}, REQUEST_BLUETOOTH_ADMIN_ID); 197 | } 198 | } 199 | 200 | private void locationCheck() { 201 | if (ActivityCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 202 | // Location permission has not been granted. 203 | ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION}, REQUEST_LOCATION_ID); 204 | } 205 | } 206 | 207 | private void updateUI() { 208 | SharedPreferences sharedPrefAgm = getSharedPreferences(getString(R.string.agm_key),Context.MODE_PRIVATE); 209 | String xAccAvg = sharedPrefAgm.getString("x_acc_avg", null); 210 | String yAccAvg = sharedPrefAgm.getString("y_acc_avg", null); 211 | String zAccAvg = sharedPrefAgm.getString("z_acc_avg", null); 212 | 213 | xAccView.setText(xAccAvg); 214 | yAccView.setText(yAccAvg); 215 | zAccView.setText(zAccAvg); 216 | 217 | xAccProgressView.setProgress(Integer.parseInt(xAccAvg)); 218 | yAccProgressView.setProgress(Integer.parseInt(yAccAvg)); 219 | zAccProgressView.setProgress(Integer.parseInt(zAccAvg)); 220 | 221 | if (Integer.parseInt(xAccAvg) > 50 || Integer.parseInt(yAccAvg) > 50 || Integer.parseInt(zAccAvg) > 50) { 222 | activityView.setText("Moving"); 223 | } else { 224 | activityView.setText("Still"); 225 | } 226 | 227 | scanProgressBar.setVisibility(View.GONE); 228 | } 229 | 230 | public void openBleScanner() { 231 | Intent i = new Intent(this, RecyclerBleDeviceActivity.class); 232 | startActivity(i); 233 | } 234 | 235 | @SuppressLint("WrongConstant") 236 | public void initializeLayout() { 237 | this.getSupportActionBar().setDisplayOptions(DISPLAY_SHOW_CUSTOM); 238 | getSupportActionBar().setDisplayShowCustomEnabled(true); 239 | getSupportActionBar().setCustomView(R.layout.actionbar); 240 | 241 | final View actionView = getSupportActionBar().getCustomView(); 242 | 243 | batteryStatus = actionView.findViewById(R.id.batteryProgressBar); 244 | scanView = actionView.findViewById(R.id.scan); 245 | scanProgressBar = actionView.findViewById(R.id.scanInProgress); 246 | reconnectView = actionView.findViewById(R.id.reconnect); 247 | 248 | xAccView = findViewById(R.id.x_acc); 249 | yAccView = findViewById(R.id.y_acc); 250 | zAccView = findViewById(R.id.z_acc); 251 | 252 | xAccProgressView = findViewById(R.id.x_acc_progress); 253 | yAccProgressView = findViewById(R.id.y_acc_progress); 254 | zAccProgressView = findViewById(R.id.z_acc_progress); 255 | 256 | activityView = findViewById(R.id.activity_summary); 257 | 258 | scanView.setOnClickListener(new View.OnClickListener() { 259 | @Override 260 | public void onClick(View view) { 261 | openBleScanner(); 262 | } 263 | }); 264 | 265 | reconnectView.setOnClickListener(new View.OnClickListener() { 266 | @Override 267 | public void onClick(View view) { 268 | scanProgressBar.setVisibility(View.VISIBLE); 269 | scanView.setVisibility(View.GONE); 270 | reconnectView.setVisibility(View.GONE); 271 | bleService.connect(deviceAddress); 272 | } 273 | }); 274 | } 275 | } -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/java/com/example/blearduinoexampleproject/RecyclerBleDeviceActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.bluetooth.BluetoothAdapter; 6 | import android.bluetooth.BluetoothDevice; 7 | import android.bluetooth.BluetoothManager; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.SharedPreferences; 11 | import android.os.Build; 12 | import android.os.Bundle; 13 | import android.os.Handler; 14 | import android.view.View; 15 | import android.widget.ImageView; 16 | import android.widget.ProgressBar; 17 | import android.widget.TextView; 18 | 19 | import androidx.annotation.RequiresApi; 20 | import androidx.appcompat.app.AppCompatActivity; 21 | import androidx.recyclerview.widget.LinearLayoutManager; 22 | import androidx.recyclerview.widget.RecyclerView; 23 | 24 | import static android.app.ActionBar.DISPLAY_SHOW_CUSTOM; 25 | 26 | /* 27 | * This is a recycler activity that displays the list of the BLE devices available in the scan. 28 | * 29 | * */ 30 | @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 31 | public class RecyclerBleDeviceActivity extends AppCompatActivity implements BleRecyclerAdapter.ItemClickListener { 32 | 33 | private BleRecyclerAdapter recyclerAdapter; 34 | private BluetoothAdapter bleAdapter; 35 | private boolean scanning; 36 | 37 | private Handler handler; 38 | private static final int REQUEST_ENABLE_BT = 4; 39 | // Stops scanning after 10 seconds. 40 | private static final long SCAN_PERIOD = 1000; 41 | private TextView scanView; 42 | private ImageView stopView; 43 | private ProgressBar scanProgressBar; 44 | 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_recycler_ble_device); 49 | 50 | handler = new Handler(); 51 | 52 | initializeLayout(); 53 | 54 | // Initializes a Bluetooth recyclerAdapter. For API level 18 and above, get a reference to 55 | // BluetoothAdapter through BluetoothManager. 56 | final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 57 | bleAdapter = bluetoothManager.getAdapter(); 58 | } 59 | 60 | @Override 61 | protected void onDestroy() { 62 | super.onDestroy(); 63 | scanning = false; 64 | bleAdapter.stopLeScan(mBleScanCallback); 65 | invalidateScanMenu(); 66 | } 67 | 68 | @Override 69 | public void onItemClick(View view, int position) { 70 | onPause(); 71 | invalidateScanMenu(); 72 | 73 | String name; 74 | if (recyclerAdapter.getDevice(position).getName() == null) { 75 | name = "Unknown Device"; 76 | } else { 77 | name = recyclerAdapter.getDevice(position).getName(); 78 | } 79 | 80 | final BluetoothDevice device = recyclerAdapter.getDevice(position); 81 | if (device == null) return; 82 | 83 | SharedPreferences sharedPref = getSharedPreferences(getString(R.string.ble_device_key),Context.MODE_PRIVATE); 84 | 85 | SharedPreferences.Editor prefBleDeviceEditor = sharedPref.edit(); 86 | prefBleDeviceEditor.putString("name",device.getName()); 87 | prefBleDeviceEditor.putString("address",device.getAddress()); 88 | prefBleDeviceEditor.apply(); 89 | 90 | final Intent intent = new Intent(this, MainActivity.class); 91 | 92 | if (scanning) { 93 | bleAdapter.stopLeScan(mBleScanCallback); 94 | scanning = false; 95 | } 96 | 97 | startActivity(intent); 98 | } 99 | 100 | @Override 101 | protected void onResume() { 102 | super.onResume(); 103 | isBleOn(); 104 | // Clear list view recyclerAdapter. 105 | recyclerAdapter.clear(); 106 | scanBleDevice(true); 107 | } 108 | @Override 109 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 110 | // User chose not to enable Bluetooth. 111 | if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { 112 | finish(); 113 | return; 114 | } 115 | super.onActivityResult(requestCode, resultCode, data); 116 | } 117 | 118 | @Override 119 | protected void onPause() { 120 | super.onPause(); 121 | scanBleDevice(false); 122 | } 123 | 124 | private BluetoothAdapter.LeScanCallback mBleScanCallback = new BluetoothAdapter.LeScanCallback() { 125 | @Override 126 | public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { 127 | runOnUiThread(new Runnable() { 128 | @Override 129 | public void run() { 130 | recyclerAdapter.addDevice(device); 131 | recyclerAdapter.notifyDataSetChanged(); 132 | } 133 | }); 134 | } 135 | }; 136 | 137 | private void scanBleDevice(final boolean enable) { 138 | if (enable) { 139 | // Stops scanning after a pre-defined scan period. 140 | handler.postDelayed(new Runnable() { 141 | @Override 142 | public void run() { 143 | scanning = false; 144 | bleAdapter.stopLeScan(mBleScanCallback); 145 | invalidateScanMenu(); 146 | } 147 | }, SCAN_PERIOD); 148 | 149 | scanning = true; 150 | bleAdapter.startLeScan(mBleScanCallback); 151 | 152 | } else { 153 | scanning = false; 154 | bleAdapter.stopLeScan(mBleScanCallback); 155 | invalidateScanMenu(); 156 | } 157 | } 158 | 159 | // Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled, 160 | // fire an intent to display a dialog asking the user to grant permission to enable it. 161 | private void isBleOn() { 162 | if (!bleAdapter.isEnabled()) { 163 | if (!bleAdapter.isEnabled()) { 164 | Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 165 | startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); 166 | } 167 | } 168 | } 169 | 170 | private void invalidateScanMenu() { 171 | scanProgressBar.setVisibility(View.GONE); 172 | stopView.setVisibility(View.GONE); 173 | scanView.setVisibility(View.VISIBLE); 174 | } 175 | 176 | @SuppressLint("WrongConstant") 177 | public void initializeLayout() { 178 | // Initialize the action bar 179 | this.getSupportActionBar().setDisplayOptions(DISPLAY_SHOW_CUSTOM); 180 | getSupportActionBar().setDisplayShowCustomEnabled(true); 181 | getSupportActionBar().setCustomView(R.layout.actionbar); 182 | 183 | // On click listeners initialized 184 | final View actionView = getSupportActionBar().getCustomView(); 185 | 186 | scanView = actionView.findViewById(R.id.scan); 187 | stopView = actionView.findViewById(R.id.stop); 188 | scanProgressBar = actionView.findViewById(R.id.scanInProgress); 189 | 190 | scanView.setVisibility(View.GONE); 191 | stopView.setVisibility(View.VISIBLE); 192 | scanProgressBar.setVisibility(View.VISIBLE); 193 | 194 | scanView.setOnClickListener(new View.OnClickListener() { 195 | @Override 196 | public void onClick(View view) { 197 | onResume(); 198 | scanProgressBar.setVisibility(View.VISIBLE); 199 | scanView.setVisibility(View.GONE); 200 | stopView.setVisibility(View.VISIBLE); 201 | } 202 | }); 203 | 204 | stopView.setOnClickListener(new View.OnClickListener() { 205 | @Override 206 | public void onClick(View view) { 207 | onPause(); 208 | invalidateScanMenu(); 209 | } 210 | }); 211 | 212 | RecyclerView recyclerView = findViewById(R.id.bleRecyclerView); 213 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 214 | recyclerAdapter = new BleRecyclerAdapter(this); 215 | recyclerAdapter.setClickListener(this); 216 | recyclerView.setAdapter(recyclerAdapter); 217 | } 218 | } -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_autorenew_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_bluetooth_connected_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_bluetooth_disabled_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_bluetooth_searching_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_bluetooth_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_close_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_directions_run_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_donut_large_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_donut_large_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_donut_small_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_more_vert_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_opacity_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/baseline_settings_bluetooth_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_green.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_orange.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_primary.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_purple.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_warn.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/drawable/progress_circle_yellow.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/layout/actionbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 25 | 26 | 37 | 38 | 51 | 52 | 61 | 62 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 22 | 23 | 29 | 30 | 40 | 41 | 42 | 49 | 50 | 51 | 56 | 57 | 63 | 64 | 69 | 70 | 78 | 79 | 80 | 81 | 89 | 90 | 103 | 104 | 105 | 123 | 124 | 137 | 138 | 150 | 151 | 152 | 153 | 154 | 166 | 167 | 168 | 186 | 187 | 200 | 201 | 213 | 214 | 215 | 225 | 226 | 227 | 245 | 246 | 259 | 260 | 272 | 273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/layout/activity_recycler_ble_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/layout/listitem_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/menu/gatt_services.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 23 | 24 | 31 | 32 | 36 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #67CAD3 4 | #EA0029 5 | #1AACB7 6 | #546E7A 7 | #33333D 8 | #27272f 9 | #045D56 10 | #1EB980 11 | #FF6859 12 | #FFCF44 13 | #B15DFF 14 | #72DEFF 15 | #FFFFFF 16 | #848489 17 | #808080 18 | #D32F2F 19 | #FF6F00 20 | #373740 21 | #00000000 22 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ble example 3 | BLE is not supported 4 | Data: 5 | Device address: 6 | State: 7 | No data 8 | Connected 9 | 10 | Disconnected 11 | BLE Device Scan 12 | Bluetooth not supported. 13 | Unknown device 14 | Unknown characteristic 15 | Unknown service 16 | 17 | Connect 18 | Disconnect 19 | Scan 20 | Stop 21 | CONNECTED 22 | DISCONNECTED 23 | askljdgjKLSDGJlKJSgdlkd 24 | KLAFDJGkljasdglJASFLDFKSJFLKASDJGK 25 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 22 | 23 | 30 | 31 | 35 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/app/src/test/java/com/example/blearduinoexampleproject/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.blearduinoexampleproject; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /BLEAndroidExampleProject/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:4.0.1" 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } -------------------------------------------------------------------------------- /BLEAndroidExampleProject/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /BLEAndroidExampleProject/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/BLEAndroidExampleProject/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /BLEAndroidExampleProject/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 28 10:54:37 EDT 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /BLEAndroidExampleProject/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name = "BLE Android Example Project" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BLE with Android and Arduino (Adafruit BLE SPI Friend module) 2 | 3 | ## Repository Structure 4 | -------------------------------------------------------------------------------- /androidAdafruitBleExample/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malbaugh/androidAdafruitBleExample/663297aeb268ae90efd6e33f00b3a96e0bf596ec/androidAdafruitBleExample/.DS_Store -------------------------------------------------------------------------------- /androidAdafruitBleExample/.idea/androidAdafruitBleExample.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /androidAdafruitBleExample/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 21 | 22 | 23 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | xmlns:android 32 | 33 | ^$ 34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | xmlns:.* 43 | 44 | ^$ 45 | 46 | 47 | BY_NAME 48 | 49 |
50 |
51 | 52 | 53 | 54 | .*:id 55 | 56 | http://schemas.android.com/apk/res/android 57 | 58 | 59 | 60 |
61 |
62 | 63 | 64 | 65 | .*:name 66 | 67 | http://schemas.android.com/apk/res/android 68 | 69 | 70 | 71 |
72 |
73 | 74 | 75 | 76 | name 77 | 78 | ^$ 79 | 80 | 81 | 82 |
83 |
84 | 85 | 86 | 87 | style 88 | 89 | ^$ 90 | 91 | 92 | 93 |
94 |
95 | 96 | 97 | 98 | .* 99 | 100 | ^$ 101 | 102 | 103 | BY_NAME 104 | 105 |
106 |
107 | 108 | 109 | 110 | .* 111 | 112 | http://schemas.android.com/apk/res/android 113 | 114 | 115 | ANDROID_ATTRIBUTE_ORDER 116 | 117 |
118 |
119 | 120 | 121 | 122 | .* 123 | 124 | .* 125 | 126 | 127 | BY_NAME 128 | 129 |
130 |
131 |
132 |
133 |
134 |
-------------------------------------------------------------------------------- /androidAdafruitBleExample/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /androidAdafruitBleExample/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 1603896722351 28 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /androidAdafruitBleExample/ble_example/ble_example.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // These are the pins for an Arduino UNO board. Change as needed 5 | #define BLUEFRUIT_SPI_CS 8 6 | #define BLUEFRUIT_SPI_IRQ 7 7 | #define BLUEFRUIT_SPI_RST 4 8 | #define BLUEFRUIT_SPI_SCK 13 9 | #define BLUEFRUIT_SPI_MISO 12 10 | #define BLUEFRUIT_SPI_MOSI 11 11 | 12 | // SETUP ADAFRUIT BLUEFRUITLE SPI FRIEND 13 | Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST); 14 | 15 | // A couple global variables to keep track of time 16 | unsigned long StartTime; 17 | unsigned long TimeReference; 18 | 19 | int count = 0; 20 | 21 | /* ACCELEROMETER SERVICE ITEMS 22 | * ----------------- */ 23 | int32_t accServiceId; 24 | int32_t xAccCharId; 25 | int32_t yAccCharId; 26 | int32_t zAccCharId; 27 | int32_t xGyroCharId; 28 | int32_t yGyroCharId; 29 | int32_t zGyroCharId; 30 | int32_t accTimeCharId; 31 | 32 | /* These are just some fake values I created so you can get some values over 33 | * the connection. In reality you would setup the AGM and get data from the 34 | * AGM to send via BLE 35 | */ 36 | int AccX = 0; 37 | int AccY = 0; 38 | int AccZ = 0; 39 | int GyroX = 0; 40 | int GyroY = 0; 41 | int GyroZ = 0; 42 | 43 | /* BATTERY SERVICE ITEMS 44 | * ----------------- */ 45 | int BatteryLevel = 100; 46 | 47 | /* This sets up the battery service for us. 48 | * You could also set it up using the UUID 49 | * like I do acceleromter service. Check out the 50 | * bluefruit documentation for examples on 51 | * how to do this as setting up a standardized 52 | * service is slightly different than 53 | * a custom 128 bit UUID. 54 | */ 55 | Adafruit_BLEBattery battery(ble); 56 | 57 | 58 | /* Function: error 59 | * --------------------------------- 60 | * small helper function 61 | * 62 | * returns: n/a - void. Error message is printed serially 63 | */ 64 | void error(const __FlashStringHelper*err) { 65 | Serial.println(err); 66 | while (1); 67 | } 68 | 69 | 70 | /* Function: updateIntCharacteristic 71 | * --------------------------------- 72 | * This function updates given integer characteristics and emitts 73 | * the updated values via the BLE module 74 | * 75 | * nameOfChar = the name of the characteristic to be emitted as a String 76 | * characteristic = the value of the characteristic - must be an integer 77 | * serviceId = the id that this characteristic belongs to 78 | * 79 | * returns: n/a - void 80 | */ 81 | void updateIntCharacteristic(String nameOfChar, int counter, int characteristic, int32_t charId) { 82 | 83 | Serial.print("Byte size of "); 84 | Serial.print(nameOfChar); 85 | Serial.print(" : "); 86 | Serial.println(sizeof(characteristic)); 87 | 88 | String msg = String(characteristic) + "," + String(counter); 89 | 90 | ble.print( F("AT+GATTCHAR=") ); 91 | ble.print( charId ); 92 | ble.print( F(",") ); 93 | ble.println(msg); 94 | 95 | Serial.print("Actual value of "); 96 | Serial.print(nameOfChar); 97 | Serial.print(" : "); 98 | Serial.println(characteristic); 99 | if ( !ble.waitForOK() ) Serial.println(F("Failed to get response!")); 100 | Serial.println(""); 101 | Serial.println(""); 102 | } 103 | 104 | 105 | /* Function: emittAccelerometerData 106 | * --------------------------------- 107 | * prepares accelerometer data and passes it along to 108 | * be emitted by the BLE module 109 | * 110 | * xAcc = the x axis accelerometer reading 111 | * yAcc = the y axis accelerometer reading 112 | * zAcc = the z axis accelerometer reading 113 | * xGyro = the x axis gyroscope reading 114 | * yGyro = the y axis gyroscope reading 115 | * zGyro = the z axis gyroscope reading 116 | * accTimeReference = the time reference for this accelerometer reading 117 | * 118 | * returns: n/a - void 119 | */ 120 | void emittAccelerometerData(int xAcc, int yAcc, int zAcc, int xGyro, int yGyro, int zGyro, unsigned long accTimeReference, int counter) { 121 | updateIntCharacteristic("x acceleration", counter, xAcc, xAccCharId); 122 | updateIntCharacteristic("y acceleration", counter, yAcc, yAccCharId); 123 | updateIntCharacteristic("z acceleration", counter, zAcc, zAccCharId); 124 | updateIntCharacteristic("x gyroscope", counter, xGyro, xGyroCharId); 125 | updateIntCharacteristic("y gyroscope", counter, yGyro, yGyroCharId); 126 | updateIntCharacteristic("z gyroscope", counter, zGyro, zGyroCharId); 127 | updateIntCharacteristic("accelerometer time", counter, int(accTimeReference), accTimeCharId); 128 | } 129 | 130 | 131 | void setup() { 132 | // SETUP BLE 133 | delay(500); 134 | boolean success; 135 | Serial.begin(115200); 136 | if ( !ble.begin(false) ) error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?")); // Set to false for silent and true for debug 137 | if (! ble.factoryReset() ) error(F("Couldn't factory reset")); 138 | ble.echo(false); 139 | ble.info(); 140 | if (! ble.sendCommandCheckOK(F("AT+GAPDEVNAME=BLE Arduino Hardware")) ) error(F("Could not set device name?")); 141 | 142 | // SETUP ACCELEROMETER SERVICE & CHARACTERISTICS 143 | success = ble.sendCommandWithIntReply( F("AT+GATTADDSERVICE=UUID128=00-00-00-01-62-7E-47-E5-A3-fC-DD-AB-D9-7A-A9-66"), &accServiceId); 144 | if (! success) error(F("Could not add accelerometer service")); 145 | success = ble.sendCommandWithIntReply( F("AT+GATTADDCHAR=UUID128=00-00-00-02-62-7E-47-E5-A3-fC-DD-AB-D9-7A-A9-66, PROPERTIES=0x2, MIN_LEN=1, MAX_LEN=20,VALUE=5,DATATYPE=1"), &xAccCharId); 146 | if (! success) error(F("Could not add x acc characteristic")); 147 | success = ble.sendCommandWithIntReply( F("AT+GATTADDCHAR=UUID128=00-00-00-03-62-7E-47-E5-A3-fC-DD-AB-D9-7A-A9-66, PROPERTIES=0x2, MIN_LEN=1, MAX_LEN=20,VALUE=5,DATATYPE=1"), &yAccCharId); 148 | if (! success) error(F("Could not add y acc characteristic")); 149 | success = ble.sendCommandWithIntReply( F("AT+GATTADDCHAR=UUID128=00-00-00-04-62-7E-47-E5-A3-fC-DD-AB-D9-7A-A9-66, PROPERTIES=0x2, MIN_LEN=1, MAX_LEN=20,VALUE=5,DATATYPE=1"), &zAccCharId); 150 | if (! success) error(F("Could not add z acc characteristic")); 151 | success = ble.sendCommandWithIntReply( F("AT+GATTADDCHAR=UUID128=00-00-00-05-62-7E-47-E5-A3-fC-DD-AB-D9-7A-A9-66, PROPERTIES=0x2, MIN_LEN=1, MAX_LEN=20,VALUE=5,DATATYPE=1"), &xGyroCharId); 152 | if (! success) error(F("Could not add x gyro characteristic")); 153 | success = ble.sendCommandWithIntReply( F("AT+GATTADDCHAR=UUID128=00-00-00-06-62-7E-47-E5-A3-fC-DD-AB-D9-7A-A9-66, PROPERTIES=0x2, MIN_LEN=1, MAX_LEN=20,VALUE=5,DATATYPE=1"), &yGyroCharId); 154 | if (! success) error(F("Could not add y gyro characteristic")); 155 | success = ble.sendCommandWithIntReply( F("AT+GATTADDCHAR=UUID128=00-00-00-07-62-7E-47-E5-A3-fC-DD-AB-D9-7A-A9-66, PROPERTIES=0x2, MIN_LEN=1, MAX_LEN=20,VALUE=5,DATATYPE=1"), &zGyroCharId); 156 | if (! success) error(F("Could not add z gyro characteristic")); 157 | success = ble.sendCommandWithIntReply( F("AT+GATTADDCHAR=UUID128=00-00-00-08-62-7E-47-E5-A3-fC-DD-AB-D9-7A-A9-66, PROPERTIES=0x2, MIN_LEN=1, MAX_LEN=20,VALUE=5,DATATYPE=1"), &accTimeCharId); 158 | if (! success) error(F("Could not add acc time characteristic")); 159 | 160 | // SETUP BATTERY SERVICE & CHARACTERISTICS 161 | battery.begin(true); // Note: this executes a ble.reset() automatically. If you don't use the battery.begin(true); command, you will need to use the ble.reset() command at the end. See the bluefruit documentation 162 | 163 | Serial.println(); 164 | } 165 | 166 | 167 | void loop() { 168 | // Update the battery level measurement 169 | battery.update(BatteryLevel); 170 | 171 | StartTime = millis(); 172 | TimeReference = millis(); 173 | 174 | count = 0; 175 | // Imaginary BIS Sweep here... 176 | for (int i = 0; i < 5; i++) { 177 | TimeReference = millis(); 178 | 179 | AccX = random(1, 100); 180 | AccY = random(1, 100); 181 | AccZ = random(1, 100); 182 | GyroX = random(1, 100); 183 | GyroY = random(1, 100); 184 | GyroZ = random(1, 100); 185 | emittAccelerometerData(AccX,AccY,AccZ,GyroX,GyroY,GyroZ, (TimeReference - StartTime),count); 186 | 187 | count++; 188 | Serial.println(count); 189 | } 190 | 191 | // Imaginary battery drain... 192 | BatteryLevel--; 193 | if (BatteryLevel == 0) { 194 | BatteryLevel = 100; 195 | } 196 | } 197 | --------------------------------------------------------------------------------