├── .gitignore
├── ESP32CSISerial.iml
├── README.md
├── build.gradle
└── src
└── main
├── AndroidManifest.xml
└── java
└── com
└── stevenmhernandez
└── esp32csiserial
├── BaseDataCollectorService.java
├── CSIDataInterface.java
├── ESP32CSISerial.java
├── FileDataCollectorService.java
└── UsbService.java
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/ESP32CSISerial.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | generateDebugSources
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ESP32 CSI Serial (for Android)
2 |
3 | This Android library allows your own standard Android application to collect Wi-Fi Channel State Information (CSI). This library should work on any standard installed Android apps on most modern Android devices; no custom Android firmware updates are required.
4 |
5 | ## Requirements
6 |
7 | ESP32 microcontroller programmed with the [ESP32 CSI Toolkit](https://stevenmhernandez.github.io/ESP32-CSI-Tool/).
8 |
9 | USB OTG (On-the-go) Cable (typically micro-usb to micro-usb depending on Android device and ESP32 selected).
10 |
11 | This library allows the app to listen for data on USB OTG from an ESP32 microcontroller running [ESP32 CSI Toolkit](https://stevenmhernandez.github.io/ESP32-CSI-Tool/). CSI data is automatically parsed and returned to your app for further custom processing.
12 |
13 | ## Setup Your Custom Android Application
14 |
15 | `git clone https://github.com/StevenMHernandez/Android-ESP32-CSI.git ESP32CSISerial`
16 |
17 | `File > New > Import Module > {Select cloned ESP32CSISerial directory}`
18 |
19 | In your project perform the following tasks:
20 |
21 | ### `build.gradle`
22 |
23 | ```
24 | allprojects {
25 | repositories {
26 | ...
27 | maven { url "https://jitpack.io" } // <- Add this line
28 | }
29 | }
30 | ```
31 |
32 | ### `app/build.gradle`
33 |
34 | ```
35 | android {
36 | ...
37 | compileOptions {
38 | encoding "UTF-8"
39 | sourceCompatibility JavaVersion.VERSION_1_8
40 | targetCompatibility JavaVersion.VERSION_1_8
41 | }
42 | }
43 |
44 | dependencies {
45 | implementation project(path: ':ESP32CSISerial')
46 | }
47 | ```
48 |
49 | ### `AndroidManifest.xml`
50 | ```
51 |
52 |
53 | // <- Add this
54 | // <- Add this
55 | // <- Add this
56 | // <- Add this
57 |
58 | . . .
59 |
60 |
61 | . . .
62 | // <- Add this
63 |
64 |
65 | ```
66 |
67 | ### `settings.gradle`
68 |
69 | ```
70 | include ':app', ':ESP32CSISerial'
71 | ```
72 |
73 | ### Attach to an activity
74 |
75 | In your MainActivity (or any activity you choose)
76 |
77 | ```
78 | private ESP32CSISerial csiSerial = new ESP32CSISerial();
79 |
80 | @Override
81 | protected void onCreate(Bundle savedInstanceState) {
82 | . . .
83 | csiSerial.setup(this, "your_project_name");
84 | csiSerial.onCreate(this);
85 | }
86 |
87 | @Override
88 | protected void onResume() {
89 | . . .
90 | csiSerial.onResume(this);
91 | }
92 |
93 | @Override
94 | protected void onPause() {
95 | . . .
96 | csiSerial.onPause(this);
97 | }
98 | ```
99 |
100 |
101 | add the interface to your activity:
102 |
103 | ```
104 |
105 | public class MainActivity extends AppCompatActivity implements CSIDataInterface {
106 |
107 | . . .
108 |
109 | int csiCounter = 0;
110 | @Override
111 | public void addCsi(String csi_string) {
112 | csiCounter++;
113 | homeFragment.homeViewModel.setText(String.valueOf(csiCounter));
114 | }
115 | }
116 | ```
117 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 |
4 | android {
5 | compileSdkVersion 29
6 | defaultConfig {
7 | minSdkVersion 19
8 | targetSdkVersion 29
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | encoding "UTF-8"
21 | sourceCompatibility JavaVersion.VERSION_1_8
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 | }
25 |
26 | allprojects {
27 | repositories {
28 | maven { url "https://jitpack.io" }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.github.felHR85:UsbSerial:6.0.6'
35 | }
36 |
37 | sourceCompatibility = "7"
38 | targetCompatibility = "7"
39 |
--------------------------------------------------------------------------------
/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/src/main/java/com/stevenmhernandez/esp32csiserial/BaseDataCollectorService.java:
--------------------------------------------------------------------------------
1 | package com.stevenmhernandez.esp32csiserial;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * This abstract base class allows us to very easily create new methods to store our data.
7 | * For example, if you wish to simple send an HTTP PUT request to a server, create a
8 | * subclass of this abstract class and implement the following methods.
9 | */
10 | public abstract class BaseDataCollectorService {
11 | /**
12 | * Do any required setup here (i.e. initiate connection to a server or creating a file)
13 | *
14 | * @param context application context if required.
15 | */
16 | public abstract void setup(Context context);
17 |
18 | /**
19 | * Handle new data (i.e. HTTP PUT request or append to a local file)
20 | *
21 | * @param csi string data direct from the ESP32 serial monitor
22 | */
23 | public abstract void handle(String csi);
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/stevenmhernandez/esp32csiserial/CSIDataInterface.java:
--------------------------------------------------------------------------------
1 | package com.stevenmhernandez.esp32csiserial;
2 |
3 | public interface CSIDataInterface {
4 | public void addCsi(String csi_string);
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/stevenmhernandez/esp32csiserial/ESP32CSISerial.java:
--------------------------------------------------------------------------------
1 | package com.stevenmhernandez.esp32csiserial;
2 |
3 |
4 | import android.app.Activity;
5 | import android.content.BroadcastReceiver;
6 | import android.content.ComponentName;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.content.ServiceConnection;
11 | import android.os.Bundle;
12 | import android.os.Handler;
13 | import android.os.IBinder;
14 | import android.os.Message;
15 | import android.provider.Settings;
16 | import android.util.Log;
17 | import android.widget.Toast;
18 |
19 | import java.lang.ref.WeakReference;
20 | import java.util.Set;
21 |
22 | public class ESP32CSISerial {
23 |
24 | private final Handler handler = new Handler();
25 | private CSIDataInterface listener;
26 | private BaseDataCollectorService csiRecorderService = new FileDataCollectorService();
27 | private String experimentName = null;
28 | private Activity activity;
29 |
30 | public void setup(CSIDataInterface listener, String experimentName) {
31 | this.listener = listener;
32 | this.experimentName = experimentName;
33 | }
34 |
35 | public void onCreate(Activity activity) {
36 | this.activity = activity;
37 | csiRecorderService.setup(activity);
38 |
39 | mHandler = new USBHandler(activity, csiRecorderService, experimentName, listener);
40 |
41 | handler.postDelayed(new Runnable() {
42 | public void run() {
43 | if (usbService != null) {
44 | String setDataString = String.format("SETTIME: %d\n", (int) (System.currentTimeMillis() / 1000));
45 | usbService.write(setDataString.getBytes());
46 | }
47 | handler.postDelayed(this, 120000 / 2);
48 | }
49 | }, 10000); //Every 120000 ms (2 minutes)
50 | }
51 |
52 | public void onResume(Activity activity) {
53 | setFilters(); // Start listening notifications from UsbService
54 | startService(UsbService.class, usbConnection, null); // Start UsbService(if it was not started before) and Bind it
55 | }
56 |
57 | public void onPause(Activity activity) {
58 | activity.unregisterReceiver(mUsbReceiver);
59 | activity.unbindService(usbConnection);
60 | }
61 |
62 | public double[] parseCsi(String csi, boolean returnPhases) {
63 | String[] strs = csi.trim().split(" ");
64 | try {
65 | int[] im_ints = new int[strs.length / 2];
66 | int[] real_ints = new int[strs.length / 2];
67 | double[] amplitudes = new double[strs.length / 2];
68 | double[] phases = new double[strs.length / 2];
69 |
70 | for (int i = 0; i < strs.length; i++) {
71 | if (i % 2 == 0) {
72 | im_ints[(int) Math.floor(i / 2.0)] = Integer.parseInt(strs[i]);
73 | } else {
74 | real_ints[(int) Math.floor(i / 2.0)] = Integer.parseInt(strs[i]);
75 | }
76 | }
77 |
78 | for (int i = 0; i < strs.length / 2; i++) {
79 | amplitudes[i] = Math.sqrt(im_ints[i] ^ 2 + real_ints[i] ^ 2);
80 | phases[i] = Math.atan2(im_ints[i], real_ints[i]);
81 | }
82 |
83 | if (returnPhases) {
84 | return phases;
85 | } else {
86 | return amplitudes;
87 | }
88 | } catch (Exception e) {
89 | Log.e("Parse CSI String", csi);
90 | Log.e("ERROR:", e.getLocalizedMessage());
91 | }
92 |
93 | return null;
94 | }
95 |
96 | /*
97 | * This handler will be passed to UsbService. Data received from serial port is displayed through this handler
98 | */
99 | private static class USBHandler extends Handler {
100 | private final WeakReference mActivity;
101 | String experimentName;
102 | CSIDataInterface listener;
103 | BaseDataCollectorService dataService;
104 | public String buffer = "";
105 |
106 | public USBHandler(Activity activity, BaseDataCollectorService dataService, String experimentName, CSIDataInterface listener) {
107 | mActivity = new WeakReference<>(activity);
108 | this.experimentName = experimentName;
109 | this.dataService = dataService;
110 | this.listener = listener;
111 | }
112 |
113 | public String updateCsiString(Activity activity, String csi) {
114 | String deviceId = Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID);
115 | return String.format("%s,%s,%s,%s\n", csi.trim(), deviceId, System.currentTimeMillis(), experimentName);
116 | }
117 |
118 | @Override
119 | public void handleMessage(Message msg) {
120 | switch (msg.what) {
121 | case UsbService.MESSAGE_FROM_SERIAL_PORT:
122 | String data = (String) msg.obj;
123 |
124 | if (data.contains("\r\n")) {
125 | String[] split = data.split("\r\n");
126 |
127 | String line;
128 | if (split.length > 0) {
129 | line = buffer + split[0];
130 | } else {
131 | line = buffer;
132 | }
133 |
134 | // Reset Buffer
135 | if (split.length > 1) {
136 | buffer = split[1];
137 | } else {
138 | buffer = "";
139 | }
140 |
141 | if (line.contains("CSI_DATA")) {
142 | dataService.handle(updateCsiString(mActivity.get(), line));
143 | listener.addCsi(line);
144 | }
145 | } else {
146 | buffer += data;
147 | }
148 |
149 | break;
150 | case UsbService.CTS_CHANGE:
151 | Toast.makeText(mActivity.get(), "CTS_CHANGE", Toast.LENGTH_LONG).show();
152 | break;
153 | case UsbService.DSR_CHANGE:
154 | Toast.makeText(mActivity.get(), "DSR_CHANGE", Toast.LENGTH_LONG).show();
155 | break;
156 | }
157 | }
158 | }
159 |
160 |
161 | /**
162 | * @See: https://github.com/felHR85/UsbSerial/blob/master/example/src/main/java/com/felhr/serialportexample/MainActivity.java
163 | */
164 | private UsbService usbService;
165 | private USBHandler mHandler;
166 |
167 | /*
168 | * Notifications from UsbService will be received here.
169 | */
170 | private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
171 | @Override
172 | public void onReceive(Context context, Intent intent) {
173 | switch (intent.getAction()) {
174 | case UsbService.ACTION_USB_PERMISSION_GRANTED: // USB PERMISSION GRANTED
175 | Toast.makeText(context, "USB Ready", Toast.LENGTH_SHORT).show();
176 | break;
177 | case UsbService.ACTION_USB_PERMISSION_NOT_GRANTED: // USB PERMISSION NOT GRANTED
178 | Toast.makeText(context, "USB Permission not granted", Toast.LENGTH_SHORT).show();
179 | break;
180 | case UsbService.ACTION_NO_USB: // NO USB CONNECTED
181 | Toast.makeText(context, "No USB connected", Toast.LENGTH_SHORT).show();
182 | break;
183 | case UsbService.ACTION_USB_DISCONNECTED: // USB DISCONNECTED
184 | Toast.makeText(context, "USB disconnected", Toast.LENGTH_SHORT).show();
185 | break;
186 | case UsbService.ACTION_USB_NOT_SUPPORTED: // USB NOT SUPPORTED
187 | Toast.makeText(context, "USB device not supported", Toast.LENGTH_SHORT).show();
188 | break;
189 | }
190 | }
191 | };
192 |
193 | private final ServiceConnection usbConnection = new ServiceConnection() {
194 | @Override
195 | public void onServiceConnected(ComponentName arg0, IBinder arg1) {
196 | usbService = ((UsbService.UsbBinder) arg1).getService();
197 | usbService.setHandler(mHandler);
198 | }
199 |
200 | @Override
201 | public void onServiceDisconnected(ComponentName arg0) {
202 | usbService = null;
203 | }
204 | };
205 |
206 | private void startService(Class> service, ServiceConnection serviceConnection, Bundle extras) {
207 | if (!UsbService.SERVICE_CONNECTED) {
208 | Intent startServiceIntent = new Intent(this.activity, service);
209 | if (extras != null && !extras.isEmpty()) {
210 | Set keys = extras.keySet();
211 | for (String key : keys) {
212 | String extra = extras.getString(key);
213 | startServiceIntent.putExtra(key, extra);
214 | }
215 | }
216 | activity.startService(startServiceIntent);
217 | }
218 | Intent bindingIntent = new Intent(this.activity, service);
219 | this.activity.bindService(bindingIntent, serviceConnection, Context.BIND_AUTO_CREATE);
220 | }
221 |
222 | private void setFilters() {
223 | IntentFilter filter = new IntentFilter();
224 | filter.addAction(UsbService.ACTION_USB_PERMISSION_GRANTED);
225 | filter.addAction(UsbService.ACTION_NO_USB);
226 | filter.addAction(UsbService.ACTION_USB_DISCONNECTED);
227 | filter.addAction(UsbService.ACTION_USB_NOT_SUPPORTED);
228 | filter.addAction(UsbService.ACTION_USB_PERMISSION_NOT_GRANTED);
229 | this.activity.registerReceiver(mUsbReceiver, filter);
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/src/main/java/com/stevenmhernandez/esp32csiserial/FileDataCollectorService.java:
--------------------------------------------------------------------------------
1 | package com.stevenmhernandez.esp32csiserial;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import java.io.File;
7 | import java.io.FileNotFoundException;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 |
11 |
12 | public class FileDataCollectorService extends BaseDataCollectorService {
13 | private String LOG_TAG = "FileDataCollectorService";
14 | private FileOutputStream localBackup = null;
15 | public String filePrefix;
16 | public String fileType;
17 |
18 | public FileDataCollectorService() {
19 | this.filePrefix = "backup";
20 | this.fileType = "csv";
21 | }
22 |
23 | public FileDataCollectorService(String filePrefix, String fileType) {
24 | this.filePrefix = filePrefix;
25 | this.fileType = fileType;
26 | }
27 |
28 | public void setup(Context context) {
29 | try {
30 | File test = new File(context.getExternalFilesDir(null), filePrefix + System.currentTimeMillis() + "." + fileType);
31 | localBackup = new FileOutputStream(test, true);
32 | } catch (FileNotFoundException e) {
33 | e.printStackTrace();
34 | Log.w(LOG_TAG, "FileOutputStream exception: - " + e.toString());
35 | }
36 |
37 | }
38 |
39 | public void handle(String csi) {
40 | try {
41 | if (localBackup != null) {
42 | localBackup.write(csi.getBytes());
43 | localBackup.flush();
44 | }
45 | } catch (IOException e) {
46 | e.printStackTrace();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/stevenmhernandez/esp32csiserial/UsbService.java:
--------------------------------------------------------------------------------
1 | package com.stevenmhernandez.esp32csiserial;
2 |
3 | import android.app.PendingIntent;
4 | import android.app.Service;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.IntentFilter;
9 | import android.hardware.usb.UsbDevice;
10 | import android.hardware.usb.UsbDeviceConnection;
11 | import android.hardware.usb.UsbManager;
12 | import android.os.Binder;
13 | import android.os.Handler;
14 | import android.os.IBinder;
15 | import android.util.Log;
16 |
17 | import com.felhr.usbserial.CDCSerialDevice;
18 | import com.felhr.usbserial.UsbSerialDevice;
19 | import com.felhr.usbserial.UsbSerialInterface;
20 |
21 | import java.io.UnsupportedEncodingException;
22 | import java.util.HashMap;
23 | import java.util.Map;
24 |
25 | // @See: https://github.com/felHR85/UsbSerial/blob/master/example/src/main/java/com/felhr/serialportexample/UsbService.java
26 | public class UsbService extends Service {
27 |
28 | public static final String TAG = "UsbService";
29 |
30 | public static final String ACTION_USB_READY = "com.felhr.connectivityservices.USB_READY";
31 | public static final String ACTION_USB_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
32 | public static final String ACTION_USB_DETACHED = "android.hardware.usb.action.USB_DEVICE_DETACHED";
33 | public static final String ACTION_USB_NOT_SUPPORTED = "com.felhr.usbservice.USB_NOT_SUPPORTED";
34 | public static final String ACTION_NO_USB = "com.felhr.usbservice.NO_USB";
35 | public static final String ACTION_USB_PERMISSION_GRANTED = "com.felhr.usbservice.USB_PERMISSION_GRANTED";
36 | public static final String ACTION_USB_PERMISSION_NOT_GRANTED = "com.felhr.usbservice.USB_PERMISSION_NOT_GRANTED";
37 | public static final String ACTION_USB_DISCONNECTED = "com.felhr.usbservice.USB_DISCONNECTED";
38 | public static final String ACTION_CDC_DRIVER_NOT_WORKING = "com.felhr.connectivityservices.ACTION_CDC_DRIVER_NOT_WORKING";
39 | public static final String ACTION_USB_DEVICE_NOT_WORKING = "com.felhr.connectivityservices.ACTION_USB_DEVICE_NOT_WORKING";
40 | public static final int MESSAGE_FROM_SERIAL_PORT = 0;
41 | public static final int CTS_CHANGE = 1;
42 | public static final int DSR_CHANGE = 2;
43 | private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
44 | private static final int BAUD_RATE = 1552000; // BaudRate. Change this value if you need
45 | public static boolean SERVICE_CONNECTED = false;
46 |
47 | private IBinder binder = new UsbBinder();
48 |
49 | private Context context;
50 | private Handler mHandler;
51 | private UsbManager usbManager;
52 | private UsbDevice device;
53 | private UsbDeviceConnection connection;
54 | private UsbSerialDevice serialPort;
55 |
56 | private boolean serialPortConnected;
57 | /*
58 | * Data received from serial port will be received here. Just populate onReceivedData with your code
59 | * In this particular example. byte stream is converted to String and send to UI thread to
60 | * be treated there.
61 | */
62 | private UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() {
63 | @Override
64 | public void onReceivedData(byte[] arg0) {
65 | try {
66 | String data = new String(arg0, "UTF-8");
67 | if (mHandler != null)
68 | mHandler.obtainMessage(MESSAGE_FROM_SERIAL_PORT, data).sendToTarget();
69 | } catch (UnsupportedEncodingException e) {
70 | e.printStackTrace();
71 | }
72 | }
73 | };
74 |
75 | /*
76 | * State changes in the CTS line will be received here
77 | */
78 | private UsbSerialInterface.UsbCTSCallback ctsCallback = new UsbSerialInterface.UsbCTSCallback() {
79 | @Override
80 | public void onCTSChanged(boolean state) {
81 | if(mHandler != null)
82 | mHandler.obtainMessage(CTS_CHANGE).sendToTarget();
83 | }
84 | };
85 |
86 | /*
87 | * State changes in the DSR line will be received here
88 | */
89 | private UsbSerialInterface.UsbDSRCallback dsrCallback = new UsbSerialInterface.UsbDSRCallback() {
90 | @Override
91 | public void onDSRChanged(boolean state) {
92 | if(mHandler != null)
93 | mHandler.obtainMessage(DSR_CHANGE).sendToTarget();
94 | }
95 | };
96 | /*
97 | * Different notifications from OS will be received here (USB attached, detached, permission responses...)
98 | * About BroadcastReceiver: http://developer.android.com/reference/android/content/BroadcastReceiver.html
99 | */
100 | private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
101 | @Override
102 | public void onReceive(Context arg0, Intent arg1) {
103 | if (arg1.getAction().equals(ACTION_USB_PERMISSION)) {
104 | boolean granted = arg1.getExtras().getBoolean(UsbManager.EXTRA_PERMISSION_GRANTED);
105 | if (granted) // User accepted our USB connection. Try to open the device as a serial port
106 | {
107 | Intent intent = new Intent(ACTION_USB_PERMISSION_GRANTED);
108 | arg0.sendBroadcast(intent);
109 | connection = usbManager.openDevice(device);
110 | new ConnectionThread().start();
111 | } else // User not accepted our USB connection. Send an Intent to the Main Activity
112 | {
113 | Intent intent = new Intent(ACTION_USB_PERMISSION_NOT_GRANTED);
114 | arg0.sendBroadcast(intent);
115 | }
116 | } else if (arg1.getAction().equals(ACTION_USB_ATTACHED)) {
117 | if (!serialPortConnected)
118 | findSerialPortDevice(); // A USB device has been attached. Try to open it as a Serial port
119 | } else if (arg1.getAction().equals(ACTION_USB_DETACHED)) {
120 | // Usb device was disconnected. send an intent to the Main Activity
121 | Intent intent = new Intent(ACTION_USB_DISCONNECTED);
122 | arg0.sendBroadcast(intent);
123 | if (serialPortConnected) {
124 | serialPort.close();
125 | }
126 | serialPortConnected = false;
127 | }
128 | }
129 | };
130 |
131 | /*
132 | * onCreate will be executed when service is started. It configures an IntentFilter to listen for
133 | * incoming Intents (USB ATTACHED, USB DETACHED...) and it tries to open a serial port.
134 | */
135 | @Override
136 | public void onCreate() {
137 | this.context = this;
138 | serialPortConnected = false;
139 | UsbService.SERVICE_CONNECTED = true;
140 | setFilter();
141 | usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
142 | findSerialPortDevice();
143 | }
144 |
145 | /* MUST READ about services
146 | * http://developer.android.com/guide/components/services.html
147 | * http://developer.android.com/guide/components/bound-services.html
148 | */
149 | @Override
150 | public IBinder onBind(Intent intent) {
151 | return binder;
152 | }
153 |
154 | @Override
155 | public int onStartCommand(Intent intent, int flags, int startId) {
156 | return Service.START_NOT_STICKY;
157 | }
158 |
159 | @Override
160 | public void onDestroy() {
161 | super.onDestroy();
162 | serialPort.close();
163 | unregisterReceiver(usbReceiver);
164 | UsbService.SERVICE_CONNECTED = false;
165 | }
166 |
167 | /*
168 | * This function will be called from MainActivity to write data through Serial Port
169 | */
170 | public void write(byte[] data) {
171 | if (serialPort != null)
172 | serialPort.write(data);
173 | }
174 |
175 | public void setHandler(Handler mHandler) {
176 | this.mHandler = mHandler;
177 | }
178 |
179 | private void findSerialPortDevice() {
180 | // This snippet will try to open the first encountered usb device connected, excluding usb root hubs
181 | HashMap usbDevices = usbManager.getDeviceList();
182 | if (!usbDevices.isEmpty()) {
183 |
184 | // first, dump the hashmap for diagnostic purposes
185 | for (Map.Entry entry : usbDevices.entrySet()) {
186 | device = entry.getValue();
187 | Log.d(TAG, String.format("USBDevice.HashMap (vid:pid) (%X:%X)-%b class:%X:%X name:%s",
188 | device.getVendorId(), device.getProductId(),
189 | UsbSerialDevice.isSupported(device),
190 | device.getDeviceClass(), device.getDeviceSubclass(),
191 | device.getDeviceName()));
192 | }
193 |
194 | for (Map.Entry entry : usbDevices.entrySet()) {
195 | device = entry.getValue();
196 | int deviceVID = device.getVendorId();
197 | int devicePID = device.getProductId();
198 |
199 | // if (deviceVID != 0x1d6b && (devicePID != 0x0001 && devicePID != 0x0002 && devicePID != 0x0003) && deviceVID != 0x5c6 && devicePID != 0x904c) {
200 | if (UsbSerialDevice.isSupported(device)) {
201 | // There is a supported device connected - request permission to access it.
202 | requestUserPermission();
203 | break;
204 | } else {
205 | connection = null;
206 | device = null;
207 | }
208 | }
209 | if (device==null) {
210 | // There are no USB devices connected (but usb host were listed). Send an intent to MainActivity.
211 | Intent intent = new Intent(ACTION_NO_USB);
212 | sendBroadcast(intent);
213 | }
214 | } else {
215 | Log.d(TAG, "findSerialPortDevice() usbManager returned empty device list." );
216 | // There is no USB devices connected. Send an intent to MainActivity
217 | Intent intent = new Intent(ACTION_NO_USB);
218 | sendBroadcast(intent);
219 | }
220 | }
221 |
222 | private void setFilter() {
223 | IntentFilter filter = new IntentFilter();
224 | filter.addAction(ACTION_USB_PERMISSION);
225 | filter.addAction(ACTION_USB_DETACHED);
226 | filter.addAction(ACTION_USB_ATTACHED);
227 | registerReceiver(usbReceiver, filter);
228 | }
229 |
230 | /*
231 | * Request user permission. The response will be received in the BroadcastReceiver
232 | */
233 | private void requestUserPermission() {
234 | Log.d(TAG, String.format("requestUserPermission(%X:%X)", device.getVendorId(), device.getProductId() ) );
235 | PendingIntent mPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
236 | usbManager.requestPermission(device, mPendingIntent);
237 | }
238 |
239 | public class UsbBinder extends Binder {
240 | public UsbService getService() {
241 | return UsbService.this;
242 | }
243 | }
244 |
245 | /*
246 | * A simple thread to open a serial port.
247 | * Although it should be a fast operation. moving usb operations away from UI thread is a good thing.
248 | */
249 | private class ConnectionThread extends Thread {
250 | @Override
251 | public void run() {
252 | serialPort = UsbSerialDevice.createUsbSerialDevice(device, connection);
253 | if (serialPort != null) {
254 | if (serialPort.open()) {
255 | serialPortConnected = true;
256 | serialPort.setBaudRate(BAUD_RATE);
257 | serialPort.setDataBits(UsbSerialInterface.DATA_BITS_8);
258 | serialPort.setStopBits(UsbSerialInterface.STOP_BITS_1);
259 | serialPort.setParity(UsbSerialInterface.PARITY_NONE);
260 | /**
261 | * Current flow control Options:
262 | * UsbSerialInterface.FLOW_CONTROL_OFF
263 | * UsbSerialInterface.FLOW_CONTROL_RTS_CTS only for CP2102 and FT232
264 | * UsbSerialInterface.FLOW_CONTROL_DSR_DTR only for CP2102 and FT232
265 | */
266 | serialPort.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
267 | serialPort.read(mCallback);
268 | serialPort.getCTS(ctsCallback);
269 | serialPort.getDSR(dsrCallback);
270 |
271 | //
272 | // Some Arduinos would need some sleep because firmware wait some time to know whether a new sketch is going
273 | // to be uploaded or not
274 | //Thread.sleep(2000); // sleep some. YMMV with different chips.
275 |
276 | // Everything went as expected. Send an intent to MainActivity
277 | Intent intent = new Intent(ACTION_USB_READY);
278 | context.sendBroadcast(intent);
279 | } else {
280 | // Serial port could not be opened, maybe an I/O error or if CDC driver was chosen, it does not really fit
281 | // Send an Intent to Main Activity
282 | if (serialPort instanceof CDCSerialDevice) {
283 | Intent intent = new Intent(ACTION_CDC_DRIVER_NOT_WORKING);
284 | context.sendBroadcast(intent);
285 | } else {
286 | Intent intent = new Intent(ACTION_USB_DEVICE_NOT_WORKING);
287 | context.sendBroadcast(intent);
288 | }
289 | }
290 | } else {
291 | // No driver for given device, even generic CDC driver could not be loaded
292 | Intent intent = new Intent(ACTION_USB_NOT_SUPPORTED);
293 | context.sendBroadcast(intent);
294 | }
295 | }
296 | }
297 | }
298 |
--------------------------------------------------------------------------------