Android实现和下位机蓝牙通讯

Posted JMatrix

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android实现和下位机蓝牙通讯相关的知识,希望对你有一定的参考价值。

1.在程序配置文件中声明蓝牙权限。

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

2.新建通用的扫描选择蓝牙设备的布局和活动。

activity_bt.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btnQueryBt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="直接进入软件"
        android:layout_marginTop="16dp"/>
    <Button
        android:id="@+id/btnSearchBt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="扫描蓝牙设备"
        android:layout_marginTop="16dp"/>

    <TextView
        android:id="@+id/txtConnectState"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"/>

    <ListView
        android:id="@+id/lstBtName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp">

    </ListView>


</LinearLayout>

BtActivity.java

public class BtActivity extends BaseActivity {
    private Button btnSearchBt;//初始界面的扫描按钮
    private Button btnIntoQuery;//初始界面的进入查询按钮
    private TextView txtConnectState;
    private ListView lstBtName;
    private BtOperation btOperation = new BtOperation();
    private BluetoothReceiver mBluetoothReceiver = new BluetoothReceiver();
    ArrayList<String> btNameList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_bt);

//        checkBtUnable();
//        initButton();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
//        unregisterReceiver(mBluetoothReceiver);
    }

    class BluetoothReceiver extends BroadcastReceiver{

        @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);
                btNameList.add(device.getName());
                btNameList.add(device.getAddress());
                //如果设备还没绑定,则将设备显示到列表中,并提醒用户配对
                if(device.getBondState()!=BluetoothDevice.BOND_BONDED){

                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                            BtActivity.this,android.R.layout.simple_list_item_1,btNameList);
                    lstBtName.setAdapter(adapter);

                    txtConnectState.setText("选择设备进行配对");
                }
                //如果设备已经配对,直接显示在列表中,然后直接跳转到主界面
                else {
                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                            BtActivity.this,android.R.layout.simple_list_item_1,btNameList);
                    lstBtName.setAdapter(adapter);
                    txtConnectState.setText("配对成功");
//                    RunActivity.actionStart(BtActivity.this,device.getAddress());
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                txtConnectState.setText("蓝牙设备搜索完毕");
            } else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
                txtConnectState.setText("正在扫描蓝牙设备");
            } else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if(device.getBondState()== BluetoothDevice.BOND_BONDING){
                    txtConnectState.setText("正在配对中");
                }else if(device.getBondState() == BluetoothDevice.BOND_BONDED){
                    txtConnectState.setText("配对成功");
//                    RunActivity.actionStart(BtActivity.this,device.getAddress());
                }
            }
        }
    }
    /**
     * 检查用户设备是否支持蓝牙,如果不支持,就退出程序,并提示用户。如果支持,直接打开蓝牙。
     */
    private void checkBtUnable(){

        if(!btOperation.checkBt()){
            Toast.makeText(this,"你的设备不支持蓝牙!请更换设备!",Toast.LENGTH_LONG).show();
//            finish();
        }
    }

    /**
     * 初始化启动界面的两个按钮
     */
    private void initButton(){
        txtConnectState = (TextView)findViewById(R.id.txtConnectState);
        //蓝牙设备清单,点击特定名称,可以连接设备
        lstBtName = (ListView)findViewById(R.id.lstBtName);
        lstBtName.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                if(btNameList.get(i).equals("HC-05")){
                    btOperation.devBond(btNameList.get(i+1));
                }
            }
        });
        //蓝牙操作按钮
        btnSearchBt = (Button)findViewById(R.id.btnSearchBt);
        btnSearchBt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                btOperation.openBt();
            }
        });

        btnIntoQuery = (Button)findViewById(R.id.btnQueryBt);
        //进入查询数据界面
        btnIntoQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                Intent intent = new Intent(BtActivity.this,QueryActivity.class);
//                startActivity(intent);
            }
        });

        btOperation.registerBtReceiver(this,mBluetoothReceiver);

        //初始化最后直接打开蓝牙,如果之前绑定过蓝牙,会直接进入,否则需要点击扫描按钮来打开蓝牙
        btOperation.openBt();
    }


}

设备配对后,还需要开启后台蓝牙消息服务

SerialBtService.java

/**
 * 的到一个远程设备地址后,和地址建立连接,建立输入输出流,
 * <p>提供发送蓝牙信息的封装</p>
 * <p>将接收的蓝牙信息拆包并用内部广播发送出去</p>
 */
public class SerialBtService extends Service {
    private static final UUID SerialBlueToothDev_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private String devAddr = null;
    private OutputStream mOutputStream=null;
    private InputStream mInputStream=null;
    private BluetoothAdapter mBluetoothAdapter=null;
    private BluetoothSocket mBluetoothSocket=null;
    private boolean mFlag=true;
    private int[] dataRec = new int[100];
    private int couts = 0;
    /**
     * 启动蓝牙传输服务类所需要的蓝牙设备地址
     */
    public static final String DEV_ADDR = "devAddr";
    /**
     * 向外发送蓝牙信息的广播Intent中,信息的标识符
     */
    public static final String BT_SEND_MESS = "BT_CMD";
    /**
     * 从外部接收的蓝牙信息的广播Intent中,信息的标识符
     * <p>表示一个拆包的信息已经发送了,需要的模块可以接收</p>
     */
    public static final String BT_RECEIVE_MESS = "BT_DATA";
    /**
     * 向外发送蓝牙信息时的注册广播标识符
     */
    public static final String BT_SEND_BROADCAST = "com.jiaweiqiang.tools.SEND_MSG";
    /**
     * 接收外部蓝牙信息时的注册广播标识符
     */
    public static final String  BT_RECEIVE_BROADCAST = "com.jiaweiqiang.fycdy.RECEIVE_MSG";
    public SerialBtService() {
    }
    //接收其他活动传来的要发送的蓝牙信息的广播接收器
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
//            String btMessage = intent.getStringExtra(BT_SEND_MESS);
            int[] btMessage = intent.getIntArrayExtra(BT_SEND_MESS);
            if(btMessage==null||btMessage.equals("")){
                Toast.makeText(MyApplication.getContext(),"无效输入",Toast.LENGTH_SHORT).show();
            }else {
                writeSingleData(btMessage);
            }

        }
    };

    @Override
    public void onDestroy() {
        super.onDestroy();
        try{
            mBluetoothSocket.close();

        }catch (IOException e){
            e.printStackTrace();
        }
        MyApplication.getLocalBroadcastManager().unregisterReceiver(mReceiver);
    }

    /**
     * 启动本服务的启动函数
     * @param context 启动服务的上下位
     * @param devAddr 蓝牙设备的mac地址
     */
    public static void startService(Context context,String devAddr){
        Intent intent = new Intent(MyApplication.getContext(),SerialBtService.class);
        intent.putExtra(DEV_ADDR,devAddr);
        context.startService(intent);
    }
    public static void stopService(Context context){
        Intent intent = new Intent(context,SerialBtService.class);
        context.stopService(intent);
    }

    /**
     * 通过服务发送蓝牙数据
     * @param mess 要发送的字符串数据
     */
    public static void sendStringMess(String mess){
        int[] data = Convert.stringToIntArray(mess);
        sendIntArrayMess(data,data.length);
    }

    /**
     * 发送byte数组出去,将每个byte的值存入int数组中,将字节数组发送出去
     * @param mess 要发送的字节数组
     * @param lenth 发送字节的长度
     */
    public static void sendIntArrayMess(int[] mess, int lenth){
        int[] copyMess = new int[lenth];
        System.arraycopy(mess,0,copyMess,0,lenth);

        Intent intent = new Intent(BT_SEND_BROADCAST);
        intent.putExtra(SerialBtService.BT_SEND_MESS,copyMess);
        MyApplication.getLocalBroadcastManager().sendBroadcast(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        devAddr = intent.getStringExtra(DEV_ADDR);//通过建立Service,Intent传入蓝牙地址
        //声明并注册发送蓝牙信息的广播接收器,以便接收到广播后,将信息发送出去
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BT_SEND_BROADCAST);
        MyApplication.getLocalBroadcastManager().registerReceiver(mReceiver,intentFilter);

        mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
        //为接收蓝牙信息单独开启一个线程
        SerialBtThread thread = new SerialBtThread();
        thread.start();

//        Toast.makeText(MyApplication.getContext(),"服务已经启动",Toast.LENGTH_SHORT).show();

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    /**
     * 向外发送蓝牙信息,这是以string的形式发送的
     * @param msg 要发送的信息
     */
    private void writeSerialBt(String msg){
        try{
            mOutputStream.write(msg.getBytes());
            mOutputStream.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 传入int数组,以单个字节形式循环把数组发送出去
     * @param msg
     */
    private void writeSingleData(int[] msg){
        try{
            for (int i= 0;i<msg.length;i++){
                mOutputStream.write(msg[i]);
            }
            mOutputStream.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    private class SerialBtThread extends Thread{
        @Override
        public void run() {
            super.run();
            //在线程中初始化蓝牙设备,和socket
            try{
                BluetoothDevice device=mBluetoothAdapter.getRemoteDevice(devAddr);
                mBluetoothSocket=device.createRfcommSocketToServiceRecord(SerialBlueToothDev_UUID);
            }catch (Exception e){
                e.printStackTrace();
                mFlag=false;
            }

            mBluetoothAdapter.cancelDiscovery();
            //连接远程设备,建立socket
            try{
                mBluetoothSocket.connect();
            }catch (IOException e){
                e.printStackTrace();
                try{
                    mBluetoothSocket.close();
                    mFlag=false;
                }catch (IOException e1){
                    e1.printStackTrace();
                }
            }
            //如果建立过程没有异常,则得到输入流和输出流
            if(mFlag){
                try {
                    mInputStream=mBluetoothSocket.getInputStream();
                    mOutputStream=mBluetoothSocket.getOutputStream();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            //开启循环接收蓝牙信息,间隔30ms
            while(true){
                readSerialBlueTooth();
                try {
                    Thread.sleep(30);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 在线程中读取蓝牙数据
     */
    private void readSerialBlueTooth() {
        int ret=0;
        int data;

        try {
            ret=mInputStream.available();
            for(int i=0;i<ret;i++){
                data = mInputStream.read();
                readPackage(data);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 接收单个的byte数据,不断添加到数组中,不断判断是否数据包已经完成
     * 进行拆包,把拆包后的信息发送出去
     * @param n
     */
    private void readPackage(int n){

        dataRec[couts] = n;
        couts++;
        if(couts >= 4){
            if(dataRec[couts-1] == 0x3c){
                if(dataRec[couts-2] == 0xc3){
                    Log.d("btTest", "readPackage: 收到包");
                    int[] copyDataRec = new int[couts-3];
                    //将数据包拆包,并取得包内数据,复制到新的数组中
                    System.arraycopy(dataRec,1,copyDataRec,0,couts-3);
                    couts = 0;
                    //将包内数据通过蓝牙发送出去
                    Intent intent = new Intent(BT_RECEIVE_BROADCAST);
                    intent.putExtra(BT_RECEIVE_MESS,copyDataRec);
                    Log.d("btTest", "readPackage: 把数组放入intent");
                    MyApplication.getLocalBroadcastManager().sendBroadcast(intent);
                }
            }
        }

    }
}

别忘了在程序配置文件中注册活动和服务

<activity android:name=".BtActivity"
            android:launchMode="singleInstance">
        </activity>
        <service android:name="com.jiaweiqiang.tools.SerialBtService" />

 

以上是关于Android实现和下位机蓝牙通讯的主要内容,如果未能解决你的问题,请参考以下文章

上下位机网络通讯怎么保密运行

什么是上位机和下位机

如何编程使上位机(界面c#)与下位机(单片机keil c)通过TCP/UDP协议来实现通信,最好有源代码,谢谢~~

本人想通过moxa nport5650实现下位机modbus与上位机wincc通讯,从wincc采集nport里数据该怎么做?

用C#语言开发上位机(来控制下位机比如了解下位机测量的温度湿度压力并控制下位机)要用到啥知识?

Android6.0 蓝牙通讯的实现