通过蓝牙将多个设备连接到一个设备上

30

我想创建一个游戏,需要通过 Bluetooth 连接多个设备(4个或以上)到一个主设备(例如平板电脑)。将有两个应用程序,一个主应用程序将从手机接收所有数据,另一个则向手机发送数据。

这是否可行?


你能让它正常工作了吗? - Jake
可以使用这个库实现:https://github.com/arissa34/Android-Multi-Bluetooth-Library - Rami
有没有类似的iOS库? - chikitin
7个回答

19

是的,这是可能的。在其最低层面上,蓝牙允许您将最多7个设备连接到一个主设备。我已经尝试过,并且在其他平台(Linux)上,我有很多手动控制,效果非常好- 但我从未在Android上尝试过,并且可能存在一些问题,因此您需要进行一些测试以确保。

其中一个问题是您需要将平板电脑设置为主设备,而Android不会给您任何明确的控制权。这可能不是问题,因为 * 当您尝试将第二个设备连接到平板电脑时,平板电脑会自动成为主设备,或者 * 您将能够通过设置套接字连接来控制主/从角色

但我要警告的是,大多数在移动设备上使用蓝牙的应用程序并没有尝试许多同时连接,并且蓝牙可能有点脆弱,例如,如果两个设备已经为某些其他应用程序建立了蓝牙连接-那么这如何影响角色?


7

蓝牙4.0允许在蓝牙个人局域网中,一个主设备可以与最多7个从设备进行通信,并且最多支持其他248个睡眠设备。

同时,您还可以使用一些从设备作为桥接器,以参与更多的设备。


3
是的,您可以这样做,我已经创建了一个库来实现这个功能。这个库叫做Androble
使用这个库可以让您将最多四个设备连接到主服务器设备上,并为每个客户端创建不同的通道,在不同的线程上运行交互。
要使用这个库,只需在您的build.gradle的依赖部分添加com.mdg.androble:library:0.1.2即可。

2
我已经下载了你的库,但是在网络包中有一些编译问题。 - eyal

1
这是建立连接并接收消息的类。 在运行应用程序之前,请确保配对设备。 如果您想要一个从/主连接,其中每个从只能向主发送消息,并且主可以向所有从广播消息。 您应该只将主设备与每个从设备配对,但不应该将从设备彼此配对。
    package com.example.gaby.coordinatorv1;
    import java.io.DataInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Set;
    import java.util.UUID;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothServerSocket;
    import android.bluetooth.BluetoothSocket;
    import android.content.Context;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.Toast;

    public class Piconet {


        private final static String TAG = Piconet.class.getSimpleName();

        // Name for the SDP record when creating server socket
        private static final String PICONET = "ANDROID_PICONET_BLUETOOTH";

        private final BluetoothAdapter mBluetoothAdapter;

        // String: device address
        // BluetoothSocket: socket that represent a bluetooth connection
        private HashMap<String, BluetoothSocket> mBtSockets;

        // String: device address
        // Thread: thread for connection
        private HashMap<String, Thread> mBtConnectionThreads;

        private ArrayList<UUID> mUuidList;

        private ArrayList<String> mBtDeviceAddresses;

        private Context context;


        private Handler handler = new Handler() {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1:
                        Toast.makeText(context, msg.getData().getString("msg"), Toast.LENGTH_SHORT).show();
                        break;
                    default:
                        break;
                }
            };
        };

        public Piconet(Context context) {
            this.context = context;

            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

            mBtSockets = new HashMap<String, BluetoothSocket>();
            mBtConnectionThreads = new HashMap<String, Thread>();
            mUuidList = new ArrayList<UUID>();
            mBtDeviceAddresses = new ArrayList<String>();

            // Allow up to 7 devices to connect to the server
            mUuidList.add(UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c666"));
            mUuidList.add(UUID.fromString("54d1cc90-1169-11e2-892e-0800200c9a66"));
            mUuidList.add(UUID.fromString("6acffcb0-1169-11e2-892e-0800200c9a66"));
            mUuidList.add(UUID.fromString("7b977d20-1169-11e2-892e-0800200c9a66"));
            mUuidList.add(UUID.fromString("815473d0-1169-11e2-892e-0800200c9a66"));
            mUuidList.add(UUID.fromString("503c7434-bc23-11de-8a39-0800200c9a66"));
            mUuidList.add(UUID.fromString("503c7435-bc23-11de-8a39-0800200c9a66"));

            Thread connectionProvider = new Thread(new ConnectionProvider());
            connectionProvider.start();

        }



        public void startPiconet() {
            Log.d(TAG, " -- Looking devices -- ");
            // The devices must be already paired
            Set<BluetoothDevice> pairedDevices = mBluetoothAdapter
                    .getBondedDevices();
            if (pairedDevices.size() > 0) {
                for (BluetoothDevice device : pairedDevices) {
                    // X , Y and Z are the Bluetooth name (ID) for each device you want to connect to
                    if (device != null && (device.getName().equalsIgnoreCase("X") || device.getName().equalsIgnoreCase("Y")
                            || device.getName().equalsIgnoreCase("Z") || device.getName().equalsIgnoreCase("M"))) {
                        Log.d(TAG, " -- Device " + device.getName() + " found --");
                        BluetoothDevice remoteDevice = mBluetoothAdapter
                                .getRemoteDevice(device.getAddress());
                        connect(remoteDevice);
                    }
                }
            } else {
                Toast.makeText(context, "No paired devices", Toast.LENGTH_SHORT).show();
            }
        }

        private class ConnectionProvider implements Runnable {
            @Override
            public void run() {
                try {
                    for (int i=0; i<mUuidList.size(); i++) {
                        BluetoothServerSocket myServerSocket = mBluetoothAdapter
                                .listenUsingRfcommWithServiceRecord(PICONET, mUuidList.get(i));
                        Log.d(TAG, " ** Opened connection for uuid " + i + " ** ");

                        // This is a blocking call and will only return on a
                        // successful connection or an exception
                        Log.d(TAG, " ** Waiting connection for socket " + i + " ** ");
                        BluetoothSocket myBTsocket = myServerSocket.accept();
                        Log.d(TAG, " ** Socket accept for uuid " + i + " ** ");
                        try {
                            // Close the socket now that the
                            // connection has been made.
                            myServerSocket.close();
                        } catch (IOException e) {
                            Log.e(TAG, " ** IOException when trying to close serverSocket ** ");
                        }

                        if (myBTsocket != null) {
                            String address = myBTsocket.getRemoteDevice().getAddress();

                            mBtSockets.put(address, myBTsocket);
                            mBtDeviceAddresses.add(address);

                            Thread mBtConnectionThread = new Thread(new BluetoohConnection(myBTsocket));
                            mBtConnectionThread.start();

                            Log.i(TAG," ** Adding " + address + " in mBtDeviceAddresses ** ");
                            mBtConnectionThreads.put(address, mBtConnectionThread);
                        } else {
                            Log.e(TAG, " ** Can't establish connection ** ");
                        }
                    }
                } catch (IOException e) {
                    Log.e(TAG, " ** IOException in ConnectionService:ConnectionProvider ** ", e);
                }
            }
        }

        private class BluetoohConnection implements Runnable {
            private String address;

            private final InputStream mmInStream;

            public BluetoohConnection(BluetoothSocket btSocket) {

                InputStream tmpIn = null;

                try {
                    tmpIn = new DataInputStream(btSocket.getInputStream());
                } catch (IOException e) {
                    Log.e(TAG, " ** IOException on create InputStream object ** ", e);
                }
                mmInStream = tmpIn;
            }
            @Override
            public void run() {
                byte[] buffer = new byte[1];
                String message = "";
                while (true) {

                    try {
                        int readByte = mmInStream.read();
                        if (readByte == -1) {
                            Log.e(TAG, "Discarting message: " + message);
                            message = "";
                            continue;
                        }
                        buffer[0] = (byte) readByte;

                        if (readByte == 0) { // see terminateFlag on write method
                            onReceive(message);
                            message = "";
                        } else { // a message has been recieved
                            message += new String(buffer, 0, 1);
                        }
                    } catch (IOException e) {
                        Log.e(TAG, " ** disconnected ** ", e);
                    }

                    mBtDeviceAddresses.remove(address);
                    mBtSockets.remove(address);
                    mBtConnectionThreads.remove(address);
                }
            }
        }

        /**
         * @param receiveMessage
         */
        private void onReceive(String receiveMessage) {
            if (receiveMessage != null && receiveMessage.length() > 0) {
                Log.i(TAG, " $$$$ " + receiveMessage + " $$$$ ");
                Bundle bundle = new Bundle();
                bundle.putString("msg", receiveMessage);
                Message message = new Message();
                message.what = 1;
                message.setData(bundle);
                handler.sendMessage(message);
            }
        }

        /**
         * @param device
         * @param uuidToTry
         * @return
         */
        private BluetoothSocket getConnectedSocket(BluetoothDevice device, UUID uuidToTry) {
            BluetoothSocket myBtSocket;
            try {
                myBtSocket = device.createRfcommSocketToServiceRecord(uuidToTry);
                myBtSocket.connect();
                return myBtSocket;
            } catch (IOException e) {
                Log.e(TAG, "IOException in getConnectedSocket", e);
            }
            return null;
        }

        private void connect(BluetoothDevice device) {
            BluetoothSocket myBtSocket = null;
            String address = device.getAddress();
            BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(address);
            // Try to get connection through all uuids available
            for (int i = 0; i < mUuidList.size() && myBtSocket == null; i++) {
                // Try to get the socket 2 times for each uuid of the list
                for (int j = 0; j < 2 && myBtSocket == null; j++) {
                    Log.d(TAG, " ** Trying connection..." + j + " with " + device.getName() + ", uuid " + i + "...** ");
                    myBtSocket = getConnectedSocket(remoteDevice, mUuidList.get(i));
                    if (myBtSocket == null) {
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            Log.e(TAG, "InterruptedException in connect", e);
                        }
                    }
                }
            }
            if (myBtSocket == null) {
                Log.e(TAG, " ** Could not connect ** ");
                return;
            }
            Log.d(TAG, " ** Connection established with " + device.getName() +"! ** ");
            mBtSockets.put(address, myBtSocket);
            mBtDeviceAddresses.add(address);
            Thread mBluetoohConnectionThread = new Thread(new BluetoohConnection(myBtSocket));
            mBluetoohConnectionThread.start();
            mBtConnectionThreads.put(address, mBluetoohConnectionThread);

        }

        public void bluetoothBroadcastMessage(String message) {
            //send message to all except Id
            for (int i = 0; i < mBtDeviceAddresses.size(); i++) {
                sendMessage(mBtDeviceAddresses.get(i), message);
            }
        }

        private void sendMessage(String destination, String message) {
            BluetoothSocket myBsock = mBtSockets.get(destination);
            if (myBsock != null) {
                try {
                    OutputStream outStream = myBsock.getOutputStream();
                    final int pieceSize = 16;
                    for (int i = 0; i < message.length(); i += pieceSize) {
                        byte[] send = message.substring(i,
                                Math.min(message.length(), i + pieceSize)).getBytes();
                        outStream.write(send);
                    }
                    // we put at the end of message a character to sinalize that message
                    // was finished
                    byte[] terminateFlag = new byte[1];
                    terminateFlag[0] = 0; // ascii table value NULL (code 0)
                    outStream.write(new byte[1]);
                } catch (IOException e) {
                    Log.d(TAG, "line 278", e);
                }
            }
        }

    }

你的主要活动应该如下所示:

package com.example.gaby.coordinatorv1;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {

    private Button discoveryButton;
    private Button messageButton;

    private Piconet piconet;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        piconet = new Piconet(getApplicationContext());

        messageButton = (Button) findViewById(R.id.messageButton);
        messageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                piconet.bluetoothBroadcastMessage("Hello World---*Gaby Bou Tayeh*");
            }
        });

        discoveryButton = (Button) findViewById(R.id.discoveryButton);
        discoveryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                piconet.startPiconet();
            }
        });

    }

}

这是XML布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >

<Button
    android:id="@+id/discoveryButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Discover"
    />

<Button
    android:id="@+id/messageButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Send message"
    />

不要忘记将以下权限添加到您的清单文件中:
    <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

嗨,我需要用蓝牙设备(BLE_V4)将我的安卓手机连接到5个arduino板子上,您的代码能否支持这种方式?目前我只将手机配对了一个BLE V4并且可以传输数据。谢谢。 - Billyjoker

0

我认为如果是串行数据广播方法,这是可能的。但您将无法将任何语音/音频数据传输到其他从设备。根据蓝牙4.0协议,该协议不支持此功能。然而,目前正在进行改进以广播音频/语音数据。


-1

我认为使用蓝牙不可能实现,但你可以尝试研究一下WiFi Peer-to-Peer
它允许一对多的连接。


据我所知,您不需要路由器。我认为它会将其中一个设备变成热点(当然没有互联网访问),并允许其他设备连接到它。 - Andreas
我的意思是其他人没有权限使用你的互联网。 - Andreas
我不确定p2p wifi的最大连接数量是多少,但是一旦您超过一定数量的连接,我相信使用服务器会是一个更好的选择。 - Andreas
我已经进行了测试。两个连接的设备都可以通过WIFI轻松地上网。然而,出于某种原因,尽管我在互联网上读到Wifi直连不需要活动WIFI,但我无法禁用WIFI并启用WIFI直连。 - android developer
1
它已经存在了。Superbeam使用它来设置WiFi-direct以交换文件。Galaxy手机有S-Beam,如果我没记错的话,它也使用NFC来建立WiFi-direct连接以交换文件。请注意,这与使用NFC建立蓝牙连接以交换文件的Android beam不同,后者速度略慢。 - Andreas
显示剩余16条评论

-1

这在一定程度上是可能的(最多2个设备),因为设备同一时间只能连接到另一个设备。在您的情况下,更好的解决方案是创建一个TCP服务器,向其他设备发送信息 - 但是,这当然需要互联网连接。还可以阅读有关三星Chord API的信息 - 它提供了您所需的功能,但是每个设备都必须连接到同一个Wi-Fi网络。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接