Android开发之蓝牙通信(一)

Android 蓝牙权限(更新到 Android 12) 访问设备位置的权限。在 Android 6.0 及以上版本中,需要获取位置权限才能扫描附近的蓝牙设备。:管理蓝牙适配器的权限,包括启用/禁用蓝牙、扫描设备和进行配对等操作。:在后台访问设备位置的权限。该权限通常在后台扫描蓝牙设备时使用。:访问蓝牙适配器的权限,用于执行蓝牙操作。需要手机开启定位服务。 阅读详情

时隔半年时间,又遇到了蓝牙开发了,之前是蓝牙连接打印相关方面的,这次需要蓝牙配对数据传输,折腾过去折腾过来,也就那么回事,下定决心系统的梳理这块的知识

Android开发之蓝牙通信(一)

Android开发之蓝牙通信(二)

Android开发之蓝牙通信(三)


蓝牙开发必练基本功
蓝牙权限

为了在您的应用程序中使用蓝牙功能,您必须声明蓝牙权限蓝牙。您需要此权限来执行任何蓝牙通信,如请求一个连接、接受一个连接和传输数据。如果你想让你的应用启动设备发现或操纵蓝牙设置,你也必须申报bluetooth_admin许可。大多数应用程序都需要此权限,仅用于发现本地蓝牙设备的能力。此权限授予的其他权限不应被使用,除非应用程序是一个“电源管理器”,将修改用户请求后的蓝牙设置。注意:如果你使用bluetooth_admin许可,那么你也必须有蓝牙许可。

<manifest ... >
  <uses-permission android:name="android.permission.BLUETOOTH" />
  ...
</manifest>
设置蓝牙

在应用程序可以通过蓝牙进行通信之前,您需要验证是否支持蓝牙设备上的支持,如果是这样的话,请确保它已启用。如果不支持蓝牙,那么您应该优雅地禁用任何蓝牙功能。如果蓝牙是支持的,但禁用,那么你可以要求用户启用蓝牙,而不会离开你的应用程序。这个设置是在两个步骤来完成,使用蓝牙适配器。

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // 设备不支持蓝牙 
}

下一步,您需要确保蓝牙功能已启用。isenabled()检查蓝牙目前是否启用。如果此方法返回错误,则禁用蓝牙。要求蓝牙启用,startactivityforresult()与action_request_enable动作意图。这将发出一个请求,使蓝牙通过系统设置(不阻止您的应用程序)。例如:

if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
查询配对设备

使用蓝牙适配器 BluetoothAdapter,您可以通过查询名单发现远程蓝牙设备配对。
设备发现是一个扫描程序,搜索本地区域的蓝牙功能的设备,然后要求一些信息(这是有时被称为“发现”,“查询”或“扫描”)然而,局域网内的蓝牙设备将响应发现请求只有在能够被发现。如果一个设备可以被发现,它会响应发现请求并共享一些信息,比如设备名称、类别,并以其独特的MAC地址。使用此信息,执行发现的设备可以选择启动一个连接到所发现的设备。

一旦一个连接是第一次与一个远程设备,一个配对请求自动提交给用户。当一个设备配对,该设备的基本信息(如设备名称,类和地址)被保存,并可以读取使用蓝牙耳机。使用一个远程设备的已知的mac地址,可以在任何时间启动一个连接,而不进行发现(假设设备处于范围内)。

记住,有一个配对和被连接之间的区别。要配对意味着两个设备都知道彼此的存在,有一个共享的链路密钥,可用于认证,并能够建立一个加密的相互连接。要连接意味着目前的设备共享一个RFCOMM通道可以互相传递数据。目前安卓蓝牙API的设备需要被配对在RFCOMM连接可以建立。(当您启动与蓝牙接口的加密连接时,自动执行配对)。

getBondedDevices获取与本机蓝牙所有绑定的远程蓝牙信息,以BluetoothDevice类实例返回,如果蓝牙开启,该函数会返回一个空集合

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}
发现设备

开始发现设备,调用startdiscovery()。该进程是异步的,该方法将立即返回一个布尔值,指示是否已成功启动。发现过程通常涉及一个约12秒的查询扫描,然后通过一个页面扫描每个发现的设备来检索其蓝牙名称。

应用程序必须注册ACTION_FOUND意图接收有关每个设备发现BroadcastReceiver。我们通过注册广播接收器接受广播,来处理一些事情:

// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

警告:执行设备发现是蓝牙适配器的一个沉重的过程,并会消耗大量的资源。一旦你找到了一个设备连接,就调用canceldiscovery()取消之前的扫描。

发现设备的Intent意图默认扫描时间120秒,一个应用程序可以设置最大持续时间为3600秒,0值表示设备总是发现。任何值低于0或高于3600自动设置为120秒)(可以这样理解:蓝牙设备运行在多少时间内可以被其他用于扫描到并连接)

Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);

关于蓝牙与服务端、客户端的链接涉及到BluetoothServerSocket 、BluetoothSocket ,没啥好说的,稍后通过官方simple一观即可。


蓝牙低功耗基于Android4.3

相比较上面这块,权限必须有以下两项(4.3的低功耗蓝牙的扫描startLeScan方法必须具备BLUETOOTH_ADMIN权限)

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

如果你确定你的应用程序是4.3以上的手机设备安装,支持低功耗蓝牙,可以在清单文件声明如下

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

如果你不确定你的app使用的设备是否支持低功耗蓝牙,但又想让支持的设备使用低功耗蓝牙,那么你可以这样做

<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

或BluetoothAdapter可以通过BluetoothManager得到,因为在BluetoothManager被装载时构造函数调用了BluetoothAdapter.getDefualtA..初始化了adapter对象


 // Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();

 public static synchronized BluetoothAdapter getDefaultAdapter() {
        if (sAdapter == null) {
            IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
            if (b != null) {
                IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
                sAdapter = new BluetoothAdapter(managerService);
            } else {
                Log.e(TAG, "Bluetooth binder is null");
            }
        }
        return sAdapter;
    }

蓝牙设备的扫描也发生了变化,通过adapter.startScan stopScan控制扫描的开关,扫描的时间可以通过postDelayed设定扫描时间

/**
 * Activity for scanning and displaying available BLE devices.
 */
public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        ...
    }
...
}

通过LeScanCallback 回调接口的实现,来处理返回结果刷新UI等方面功能的实现。

private LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {
        runOnUiThread(new Runnable() {
           @Override
           public void run() {
               mLeDeviceListAdapter.addDevice(device);
               mLeDeviceListAdapter.notifyDataSetChanged();
           }
       });
   }
};

BluetoothGatt BluetoothGattCallback等相关的Gat类都大同小异,根据google官方提供的simple过一遍即可。
蓝牙开发调试如果你想使用adb命令可以参考https://developer.android.com/training/wearables/apps/bt-debugging.html


谷歌官方蓝牙开源项目

通过官网找到三个开源项目,但是貌似都不能下载,只能通过github检索google的仓库了

先看android-BluetoothAdvertisements运行效果图

从BluetoothAdvertisements开源项目首先get到一点,剥离生命周期!相关代码块如下

public class ScannerFragment extends ListFragment {


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        setRetainInstance(true);
        // 这里传入的不是Activity,而是通过Activity获取Application,让其不再跟随活动Activity的生命周期
        mAdapter = new ScanResultAdapter(getActivity().getApplicationContext(),
                LayoutInflater.from(getActivity()));
        mHandler = new Handler();

    }

扫描代码块并没有用BluetoothAdapter.startScan,经过源码查看发现依然是过时方法,simple里面直接通过adapter获取BluetoothLeScanner,调用了scanner的startScan

public void startScanning() {
  // .........略............

  mScanCallback = new SampleScanCallback();
  mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback);
}

然而你如果想下载完这个开源项目就想运行看效果,伙计,不得不说你是绝对不会成功的,buildScanFilters里面添加了扫描过滤规则,uuid无法匹配你是看不到任何东西的,根据代码提示注释掉下面的过滤条件可以看到所有的蓝牙设备。

  private List<ScanFilter> buildScanFilters() {
        List<ScanFilter> scanFilters = new ArrayList<>();

        ScanFilter.Builder builder = new ScanFilter.Builder();
        // 注释掉下面的扫描过滤规则,才能扫描到(uuid不匹配没法扫描到的)
       // builder.setServiceUuid(Constants.Service_UUID);
        //scanFilters.add(builder.build());

        return scanFilters;
    }

在低功耗模式下执行蓝牙LE扫描。这是默认扫描模式,因为它消耗最小功率

  private ScanSettings buildScanSettings() {
        ScanSettings.Builder builder = new ScanSettings.Builder();
        builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
        return builder.build();
    }

扫描的结果回调我们可以根据自己的需求做具现

  private class SampleScanCallback extends ScanCallback {

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
            //批处理扫描结果
            for (ScanResult result : results) {
                mAdapter.add(result);
            }
            mAdapter.notifyDataSetChanged();
        }

        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            //扫描到一个立即回调
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
          //扫描失败
        }
    }

扫描开始之前的逻辑判断自行参考simple MainActivity.至于AdvertiserService相关的差不多的就略过啦、
android-BluetoothLeGatt这个simple个人觉得没多少特别之处用到的核心上面都有提到,基于android4.3+版本的蓝牙开发simple,mainfest可以看看,service个人感觉有点乱糟糟的。

再看BluetoothChat,首先需要让蓝牙被感知扫描链接,设置相应的模式和允许被扫描到的时长。

  /**
     * Makes this device discoverable.
     */
    private void ensureDiscoverable() {
        if (mBluetoothAdapter.getScanMode() !=
                BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
            Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
            startActivity(discoverableIntent);
        }
    }

蓝牙适配器getRemoteDevice(address)后进行连接,核心代码参考BluetoothChatService.ConnectThread线程部分。而数据的发送核心代码只需要把发送的string message转换byte[] ,调用ChatServiced的write方法

   /**
     * Write to the ConnectedThread in an unsynchronized manner
     *
     * @param out The bytes to write
     * @see ConnectedThread#write(byte[])
     */
    public void write(byte[] out) {
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mState != STATE_CONNECTED) return;
            r = mConnectedThread;
        }
        // Perform the write unsynchronized
        r.write(out);
    }

最后提供一个工具类BluetoothHelper.java (这个工具类整理的并不完善,关于扫描和链接兼容都没写进去,具体开发链接参照官方simple的ChartService,LE相关的扫描参照simple吧,兼容主要是api11 、api18LE 、api21)


/**
 * Created by idea on 2016/7/4.
 */
public class BluetoothHelper {

    private static BluetoothHelper instance;
    private BluetoothAdapter mBluetoothAdapter;
    private Activity mContext;

    /**
     * 获取BluetoothHelper实例,采用Application剥离Activity生命周期
     * @param activity
     * @return BluetoothHelper
     */
    public static BluetoothHelper getInstance(Activity activity) {

        if (instance == null) {
            synchronized (BluetoothHelper.class) {
                if (instance == null) {
                    instance = new BluetoothHelper(activity);
                }
            }
        }

        return instance;
    }

    /**
     * 私有构造函数
     * @param activity
     * @hide
     */
    private BluetoothHelper(Activity activity) {
        this.mContext = activity;
        mBluetoothAdapter = getAdapter();
    }

    /***
     * 获取BluetoothAdapter
     *
     * @return BluetoothAdapter
     * @hide
     */
    private BluetoothAdapter getAdapter() {
        BluetoothAdapter mBluetoothAdapter;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            mBluetoothAdapter = ((BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
        } else {
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        return mBluetoothAdapter;
    }

    /**
     * 获取已经存在的adapter实例
     * @return
     */
    public BluetoothAdapter getBluetoothAdapter() {
        return mBluetoothAdapter;
    }

    /**
     * 判断手机手否支持蓝牙通信
     *
     * @return boolean
     */
    public boolean checkSupperBluetooth() {

        return mBluetoothAdapter == null;
    }

    /**
     * 检查蓝牙状态是否打开可用
     *
     * @return boolean
     */
    public boolean checkBluetoothEnable() {

        return mBluetoothAdapter.isEnabled();
    }


    /**
     * 如果蓝牙设备支持的情况下,并未打开蓝牙,需要请求蓝牙打开
     */
    public void requestOpenBluetooth() {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        mContext.startActivityForResult(enableBtIntent, Constants.REQUEST_ENABLE_BT);
    }

    /**
     * 判断我们请求打开蓝牙的结果
     *
     * @param requestCode
     * @param resultCode
     * @param onOpenBluetoothLisenter
     */
    public void performResult(int requestCode, int resultCode, OnBluetoothListener.OnOpenBluetoothLisenter onOpenBluetoothLisenter) {

        switch (requestCode) {
            case Constants.REQUEST_ENABLE_BT:

                if (onOpenBluetoothLisenter != null) {
                    if (resultCode == Activity.RESULT_OK) {

                        if (checkBluetoothEnable()) {
                            onOpenBluetoothLisenter.onSuccess();
                        } else {
                            onOpenBluetoothLisenter.onFail("蓝牙不可用,会影响到相关功能的使用");
                        }

                    } else {
                        onOpenBluetoothLisenter.onFail("Error");
                    }
                }
        }
    }

    /***
     * 是否支持蓝牙低功耗广播(4.3+)
     *
     * @return boolean
     * @hide
     */
    @Deprecated
    public boolean isSupperBluetoothLE() {

        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
    }


    /**
     * 判断是否支持LE
     *
     * @return boolean
     * @hide
     */
    @Deprecated
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public boolean isMultipleAdvertisementSupported() {
        return mBluetoothAdapter.isMultipleAdvertisementSupported();
    }

    /**
     * 检测是否支持蓝牙低功耗,检查方式走版本分之
     *
     * @return boolean
     */
    public boolean checkSupperBluetoothLE() {
        final int version = Build.VERSION.SDK_INT;
        if (version >= Build.VERSION_CODES.LOLLIPOP) {
            return isMultipleAdvertisementSupported();
        } else {
            return isSupperBluetoothLE();
        }
    }

    /**
     * 允许蓝牙在某段时间内被扫描链接到
     *
     * @param duration
     */
    private void ensureDiscoverable(int duration) {
        if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
            Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration);
            mContext.startActivity(discoverableIntent);
        }
    }

    /**
     * 基于APi21的扫描蓝牙
     * @param mScanCallback
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public void startScanningApi21(ScanCallback mScanCallback){
        mBluetoothAdapter.getBluetoothLeScanner().startScan(buildScanFilters(), buildScanSettings(),mScanCallback);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private List<ScanFilter> buildScanFilters() {
        List<ScanFilter> scanFilters = new ArrayList<>();

        ScanFilter.Builder builder = new ScanFilter.Builder();
        // 注释掉下面的扫描过滤规则,才能扫描到(uuid不匹配没法扫描到的)
        // builder.setServiceUuid(Constants.Service_UUID);
        //scanFilters.add(builder.build());

        return scanFilters;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private ScanSettings buildScanSettings() {
        ScanSettings.Builder builder = new ScanSettings.Builder();
        builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
        return builder.build();
    }

    /**
     * 注册广播用于接收扫描结果,Api11的方法需要用到
     */
    public void registerReceiver(){
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        mContext.registerReceiver(receiver, filter);

        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        mContext.registerReceiver(receiver, filter);
    }

    /**
     * 销毁同步生命周期
     */
    protected void onDestroy() {
        if (mBluetoothAdapter != null) {
            mBluetoothAdapter.cancelDiscovery();
        }
        mContext.unregisterReceiver(receiver);
    }

    /**
     * Api11+都可以使用的蓝颜扫描
     */
    private void doDiscovery() {

        if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
        mBluetoothAdapter.startDiscovery();
    }


    /**
     * The BroadcastReceiver that listens for discovered devices and changes the title when
     * discovery is finished
     */
    private final BroadcastReceiver receiver = new BroadcastReceiver() {

        ArrayList<BluetoothDevice> results = new ArrayList<>();

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    results.add(device);
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {

            }
        }
    };
}

小结

梳理知识,掌握脉络,万变不离其宗。

参考资料

https://developer.android.com/guide/topics/connectivity/bluetooth.html#ConnectingDevices
https://developer.android.com/guide/topics/connectivity/bluetooth-le.html

Android-Ble蓝牙通讯开发–扫描,连接,发送和接收数据,分包解包(附源码) 万物互联的物联网时代的已经来临,ble蓝牙开发在其中扮演着举重若轻的角色。最近刚好闲一点,抽时间梳理下这块的知识点。 涉及ble蓝牙通讯的客户端(开启、扫描、连接、发送和接收数据、分包解包)和服务端(初始化广播数据、开始广播、配置Services、Server回调操作)整个环节以及一些常见的问题即踩过的一些坑。 阅读详情

相关推荐

Android Studio制作手机App:通过手机蓝牙(Bluetooth)与STM32上的低功耗蓝牙(HC-42)连接通信,实现手机端对单片机的控制。

在这之前,我想先声明一点,手机与手机间的蓝牙连接方式”与“手机与HC间的蓝牙连接方式”是不一样的。原因就是手机搭配的是“经典蓝牙”模块,HC等蓝牙属于“低功耗蓝牙”模块。本文的内容只讲如何实现手机与HC蓝牙的配对,如果想了解一下手机与手机,手机与平板间的“经典蓝牙”通信方式,可以看我往期的博文,这篇博文讲的是如何制作一个基于蓝牙通信的聊天软件(类似于微信功能),也是一个挺有意思的项目(制作一个手机APP,无线连接HC蓝牙模块,将手机端数据发送给HC,从而控制STM32,文末会有资源分享。

weiybin的博客 2万+

Android 蓝牙通信

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

csdn_pun的博客 3316

Android 蓝牙开发(一)蓝牙通信

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

奋斗的菜鸟ing 7万+

Android硬件通信之 蓝牙通信

蓝牙通信

qq_29848853的博客 3845

手机APP开发之MIT Appinventor详细实战教程(一),利用通过蓝牙控制单片机,以及实现单片机与android设备之间的串口通信

MIT Appinventor 是编程领域较为受欢迎且适用的编程软件 ,其操作流程和使用方法简单,一种深受广大编程小镇的喜爱今天为大家介绍一种利用它开发手机APP的实战经历 ——手机APP开发之MIT Appinventor详细实战教程(一),利用通过蓝牙控制单片机,以及实现单片机与android设备之间的串口通信。 (一)前期软件准备和硬件准备 软件准备主要是keil uvisi...

weixin_42619971的博客 3万+

基于QT的安卓手机蓝牙APP开发

最近做项目,需要开发安卓应用,实现串口的收发,目测CH340G在安卓手机上非常麻烦,而且驱动都是Java版本的, 就没选择,博主在大二的时候学习过Java SE基本的语法,写过一些小程序就放弃了Java的道路。最后选择了蓝牙无线透传模块,实现串口通信。现在Qt跨平台支持安卓,是在是令人欣喜。在网上找资料,用Qt on Android做蓝牙驱动的几乎没有,也没有相关例程,所以准备撰写此文,献给广大嵌入式程序员们。 一、软硬件平台 1.1 硬件平台 1、蓝牙:HC-05,(淘宝上有卖),它的接口就是跟串口一样的

果果小师弟的博客 1万+

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

使用设备 基本概念 基本流程本文意在介绍蓝牙开发的主要流程,学习使用蓝牙开发一个星期了,写写一个星期以来遇到的一些小问题,还有介绍下流程。开发具有基本的通信功能,本项目主要是用于与蓝牙模块的串口读写功能。 下一篇文章还有Android开发之蓝牙(二)——基于BLE协议蓝牙模块通信: http://blog.youkuaiyun.com/wzhworld/article/details/76324738使用设

zh1豪的博客 3万+

安卓APP蓝牙上位机的编写一——APP结构

安卓APP蓝牙上位机开发的第一步

weixin_42119432的博客 1万+

Android 蓝牙通信开发(一) 搜索蓝牙设备

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

magician_Coder的博客 1537

Android蓝牙开发—经典蓝牙详细开发流程

https://blog.youkuaiyun.com/zqf_888/article/details/81060606

Errol's Blog 1万+

Viobot使用教程总贴

使用教程总贴

weixin_66666206的博客 1408

android spp 服务端,Android开发之蓝牙(一)——基于SPP协议蓝牙模块通信,androidspp...

Android开发之蓝牙(一)——基于SPP协议蓝牙模块通信,androidspp使用设备基本概念基本流程本文意在介绍蓝牙开发的主要流程,学习使用蓝牙开发一个星期了,写写一个星期以来遇到的一些小问题,还有介绍下流程。开发具有基本的通信功能,本项目主要是用于与蓝牙模块的串口读写功能。下一篇文章还有Android开发之蓝牙(二)——基于BLE协议蓝牙模块通信:http://blog.youkuaiyun.com/...

weixin_35976717的博客 1108

从原理到调试:手把手教你用Matlab解码互补格雷码与相移码(附数据与常见错误排查)

本文详细介绍了如何使用Matlab解码互补格雷码与相移码,涵盖从原理到调试的全过程。通过实际案例和代码示例,帮助读者掌握结构光三维重建中的相位解包裹技术,并提供了常见错误排查与优化建议,提升解码效率与准确性。

weixin_29090917的博客 218

Android学习—蓝牙

蓝牙 蓝牙是一种支持设备短距离通信的无线电技术。能在包括移动电话,PDA,无线耳机,笔记本电脑等众多设备之间进行无线信息交换。利用“蓝牙”技术,能够有效的简化移动通信终端设备之间的通信,也能够成功的简化设备与Internet之间的通信,使得数据传输变得更加迅速高效,为无线通信拓宽道路。 Android 2.0 引入蓝牙端口,开发时需要真机测试且硬件支持。 蓝牙的基本设置 //打开蓝牙设备 ...

qq_41951731的博客 204

技术美术个人笔记(十六)——Houdini到UE的大地形

文章目录关于地形白模制作关于PDG流程 关于地形白模制作 用heightfield noise和笔刷刷出合适的mask做山形叠加: 用remap可快捷反转mask ,用来做平原地区的小型丘陵: 接着用clip切平最低处,用distort制造侵蚀等,对部分地区用笔刷进行微调拓宽峡谷: 用经过sweep打平的曲线以及裁切的各种多面体用heightfield_project投影制作河道和水池,用blur mask并remap的方法拓宽河道 用distort、erode制造合适的弯曲和侵蚀,同理可挖出月牙形

小谁的博客 3178

实验四 基于IDEA+SpringBoot+Maven+Thymeleaf的系统实现实验目的

通过这次实验,我学习了如何成功进行IDEA+SpringBoot+maven的系统实现,搭建好IDEA+SpringBoot+maven的系统环境,成功地安装并配置了maven的运行环境,学会了创建springboot的三种方式,并且能将之前的实验结合起来运用。pom.xml中引入thymeleaf依赖,在classpath:/templates/下新建HTML页面(可采用实验一创建的页面),新建controller进行测试,访问页面后返回success成功数据。同时掌握和thymeleaf的基本语法。

m0_73502929的博客 1379

探索Android蓝牙应用程序下载仓库:打造嵌入式设备高效通信

探索Android蓝牙应用程序下载仓库:打造嵌入式设备高效通信 去发现同类优质开源项目:https://gitcode.com/ 在当下智能化设备的潮流中,蓝牙技术作为一种便捷的无线通信方式,被广泛应用于各种嵌入式设备。今天,我们就来介绍一款开源项目——Android蓝牙应用程序下载仓库,该项目为开发者提供了一个基于Android平台的蓝牙应用程序资源下载平台,下面让我们一起深入了解。 项目介绍 ...

gitblog_06740的博客 1037

SAP-ABAP:SAP信贷管理核心:CL_UKM_BUPA_FACTORY类完整解析与实践指南

SAP信贷管理核心类CL_UKM_BUPA_FACTORY解析 CL_UKM_BUPA_FACTORY是SAP信贷管理(UKM)模块的核心类,作为业务伙伴信贷数据的中央控制台,它提供: 统一入口:标准化实例化客户/供应商信贷对象 主要功能: 获取单条/批量业务伙伴数据 安全保存数据变更 封装底层数据访问逻辑 典型应用场景: 信贷主数据批量维护 自动化限额调整 订单信贷实时检查 性能优化建议: 使用批量方法替代循环单条处理 合理设置字段选择 实施缓存策略 该类通过简洁API简化复杂信贷操作,是SAP信贷管理开

baidu_35680696的博客 1208

FastBle终极指南:Android蓝牙BLE开发框架在物联网设备中的完整应用方案

FastBle是一款专为Android平台设计的蓝牙低功耗(BLE)快速开发框架,它为智能家居、物联网设备开发提供了简单高效的通信解决方案。作为Android蓝牙BLE开发框架,FastBle通过简洁的API封装了蓝牙设备过滤、扫描、连接、读写、通知订阅等复杂操作,让开发者能够快速构建稳定可靠的蓝牙通信应用。🚀 ## 为什么选择FastBle进行物联网开发? 在智能家居和物联网设备开发中,蓝

gitblog_01025的博客 666

华为OD机考真题 新系统 9月23号 【世界杯徽章换购】多语言题解

华为OD机试真题 新系统【世界杯徽章换购】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【世界杯徽章换购】多语言题解。

qq_45776114的博客 685

【逐函数讲解ORB_SLAM3源码】获取普通帧率、关键帧、IMU数据

获得imu的旋转 mRwc * mImuCalib.mTcb.rotationMatrix(); Sophus::SE3 mTwc 创建ORB_SLAM3系统 Create SLAM system. It initializes all system threads and gets ready to process frames. ORB_SLAM3::System SLAM(argv[1],argv[2],ORB_SLAM3::System::IMU_RGBD, true, 0, file_nam

小秋 AI SLAM 入门实战教程 1242

基于Simulink进行步进电机的重复控制仿真建模

重复控制概述目的:通过引入一个周期延迟环节来补偿系统的周期性误差,从而提高系统对周期性参考信号或扰动的跟踪性能。方法内部模型原理:在控制器中嵌入一个与周期性信号相关的模型(例如周期延迟环节),以实现对周期性误差的零稳态误差控制。滤波器设计:为了避免高频噪声放大,通常会在重复控制器中加入低通滤波器。优势对周期性信号具有良好的跟踪性能。不需要精确的数学模型即可实现高性能控制。挑战:需要合理设计重复控制器的参数以避免系统不稳定。

amy_mhd的博客 266

Android-BluetoothKit终极指南:如何快速构建稳定可靠的BLE蓝牙通信应用

Android-BluetoothKit是一个专为Android平台设计的BLE蓝牙通信框架,旨在彻底解决Android蓝牙开发中的兼容性、连接稳定性和API复杂性等核心痛点。这个强大的开源库通过精心设计的架构和简洁的API,让开发者能够快速实现与各种BLE设备的稳定通信,无论是智能家居、健康监测还是物联网设备,都能获得卓越的开发体验。 ## 🚀 从零到一:BLE开发的新起点 传统的Andr

gitblog_00514的博客 794

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

上一篇: Android开发之反射与注解
下一篇: GUI从入门到精通(一)
AnalyzeSystem
博客等级 码龄11年 204粉丝 114原创
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值