Android开发之蓝牙通信(二)

Android 蓝牙开发(一)蓝牙通信 随着可穿戴设备的流行,研究蓝牙是必不可少的一门技术了。 总结了下蓝牙开发使用的一些东西分享一下。 首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。 <uses-permissionandroid:name="android.permission.BLUETOOTH" /> 允许程序连接到已配对的蓝牙设备。 <uses-permissionandro 阅读详情

接着上篇蓝牙通信往下写,若有不对还请指出,大家共同进步。

Android开发之蓝牙通信(一)

Android开发之蓝牙通信(二)

Android开发之蓝牙通信(三)


先大概了解蓝牙扫描连接收发数据的工作流程,这里只提notify接收数据,read自动忽略。首先蓝牙需要动态权限,个人采用动态权限的开源库permissionsdispatcher,不懂若有需要可以github检索这里不在叙述(本篇多为代码块,完整接口和类可以自己拷贝到项目+开源库就可以直接用)

首先是一个接口定义,关于蓝牙的使用之前的一些判断OnBluetoothInspectListener


public interface IBluetoothInspectListener {
    public interface OnBluetoothCheckListener {
        void onOpenBluetoothState();

        void onRefuseOpenBluetoothState();

        void unSupportBluetooth();
    }

    public interface OnBluetoothInspectListener {
        void onInspect();

        void onSupportBluetooth();

        boolean isOpenBluetooth();

        void onOpenBluetooth();
    }
}

实现类OnBluetoothInspectListenerImpl


public class OnBluetoothInspectListenerImpl implements OnBluetoothInspectListener {
    private static final int REQUEST_ENABLE_BT = 1;
    private static OnBluetoothInspectListenerImpl instance;
    private OnBluetoothCheckListener onBluetoothCheckListener;
    BluetoothAdapter mBluetoothAdapter;
    Activity mContext;

    private OnBluetoothInspectListenerImpl(Activity mContext, OnBluetoothCheckListener onBluetoothCheckListener) {
        this.mContext = mContext;
        this.onBluetoothCheckListener = onBluetoothCheckListener;
        this.mBluetoothAdapter = ((BluetoothManager)mContext.getSystemService("bluetooth")).getAdapter();
    }

    public static OnBluetoothInspectListenerImpl init(Activity activity, OnBluetoothCheckListener onBluetoothCheckListener) {
        instance = new OnBluetoothInspectListenerImpl(activity, onBluetoothCheckListener);
        return instance;
    }

    public void onInspect() {
        if(this.mBluetoothAdapter == null) {
            this.onBluetoothCheckListener.unSupportBluetooth();
        } else {
            this.onSupportBluetooth();
        }

    }

    public void onSupportBluetooth() {
        if(this.isOpenBluetooth()) {
            this.onBluetoothCheckListener.onOpenBluetoothState();
        } else {
            this.onOpenBluetooth();
        }

    }

    public boolean isOpenBluetooth() {
        return this.mBluetoothAdapter.isEnabled();
    }

    public void onOpenBluetooth() {
        Intent enableBtIntent = new Intent("android.bluetooth.adapter.action.REQUEST_ENABLE");
        this.mContext.startActivityForResult(enableBtIntent, 1);
    }

    public void performResult(int requestCode, int resultCode) {
        switch(requestCode) {
        case 1:
            if(this.onBluetoothCheckListener != null) {
                if(this.isOpenBluetooth()) {
                    this.onBluetoothCheckListener.onOpenBluetoothState();
                } else {
                    this.onBluetoothCheckListener.onRefuseOpenBluetoothState();
                }
            }
        default:
        }
    }

    public BluetoothAdapter getBluetoothAdapter() {
        return this.mBluetoothAdapter;
    }
}

具体使用这块的代码块(根据动态权限结果,有蓝牙的权限后调用onBluetoothInspectListenerImpl.onInspect();方法即可,会自动根据相应函数执行回调,onOpenBluetoothState方法回调时表示可以开始蓝牙扫描了)


public class BluetoothActivity extends AppCompatActivity implements IBluetoothInspectListener.OnBluetoothCheckListener{

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //.........略..................

        onBluetoothInspectListenerImpl = OnBluetoothInspectListenerImpl.init(this, this);
        //.......动态权限.........

    }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        onBluetoothInspectListenerImpl.performResult(requestCode, resultCode);
    }


  @Override
  public void onOpenBluetoothState() {
      //扫描读写
      startScan();
  }

 @Override
 public void onRefuseOpenBluetoothState() {
     showInformation("您拒绝了开启蓝牙将影响到功能的使用");
     finish();
 }

 @Override
 public void unSupportBluetooth() {
     showInformation("手机不支持低功耗蓝牙通信");
     finish();
 }
}

蓝牙扫描代码个人感觉比较繁琐,最后选用了小米一哥们的开源库https://github.com/dingjikerbo/BluetoothKit,由于该库没有提供compile路径,之前blog提到过搭建maven私服,于是下载源码下来把module上传到了本地maven私服,根据开发需求,进行了简单得的封装

接口定义IBluetoothListener、BluetoothCallback


public interface IBluetoothListener {
    public void startScan();
    public void stopScan();
    public void connect();
    public void read();
    public void write();
    public void registerNotify();
    public void unRegisterNotify();
    public void disconnect();
    public void release();
}


public interface BluetoothCallback {

    public void showInformation(String information);

    public void toastMessage(String message);

    public void readResponse(byte[] bytes);
}

实现类OnBluetootListener ,UUID特别重要,不能随便copy,最好找硬件那边要。(如果你拷贝了该类需要自行补充完整UUID)


public class OnBluetootListener implements IBluetoothListener {

    public static final UUID SERVICE_UUID ;

    public static final UUID WRITE_CHARA ;

    public static final UUID READ_CHARA ;

    private byte[] bytes;
    private String deviceMac;
    private SearchRequest request;
    private IBluetoothClient mClient;
    private BluetoothDevice currentBluetoothDevice;
    private BluetoothCallback callback;

    private boolean isStopScan = false;

    public OnBluetootListener(Context context, String deviceMac, byte[] bytes, BluetoothCallback callback) {
        this.deviceMac = deviceMac;
        this.bytes = bytes;
        this.callback = callback;
        mClient = BluetoothClient.getInstance(context);
        request = new SearchRequest.Builder()
                .searchBluetoothLeDevice(3000, 1)   // scan Bluetooth LE device for 3000ms, 3 times
                .searchBluetoothClassicDevice(5000) // then scan Bluetooth Classic device for 5000ms, 1 time
                .searchBluetoothLeDevice(2000)      // at last scan Bluetooth LE device for 2000ms
                .build();
    }

    @Override
    public void startScan() {
        //执行扫描任务
        callback.showInformation("扫描设备..");
        mClient.search(request, new SearchResponse() {
            @Override
            public void onSearchStarted() {

            }

            @Override
            public void onDeviceFounded(SearchResult device) {
                if (device.device.getAddress().equals(deviceMac)&&!isStopScan) {
                    isStopScan=true;
                    //扫描到指定设备后停止扫描
                    callback.showInformation("发现设备");
                    currentBluetoothDevice = device.device;

                    //连接设备
                    connect();

                    stopScan();
                }
            }

            @Override
            public void onSearchStopped() {
                if (currentBluetoothDevice == null) {
                    callback.toastMessage("没有发现匹配的设备");
                }
            }

            @Override
            public void onSearchCanceled() {

            }
        });
    }

    @Override
    public void stopScan() {
        mClient.stopSearch();
    }

    @Override
    public void connect() {
        mClient.connect(deviceMac, new BleConnectResponse() {
            @Override
            public void onResponse(int code, Bundle data) {
                if (code == REQUEST_SUCCESS) {
                    //连接成功
                    //注册notify以后发送数据和读取数据
                    callback.showInformation("连接成功");
                    registerNotify();
                } else {
                    release();
                    callback.toastMessage("连接失败");
                }
            }
        });
    }

    @Override
    public void read() {
        mClient.read(deviceMac, SERVICE_UUID, READ_CHARA, new BleReadResponse() {
            @Override
            public void onResponse(int code, byte[] data) {
                if (code == REQUEST_SUCCESS) {
                    callback.showInformation(data.toString());
                } else {
                    release();
                    callback.toastMessage("读取数据失败");
                }
            }
        });
    }

    @Override
    public void write() {
        mClient.write(deviceMac, SERVICE_UUID, WRITE_CHARA, bytes, new BleWriteResponse() {
            @Override
            public void onResponse(int code) {
                if (code == REQUEST_SUCCESS) {
                    callback.showInformation("发送数据成功");
                    // read();
                } else {
                    release();
                    callback.toastMessage("发送数据失败");
                }
            }
        });
    }

    public void write(byte[] bytes) {
        mClient.write(deviceMac, SERVICE_UUID, WRITE_CHARA, bytes, new BleWriteResponse() {
            @Override
            public void onResponse(int code) {
                if (code == REQUEST_SUCCESS) {
                } else {
                    release();
                    callback.toastMessage("发送数据失败");
                }
            }
        });
    }

    @Override
    public void registerNotify() {
        mClient.notify(deviceMac, SERVICE_UUID, READ_CHARA, new BleNotifyResponse() {
            @Override
            public void onNotify(UUID service, UUID character, byte[] value) {
                callback.readResponse(value);
            }

            @Override
            public void onResponse(int code) {
                if (code == REQUEST_SUCCESS) {
                   // callback.showInformation("注册蓝牙通知成功");
                   //write数据需要注意,个别手机有限制一次发送数据最大值,如果超过了需要自己分多次发送
                    callback.showInformation("数据已发送,等待数据响应");
                } else {
                    release();
                    callback.toastMessage("发送失败,请再试一次");
                }
            }
        });
    }

    @Override
    public void unRegisterNotify() {
        mClient.unnotify(deviceMac, SERVICE_UUID, READ_CHARA, new BleUnnotifyResponse() {
            @Override
            public void onResponse(int code) {

            }
        });
    }

    @Override
    public void disconnect() {
        mClient.disconnect(deviceMac);
    }

    @Override
    public void release() {
        unRegisterNotify();
        disconnect();
        stopScan();
    }

    public BluetoothDevice getCurrentBluetoothDevice(){
        return  currentBluetoothDevice;
    }
}

根据上面的实现类,使用时只需在onCreate获取实现类实例,在蓝牙判断检查的回掉函数执行扫描,通过这个实现类代理即可

listener = new OnBluetootListener(this,macAddress,sendMessageBytes,this);

//...........................................
@Override
public void onOpenBluetoothState() {
    //扫描读写
    listener.startScan();
}

开启扫描,扫描到指定设备,开始注册蓝牙通知服务,注册成功了就开始写入数据等待返回数据,大致工作流程就这样的,主要关注回掉函数如下

 @Override
    public void showInformation(String message) {
        toast.setText(message);
     // Logger.e(message);
    }

    @Override
    public void toastMessage(String s) {
        Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                finish();
            }
        },1000);
    }

    @Override
    public void readResponse(byte[] bytes) {

    }

再提一点,数据按照字节传输,一次接受的字节长度有限制,如果要保证自己接受完整,需要自己采用相应的办法,如下


    @Override
    public void readResponse(byte[] bytes) {
        saveBytes(bytes, responseBytes);
        if (readCount == responseBytes.length) {
            onResponseReadResult(responseBytes);//解析回传数据
        }
    }

    /**
     * 拼接byte数据
     *
     * @param src
     * @param ds
     */
    public void saveBytes(byte[] src, byte[] ds) {
        for (int i = 0; i < src.length; i++) {
            ds[i + readCount] = src[i];
        }
        readCount = readCount + src.length;
    }

最后一点,与硬件通信一般都会用到Crc8、Crc16算法相关的,而硬件开发用C与java有所不同,算法转换这里提供一个Crc8的一个类(网上多为c char,java的有长度限制直接拷贝到java里用会有点问题)


public class Crc8Helper {


    /**
     * CRC8算法
     *
     * @param bytes
     * @return
     */
    public static byte CRC8_Table(byte[] bytes)// counter默认为2
    {
        return calcCrc8(bytes);
    }

    static byte[] crc8_tab = {(byte) 0, (byte) 94, (byte) 188, (byte) 226, (byte) 97, (byte) 63, (byte) 221, (byte) 131, (byte) 194, (byte) 156, (byte) 126, (byte) 32, (byte) 163, (byte) 253, (byte) 31, (byte) 65, (byte) 157, (byte) 195, (byte) 33, (byte) 127, (byte) 252, (byte) 162, (byte) 64, (byte) 30, (byte) 95, (byte) 1, (byte) 227, (byte) 189, (byte) 62, (byte) 96, (byte) 130, (byte) 220, (byte) 35, (byte) 125, (byte) 159, (byte) 193, (byte) 66, (byte) 28, (byte) 254, (byte) 160, (byte) 225, (byte) 191, (byte) 93, (byte) 3, (byte) 128, (byte) 222, (byte) 60, (byte) 98, (byte) 190, (byte) 224, (byte) 2, (byte) 92, (byte) 223, (byte) 129, (byte) 99, (byte) 61, (byte) 124, (byte) 34, (byte) 192, (byte) 158, (byte) 29, (byte) 67, (byte) 161, (byte) 255, (byte) 70, (byte) 24,
            (byte) 250, (byte) 164, (byte) 39, (byte) 121, (byte) 155, (byte) 197, (byte) 132, (byte) 218, (byte) 56, (byte) 102, (byte) 229, (byte) 187, (byte) 89, (byte) 7, (byte) 219, (byte) 133, (byte) 103, (byte) 57, (byte) 186, (byte) 228, (byte) 6, (byte) 88, (byte) 25, (byte) 71, (byte) 165, (byte) 251, (byte) 120, (byte) 38, (byte) 196, (byte) 154, (byte) 101, (byte) 59, (byte) 217, (byte) 135, (byte) 4, (byte) 90, (byte) 184, (byte) 230, (byte) 167, (byte) 249, (byte) 27, (byte) 69, (byte) 198, (byte) 152, (byte) 122, (byte) 36, (byte) 248, (byte) 166, (byte) 68, (byte) 26, (byte) 153, (byte) 199, (byte) 37, (byte) 123, (byte) 58, (byte) 100, (byte) 134, (byte) 216, (byte) 91, (byte) 5, (byte) 231, (byte) 185, (byte) 140, (byte) 210, (byte) 48, (byte) 110, (byte) 237,
            (byte) 179, (byte) 81, (byte) 15, (byte) 78, (byte) 16, (byte) 242, (byte) 172, (byte) 47, (byte) 113, (byte) 147, (byte) 205, (byte) 17, (byte) 79, (byte) 173, (byte) 243, (byte) 112, (byte) 46, (byte) 204, (byte) 146, (byte) 211, (byte) 141, (byte) 111, (byte) 49, (byte) 178, (byte) 236, (byte) 14, (byte) 80, (byte) 175, (byte) 241, (byte) 19, (byte) 77, (byte) 206, (byte) 144, (byte) 114, (byte) 44, (byte) 109, (byte) 51, (byte) 209, (byte) 143, (byte) 12, (byte) 82, (byte) 176, (byte) 238, (byte) 50, (byte) 108, (byte) 142, (byte) 208, (byte) 83, (byte) 13, (byte) 239, (byte) 177, (byte) 240, (byte) 174, (byte) 76, (byte) 18, (byte) 145, (byte) 207, (byte) 45, (byte) 115, (byte) 202, (byte) 148, (byte) 118, (byte) 40, (byte) 171, (byte) 245, (byte) 23, (byte) 73, (byte) 8,
            (byte) 86, (byte) 180, (byte) 234, (byte) 105, (byte) 55, (byte) 213, (byte) 139, (byte) 87, (byte) 9, (byte) 235, (byte) 181, (byte) 54, (byte) 104, (byte) 138, (byte) 212, (byte) 149, (byte) 203, (byte) 41, (byte) 119, (byte) 244, (byte) 170, (byte) 72, (byte) 22, (byte) 233, (byte) 183, (byte) 85, (byte) 11, (byte) 136, (byte) 214, (byte) 52, (byte) 106, (byte) 43, (byte) 117, (byte) 151, (byte) 201, (byte) 74, (byte) 20, (byte) 246, (byte) 168, (byte) 116, (byte) 42, (byte) 200, (byte) 150, (byte) 21, (byte) 75, (byte) 169, (byte) 247, (byte) 182, (byte) 232, (byte) 10, (byte) 84, (byte) 215, (byte) 137, (byte) 107, 53};

    /**
     * 计算数组的CRC8校验值
     *
     * @param data 需要计算的数组
     * @return CRC8校验值
     */
    public static byte calcCrc8(byte[] data) {
        return calcCrc8(data, 0, data.length, (byte) 0);
    }

    /**
     * 计算CRC8校验值
     *
     * @param data   数据
     * @param offset 起始位置
     * @param len    长度
     * @return 校验值
     */
    public static byte calcCrc8(byte[] data, int offset, int len) {
        return calcCrc8(data, offset, len, (byte) 0);
    }

    /**
     * 计算CRC8校验值
     *
     * @param data   数据
     * @param offset 起始位置
     * @param len    长度
     * @param preval 之前的校验值
     * @return 校验值
     */
    public static byte calcCrc8(byte[] data, int offset, int len, byte preval) {
        byte ret = preval;
        for (int i = offset; i < (offset + len); ++i) {
            ret = crc8_tab[(0x00ff & (ret ^ data[i]))];
        }
        return ret;
    }

    /**
     * 计算CRC8校验值
     *
     * @param data
     *            数据
     * @param offset
     *            起始位置
     * @param len
     *            长度
     * @param preval
     *            之前的校验值
     * @return 校验值
     */
    public static byte parseCalcCrc8(byte[] data, int offset, int len, byte preval) {
        byte ret = preval;
        for (int i = offset; i < (offset + len); ++i) {
            ret = crc8_tab[(0x00ff & (ret ^ data[i]))];
        }
        return ret;
    }
}

小结:蓝牙通信这块的开发会遇到高地位的转换、机型适配的烦恼(小米5.1、4.4手机),个人这块工作暂时告一段落,用过api直接写通信模块,也用过第三方框架,感觉都是麻烦事,还要动态权限一对代码,希望有一天有位大神闪亮登场,搞个比较牛逼的开源库让我一键调用就好了。

Android蓝牙开发—经典蓝牙详细开发流程 https://blog.youkuaiyun.com/zqf_888/article/details/81060606 阅读详情

相关推荐

Android基于Modbus RTU协议通过蓝牙与蓝牙串口开发板通信

Android基于ModbusRtu协议通过蓝牙与蓝牙串口通信实现开发板读写前言实现原理代码实现 前言 最近接到一位老师的项目,他们公司要求利用Android基于ModbusRtu协议通过蓝牙与蓝牙串口通信实现开发板读写,然后通过开发板控制硬件设备从而实现Android移动设备与硬件交互。所以今天我给大家分享下Android基于ModbusRtu协议的蓝牙通信。当然Android也可以直接通过串口利用ModbusRtu协议与开发板通信,或者基于Modbus TCP通信。至于非蓝牙非Modbus协议通信,大家

weixin_42644340的博客 7357

Android 蓝牙通信

Android 蓝牙通信一、现在的无线通信方案及差别二、蓝牙传输功能开发1、准备阶段1)权限2)蓝牙的开启/关闭/状态判断设备是支持蓝牙获取蓝牙状态启动/关闭2、查找设备1) 让设备可见2) 查找设备首先启动查找设备然后是通过广播来获取到查找的结果。3) 绑定设备对于已经绑定的设备对于未绑定的设备3、通讯过程通讯的两个设备蓝牙一定要绑定的吗?总结 一、现在的无线通信方案及差别 上图是2018年甚至是更老的时候的方案 除以上之外还有 红外线传输 但是红外线是直线的传输方式(单向传输?)不适应无线通讯 二、蓝

csdn_pun的博客 3316

Android 蓝牙通信及WiFi开发

在我们正常的Android蓝牙功能开发步骤中,一般要经过系统权限和蓝牙开关状态监测、设备扫描、设备连接、蓝牙数据通信这几个过程。 在Android 4.3系统之后,我们可以使用蓝牙4.0(低功耗蓝牙),它最主要的特点是低功耗,普及率高。现在所说的蓝牙设备,大部分都是在说4.0设备,ble也特指4.0设备。 在4.0之前重要的版本有2.1版本-基本速率/增强数据率(BR/EDR)和3.0 高速蓝牙版

Bug不知所起,一往而深。 5565

Android硬件通信之 蓝牙通信

蓝牙通信

qq_29848853的博客 3845

安卓经典蓝牙开发,包括自动连接开发板蓝牙并通信

安卓经典蓝牙开发,包括自动连接开发板蓝牙并通信蓝牙开发首先弄清楚两点:手机蓝牙连接手机蓝牙与手机蓝牙连接外设蓝牙(比如开发板上的蓝牙),这两者连接方式要区分开来,前者需要两个手机同时安装开发的APP,APP里面的代码指定了唯一的UUID用于通信,而后者只能在手机APP里面通过反射的方式获取外设蓝牙的UUID,然后连接通信。本文是使用后者。第二是要知道经典蓝牙开发与低功耗蓝牙开发(即Ble)二者是不...

Crook的专栏 4428

Android Studio开发(六)短距离无线通信——蓝牙通信

Android Studio开发(六)短距离无线通信——蓝牙通信Android Studio开发(六)蓝牙通信一、任务需求二、短距离无线通信技术(Short-Distance Wirleless Communication)1. 简介2. 几种常用的短距离无线通信技术2.1 蓝牙(Bluetooth)2.2 Wi-Fi2.3 近场通信(Near Field Communication,NFC)三、使用蓝牙在安卓中进行简单通信1. socket套接字2. 在AS中声明权限使用蓝牙3. 编码阶段四、实现效果 A

Louloo的博客 3045

Android蓝牙通信

Android 平台包含蓝牙网络堆栈支持,此支持能让设备以无线方式与其他蓝牙设备交换数据。应用框架提供通过 Android Bluetooth API 访问蓝牙功能的权限。这些 API 允许应用以无线方式连接到其他蓝牙设备,从而实现点到点和多点无线功能。 使用蓝牙进行通信的四大必需任务:设置蓝牙、查找局部区域内的配对设备或可用设备、连接设备,以及在设备之间传输数据。 为了让支持蓝牙的设备能够在彼此之间传输数据,它们必须先通过配对过程形成通信通道。其中一台设备(可检测到的设备)需将自身设置为可接收传入的连接请

qq_51771271的博客 5063

安卓蓝牙BLE设备开发

前段时间做了一个有关于安卓蓝牙BLE设备的开发项目,主要的功能包括了搜索蓝牙ble设备和ble设备的数据读写等等,本篇博客用于记录安卓蓝牙ble设备的通信的细节。  其实关于BLE设备的通信在API中已经讲地比较清楚了,这里只是做一个总结,如果要进行BLE设备的开发,首先可以阅读API. BLE有关APIBLE设备的定义和特点 BLE-维基百科  对于BLE设备的定义,我们只需要看维基百科的说

maowei117的博客 2690

android 如何蓝牙通信

在 Android 中,如果您的应用需要使用蓝牙功能,则需要在 AndroidManifest.xml 文件中声明 BLUETOOTH 权限。需要注意的是,如果您的应用需要使用 BluetoothAdapter 的其他功能,例如启用蓝牙或搜索蓝牙设备,则还需要请求 BLUETOOTH 和 BLUETOOTH_ADMIN 权限。需要注意的是,在进行蓝牙通信时,需要获取 BLUETOOTH 和 BLUETOOTH_ADMIN 权限。以上是一个简单的蓝牙通信的示例,实际应用中可能需要更复杂的操作和处理。

欢迎来到“無昂博奥”的博客 1725

Android蓝牙开发实战总结

公司最近的项目是一款带投影的智能Android手表开发,提及到穿戴设备,便携设备以及车载多媒体肯定设计到蓝牙开发。对于一般的软件开发人员,蓝牙基本不会用到,或许只用来传文件。其实蓝牙不仅仅可以做这些,还可以进行设备间通信,收发指令等等。公司一代产品用的是经典蓝牙进行通信,由于耗电的原因改为BLE通信。     哇,第一次这么认真的坐下来写笔记,长话短说,接下来主要从经典蓝牙和BLE两方面聊聊蓝牙

Android工程师Simon的博客 4275

android 8.1 蓝牙开发 静默开启蓝牙 将蓝牙暴露给其他蓝牙设备 扫描蓝牙设备 静默配对 建联相互通信

蓝牙静默配对

qq_37630270的博客 2832

安卓BLE蓝牙通讯

正常情况下,当触发触发onConnectSuccess回调时,设备已经连上的蓝牙的GATT,此时可认为蓝牙已连接,此时中心设备是无法和外围设备通讯的,还需根据蓝牙协议绑定指定的特征UUID,一般中心设备接收数据的为NOTIFY_CHARACTERISTIC_UUID,向外围设备发送消息的为WRITE_CHARACTERISTIC_UUID,大部分外设备还有描述符,例如接收数据需要的描述符NOTIFY_DESCRIPTOR_UUID等。断开设备后,进行重新扫描,再次连接时,可接收到Notify的消息。

weixin_41119184的博客 2830

Android GNSS 模块分析(三)JNI 层

Android GNSS;定位服务;GPS;NMEA协议;LocationManagerService;导航;

Yang_Mao_Shan的博客 5155

Android开发之蓝牙(二)——基于BLE协议蓝牙模块通信

介绍 基本概念 Android蓝牙开发流程 app是ble与spp选择连接蓝牙模块,关于spp的可以看 介绍摘要:蓝牙4.0——BLE是近年来应用比较广泛的profile,与传统蓝牙相比,其低功耗的特征最为显著,在蓝牙模块中(CC2540),一块纽扣电池可供电半年到一年之间。

zh1豪的博客 9538

advanced installer 打包Flutter的Windows程序安装包(基本打包、开机自启、定时自动检测升级、包含卸载程序)

advanced installer 打包Flutter的Windows程序安装包(基本打包、开机自启、定时自动检测升级、包含卸载程序)

qq_45634989的博客 2878

手把手教你配置C6678的SPI启动:从NorFlash烧写到多核加载的完整流程

本文详细介绍了TMS320C6678多核DSP的SPI启动全流程,包括硬件准备、镜像文件生成与转换、NorFlash烧写与验证、多核启动同步机制以及常见问题排查。通过实战演示,帮助开发者高效配置SPI启动模式,解决启动失败、多核同步异常等问题,提升嵌入式系统开发效率。

weixin_27076351的博客 151

Android 蓝牙通信开发(二) 创建蓝牙连接

蓝牙通信是我们日常生活中比较方便的一种通信技术,Android从2.0版本的SDK就开始支持蓝牙。对开发人员来说,应用程序中蓝牙还是一种用来创建点对点连接通信的简单而高效的方式。

magician_Coder的博客 1544

UE5 AI寻路全攻略:从Nav Mesh基础到动态障碍与多模式导航

在游戏开发中,AI寻路是赋予非玩家角色自主移动能力的关键技术。其核心原理是通过预计算或实时生成一个表示可行走区域的导航网格,AI路径查找算法(如A*)在此网格上规划最优路径。这项技术的价值在于,它能极大提升游戏世界的真实感和交互性,使NPC能够智能地绕开障碍、穿越复杂地形,广泛应用于开放世界、RPG、策略游戏等多种场景。本文聚焦于虚幻引擎5的寻路系统,深入探讨其寻路网格体配置。针对动态环境挑战,文章详细介绍了如何利用寻路修饰体积和动态障碍物组件实现导航网格的实时更新;同时,针对游泳、飞行等特殊移动需求,解析

weixin_33226816的博客 360

Android 蓝牙开发之搜索、配对、连接、通信大全

蓝牙( Bluetooth®):是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。蓝牙设备最多可以同时和7个其它蓝牙设备建立连接,进行通信,当然并不是每一个蓝牙都可以达到最大值。下面,我们从蓝牙的基本概念开始,一步一步开始了解蓝牙。(尊重劳动成果,转载请注明出处http://blog....

weixin_33686714的博客 5972

QT | QT打包生成单个EXE文件

1.以release方式运行程序 2.在release文件夹中找到.exe文件 如果没有找到exe,可能是因为在项目中勾选了shadow build,你可以在构建目录中找到exe 3.将exe放在一个新建的文件夹中,因为待会儿会生成很多dll文件 4.点击电脑左下角搜索QT,打开Qt 5.8 for Desktop (MinGQW 5.3.0 32bit) 5.进入刚刚新建的文件夹,输入wi...

⊳ideal world⊲~ヽ(゚︺゚)Hi 1238

10.2 构网型变流器引发的次同步振荡与宽频振荡问题:机理分析与抑制技术

随着构网型变流器在新型电力系统中的规模化部署,其作为电压源主动支撑电网的能力得到验证的同时,也引发了一系列前所未有的稳定性问题。传统的同步发电机主导的电力系统中,振荡模式主要集中于**低频区间**。然而,构网型变流器通过快速数字控制实现的虚拟同步、惯性响应等先进功能,其控制环路动态与电网阻抗、其他电力电子设备之间产生了复杂的交互作用。这种交互不仅可能诱发经典的**次同步振荡**,更可能催生频率范围从数赫兹到数百赫兹甚至上千赫兹的**宽频振荡**。

技术工作者 1301

Android开发——蓝牙通信实现

Android开发——蓝牙通信实现项目需求项目主要代码及功能实现AndroidManifest.xmlres/values/string.xmlactivity_main.xmlChatServiceres/menu/option_menu.xmldevice_list.xmlDeviceListBlueToothChat项目结果展示源码地址 项目需求 运用蓝牙进程通信,实现两个手机的蓝牙进行通信聊天。 项目主要代码及功能实现 AndroidManifest.xml 给予项目蓝牙通信的相关权限 &l

lpl9472的博客 6277

手把手教你学simulink实例--基于Simulink的船舶动力装置性能评估与仿真

船舶动力装置是船舶运行的核心系统,其性能直接影响船舶的安全性、经济性和环保性。基于MATLAB/Simulink平台进行船舶动力装置性能评估与仿真是研究和优化船舶动力系统的重要手段。通过建模与仿真,可以分析动力装置在不同工况下的性能表现,并验证控制策略的有效性。本项目旨在构建一个完整的船舶动力装置性能评估与仿真模型,包括发动机建模、传动系统建模、负载分析以及性能评估模块,并通过仿真分析其动态响应和性能指标。

MHD0815的博客 971

Android Studio开发之蓝牙通信

安卓开发-蓝牙通信 功能需求:在微信程序的第一子项中完成“蓝牙聊天功能” 开发步骤: 配置文件注册 设计界面布局 编写用于蓝牙会话的服务组件ChatService 分别建立供主Activity使用的菜单文件res/menu/optionmenu.xml、选择好友(即已经配对过的蓝牙设备)的界面布局文件devicelist.xml 新建Activity组件DeviceList,实现选取与之会话的蓝牙设备 部分代码展示: 配置文件注册 <manifest xmlns:android="http://s

qq_45035807的博客 3594

Android15音频进阶之4种调试线程死锁利器(八十九)

本篇目的:Android15音频进阶之4种调试线程死锁利器对于开发者来说,最令人头疼的,莫过于多线程交互出问题的时候,我们应该从哪里找突破口呢?下面来介绍一下调试Android音频中线程死锁的四种方案线程死锁是并发编程中常见的问题之一,它指的是在多个线程竞争资源的过程中,由于相互等待对方持有的资源而无法继续执行,从而导致所有线程都无法向前推进的现象。死锁不仅会导致系统性能严重下降,甚至可能导致整个系统崩溃。线程死锁的产生有四个必要条件:互斥条件:资源不能被多个线程共同使用,只能由一个线程独占。

Android系统攻城狮 1996

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

上一篇: Android Studio开发脱离鼠标之路
下一篇: 前端学习CSS网页布局篇(五)
AnalyzeSystem
博客等级 码龄11年 204粉丝 114原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值