【问题标题】:How to read and write text data using Bluetooth plugin如何使用蓝牙插件读写文本数据
【发布时间】:2017-11-01 08:01:54
【问题描述】:

有人可以提供如何使用蓝牙cn1库读取和写入文本数据的示例代码吗?我尝试查看项目源代码 (https://github.com/chen-fishbein/bluetoothle-codenameone) 示例代码,但找不到使用适当方法读取/写入文本数据的任何代码。这些方法也没有任何 Java 文档。

【问题讨论】:

  • 我猜你对文件传输感兴趣,但蓝牙并不完全那样工作。一旦您连接到设备并与之形成协议,您就可以交换数据,我猜您将需要串行连接,但我自己不需要蓝牙专家,这取决于设备......请注意,您在API,您可以检测是否支持特定协议。
  • 我实际上只需要发送文本命令并从 BLE UART 适配器 (adafruit.com/product/2267) 读取响应。我面临的挑战是弄清楚要为字符串服务传递哪些参数,字符串特征?
  • 这是这个插件的一个分支,它更详细地介绍了各种 BT 选项及其值:github.com/randdusing/cordova-plugin-bluetoothle

标签: bluetooth codenameone


【解决方案1】:

这是我用来发送的代码:

public void sendMessage(String message) {
    if (Display.getInstance().isSimulator()) {
        System.out.println(message);
    } else {
        // System.out.println("Sending message: " + message);
        String b64WriteString = Base64.encode(message.getBytes());
        try {
            bt.write(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent evt) {

                }
            }, deviceAddress, services.get(0), sendDataCharacteristic, b64WriteString, false);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

在这里接收:

private void registerNotifications() {
    System.out.print("Registering notifications...");
    try {
        bt.subscribe(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                JSONObject dataIncoming = (JSONObject) evt.getSource();
                String base64Value = "";
                try {
                    if (dataIncoming.getString("status").equals("subscribedResult")) {
                        base64Value = dataIncoming.getString("value");
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                String message = new String(Base64.decode(base64Value.getBytes()));
                Display.getInstance().callSerially(new Runnable() {

                    @Override
                    public void run() {
                        messageReceived.set(message);
                    }
                });
            }
        }, deviceAddress, services.get(0), receiveDataCharacteristic);
        System.out.println("Registered");
        System.out.println("Starting communication");
    } catch (IOException e) {
        System.err.println("Unable to register notifications " + e.getMessage());
        e.printStackTrace();
    }
}

我为服务和特征 UUID 定义了字段。此外,可能不再需要 callSerally 了。我想我记得 CN1LIB 已更新为执行此操作,但我不记得了。

对于该设备,服务特征是“6E400001-B5A3-F393-E0A9-E50E24DCCA9E”

sendCharacteristic 是“0x0002”

receiveCharacteristic 为“0x0003”

【讨论】:

  • 我没有意识到必须对字符串使用 base64 编码。但我遇到的问题似乎与“未找到服务”有关,即使我使用此设备示例中的值 (github.com/don/cordova-plugin-ble-central/blob/master/examples/…)。这些也必须编码吗?
  • UUID 上没有编码。我建议安装 NRF 工具箱应用程序。这样,您应该能够看到设备正在宣传哪些服务。
【解决方案2】:

在采纳了 James H 的建议并尝试了一些错误之后,我终于设法让 Adafruit 的 Bluefruit LE Friend 之间的数据传输始终如一,至少在 Android 设备上是这样。不过不确定iOS,因为我还没有测试过。以下是所需的关键代码片段。

首先,您需要服务、TX 和 RX 特性 UUID。这些 UUID 被发现 here。请注意,这些不必是大写的。

public static final String UUID_SERVICE = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
public static final String UUID_RX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
public static final String UUID_TX = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";

接下来,扫描并找到设备后,调用 connect() 方法进行实际连接,并严格调用 discover() 方法。一旦调用了 discover() 回调,然后添加“订阅者”以接收数据。

private void connect(String address) {
    bleAddress = address; // set the global BLE address

    if (!connected) {
        // start an infinite progress dialog
        final Dialog ip = new InfiniteProgress().showInifiniteBlocking();

        try {
            bt.connect(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent evt) {
                    ip.dispose();

                    Object obj = evt.getSource();
                    print("Connected to Bluetooth LE device ...\n" + obj, true);
                    // must be called on Android devices in order to load on the UUIDs, otherwise there is an error that service can't be found. Won't do anything on ios though?
                    discover(); 

                    connected = true;
                }

            }, address);
        } catch (IOException ex) {
            ip.dispose();

            String message = "Error connecting to bluetooth device: " + address;
            print(message + "\n" + ex.getMessage(), false);
        }
    } else {
        String message = "BLE device already connected to: " + address;
        print(message, false);
    }
}

private void discover() {
    try {
        bt.discover(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                print("BLE Information Received ...", true);
                addSubscriber();
            }

        }, bleAddress);

    } catch (Exception ex) {
        print(ex.getMessage(), false);
    }

    // if we running on is add the subscriber here since the above bt call
    // does nothing?
    if (Display.getInstance().getPlatformName().equals("ios")) {
        print("Adding subscriber for iOS Device", true);
        addSubscriber();
    }
}

private void addSubscriber() {
    try {
        bt.subscribe(new ActionListener() {
            StringBuilder sb = new StringBuilder();

            @Override
            public void actionPerformed(ActionEvent evt) {
                JSONObject dataIncoming = (JSONObject) evt.getSource();
                String base64Value = "";
                try {
                    if (dataIncoming.getString("status").equals("subscribedResult")) {
                        base64Value = dataIncoming.getString("value");
                    }
                } catch (JSONException e) {
                    console.setText("Error reading data: " + e.getMessage());
                }

                String message = new String(Base64.decode(base64Value.getBytes()));
                sb.append(message);

                if (message.endsWith("\r\n")) {
                    processData(sb.toString());
                    sb = new StringBuilder();
                }
            }

        }, bleAddress, UUID_SERVICE, UUID_RX);

        String message = console.getText() + "\nSubcriber added ...";
        console.setText(message);
    } catch (IOException ex) {
        String message = "Error Subscribing: " + ex.getMessage();
        console.setText(message);
    }
}

所以这会建立连接,发现服务,最后添加接收数据的订阅者方法,并严格使用缓冲区收集接收到的数据,直到接收到 CRLF 字符。

但是,我遇到的另一个主要问题是 BLE 规范的默认 23 字节发送限制(可能是 Android 独有的问题?)。如果您尝试发送更多信息,则连接将被断开,并且不会返回任何有意义的错误消息。为了解决这个问题,我使用了建议的技术here,它需要将数据拆分为 20 字节数组的块。由于我们发送常规 ASCII 文本,那么 20 个字符应该是 20 个字节,所以我只是将文本拆分为 20 个字符长的字符串。它不是最有效的,而且更容易调试。

private void sendText(final String data) {
    try {
        String b64String = Base64.encode(data.getBytes());

        bt.write(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                if(data.endsWith("\r\n")) {
                    print("Data sent ...", true);
                }
            }

        }, bleAddress, UUID_SERVICE, UUID_TX, b64String, false);
    } catch (IOException ex) {
        String message = "Error sending: " + data + "\n"
                + UUID_SERVICE + "\n"
                + UUID_TX + "\n"
                + ex.getMessage();
        print(message, false);
    }
}

private void splitAndSend(String text) {
    text += "\r\n";

    // first split data in chunk size of 20 chracters
    ArrayList<String> sl = new ArrayList<>();

    char[] data = text.toCharArray();       
    int len = data.length;
    int chunkSize = 20;

    for (int i=0; i < len; i+= chunkSize) {
        sl.add(new String(data, i, Math.min(chunkSize,len - i)));
    }

    // now send chunks amd wait 100 ms to prevent any erros
    for(String word: sl) {
        sendText(word);
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {}
    }
}

可以在here 找到带有 GUI 内容的完整源代码,但这绝对是一项正在进行的工作。

【讨论】:

  • 我发现的其他几件事:您肯定想在退出应用程序之前断开然后关闭 BT 连接。此外,Android 似乎需要 15 秒才能意识到发生了断开连接(尤其是在远程设备断开连接的情况下)。如果您在此期间尝试再次连接,整个 BLE 堆栈可能会卡住。我在尝试重新连接之前延迟了 20 秒以避免这种情况。哦,我总是使用 Connect 而不是 Reconnect,因为后者在 Android 上对我来说效果不佳。
猜你喜欢
  • 1970-01-01
  • 2020-02-17
  • 1970-01-01
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-06
相关资源
最近更新 更多