分类: 开发文档

  • FY-105技术手册

    2.1参数功能表

    参数描述
    基本参数操作系统LuatOS
    Flash4MB
    RAM4MB
    模组芯片780epm
    4G网络中国移动/电信/联通三大运营商全都支持
    电源DC:7~30V,推荐12V/1A
    工作电流平均21mA~45mA,最大:60mA(12V)
    工作温度-40°C~ +85°C
    状态指示灯PWR电源指示灯
    RUN主程序运行指示灯
    NET网络指示灯
    接口SIM卡小卡规格
    USB接口TYPE-C接口,
    UART接口RS485-1
    模拟量输入AI1,AI2 电压0~5v
    数字量输入DI1,DI2
    继电器输出DO1,DO2 常开,常闭都支持
    天线接口SMA外螺内孔
    外形尺寸83mm*61mm*26mm
    重量200g
    频段LTE-TDDB34/B38/B39/B40/B41
    LTE-FDDB1/B3/B5/B8
    射频发射功率LTE-TDDClass3(23dBm+1/-3dB
    LTE-FDDClass3(23dBm+-2dB)
    数据传输速率行理论最大速率:5Mbps 下行理论最大速率:10Mbps
    软件功能配置模式上位机离线配置、WEB 在线配置
    网络协议TCP/UDP/HTTP/MQTT
    网络通道数量6
    传输加密支持SSL加密,证书认证
    应用协议Modbus、DL/T645、CJ188、S651、HJ212、SZY206
    连接认证支持动态密码认证
    自定义规则时间触发、条件触发、函数触发规则
    二次计算支持规则触发前、触发后的变量表达式计算
    输出JSON可自定义
    域名解析DNS支持
    心跳包机制支持
    注册包机制支持
    DFOTA升级支持
    指令透传支持
    自定义协议解析支持
    工作模式透传模式Modbus TCP<=>Modbus RTU(Modbus RTU/TCP 互转)Modbus RTU<=>JSON(自动读写、转换 Modbus 协议为 JSON 上传
    IOT服务平台端已支持: 腾讯云、阿里云、ONENET、 华为云、涂鸦云、百度云、Tlink云
  • FY-104技术规格

    2.1参数功能表

    参数描述
    基本参数操作系统LuatOS
    Flash4MB
    RAM4MB
    模组芯片780epm
    4G网络中国移动/电信/联通三大运营商全都支持
    电源DC:7~30V,推荐12V/1A
    工作电流平均21mA~45mA,最大:60mA(12V)
    工作温度-40°C~ +85°C
    状态指示灯PWR电源指示灯
    RUN主程序运行指示灯
    NET网络指示灯
    接口SIM卡小卡规格
      
    USB接口TYPE-C接口,
    UART接口串口1:RS232串口2:RS485-1串口3:RS485-2
    天线接口SMA外螺内孔
    外形尺寸83mm*61mm*26mm
    重量200g
    频段LTE-TDDB34/B38/B39/B40/B41
    LTE-FDDB1/B3/B5/B8
    射频发射功率LTE-TDDClass3(23dBm+1/-3dB
    LTE-FDDClass3(23dBm+-2dB)
    数据传输速率行理论最大速率:5Mbps 下行理论最大速率:10Mbps
    软件功能配置模式上位机离线配置、WEB 在线配置
    网络协议TCP/UDP/HTTP/MQTT
    网络通道数量6
    传输加密支持SSL加密,证书认证
    应用协议Modbus、DL/T645、CJ188、S651、HJ212、SZY206
    连接认证支持动态密码认证
    自定义规则时间触发、条件触发、函数触发规则
    二次计算支持规则触发前、触发后的变量表达式计算
     输出JSON 可自定义
    域名解析DNS支持
    心跳包机制支持
    注册包机制支持
    DFOTA升级支持
     指令透传 支持
     自定义协议解析 支持
    工作模式透传模式Modbus TCP<=>Modbus RTU(Modbus RTU/TCP 互转)Modbus RTU<=>JSON(自动读写、转换 Modbus 协议为 JSON 上传
    IOT服务平台端已支持: 腾讯云、阿里云、ONENET、 华为云、涂鸦云、百度云、Tlink云 
  • mqtt c语言实例

    在 Linux 下使用 C 语言和 Mosquitto 库实现 MQTT 通信是一种轻量级且高效的方式。Mosquitto 提供了 C/C++ 的客户端库 libmosquitto,可以直接操作 MQTT 协议。
    以下是详细的实现步骤

    1. 安装 Mosquitto 开发库

    在 Ubuntu/Debian 系统中,安装 Mosquitto 客户端和开发库:
    sudo apt-get update
    sudo apt-get install libmosquitto-dev mosquitto-clients

    2. 编写 MQTT 客户端代码

    以下是两个示例代码:一个用于发布消息,另一个用于订阅消息。

      示例 1:发布消息(Publisher)

      #include <stdio.h>
      #include <stdlib.h>
      #include <mosquitto.h>
      #include <string.h>
      
      #define MQTT_HOST "localhost"
      #define MQTT_PORT 1883
      #define MQTT_TOPIC "test/topic"
      #define MQTT_MESSAGE "Hello, MQTT from C!"
      #define QOS 1  // 服务质量等级
      int main() {
          struct mosquitto *mosq = NULL;
          int rc;
      // 初始化 Mosquitto 库
      mosquitto_lib_init();
      
      // 创建客户端实例
      mosq = mosquitto_new(NULL, true, NULL);
      if (!mosq) {
          fprintf(stderr, "Error: 创建客户端失败\n");
          return -1;
      }
      
      // 连接到 MQTT 代理
      rc = mosquitto_connect(mosq, MQTT_HOST, MQTT_PORT, 60);
      if (rc != MOSQ_ERR_SUCCESS) {
          fprintf(stderr, "连接失败: %s\n", mosquitto_strerror(rc));
          mosquitto_destroy(mosq);
          return -1;
      }
      
      // 发布消息
      rc = mosquitto_publish(mosq, NULL, MQTT_TOPIC, strlen(MQTT_MESSAGE), MQTT_MESSAGE, QOS, false);
      if (rc != MOSQ_ERR_SUCCESS) {
          fprintf(stderr, "发布失败: %s\n", mosquitto_strerror(rc));
      } else {
          printf("消息已发布到主题: %s\n", MQTT_TOPIC);
      }
      
      // 断开连接并清理资源
      mosquitto_disconnect(mosq);
      mosquitto_destroy(mosq);
      mosquitto_lib_cleanup();
      
      return 0;
      }

      }
      示例 2:订阅消息(Subscriber)

      #include <stdio.h>
      #include <stdlib.h>
      #include <mosquitto.h>
      #include <string.h>
      
      #define MQTT_HOST "localhost"
      #define MQTT_PORT 1883
      #define MQTT_TOPIC "test/topic"
      #define QOS 1
      
      // 收到消息的回调函数
      void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg) {
          if (msg->payloadlen) {
              printf("收到消息 [%s]: %s\n", msg->topic, (char *)msg->payload);
          } else {
              printf("收到空消息\n");
          }
      }
      
      int main() {
          struct mosquitto *mosq = NULL;
          int rc;
      
          mosquitto_lib_init();
      
          mosq = mosquitto_new(NULL, true, NULL);
          if (!mosq) {
              fprintf(stderr, "Error: 创建客户端失败\n");
              return -1;
          }
      
          // 设置消息回调函数
          mosquitto_message_callback_set(mosq, on_message);
      
          rc = mosquitto_connect(mosq, MQTT_HOST, MQTT_PORT, 60);
          if (rc != MOSQ_ERR_SUCCESS) {
              fprintf(stderr, "连接失败: %s\n", mosquitto_strerror(rc));
              mosquitto_destroy(mosq);
              return -1;
          }
      
          // 订阅主题
          rc = mosquitto_subscribe(mosq, NULL, MQTT_TOPIC, QOS);
          if (rc != MOSQ_ERR_SUCCESS) {
              fprintf(stderr, "订阅失败: %s\n", mosquitto_strerror(rc));
              mosquitto_destroy(mosq);
              return -1;
          }
      
          printf("等待消息...\n");
      
          // 进入事件循环(阻塞式)
          mosquitto_loop_forever(mosq, -1, 1);
      
          // 清理资源(实际不会执行到这里,需手动终止程序)
          mosquitto_destroy(mosq);
          mosquitto_lib_cleanup();
      
          return 0;
      }
      

      }

      3. 编译代码

      使用以下命令编译代码(需链接 libmosquitto):

      编译发布者

      gcc publisher.c -o publisher -lmosquitto

      编译订阅者

      gcc subscriber.c -o subscriber -lmosquitto

      4. 运行程序

      步骤 1:启动 Mosquitto 代理
      确保 Mosquitto 代理正在运行:

      mosquitto -v

      步骤 2:运行订阅者
      ./subscriber

      步骤 3:运行发布者
      ./publisher

      5. 输出结果


      订阅者输出:

        复制
        等待消息…
        收到消息 [test/topic]: Hello, MQTT from C!
        发布者输出:

        复制
        消息已发布到主题: test/topic

        6.关键代码解释

        初始化与清理:

          mosquitto_lib_init():初始化 Mosquitto 库。

          mosquitto_lib_cleanup():清理资源。

          客户端对象:

          mosquitto_new():创建客户端实例。

          mosquitto_connect():连接到 MQTT 代理。

          发布消息:

          mosquitto_publish():发布消息到指定主题。

          订阅消息:

          mosquitto_subscribe():订阅主题。

          mosquitto_message_callback_set():设置消息到达时的回调函数。

          事件循环:

          mosquitto_loop_forever():进入阻塞式事件循环,等待消息。

          7. 扩展功能


          添加认证:

            mosquitto_username_pw_set(mosq, “username”, “password”);
            使用 TLS 加密:

            mosquitto_tls_set(mosq, “ca.crt”, NULL, “client.crt”, “client.key”, NULL);
            保留消息(Retained Message):

            mosquitto_publish(mosq, NULL, MQTT_TOPIC, strlen(MQTT_MESSAGE), MQTT_MESSAGE, QOS, true);

            8.参考文档


            Mosquitto C 客户端库文档

              MQTT 协议规范

              通过上述代码,你可以快速实现基于 C 语言的 MQTT 通信。如果需要异步操作,可以结合多线程或 mosquitto_loop_start() 实现非阻塞行为。

            1. mqtt服务器搭建

              在 Ubuntu 18.04 上搭建一个简单的 MQTT 服务器,可以使用 Eclipse Mosquitto,它是一个开源的 MQTT 代理(broker),支持 MQTT 协议。以下是详细的步骤:

              1. 安装 Mosquitto

              Mosquitto 可以通过 Ubuntu 的包管理器直接安装。

                更新包列表
                sudo apt update
                安装 Mosquitto Broker 和客户端工具

                sudo apt install mosquitto mosquitto-clients
                mosquitto:MQTT 服务器(broker)。

                mosquitto-clients:MQTT 客户端工具,用于测试。

                2. 启动 Mosquitto 服务


                安装完成后,Mosquitto 服务会自动启动。你可以通过以下命令检查服务状态:


                  sudo systemctl status mosquitto
                  如果服务没有启动,可以手动启动:


                  sudo systemctl start mosquitto
                  设置开机自启动:


                  sudo systemctl enable mosquitto

                  3. 测试 MQTT 服务器


                  使用 mosquitto-clients 工具测试 MQTT 服务器是否正常工作。

                    订阅主题
                    在一个终端中运行以下命令,订阅一个主题(例如 test/topic):


                    mosquitto_sub -h localhost -t “test/topic”
                    -h localhost:指定 MQTT 服务器地址。

                    -t “test/topic”:指定订阅的主题。

                    发布消息
                    在另一个终端中运行以下命令,向 test/topic 主题发布消息:


                    mosquitto_pub -h localhost -t “test/topic” -m “Hello, MQTT!”
                    -m “Hello, MQTT!”:要发布的消息内容。

                    如果一切正常,订阅的终端会收到消息:


                    Hello, MQTT!

                    4. 配置 Mosquitto(可选)


                    Mosquitto 的配置文件位于 /etc/mosquitto/mosquitto.conf。你可以根据需要修改配置。

                      示例:允许匿名访问
                      默认情况下,Mosquitto 允许匿名访问。如果需要禁用匿名访问,可以编辑配置文件:


                      sudo nano /etc/mosquitto/mosquitto.conf
                      找到以下行并修改:

                      ini

                      allow_anonymous false
                      然后设置用户名和密码:


                      sudo mosquitto_passwd -c /etc/mosquitto/passwd your_username
                      重启 Mosquitto 服务以应用更改:


                      sudo systemctl restart mosquitto

                      5. 防火墙配置(如果需要)


                      如果服务器启用了防火墙(如 ufw),需要允许 MQTT 的默认端口(1883)。


                        sudo ufw allow 1883

                        6. 使用客户端连接


                        你可以使用任何 MQTT 客户端连接到服务器。例如:

                          MQTT Explorer(图形化客户端)

                          Paho MQTT(Python 库)

                          Eclipse Paho(Java 库)

                          连接时,指定服务器地址和端口(默认 localhost:1883)。

                          7. 停止或卸载 Mosquitto

                          如果需要停止 Mosquitto 服务:


                            sudo systemctl stop mosquitto
                            如果需要卸载 Mosquitto:


                            sudo apt remove –purge mosquitto mosquitto-clients
                            总结
                            通过以上步骤,你可以在 Ubuntu 18.04 上快速搭建一个简单的 MQTT 服务器,并使用客户端工具进行测试。Mosquitto 是一个轻量级且功能强大的 MQTT 代理,适合用于 IoT 项目或消息传递系统。

                          1. modbus arm 编译

                            1. 安装交叉编译工具链

                            (以交叉编译为arm的gcc为例)

                            sudo apt-get install gcc-arm-linux-gnueabihf

                            2. 下载libmodbus源代码

                            wget http://libmodbus.org/releases/libmodbus-X.Y.tar.gz

                            解压源代码

                            tar xzf libmodbus-X.Y.tar.gz
                            cd libmodbus-X.Y

                            /opt/hisi-linux/x86-arm/arm-hisiv500-linux/target/bin/arm-hisiv500-linux-g++

                            3. 配置(使用交叉编译工具链)

                            unzip libmodbus-3.1.10.zip
                            sudo apt-get update
                            sudo apt-get install autoconf
                            audo apt-get isntall libtool
                            ./autogen.sh
                            ./configure –host=arm-hisiv500-linux –prefix=/home/linkpi/work/cross/v500/arm-modbus

                            4. 编译

                            make

                            make CC=/opt/hisi-linux/x86-arm/arm-hisiv500-linux/target/bin/arm-hisiv500-linux-gcc CXX=/opt/hisi-linux/x86-arm/arm-hisiv500-linux/target/bin/arm-hisiv500-linux-g++ CFLAGS=”-std=c11″

                            安装

                            sudo make install

                          2. ModbusRtu通信协议

                            1. 协议基础

                            主从架构:单主站(Master)轮询多个从站(Slave),从站仅在收到主站请求后响应。

                            传输模式

                            二进制编码:数据以二进制形式传输,效率高于ASCII模式。

                            物理层:通常基于RS-485(多点通信)或RS-232(点对点)。

                            波特率:常用9600、19200、38400等,需所有设备一致。

                            地址范围:从站地址为1~247(0为广播地址,但实际中极少使用)。


                            2. 数据帧格式

                            Modbus RTU帧由地址、功能码、数据、CRC校验组成,格式如下:

                            字段长度说明
                            起始符帧间需保持至少3.5字符时间的静默(由波特率计算)
                            从站地址1字节1~247
                            功能码1字节定义操作类型(如读/写寄存器)
                            数据字段N字节具体操作的数据(如寄存器地址、数量、值等)
                            CRC校验2字节循环冗余校验(Cyclic Redundancy Check)
                            结束符帧间需保持至少3.5字符时间的静默

                            示例帧(读取保持寄存器):

                            主站请求01 03 00 6B 00 03 76 87

                            01:从站地址

                            03:功能码(读保持寄存器)

                            00 6B:起始寄存器地址(0x006B = 十进制107)

                            00 03:寄存器数量(3个)

                            76 87:CRC校验

                            从站响应01 03 06 02 2B 00 00 00 64 45 6F

                            01:从站地址

                            03:功能码

                            06:数据字节数(6字节)

                            02 2B 00 00 00 64:寄存器值(按顺序解析)

                            45 6F:CRC校验


                            3. 核心功能码

                            常用功能码如下:

                            功能码名称操作
                            01读线圈状态读取单个或多个线圈(输出)的ON/OFF状态
                            02读输入状态读取单个或多个输入(离散量输入)的状态
                            03读保持寄存器读取单个或多个保持寄存器的值(常用)
                            04读输入寄存器读取输入寄存器的值(如传感器数据)
                            05写单个线圈强制单个线圈为ON/OFF
                            06写单个保持寄存器写入单个保持寄存器的值
                            15写多个线圈写入多个线圈状态
                            16写多个保持寄存器写入多个保持寄存器的值

                            4. 数据模型

                            Modbus定义四类数据存储区:

                            数据类型读写权限地址范围
                            线圈(Coils)读/写00001 ~ 09999
                            离散输入(Inputs)只读10001 ~ 19999
                            保持寄存器(Holding Registers)读/写40001 ~ 49999
                            输入寄存器(Input Registers)只读30001 ~ 39999

                            5. CRC校验

                            计算范围:从地址字段到数据字段的所有字节。

                            算法:使用多项式 0xA001(CRC-16)。

                            工具:可通过查表法或在线工具生成校验码。


                            6. 异常响应

                            从站返回异常时,功能码最高位置1(原功能码 + 0x80),并附加错误码:

                            示例01 83 02 C0 F1

                            83:功能码0x03的异常(03 + 80 = 0x83)

                            02:错误码(02表示“无效地址”)


                            7. 物理层配置

                            RS-485:需终端电阻(120Ω)防止信号反射,支持最多32个设备。

                            接线:A/B线(差分信号)需正确连接,避免干扰。

                            波特率/数据位/校验位:常见配置为 8-N-1(8数据位、无校验、1停止位)。


                            8. 优缺点

                            优点

                            简单易实现,资源占用低。

                            实时性强,适合低速串行通信。

                            缺点

                            无加密或身份验证机制,安全性低。

                            主从架构不支持多主站。


                            9. 应用场景

                            PLC与传感器、仪表、变频器等设备通信。

                            工业控制系统(SCADA、DCS等)中的数据采集与控制。

                            10. 写入单个线圈实例

                            功能码05(写单个线圈)

                            作用:强制线圈(Coil)的值为 ON(0xFF00)或 OFF(0x0000)。


                            10.1 请求帧格式

                            字段字节数示例值说明
                            从站地址1字节01目标从站地址(1~247)
                            功能码1字节05写单个线圈
                            线圈地址2字节00 13线圈地址(0x0013 = 十进制19,对应协议地址 00020
                            写入值2字节FF 00 或 00 00FF00 表示ON,0000 表示OFF
                            CRC校验2字节8C 3A校验地址、功能码、地址、数据的CRC

                            10.2 示例请求帧(将线圈20设为ON):

                            01 05 00 13 FF 00 8C 3A

                            解析

                            01:从站地址1。

                            05:功能码(写线圈)。

                            00 13:线圈地址为0x0013(十进制19),对应Modbus协议地址 00020(地址从1开始)。

                            FF 00:强制线圈为ON。

                            8C 3A:CRC校验码。


                            10.3 响应帧格式

                            成功写入后,从站返回与请求相同的帧(确认写入值):

                            01 05 00 13 FF 00 8C 3A

                            字段含义与请求帧完全一致。


                            10.4 异常响应

                            若写入失败(如地址无效),从站返回异常帧:

                            01 85 02 80 71

                            解析

                            01:从站地址。

                            85:异常功能码(0x05 + 0x80 = 0x85)。

                            02:错误码(02表示“无效地址”)。

                            80 71:CRC校验。


                            10.5  关键注意事项

                            线圈地址偏移

                            协议中线圈地址范围为 00001-09999,但在数据帧中需转换为 0-based 地址(减1)。
                            例如:协议地址 00020 → 数据帧地址 0x0013(十进制19)。

                            写入值固定

                            FF 00 表示ON,00 00 表示OFF,其他值无效。

                            CRC计算工具

                            使用在线工具或代码库(如Python的 crcmod)生成校验码,避免手动计算错误。


                            10.6 实际应用场景

                            控制继电器、电磁阀等设备的开关。

                            示例代码(Python伪代码):

                            python

                            import serialimport crcmod

                            # 构造请求帧

                            slave_address = 0x01

                            function_code = 0x05

                            coil_address = 0x0013  # 对应协议地址00020

                            value = 0xFF00         # ON

                            frame = bytes([slave_address, function_code]) + coil_address.to_bytes(2, ‘big’) + value.to_bytes(2, ‘big’)

                            # 计算CRC

                            crc_func = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF)

                            crc = crc_func(frame)

                            frame += crc.to_bytes(2, ‘little’)  # CRC以小端字节序附加

                            # 通过串口发送

                            ser = serial.Serial(‘/dev/ttyUSB0’, baudrate=9600, timeout=1)

                            ser.write(frame)

                            总结来说,用户需要明确的步骤说明、实际例子以及常见问题的解决方法。确保回答结构清晰,覆盖所有关键点,特别是数据打包和地址转换,这些通常是容易出错的地方。

                            以下是 Modbus RTU 写多个线圈(功能码 15,十六进制 0x0F) 的详细指令示例和解析:


                            11. 写入多个线圈实例

                            功能码15(0x0F,写多个线圈)

                            作用:一次性写入多个线圈(Coils)的ON/OFF状态。


                            11.1 请求帧格式

                            字段字节数示例值说明
                            从站地址1字节01目标从站地址(1~247)
                            功能码1字节0F写多个线圈
                            起始线圈地址2字节00 13起始线圈地址(0x0013 = 十进制19,对应协议地址 00020
                            线圈数量2字节00 06要写入的线圈数量(6个线圈)
                            字节数1字节01数据字段的字节数(6线圈需1字节,见下文说明)
                            数据(线圈状态)N字节B2线圈状态的二进制位组合(B2 = 1011 0010,见解析)
                            CRC校验2字节4E 8B校验所有字段的CRC值

                            示例请求帧(写入线圈20~25的6个线圈):

                            01 0F 00 13 00 06 01 B2 4E 8B

                            解析

                            01:从站地址1。

                            0F:功能码(写多个线圈)。

                            00 13:起始线圈地址0x0013(十进制19,对应协议地址 00020)。

                            00 06:写入6个线圈(地址20~25)。

                            01:数据字段占1字节(6个线圈需1字节,8位中仅使用低6位)。

                            B2:二进制 1011 0010,解析为线圈状态(见下表)。

                            4E 8B:CRC校验。

                            线圈状态解析(按地址顺序):

                            线圈地址202122232425未使用未使用
                            位值10110010
                            状态ONOFFONONOFFOFF

                            注意

                            数据字段的每个字节表示8个线圈状态,按位打包,低位在前(如字节 B2 的二进制为 10110010,但实际解析顺序为从右到左):

                            第1位(最低位):地址20 → 0(对应二进制 10110010 的第0位是 0,但此处示例可能需要修正位顺序)。

                            修正说明:Modbus协议规定,数据字段的每个字节中,最低位对应起始地址,后续位依次递增。例如:

                            字节 B2(二进制 10110010)解析为:

                            位0(最低位)→ 地址20:0(OFF)

                            位1 → 地址21:1(ON)

                            位2 → 地址22:0(OFF)

                            位3 → 地址23:0(OFF)

                            位4 → 地址24:1(ON)

                            位5 → 地址25:1(ON)

                            位6和7未使用(填充0)。

                            实际应根据具体需求调整位顺序。


                            11.3  响应帧格式

                            成功写入后,从站返回确认帧:

                            01 0F 00 13 00 06 3D 8C

                            解析

                            01:从站地址。

                            0F:功能码。

                            00 13:起始地址。

                            00 06:写入的线圈数量。

                            3D 8C:CRC校验。


                            11.4 异常响应

                            若写入失败(如地址越界),返回异常帧:

                            01 8F 02 80 71

                            解析

                            01:从站地址。

                            8F:异常功能码(0x0F + 0x80 = 0x8F)。

                            02:错误码(02表示“无效地址”)。

                            80 71:CRC校验。


                            11.5 关键注意事项

                            数据字节数计算

                            线圈数量为 N,所需字节数为 ceil(N / 8)
                            例如:6个线圈 → 1字节,10个线圈 → 2字节。

                            位顺序

                            每个字节中,最低位(LSB)对应起始地址,后续位依次递增。
                            例如:数据字节 0x01(二进制 00000001)表示仅第一个线圈为ON。

                            未使用位填充

                            若线圈数量不足8的倍数,最后一个字节的高位补0(例如6个线圈时,最后2位无意义)。


                            11.6 实际应用场景

                            批量控制多个继电器、指示灯或电磁阀。

                            Python伪代码示例

                            python

                            import serialimport crcmod

                            def write_multiple_coils(slave_addr, start_addr, coil_states):

                                # 计算字节数和数据

                                coil_count = len(coil_states)

                                byte_count = (coil_count + 7) // 8

                                data_bytes = bytearray(byte_count)

                                # 将线圈状态打包为字节

                                for i in range(coil_count):

                                    if coil_states[i]:

                                        byte_index = i // 8

                                        bit_index = i % 8

                                        data_bytes[byte_index] |= (1 << bit_index)

                                # 构造请求帧

                                frame = bytes([slave_addr, 0x0F]) 

                                frame += start_addr.to_bytes(2, ‘big’)

                                frame += coil_count.to_bytes(2, ‘big’)

                                frame += bytes([byte_count])

                                frame += data_bytes

                                # 计算CRC

                                crc_func = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF)

                                crc = crc_func(frame)

                                frame += crc.to_bytes(2, ‘little’)

                                # 发送帧

                                ser = serial.Serial(‘/dev/ttyUSB0’, baudrate=9600)

                                ser.write(frame)

                            # 示例:写入线圈20~25为 [ON, OFF, ON, ON, OFF, OFF]

                            write_multiple_coils(1, 19, [1, 0, 1, 1, 0, 0])


                            11.7 常见问题

                            位顺序错误:确保数据字节的位顺序与线圈地址顺序一致。

                            字节数不足:未正确计算 ceil(N/8) 会导致数据截断。

                            地址偏移:协议地址(如00020)需转换为0-based地址(19)。

                            12. 写单个保持寄存器(功能码 0x06)

                            作用:向从站设备的单个保持寄存器写入一个16位值。

                            请求帧格式

                            [从站地址] [功能码] [寄存器地址高] [寄存器地址低] [数据高] [数据低] [CRC低] [CRC高]

                            示例

                            假设:

                            从站地址:0x01

                            寄存器地址:0x0002(对应MODBUS地址40003,因为地址从40001开始)

                            写入数据:0x00C8(十进制200)

                            请求帧(十六进制):

                            01 06 00 02 00 C8 08 03

                            分解

                            01:从站地址

                            06:功能码(写单个寄存器)

                            00 02:寄存器地址(大端格式)

                            00 C8:写入的数据(大端格式)

                            08 03:CRC校验值(低字节在前)


                            13. 写多个保持寄存器(功能码 0x10)

                            作用:向从站设备的连续多个保持寄存器写入多个16位值。

                            请求帧格式

                            [从站地址] [功能码] [起始地址高] [起始地址低] [寄存器数量高] [寄存器数量低] [字节数] [数据1高] [数据1低] [数据2高] [数据2低] … [CRC低] [CRC高]

                            示例

                            假设:

                            从站地址:0x01

                            起始寄存器地址:0x0000(对应MODBUS地址40001)

                            写入2个寄存器:

                            寄存器1数据:0x1234

                            寄存器2数据:0x5678

                            请求帧(十六进制):

                            01 10 00 00 00 02 04 12 34 56 78 7C 9A

                            分解

                            01:从站地址

                            10:功能码(写多个寄存器)

                            00 00:起始地址(大端格式)

                            00 02:寄存器数量(写入2个寄存器)

                            04:后续数据的总字节数(2寄存器 × 2字节 = 4字节)

                            12 34:第一个寄存器的数据(0x1234)

                            56 78:第二个寄存器的数据(0x5678)

                            7C 9A:CRC校验值(低字节在前)


                            关键注意事项

                            寄存器地址与MODBUS地址的映射

                            MODBUS地址40001对应协议中的寄存器地址0x0000,40002对应0x0001,依此类推。

                            数据格式

                            所有数值均为大端格式(高字节在前),但CRC校验值为小端格式(低字节在前)

                            CRC计算

                            使用标准MODBUS CRC16算法,可借助工具(如在线CRC计算器)生成。

                            从站响应

                            成功写入后,从站会返回与请求相同的帧(写单个寄存器)或返回起始地址和寄存器数量(写多个寄存器)。

                            14. 读取离散输入(Inputs,功能码 02)

                                   在 Modbus 协议中,读取离散输入(Inputs,功能码 02)和读取线圈(Coils,功能码 01)的返回数据格式完全相同,均以 按位打包的字节(bit-packed bytes) 形式表示每个输入或线圈的状态。以下是具体分析:


                            1. 返回数据格式

                            无论是读取线圈(01)还是离散输入(02),响应报文的数据部分均为 位(bit)的紧凑打包形式

                            每个字节(Byte)表示 8 个输入/线圈的状态(从低地址到高地址依次排列)。

                            字节内位的顺序:最低有效位(LSB)对应第一个地址,最高有效位(MSB)对应第 8 个地址。

                            例如,若读取地址 0~7 的状态为 [1,0,1,0,1,0,1,0],则返回字节为 `0x55`(二进制 01010101)。

                            剩余位填充:如果请求的输入/线圈数量不是 8 的倍数,最后一个字节的高位部分补零。


                            2. 响应报文结构

                            以功能码 02(读离散输入) 为例:

                            响应报文格式:

                            [事务ID] [协议ID] [长度] [单元ID] [功能码] [字节数] [数据字节1] [数据字节2] …

                            字节数(Byte Count):表示后续数据的总字节数。

                            数据字节(Data Bytes):按位打包的输入状态。


                            3. 示例对比

                            假设读取 10 个离散输入(地址 0~9),响应报文如下:

                            功能码(02) | 字节数(2) | 数据字节1(0xCD) | 数据字节2(0x01)

                            数据解析

                            字节1 0xCD → 二进制 11001101,表示地址 0~7 的状态:1,0,1,1,0,0,1,1

                            字节2 0x01 → 二进制 00000001,表示地址 8~9 的状态:1,0(高位补零)。


                            4. 与线圈(Coils,功能码 01)的区别

                            功能码不同:线圈(01)与离散输入(02)的功能码不同,但数据格式一致。

                            读写权限

                            线圈(Coils):可读可写(功能码 01 读,05 写单个,15 写多个)。

                            离散输入(Inputs):只读(功能码 02)。


                            5. 解析方法

                            无论是线圈还是离散输入,均需按以下步骤解析:

                            提取 字节数 字段,确定数据长度。

                            将数据字节转换为二进制,按位拆解。

                            按顺序映射到具体地址。

                            代码示例(Lua)

                            — 假设收到数据字节为 {0xCD, 0x01},读取 10 个输入local bytes = {0xCD, 0x01}local bits = {}

                            for i, byte in ipairs(bytes) do

                                for bit_pos = 0, 7 do

                                    local bit = (byte >> bit_pos) & 1

                                    bits[#bits + 1] = bit

                                endend

                            — 输出地址 0~9 的状态for addr = 0, 9 do

                                print(“地址”, addr, “状态:”, bits[addr + 1])  — Lua 索引从 1 开始end


                            6. 总结

                            是,返回包格式相同:离散输入(02)和线圈(01)的响应数据均以 位打包的字节 形式返回。

                            唯一区别:功能码和寄存器类型(线圈可写,离散输入只读)。

                            解析代码可复用,只需注意功能码对应的寄存器类型。

                            在 MODBUS RTU 协议中,功能码 0x10(写多个保持寄存器) 只能操作连续的寄存器地址。如果需要对不连续地址写入数据,必须分多次发送不同的请求,或依赖设备支持自定义扩展协议。以下是针对你的需求的详细分析和解决方案:


                            15. 是否可以用10 写入非连续地址的数据:比如

                            用指令 16 写多个保持寄存器 可以是不连续地址吗, 比如开始地址0000,我要写00,04,06 这三个地址,00,04写入4字节浮点数(ABCD序),06写入2个字节的整数(AB需要)

                            标准MODBUS协议的限制

                            功能码 0x10 的规则:必须指定一个连续的寄存器块,起始地址 + 寄存器数量,且数据按顺序填充。

                            你的需求矛盾

                            要写入 000000040006 三个不连续地址。

                            每个浮点数占用 2 个连续寄存器(4 字节,ABCD 序),整数占用 1 个寄存器(2 字节,AB 序)。

                            无法通过单次功能码 0x10 实现

                            在 Modbus RTU 协议中,功能码 0x04(读输入寄存器) 支持一次性读取多个连续的输入寄存器。以下是具体说明和示例:


                            14. 读取一个或多个输入寄存器

                            功能码0x04(读输入寄存器)

                            用途:从从站设备读取只读输入寄存器的数据(例如传感器数值、设备状态等)。

                            支持范围:可一次性读取 1~125 个连续寄存器(每个寄存器为 16 位)。


                            1. 请求帧格式

                            [从站地址] [功能码] [起始地址高] [起始地址低] [寄存器数量高] [寄存器数量低] [CRC低] [CRC高]

                            起始地址:输入寄存器的起始地址(大端格式,即高字节在前)。

                            寄存器数量:要读取的寄存器个数(大端格式)。

                            示例

                            从站地址:0x01

                            起始地址:0x0000(对应 Modbus 地址 30001)

                            读取数量:3 个寄存器

                            请求帧(十六进制)

                            01 04 00 00 00 03 [CRC]

                            分解

                            01:从站地址

                            04:功能码(读输入寄存器)

                            00 00:起始地址(大端格式)

                            00 03:读取 3 个寄存器(大端格式)

                            [CRC]:CRC 校验值(低字节在前)。


                            2. 响应帧格式

                            [从站地址] [功能码] [字节数] [数据1高] [数据1低] [数据2高] [数据2低] … [CRC低] [CRC高]

                            字节数:后续数据的字节总数(寄存器数量 × 2)。

                            数据格式:每个寄存器数据为大端格式(高字节在前)。

                            示例响应(假设读取到 3 个寄存器的值为 0x12340x56780x9ABC):

                            01 04 06 12 34 56 78 9A BC [CRC]

                            分解

                            01:从站地址

                            04:功能码

                            06:数据总字节数(3 寄存器 × 2 字节 = 6 字节)

                            12 34:第一个寄存器的值(0x1234)

                            56 78:第二个寄存器的值(0x5678)

                            9A BC:第三个寄存器的值(0x9ABC)

                            [CRC]:CRC 校验值。


                            3. 关键注意事项

                            (1) 地址范围限制

                            Modbus 输入寄存器的逻辑地址范围为 30001~39999(协议中的寄存器地址为 0x0000~0x270F)。

                            确保起始地址和寄存器数量不超出设备支持的范围(参考从站设备文档)。

                            (2) 数据解析

                            若数据为 32 位浮点数或 32 位整数,需根据设备定义的字节序(如 ABCD、CDAB 等)组合两个寄存器的值。

                            例如,浮点数占用地址 0000 和 0001,需将两个寄存器的值拼接后转换。

                            (3) 错误响应

                            如果请求非法(如地址无效或数量超限),从站返回错误帧:

                            [从站地址] [功能码 + 0x80] [异常码] [CRC]

                            异常码0x02(非法地址)、0x03(非法数据值)等。


                            4. 实际应用场景

                            (1) 读取传感器数据

                            假设从站的输入寄存器存储了以下数据:

                            地址 30001(0x0000):温度值(16 位整数)

                            地址 30002(0x0001):湿度值(16 位整数)

                            地址 30003(0x0002):压力值(32 位浮点数,占用 0x0002 和 0x0003)

                            请求帧(读取 4 个寄存器)

                            01 04 00 00 00 04 [CRC]

                            响应帧

                            01 04 08 00 64 00 32 43 1C 00 00 [CRC]

                            00 64 → 温度值(0x0064 = 100)

                            00 32 → 湿度值(0x0032 = 50)

                            43 1C 00 00 → 浮点数(需按 ABCD 序解析为 0x431C0000 = 156.0)。


                            6. 总结

                            功能码 0x04 支持一次性读取多个连续的输入寄存器,最大数量为 125 个

                            需确保请求的地址和数量在设备允许范围内,并正确解析数据格式(如浮点数、整数、字节序)。

                          3. Lua高级应用编程

                            1.子类重写父类函数的方法

                            在 Lua 中实现面向对象编程时,确实可以让子类重写父类函数的同时复用父类的逻辑。以下是实现这种模式的几种方法:

                            1.1 基础实现方式

                            -- 父类local Parent = {}
                            Parent.__index = Parent
                            function Parent:new()
                            local obj = {}
                            setmetatable(obj, self)
                            return objend
                            function Parent:start()
                            print("父类start: 执行通用初始化操作")
                            -- 通用参数设置等
                            self.commonProperty = "所有子类共享的属性"end
                            -- 子类local Child = Parent:new() -- 继承父类
                            Child.__index = Child
                            function Child:start()
                            -- 先调用父类的start方法
                            Parent.start(self) -- 然后执行子类特有的逻辑
                            print("子类start: 执行特有操作")
                            self.specialProperty = "子类特有的属性"end
                            -- 使用示例local child = Child:new()
                            child:start()-- 输出:-- 父类start: 执行通用初始化操作-- 子类start: 执行特有操作

                            1.2 使用 super 关键字模式(更优雅的实现)

                            -- 创建类系统的基础设施local function class()
                            local cls = {}
                            cls.__index = cls function cls:new(…)
                            local obj = setmetatable({}, self)
                            if obj.ctor then
                            obj:ctor(…)
                            end
                            return obj
                            end -- 添加继承功能
                            function cls:extend()
                            local subclass = {}
                            subclass.super = self -- 保存父类引用
                            setmetatable(subclass, {__index = self})
                            subclass.__index = subclass
                            return subclass
                            end return clsend
                            -- 父类定义local Parent = class()
                            function Parent:ctor()
                            -- 构造函数end
                            function Parent:start()
                            print("父类start: 执行通用初始化")
                            self.commonValue = 100end
                            -- 子类定义local Child = Parent:extend()
                            function Child:start()
                            -- 调用父类方法
                            self.super.start(self) -- 子类特有逻辑
                            print("子类start: 执行特有逻辑")
                            self.childValue = self.commonValue + 50 -- 可以访问父类设置的属性end
                            -- 使用示例local obj = Child:new()
                            obj:start()-- 输出:-- 父类start: 执行通用初始化-- 子类start: 执行特有逻辑print(obj.commonValue, obj.childValue) -- 输出: 100 150

                            1.3 使用闭包保存父类方法(更灵活的方式)

                            -- 父类local Parent = {}
                            function Parent:new()
                            local obj = {}
                            setmetatable(obj, {__index = self}) -- 保存原始start方法
                            obj.parentStart = self.start return objend
                            function Parent:start()
                            print("父类start: 通用操作")
                            self.baseConfig = {width = 100, height = 200}end
                            -- 子类local Child = Parent:new()
                            function Child:start()
                            -- 调用父类方法
                            self:parentStart() -- 子类特有逻辑
                            print("子类start: 扩展操作")
                            self.childConfig = {color = "red"}end
                            -- 使用示例local instance = Child:new()
                            instance:start()-- 输出:-- 父类start: 通用操作-- 子类start: 扩展操作

                            1.4 使用元表链式调用(高级技巧

                            local function inherit(parent)
                            local child = {}
                            setmetatable(child, {__index = parent}) -- 重写方法时自动调用父类方法
                            function child:override(methodName, newMethod)
                            local parentMethod = parent[methodName] self[methodName] = function(...) if parentMethod then parentMethod(...) end return newMethod(...) end end return childend
                            -- 父类local Parent = {}function Parent:start()
                            print("父类start: 基础配置")end
                            -- 子类local Child = inherit(Parent)
                            Child:override("start", function(self)
                            print("子类start: 扩展配置")end)
                            -- 使用示例local obj = setmetatable({}, {__index = Child})
                            obj:start()-- 输出:-- 父类start: 基础配置-- 子类start: 扩展配置


                            2. 多继承实现

                            多继承场景下的正确模式

                            #若需要实现多继承,应通过 混合元表 实现,但仍需保持构造函数的独立性。以下是一个安全的多继承构造函数模板:

                            lua
                            
                            #-- 基类 A
                            
                            local A = {}
                            
                            A.__index = A
                            
                            function A.new()
                            
                                local obj = {}
                            
                                setmetatable(obj, A)
                            
                                return obj
                            
                            end
                            
                            #-- 基类 B
                            
                            local B = {}
                            
                            B.__index = B
                            
                            function B.new()
                            
                                local obj = {}
                            
                                setmetatable(obj, B)
                            
                                return obj
                            
                            end
                            
                            #-- 子类 C 继承 A 和 B
                            
                            local C = {}
                            
                            C.__index = C
                            
                            #-- 多继承构造函数
                            
                            function C.new()
                            
                                local obj = {}
                            
                                -- 合并基类方法(根据优先级)
                            
                                local meta = {
                            
                                    __index = function(t, k)
                            
                                        return A[k] or B[k]  -- 按优先级查找
                            
                                    end
                            
                                }
                            
                                setmetatable(obj, meta)
                            
                                -- 初始化子类特有属性
                            
                                obj._specs = {}
                            
                                return obj
                            
                            end

                          4. 密码保护:WSTools 4G网关使用手册

                            此内容受密码保护。如需查阅,请在下列字段中输入您的密码。

                          5. 卫软科技

                            佛山卫软科技有限公司成立于2025年,是一家专注于物联网终端数据采集产品及平台软件研发的高科技企业,致力于为客户提供高性能通信产品与一体化解决方案。公司以国产芯片应用为核心,重点开展基于国产芯片的物联网及工业控制终端产品的设计与研发,推动全国产化技术在物联网领域的落地与应用。
                            我们专注于为不同行业定制个性化的硬件设备,结合各类传感器技术与特定场景需求,开发匹配的物联网平台应用软件系统,实现终端数据的智能采集与高效传输。目前产品主要应用于物联网终端数据采集、远程控制、工业PLC控制等场景,覆盖智慧能源、气象监测、移动通信、医疗健康等多个领域。
                            公司核心研发团队由具备国家认证高级工程师资质的资深技术人员组成,成员平均拥有20年以上行业经验,自2003年起便致力于芯片级嵌入式产品开发,早期已在三星、海思等芯片平台上成功研发多项产品,广泛应用于智能家居、安防监控、气象设备、车载终端、音视频编解码等领域。目前,公司正持续推进基于移动、海思、全志、瑞芯微等国产芯片的嵌入式产品研发,持续拓展物联网、音视频、医疗等更多应用场景。
                            卫软科技始终以技术创新为驱动,以客户需求为导向,致力于为行业提供稳定、可靠、先进的物联网产品与解决方案,助力产业智能化升级。

                          6. Hello world!

                            Welcome to weisoft. This is your first post. Edit or delete it, then start writing!