Notes on the documentation

This description is only intended for the use of trained specialists in control and automation engineering who are familiar with the applicable national standards.
It is essential that the following notes and explanations are followed when installing and commissioning these components.

The responsible staff must ensure that the application or use of the products described satisfy all the requirements for safety, including all the relevant laws, regulations, guidelines and standards.

Disclaimer

The documentation has been prepared with care. The products described are, however, constantly under development.
For that reason the documentation is not in every case checked for consistency with performance data, standards or other characteristics.
In the event that it contains technical or editorial errors, we retain the right to make alterations at any time and without warning.
No claims for the modification of products that have already been supplied may be made on the basis of the data, diagrams and descriptions in this documentation.

Trademarks

Beckhoff®, TwinCAT®, EtherCAT®, Safety over EtherCAT®, TwinSAFE® and XFC® are registered trademarks of and licensed by Beckhoff Automation GmbH.
Other designations used in this publication may be trademarks whose use by third parties for their own purposes could violate the rights of the owners.

Patent Pending

The TwinCAT Technology is covered, including but not limited to the following patent applications and patents:
EP0851348, US6167425 with corresponding applications or registrations in various other countries.

Copyright

© Beckhoff Automation GmbH & Co. KG, Germany.
The reproduction, distribution and utilization of this document as well as the communication of its contents to others without express authorization are prohibited.
Offenders will be held liable for the payment of damages. All rights reserved in the event of the grant of a patent, utility model or design.


Overview

Description | Source text
Example 1: Read DLL version | Download
Example 2: Write flag synchronously into PLC | Download
Example 3: Read flag synchronously from the PLC | Download
Example 4: Read ADS status | Download
Example 5: Read ADS information | Download
Example 6: Start/stop PLC | Download
Example 7: Access an array in the PLC | Download
Example 8: Event driven reading | Download
Example 9: Access by variable name | Download
Example 10: Upload PLC variable declaration (static) | Download
Example 11: Detect status change in TwinCAT router and the PLC | Download
Example 12: Event-Driven Detection of Changes to the Symbol Table | Download
Example 13: Reserved |
Example 14: Upload Symbolinformation for one single variable | Download
Example 15: Read PLC Variable via multi-threading | Download
Example 16: Read PLC variable declaration | Download
Example 17: ADS-sum command: read or write | Download
Example 18: ADS-sum command: Get and release | Download
Example 19: Reserved |
Example 20: Transmitting structures to the PLC | Download
Example 21: Reading and writing of TIME/DATE variables | Download


Linking into Visual C++

TwinCAT TcAdsDLL: Reference

Linking C++ ADS library for TwinCAT2 in Visual Studio

ADS API

All ADS libraries are installed with every TwinCAT per default in the '\TwinCAT\AdsAPI\' directory.

If you only want to develop a ADS client (e.g. HMI, SCADA), you can install our royalty free product TwinCAT CP

Include the Header files.

To use the functionality of the TcAdsDll in your project you have to include the TcAdsApi.h and the TcAdsDef.h header files into your project.

#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

Add the Library to your project

To use the functionality of the TcAdsDll you have to include the TcAdsDll.lib. In the Visual Studio you have to select the menu item Project|Properties.

On the project settings dialog you select the scope of the settings for: Configuration Properties. To include the library, you have to add the path to the TcAdsDll.Lib in the Additional Dependencies modules text box.


Read DLL version

Download

Description

This program determines the version of the DLL file.

Sourcecode

#include <iostream.h>
#include <conio.h>
#include <windows.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void main()
{
  long        nTemp;
  AdsVersion* pDLLVersion;

  nTemp = AdsGetDllVersion();
  pDLLVersion = (AdsVersion *)&nTemp;
  cout << "Version: " << (int)pDLLVersion->version << '\n';
  cout << "Revision: " << (int)pDLLVersion->revision << '\n';
  cout << "Build: " << pDLLVersion->build << '\n';
  cout.flush();
  _getch();
}


Write flag synchronously into the PLC

Description

In this example program, the value that the user has entered is written into flag double word 0.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void main()
{
  long     nErr, nPort;
  AmsAddr  Addr;
  PAmsAddr pAddr = &Addr;
  DWORD    dwData;

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 PLC1 = 801
  pAddr->port = 801;

  // Read value from user that is to be written to the PLC
  cout << "Value: ";
  cin >> dwData;

  // Write value to MD0
  nErr = AdsSyncWriteReq( pAddr, 0x4020, 0x0, 0x4, &dwData );
  if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

  _getch();
  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


Read flag synchronously from the PLC

Description

In this example program the value in flag double word 0 in the PLC is read and displayed on the screen.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void main()
{
  long      nErr, nPort;
  AmsAddr   Addr;
  PAmsAddr  pAddr = &Addr;
  DWORD     dwData;

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 PLC1 = 801
  pAddr->port = 801;

  // Read value from MD0 and display
  do
  {
    nErr = AdsSyncReadReq(pAddr, 0x4020, 0x0, 0x4, &dwData);
    if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
    else cout << dwData << '\n';
    cout.flush();
  }
  while (getch() == '\r');    // Read the next value (use Carriage return as delimitter), stop otherwise

  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


Read ADS status

Description

This program reads the status of the PLC. The variable of type ADSSTATE which contains information such as, for example, whether the PLC is in the RUN or STOP state.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void main()
{
  ADSSTATE   nAdsState;
  USHORT	nDeviceState;
  long	nErr, nPort;
  AmsAddr	Addr;
  PAmsAddr   pAddr = &Addr;

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 PLC1 = 801
  pAddr->port = 801;

  do
  {
    nErr = AdsSyncReadStateReq(pAddr, &nAdsState, &nDeviceState);
    if (nErr)
      cerr << "Error: AdsSyncReadStateReq: " << nErr << '\n';
    else
    {
      cout << "AdsState: " << nAdsState << '\n';
      cout << "DeviceState: " << nDeviceState << '\n';
    }
    cout.flush();
  }
  while ( getch() == '\r'); // continue on a carriage return, finish for any other key

  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


Read ADS information

Description

Each ADS device contains a version number and an identification. The example program reads this information from the PLC and displays it on the screen.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void main()
{
  LONG           nErr, nPort;
  AdsVersion     Version;
  AdsVersion     *pVersion = &Version;
  char           pDevName[50];
  AmsAddr        Addr;
  PAmsAddr       pAddr = &Addr;

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 PLC1 = 801
  pAddr->port = 801;

  nErr = AdsSyncReadDeviceInfoReq(pAddr, pDevName, pVersion);
  if (nErr)
    cerr << "Error: AdsSyncReadDeviceInfoReq: " << nErr << '\n';
  else
  {
    cout << "Name: " << pDevName << '\n';
    cout << "Version: " << (int)pVersion->version << '\n';
    cout << "Revision: " << (int)pVersion->revision << '\n';
    cout << "Build: " << pVersion->build << '\n';
  }
  cout.flush();
  getch();

  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


Start/stop PLC

Description

The following program starts or stops run-time system 1 in the PLC.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>

// ADS headers for TwinCAT2
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void main()
{
  USHORT      nAdsState;
  USHORT      nDeviceState = 0;
  long        nErr, nPort;
  int         ch;
  void        *pData = NULL;
  AmsAddr     Addr;
  PAmsAddr    pAddr = &Addr;

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 RTS1
  pAddr->port = 801;

  cout << "(R) -> PLC Run\n";
  cout << "(S) -> PLC Stop\n";
  cout.flush();
  ch = getch();
  ch = toupper(ch);
  while ( (ch == 'R') || (ch == 'S') )
  {
    switch (ch)
    {
      case 'R':
        nAdsState = ADSSTATE_RUN;
        break;
      case 'S':
        nAdsState = ADSSTATE_STOP;
        break;
    }
    nErr = AdsSyncWriteControlReq (pAddr, nAdsState, nDeviceState, 0, pData);
    if (nErr) cerr << "Error: AdsSyncWriteControlReq: " << nErr << '\n';
    ch = _getch();
    ch = toupper(ch);
  }

  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


Access an array in the PLC

Description

An array, located in the PLC, is to be read by means of a read command. The variable is addressed here by its handle. The length of the whole array is provided as the length for the function AdsSyncReadReq(). The address of the first array element is given as variable.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>

// ADS headers for TwinCAT2
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void main()
{
  long          nErr, nPort;
  AmsAddr       Addr;
  PAmsAddr      pAddr = &Addr;
  unsigned long lHdlVar;
  int           nIndex;
  short         Data[10];
  char          szVar []={"MAIN.PLCVar"};

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';
  pAddr->port = AMSPORT_R0_PLC_RTS1;

  // Fetch handle for the PLC variable
  nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
  if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
  // Read values of the PLC variables (by handle)
  nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(Data), &Data[0]);
  if (nErr)
    cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
  else
  {
    for (nIndex = 0; nIndex < 10; nIndex++)
      cout << "Data[" << nIndex << "]: " << Data[nIndex] << '\n';
  }
  cout.flush();
  _getch();
  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}

PLC program

PROGRAM MAIN
VAR
    PLCVar 			: ARRAY[0..9] OF WORD;
    TP_1			: ARRAY[0..9] OF TP;
    TOGGEL			: BOOL;
    nIndex			: INT;
END_VAR

TOGGEL := NOT TOGGEL;

FOR nIndex := 0 TO 9 DO
    TP_1[nIndex]( IN := TOGGEL, PT := t#1s);
    IF (TP_1[nIndex].Q = 0) THEN
        PLCVar[nIndex] := PLCVar[nIndex] + 1 + nIndex;
    END_IF
END_FOR


Event driven reading

Description

If values from a PLC or NC are to be displayed continuously on a user interface, then it is very inefficient to use AdsSyncReadReq(), since this function must be called cyclically. By defining what are known as notifications (messages), a TwinCAT server can be made to transmit values via ADS to another ADS device. A distinction is drawn between whether the TwinCAT server is to transmit the values cyclically, or only when the values change.

A notification is begun with the AdsSyncAddDeviceNotificationReq() function. After this, the callback function is automatically invoked by TwinCAT. AdsSyncDelDeviceNotificationReq() is used to halt the notification again. Since the number of notifications is limited, you should ensure the notifications no longer required by your program are deleted. You will find further information under the description of the AdsNotificationAttrib structure.

The following program starts a notification on a variable handle in the PLC. Each time the PLC variable changes, the callback function is invoked. The callback function receives a variable of type AdsNotificationHeader() as one of its parameters. This structure contains all the necessary information (value, time stamp, ...).

Hint: Don't use time intensive executions or ADS commands inside of your callback. Remind to sync your callback and your mainthread, if you access each other (e.g. critical sections, mutex, events).

Sourcecode

#include <iostream>
#include <conio.h>
#include <windows.h>
#include <winbase.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

using namespace std;

void _stdcall Callback(AmsAddr*, AdsNotificationHeader*, unsigned long);

void main()
{
  long                   nErr, nPort;
  AmsAddr                Addr;
  PAmsAddr               pAddr = &Addr;
  ULONG                  hNotification, hUser;
  AdsNotificationAttrib  adsNotificationAttrib;
  char		szVar []={"MAIN.PlcVar"};

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';
  pAddr->port = 801;

  // Set the attributes of the notification
  adsNotificationAttrib.cbLength = 4;
  adsNotificationAttrib.nTransMode = ADSTRANS_SERVERONCHA;
  adsNotificationAttrib.nMaxDelay = 0;
  adsNotificationAttrib.nCycleTime = 10000000; // Unit = 100ns, 1sec = 10000000

  // Get variable handle
  nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(hUser), &hUser, sizeof(szVar), szVar);
  if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';

  // Initiate the transmission of the PLC-variable
  nErr = AdsSyncAddDeviceNotificationReq(pAddr, ADSIGRP_SYM_VALBYHND, hUser, &adsNotificationAttrib, Callback, hUser, &hNotification);

  if (nErr) cerr << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << '\n';
  cout.flush();

  // Wait for user intraction (keystroke)
  _getch();

  // Finish the transmission of the PLC-variable
  nErr = AdsSyncDelDeviceNotificationReq(pAddr, hNotification);
  if (nErr) cerr << "Error: AdsSyncDelDeviceNotificationReq: " << nErr << '\n';

  // Release  the variable handle
  nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(hUser), &hUser);
  if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

  // Close the communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}

// Callback-function
void __stdcall Callback(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser)
{
  int                     nIndex;
  static ULONG            nCount = 0;
  SYSTEMTIME              SystemTime, LocalTime;
  FILETIME                FileTime;
  LARGE_INTEGER           LargeInteger;
  TIME_ZONE_INFORMATION   TimeZoneInformation;

  cout << ++nCount << ". Notification:\n";

  // print (to screen)) the value of the variable
  cout << "Value: " << *(ULONG *)pNotification->data << '\n';
  cout << "Notification: " << pNotification->hNotification << '\n';

  // Convert the timestamp into SYSTEMTIME
  LargeInteger.QuadPart = pNotification->nTimeStamp;
  FileTime.dwLowDateTime = (DWORD)LargeInteger.LowPart;
  FileTime.dwHighDateTime = (DWORD)LargeInteger.HighPart;
  FileTimeToSystemTime(&FileTime, &SystemTime);

  // Convert the time value Zeit to local time
  GetTimeZoneInformation(&TimeZoneInformation);
  SystemTimeToTzSpecificLocalTime(&TimeZoneInformation, &SystemTime, &LocalTime);

  // Print out the timestamp
  cout << LocalTime.wHour << ":" << LocalTime.wMinute << ":" << LocalTime.wSecond << '.' << LocalTime.wMilliseconds;

  // Print out the ADS-address of the sender
  cout << "\nServerNetId: ";
  for (nIndex = 0; nIndex < 6; nIndex++)
    cout << (int)pAddr->netId.b[nIndex] << ".";
  cout << " Port: " << pAddr->port << "\n\n";
  cout.flush();
}


Access by variable name

Description

The following program accesses a PLC variable that does not have an address. Access must therefore be made by the variable name. Once the PLC variable in the example program exceeds 10 it is reset to 0.

All data that ADS devices make available to the outside is organised by means of IndexGroups and IndexOffset. An IndexGroup can be thought of as a table, with each entry being addressed by the IndexOffset. The TwinCAT PLC has, for example, IndexGroups in which the variables that belong to the input/output or flags regions are stored. IndexGroups are also available to the TwinCAT PLC through which system functions may be addressed.

The IndexGroups ADSIGRP_SYM_HNDBYNAME and ADSIGRP_ SYM_VALBYHND are important for the example program. The IndexGroup ADSIGRP_SYM_HNDBYNAME is used to request a handle from a PLC variable identified by name. The variable can be accessed with the aid of this handle and the IndexGroup ADSIGRP_SYM_VALBYHND. The variable's handle is passed as the IndexOffset.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>

// ADS headers
#include "c:\twincat\adsapi\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\adsapi\tcadsdll\include\tcadsapi.h"

using namespace std;

void main()
{
  long      nErr, nPort;
  AmsAddr   Addr;
  PAmsAddr  pAddr = &Addr;
  ULONG     lHdlVar, nData;
  char      szVar []={"MAIN.PLCVar"};

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';
  pAddr->port = AMSPORT_R0_PLC_RTS1;

  // Get the handle of the PLC-variable
  nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
  if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
  do
  {
    // Read the value of a PLC-variable, via handle
    nErr = AdsSyncReadReq( pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData );
    if (nErr)
      cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
    else
      cout << "Value: " << nData << '\n';
    cout.flush();
    if (nData > 10)
    {
      // Write the value of the PLC variable to 0
      nData = 0;
      nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData);
      if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';
    }
  }
  while ( _getch() == '\r');  // Get next value with ENTER-button

  // Release the handle of the PLC-variable
  nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
  if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

  // Close the communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


Read PLC variable declaration

TwinCAT ADS-DLL: Examples

Upload PLC variable declaration (static)

Download

Description

The following information is transferred when accessing the variable declaration:

  • Variable name
  • Data type
  • Length
  • Address (IndexGroup / IndexOffset)
  • Comment

All the information listed above is transmitted in a data stream. Before this can be read, the first AdsSyncReadReq() is used to obtain the length. The data itself is transferred with the second AdsSyncReadReq(). The pchSymbols variable is a pointer, pointing to this region. The FOR-loop copies the corresponding data region into the pAdsSymbolEntry structure for each individual PLC variable. The individual information items in the PLC variables are stored in this structure. The macros PADSSYMBOLNAME, PADSSYMBOLTYPE and PADSSYMBOLCOMMENT simplify the evaluation of this data.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include <assert.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void main()
{
  long                  nErr, nPort;
  char                  *pchSymbols = NULL;
  UINT                  uiIndex;
  AmsAddr               Addr;
  PAmsAddr              pAddr = &Addr;
  AdsSymbolUploadInfo   tAdsSymbolUploadInfo;
  PAdsSymbolEntry       pAdsSymbolEntry;

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';
  pAddr->port = 801;

  // Read the length of the variable declaration
  nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_UPLOADINFO, 0x0, sizeof(tAdsSymbolUploadInfo), &tAdsSymbolUploadInfo);
  if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
  pchSymbols = new char[tAdsSymbolUploadInfo.nSymSize];
  assert(pchSymbols);

  // Read information about the PLC variables
  nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_UPLOAD, 0, tAdsSymbolUploadInfo.nSymSize, pchSymbols);
  if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';

  // Output information about the PLC variables
  pAdsSymbolEntry = (PAdsSymbolEntry)pchSymbols;
  for (uiIndex = 0; uiIndex < tAdsSymbolUploadInfo.nSymbols; uiIndex++)
  {
    cout << PADSSYMBOLNAME(pAdsSymbolEntry) << "\t\t"
         << pAdsSymbolEntry->iGroup << '\t'
         << pAdsSymbolEntry->iOffs << '\t'
         << pAdsSymbolEntry->size << '\t'
         << PADSSYMBOLTYPE(pAdsSymbolEntry) << '\t'
         << PADSSYMBOLCOMMENT(pAdsSymbolEntry) << '\n';
    pAdsSymbolEntry = PADSNEXTSYMBOLENTRY(pAdsSymbolEntry); cout.flush();
  }
  getch();

  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Fehler: AdsPortClose: " << nErr << '\n';

  // Release memory
  if (pchSymbols) delete(pchSymbols);
}


Detect status change in TwinCAT router and the PLC

Description

When an application is actually running it is often important to interrogate the status of TwinCAT and/or of its components; e.g., whether the PLC is in the RUN state. To avoid the need to repeatedly issue this inquiry, changes in the status can be detected very effectively with the aid of callback functions.
The following example program monitors the status of the PLC (run-time system 1) and of the TwinCAT router.
By invoking the AdsAmsRegisterRouterNotification() function, the given callback function will be invoked every time the status of the TwinCAT router changes. The current status can be interrogated by means of the parameters that are transferred.
The AdsSyncAddDeviceNotificationReq() is used to monitor the status of the PLC. The data that is passed to the callback function represents the current status of the PLC.

Sourcecode

#include <iostream.h>
#include <conio.h>
#include <windows.h>
#include <winbase.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void __stdcall Callback(AmsAddr*, AdsNotificationHeader*, ULONG);
void __stdcall RouterCall(LONG);

void main()
{
  LONG                    nErr, nPort;
  ULONG                   hNotification, hUser = 0;
  AmsAddr                 Addr;
  PAmsAddr                pAddr = &Addr;
  AdsNotificationAttrib   adsNotificationAttrib;

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // Select Port: TwinCAT2 PLC1
  pAddr->port = 801;

  nErr = AdsAmsRegisterRouterNotification(&RouterCall);
  if (nErr) cerr << "Error: AdsAmsRegisterRouterNotification: " << nErr << '\n';

  // Invoke notification
  adsNotificationAttrib.cbLength       = sizeof(short);
  adsNotificationAttrib.nTransMode     = ADSTRANS_SERVERONCHA;
  adsNotificationAttrib.nMaxDelay      = 0; // report each change directly
  adsNotificationAttrib.dwChangeFilter = 0;
  nErr = AdsSyncAddDeviceNotificationReq(pAddr, ADSIGRP_DEVICE_DATA, ADSIOFFS_DEVDATA_ADSSTATE, &adsNotificationAttrib, Callback, hUser, &hNotification);
  if (nErr) cerr << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << "\n";
  getch();

  // The following calls return errors if TwinCAT is halted
  nErr = AdsSyncDelDeviceNotificationReq(pAddr, hNotification);
  if (nErr) cerr << "Error: AdsSyncDelDeviceNotificationReq: " << nErr << '\n';

  nErr = AdsAmsUnRegisterRouterNotification();
  if (nErr) cerr << "Error: AdsAmsUnRegisterRouterNotification: " << nErr << '\n';

  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';

  return;
}

// ADS state callback function
void __stdcall Callback(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser)
{
  INT       nIndex;
  nIndex = *(short *)pNotification->data;
  switch (nIndex)
  {
  case ADSSTATE_RUN:
    cout << "PLC run\n";
    break;
  case ADSSTATE_STOP:
    cout << "PLC stop\n";
    break;
  default :
    cout << "PLC ADS-State" << nIndex << "\n";
    break;
  }
  cout.flush ();
}

// TwinCAT router callback function
void __stdcall RouterCall (long nReason)
{
  switch (nReason)
  {
  case AMSEVENT_ROUTERSTOP:
    cout << "TwinCAT-Router stop\n";
    break;
  case AMSEVENT_ROUTERSTART:
    cout << "TwinCAT-Router start\n";
    break;
  case AMSEVENT_ROUTERREMOVED:
    cout << "TwinCAT-Router removed\n";
    break;
  default:
    cout << "TwinCAT-Router AMS-Event " << nReason << "\n";
    break;
  }
  cout.flush ();
}


Event-Driven Detection of Changes to the Symbol Table

Description

ADS devices that support symbol names (PLC, NC, ...) store those names in an internal table. A handle is assigned here to each symbol. The symbol handle is necessary in order to be able to access the variables (see also Example 9). If the symbol table changes because, for instance, a new PLC program is written into the controller, the handles must be ascertained once again. The example below illustrates how changes to the symbol table can be detected.

Sourcecode

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include <winbase.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

void __stdcall SymbolChanged(AmsAddr*, AdsNotificationHeader*, unsigned long);

void main()
{
  long                   nErr;
  AmsAddr                Addr;
  PAmsAddr               pAddr = &Addr;
  ULONG                  hNotification;
  AdsNotificationAttrib  adsNotificationAttrib;

  // Open communication port on the ADS router
  AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // Select Port: TwinCAT2 PLC1
  pAddr->port = 801;

  // Specify attributes of the notification
  adsNotificationAttrib.cbLength = 1;
  adsNotificationAttrib.nTransMode = ADSTRANS_SERVERONCHA;
  adsNotificationAttrib.nMaxDelay = 5000000; // 500ms
  adsNotificationAttrib.nCycleTime = 5000000; // 500ms

  // Start notification for changes to the symbol table
  nErr = AdsSyncAddDeviceNotificationReq(pAddr, ADSIGRP_SYM_VERSION, 0, &adsNotificationAttrib, SymbolChanged, NULL, &hNotification);
  if (nErr) cerr << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << '\n';

  // Wait for a key-press from the user
  _getch();

  // Stop notification
  nErr = AdsSyncDelDeviceNotificationReq(pAddr, hNotification);
  if (nErr) cerr << "Error: AdsSyncDelDeviceNotificationReq: " << nErr << '\n';

  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}

// Callback function
void __stdcall SymbolChanged(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser)
{
  cout << "Symboltabelle hat sich geändert!\n";
  cout.flush();
}


Read PLC variable declaration

TwinCAT ADS-DLL: Examples

Reading the PLC variable declaration of an individual variable

Download

Description

The following information is transferred when accessing the variable declaration:

  • Variable name
  • Data type
  • Length
  • Address (IndexGroup / IndexOffset)
  • Comment

The AdsSyncReadWriteReq() call is used to read the variable information. The variable name is transferred to the function via parameter pWriteData. After the call the requested information is contained in variable pAdsSymbolEntry . The individual information items in the PLC variables are stored in this structure. The macros PADSSYMBOLNAME, PADSSYMBOLTYPE and PADSSYMBOLCOMMENT simplify the evaluation of this data. In the next step, the data type of the variable is evaluated via pAdsSymbolEntry->dataType.
If the data type is UDINT or ARRAY OF UDINT, the value of this variable is also read.

Sourcecode

#include <windows.h>
#include <conio.h>
#include <assert.h>
#include <string.h>
#include <iostream.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

typedef enum AdsDataTypeId
{
    ADST_VOID = VT_EMPTY,
    ADST_INT8 = VT_I1,
    ADST_UINT8 = VT_UI1,
    ADST_INT16 = VT_I2,
    ADST_UINT16 = VT_UI2,
    ADST_INT32 = VT_I4,
    ADST_UINT32 = VT_UI4,
    ADST_INT64 = VT_I8,
    ADST_UINT64 = VT_UI8,
    ADST_REAL32 = VT_R4,
    ADST_REAL64 = VT_R8,
    ADST_STRING = VT_LPSTR,
    ADST_WSTRING = VT_LPWSTR,
    ADST_REAL80 = VT_LPWSTR+1,
    ADST_BIT = VT_LPWSTR+2,
    ADST_BIGTYPE = VT_BLOB,
    ADST_MAXTYPES = VT_STORAGE,
} ADS_DATATYPE;

typedef struct _ValueString
{
    DWORD dwValue;
    char* szLabel;
} ValueString;

ValueString AdsDatatypeString[] =
{
    { VT_EMPTY, "ADST_VOID", },
    { VT_I1, "ADST_INT8", },
    { VT_UI1, "ADST_UINT8", },
    { VT_I2, "ADST_INT16", },
    { VT_UI2, "ADST_UINT16", },
    { VT_I4, "ADST_INT32", },
    { VT_UI4, "ADST_UINT32", },
    { VT_I8, "ADST_INT64", },
    { VT_UI8, "ADST_UINT64", },
    { VT_R4, "ADST_REAL32", },
    { VT_R8, "ADST_REAL64", },
    { VT_LPSTR, "ADST_STRING", },
    { VT_LPWSTR, "ADST_WSTRING", },
    { VT_LPWSTR+2, "ADST_BIT", },
    { VT_BLOB, "ADST_BIGTYPE", },
    { VT_STORAGE, "ADST_MAXTYPES", },
};

void main()
{
    long nErr, nPort;
    AmsAddr Addr;
    PAmsAddr pAddr = &Addr;
    char szVariable[255];
    BYTE buffer[0xFFFF];
    PAdsSymbolEntry pAdsSymbolEntry;

    // Open communication port on the ADS router
    nPort = AdsPortOpen();
    nErr = AdsGetLocalAddress(pAddr);
    if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

    // Select Port: TwinCAT2 PLC1
    pAddr->port = 801;

    for(;;)
    {
        cout << "Enter variable Name: ";
        cin >> szVariable;

        nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_INFOBYNAMEEX, 0, sizeof(buffer), buffer, strlen(szVariable)+1, szVariable);
        if (nErr)
        {
            cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
        }
        else
        {
            pAdsSymbolEntry = (PAdsSymbolEntry)buffer;
            cout << "Name: " << PADSSYMBOLNAME(pAdsSymbolEntry) << "\n"
            <<"Index Group: "<< pAdsSymbolEntry->iGroup << '\n'
            <<"Index Offset: "<< pAdsSymbolEntry->iOffs << '\n'
            <<"Size: "<< pAdsSymbolEntry->size << '\n'
            <<"Type: "<< (char*)PADSSYMBOLTYPE(pAdsSymbolEntry) << '\n'
            <<"Comment: "<< (char*)PADSSYMBOLCOMMENT(pAdsSymbolEntry) << '\n';

            switch(pAdsSymbolEntry->dataType)
            {
            case ADST_UINT32:
                {
                    int nElements = pAdsSymbolEntry->size/sizeof(unsigned long);
                    unsigned long *pVal = new unsigned long[nElements];
                    cout << "Datatype: ADST_UINT32" <<'\n';
                    AdsSyncReadReq(pAddr, pAdsSymbolEntry->iGroup, pAdsSymbolEntry->iOffs, pAdsSymbolEntry->size, pVal);
                    if( nErr )
                    {
                        cerr << "Error: AdsSyncReadReq: Unable to read Value" << nErr << '\n';
                    }
                    else
                    {
                        cout << "Value: ";
                        for( int i=0; i<nElements; i++ )
                        {
                            cout << pVal[i] << '\t';
                        }
                        cout << '\n';
                    }
                }
                break;
            default:
                {
                    int nType = sizeof(AdsDatatypeString)/sizeof(ValueString);
                    for(int i=0; i< nType; i++)
                    {
                        if( AdsDatatypeString[i].dwValue == pAdsSymbolEntry->dataType )
                        {
                            cout << "Datatype:" << AdsDatatypeString[i].szLabel <<'\n';
                            break;
                        }
                    }
                    if( i == nType )
                        cout << "Datatype:" << "Unknown datatype:" << pAdsSymbolEntry->dataType <<'\n';
                }
                break;
            }
        }
        cout << "Exit(y/n)" << '\n';
        cout.flush();

        if(_getch() == 'y')
            break;
    }
    // Close communication port
    nErr = AdsPortClose();
    if (nErr) cerr << "Fehler: AdsPortClose: " << nErr << '\n';
}

PLC program

VAR_GLOBAL
    PLCVarBYTE AT %MB0: BYTE; (* PLCVarBYTE *)
    PLCVarWORD AT %MB4: WORD; (* PLCVarWORD *)
    PLCVarDWORD AT %MB8: DWORD; (* PLCVarDWORD *)
    PLCVarBOOL AT %MB12: BOOL; (* PLCVarBOOL *)
    PLCVarINT AT %MB16: INT; (* PLCVarINT *)
    PLCVarSINT AT %MB20: SINT; (* PLCVarSINT *)
    PLCVarUSINT AT %MB24: USINT; (* PLCVarUSINT *)
    PLCVarUINT AT %MB28: UINT; (* PLCVarUINT *)
    PLCVarDINT AT %MB32: DINT; (* PLCVarDINT *)
    PLCVarUDINT AT %MB34: UDINT := 1234; (* PLCVarUDINT *)
    PLCVarREAL AT %MB38: REAL; (* PLCVarREAL *)
    PLCVarLREAL AT %MB42: LREAL; (* PLCVarLREAL *)

    PLCVarUDINTArray :ARRAY[0..5] OF UDINT; (* PLCVarINTArray*)
END_VAR


Read PLC variable via multi-threading

TwinCAT ADS-DLL: Examples

Read PLC variable via multithreading

Download

Description

The sample explains the creation of two threads, which reads a variable of a remote plc in different time intervals.

Note: Use the extended ADS functions if you want to develop a multi threaded application with ADS functionalities.

Sourcecode

#include <iostream>
#include <windows.h>
#include <conio.h>

// ADS headers
#include "c:\twincat\adsapi\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\adsapi\tcadsdll\include\tcadsapi.h"

using namespace std;

DWORD WINAPI ThreadFunction1(LPVOID parameter)
{
    long		nErr, nPort;
    AmsAddr		Addr;
    PAmsAddr	pAddr = &Addr;
    ULONG		lHdlVar, nData;
    ULONG		cbReturn = 0;
    bool		bRun = true;
    char		szVar []={"MAIN.PLCVarRTS1"};

    // Open communication port on local ADS router
    // Use the ADS-Ex-Methods if you implement multi threading

    nPort = AdsPortOpenEx();
    nErr = AdsGetLocalAddressEx(nPort, pAddr);
    if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

    // TwinCAT2 RTS1 Port
    pAddr->port = 801;

    // Get the handle of the PLC-variable
    nErr = AdsSyncReadWriteReqEx2(nPort, pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar, &cbReturn);
    if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';

    while(bRun)
    {

        DWORD wait = 0;

        // Wait 100ms for stopEvent
        wait = WaitForSingleObject(parameter, 100);

        // If timeout elapse read plc data
        if(WAIT_TIMEOUT == wait)
        {
            // Read the value of a PLC-variable, via handle
            nErr = AdsSyncReadReqEx2(nPort, pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData, &cbReturn);
            if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
            else cout << "Actual thread --> Thread1 Value: " << nData << '\n';
            cout.flush();
        }

        else
        {
            bRun = false;
        }
    }

    // Release the handle of the PLC-variable
    nErr = AdsSyncWriteReqEx(nPort, pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
    if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

    // Close the communication port
    nErr = AdsPortCloseEx(nPort);
    if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';

    return 1;
}

DWORD WINAPI ThreadFunction2(LPVOID parameter)
{
    long		nErr, nPort;
    AmsAddr		Addr;
    PAmsAddr	pAddr = &Addr;
    ULONG		lHdlVar, nData;
    ULONG		cbReturn = 0;
    bool		bRun = true;
    char		szVar []={"MAIN.PLCVarRTS2"};

    // Open communication port on the local ADS router
    // Use the ADS-Ex-Methods if you implement multi threading and open an additional port

    nPort = AdsPortOpenEx();
    nErr = AdsGetLocalAddressEx(nPort, pAddr);
    if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

    // TwinCAT2 RTS1 Port
    pAddr->port = 801;

    // Get the handle of the PLC-variable
    nErr = AdsSyncReadWriteReqEx2(nPort, pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar, &cbReturn);
    if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';

    while(bRun)
    {
        DWORD wait = 0;

        // Wait 500ms for stopEvent
        wait = WaitForSingleObject(parameter, 500);

        // If timeout elapse read plc data
        if(WAIT_TIMEOUT == wait)
        {
            // Read the value of a PLC-variable, via handle
            nErr = AdsSyncReadReqEx2(nPort, pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData, &cbReturn);
            if (nErr)
                cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
            else
                cout << "Actual thread --> Thread2 Value: " << nData << '\n';
            cout.flush();
        }

        else
        {
            bRun = false;
        }
    }

    // Release the handle of the PLC-variable
    nErr = AdsSyncWriteReqEx(nPort, pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
    if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

    // Close the communication port
    nErr = AdsPortCloseEx(nPort);
    if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';

    return 1;
}

void main()
{
    // StopEvents are used to end the threads
    HANDLE hStopThread1 = CreateEvent(0, FALSE, FALSE, "StopEvent1");
    HANDLE hStopThread2 = CreateEvent(0, FALSE, FALSE, "StopEvent2");

    DWORD threadId1 = 1;
    DWORD threadId2 = 2;

    // Thread creation
    HANDLE hThread1 = CreateThread(0,			// no security
                0,			// default stack size
                ThreadFunction1,
                (void*)hStopThread1, 	// parameter
                0,			// creation flags
                &threadId1   		// threadId
                );

    HANDLE hThread2 = CreateThread(0,			// no security
                0,			// default stack size
                ThreadFunction2,
                (void*)hStopThread2, 	// parameter
                0,			// creation flags
                &threadId2   		// threadId
                );

    // Loop only for demonstration
    for(int i = 0; i < 10; ++i)
    {
        cout << "Actual thread --> Main Thread" << '\n';
        Sleep(1000);
    }

    // Stop threads
    SetEvent(hStopThread1);
    SetEvent(hStopThread2);
    cout << "Press key to exit: ";
    _getch();
}


Upload PLC-variabledeclaration (dynamic)

Description

This sample describes how to upload the PLC symbol information in a more efficient dynamic way.

The PLC symbol information conatin the following parts :

  • Variablename
  • datentype
  • length
  • address (IndexGroup / IndexOffset)
  • comment

NOTE :
We highly recomment to NOT work with this IndexGroup/IndexOffset for ADS communication but instead use handles of symbols for ADS communication.
After uploading the information "name", "datatype" and "length" it makes sense to request a handle for this symbol.

Read the major information via ADS into internal class "CAdsParseSymbols" :

// Read major symbol information via ADS from device

AdsSymbolUploadInfo2 info;
nResult = AdsSyncReadReq(&m_amsAddr, ADSIGRP_SYM_UPLOADINFO2, 0, sizeof(info), &info);
if ( nResult == ADSERR_NOERR )
{
    // size of symbol information
    PBYTE pSym = new BYTE[info.nSymSize];
    if ( pSym )
    {
        // upload symbols (instances)
        nResult = AdsSyncReadReq(&m_amsAddr, ADSIGRP_SYM_UPLOAD, 0, info.nSymSize, pSym);
        if ( nResult == ADSERR_NOERR )
        {
            // get size of datatype description
            PBYTE pDT = new BYTE[info.nDatatypeSize];
            if ( pDT )
            {
                // upload datatye-descriptions
                nResult = AdsSyncReadReq(&m_amsAddr, ADSIGRP_SYM_DT_UPLOAD, 0, info.nDatatypeSize, pDT);
                if ( nResult == ADSERR_NOERR )
                {
                    // create class-object for each datatype-description
                    m_pDynSymbols = new CAdsParseSymbols(pSym, info.nSymSize, pDT, info.nDatatypeSize);
                    if ( m_pDynSymbols == NULL )
                        nResult = ADSERR_DEVICE_NOMEMORY;
                    }
                    delete pDT;
                }
            }
            delete pSym;
        }
    }
}

Get Parent :
The routine Get Parent will jump NOT jump to the direct parent of a child. Instead this command will jump to the next entry of the direct parent.

Get Sibling:
Selecting this option will return the next symbol within the current hierachy level. Symbols containing child information will be displayed, but the child elements will not be displayed.

Get Child:
If the current symbol contains child-symbol information (so the current symbol is an instance of a datatype description), this command will enter the next hierachy and return the information about first child object.

Get Next:
Clicking this button the next symbol will be extracted from internal class "CAdsParseSymbols" and be displayed.
Selecting always just this option allows to navigate from first ADS-symbol through the hierachy symbol tree to the end of list.


ADS Sum Command : Read

TwinCAT ADS-DLL: Sample

ADS-sum command: Read or Write a list of variables with one single ADS-command

Download

This sample describes how to read multiple single variables with one single ADS API call

Note that ADS is just a transport layer, but there could be important side effects : So read this requirements and take care on limitations.

Background :

ADS offers powerful and fast communication to exchange any kind of information. It's possible to read single variables or complete arrays and structures with each one single ADS-API call.
This new ADS command offers to read with one singale ADS call multiple variables which are not structured within a linear memory.

As a result the ADS caller application (like scada Systems etc.) can extremly speed up cyclic polling :
Sample :

  • Until now : Polling 4000 single variables which are not in a linear area (like array / structure / fixed PLC address ) would cause 4000 single Ads-ReadReq with each 1-2 ms protocol time.
    As a result the scanning of these variables take 4000ms-8000ms.
  • New Ads-Command allows to read multiple variables with one single ADS-ReadReq : 4000 single variables are handled with e.g. 8 single Ads-ReadReq (each call requesting 500 variables) with each 1-2 ms protocol time.
    As a result the scanning of these variables take just few 10ms.

REQUIREMENTS AND IMPORTANT LIMITATIONS :

Note that ADS is just a transport layer, but there could be important side effects. So read this requirements and take care on limitations :

  • Version of target ADS Device:
    ADS itself is just the transport layer, but the requested ADS device has to support the ADS-Command.
  • Bytes length of requested data:
    Requesting a large list of values from variables is fine, but the requested data of the Ads-response (the data-byte-length) have to pass the AMS-router (size by default a 2048kb)
    So the caller has to limit the requested variables based on calculation of requested data-byte-length.
  • Number of Sub-ADS calls : Highly recommended to max. 500 !
    If the PLC is processing one ADS request, it will completely work on this single ADS request BEFORE starting neyt PLC cycle.
    As a result one single ADS request with 200.000 sub-Ads-requests would cause that PLC would collect and copy 200.000 variables into one single ADS response, before starting next PLC.
    So this large number of ads-sub-commands will jitter the PLC execution !
    We highly recommend to not request more than 500 Ads-Sub commands
// This code snippet using ADSIGRP_SUMUP_READ with IndexGroup 0xF080 and IndexOffset as number of ADS-sub-commands
// Demonstrates how to read a list of variables, see full demo-code

// Use ADS-ReadWrite request : "Write" the requested data down to ADS device and "Read" the received answer

 nErr = AdsSyncReadWriteReq( 	pAddr,
                0xF080, 		// Sum-Command, response will contain ADS-error code for each ADS-Sub-command
                reqNum,		// number of ADS-Sub-Commands
                4*reqNum+reqSize, // number requested bytes in the sample two variables each 4 bytes. NOTE : we request additional "error"-flag(long) for each ADS-sub commands
                (void*)(mAdsSumBufferRes), // provide buffer for response
                12*reqNum,	// send 12 bytes for each variable (each ads-Sub command consist of 3 * ULONG : IG, IO, Len)
                &parReq);

// This code snippet using ADSIGRP_SUMUP_WRITE with IndexGroup 0xF081 and IndexOffset as number of ADS-sub-commands
// Demonstrates how to write a list of variables, see full demo-code

// Use ADS-ReadWrite request : "Write" the send a list of data to list of variables down to ADS device and "Read" the received return codes

 nErr = AdsSyncReadWriteReq( 	pAddr,
                0xF081,		// ADS list-write command
                reqNum,		// number of ADS-Sub commands
                4*reqNum,		// we expect an ADS-error-return-code (long) for each ADS-Sub command
                (void*)(mAdsSumBufferRes), // provide space for the response containing the return codes
                16*reqNum,	// cbyteLen : in THIS sample we send two variables each 4 byte
                        // --> send 16 bytes (IG1, IO1, Len1, IG2, IO2, Len2, Data1, Data2)
                &parReq); 	// buffer with data


ADS-Summenkommando: Holen und Freigeben von mehreren Handles

TwinCAT ADS-DLL: Samples

ADS-sum command: Get and release several handles

Download

Requirement: TwinCAT 2.11 >= Build 1550

This sample shows how to get and release several handles with the ADS-sum command. It's constructed like the AdsSyncReadWriteRequest and is used as container to transport the sub commands

1. Get handles

First, all necessary headers have to be included.
#include <iostream.h>
#include <windows.h>
#include <conio.h>

// TwinCAT2 Headers
#include "c:\twincat\adsapi\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\adsapi\tcadsdll\include\tcadsapi.h"

Next step is to define a structure, declare variables and allocate memory.
    // Structure declaration for values
    typedef struct dataReq
    {
        unsigned long		indexGroup;	// index group in ADS server interface
        unsigned long		indexOffset;	// index offset in ADS server interface
        unsigned long		rlength;		// count of bytes to read
        unsigned long		wlength;		// count of bytes to write
    }TDataReq, *PTDataReq;

    // Variables declaration
    AmsAddr		Addr;
    LONG		nErr, nPort;
    PAmsAddr		pAddr = &Addr;

    char		szVar1[] = {".bVar01"};
    char		szVar2[] = {".bVar02"};

    // Allocate memory
    ULONG	cbReq	= ( sizeof(TDataReq)*2 ) + sizeof(szVar1) + sizeof(szVar2);
    BYTE*	pBuffReq	= new BYTE[cbReq];

    BYTE*	pBuffRes	= new BYTE[24];

    // Put structure over memory
    PTDataReq	 	pDataReq = (PTDataReq)pBuffReq;
    ULONG*	 	pDataRes = (ULONG*)pBuffRes;

The values which can be transferred are written behind the last structure.

    // pDataReq-> structure 1
    pDataReq->indexGroup	= ADSIGRP_SYM_HNDBYNAME;
    pDataReq->indexOffset	= 0x0;
    pDataReq->rlength		= sizeof(ULONG);
    pDataReq->wlength		= sizeof(szVar1);

    // Skip to next structure
    pDataReq = pDataReq+1;

    // pDataReq-> structure 2
    pDataReq->indexGroup	= ADSIGRP_SYM_HNDBYNAME;
    pDataReq->indexOffset	= 0x0;
    pDataReq->rlength		= sizeof(ULONG);
    pDataReq->wlength		= sizeof(szVar2);

    // Skip to write data 1
    char*	szVarName = ( (char*)pDataReq ) + sizeof(TDataReq);
    strncpy( szVarName, szVar1, sizeof(szVar1) );

    // Skip to write data 2
    szVarName = szVarName + sizeof(szVar1);
    strncpy( szVarName, szVar2, sizeof(szVar2) );

For the communication a open port is necessary. After that the local address is handed over. If it comes to transmission the port is assigned to the address of the run time system 1 first.

The parameters for the sum command consist of IndexGroup (0xf082) - call the sum command, IndexOffset (0x2) - count of sub commands, ReadLength (0x18) - size of the data which can be read, ReadData (pBuffRes) - memory which read data assumes, WriteLength (cbReq) - size of the data which can be send and WriteLength (pBuffReq) - memory which contains data that can be sent.
    // Open communication port on the ADS router
    nPort = AdsPortOpen();
    nErr  = AdsGetLocalAddress(pAddr);

    cout << "open port:  ";
        if (nErr == 0)
        {
            cout << "OK" << '\n';

            // Get handles
            pAddr->port = AMSPORT_R0_PLC_RTS1;
            nErr = AdsSyncReadWriteReq(
                pAddr,
                0xf082,		// ADS list-read-write command
                0x2,		// number of ADS-sub commands
                0x18,		// we expect an ADS-error-return-code for each ADS-sub command
                pBuffRes,		// provide space for the response containing the return codes
                cbReq,		// cbReq : send 48 bytes (IG1, IO1, RLen1, WLen1,
                        // IG2, IO2, RLen2, WLen2, Data1, Data2)
                pBuffReq );	// buffer with data
        }
        else {cout << "ERROR [" << nErr << "]" << '\n';};

    cout << "connect:    ";
        if (nErr == 0)
        {
            cout << "OK" << '\n';

            // Skip to handle 1 and examine the value
            ULONG nVarHandle = *( (ULONG*)pBuffRes );
            if (nVarHandle != 0)
            {
                cout << " > handle1: ";
                cout << "ERROR [" << nVarHandle << "]" << '\n';
            }

            // Skip to handle 2 and examine the value
            nVarHandle = *( (ULONG*)pBuffRes + 2 );
            if (nVarHandle != 0)
            {
                cout << " > handle2: ";
                cout << "ERROR [" << nVarHandle << "]" << '\n';
            }
        }
        else {cout << "ERROR [" << nErr << "]" << '\n';};

2. Release handles

Define a structure, declare variables and allocate memory again.
    // Structure declaration for values
    typedef struct dataRel
    {
        unsigned long		indexGroup;	// index group in ADS server interface
        unsigned long		indexOffset;	// index offset in ADS server interface
        unsigned long		length;		// count of bytes to write
    }TDataRel, *PTDataRel;

    // Variables declaration
    ULONG*	nVar1	= (ULONG*)pBuffRes+4;
    ULONG*	nVar2	= (ULONG*)pBuffRes+5;

    // Allocate memory
    ULONG	cbRel	   = sizeof(TDataRel)*2 + sizeof(ULONG)*2;
    BYTE*	pBuffRel    = new BYTE[cbRel];

    ULONG	cbRelRes    = sizeof(ULONG)*2;
    BYTE*	pBuffRelRes = new BYTE[cbRelRes];

    // Put structure over memory
    PTDataRel  pDataRel    = (PTDataRel)pBuffRel;
    ULONG*	  pDataRelRes = (ULONG*)pBuffRelRes;

The values which can be transferred are written behind the last structure again.

    // pDataRel-> structure 1
    pDataRel->indexGroup	= ADSIGRP_IOIMAGE_RWIB;
    pDataRel->indexOffset	= 0x0;
    pDataRel->length		= sizeof(ULONG);

    // Skip to next structure
    pDataRel++;

    // pDataReq-> structure 2
    pDataRel->indexGroup	= ADSIGRP_IOIMAGE_RWIB;
    pDataRel->indexOffset	= 0x0;
    pDataRel->length		= sizeof(ULONG);

    // Skip to next structure
    pDataRel++;

    // Write handles into structure
    memcpy(	       pDataRel,   nVar1, sizeof(ULONG) );
    memcpy( (ULONG*)pDataRel+1, nVar2, sizeof(ULONG) );

The existing connection is used to release the handles.

The parameters for the sum command consist of IndexGroup (0xf081) - call the sum command, IndexOffset (0x2) - count of sub commands, ReadLength (cbRelRes) - size of the data which can be read, ReadData (pBuffRelRes) - memory which read data assumes, WriteLength (cbRel) - size of the data which can be send and WriteLength (pBuffRel) - memory which contains data that can be sent.

Last thing left is releasing the handles and closing the port.
    // Release handles
    nErr = AdsSyncReadWriteReq(
        pAddr,
        0xf081,		// ADS list-write command
        0x2,		// number of ADS-sub commands
        cbRelRes,		// we expect an ADS-error-return-code for each ADS-sub command
        pBuffRelRes,	// provide space for the response containing the return codes
        cbRel,		// cbReq : send 40 bytes (IG1, IO1, Len1, IG2, IO2, Len2, Data1, Data2)
        pBuffRel );	// buffer with data

    cout << "disconnect: ";
        if (nErr == 0)
        {
            cout << "OK" << '\n';

            // Skip to handle 1 and examine the value
            ULONG nVarHandle = *( (ULONG*)pBuffRes );
            if (nVarHandle != 0)
            {
                cout << " > handle1: ";
                cout << "ERROR [" << nVarHandle << "]" << '\n';
            }

            // Skip to handle 2 and examine the value
            nVarHandle = *( (ULONG*)pBuffRes + 2 );
            if (nVarHandle != 0)
            {
                cout << " > handle2: ";
                cout << "ERROR [" << nVarHandle << "]" << '\n';
            }
        }
        else {cout << "ERROR [" << nErr << "]" << '\n';};

    // Close the communication port
    nErr = AdsPortClose();
    cout << "close port: ";
        if (nErr == 0) {cout << "OK" << '\n' << "-------------------" << '\n';}
        else {cout << "ERROR [" << nErr << "]" << '\n' << "-------------------" << '\n';}

    cout.flush();

    // Wait for key press
    getch();
}


Beckhoff Information System - English

TwinCAT TcAdsDLL: Examples

Transmitting structures to the PLC

Download

Description

This example shows how to write a structure to the PLC via ADS. The elements in the structure have different data types.

Sourcecode

#include <stdio.h>
#include <tchar.h>
#include "windows.h"

#include "c:\twincat\adsapi\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\adsapi\tcadsdll\include\tcadsapi.h"

// Create new struct
typedef struct PlcStruct {
    INT16	shortVal;
    INT32	intVal;
    byte	byteVal;
    DOUBLE	doubleVal;
    FLOAT	floatVal;
} SPlcVar, *pSPlcVar;

int _tmain(int argc, _TCHAR* argv[])
{
    long     nErr, nPort;
    AmsAddr  Addr;
    PAmsAddr pAddr = &Addr;
    ULONG     lHdlVar;

    // New struct. Assign test values
    PlcStruct PlcVar;

    PlcVar.shortVal = 1;
    PlcVar.intVal = 2;
    PlcVar.byteVal = 3;
    PlcVar.doubleVal = 4.04;
    PlcVar.floatVal = (FLOAT)5.05;

    // Declare PLC variable which should notify changes
    char      szVar []={"MAIN.PLCVar"};

    // Extract values from struct and write to byte array
    // Circumvent memory holes caused by padding
    BYTE *pData = new BYTE[19];
    int nIOffs = 0;

    memcpy_s(&pData[nIOffs], 19, &PlcVar.shortVal, 2);
    nIOffs += 2;
    memcpy_s(&pData[nIOffs], 17, &PlcVar.intVal, 4);
    nIOffs += 4;
    memcpy_s(&pData[nIOffs], 13, &PlcVar.byteVal, 1);
    nIOffs++;
    memcpy_s(&pData[nIOffs], 12, &PlcVar.doubleVal, 8);
    nIOffs += 8;
    memcpy_s(&pData[nIOffs], 4, &PlcVar.floatVal, 4);

    // Open communication port on the ADS router
    nPort = AdsPortOpen();
    nErr = AdsGetLocalAddress(pAddr);
    if (nErr) printf("Error: Ads: Open port: %d\n", nErr);
    pAddr->port = 801;

    // Get variable handle
    nErr = AdsSyncReadWriteReq(pAddr,
                ADSIGRP_SYM_HNDBYNAME,
                0x0,
                sizeof(lHdlVar),
                &lHdlVar,
                sizeof(szVar),
                szVar);

    // Write the struct to the Plc
    AdsSyncWriteReq(pAddr,
                    ADSIGRP_SYM_VALBYHND, 	// IndexGroup
                    lHdlVar,			// IndexOffset
                    0x13,			// Size of struct
                    (void*) pData);

    if (nErr) printf("Error: Ads: Write struct: %d\n", nErr);
    else  printf("Structure successful written\n");

    // Close communication
    delete [] pData;

    //Release handle of plc variable
    nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
    if (nErr) printf("Error: AdsSyncWriteReq: %d \n", nErr);

    nErr = AdsPortClose();
    if (nErr) printf("Error: Ads: Close port: %d\n", nErr);

    getchar();
}


Beckhoff Information System - English

TwinCAT TcAdsDLL: Examples

Reading and writing of TIME/DATE variables

Download

Description

The PLC contains the TIME variable MAIN.Time1 and the DT variable MAIN.Date1. This example shows how to read, write and display those variables:

Sourcecode

#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <time.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

#define TIME_LENGHT 56
#define DATE_LENGHT 62
#define MON_START 1
#define YEAR_START 1900

int _tmain(int argc, _TCHAR* argv[])
{
    long lErr, lPort;
    long lTime, lMs, lSek, lMin, lHour, lDay;
    AmsAddr  Addr;
    PAmsAddr pAddr = &Addr;
    DWORD dwTime, dwDate;
    ULONG     lHdlTime, lHdlDate;

// Declare PLC variable
    char      szPlcTime []={"MAIN.Time1"};
    char      szPlcDate []={"MAIN.Date1"};

// Open the communication
    lPort = AdsPortOpen();
    lErr = AdsGetLocalAddress(pAddr);
    if(lErr) printf_s((char*)"Error: Getting local adress: 0x%i  \n", lErr);
    pAddr->port = AMSPORT_R0_PLC_RTS1;

// Get variable handle
        lErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlTime), &lHdlTime, sizeof(szPlcTime), szPlcTime);
    lErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlDate), &lHdlDate, sizeof(szPlcDate), szPlcDate);

// Read from MAIN.Time1
    lErr = AdsSyncReadReq(pAddr,
                            ADSIGRP_SYM_VALBYHND,	// IndexGroup
                            lHdlTime,			// IndexOffset
                            0x4,			// Size of DWORD
                            &dwTime);
    if(lErr) printf_s((char*)"Error: Read time variable: 0x%i  \n", lErr);

    //Convert DWORD to Time
    lTime = (long)dwTime;
    lMs		= (lTime % 1000);
    lSek	= (lTime / 1000) % 60;
    lMin	= (lTime / 60000) % 60;
    lHour	= (lTime / 3600000) % 24;
    lDay	= (lTime / 86400000) % 365;

    wchar_t szTime[TIME_LENGHT];
    wsprintf(szTime, L"Time from PLC: %dd %dh %dm %ds %dms \n",
                        lDay,
                        lHour,
                        lMin,
                        lSek,
                        lMs);
    wprintf_s(szTime);

//Write to MAIN.Time1
    //Manipulate DWORD for demonstration
    dwTime += 3600000;  //Add 3600000ms (One hour)

    //AdsWrite
    lErr = AdsSyncWriteReq(pAddr,
                            ADSIGRP_SYM_VALBYHND,	//IndexGroup
                            lHdlTime,			//IndexOffset
                            0x4,
                            &dwTime);
    if(lErr) printf_s((char*)"Error: Write time variable: 0x%i \n", lErr);

//Read from MAIN.Date1
    //AdsRead
    lErr = AdsSyncReadReq(pAddr,
                            ADSIGRP_SYM_VALBYHND,	//IndexGroup
                            lHdlDate,			//IndexOffset
                            0x4,
                            &dwDate);
    if(lErr) printf_s((char*)"Error: Read date variable: 0x%i  \n", lErr);

    //Convert long to date
    time_t tDate(dwDate);
    tm tmDate;

    gmtime_s(&tmDate, &tDate);

    wchar_t szDate[DATE_LENGHT];
    wsprintf(szDate, L"Date from PLC: %d/%d/%d %d:%d:%d \n",
                tmDate.tm_mday,
                tmDate.tm_mon + MON_START,
                tmDate.tm_year + YEAR_START,
                tmDate.tm_hour,
                tmDate.tm_min,
                tmDate.tm_sec);
    wprintf_s(szDate);

//Write to MAIN.Date1
    //Manipulate DWORD for demonstration
    dwDate += 3600; //Add 3600s (One hour)

    //AdsWrite
    lErr = AdsSyncWriteReq(pAddr,
                            ADSIGRP_SYM_VALBYHND,	//IndexGroup
                            lHdlDate,			//IndexOffset
                            0x4,
                            &dwDate);
    if(lErr) printf_s((char*)L"Error: Write time variable: 0x%i \n", lErr);

//Releases handles of plc variable
    lErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlTime), &lHdlTime);
    if (lErr) printf("Error: AdsSyncWriteReq: %d \n", lErr);
    lErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlDate), &lHdlDate);
    if (lErr) printf("Error: AdsSyncWriteReq: %d \n", lErr);

//Close the communication
    lErr = AdsPortClose();
    if(lErr) printf_s((char*)L"Error: Closing connection: 0x%i \n", lErr);

    printf_s("\nPress enter to exit..");
    getchar();
}


Linking in Embedded Visual C++

TwinCAT TcAdsDLLCe: Reference

Linking in Visual Studio

To use the TcAdsDllCe in your Visual Studio project, you have to perform following steps:

  • Open Project Settings Dialog
  • Select Link Tab and insert the path of the library corresponding to your processor in the edit box Object/library modules:

- StrongArm: TwinCATDirectory\ADS Api\AdsDllCe\lib\arm\TcAdsDllCe.lib

- MIPS: StrongArm: TwinCATDirectory\ADS Api\AdsDllCe\lib\mips\TcAdsDllCe.lib

- X86: TwinCATDirectory\ADS Api\TcAdsDllCe\lib\x86\TcAdsDllCe.lib

The functions of the TcAdsDllCE.dll are compatible to those of the TcAdsDll.dll.

Additionally the Windows CE SDK is needed. It is located on our FTP Server.

Connection to Visual Studio

Visual Studio allows to connect to Windows CE Devices for transfer your application or online debugging.

Please read following MSDN-articel to configure the connection.


TcAdsDllCE Samples Übersicht

TwinCAT TcAdsDLLCe: Examples

Overview

Description | Source
Example 1: Read the value of a SPS-Variable | Sample01-CE-C-ADS-DLL-Read.zip
Example 2: Write a value to a SPS-Variable | Sample02-CE-C-ADS-DLL-Write.zip
Example 3: Access by variable name | Sample03-CE-C-ADS-DLL-ReadWriteByName.zip
Example 4: Event driven reading | Sample04-CE-C-ADS-DLL-Events.zip


ADS Return Codes

Error codes: 0x000..., 0x500..., 0x700..., 0x1000..., 0x274C...

Global Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x0 | 0 | no error |  |
0x1 | 1 | Internal error |  |
0x2 | 2 | No Rtime |  |
0x3 | 3 | Allocation locked memory error |  |
0x4 | 4 | Insert mailbox error | No ADS mailbox was available to process this message. | Reduce the number of ADS calls (e.g ADS-Sum commands or Max Delay Parameter)
0x5 | 5 | Wrong receive HMSG |  |
0x6 | 6 | target port not found | ADS Server not started |
0x7 | 7 | target machine not found | Missing ADS routes |
0x8 | 8 | Unknown command ID |  |
0x9 | 9 | Bad task ID |  |
0xA | 10 | No IO |  |
0xB | 11 | Unknown ADS command |  |
0xC | 12 | Win 32 error |  |
0xD | 13 | Port not connected |  |
0xE | 14 | Invalid ADS length |  |
0xF | 15 | Invalid ADS Net ID |  |
0x10 | 16 | Low Installation level |  |
0x11 | 17 | No debug available |  |
0x12 | 18 | Port disabled |  |
0x13 | 19 | Port already connected |  |
0x14 | 20 | ADS Sync Win32 error |  |
0x15 | 21 | ADS Sync Timeout |  |
0x16 | 22 | ADS Sync AMS error |  |
0x17 | 23 | ADS Sync no index map |  |
0x18 | 24 | Invalid ADS port |  |
0x19 | 25 | No memory |  |
0x1A | 26 | TCP send error |  |
0x1B | 27 | Host unreachable |  |
0x1C | 28 | Invalid AMS fragment |  |

Router Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x500 | 1280 | ROUTERERR_NOLOCKEDMEMORY | No locked memory can be allocated |
0x501 | 1281 | ROUTERERR_RESIZEMEMORY | The size of the router memory could not be changed |
0x502 | 1282 | ROUTERERR_MAILBOXFULL | The mailbox has reached the maximum number of possible messages. The current sent message was rejected | Check the connection between the communication partners
0x503 | 1283 | ROUTERERR_DEBUGBOXFULL | The mailbox has reached the maximum number of possible messages. The sent message will not be displayed in the debug monitor | Check the connection to the debug monitor
0x504 | 1284 | ROUTERERR_UNKNOWNPORTTYPE | The port type is unknown |
0x505 | 1285 | ROUTERERR_NOTINITIALIZED | Router is not initialised |
0x506 | 1286 | ROUTERERR_PORTALREADYINUSE | The desired port number is already assigned |
0x507 | 1287 | ROUTERERR_NOTREGISTERED | Port not registered |
0x508 | 1288 | ROUTERERR_NOMOREQUEUES | The maximum number of Ports reached |
0x509 | 1289 | ROUTERERR_INVALIDPORT | The port is invalid. |
0x50A | 1290 | ROUTERERR_NOTACTIVATED | TwinCAT Router not active |
0x50B | 1291 | ROUTERERR_FRAGMENTBOXFULL |  |
0x50C | 1292 | ROUTERERR_FRAGMENTTIMEOUT |  |
0x50D | 1293 | ROUTERERR_TOBEREMOVED |  |

General ADS Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x700 | 1792 | error class <device error> |  |
0x701 | 1793 | Service is not supported by server |  |
0x702 | 1794 | invalid index group |  |
0x703 | 1795 | invalid index offset |  |
0x704 | 1796 | reading/writing not permitted |  |
0x705 | 1797 | parameter size not correct |  |
0x706 | 1798 | invalid parameter value(s) |  |
0x707 | 1799 | device is not in a ready state |  |
0x708 | 1800 | device is busy |  |
0x709 | 1801 | invalid context (must be in Windows) |  |
0x70A | 1802 | out of memory |  |
0x70B | 1803 | invalid parameter value(s) |  |
0x70C | 1804 | not found (files, ...) |  |
0x70D | 1805 | syntax error in command or file |  |
0x70E | 1806 | objects do not match |  |
0x70F | 1807 | object already exists |  |
0x710 | 1808 | symbol not found |  |
0x711 | 1809 | symbol version invalid | Onlinechange | Release handle and get a new one
0x712 | 1810 | server is in invalid state |  |
0x713 | 1811 | AdsTransMode not supported |  |
0x714 | 1812 | Notification handle is invalid | Onlinechange | Release handle and get a new one
0x715 | 1813 | Notification client not registered |  |
0x716 | 1814 | no more notification handles |  |
0x717 | 1815 | size for watch too big |  |
0x718 | 1816 | device not initialized |  |
0x719 | 1817 | device has a timeout |  |
0x71A | 1818 | query interface failed |  |
0x71B | 1819 | wrong interface required |  |
0x71C | 1820 | class ID is invalid |  |
0x71D | 1821 | object ID is invalid |  |
0x71E | 1822 | request is pending |  |
0x71F | 1823 | request is aborted |  |
0x720 | 1824 | signal warning |  |
0x721 | 1825 | invalid array index |  |
0x722 | 1826 | symbol not active | Onlinechange | Release handle and get a new one
0x723 | 1827 | access denied |  |
0x724 | 1828 | missing license |  | Activate license for TwinCAT 3 function
0x72c | 1836 | exception occured during system start |  | Check each device transistions
0x740 | 1856 | Error class <client error> |  |
0x741 | 1857 | invalid parameter at service |  |
0x742 | 1858 | polling list is empty |  |
0x743 | 1859 | var connection already in use |  |
0x744 | 1860 | invoke ID in use |  |
0x745 | 1861 | timeout elapsed |  | Check ADS routes of sender and receiver and your firewall setting
0x746 | 1862 | error in win32 subsystem |  |
0x747 | 1863 | Invalid client timeout value |  |
0x748 | 1864 | ads-port not opened |  |
0x750 | 1872 | internal error in ads sync |  |
0x751 | 1873 | hash table overflow |  |
0x752 | 1874 | key not found in hash |  |
0x753 | 1875 | no more symbols in cache |  |
0x754 | 1876 | invalid response received |  |
0x755 | 1877 | sync port is locked |  |

RTime Error Codes

Hex | Dec | Description | Possible Causes
0x1000 | 4096 | RTERR_INTERNAL | Internal fatal error in the TwinCAT real-time system
0x1001 | 4097 | RTERR_BADTIMERPERIODS | Timer value not vaild
0x1002 | 4098 | RTERR_INVALIDTASKPTR | Task pointer has the invalid value ZERO
0x1003 | 4099 | RTERR_INVALIDSTACKPTR | Task stack pointer has the invalid value ZERO
0x1004 | 4100 | RTERR_PRIOEXISTS | The demand task priority is already assigned
0x1005 | 4101 | RTERR_NOMORETCB | No more free TCB (Task Control Block) available. Maximum number of TCBs is 64
0x1006 | 4102 | RTERR_NOMORESEMAS | No more free semaphores available. Maximum number of semaphores is 64
0x1007 | 4103 | RTERR_NOMOREQUEUES | No more free queue available. Maximum number of queue is 64
0x1008 | 4104 | TwinCAT reserved. |
0x1009 | 4105 | TwinCAT reserved. |
0x100A | 4106 | TwinCAT reserved. |
0x100B | 4107 | TwinCAT reserved. |
0x100C | 4108 | TwinCAT reserved. |
0x100D | 4109 | RTERR_EXTIRQALREADYDEF | An external synchronisation interrupt is already applied
0x100E | 4110 | RTERR_EXTIRQNOTDEF | No external synchronsiation interrupt applied
0x100F | 4111 | RTERR_EXTIRQINSTALLFAILED | The apply of the external synchronisation interrupt failed
0x1010 | 4112 | RTERR_IRQLNOTLESSOREQUAL | Call of a service function in the wrong context
0x1017 | 4119 | RTERR_VMXNOTSUPPORTED | Intel VT-x extension is not supported.
0x1018 | 4120 | RTERR_VMXDISABLED | Intel VT-x extension is not enabled in BIOS.
0x1019 | 4121 | RTERR_VMXCONTROLSMISSING | Missing feature in Intel VT-x extension.
0x101A | 4122 | RTERR_VMXENABLEFAILS | Enabling Intel VT-x fails.

TCP Winsock Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x274c | 10060 | A socket operation was attempted to an unreachable host | Host unreachable | Check network connection via ping
0x274d | 10061 | A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. | Host unreachable | Check network connection via ping
0x2751 | 10065 | No connection could be made because the target machine actively refused it |  |
 |  | Further Winsock error codes: Win32 Error Codes |  |