是设备解决方案的一部分。

由Android,我想介绍如何执行由蓝牙SPP(串行端口配置文件)的无线通信。

Android SDK中,以有一个名为“BluetoothChat”一个示例项目,因为它已经实现在执行蓝牙通信所需Ichitori的功能,我们想解释给该组。

BluetoothChat是,它说假设的Android终端之间的连接,但这次我想谈谈与其他设备,如PC连接,使用SPP目标。

●蓝牙授权(许可)
首先,为了使用蓝牙功能的应用,“蓝牙”“BLUETOOTH_ADMIN”两个蓝牙权限之一,则必须在许可证的表明自己的至少一种声明。

[蓝牙] 
连接请求时,连接接受,并且,必要的,以便执行任何蓝牙通信,如THETA传输。

[BLUETOOTH_ADMIN] 
必要的,以便进行设备发现和蓝牙设置的初始化操作。
※如果您想使用BLUETOOTH_ADMIN许可,还需要蓝牙权限。

它显示了声明允许在应用程序下清单蓝牙的过程。
①打开的应用程序清单
②[权限]选择选项卡
③按钮,然后单击[添加...]中
选择所需的权限④Uses 
⑤[确定],然后点击按钮
⑥Attributes的名称列表的使用权限更
 android.permission.BLUETOOTH或android.permission.BLUETOOTH_ADMIN 
选择

通过该操作,下面的描述将被添加到清单。


●收购并启用蓝牙适配器

在应用程序通过蓝牙进行沟通,首先,你需要确保蓝牙设备支持。
调用BluetoothAdapter.getDefaultAdapter()方法来获取本地BluetoothAdapter。如果getDefaultAdapter(结果)为空,则表示该设备不支持蓝牙。

以下是在BluetoothChat有关部分的源代码。
[BluetoothChat.java]

// Local Bluetooth adapterprivate BluetoothAdapter mBluetoothAdapter = null; public void onCreate(Bundle savedInstanceState) {
             :
    // Get local Bluetooth adapter
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();     // If the adapter is null, then Bluetooth is not supported
    if (mBluetoothAdapter == null) {
        Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
        finish();
        return;
    }}

一旦确认该设备支持蓝牙,看是否已开启蓝牙功能,然后调用isEnabled()方法。如果isEnabled()的结果是假的,蓝牙被禁用。要启用蓝牙,使用ACTION_REQUEST_ENABLE行动意图,调用startActivityForResult()。
这使您可以生成请求通过系统配置以启用蓝牙。

以下是在BluetoothChat有关部分的源代码。
[BluetoothChat.java]

// If BT is not on, request that it be enabled.// setupChat() will then be called during onActivityResultif (!mBluetoothAdapter.isEnabled()) {
    Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableIntent, REQUEST_ENABLE_BT);// Otherwise, setup the chat session} else {
    if (mChatService == null) setupChat();}


当您启动ACTION_REQUEST_ENABLE行动意图,之后,对话框,请求允许启用蓝牙显示。如果选择“是”,系统会启动蓝牙激活。运行蓝牙激活,你会看到类似下面的对话框。



该过程完成后,将返回到应用程序。

蓝牙结果启用,您将收到的onActivityResult()回调。
在成功激活的情况下,结果代码是RESULT_OK。发生错误或者用户选择在许可证申请“否”,案件的结果代码不能启用蓝牙将RESULT_CANCELED。

以下是在BluetoothChat有关部分的源代码。
[BluetoothChat.java]


public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
  case REQUEST_ENABLE_BT:
      // When the request to enable Bluetooth returns
      if (resultCode == Activity.RESULT_OK) {
          // Bluetooth is now enabled, so set up a chat session
          setupChat();
      } else {
          // User did not enable Bluetooth or an error occured
          Log.d(TAG, "BT not enabled");
          Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
            finish();
      }
    }

以上,蓝牙设备存在于本地,它能够确认有效。
然后搜索连接的设备进行了说明。

●搜索远程设备的
要搜索的一个远程蓝牙设备,该方法和一对(键的话),以使用设备的发现功能有两种方法来查询的设备的列表。

该装置的发现是蓝牙功能的设备的过程中进行扫描是否在局部区域。为设备到发现的请求作出响应,必须具有有效的是有可能发现。(搭载的Android设备未在可能发现默认开启)

当它是第一次建立与远程设备的连接,配对的自动请求将被呈现给用户。当设备被配对,该装置的基本信息将被可使用蓝牙API(设备名称,类信息,例如MAC地址)尚未保存读取。因此,为了能够与已知的MAC地址到远程设备来初始化,则最好不要有运行的发现过程耗时。

应用程序中,首先提出了对设备到用户的列表,我认为这使得该装置的目标进行扫描用的设备的发现功能,除非该列表是可取的。BluetoothChat即使它已经以这种方式取得。

一对▼询问设备
到对器件的查询,调用getBondedDevices()方法。
此方法返回表示已配对设备一套BluetoothDevice类的。

在BluetoothChat,选择“连接设备”将被从选项菜单中执行时,对器件的询问。的源代码包含在DeviceListActivity.java。
[DeviceListActivity.java]

// Get the local Bluetooth adaptermBtAdapter = BluetoothAdapter.getDefaultAdapter(); // Get a set of currently paired devicesSet pairedDevices = mBtAdapter.getBondedDevices(); // If there are paired devices, add each one to the ArrayAdapterif (pairedDevices.size() > 0) {
    findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
    for (BluetoothDevice device : pairedDevices) {
        mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }} else {
    String noDevices = getResources().getText(R.string.none_paired).toString();
    mPairedDevicesArrayAdapter.add(noDevices);}


是MAC地址什么是需要BluetoothDevice类对象初始化连接。
在这个例子中,显示给用户(mPairedDevicesArrayAdapter)一个ArrayAdapter的一部分
作为已保存的MAC地址。(由的getAddress后天())

▼设备发现的
一对器件没有,或者,在对象被连接到配对的设备列表不存在,然后搜索是接近使用该设备的发现功能的蓝牙设备的情况。

要启动设备的发现,调用startDiscovery()方法。过程将是异步执行(被调用后立即返回startDiscovery())。发现的过程中,通常有大约12秒的查询扫描,然后页的扫描到寻找那些被发现之后每个设备的蓝牙名称。

在BluetoothChat上所显示DeviceListActivity当您选择从选项菜单“连接的设备”,该设备的发现将在执行“扫描设备”时进行。

以下是在BluetoothChat有关部分的源代码。
[DeviceListActivity.java]

@Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
            :
    // Initialize the button to perform device discovery
    Button scanButton = (Button) findViewById(R.id.button_scan);
    scanButton.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            doDiscovery();
            v.setVisibility(View.GONE);
        }
    }); (途中省略) /**
 * Start device discover with the BluetoothAdapter
 */private void doDiscovery() {
    if (D) Log.d(TAG, "doDiscovery()");     // Indicate scanning in the title
    setProgressBarIndeterminateVisibility(true);
    setTitle(R.string.scanning);     // Turn on sub-title for new devices
    findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);     // If we're already discovering, stop it
    if (mBtAdapter.isDiscovering()) {
        mBtAdapter.cancelDiscovery();
    }     // Request discover from BluetoothAdapter
    mBtAdapter.startDiscovery();}

当您运行startDiscovery()时,系统会广播为每个设备ACTION_FOUND意图。该应用程序,以获得有关每个发现的设备的信息,您需要注册对ACTION_FOUND意图广播接收器。

以下是在BluetoothChat有关部分的源代码。
[DeviceListActivity.java]

// The BroadcastReceiver that listens for discovered devices and// changes the title when discovery is finishedprivate final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();         // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // If it's already paired, skip it, because it's been listed already
            if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
            }
        // When discovery is finished, change the Activity title
        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            setProgressBarIndeterminateVisibility(false);
            setTitle(R.string.select_device);
            if (mNewDevicesArrayAdapter.getCount() == 0) {
                String noDevices = getResources().getText(R.string.none_found).toString();
                mNewDevicesArrayAdapter.add(noDevices);
            }
        }
    }};

发现过程的执行是蓝牙适配器一个沉重的处理,并消耗了大量的资源。有一次,我发现被连接的设备,你必须尝试连接前调用cancelDiscovery()停止的发现。

▼启用发现功能
进行说明,但是,搭载的Android设备不默认情况下,可能发现开启。如果要使得它可以发现本地设备从其他设备,调用startActivityForResult()中ACTION_REQUEST_DISCOVERABLE行动意图。

正如你已经启用了发现模式将通过系统配置公布结果。缺省情况下,设备,但它是120秒,习惯被发现,可以通过添加对意图EXTRA_DISCOVERABLE_DURATION附加信息来定义(最多300秒)为不同的时间段。

在BluetoothChat,使得在选择“让发现”从选项菜单做的时候发现功能。

以下是在BluetoothChat有关部分的源代码。
[BluetoothChat.java]

private void ensureDiscoverable() {
    if(D) Log.d(TAG, "ensure discoverable");
    if (mBluetoothAdapter.getScanMode() !=
        BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
        Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
        discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
        startActivity(discoverableIntent);
    }}

顺便说一句,有必要启用发现功能,只要你想举办一个接受该应用程序来在连接的服务器套接字。(不适用于连接作为一个客户端应用程序)

以上,要连接到的我们可以得到设备的MAC地址。然后描述该设备的连接。

●连接的设备
通过该应用程序,以创建该两个设备之间的连接时,将需要实现两个机制的服务器端和客户端。它必须是设备中的一个是打开一个服务器套接字,因为它需要将其他是初始化连接(初始化连接时,使用该服务器设备的MAC地址)。在相同的RFCOMM信道服务器和客户端,它被认为是已经连接到的情况下,每个接收到的已连接的BluetoothSocket。

在这一点上,每个设备都可以得到的输入和输出流,就可以开始数据传输。

服务器和客户端设备将赢得以不同的方式为每个所需的的BluetoothSocket。服务器收到的BluetoothSocket时传入的连接被接受,当你打开RFCOMM通道到服务器的客户端将收到的BluetoothSocket。

▼连接作为服务器

当你想在两个设备连接,你需要作为由一个服务器是打开BluetoothServerSocket持有它。服务器套接字的目的,并侦听传入的连接请求,就是当对方接受的连接,提供已经连接的BluetoothSocket。

当的BluetoothSocket从BluetoothServerSocket获得的,如果你不想接受进一步的连接,可以放弃BluetoothServerSocket。

1. listenUsingRfcommWithServiceRecord(字符串,UID)调用,以获得BluetoothServerSocket。

串,一个可以被识别的服务的名称,系统将设备它(服务发现协议:SDP)上自动新服务发现协议将写入数据库条目。(该名称是可选的,可以是一个简单的应用程序的名称)
此外,UUID也包含在SDP条目,这将是在与客户端设备的连接的组匹配。
为了确保连接被接受,你将需要这些UUID匹配。为UUID,后面描述。

2.调用accept()方法,并开始收听连接请求。

直到发生异常时此调用,连接被接受,将被阻止。
远程装置中,当服务器套接字发送了匹配连接请求注册的UUID,所述连接被接受。
成功的案例,accept()返回已连接的BluetoothSocket。

3.如果您不希望接受更多的连接,调用close()。

此服务器套接字和所有的资源被释放,但连接一直的BluetoothSocket返回的接受()未关闭。
不像TCP / IP协议,以允许RFCOMM是在同一时间,每个通道一个仅连接的客户端。因此,在大多数情况下,它接受一个连接的套接字之后立即调用BluetoothServerSocket的close()。

调用accept()方法,因为封锁的通话,你应该在一个单独的线程中运行。
处理,通常使用BluetoothServerSocket和的BluetoothSocket用一个新的线程,这是由应用程序管理处理。

以下是在BluetoothChat有关部分的源代码。
[BluetoothChatService.java]

/**
 * This thread runs while listening for incoming connections. It behaves
 * like a server-side client. It runs until a connection is accepted
 * (or until cancelled).
 */private class AcceptThread extends Thread {
    // The local server socket
    private final BluetoothServerSocket mmServerSocket;     public AcceptThread() {
        BluetoothServerSocket tmp = null;         // Create a new listening server socket
        try {
            tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) {
            Log.e(TAG, "listen() failed", e);
        }
        mmServerSocket = tmp;
    }     public void run() {
        if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);
        setName("AcceptThread");
        BluetoothSocket socket = null;         // Listen to the server socket if we're not connected
        while (mState != STATE_CONNECTED) {
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                Log.e(TAG, "accept() failed", e);
                break;
            }             // If a connection was accepted
            if (socket != null) {
                synchronized (BluetoothChatService.this) {
                    switch (mState) {
                    case STATE_LISTEN:
                    case STATE_CONNECTING:
                        // Situation normal. Start the connected thread.
                        connected(socket, socket.getRemoteDevice());
                        break;
                    case STATE_NONE:
                    case STATE_CONNECTED:
                        // Either not ready or already connected. Terminate new socket.
                        try {
                            socket.close();
                        } catch (IOException e) {
                            Log.e(TAG, "Could not close unwanted socket", e);
                        }
                        break;
                    }
                }
            }
        }
        if (D) Log.i(TAG, "END mAcceptThread");
    }     public void cancel() {
        if (D) Log.d(TAG, "cancel " + this);
        try {
            mmServerSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "close() of server failed", e);
        }
    }}


▼连接作为客户端
进行远程设备(即拥有一个开放的服务器套接字设备)的初始化,你需要赢得代表首次远程设备的BluetoothDevice类的对象。
然后,您需要初始化收购来连接使用BluetoothDevice类的的BluetoothSocket。

的基本程序如下。
1.使用BluetoothDevice类,通过调用createRfcommSocketToServiceRecord(UUID),初始化的BluetoothSocket。

UUID在这里通过,则需要以匹配使用由服务器设备时,服务器打开BluetoothServerSocket的UUID。

2.初始化通过调用connect连接()。

当远程设备接受,而你正在连接共享使用的连接,RFCOMM通道,你从连接回来()。
这种方法也已被呼叫。出于某种原因,异常如果connect()方法连接失败已成为发生超时。

以下是在BluetoothChat有关部分的源代码。
[BluetoothChatService.java]

/**
 * This thread runs while attempting to make an outgoing connection
 * with a device. It runs straight through; the connection either
 * succeeds or fails.
 */private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;     public ConnectThread(BluetoothDevice device) {
        mmDevice = device;
        BluetoothSocket tmp = null;         // Get a BluetoothSocket for a connection with the
        // given BluetoothDevice
        try {
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) {
            Log.e(TAG, "create() failed", e);
        }
        mmSocket = tmp;
    }     public void run() {
        Log.i(TAG, "BEGIN mConnectThread");
        setName("ConnectThread");         // Always cancel discovery because it will slow down a connection
        mAdapter.cancelDiscovery();         // Make a connection to the BluetoothSocket
        try {
            // This is a blocking call and will only return on a
            // successful connection or an exception
            mmSocket.connect();
        } catch (IOException e) {
            connectionFailed();
            // Close the socket
            try {
                mmSocket.close();
            } catch (IOException e2) {
                Log.e(TAG, "unable to close() socket during connection failure", e2);
            }
            // Start the service over to restart listening mode
            BluetoothChatService.this.start();
            return;
        }         // Reset the ConnectThread because we're done
        synchronized (BluetoothChatService.this) {
            mConnectThread = null;
        }         // Start the connected thread
        connected(mmSocket, mmDevice);
    }     public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "close() of connect socket failed", e);
        }
    }}

完成对远程设备或多个方面,我能够赢得必要的BluetoothSocket的发送和接收。这里,的UUID的简要说明,连接到PC等使用BluetoothChat时,这是一个问题。

★对于UUID
的UUID(通用唯一标识符=通用唯一标识符)是128位的标准格式,它会被用于字符串唯一标识信息的ID。

下表是蓝牙分配的数字文档中定义的最常见的UUID的列表。


如果通过SPP通信,必须指定00001101-0000-1000-8000-00805F9B34FB到UUID。
[BluetoothChatService.java]

// Unique UUID for this applicationprivate static final UUID   MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66"); 

 // Unique UUID for SPP applicationprivate static final UUID  	MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");



●传输和接收数据
的设备连接成功后,你们每个人都已经连接的BluetoothSocket。
使用的BluetoothSocket,处理发送和接收的任何数据如下。

1.在的getOutputStream的getInputStream()()方法来获取InputStream和OutputStream使用套接字来办理过户。

2.流方面,阅读和在read()和write()方法写入数据。

阅读(),write()方法,因为阻止呼叫,你应该使用线程流的读取和写入。

以下是在BluetoothChat有关部分的源代码。

[BluetoothChatService.java]

/**
 * This thread runs during a connection with a remote device.
 * It handles all incoming and outgoing transmissions.
 */private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;     public ConnectedThread(BluetoothSocket socket) {
        Log.d(TAG, "create ConnectedThread");
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;         // Get the BluetoothSocket input and output streams
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            Log.e(TAG, "temp sockets not created", e);
        } 
        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }     public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;         // Keep listening to the InputStream while connected
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);                 // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "disconnected", e);
                connectionLost();
                break;
            }
        }
    }     /**
     * Write to the connected OutStream.
     * @param buffer  The bytes to write
     */
    public void write(byte[] buffer) {
        try {
            mmOutStream.write(buffer);             // Share the sent message back to the UI Activity
            mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
                    .sendToTarget();
        } catch (IOException e) {
            Log.e(TAG, "Exception during write", e);
        }
    }     public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "close() of connect socket failed", e);
        }
    }}

●检查操作
在模拟器上的,因为它不执行蓝牙功能的确认,您需要使用实际的设备做验证。

在这一次的远程端,我们使用那些有蓝牙适配器到您的台式PC的USB类型。您分配一个COM端口在蓝牙设置屏幕传入。在PC端的软件用于TeraTerm。在Android终端上的UUID安装BluetoothChat改变为一个用于SPP,并运行。

这里,出现问题。。。

它不杀尽快运行BluetoothChat开始。启用USB调试终端,进行真机调试,USB连接,onResume()被称为在启动的时候,我们发现当你调用启动的BluetoothChatService()方法,从他们中间那个秋天。

因为我不知道好(这似乎是实例的关系),但现在通过注释掉相应行正常工作。

[BluetoothChat.java]

@Override    public synchronized void onResume() {
        super.onResume();
        if(D) Log.e(TAG, "+ ON RESUME +");         // Performing this check in onResume() covers the case in which BT was
        // not enabled during onStart(), so we were paused to enable it...
        // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
        if (mChatService != null) {
            // Only if the state is STATE_NONE, do we know that we haven't started already
            if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
            	// Start the Bluetooth chat services
            //	mChatService.start();	
        }
    }

在启动后,运行从选项菜单中的“连接设备”,并连接到从列表中选择PC的主题。(对于在这种情况下,第一次,扫描设备运行)
连接到并连接到标题的右上角:我认为这是(PC名称)和显示。

输入在Android终端侧按“Android的蓝牙SPP测试”[发送]按钮,
确认已收到在PC端的字符串后,表示“OK(行)”当你进入屏幕以下。

尽管有改进,如下面被发现,就可以确认是否可以成功地发送和接收。
-分隔符并不在句子中的新行代码在传输时不添加坚持
和接收是不是已经进入每次在一行接一行的基础,并输出到日志中的字符(1字符在时间显示)

这个区域,我认为,可以通过查看阅读的地方()从实现和write()方法的ConnectedThread流得到改善。
此外,我认为这是通过查看TeraTerm屏幕注意到,但在PC端的波特率是相当快,921600bps。

以上,我想结束蓝牙SPP通信。