jQuery鼠标指针特效

Android--关于串口通信

利用串口,可以让Android主板与各种传感器和智能设备之间通信。Google自己有一个关于Android串口通信

集成环境

一般串口通信开发,需要用到JNI和NDK方面的知识。首先需要搭建环境,导入相应的.so文件(.so文件是Unix的动态连接库,本身是二进制文件,是由C/C++编译而来的),没有就自己新建libs,将.so文件复制进去。

之后需要再Gradle文件,将libs中的东西引入编译,不然访问不到。如下图

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"
    
    defaultConfig {
       ......

      /*选择处理器相应的架构*/
        ndk {
            abiFilters "armeabi"
        }

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
        .....
    /*加载 so库*/
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}

Google的demo中的 SerialPort.java (串口代码)

/*
 * Copyright 2009 Cedric Priscal
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package android_serialport_api;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.util.Log;

public class SerialPort {

	private static final String TAG = "SerialPort";

	/*
	 * Do not remove or rename the field mFd: it is used by native method close();
	 */
	private FileDescriptor mFd;
	private FileInputStream mFileInputStream;//输入流
	private FileOutputStream mFileOutputStream;//输出流

    // 设备号(串口地址),波特率,flags 默认为0
	public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {

		/* Check access permission  检查权限 */
		if (!device.canRead() || !device.canWrite()) {
			try {
				/* Missing read/write permission, trying to chmod the file */
				Process su;
				su = Runtime.getRuntime().exec("/system/bin/su");
				String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
						+ "exit\n";
				su.getOutputStream().write(cmd.getBytes());
				if ((su.waitFor() != 0) || !device.canRead()
						|| !device.canWrite()) {
					throw new SecurityException();
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new SecurityException();
			}
		}

		mFd = open(device.getAbsolutePath(), baudrate, flags);//打开串口
		if (mFd == null) {
			Log.e(TAG, "native open returns null");
			throw new IOException();
		}
		mFileInputStream = new FileInputStream(mFd);
		mFileOutputStream = new FileOutputStream(mFd);
	}

	// Getters and setters
	public InputStream getInputStream() {
		return mFileInputStream;
	}

	public OutputStream getOutputStream() {
		return mFileOutputStream;
	}

	// JNI
	private native static FileDescriptor open(String path, int baudrate, int flags);
	public native void close();
	static {
		System.loadLibrary("serial_port");
	}
}

// 如何使用
SerialPort serial = new SerialPort(new File("/dev/goc_serial"),115200,0);

**[SerialPortUtil](https://juejin.cn/post/6844903606982967303)**
public class SerialPortUtil {

    private SerialPort serialPort = null;
    private InputStream inputStream = null;
    private OutputStream outputStream = null;
    private ReceiveThread mReceiveThread = null;
    private boolean isStart = false;

    /**
     * 打开串口,接收数据
     * 通过串口,接收单片机发送来的数据
     */
    public void openSerialPort() {
        try {
            serialPort = new SerialPort(new File("/dev/ttyS0"), 9600, 0);
            //调用对象SerialPort方法,获取串口中"读和写"的数据流
            inputStream = serialPort.getInputStream();
            outputStream = serialPort.getOutputStream();
            isStart = true;

        } catch (IOException e) {
            e.printStackTrace();
        }
        getSerialPort();
    }

    /**
     * 关闭串口
     * 关闭串口中的输入输出流
     */
    public void closeSerialPort() {
        Log.i("test", "关闭串口");
        try {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
            isStart = false;
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 发送数据
     * 通过串口,发送数据到单片机
     *
     * @param data 要发送的数据
     */
    public void sendSerialPort(String data) {
        try {
            byte[] sendData = DataUtils.HexToByteArr(data);//字符串转为字节数组
            outputStream.write(sendData);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void getSerialPort() {
        if (mReceiveThread == null) {

            mReceiveThread = new ReceiveThread();
        }
        mReceiveThread.start();
    }

    /**
     * 接收串口数据的线程
     */

    private class ReceiveThread extends Thread {
        @Override
        public void run() {
            super.run();
            while (isStart) {
                if (inputStream == null) {
                    return;
                }
                byte[] readData = new byte[1024];
                try {
                    int size = inputStream.read(readData);
                    if (size > 0) {
                        String readString = DataUtils.ByteArrToHex(readData, 0, size);//字节数组转为字符串
                        //EventBus.getDefault().post(readString);事件通知,拿到数据
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

}



/**
 * 串口数据转换工具类
 */
public class DataUtils {
    //-------------------------------------------------------
    // 判断奇数或偶数,位运算,最后一位是1则为奇数,为0是偶数
    public static int isOdd(int num) {
        return num & 1;
    }

    //-------------------------------------------------------
    //Hex字符串转int
    public static int HexToInt(String inHex) {
        return Integer.parseInt(inHex, 16);
    }

    public static String IntToHex(int intHex){
        return Integer.toHexString(intHex);
    }

    //-------------------------------------------------------
    //Hex字符串转byte
    public static byte HexToByte(String inHex) {
        return (byte) Integer.parseInt(inHex, 16);
    }

    //-------------------------------------------------------
    //1字节转2个Hex字符
    public static String Byte2Hex(Byte inByte) {
        return String.format("%02x", new Object[]{inByte}).toUpperCase();
    }

    //-------------------------------------------------------
    //字节数组转转hex字符串
    public static String ByteArrToHex(byte[] inBytArr) {
        StringBuilder strBuilder = new StringBuilder();
        for (byte valueOf : inBytArr) {
            strBuilder.append(Byte2Hex(Byte.valueOf(valueOf)));
            strBuilder.append(" ");
        }
        return strBuilder.toString();
    }

    //-------------------------------------------------------
    //字节数组转转hex字符串,可选长度
    public static String ByteArrToHex(byte[] inBytArr, int offset, int byteCount) {
        StringBuilder strBuilder = new StringBuilder();
        int j = byteCount;
        for (int i = offset; i < j; i++) {
            strBuilder.append(Byte2Hex(Byte.valueOf(inBytArr[i])));
        }
        return strBuilder.toString();
    }

    //-------------------------------------------------------
    //转hex字符串转字节数组
    public static byte[] HexToByteArr(String inHex) {
        byte[] result;
        int hexlen = inHex.length();
        if (isOdd(hexlen) == 1) {
            hexlen++;
            result = new byte[(hexlen / 2)];
            inHex = "0" + inHex;
        } else {
            result = new byte[(hexlen / 2)];
        }
        int j = 0;
        for (int i = 0; i < hexlen; i += 2) {
            result[j] = HexToByte(inHex.substring(i, i + 2));
            j++;
        }
        return result;
    }

    /**
     * 按照指定长度切割字符串
     *
     * @param inputString 需要切割的源字符串
     * @param length      指定的长度
     * @return
     */
    public static List<String> getDivLines(String inputString, int length) {
        List<String> divList = new ArrayList<>();
        int remainder = (inputString.length()) % length;
        // 一共要分割成几段
        int number = (int) Math.floor((inputString.length()) / length);
        for (int index = 0; index < number; index++) {
            String childStr = inputString.substring(index * length, (index + 1) * length);
            divList.add(childStr);
        }
        if (remainder > 0) {
            String cStr = inputString.substring(number * length, inputString.length());
            divList.add(cStr);
        }
        return divList;
    }

    /**
     * 计算长度,两个字节长度
     *
     * @param val value
     * @return 结果
     */
    public static String twoByte(String val) {
        if (val.length() > 4) {
            val = val.substring(0, 4);
        } else {
            int l = 4 - val.length();
            for (int i = 0; i < l; i++) {
                val = "0" + val;
            }
        }
        return val;
    }

    /**
     * 校验和
     *
     * @param cmd 指令
     * @return 结果
     */
    public static String sum(String cmd) {
        List<String> cmdList = DataUtils.getDivLines(cmd, 2);
        int sumInt = 0;
        for (String c : cmdList) {
            sumInt += DataUtils.HexToInt(c);
        }
        String sum = DataUtils.IntToHex(sumInt);
        sum = DataUtils.twoByte(sum);
        cmd += sum;
        return cmd.toUpperCase();
    }

}




串口工具移植到系统

串口在系统framework 中的应用

关于USB

Android USB 转串口通信开发基本流程

public class MainActivity_Toast extends AppCompatActivity {
    public static String TAG = "MainActivity_Toast";

    private static final int REQUEST_OVERLAY = 5004;
    public static boolean CanShowFloat = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main__toast);
        UsbManager usbManager = (UsbManager) getApplicationContext().getSystemService(Context.USB_SERVICE);
        Map<String, UsbDevice> usbList = usbManager.getDeviceList();

        IntentFilter usbDeviceStateFilter = new IntentFilter();
        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        registerReceiver(mUsbReceiver, usbDeviceStateFilter);
    }

    BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                Log.e(TAG,"拔出usb了");
                Toast.makeText(MainActivity_Toast.this,"拔出usb了",Toast.LENGTH_SHORT).show();
            }else if(UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)){
                Log.e(TAG,"插入usb了");
                UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (device != null) {
                    toastDialog();
                    Log.e(TAG,"设备的ProductId值为:"+device.getProductId());
                    Log.e(TAG,"设备的VendorId值为:"+device.getVendorId());
                }
            }
        }
    };


    /*<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />*/
    private void toastDialog() {
        WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        RequestOverlayPermission(this);
        View view = LayoutInflater.from(this).inflate(R.layout.loading_layout,null);
        WindowManager.LayoutParams para  = new WindowManager.LayoutParams();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            para.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            para.type = WindowManager.LayoutParams.TYPE_PHONE;
        }
        wm.addView(view,para);
    }


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

    /**
     * 动态请求悬浮窗权限
     */
    public void RequestOverlayPermission(AppCompatActivity Instatnce) {
        if (Build.VERSION.SDK_INT >= 23) {
            if (!Settings.canDrawOverlays(Instatnce)) {
                //启动Activity让用户授权
                /*Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, 1010);*/

                String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
                Intent intent = new Intent(ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + Instatnce.getPackageName()));
                Instatnce.startActivityForResult(intent, REQUEST_OVERLAY);
            } else {
                CanShowFloat = true;
            }

        }
    }

    /** 浮窗权限请求,Activity执行结果,回调函数 */
    public  void onActivityResult(int requestCode, int resultCode, Intent data, final AppCompatActivity Instatnce)
    {
        // Toast.makeText(activity, "onActivityResult设置权限!", Toast.LENGTH_SHORT).show();
        if (requestCode == REQUEST_OVERLAY)		// 从应用权限设置界面返回
        {
            if(resultCode == AppCompatActivity.RESULT_OK)
            {
                CanShowFloat = true;		// 设置标识为可显示悬浮窗
            }
            else
            {
                CanShowFloat = false;

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    if (!Settings.canDrawOverlays(Instatnce))	// 若当前未允许显示悬浮窗,则提示授权
                    {
                        AlertDialog.Builder builder = new AlertDialog.Builder(Instatnce);
                        builder.setCancelable(false);
                        builder.setTitle("悬浮窗权限未授权");
                        builder.setMessage("应用需要悬浮窗权限,以展示浮标");
                        builder.setPositiveButton("去添加 权限", new DialogInterface.OnClickListener()
                        {
                            @Override
                            public void onClick(DialogInterface dialog, int which)
                            {
                                dialog.dismiss();

                                RequestOverlayPermission(Instatnce);
                            }
                        });

                        builder.setNegativeButton("拒绝则 退出", new DialogInterface.OnClickListener()
                        {
                            @Override
                            public void onClick(DialogInterface dialog, int which)
                            {
                                dialog.dismiss();

                                // 若拒绝了所需的权限请求,则退出应用
                                Instatnce.finish();
                                System.exit(0);
                            }
                        });
                        builder.show();
                    }
                }
            }
        }
    }
}
posted @   僵小七  阅读(1852)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示