You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Arduino UNO(从站)通过Modbus TCP向CodeSys传输YF-S201流量计数据问题排查

代码问题分析与修正

核心问题1:中断变量的非原子访问

pulse_freq是在中断服务函数中修改的volatile变量,主循环直接读取时可能因CPU中断触发导致字节拆分,出现错误读数。必须用原子操作包裹读取过程:

// 替换原循环中的pulse_freq读取逻辑
noInterrupts();
unsigned long temp_pulse = pulse_freq;
pulse_freq = 0; // 重置计数需在原子操作内,避免中断修改导致数据丢失
interrupts();
double flowRate = temp_pulse / 7.5;

核心问题2:Modbus寄存器类型不匹配

andresarmento的ModbusIP库中,Hreg()操作的是16位无符号整数寄存器,你直接传入double类型的flowRate会导致精度丢失和类型转换错误。解决方案是将流量值转为整数存储(比如保留两位小数):

// 将流量值缩放为整数,适配16位寄存器
uint16_t flowRateScaled = static_cast<uint16_t>(flowRate * 100);
mb.Hreg(FLOWMETER_HREG, flowRateScaled);

PLC端读取后再除以100即可还原真实流量值。

其他潜在问题

  • IP网段一致性:确认PLC的IP与Arduino的169.254.149.2处于同一链路本地网段(169.254.0.0/16),避免隐性连通性问题。
  • 寄存器地址偏移:Modbus保持寄存器的地址在不同工具/PLC中存在偏移规则:
    • 代码中FLOWMETER_HREG = 100是0-based寄存器索引,对应标准Modbus地址为40101(保持寄存器起始地址为40001,索引0对应40001)。
    • 若PLC支持直接输入索引,可填100;若需标准地址,需填40101。
FC3功能码适用性确认

FC3(读保持寄存器)完全适用于你的场景:

  • 代码中mb.addHreg()创建的是Modbus保持寄存器,正是FC3的读取对象。
  • FC1(读线圈)对应离散输出寄存器,FC4(读输入寄存器)对应模拟输入寄存器,这两个功能码与你定义的寄存器类型不匹配,因此测试无效。
调试步骤建议
  1. 用Modbus调试工具验证Arduino端:使用Modbus Poll或类似工具,连接Arduino的IP和默认端口502,用FC3读取寄存器100(或40101),确认能获取正确的缩放后数值。
  2. 检查PLC配置:确保PLC中Modbus客户端的:
    • 功能码设置为FC3
    • 寄存器地址与Arduino端匹配(注意偏移规则)
    • 数据类型设置为16位无符号整数(后续自行缩放还原)
  3. 排查网络问题:用ping命令确认PLC与Arduino的连通性,若丢包需检查以太网扩展板接线或供电。

修正后的完整代码:

#include <SPI.h>
#include <Ethernet.h>
#include <Modbus.h>
#include <ModbusIP.h>

// Flowmeter and Modbus settings
const int FLOWMETER_SENSOR = 2; // Pin for flowmeter sensor
const int FLOWMETER_HREG = 100; // Holding register for flowmeter data

// ModbusIP object
ModbusIP mb;

// Flowmeter variables
volatile unsigned long pulse_freq = 0;
unsigned long lastTime;

void pulseCounter() {
    pulse_freq++;
}

void setup() {
    // Modbus TCP and Ethernet settings
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
    byte ip[] = { 169, 254, 149, 2 }; 
    mb.config(mac, ip);

    // Flowmeter setup
    pinMode(FLOWMETER_SENSOR, INPUT);
    attachInterrupt(0, pulseCounter, RISING);
    mb.addHreg(FLOWMETER_HREG);
    lastTime = millis();
    Serial.begin(9600);
}

void loop() {
    mb.task();

    unsigned long currentTime = millis();
    if (currentTime - lastTime > 1000) { // Every second
        lastTime = currentTime;
        
        // 原子操作读取并重置脉冲计数
        noInterrupts();
        unsigned long temp_pulse = pulse_freq;
        pulse_freq = 0;
        interrupts();
        
        double flowRate = temp_pulse / 7.5; // Calculate flow rate
        uint16_t flowRateScaled = static_cast<uint16_t>(flowRate * 100); // 缩放为整数
        mb.Hreg(FLOWMETER_HREG, flowRateScaled); // Update Modbus register
        
        Serial.print("Flow Rate: ");
        Serial.println(flowRate, 2); 
    }
}

内容的提问来源于stack exchange,提问作者Heroking18

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 16:50:38