4

我用ionic撸了一个USB转串口的调试工具

 3 years ago
source link: https://segmentfault.com/a/1190000040616338
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

由于最近应产品经理的需求,需要做一个Android版的上位机APP,为此专门到某宝上购买了一个Type-C转串口的小设备,然后就开始折腾了。花了几天的时间就把上位机APP做出来了,后来在空闲时间又做了一个串口调试的小工具,效果如下图

串口调试

ionic start blank

创建一个空白项目

安装串口插件

要做一个串口通讯的工具,那就得和硬件打交道,正好根据ionic官方文档,我找到了一个串口通讯的插件,名为cordovarduino,经过尝试之后,发现此插件由于久年失修,虽然可以使用,但是在收发数据的时候总是无法完整接收到数据。根据对其代码查看,发现其中lib目录下有一个usbseriallibrary.jar文件,这个应该就是USB串口的驱动文件了吧。

久年失修的插件,估计就是这个jar包有问题,应该更新一下这个jar包就可以了,因此,通过usb-serial-for-android这个项目的介绍我重新打包了一个jar包,完成后尝试了一下,确实很完美,并且收发数据也没有任何问题了。因此,自己根据cordovarduino项目重新copy了一个项目cordova-plugin-usbserialport,因此你只需要安装我提供的插件即可

安装串口插件

ionic cordova plugin add cordova-plugin-usbserialport

安装本地数据存储插件

ionic cordova plugin add cordova-plugin-nativestorage
npm install @ionic-native/native-storage

安装状态栏插件

ionic cordova plugin add cordova-plugin-statusbar
npm install @ionic-native/status-bar

安装设备信息插件

ionic cordova plugin add cordova-plugin-device
npm install @ionic-native/device

安装获取版本号插件

ionic cordova plugin add cordova-plugin-app-version
npm install @ionic-native/app-version

安装APP最小化插件

ionic cordova plugin add cordova-plugin-appminimize
npm install @ionic-native/app-minimize

安装后台运行插件

ionic cordova plugin add cordova-plugin-background-mode
npm install @ionic-native/background-mode

串口操作主要代码

declare let usbSerialPort: any; // 引入串口插件
// 打开串口
async openSerialPort() {
    const config = await this.nativeStorage.getItem('config');
    // First request permission
    usbSerialPort.requestPermission(() => {
        console.log('get permission success.');
        usbSerialPort.getDevice(data => {
            this.title = data.name;
        });
        // open serial
        usbSerialPort.open(config, () => {
            console.log('Serial connection opened');
            // get open status
            this.isOpen();
            // read listener
            usbSerialPort.readListener(data => {
                clearTimeout(this.timer);
                const view = new Uint8Array(data);
                console.log(this.utils.bytes2HexString(view));
                this.receiveDataArray.push(view);
                this.timer = setTimeout(() => {
                    const now = new Date();
                    const dateMs = now.getMilliseconds();
                    this.zone.run(() => {
                        const date = `<span style="color: #2fdf75">${this.utils.formatDate(now, 'hh:mm:ss')}.${dateMs} > </span>`;
                        const resultUint8Array = this.utils.concatUint(Uint8Array, ...this.receiveDataArray);
                        if (!this.utils.bytes2HexString(resultUint8Array)) {
                            return;
                        }
                        this.receiveData += `
<div style="
-webkit-user-select: auto;
-moz-user-select: auto;
-ms-user-select: auto;
user-select: auto;">
${date}${this.utils.strDivision(this.utils.bytes2HexString(resultUint8Array), 2)}
</div>
`;
                        this.receiveData += `<div style="margin-top:8px"></div>`;
                        this.receiveLength = this.utils.bytes2HexString(resultUint8Array).length / 2;
                        this.scrollToBottom();
                    });
                }, 500);
            }, err => {
                console.log(`Read listener error: ${err}`);
            });
        });
    }, err => {
        console.log(`Get permission error: ${err}`);
        if (this.openStatus) {
            this.zone.run(() => {
                this.openStatus = false;
                this.title = this.translate.instant('SERIAL_DEVICE_TITLE');
            });
        }
        this.presentToast(this.translate.instant('NO_DEVICE_CONNECTED'));
    });
}
// 串口写入
writerSerial() {
    if (!this.openStatus) {
        if (this.pack) {
            this.presentAlert();
        }
        return;
    }
    this.receiveDataArray = [];
    const now = new Date();
    const dateMs = now.getMilliseconds();
    if (this.isWriterHex) {
        usbSerialPort.writeHex(this.pack, (res: any) => {
            console.log('writer res: ', res);
            const date = `<span style="color:#3880ff">${this.utils.formatDate(now, 'hh:mm:ss')}.${dateMs} < </span>`;
            this.receiveData += `<div>${date}${this.utils.strDivision(this.pack, 2)}</div>`;
            this.sendLength = this.pack.length / 2;
        }, err => {
            console.log('writer hex err: ', err);
            this.presentToast();
            this.closeSerial();
        });
    } else {
        usbSerialPort.write(this.pack, (res: any) => {
            console.log('writer res: ', res);
            const date = `<span style="color:#3880ff">${this.utils.formatDate(now, 'hh:mm:ss')}.${dateMs} < </span>`;
            this.receiveData += `<div>
${date}${this.utils.strDivision(this.utils.bufToHex(this.utils.stringToBytes(this.pack)), 2)}
</div>`;
            this.sendLength = this.utils.getStringByteLength(this.pack);
        }, err => {
            console.log('writer string err: ', err);
            this.presentToast();
            this.closeSerial();
        });
    }
}
// 串口开启状态
isOpen() {
    usbSerialPort.isOpen(status => {
        console.log(`Serial open status: ${status}`);
        this.zone.run(() => {
            this.openStatus = status;
        });
    });
}
// 关闭串口
closeSerial(isOpenSerial?: boolean) {
    usbSerialPort.close(() => {
        this.isOpen();
        this.receiveDataArray = [];
        if (isOpenSerial) {
            this.openSerialPort();
        }
    });
}

为了能够对串口波特率进行设置,我还做了一个设置页面,主要用于设置波特率、数据位、停止位、以及收发数据记录的背景颜色切换、语言切换等功能。

重要代码如下:

version: any = '';
config: any = {};
// eslint-disable-next-line @typescript-eslint/ban-types
configTemp: object = {};
// 颜色列表
colorList: any[] = [
    'color-white',
    'color-red',
    'color-blue',
    'color-cyan',
    'color-yellow',
    'color-green',
    'color-black',
    'color-cornsilk',
    'color-darkviolet',
    'color-gainsboro',
    'color-maroon',
    'color-pink',
];
lang: any;
constructor(
    private appVersion: AppVersion,
    private nativeStorage: NativeStorage,
    private modalController: ModalController,
    private translate: TranslateService,
    private zone: NgZone
) { }

ionViewWillEnter() {
    this.initBackgroundColor();
    this.getVersion();
    this.getSerialPortConfig();
    this.getLanguage();
}

async initBackgroundColor() {
    const backgroundClass = await this.nativeStorage.getItem('backgroundClass');
    console.log('settings backagroun class', backgroundClass);

    const activeClass = 'color-active';
    this.colorList.forEach((item, index) => {
        if (item === backgroundClass) {
            console.log('have same');
            this.zone.run(() => {
                this.colorList[index] = `${item} ${activeClass}`;
            });
        }
    });
    console.log('color list', this.colorList);

}

/**
 * get App version
 *
 * @memberof SettingsPage
 */
async getVersion() {
    this.version = await this.appVersion.getVersionNumber();
}

/**
 * Get serial port config
 *
 * @memberof SettingsPage
 */
async getSerialPortConfig() {
    this.config = await this.nativeStorage.getItem('config');
    this.configTemp = Object.assign({}, this.config);
    console.log('config', this.config);
}

async setSerialPortConfig() {
    await this.nativeStorage.setItem('config', this.config);
    const configIsCHange = JSON.stringify(this.configTemp) !== JSON.stringify(this.config);
    this.modalController.dismiss({ configIsChange: configIsCHange });
}

async setBackgroundColor(className: string) {
    await this.nativeStorage.setItem('backgroundClass', className);
    this.modalController.dismiss();
}

async getLanguage() {
    this.lang = await this.nativeStorage.getItem('locale');
}

async setLanguage() {
    await this.nativeStorage.setItem('locale', this.lang);
    this.translate.setDefaultLang(this.lang);
    this.translate.use(this.lang);
}

Cordova确实太老了,感觉都快已经被Apache抛弃了,Cordova的更新速度也很慢,就连目前的ionic都开发了自己的混合框架capacitor,而且也兼容Cordova插件,只不过面对react-native以及flutter来说,ionic目前处于一个比较尴尬的场面,因为react-native与flutter从性能上都可以碾压ionic,不过ionic的优点就是打包后apk占用空间是极小的。

不论如何,ionic相对于react-native和flutter来说,可以让前端开发人员快速上手,并且快速开发与发布应用,其中坑较少,学习成本低,再加上如今的ionic已经完全从一个依赖于Cordova的移动端框架转变为了UI框架,你可以使用angular、vue、react甚至是原生JavaScript进行快速开发。

不过capacitor的插件目前少之又少,而Cordova的插件虽然多,但是太旧很多插件更新速度太慢,大家就抱着学习的态度去使用就可以了,当然如果选择ionic来作为生产力框架的话也没多大问题。

APP项目 https://github.com/king2088/i...
cordova串口插件项目 https://github.com/king2088/c...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK