背景

公司最近的一个项目中需要使用LED显示屏实时显示一些数据,经过调研,项目经理选择了泰美泉公司的产品,前几日硬件设备到了之后,笔者负责的中间件组就马不停蹄的开始了实际的调研与测试工作,因为之前有过对接LED设备的经验,所以对这次的调研还是比较有把握的。LED调研最核心的要解决,这个LED是否可以满足我方的项目需求。

项目需求的核心点如下:

  • 实时性
  • 稳定性

具体的展示UI是否可以满足也是需要考虑的方面,但是既然已经采购,UI就只能在使用这个LED的基础下尽可能的满足显示需求,所以本次测试不考虑UI设计。

针对不同的关注点,笔者分配组员进行了相关的调研测试。

泰美泉LED调研-开发模式的确定

设备连接与启动

泰美泉LED是针对我公司定制的,与服务器的通信方式为串口无线方式,具体如下

  • LED端:串口转无线模块(内部封装好了,只保留了一根天线暴露在外)
  • 服务器端:USB转串口无线模块

win10笔记本电脑连接上usb串口无线模块后,可以自动安装驱动,安装完驱动后,打开设备管理器,可以看到端口部分这个设备正常显示了出来。

右键属性,可以设置串口的波特率和串口号。设置好属性后,需要检查下LED端是否正常启动,检查方法:LED端使用移动电源供电,移动电源开关打开后,屏幕会闪烁一下,代表正常启动。

设备基本调试

设备和无线模块都连接好后,使用串口工具发送指令来查看LED设备是否可以正常工作。串口工具使用的是sscom串口调试助手。打开后显示如下:

选择端口号为无线模块对应的端口号,波特率对应修改,点击打开串口就可以开始测试了。

具体依次发送的测试内容如下:

  1. !#001%ZD00$$ //先清除所有区域,执行一次就行
  2. !#001%ZI01%ZC0000000000960064%ZA01%ZS03%ZH0050%F16%C1%AH1%AV1欢迎使用北京泰美泉2000型中长跑测试仪$$

发现串口正确回复,并且LED屏幕显示了测试文字-欢迎使用北京泰美泉2000型中长跑测试仪。设备基本调试步骤成功。

动态库环境调试

上面的步骤是直接使用串口,调用字库协议来发送数据,此外,LED还提供了另外一种方式,就是调用dll动态库的方式来控制LED屏幕。这种方式需要搭建Java开发环境,并加载泰美泉提供的demo程序进行测试。

组员在进行测试时,发现虽然已经正确修改了配置文件,但是Demo程序测试时会反馈失败,咨询对方技术人员后得知,数据未从串口发出,后来对接了另外一个动态库的技术人员,远程后,发现原来使用的Demo程序版本太低,使用对方的Demo程序就可以正常显示。

工作小启示:

很多时候与我们对接的是设备的销售商或者商务,对方提供的资料未必是最新的资料,尽量找对方的一线人员获取最新的资料数据。

动态库调试最核心的步骤是修改配置文件信息,配置文件信息如下:

[地址:0]
CardType=21
CardAddress=0
CommunicationMode=0
ScreemHeight=64
ScreemWidth=96
SerialBaud=9600
SerialNum=3
NetPort=5005
IpAddress0=192
IpAddress1=168
IpAddress2=1
IpAddress3=236
ColorStyle = 1

测试节目模式和实时模式后,均无法满足我们对于实时性更新的要求,所以本次开发不会选用动态库模式。

字库模式调试

既然动态库模式行不通,我们就开始测试字库模式,字库模式的协议比较简单,但是也比较受限,经过测试,虽然可以满足实时性的需求,但是针对目前的UI设计就无法满足,只能重新设计UI。

最终确定的UI如下:

阶段总结

经过上述的一番测试,我们组最终确定了使用字库模式进行开发。具体开发计划是使用Java语言,调用串口工具类,直接发送协议字符串给LED进行实时显示。这个阶段的目标已经达到。


泰美泉LED调研-Java串口环境

串口环境搭建和工具类

关于java串口环境的搭建的主题,网络上已经有很多优秀的文章,笔者主要参考了Java程序与串口的通信实现及调试 这篇文章进行了串口环境的搭建。

核心步骤:

  1. 工程中创建lib文件夹,并放入RXTXcomm.jar,使用Maven构建项目的要在pom文件中添加下面的代码,使maven可以管理我们自己的jar包。

    <dependency>
    <groupId>rxtx</groupId>
    <artifactId>rxtx</artifactId>
    <version>0.1</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/RXTXcomm.jar</systemPath>
    </dependency>
  2. JAVA_HOME/jre/bin 目录下放入两个dll

    • rxtxParallel.dll
    • rxtxSerial.dll
  3. 使用我整理好的工具类即可

    package com.icomp.tpft.util;
    
    import gnu.io.*;
    import org.apache.log4j.Logger; import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.Enumeration;
    import java.util.TooManyListenersException;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue; /**
    * 串口发送工具类,实现中会自动启动一个线程来处理接收数据并打印,无业务处理
    * (工具类可以直接用于不关注接收数据的场景)
    */
    public class ComUtil extends Thread implements SerialPortEventListener { private static final Logger logger = Logger.getLogger(ComUtil.class); // 监听器,我的理解是独立开辟一个线程监听串口数据
    private static CommPortIdentifier portId; // 串口通信管理类
    private static Enumeration<?> portList; // 有效连接上的端口的枚举
    private InputStream inputStream; // 从串口来的输入流
    private static OutputStream outputStream;// 向串口输出的流
    private static SerialPort serialPort; // 串口的引用
    // 堵塞队列用来存放读到的数据
    private BlockingQueue<String> msgQueue = new LinkedBlockingQueue<String>(); /**
    * 接收串口数据方法
    *
    * @param serialPortEvent
    */
    @Override
    public void serialEvent(SerialPortEvent serialPortEvent) {
    switch (serialPortEvent.getEventType()) {
    case SerialPortEvent.BI:
    case SerialPortEvent.OE:
    case SerialPortEvent.FE:
    case SerialPortEvent.PE:
    case SerialPortEvent.CD:
    case SerialPortEvent.CTS:
    case SerialPortEvent.DSR:
    case SerialPortEvent.RI:
    case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
    break;
    case SerialPortEvent.DATA_AVAILABLE:// 当有可用数据时读取数据
    byte[] readBuffer = new byte[200];
    try {
    int numBytes = -1;
    while (inputStream.available() > 0) {
    numBytes = inputStream.read(readBuffer); if (numBytes > 0) {
    msgQueue.add(":收到的数据为:"
    + new String(readBuffer));
    readBuffer = new byte[200];// 重新构造缓冲对象,否则有可能会影响接下来接收的数据
    } else {
    msgQueue.add("本轮次没有读到数据");
    }
    }
    } catch (IOException e) {
    logger.error("出现异常", e);
    }
    break;
    }
    } /**
    * 打开串口方法
    * comName 端口号
    *
    * @return 返回1 表示端口打开成功,返回 0表示端口打开失败
    */
    public int startComPort(String comName) {
    // 通过串口通信管理类获得当前连接上的串口列表
    portList = CommPortIdentifier.getPortIdentifiers(); while (portList.hasMoreElements()) { // 获取相应串口对象
    portId = (CommPortIdentifier) portList.nextElement(); logger.info("设备类型:--->" + portId.getPortType());
    logger.info("设备名称:---->" + portId.getName());
    // 判断端口类型是否为串口
    if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
    // 判断如果COM4串口存在,就打开该串口
    if (portId.getName().equals(comName)) {
    try {
    // 打开串口名字为COM_3(名字任意),延迟为2毫秒
    serialPort = (SerialPort) portId.open(comName, 2000); } catch (PortInUseException e) {
    logger.error("出现异常", e);
    return 0;
    }
    // 设置当前串口的输入输出流
    try {
    inputStream = serialPort.getInputStream();
    outputStream = serialPort.getOutputStream();
    } catch (IOException e) {
    logger.error("出现异常", e);
    return 0;
    }
    // 给当前串口添加一个监听器
    try {
    serialPort.addEventListener(this);
    } catch (TooManyListenersException e) {
    logger.error("出现异常", e);
    return 0;
    }
    // 设置监听器生效,即:当有数据时通知
    serialPort.notifyOnDataAvailable(true); // 设置串口的一些读写参数
    try {
    // 比特率、数据位、停止位、奇偶校验位
    serialPort.setSerialPortParams(9600,
    SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
    SerialPort.PARITY_NONE);
    } catch (UnsupportedCommOperationException e) {
    logger.error("出现异常", e);
    return 0;
    }
    return 1;
    }
    }
    }
    return 0;
    } @Override
    public void run() {
    try {
    logger.info("--------------任务处理线程运行了--------------");
    while (true) {
    Thread.sleep(1, 10);
    // 如果堵塞队列中存在数据就将其输出
    if (msgQueue.size() > 0) {
    logger.info(msgQueue.take());
    }
    }
    } catch (InterruptedException e) {
    logger.error("出现异常", e);
    }
    } /**
    * 发送指令方法(未初始化自动初始化)
    *
    * @param command
    */
    public static void writeCommand(String command, String comName) {
    if (outputStream == null) {
    logger.info("当前串口未初始化,无法写入数据!即将开始初始化!");
    ComUtil cRead = new ComUtil();
    int i = cRead.startComPort(comName);
    if (i == 1) {
    logger.info("当前串口初始化成功!");
    // 启动线程来处理收到的数据
    cRead.start();
    logger.info("读取线程启动!");
    doWriteCommand(command);
    } else {
    logger.info("当前串口初始化失败!");
    return;
    }
    } else {
    doWriteCommand(command);
    }
    } private static void doWriteCommand(String command) {
    try {
    //String st = "!#001%ZI01%ZC0000000000960064%ZA01%ZS03%ZH0050%F16%C1%AH1%AV1101:10'00 01202:20'00 02303:30'00 03404:40'00 04$$";
    String st = command;
    //logger.info("发出字节:" + HexStringUtils.toHexString(st.getBytes("gbk")));
    outputStream.write(st.getBytes("gbk"), 0,
    st.getBytes("gbk").length);
    } catch (IOException e) {
    logger.error("出现异常", e);
    }
    } public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
    ComUtil.writeCommand("!#001%ZI01%ZC0000000000960064%ZA01%ZS03%ZH0050%F16%C1%AH1%AV1101:10'00 01202:20'00 02303:30'00 03404:40'00 04$$", "COM3");
    Thread.sleep(1000);
    }
    }
    }
  4. 使用main方法测试即可。注意,要修改对应的串口参数,以及修改串口具体指令

业务方法

具体的业务代码这里就不更新在这里了,业务代码的核心会使用

ComUtil.writeCommand

这个方法来调用驱动层发送。


总结

本次测试,测试了LED的实时性,并且成功的搭建了JAVA的串口开发环境,我们项目组对于LED硬件对接又多了一些经验。

工作日记-LED串口开发的更多相关文章

  1. JAVA串口开发帮助类分享-及写在马年末

    摘要: 在系统集成开发过程中,存在着各式的传输途径,其中串口经常因其安全性高获得了数据安全传输的重用,通过串口传输可以从硬件上保证数据传输的单向性,这是其它介质所不具备的物理条件.下面我就串口java ...

  2. android 串口开发第二篇:利用jni实现android和串口通信

    一:串口通信简介 由于串口开发涉及到jni,所以开发环境需要支持ndk开发,如果未配置ndk配置的朋友,或者对jni不熟悉的朋友,请查看上一篇文章,android 串口开发第一篇:搭建ndk开发环境以 ...

  3. 安卓基于谷歌串口api进行串口开发

    准备材料 AndroidStudio 谷歌android-serialport-api 前情提要 网上提供很多基于c语言对安卓串口开发,有jni.cmake等等,不过都太高深,谷歌提供的api已经可以 ...

  4. C# 串口开发

    在单片机项目开发中,上位机也是一个很重要的部分,主要用于数据显示(波形.温度等).用户控制(LED,继电器等),下位机(单片机)与 上位机之间要进行数据通信的两种方式都是基于串口的: USB转串口 - ...

  5. windows串口编程Win32,PComm串口开发

    https://blog.csdn.net/u011430225/article/details/51496456 https://blog.csdn.net/eit520/article/detai ...

  6. SerialPort 串口开发

    private SerialPort sPort = new SerialPort(); //串行端口资源 /// <summary> /// 函数功能:打开串口/关闭串口 /// < ...

  7. android学习日记24--Android 菜单开发

    菜单是任何应用程序必不可少的一项.按下Menu键或者长按某个View就会弹出相应菜单,当然前提是应用程序有实现菜单功能. Android平台下的菜单有:Options Menu(选项菜单).Subme ...

  8. android 串口开发第一篇:搭建ndk开发环境以及第一个jni调用程序

    一:ndk环境搭建 1:开发环境 我使用的是android studio 2.3.3版本,搭建ndk开发环境比较简单,打开File----Settings----Appearance&Beha ...

  9. Android串口开发

    参考资料: https://www.jianshu.com/p/9249ed03e745 GitHUb地址: https://github.com/AIlll/AndroidSerialPort An ...

  10. ubuntu 配置minicom 进行串口开发

    sudo apt-get install minicom        sudo minicom -s        Serial port setup        Save  setup as d ...

随机推荐

  1. Optional的使用与解析

    引言 今天在项目中看到了大量Optional的使用,之前我也了解过Optional,是Java8中的新特性,并且便利地为空指针问题提供了处理方法,可以避免繁琐的if/else. 但是并没有真正在项目中 ...

  2. 【Windows】修改虚拟内存位置

    问题:系统优化中,希望将pagefile.sys文件(即虚拟内存)移动到其他盘中,在网上查找解决办法,找了很多,按照方法设置完成后,pagefile.sys文件依然存在,后来,找到了一篇文章解决了,现 ...

  3. 使用CRM REST Builder的Predefined Query在js结合FetchXML语句进行查询

    一般情况下使用拓展工具RESTBuilder编辑器,可以很方便的进行操作js中增删改查均能实现,但在某些较为特殊的场景下,需要根据条件去拼接查询过滤条件的,使用编辑器生成的代码无法实现,需要结合使用f ...

  4. [软件工具使用记录] windows离线ollama部署本地模型并配置continue实现离线代码补全

    qwen2.5coder发布之后,觉得差不多可以实现离线模型辅助编程了,所以尝试在公司内网部署模型,配合vsocde插件continue实现代码提示.聊天功能. 目前使用qwen2.5coder的32 ...

  5. 学Shiro完结版-3

    第八章 拦截器机制--<跟我学Shiro> 8.1 拦截器介绍 Shiro使用了与Servlet一样的Filter接口进行扩展:所以如果对Filter不熟悉可以参考<Servlet3 ...

  6. python包学习:-了解

    本节先做一些了解. numpy 参考:NumPy使用 NumPy 教程 NumPy是Python中科学计算的基础包.它是一个Python库,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于 ...

  7. 使用pkg对nodejs项目打包

    pkg的介绍可以阅读官方说明.下面针对使用做一个简单的说明. 1.  安装, 假设需要将项目打包成 win-x86 下运行的软件包,需要确定使用的 pkg 和 pkg-fecth 的版本,目前最后一个 ...

  8. mysl 修改数据存储位置后服务启动后停止

    在 Windows 系统中安装完 mysql 后,如果是生产用的机器,通常会修改数据存储位置.基本步骤: 1. 停止 mysql 服务: 2. 修改 my.ini 文件中的 datadir=" ...

  9. 存储过程专题(Oracle)

    本文转自 https://www.cnblogs.com/lukelook/p/9600407.html,感谢博主 豆豆DE思念 整理分享. 1.Oracle 存储过程基本格式  最简单的版本 is ...

  10. h5使用vue-photo-preview 做全屏预览

    h5页面使用全屏预览 最近需要在微信小程序中跳转到h5页面 在h5页面中需要进行图片预览展示 由于没有使用第三方的组件库. 只能手写,但是时间很紧张. 所以只能够寻找第三方的插件 vue-photo- ...