最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【记录】编写Android中的蓝牙模块驱动和底层HART设备

Bluetooth crifan 4734浏览 0评论

【背景】

手上有个android设备:

现在希望可以在PAD上通过蓝牙去连接蓝牙的HART猫,然后再去操作HART的设备。

现在就是去android中写对应的蓝牙模块的程序,此处暂且叫做蓝牙驱动吧。

其中此处的开发环境是ADT。

【折腾过程】

1.参考:

Bluetooth | Android Developers

去添加权限:

add user permission android.permission.BLUETOOTH

添加好了:

added android.permission.BLUETOOTH and BLUETOOTH_ADMIN

2.

参考代码期间,遇到:

【已解决】Android的蓝牙实例代码中找不到REQUEST_ENABLE_BT

然后再去用BluetoothAdapter去检测是否支持蓝牙,且支持的话去打开:

	private final int REQUEST_ENABLE_BT = 1;
	private BluetoothAdapter mBluetoothAdapter;
	private void testBluetooth() {
		mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();

		if (mBluetoothAdapter == null) {
		    // Device does not support Bluetooth
			Toast.makeText(getApplicationContext(), "Device does not support Bluetooth", Toast.LENGTH_LONG).show();
		}
		else{
			//android.bluetooth.BluetoothAdapter@211120b0
			Toast.makeText(getApplicationContext(), "Device support Bluetooth", Toast.LENGTH_SHORT).show();
			
			if (!mBluetoothAdapter.isEnabled()) {
			    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
			}
			else{
				scanOrDiscoverBtDevices();
			}
		}

	}
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode == REQUEST_ENABLE_BT)
        {
        	if(resultCode == RESULT_OK){
        		Toast.makeText(getApplicationContext(), "Enabled Bluetooth now", Toast.LENGTH_LONG).show();
        		scanOrDiscoverBtDevices();
        	}
        	else{
        		Toast.makeText(getApplicationContext(), "Not enable Bluetooth !", Toast.LENGTH_LONG).show();
        	}
        }
    }

3.关于检测蓝牙状态变化,而去实现蓝牙被打开还是关闭了,可以参考:

Optionally, your application can also listen for the ACTION_STATE_CHANGED broadcast Intent, which the system will broadcast whenever the Bluetooth state has changed. This broadcast contains the extra fields EXTRA_STATE and EXTRA_PREVIOUS_STATE, containing the new and old Bluetooth states, respectively. Possible values for these extra fields are STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, and STATE_OFF. Listening for this broadcast can be useful to detect changes made to the Bluetooth state while your app is running.

4.另外,看到提示了:

Tip: Enabling discoverability will automatically enable Bluetooth. If you plan to consistently enable device discoverability before performing Bluetooth activity, you can skip step 2 above. Read about enabling discoverability, below.

这也就是我之前疑惑的:

别的那个android的app,可以点击开启蓝牙,而无需弹出请求权限的对话框。

就是通过这个discovery功能实现的。

 

提示:

后来又遇到:

【已解决】Android中运行startActivityForResult后但是onActivityResult不执行

 

5.目前已经实现了,既可以scan那些paired,也可以discover搜寻当前附近的蓝牙设备。

代码如下:

	private final int REQUEST_ENABLE_BT = 1;
	private BluetoothAdapter mBluetoothAdapter;
	private void testBluetooth() {
		mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();

		if (mBluetoothAdapter == null) {
		    // Device does not support Bluetooth
			Toast.makeText(getApplicationContext(), "Device does not support Bluetooth", Toast.LENGTH_LONG).show();
		}
		else{
			//android.bluetooth.BluetoothAdapter@211120b0
			Toast.makeText(getApplicationContext(), "Device support Bluetooth", Toast.LENGTH_SHORT).show();
			
			if (!mBluetoothAdapter.isEnabled()) {
			    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
			}
			else{
				scanOrDiscoverBtDevices();
			}
		}

	}

	private void scanOrDiscoverBtDevices(){
		//scanBtDevices();
		
		discoverBtDevices();
	}
	
	// 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 btDev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
	            //mArrayAdapter.add(btDev.getName() + "\n" + btDev.getAddress());
	            Toast.makeText(getApplicationContext(), btDev.getName() + "\n" + btDev.getAddress(), Toast.LENGTH_LONG).show();
	        }
	    }
	};

	private void discoverBtDevices(){
		// Register the BroadcastReceiver
		IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
		registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
		
		mBluetoothAdapter.startDiscovery();
	}
	
	private void scanBtDevices(){
	    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); //[BC:85:1F:96:99:C9, 00:06:66:4C:75:FE]
	 // If there are paired devices
	 if (pairedDevices.size() > 0) {
		 //ArrayAdapter mArrayAdapter = new ArrayAdapter();
		 
	     // Loop through paired devices
	     for (BluetoothDevice btDev : pairedDevices) {
	         //mArrayAdapter.add(btDev.getName() + "\n" + btDev.getAddress());
	    	 Toast.makeText(getApplicationContext(), btDev.getName() + "\n" + btDev.getAddress(), Toast.LENGTH_LONG).show();
	     }
	 }
	}
	
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode == REQUEST_ENABLE_BT)
        {
        	if(resultCode == RESULT_OK){
        		Toast.makeText(getApplicationContext(), "Enabled Bluetooth now", Toast.LENGTH_LONG).show();
        		scanOrDiscoverBtDevices();
        	}
        	else{
        		Toast.makeText(getApplicationContext(), "Not enable Bluetooth !", Toast.LENGTH_LONG).show();
        	}
        }
    }

6.继续折腾。

对于可以被搜索“Enabling discoverability”暂时就不去关心了。毕竟当前android设备能搜到HART猫即可。暂时无需考虑被搜索到。

7.再去建立连接,期间出现UUID和connect失败方面的问题:

【已解决】Android中连接蓝牙设备时遇到createRfcommSocketToServiceRecord的UUID问题和BluetoothSocket的connect失败

最终,用如下代码:

	private String mactekHartModemName;
	private UUID mactekHartModemUuid;
    
	//void afterFoundBtHartModem(BluetoothDevice btDev, Parcelable[] btDevUuid){
	void afterFoundBtHartModem(BluetoothDevice btDev, UUID btDevUuid){
		if(null != btDevUuid){
						
		}
		
    	//mactekHartModemName = btDev.getName(); //"MACTekViator75FE"
    	//mactekHartModemUuid = UUID.fromString(mactekHartModemName);
		
		String uuidValue;
		//http://www.guidgenerator.com/online-guid-generator.aspx
		//uuidValue = "e214d9ae-c3ba-4e25-abb5-299041353bc3";
		
		//https://groups.google.com/forum/#!topic/android-developers/vyTEJOXELos
		//uuidValue = "00001101-0000-100­0-8000-00805F9B34FB";
		//uuidValue = "00000003-0000-100­0-8000-00805F9B34FB";
		uuidValue = "00001101-0000-1000-8000-00805F9B34FB";

		mactekHartModemUuid = UUID.fromString(uuidValue);

    	ConnectThread connectBtThread = new ConnectThread(btDev);
    	connectBtThread.start();
	}

    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
     
        public ConnectThread(BluetoothDevice device) {
            // Use a temporary object that is later assigned to mmSocket,
            // because mmSocket is final
            BluetoothSocket tmp = null;
            mmDevice = device;
     
            // Get a BluetoothSocket to connect with the given BluetoothDevice
            try {
                // MY_UUID is the app's UUID string, also used by the server code
                tmp = device.createRfcommSocketToServiceRecord(mactekHartModemUuid);//00001101-0000-1000-8000-00805F9B34FB

            } catch (IOException e) { }
            mmSocket = tmp;
        }
     
        public void run() {
            // Cancel discovery because it will slow down the connection
            mBluetoothAdapter.cancelDiscovery();
     
            try {
                // Connect the device through the socket. This will block
                // until it succeeds or throws an exception
                mmSocket.connect();
            } catch (IOException connectException) {
                // Unable to connect; close the socket and get out
                try {
                    mmSocket.close();
                } catch (IOException closeException) { }
                return;
            }
     
            // Do work to manage the connection (in a separate thread)
            manageConnectedSocket(mmSocket);
        }
     
        /** Will cancel an in-progress connection, and close the socket */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }

可以正常触发android系统打开配对窗口,输入密码:

pair with MACTekViator75FE bluetooth hart modem

后,是可以配对成功的:

use uuid 00001101-0000-1000-8000-00805F9B34FB then bluetooth connect will ok

8.然后接着再去试试发送数据给蓝牙HART猫是否成功,能否收到返回的数据。

最后所用代码为:

	private final int REQUEST_ENABLE_BT = 1;
	private BluetoothAdapter mBluetoothAdapter;
	private void testBluetooth() {
		mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();

		if (mBluetoothAdapter == null) {
		    // Device does not support Bluetooth
			Toast.makeText(getApplicationContext(), "Device does not support Bluetooth", Toast.LENGTH_LONG).show();
		}
		else{
			//android.bluetooth.BluetoothAdapter@211120b0
			Toast.makeText(getApplicationContext(), "Device support Bluetooth", Toast.LENGTH_SHORT).show();
			
			if (!mBluetoothAdapter.isEnabled()) {
			    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
			}
			else{
				scanOrDiscoverBtDevices();
			}
		}
	}

	private void scanOrDiscoverBtDevices(){
		//scanBtDevices();
		
		discoverBtDevices();
	}
    
	// 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 btDev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

	            //mArrayAdapter.add(btDev.getName() + "\n" + btDev.getAddress());
		    	 String btDevName = btDev.getName();
		    	 String btDevMacAddr = btDev.getAddress();
	            Toast.makeText(getApplicationContext(), btDevName + "\n" + btDevMacAddr, Toast.LENGTH_LONG).show();

	            if(btDevName.contains("MACTekViator")){ //MACTekViator75FE
	            	afterFoundBtHartModem(btDev);
	            }
	        }
	    }
	};

	private void discoverBtDevices(){
		// Register the BroadcastReceiver
		IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
		registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
		
		mBluetoothAdapter.startDiscovery();
	}
	
	private void scanBtDevices(){
	    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); 
	    //[BC:85:1F:96:99:C9, 00:06:66:4C:75:FE]

		 // If there are paired devices
		 if (pairedDevices.size() > 0) {

		     // Loop through paired devices
		     for (BluetoothDevice btDev : pairedDevices) {
		         //mArrayAdapter.add(btDev.getName() + "\n" + btDev.getAddress());
		    	 //Toast.makeText(getApplicationContext(), btDev.getName() + "\n" + btDev.getAddress(), Toast.LENGTH_LONG).show();
		    	 
		    	 String btDevName = btDev.getName();
		    	 String btDevMacAddr = btDev.getAddress();
		    	 Toast.makeText(getApplicationContext(), btDevName + "\n" + btDevMacAddr, Toast.LENGTH_LONG).show();
	
				if(btDevName.contains("MACTekViator")){ //found our concerned Bluetooth HART Modem
					afterFoundBtHartModem(btDev);
					break;
				}
		     }
	 }
	}
	
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode == REQUEST_ENABLE_BT)
        {
        	if(resultCode == RESULT_OK){
        		Toast.makeText(getApplicationContext(), "Enabled Bluetooth now", Toast.LENGTH_LONG).show();
        		scanOrDiscoverBtDevices();
        	}
        	else{
        		Toast.makeText(getApplicationContext(), "Not enable Bluetooth !", Toast.LENGTH_LONG).show();
        	}
        }
    }
    
	private UUID mactekHartModemUuid;
    
	void afterFoundBtHartModem(BluetoothDevice btDev){
		String sspUuid;
		
		//http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createRfcommSocketToServiceRecord%28java.util.UUID%29
		//Hint: If you are connecting to a Bluetooth serial board then try using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
		//However if you are connecting to an Android peer then please generate your own unique UUID. 
		sspUuid = "00001101-0000-1000-8000-00805F9B34FB";

		mactekHartModemUuid = UUID.fromString(sspUuid);

    	ConnectThread connectBtThread = new ConnectThread(btDev);
    	connectBtThread.start();
	}

    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;

        public ConnectThread(BluetoothDevice device) {
            // Use a temporary object that is later assigned to mmSocket,
            // because mmSocket is final
            BluetoothSocket tmp = null;
            mmDevice = device;
     
            // Get a BluetoothSocket to connect with the given BluetoothDevice
            try {
                // MY_UUID is the app's UUID string, also used by the server code
                tmp = device.createRfcommSocketToServiceRecord(mactekHartModemUuid);//00001101-0000-1000-8000-00805F9B34FB

            } catch (IOException e) { }
            mmSocket = tmp;
        }
     
        public void run() {
            // Cancel discovery because it will slow down the connection
            mBluetoothAdapter.cancelDiscovery();
     
            try {
                // Connect the device through the socket. This will block
                // until it succeeds or throws an exception
                mmSocket.connect();
            } catch (IOException connectException) {
                // Unable to connect; close the socket and get out
                try {
                    mmSocket.close();
                } catch (IOException closeException) { }
                return;
            }
     
            // Do work to manage the connection (in a separate thread)
            manageConnectedSocket(mmSocket);
        }
     
        /** Will cancel an in-progress connection, and close the socket */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }    

    private void manageConnectedSocket(BluetoothSocket socket){
    	//Toast.makeText(getApplicationContext(), "Bluetooth HART Modem Connected", Toast.LENGTH_LONG).show();
    	ConnectedThread connectedThread = new ConnectedThread(socket);
    	connectedThread.start();

    	//command 0
    	String command0Str = "FFFFFFFFFF0280000082";
		byte[] commnd0Bytes = HexString2Bytes(command0Str);
    	connectedThread.write(commnd0Bytes);
    	//read out command 0 response
    	byte[] readoutBuffer = new byte[1024];
    	
    	//Note: here use DEBUG, so will take some time, so follow can read out real response data
    	//if no DEBUG, just run through, will only get -1
    	//readoutBuffer = connectedThread.read(); //[-1,
    	readoutBuffer = connectedThread.read(); //[-1, -1, -1, -1, 6, -128, 0, 14, 0, 69, -2, 54, 2, 5, 5, 2, 13, 1, 3, 43, -11, -82, 122, 0, 0, 0, 0,
    	parseHartCommand0Resp(readoutBuffer);
    	//readoutBuffer = connectedThread.read();
    }
    
    private void parseHartCommand0Resp(byte[] command0RespBytes){
    	//-1, -1, -1, -1, 6, -128, 0, 14, 0, 69, -2, 54, 2, 5, 5, 2, 13, 1, 3, 43, -11, -82, 122,
    	//FF FF FF FF 06 80 00 0E 00 45 FE 36 02 05 02 0D 01 03 2B F5 AE 7A
    	//(hex value) --->
    	//FF FF FF FF 06 80 00 0E 00 45 
    	//FE ==254(expansion)
    	//ManufactureIdentificationCode=36==Yamatake
    	//ManufactureDeviceType=02
    	//PreampleNumber=5 
    	//UniversalCommandRevision=5==HART5
    	//DeviceSpecificCommandRevision=2
    	//SoftwareRevision=13
    	//HardwareRevision=1
    	//DeviceFunctionFlag=3
    	//DeviceIdNumber=2B F5 AE
    	//CommonPracticeCommandRevision=7A
    }

	private boolean isReadoutRespOk = false; 
	
	private class ConnectedThread extends Thread {
	    private final BluetoothSocket mmSocket;
	    private final InputStream mmInStream;
	    private final OutputStream mmOutStream;
	 
	    public ConnectedThread(BluetoothSocket socket) {
	        mmSocket = socket;
	        InputStream tmpIn = null;
	        OutputStream tmpOut = null;
	 
	        // Get the input and output streams, using temp objects because
	        // member streams are final
	        try {
	            tmpIn = socket.getInputStream();
	            tmpOut = socket.getOutputStream();
	        } catch (IOException e) { }
	 
	        mmInStream = tmpIn;
	        mmOutStream = tmpOut;
	    }
	 
	    public void run() {
//	        byte[] buffer = new byte[1024];  // buffer store for the stream
//	        int bytes; // bytes returned from read()
//	 
//	        // Keep listening to the InputStream until an exception occurs
//	        while (true) {
//	            try {
//	                // Read from the InputStream
//	                bytes = mmInStream.read(buffer);
//	                // Send the obtained bytes to the UI activity
//	                //mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
//	                if(bytes > 0){
//	                	//Toast.makeText(getApplicationContext(), "Got HART Command 0 resp data: " + buffer.toString(), Toast.LENGTH_LONG).show();
//	                	isReadoutRespOk = true;
//	                	
//	                	//-1, -1, -1, -1, 6, -128, 0, 14, 0, 69, -2, 54, 2, 5, 5, 2, 13, 1, 3, 43, -11, -82, 122,
//	                	//parseHartCommand0ResponseData();
//	                }
//	                else{
//	                	isReadoutRespOk = false;
//	                }
//
//	            } catch (IOException e) {
//	                break;
//	            }
//	        }
	    }
	 
	    /* Call this from the main activity to send data to the remote device */
	    public void write(byte[] bytes) {
	        try {
	            mmOutStream.write(bytes);
	        } catch (IOException e) { }
	    }
	 
	    public byte[] read() {
	    	byte[] buffer = new byte[1024];  // buffer store for the stream
	    	int bytes;
	        try {
	        	bytes = mmInStream.read(buffer);
	        } catch (IOException e) { 
	        	
	        }
			return buffer;
	    }
	 
	    /* Call this from the main activity to shutdown the connection */
	    public void cancel() {
	        try {
	            mmSocket.close();
	        } catch (IOException e) { }
	    }
	}
    
	//copy from other code
	// string to hex array
	public byte[] HexString2Bytes(String hexStr) {
		if (null == hexStr || 0 == hexStr.length()) {
			return null;
		}
		byte[] ret = new byte[hexStr.length() / 2];
		byte[] tmp = hexStr.getBytes();
		for (int i = 0; i < (tmp.length / 2); i++) {
			ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
		}
		return ret;
	}
	
	private  byte uniteBytes(byte src0, byte src1) {
		byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 }))
				.byteValue();
		_b0 = (byte) (_b0 << 4);
		byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 }))
				.byteValue();
		byte result = (byte) (_b0 | _b1);
		return result;
	}
 

 

【总结】

至此,终于算是跑通了,可以通过:

平板上面的蓝牙,扫描找到蓝牙HART猫,然后通过HART猫发送command 0给HART设备,获得对应的信息:

command 0 can response data bytes from mactek viator bluetooth hart modem

 

注:

此处所用的HART设备和这里:

【记录】试用通过蓝牙操作HART设备的Android的程序:teknikol COMMANDER

是一样的:

所以上述代码解析后的信息,也是和之前一致的。

转载请注明:在路上 » 【记录】编写Android中的蓝牙模块驱动和底层HART设备

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
83 queries in 0.187 seconds, using 22.12MB memory