显示附近蓝牙设备

This commit is contained in:
fengjignqi
2026-06-04 08:49:28 +08:00
parent 20a08e9b61
commit 0f9c8c7329
3 changed files with 269 additions and 21 deletions

View File

@@ -230,6 +230,8 @@ dependencies {
implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0' implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0'
//蓝牙通讯 //蓝牙通讯
implementation 'no.nordicsemi.android:ble:2.11.0' implementation 'no.nordicsemi.android:ble:2.11.0'
//espressif官方sdk用于esp固件的扫描配网等。
implementation 'com.github.espressif:esp-idf-provisioning-android:lib-2.2.3'
} }

View File

@@ -41,6 +41,7 @@ import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.network.BaseBean; import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.manager.ActivityManager; import com.ckkj.water.utils.manager.ActivityManager;
import com.ckkj.water.utils.manager.ThemeManager; import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.manager.WaterDeviceManager;
import com.ckkj.water.utils.show.ToastUtil; import com.ckkj.water.utils.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar; import com.gyf.immersionbar.ImmersionBar;
import com.maning.mlkitscanner.scan.MNScanManager; import com.maning.mlkitscanner.scan.MNScanManager;
@@ -48,12 +49,14 @@ import com.maning.mlkitscanner.scan.callback.act.MNScanCallback;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import no.nordicsemi.android.ble.observer.ConnectionObserver;
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat; import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
import no.nordicsemi.android.support.v18.scanner.ScanCallback; import no.nordicsemi.android.support.v18.scanner.ScanCallback;
import no.nordicsemi.android.support.v18.scanner.ScanFilter; import no.nordicsemi.android.support.v18.scanner.ScanFilter;
@@ -80,6 +83,7 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
private BleScanDeviceAdapter bleAdapter; private BleScanDeviceAdapter bleAdapter;
private List<WaterDeviceEntity> mDeviceList = new ArrayList<>(); private List<WaterDeviceEntity> mDeviceList = new ArrayList<>();
private Set<String> discoveredMacSet = new HashSet<>(); private Set<String> discoveredMacSet = new HashSet<>();
private WaterDeviceManager waterDeviceManager;
@Override @Override
public DevicePresenter createPresenter() { public DevicePresenter createPresenter() {
@@ -189,8 +193,160 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
} }
}; };
scanner.startScan(null, settings, mScanCallback); scanner.startScan(null, settings, mScanCallback);
}
/**
* 停止 BLE 扫描
*/
private void stopBleScan() {
if (mScanCallback != null) {
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
scanner.stopScan(mScanCallback);
Log.e("TEST", "停止扫描");
}
}
/**
* 连接蓝牙设备
*/
private void connectToDevice(String deviceAddress, String deviceName) {
if (waterDeviceManager != null) {
// 断开之前的连接
waterDeviceManager.disconnect().enqueue();
waterDeviceManager.close();
}
// 根据地址获取 BluetoothDevice
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
if (device == null) {
ToastUtil.showShort(mContext, "设备无效");
return;
}
waterDeviceManager = new WaterDeviceManager(mContext);
// 设置数据回调
waterDeviceManager.setOnDataReceivedListener(bytes -> {
Log.e("TEST", "设备响应: " + Arrays.toString(bytes));
runOnUiThread(() -> {
// TODO: 处理设备返回的数据,比如 WiFi 配置响应
handleDeviceResponse(bytes);
});
});
// 连接设备
waterDeviceManager.connect(device)
.retry(3, 100) // 重试3次间隔100ms
.done(bluetoothDevice -> {
Log.e("TEST", "连接成功: " + deviceName);
runOnUiThread(() -> {
ToastUtil.showShort(mContext, "连接成功: " + deviceName);
// TODO: 连接成功后,发送 WiFi 配置
// sendWifiConfig("SSID", "PASSWORD");
});
})
.fail((bluetoothDevice, status) -> {
Log.e("TEST", "连接失败: " + status);
runOnUiThread(() -> {
ToastUtil.showShort(mContext, "连接失败,请重试");
});
})
.enqueue();
waterDeviceManager.setConnectionObserver(new ConnectionObserver() {
@Override
public void onDeviceConnecting(@NonNull BluetoothDevice device) {
Log.e("TEST", "onDeviceConnecting");
}
@Override
public void onDeviceConnected(@NonNull BluetoothDevice device) {
Log.e("TEST", "onDeviceConnected");
}
@Override
public void onDeviceFailedToConnect(@NonNull BluetoothDevice device, int reason) {
Log.e("TEST", "onDeviceFailedToConnect");
}
@Override
public void onDeviceReady(@NonNull BluetoothDevice device) {
Log.e("TEST", "onDeviceReady");
}
@Override
public void onDeviceDisconnecting(@NonNull BluetoothDevice device) {
Log.e("TEST", "onDeviceDisconnecting");
}
@Override
public void onDeviceDisconnected(
@NonNull BluetoothDevice device,
int reason) {
Log.e("TEST", "onDeviceDisconnected reason=" + reason);
}
});
}
/**
* 发送 WiFi 配置到设备
*/
public void sendWifiConfig(String ssid, String password) {
if (waterDeviceManager == null || !waterDeviceManager.isBlConnected()) {
Log.e("TEST", "设备未连接无法发送WiFi配置");
return;
}
// TODO: 根据设备协议组装数据
// 示例:假设协议是 [header(1) + ssid_len(1) + ssid(n) + pwd_len(1) + pwd(m)]
byte[] ssidBytes = ssid.getBytes();
byte[] pwdBytes = password.getBytes();
byte[] data = new byte[2 + ssidBytes.length + 2 + pwdBytes.length];
int index = 0;
data[index++] = 0x01; // header
data[index++] = (byte) ssidBytes.length;
System.arraycopy(ssidBytes, 0, data, index, ssidBytes.length);
index += ssidBytes.length;
data[index++] = (byte) pwdBytes.length;
System.arraycopy(pwdBytes, 0, data, index, pwdBytes.length);
waterDeviceManager.send(data);
Log.e("TEST", "发送WiFi配置: SSID=" + ssid + ", PWD=" + password);
}
/**
* 处理设备响应数据
*/
private void handleDeviceResponse(byte[] data) {
if (data == null || data.length == 0) {
return;
}
// TODO: 根据设备协议解析响应数据
// 示例:假设响应格式是 [type(1) + status(1) + ...]
Log.e("TEST", "处理设备响应: " + Arrays.toString(data));
if (data.length >= 2) {
byte type = data[0];
byte status = data[1];
switch (type) {
case 0x01: // WiFi配置响应
if (status == 0x00) {
ToastUtil.showShort(mContext, "WiFi配置成功");
// TODO: 下一步操作
} else {
ToastUtil.showShort(mContext, "WiFi配置失败错误码: " + status);
}
break;
default:
Log.e("TEST", "未知响应类型: " + type);
break;
}
}
} }
/** /**
@@ -260,9 +416,18 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
bleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { bleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override @Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) { public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
ToastUtil.showShort(mContext, TextUtils.isEmpty(mDeviceList.get(position).getDeviceName()) ? WaterDeviceEntity device = mDeviceList.get(position);
"未知设备" : mDeviceList.get(position).getDeviceName()); String deviceName = TextUtils.isEmpty(device.getDeviceName()) ?
//TODO 准备连接设备,方案未定 "未知设备" : device.getDeviceName();
String deviceAddress = device.getMacAddress();
ToastUtil.showShort(mContext, "正在连接: " + deviceName);
// 停止扫描
stopBleScan();
// 连接设备
connectToDevice(deviceAddress, deviceName);
} }
}); });
} }
@@ -433,9 +598,13 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (mScanCallback != null) { // 停止扫描
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner(); stopBleScan();
scanner.stopScan(mScanCallback); // 断开蓝牙连接
if (waterDeviceManager != null) {
waterDeviceManager.disconnect().enqueue();
waterDeviceManager.close();
waterDeviceManager = null;
} }
} }
} }

View File

@@ -8,6 +8,8 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.ckkj.water.AppConfig;
import java.util.Arrays; import java.util.Arrays;
import java.util.UUID; import java.util.UUID;
@@ -21,11 +23,20 @@ import no.nordicsemi.android.ble.BleManager;
public class WaterDeviceManager extends BleManager { public class WaterDeviceManager extends BleManager {
private BluetoothGattCharacteristic writeCharacteristic; private BluetoothGattCharacteristic writeCharacteristic;
private BluetoothGattCharacteristic notifyCharacteristic; private BluetoothGattCharacteristic notifyCharacteristic;
private OnDataReceivedListener dataListener;
public interface OnDataReceivedListener {
void onDataReceived(byte[] data);
}
public WaterDeviceManager(@NonNull Context context) { public WaterDeviceManager(@NonNull Context context) {
super(context); super(context);
} }
public void setOnDataReceivedListener(OnDataReceivedListener listener) {
this.dataListener = listener;
}
@NonNull @NonNull
@Override @Override
protected BleManagerGattCallback getGattCallback() { protected BleManagerGattCallback getGattCallback() {
@@ -39,35 +50,88 @@ public class WaterDeviceManager extends BleManager {
setNotificationCallback(notifyCharacteristic) setNotificationCallback(notifyCharacteristic)
.with((device, data) -> { .with((device, data) -> {
byte[] bytes = data.getValue(); byte[] bytes = data.getValue();
Log.e("TEST", "收到数据: " + Arrays.toString(bytes));
Log.e("TEST", "收到数据: " if (dataListener != null) {
+ Arrays.toString(bytes)); dataListener.onDataReceived(bytes);
}
}); });
enableNotifications(notifyCharacteristic) enableNotifications(notifyCharacteristic)
.enqueue(); .enqueue();
} }
// @Override
// public boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) {
//
// BluetoothGattService service =
// gatt.getService(UUID.fromString(
// AppConfig.BLUETOOTH_DEVICE_UUID));
//
// if (service == null) {
// return false;
// }
//
// writeCharacteristic =
// service.getCharacteristic(UUID.fromString(
// AppConfig.BLUETOOTH_DEVICE_UUID));
//
// notifyCharacteristic =
// service.getCharacteristic(UUID.fromString(
// AppConfig.BLUETOOTH_DEVICE_UUID));
//
// return writeCharacteristic != null
// && notifyCharacteristic != null;
// }
@Override @Override
public boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) { public boolean isRequiredServiceSupported(
@NonNull BluetoothGatt gatt) {
BluetoothGattService service = for (BluetoothGattService service :
gatt.getService(UUID.fromString( gatt.getServices()) {
"0000ffff-0000-1000-8000-00805f9b34fb"));
if (service == null) { Log.e("TEST",
return false; "Service=" + service.getUuid());
for (BluetoothGattCharacteristic c :
service.getCharacteristics()) {
int properties = c.getProperties();
Log.e("TEST",
"Characteristic="
+ c.getUuid()
+ " props="
+ properties);
if ((properties &
BluetoothGattCharacteristic.PROPERTY_WRITE)
!= 0) {
writeCharacteristic = c;
} }
writeCharacteristic = if ((properties &
service.getCharacteristic(UUID.fromString( BluetoothGattCharacteristic.PROPERTY_NOTIFY)
"0000ff01-0000-1000-8000-00805f9b34fb")); != 0) {
notifyCharacteristic = notifyCharacteristic = c;
service.getCharacteristic(UUID.fromString( }
"0000ff02-0000-1000-8000-00805f9b34fb")); }
}
Log.e("TEST",
"write="
+ (writeCharacteristic != null
? writeCharacteristic.getUuid()
: "null"));
Log.e("TEST",
"notify="
+ (notifyCharacteristic != null
? notifyCharacteristic.getUuid()
: "null"));
return writeCharacteristic != null return writeCharacteristic != null
&& notifyCharacteristic != null; && notifyCharacteristic != null;
@@ -83,10 +147,23 @@ public class WaterDeviceManager extends BleManager {
public void send(byte[] data) { public void send(byte[] data) {
if (writeCharacteristic == null) { if (writeCharacteristic == null) {
Log.e("TEST", "send: Write characteristic is null");
return; return;
} }
writeCharacteristic(writeCharacteristic, data) writeCharacteristic(writeCharacteristic, data)
.enqueue(); .enqueue();
} }
public boolean isBlConnected() {
// 通过反射或状态检查来判断是否已连接
// BleManager 没有直接的 isConnected 方法,需要检查内部状态
try {
// 检查 writeCharacteristic 是否可用(连接后会初始化)
return writeCharacteristic != null;
} catch (Exception e) {
return false;
}
}
} }