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

如何在TwinCAT(C++)中读取Emergency(EMCY)消息?

解决方案:用C++读取TwinCAT EtherCAT从站EMCY邮箱消息

核心思路

EtherCAT的EMCY消息属于CoE(CANopen over EtherCAT)邮箱对象,无法通过PDO映射,必须通过ADS接口直接访问从站的邮箱区域,或利用TwinCAT的事件通知机制实现触发式获取,而非周期性轮询。


方法一:使用TwinCAT ADS C++ API周期性读取

前置准备

  • 项目中引入TwinCAT ADS的头文件与库文件:
    • 头文件路径:C:\TwinCAT\AdsApi\C++\Include
    • 库文件:TcAds.lib(32位)或TcAdsX64.lib(64位)
  • 运行程序的PC需安装TwinCAT Runtime,本地访问默认无需额外权限

代码示例

#include <iostream>
#include <iomanip>
#include "TcAdsDef.h"
#include "TcAdsAPI.h"

int main() {
    long nErr;
    AmsAddr amsAddr;

    // 打开ADS端口
    nErr = AdsPortOpen();
    if (nErr != 0) {
        std::cerr << "ADS端口打开失败,错误码: " << nErr << std::endl;
        return 1;
    }

    // 获取本地AMS地址
    nErr = AdsGetLocalAddress(&amsAddr);
    if (nErr != 0) {
        std::cerr << "获取本地AMS地址失败,错误码: " << nErr << std::endl;
        AdsPortClose();
        return 1;
    }

    // 配置目标从站的AMS参数:替换为你的从站NetId和端口
    amsAddr.port = 0x0001; // 规则:0x0001 + 从站在总线中的位置(第2个从站为0x0002)
    strcpy(amsAddr.netId, "192.168.0.1.1.1");

    // 读取EMCY消息:CoE紧急对象索引组0x1001,子索引0x0000(组合值为0x10010000)
    unsigned char emcyBuffer[16] = {0};
    unsigned long bytesRead;

    nErr = AdsSyncReadReq(&amsAddr, ADSIGRP_COE_READ, 0x10010000, sizeof(emcyBuffer), emcyBuffer, &bytesRead);
    if (nErr != 0) {
        std::cerr << "读取EMCY消息失败,错误码: " << nErr << std::endl;
        AdsPortClose();
        return 1;
    }

    // 解析EMCY数据(示例:打印错误码与附加信息)
    unsigned short errorCode = (emcyBuffer[1] << 8) | emcyBuffer[0];
    std::cout << "EMCY错误码: 0x" << std::hex << std::setw(4) << std::setfill('0') << errorCode << std::endl;
    std::cout << "附加数据: ";
    for (int i = 2; i < bytesRead; i++) {
        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)emcyBuffer[i] << " ";
    }
    std::cout << std::dec << std::endl;

    AdsPortClose();
    return 0;
}

关键说明

  • ADSIGRP_COE_READ是CoE读取的固定索引组,0x10010000为紧急对象索引(0x1001)与子索引(0x0000)的组合值(索引<<16 | 子索引)
  • 从站的AMS端口需按总线位置计算,第一个从站为0x0001,依次递增
  • 数据解析需参考从站的CoE手册,不同厂商的EMCY消息格式可能存在差异

方法二:触发式获取(ADS事件通知)

若无需周期性轮询,可配置TwinCAT在EMCY消息到来时触发ADS通知,实现异步获取:

代码示例

#include <iostream>
#include <thread>
#include <iomanip>
#include "TcAdsDef.h"
#include "TcAdsAPI.h"

// ADS通知回调函数
void EmcyNotificationCallback(const AmsAddr* pAddr, const AdsNotificationHeader* pNotification, uint32_t hUser) {
    unsigned char* emcyData = (unsigned char*)pNotification->data;
    unsigned short errorCode = (emcyData[1] << 8) | emcyData[0];
    std::cout << "[触发通知] EMCY错误码: 0x" << std::hex << std::setw(4) << std::setfill('0') << errorCode << std::endl;
}

int main() {
    long nErr;
    AmsAddr amsAddr;
    uint32_t hNotification;

    AdsPortOpen();
    AdsGetLocalAddress(&amsAddr);
    amsAddr.port = 0x0001; // 替换为目标从站端口
    strcpy(amsAddr.netId, "192.168.0.1.1.1"); // 替换为目标从站NetId

    // 注册EMCY对象的变化通知
    nErr = AdsSyncAddDeviceNotificationReq(&amsAddr, ADSIGRP_COE_READ, 0x10010000, sizeof(unsigned char)*16,
        ADSTRANS_SERVERCYCLE, 0, nullptr, EmcyNotificationCallback, &hNotification);
    if (nErr != 0) {
        std::cerr << "注册通知失败,错误码: " << nErr << std::endl;
        AdsPortClose();
        return 1;
    }

    std::cout << "等待EMCY通知...(按Ctrl+C退出)" << std::endl;
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    // 取消通知(实际中需在退出前调用)
    AdsSyncDelDeviceNotificationReq(&amsAddr, hNotification);
    AdsPortClose();
    return 0;
}

常见问题排查

  • ADS连接失败:检查TwinCAT Runtime是否运行,从站NetId与端口是否正确,本地防火墙是否放行ADS默认端口(48898)
  • 读取返回错误码0x700(ADSERR_DEVICE_NOT_FOUND):确认从站已在线,端口号计算规则为0x0001 + 从站在EtherCAT总线中的位置
  • 数据为空或格式错误:核对从站CoE手册中EMCY对象的索引/子索引,确保缓冲区大小匹配从站的消息长度
  • pyads读取失败:确认使用了正确的CoE索引组/子索引,从站AMS端口配置正确,且程序以管理员权限运行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 14:02:46