在采纳了 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 内容的完整源代码,但这绝对是一项正在进行的工作。