Android Bluetooth Serial Communication over SPP Source Code Explained
I was searching for particular code on how my Android device can communicate with Arduino using HC-05 module. Suddenly, I found this code on this site. However, this code might seems too arcane to understand
Of course, I'm not happy just copying these source code and import it on my project so, I studied the above code and here's what I've got. Let's get started.
1. Import the necessary packages for the project
2. The class body
Line 2: it's just a constant variable to be passed as an argument in the function to avoid repetitive typing.
Line 4: Class variable for buttons
Line 6-9: before you communicate with the other bluetooth device, you need a bluetooth adapter (something like a physical representation of your bluetooth device); a bluetooth socket where you could "plug" (create a connection) it on a slave device or to the HC-05 module. Here's an analogy: if your are familiar in computer networking, a socket (male) may resemble into an RJ-45). Later, this "raw" socket is to be set where "port" it needs to be connected (analogy: of course, your computer's network port, but assume that you have many ports on a computer, let's say 5. You need to specify where it should be connected later.) The OutputStream is a class that provides the method read() and write(). Stream is like pathway, in our case, pathway for which path the data should pass in order to get it to its destination. it could be resemble to CAT-5 cable. Therefore, we can conclude that socket and output stream works together (just like RJ-45 is attached to the CAT-5 cable).
Line 12: Wew, this the most intriguing part. UUID or Universal Unique IDentifier (aka ServiceID) is somewhat like well-known ports in networking. But in the case of Bluetooth, it just don't use port numbers. instead they use UUID's (So if you have port 21 for ftp, a well-known port, then you have
so if you are an android developer, some portion of the codes are pretty straightforward.I'll skip to line 11
Line 11: You need to initialized your class variable btAdapter that points to the physical adapter of your mobile phone (if your mobile phone supports bluetooth, probably you have already one) by calling the BluetoothAdapter 's static method: getDefaultAdapter().
Line 12: You check your bluetooth adpater if it is already enabled or not. if it doesn't you can start it out by using this code:
Create an intent and pass the btAdapter's static variable ACTION_REQUEST_ENABLE which causes a dialog box to pop-up for enabling bluetooth. the startForActivityForResult() is called for to trigger the action in the enableBtIntent();
The REQUEST_ENABLE_BT is the "ID" you used if wanted further to take an action if the REQUEST_ENABLE_BT failed to do so.
The Toast.makeText(context, "message", duration).show() causes the "message" to display on the ui for duration you specified. the getBaseContext is where the Toast.makeText() is associated or belongs to. Majority says that you should use getApplicationContext() instead. duno why. Anyway you can still use that code provided.
The rest of the codes in the onCreate() are pretty self-explanatory .
4. Defining the onResume() callback
Line 7: Sets up a pointer to the device that we want to connect with and provide the
address we defined a while ago.
Line 14: the most interesting part: createRfcommSocketToServiceRecord();
As I have before, btSocket needs to be initialized to the "port" where it needs to be connected. So you use createRfcommSocketToServiceRecord(UUID); method to do just that. You see that between the L2CAP and audio we have the SDP. SDP (Service Discovery Protocol) is a server that maintains a service record, in which the port (channel) and associated application service are kept. So, createRfcommSocketToServiceRecord(MY_UUID);is simply querying the service record through SDP ('cause it has all the information you need to be able connect to the SPP or whatever you UUID you registered and SDP will take care of the rest. Just think that method will return to you the appropriate ready-outgoing socket you need. Don't bother yourself.)
Analogy:
"You specified where the RJ-45 plug must be connected to by specifying the UUID inside the createRfcommSocketToServiceRecord();
and calling the btSocket.connect(); you simply plugged in the cable to one of your router's network port and you unplug it by calling btSocket.close()."
createRfcommSocketToServiceRecord(); needs to be inside try catch because it throws an IOExeption.
Line 40: Since btSocket() does not provide a write() method, will need to get the output stream of that socket (were the data will be written) and store it to an OutputStream object.
For further details about RFCOMM and SDP, check these links below:
Link 1
Link 2
List of Assigned Numbers
Link 1 - Assigned Numbers for Service Discovery
5. Finally, method for sending the data
Line 2: RS-232 sends and read data 8 bits at a time (or 1 byte). You need to encode your message in
a sequence of bytes and pass it to the write method of OutputStream instance as an argument.
I hope this helps for better understanding to creating a bluetooth connection over a serial port profile. If there was something I missed out, just let me know, tnx... ;)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
| package com.example.ledonoff;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
import com.example.ledonoff.R;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class LEDOnOff extends Activity {
private static final String TAG = "LEDOnOff";
Button btnOn, btnOff;
private static final int REQUEST_ENABLE_BT = 1;
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private OutputStream outStream = null;
// Well known SPP UUID
private static final UUID MY_UUID =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// Insert your bluetooth devices MAC address
private static String address = "00:00:00:00:00:00";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "In onCreate()");
setContentView(R.layout.main);
btnOn = (Button) findViewById(R.id.btnOn);
btnOff = (Button) findViewById(R.id.btnOff);
btAdapter = BluetoothAdapter.getDefaultAdapter();
checkBTState();
btnOn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("1");
Toast msg = Toast.makeText(getBaseContext(),
"You have clicked On", Toast.LENGTH_SHORT);
msg.show();
}
});
btnOff.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("0");
Toast msg = Toast.makeText(getBaseContext(),
"You have clicked Off", Toast.LENGTH_SHORT);
msg.show();
}
});
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "...In onResume - Attempting client connect...");
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = btAdapter.getRemoteDevice(address);
// Two things are needed to make a connection:
// A MAC address, which we got above.
// A Service ID or UUID. In this case we are using the
// UUID for SPP.
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
btAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
Log.d(TAG, "...Connecting to Remote...");
try {
btSocket.connect();
Log.d(TAG, "...Connection established and data link opened...");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
// Create a data stream so we can talk to server.
Log.d(TAG, "...Creating Socket...");
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and output stream creation failed:" + e.getMessage() + ".");
}
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "...In onPause()...");
if (outStream != null) {
try {
outStream.flush();
} catch (IOException e) {
errorExit("Fatal Error", "In onPause() and failed to flush output stream: " + e.getMessage() + ".");
}
}
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onPause() and failed to close socket." + e2.getMessage() + ".");
}
}
private void checkBTState() {
// Check for Bluetooth support and then check to make sure it is turned on
// Emulator doesn't support Bluetooth and will return null
if(btAdapter==null) {
errorExit("Fatal Error", "Bluetooth Not supported. Aborting.");
} else {
if (btAdapter.isEnabled()) {
Log.d(TAG, "...Bluetooth is enabled...");
} else {
//Prompt user to turn on Bluetooth
Intent enableBtIntent = new Intent(btAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
}
private void errorExit(String title, String message){
Toast msg = Toast.makeText(getBaseContext(),
title + " - " + message, Toast.LENGTH_SHORT);
msg.show();
finish();
}
private void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(TAG, "...Sending data: " + message + "...");
try {
outStream.write(msgBuffer);
} catch (IOException e) {
String msg = "In onResume() and an exception occurred during write: " + e.getMessage();
if (address.equals("00:00:00:00:00:00"))
msg = msg + ".\n\nUpdate your server address from 00:00:00:00:00:00 to the correct address on line 37 in the java code";
msg = msg + ".\n\nCheck that the SPP UUID: " + MY_UUID.toString() + " exists on server.\n\n";
errorExit("Fatal Error", msg);
}
}
}
|
Of course, I'm not happy just copying these source code and import it on my project so, I studied the above code and here's what I've got. Let's get started.
1. Import the necessary packages for the project
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| package com.example.ledonoff;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
import com.example.ledonoff.R;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public class LEDOnOff extends Activity {
private static final String TAG = "LEDOnOff";
Button btnOn, btnOff;
private static final int REQUEST_ENABLE_BT = 1;
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private OutputStream outStream = null;
// Well known SPP UUID
private static final UUID MY_UUID =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// Insert your bluetooth devices MAC address
private static String address = "00:00:00:00:00:00";
|
Line 4: Class variable for buttons
Line 6-9: before you communicate with the other bluetooth device, you need a bluetooth adapter (something like a physical representation of your bluetooth device); a bluetooth socket where you could "plug" (create a connection) it on a slave device or to the HC-05 module. Here's an analogy: if your are familiar in computer networking, a socket (male) may resemble into an RJ-45). Later, this "raw" socket is to be set where "port" it needs to be connected (analogy: of course, your computer's network port, but assume that you have many ports on a computer, let's say 5. You need to specify where it should be connected later.) The OutputStream is a class that provides the method read() and write(). Stream is like pathway, in our case, pathway for which path the data should pass in order to get it to its destination. it could be resemble to CAT-5 cable. Therefore, we can conclude that socket and output stream works together (just like RJ-45 is attached to the CAT-5 cable).
Line 12: Wew, this the most intriguing part. UUID or Universal Unique IDentifier (aka ServiceID) is somewhat like well-known ports in networking. But in the case of Bluetooth, it just don't use port numbers. instead they use UUID's (So if you have port 21 for ftp, a well-known port, then you have
00001101-0000-1000-8000-00805F9B34FB
for Serial Port Profile in Bluetooth (emulates RS-232 ports). By the way, it uses the what we called RFCOMM protocol (it's just another TCP) and it has 30 channels. The channels in this protocol uses 128 bit address. The one provided is the one that is used for serial communication. Serial Port Profile is just one we used, but there are other profiles as well. Check this out for more service and protocols. but if you're still confused. you may refer to this:| (image from IVT corp.) |
You can specify your own UUID if you're using RFCOMM because UUID's are assigned in a channel during runtime. For further details, click here
Line 16: you need to specify the address of the Bluetooth address that you want to connect to. It can be another bluetooth mobile phone or an HC-05 module.
3. Defining the onCreate() method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "In onCreate()");
setContentView(R.layout.main);
btnOn = (Button) findViewById(R.id.btnOn);
btnOff = (Button) findViewById(R.id.btnOff);
btAdapter = BluetoothAdapter.getDefaultAdapter();
checkBTState();
btnOn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("1");
Toast msg = Toast.makeText(getBaseContext(),
"You have clicked On", Toast.LENGTH_SHORT);
msg.show();
}
});
btnOff.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("0");
Toast msg = Toast.makeText(getBaseContext(),
"You have clicked Off", Toast.LENGTH_SHORT);
msg.show();
}
});
}
|
Line 11: You need to initialized your class variable btAdapter that points to the physical adapter of your mobile phone (if your mobile phone supports bluetooth, probably you have already one) by calling the BluetoothAdapter 's static method: getDefaultAdapter().
Line 12: You check your bluetooth adpater if it is already enabled or not. if it doesn't you can start it out by using this code:
1
2
| Intent enableBtIntent = new Intent(btAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
|
The REQUEST_ENABLE_BT is the "ID" you used if wanted further to take an action if the REQUEST_ENABLE_BT failed to do so.
The Toast.makeText(context, "message", duration).show() causes the "message" to display on the ui for duration you specified. the getBaseContext is where the Toast.makeText() is associated or belongs to. Majority says that you should use getApplicationContext() instead. duno why. Anyway you can still use that code provided.
The rest of the codes in the onCreate() are pretty self-explanatory .
4. Defining the onResume() callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
| public void onResume() {
super.onResume();
Log.d(TAG, "...In onResume - Attempting client connect...");
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = btAdapter.getRemoteDevice(address);
// Two things are needed to make a connection:
// A MAC address, which we got above.
// A Service ID or UUID. In this case we are using the
// UUID for SPP.
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
btAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
Log.d(TAG, "...Connecting to Remote...");
try {
btSocket.connect();
Log.d(TAG, "...Connection established and data link opened...");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
// Create a data stream so we can talk to server.
Log.d(TAG, "...Creating Socket...");
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and output stream creation failed:" + e.getMessage() + ".");
}
}
|
address we defined a while ago.
Line 14: the most interesting part: createRfcommSocketToServiceRecord();
As I have before, btSocket needs to be initialized to the "port" where it needs to be connected. So you use createRfcommSocketToServiceRecord(UUID); method to do just that. You see that between the L2CAP and audio we have the SDP. SDP (Service Discovery Protocol) is a server that maintains a service record, in which the port (channel) and associated application service are kept. So, createRfcommSocketToServiceRecord(MY_UUID);is simply querying the service record through SDP ('cause it has all the information you need to be able connect to the SPP or whatever you UUID you registered and SDP will take care of the rest. Just think that method will return to you the appropriate ready-outgoing socket you need. Don't bother yourself.)
Analogy:
"You specified where the RJ-45 plug must be connected to by specifying the UUID inside the createRfcommSocketToServiceRecord();
and calling the btSocket.connect(); you simply plugged in the cable to one of your router's network port and you unplug it by calling btSocket.close()."
createRfcommSocketToServiceRecord(); needs to be inside try catch because it throws an IOExeption.
Line 40: Since btSocket() does not provide a write() method, will need to get the output stream of that socket (were the data will be written) and store it to an OutputStream object.
For further details about RFCOMM and SDP, check these links below:
Link 1
Link 2
List of Assigned Numbers
Link 1 - Assigned Numbers for Service Discovery
5. Finally, method for sending the data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| private void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(TAG, "...Sending data: " + message + "...");
try {
outStream.write(msgBuffer);
} catch (IOException e) {
String msg = "In onResume() and an exception occurred during write: " + e.getMessage();
if (address.equals("00:00:00:00:00:00"))
msg = msg + ".\n\nUpdate your server address from 00:00:00:00:00:00 to the correct address on line 37 in the java code";
msg = msg + ".\n\nCheck that the SPP UUID: " + MY_UUID.toString() + " exists on server.\n\n";
errorExit("Fatal Error", msg);
}
}
|
a sequence of bytes and pass it to the write method of OutputStream instance as an argument.
I hope this helps for better understanding to creating a bluetooth connection over a serial port profile. If there was something I missed out, just let me know, tnx... ;)
Comments
Post a Comment