You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android App点击按钮向ESP32写入BLE特征值失败问题

Android BLE向ESP32写入数据失败问题排查

问题背景

开发的Android应用可正常接收ESP32返回的BLE数据(示例格式:UPS%12345678%223%012.8%000%223%00.0%04%1%001%13.5%00.0%00.3%00.0%00.4%000.0%000.0%END),但点击按钮向ESP32写入构造字符串(如builderList.get(1).toString() + "%" + " ON" + "%" + "END")时,数据始终无法传输成功。

相关核心代码:

public class MainScreen extends AppCompatActivity implements Serializable, Observer {
    BluetoothAdapter mBluetoothAdapter;

    public static final String Tag="DetailBle";
    boolean MAINSSTATUS;
    boolean SOLARSTATUS;
    private static boolean UPSSTATUS;
    boolean BATTERYFULL;
    String CHARGING_STATUS,CHARGING_LABEL;
    View view;
    LinearLayout statusLayout;
    int psckecked;
    Alarms alarms;

    List<BluetoothGattCharacteristic> characteristicsList = new ArrayList<>();
    List<BluetoothGattService> servicesList;
    BleDevice bleDevice;
    BluetoothGattService service;
    List<StringBuilder> builderList;

    // UI组件定义
    LinearLayout solarArrowLoad, solarArrowBattery, gridArrowLoad, gridArrowBattery,batteryArrowLoad;
    TextView solarToloadP,solarToBatteryCur;
    TextView solarCurrent,batteryVolt,batteryCurrent,SRnumber, gridVolt,loadPersent;
    TextView connetionStatus;
    Button upsStatus,upsMode,highCharging,powerSaving;
    ImageView solarPanel,battery,homeLoad,supply;

    int writeFlag;
    ClickDialog clickDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail_ble);
        ObserverManager.Companion.getInstance().addObserver(this);
        mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();

        BleDevice bleDevice1 = getIntent().getParcelableExtra("bleDevice");
        if (bleDevice1 != null){
            Log.d("","javed"+bleDevice1.getName());
            start(bleDevice1);
        } else {
            Toast.makeText(this, "yaha dikaat hai ky  bhai", Toast.LENGTH_SHORT).show();
        }
    }

    void start(BleDevice bleDevice1){
        bleDevice=bleDevice1;
        Log.d("","oldhai"+bleDevice.getName());

        if (bleDevice!=null){
            if (BleManager.getInstance().isConnected(bleDevice)){
                initView();
                setData();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
                write("hello sir");
                Log.d("Tag","msg"+write("i am coder"));
                Log.d("Tag","msg"+write("i am coder"));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
                indicate();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
                setMTU();
            } else {
                Toast.makeText(this, "Device is not connected", Toast.LENGTH_SHORT).show();
                Log.d(Tag+"onCreate","Device id not connected");
            }
        }
        setConnectivityStatus();
    }

    @Override
    protected void onStart() { super.onStart(); }
    @Override
    protected void onResume() { super.onResume(); }

    private void setConnectivityStatus() {
        if (getConnectivityStatus()) {
            connetionStatus.setText(getText(R.string.connected));
            Log.d(Tag,"Connected");
        } else {
            connetionStatus.setText(R.string.active_disconnected);
            Log.d(Tag,"Disconnected");
        }
    }

    private boolean getConnectivityStatus() {
        return BleManager.getInstance().isConnected(bleDevice);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (bleDevice!=null){
            BleManager.getInstance().disconnect(bleDevice);
            BleManager.getInstance().destroy();
        }
        stopNotification();
    }

    private void initView() {
        servicesList = new ArrayList<>();
        builderList= new ArrayList<>();
        connetionStatus=findViewById(R.id.connected_status);
        solarToloadP=findViewById(R.id.solar_to_load_power);
        solarToBatteryCur=findViewById(R.id.solar_to_battery_current);
        SRnumber = findViewById(R.id.serialNO);
        gridVolt =findViewById(R.id.ip_volt);
        loadPersent=findViewById(R.id.load_presentge);
        batteryVolt=findViewById(R.id.battery_volt);
        batteryCurrent=findViewById(R.id.battrery_to_load_power);
        upsStatus=findViewById(R.id.ups_status);
        upsMode=findViewById(R.id.ups_mode);
        highCharging=findViewById(R.id.high_charging);
        powerSaving=findViewById(R.id.power_saving);
        solarPanel=findViewById(R.id.solar_panel);
        supply=findViewById(R.id.supply);
        battery=findViewById(R.id.battery);
        homeLoad=findViewById(R.id.load);
        gridArrowLoad =findViewById(R.id.grid_arroweup);
        gridArrowBattery =findViewById(R.id.grid_arrowdown);
        solarArrowLoad =findViewById(R.id.solar_arrow_up);
        solarArrowBattery =findViewById(R.id.solar_arrow_mid);
        batteryArrowLoad =findViewById(R.id.battery_to_load);
    }

    private void setData() {
        BluetoothGatt gatt = BleManager.getInstance().getBluetoothGatt(bleDevice);
        alarms=new Alarms(MainScreen.this);
        clickDialog = new ClickDialog(MainScreen.this);
        view =getWindow().getDecorView().findViewById(android.R.id.content);
        for (BluetoothGattService service : gatt.getServices()) {
            servicesList.add(service);
        }
        service = servicesList.get(2);
        characteristicsList.addAll(service.getCharacteristics());

        upsStatus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!builderList.isEmpty()) {
                    if (upsStatus.getText() == getString(R.string.ups_on)) {
                        String input = builderList.get(1).toString() + "%" + "OFF" + "%" + "END";
                        Log.d("","upsss"+builderList.subList(1,10));
                        int status = write(input);
                        if (status == 1)
                            upsStatus.setText(getString(R.string.ups_off));
                        else
                            Toast.makeText(MainScreen.this, "Operation Fail try again",
                                    Toast.LENGTH_SHORT).show();
                    } else {
                        String input = builderList.get(1).toString() + "%" + " ON" + "%" + "END";
                        int status = write(input);
                        if (status == 1)
                            upsStatus.setText(getString(R.string.ups_on));
                        else
                            Toast.makeText(MainScreen.this, "Operation Fail try again",
                                    Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });
    }

    // 缺失的write、indicate、setMTU、stopNotification方法实现
    private int write(String input) {
        // 待实现
        return 0;
    }

    private void indicate() { /* 待实现 */ }
    private void setMTU() { /* 待实现 */ }
    private void stopNotification() { /* 待实现 */ }
}

排查与修复步骤

1. 修正字符串对比逻辑

代码中使用==对比按钮文本与字符串资源,会因对象引用不同导致判断错误,替换为内容对比:

// 原错误代码
if (upsStatus.getText() == getString(R.string.ups_on)) { ... }

// 修正后
if (getString(R.string.ups_on).equals(upsStatus.getText().toString())) { ... }

2. 确认写入特征值的属性

确保选中的BLE特征值支持写入操作,遍历服务下的特征值并筛选可写入项:

// 在setData方法中添加特征值属性检查
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
    int properties = characteristic.getProperties();
    if ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0 ||
        (properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0) {
        Log.d(Tag, "可写入特征UUID: " + characteristic.getUuid());
        // 记录该特征值,后续写入使用
    }
}

必须使用ESP32端配置为接收写入的特征UUID,不能直接依赖servicesList.get(2)的索引。

3. 完善write方法实现

实现标准的BLE写入逻辑,确保数据编码与ESP32端一致:

private int write(String input) {
    if (bleDevice == null || !BleManager.getInstance().isConnected(bleDevice)) {
        Log.d(Tag, "设备未连接");
        return -1;
    }
    BluetoothGatt gatt = BleManager.getInstance().getBluetoothGatt(bleDevice);
    if (gatt == null) {
        Log.d(Tag, "GATT实例为空");
        return -1;
    }
    // 找到可写入的特征值(建议通过UUID匹配,而非索引)
    BluetoothGattCharacteristic writeChar = null;
    for (BluetoothGattCharacteristic chara : characteristicsList) {
        int prop = chara.getProperties();
        if ((prop & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) {
            writeChar = chara;
            break;
        }
    }
    if (writeChar == null) {
        Log.d(Tag, "未找到可写入特征值");
        return -1;
    }
    // 设置数据(UTF-8编码,需与ESP32端一致)
    writeChar.setValue(input.getBytes(StandardCharsets.UTF_8));
    // 执行写入,WRITE_TYPE_DEFAULT需要设备应答
    boolean success = gatt.writeCharacteristic(writeChar);
    return success ? 1 : 0;
}

4. 监听写入结果回调

在BleManager的BluetoothGattCallback中实现写入回调,确认操作是否成功:

@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    super.onCharacteristicWrite(gatt, characteristic, status);
    if (status == BluetoothGatt.GATT_SUCCESS) {
        Log.d(Tag, "数据写入成功");
        // 可在此更新UI状态,无需依赖write方法的返回值
    } else {
        Log.d(Tag, "写入失败,状态码: " + status);
        // 状态码含义:GATT_FAILURE(133)为通用失败,GATT_INVALID_ATTRIBUTE_LENGTH(135)为数据过长需调整MTU
    }
}

5. 匹配ESP32端数据格式

检查写入字符串是否符合ESP32的解析逻辑:

  • 移除多余空格:比如" ON"改为"ON",确保与ESP32端指令格式一致
  • 确认builderList.get(1)的内容是否为ESP32预期的指令前缀
  • 结尾的%END需和ESP32端的结束标记完全匹配

6. 优化BLE操作线程

移除代码中的Thread.sleep(),BLE操作依赖回调而非阻塞等待,避免主线程卡顿。

7. 确认MTU设置有效性

若写入字符串长度超过23字节(默认MTU),需确保MTU协商成功:

private void setMTU() {
    BluetoothGatt gatt = BleManager.getInstance().getBluetoothGatt(bleDevice);
    if (gatt != null) {
        // 请求更大的MTU,最大支持512
        gatt.requestMtu(512);
    }
}

// 在BluetoothGattCallback中监听MTU变化
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
    super.onMtuChanged(gatt, mtu, status);
    if (status == BluetoothGatt.GATT_SUCCESS) {
        Log.d(Tag, "MTU协商成功: " + mtu);
    } else {
        Log.d(Tag, "MTU协商失败");
    }
}

内容的提问来源于stack exchange,提问作者Javed Quantum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 00:45:56