Post by dkugler on Mail_Service_SL Lib
CODESYS Forge
talk
(Post)
Last updated: 3 days ago
Home (version 1) discussion
jzhvymetal77
wiki
(Thread)
Home (version 1) discussion
Last updated: 3 days ago
Mail_Service_SL Lib
CODESYS Forge
talk
(Thread)
Mail_Service_SL Lib
Last updated: 3 days ago
wiki Discussion
juanp
wiki
(Discussion)
Forum for wiki comments
Last updated: 3 days ago
blog Discussion
juanp
blog
(Discussion)
Forum for blog comments
Last updated: 3 days ago
(no subject)
juanp
wiki
(Thread)
Last updated: 3 days ago
Home
juanp
wiki
(WikiPage)
Project Members: juanp (admin)
Last updated: 3 days ago
wiki Discussion
hugocruz
wiki
(Discussion)
Forum for wiki comments
Last updated: 3 days ago
blog Discussion
hugocruz
blog
(Discussion)
Forum for blog comments
Last updated: 3 days ago
(no subject)
hugocruz
wiki
(Thread)
Last updated: 3 days ago
Home
hugocruz
wiki
(WikiPage)
Project Members: hugocruz (admin)
Last updated: 3 days ago
Post by juanp on Discussion for Generic page
I/O Drivers
doc
(Post)
Hello, I am looking for a devdesc.xml for a driver, such as the Modbus driver, where the number of I/O's can be defined via a value in the parameter tab. In short, the array size of the I/O mapping not as predefined constant, but rather as a variable. Could you help me with this? Thank you in advance.
Last updated: 3 days ago
Generic
I/O Drivers
doc
(WikiPage)
Device Description XML Schema Device identification Connector Interfaces Parameter Set I/O Channels Other parameter attributes Datatypes Basic datatypes Structures Bit Channels Arrays Driver Interface IoDrvUpdateConfiguration Registering for a connector Reading Configuration Preparing I/O channels IoDrvReadInputs / Iodrvwriteoutputs Driver Documentation Generic I/O Driver SPI Driver I²C Driver Driver FB Database Driver IDs A very common way to integrate your own I/O drivers is to use an FB interface inside of a library. A much better way, and much more convenient from the usability is the integration in form of a real I/O driver. This approach should always been choosen when you decide to share your driver with others. To implement an I/O driver in CODESYS you need mainly two things: A device description An FB, implementing the I/O driver interface In the following you will find some general rules to follow while implementing your driver. Beside this guide, you should check out the different templates for different kinds of drivers to start from. Because in detail the implementation will be diffferent if you implement an SPI driver or you read a value from a sensor using a REST API. But independent of the kind of driver, which you plan to implement. If you are not routined in writing such software, it is a good idea to prove the access of the hardware directly in an application. Device Description Every device, which you can add to your CODESYS project, is described in an XML format inside of a Device Description. For finetuning and to get a deeper understanding of the format, please check out the schema file. In the following we will just explain the basics to enable you to write your first description. XML Schema The XML schema can be downloaded under the URI, which is defined in every XML file: http://www.3s-software.com/schemas/DeviceDescription-1.0.xsd Device identification That the device can be uniquely identified in the device repository on every computer in the world, we need to maintain a few IDs. Please register the ID, which you will be using in the Device Database. Device Type: Either use the matching one from the templates, or use 8000. VendorID: Use 0003, as this is reserved for all public domain drivers DeviceID: Use a unique one, which you added to the Device Database Version: You are free with that, but you should always increase it when you make changes to the device description Note: The combination of all IDs, described above has to be globally unique. Official versions of a driver with the same IDs, but different content shall never exist. Connector A device or module, which you can add to the device tree in CODESYS contains at least one connector. This connector is used to attach the module to a parent device. <Connector moduleType="8023" interface="Common.PCI" role="child" explicit="false" connectorId="1" hostpath="-1"> <InterfaceName name="local:PCI">PCI-Bus</InterfaceName> <Slot count="1" allowEmpty="false"></Slot> </Connector> moduletype: use 8000 + the DeviceID. This way you make sure, that you have no conflicts with other public domain drivers. interface: For submodules, like SPI, I2C, or similar, use the interface from the template. For all others, you can use Common.PCI. This interface can be easily attached to most PLCs. ConnectorID: simply enumerate the connectors within your modules. This ID is only used to specify the so called "host path" Interfaces Interfaces are symbolic names to match compatible connectors, so to find compatible devices or modules. We have some different concepts to configure our device tree in CODESYS. Variable Connectors You don't see anything in the device tree. But when you click on "Add Device" you get a list of all devices, which are providing an interface, that is compatible to the interfaces of the parent. The number of allowed devices which can be added to a variable connector can be varied. <Connector moduleType="8023" interface="OpenSource:Internal" role="parent" explicit="false" connectorId="2" hostpath="1"> <InterfaceName name="local:internal">Internal</InterfaceName> <Var max="4"></Var> Fix connectors Fix connectors are very similar to Variable connectors, but the devices are added implicitely and can't be changed. <Connector moduleType="8023" interface="OpenSource:FixConnectorInterface" role="parent" explicit="false" connectorId="2" hostpath="1"> <InterfaceName name="local:internal">Internal</InterfaceName> <Fixed> <!-- This is an example of a fixed module specified in the same file --> <Module> <LocalModuleId>8023</LocalModuleId> </Module> <!-- This is an example of a fixed module specified by DeviceIdentification. The defininition of this module is in another *.devdesc.xml --> <Module> <DeviceIdentification deviceType="40107" deviceId="0001 bcde" version="3.5.9.0"/> </Module> </Fixed> Slots Slots are again similar to variable slots. But the user can see the slot in the device tree. Because every slot has a fix place in the tree, and because the user can plug and remove the devices, the specialty for the driver is, that there can be empty slots. This was a huge preamble for an easy topic. But it was important that you get an idea of how the result will look like. Interfaces are easy. You define one interface per child connector and one for every parent connector. There are no fix restrictions for the interface names. But it is an unofficial rule, that they should be prefixed with a short or full name of the vendor. <Connector ConnectorId="2" HostPath="-1" interface="MyCompany.A" moduleType="40101" role="parent"> <Slot allowEmpty="false" count="16"/> </Connector> <Connector ConnectorId="3" HostPath="-1" interface="MyCompany.B" moduleType="40102" role="parent"> <Slot allowEmpty="false" count="16"/> </Connector> ... <Modules> <Module> <ModuleId>1701</ModuleId> <DeviceInfo> <Name name="localStrings:Name1704">Digital Input</Name> <Description name="localStrings:Desc1704"/> <Vendor name="localStrings:3S">3S-Smart Software Solutions</Vendor> <OrderNumber/> </DeviceInfo> <Connector ConnectorId="2" HostPath="-1" interface="MyCompany.A" moduleType="41101" role="child"> <Slot allowEmpty="false" count="1"/> <HostParameterSet> <Parameter ParameterId="1000" type="std:BIT"> <Attributes channel="input" download="true" functional="false" offlineaccess="readwrite" onlineaccess="readwrite"/> <Default>0</Default> <Name name="local:in1">in1</Name> </Parameter> </HostParameterSet> </Connector> </Module> <Module> <ModuleId>1702</ModuleId> <DeviceInfo> <Name name="localStrings:Name1705">Digital Output</Name> <Description name="localStrings:Desc1705"/> <Vendor name="localStrings:3S">3S-Smart Software Solutions</Vendor> <OrderNumber/> </DeviceInfo> <Connector ConnectorId="3" HostPath="-1" interface="MyCompany.A" moduleType="41102" role="child"> <Slot allowEmpty="false" count="1"/> <HostParameterSet> <Parameter ParameterId="1000" type="std:BIT"> <Attributes channel="output" download="true" functional="false" offlineaccess="readwrite" onlineaccess="readwrite"/> <Default>0</Default> <Name name="local:in1">in1</Name> </Parameter> </HostParameterSet> </Connector> </Module> <Module> <ModuleId>1703</ModuleId> <DeviceInfo> <Name name="localStrings:Name1705">Digital Output</Name> <Description name="localStrings:Desc1705"/> <Vendor name="localStrings:3S">3S-Smart Software Solutions</Vendor> <OrderNumber/> </DeviceInfo> <Connector ConnectorId="4" HostPath="-1" interface="MyCompany.B" moduleType="41103" role="child"> <Slot allowEmpty="false" count="1"/> <HostParameterSet> <Parameter ParameterId="1000" type="std:BIT"> <Attributes channel="output" download="true" functional="false" offlineaccess="readwrite" onlineaccess="readwrite"/> <Default>0</Default> <Name name="local:in1">in1</Name> </Parameter> </HostParameterSet> </Connector> </Module> <Module> <ModuleId>1704</ModuleId> <DeviceInfo> <Name name="localStrings:Name1706">PWM Output</Name> <Description name="localStrings:Desc1706"/> <Vendor name="localStrings:3S">3S-Smart Software Solutions</Vendor> <OrderNumber/> </DeviceInfo> <Connector ConnectorId="5" HostPath="-1" interface="MyCompany.B" moduleType="41104" role="child"> <Slot allowEmpty="false" count="1"/> </Connector> </Module> </Modules> Parameter Set A device can roughly hold two kinds of parameters: configuration parameters, which are important to initialize the I/O system I/O channels, which can be mapped to variables I/O Channels Data types, default values, etc. are defined equally for all kinds of parameters. But there is a significant difference in how the different kinds of parameters are processed. I/O channels are processed in the same way as configuration parameters, thus they have additional mapping information assigned to it, so that they are also passed on to IoDrvReadInputs/-WriteOutputs. I/O channels are marked with the "channel" attribute. <Attributes channel="input" download="true" functional="false" offlineaccess="write" onlineaccess="readwrite" /> Other parameter attributes Download specifies if the parameter is downloaded as part of the connectorlist, that is passed to IoDrvUpdateConfiguration Functional specifies if reading or writing the parameter results in a function call to IoDrvReadParamet/-WriteParameter Online-/OfflineAccess defines the allowed permissions in online or offline mode. Datatypes Both can be configured with the same structured datatypes. They can be located globally on device level, then they are called DeviceParameters. If they are part of the device that the user adds to the tree, then, they are called HostParameters and attached to one of its connectors. You can use virtually every datatype for a parameter, which can be used in IEC61131. This includes especially arrays and structures. But you have to be aware that you need to define those datatypes consistently twice (in your driver library and your device description). There is no extra check. If they don't match between your device description and your code, it will just not work, crash, or whatever... Some examples... Basic datatypes Basic datatypes don't need a declaration. They can be used with the namespace prefix "std". So "std:BOOL" is the equivalent of a BOOL datatype in IEC. DevDesc: <Parameter ParameterId="1000" type="std:DWORD"> <Attributes channel="input" download="true" functional="false" offlineaccess="write" onlineaccess="readwrite" /> <Default>0</Default> <Name name="local:DWIN">DWORD Input</Name> <Description name="local:DWIN.Desc">DWORD Input</Description> </Parameter> Structures IEC: TYPE DUT : STRUCT BYTE1 : BYTE; BYTE2 : BYTE; BYTE3 : BYTE; BYTE4 : BYTE; END_STRUCT END_TYPE DevDesc: <Types namespace="local"> <StructType name="Channel4Byte"> <Component identifier="Byte0" type="std:BYTE"> <Default /> <VisibleName name="local:Byte0">Byte0</VisibleName> </Component> <Component identifier="Byte1" type="std:BYTE"> <Default /> <VisibleName name="local:Byte1">Byte1</VisibleName> </Component> <Component identifier="Byte2" type="std:BYTE"> <Default /> <VisibleName name="local:Byte2">Byte2</VisibleName> </Component> <Component identifier="Byte3" type="std:BYTE"> <Default /> <VisibleName name="local:Byte3">Byte3</VisibleName> </Component> </StructType> </Types> ... <Parameter ParameterId="1000" type="local:Channel4Byte"> <Attributes channel="input" download="true" functional="false" offlineaccess="write" onlineaccess="readwrite" /> <Default>0</Default> <Name name="local:DWIN">DWORD Input</Name> <Description name="local:DWIN.Desc">DWORD Input</Description> <DefaultMapping> <Element name="Byte0">Byte0</Element> <Element name="Byte1">Byte1</Element> <Element name="Byte2">Byte2</Element> <Element name="Byte3">Byte3</Element> </DefaultMapping> </Parameter> Bit Channels IEC: TYPE DUT : STRUCT BYTE1 : BYTE; BYTE2 : BYTE; BYTE3 : BYTE; BYTE4 : BYTE; END_STRUCT END_TYPE DevDesc: <BitfieldType basetype="std:BYTE" name="Bitfield"> <Component identifier="Bit0" type="std:BOOL"> <Default /> <VisibleName name="local:Bitfield.Bit0">Bit0</VisibleName> </Component> <Component identifier="Bit1" type="std:BOOL"> <Default /> <VisibleName name="local:Bitfield.Bit1">Bit1</VisibleName> </Component> <Component identifier="Bit2" type="std:BOOL"> <Default /> <VisibleName name="local:Bitfield.Bit2">Bit2</VisibleName> </Component> <Component identifier="Bit3" type="std:BOOL"> <Default /> <VisibleName name="local:Bitfield.Bit3">Bit3</VisibleName> </Component> <Component identifier="Bit4" type="std:BOOL"> <Default /> <VisibleName name="local:Bitfield.Bit4">Bit4</VisibleName> </Component> <Component identifier="Bit5" type="std:BOOL"> <Default /> <VisibleName name="local:Bitfield.Bit5">Bit5</VisibleName> </Component> <Component identifier="Bit6" type="std:BOOL"> <Default /> <VisibleName name="local:Bitfield.Bit6">Bit6</VisibleName> </Component> <Component identifier="Bit7" type="std:BOOL"> <Default /> <VisibleName name="local:Bitfield.Bit7">Bit7</VisibleName> </Component> </BitfieldType> <StructType name="local:Channel4Byte"> <Component identifier="Byte0" type="local:Bitfield"> <Default /> <VisibleName name="local:Byte0">Byte0</VisibleName> </Component> <Component identifier="Byte1" type="local:Bitfield"> <Default /> <VisibleName name="local:Byte1">Byte1</VisibleName> </Component> <Component identifier="Byte2" type="local:Bitfield"> <Default /> <VisibleName name="local:Byte2">Byte2</VisibleName> </Component> <Component identifier="Byte3" type="local:Bitfield"> <Default /> <VisibleName name="local:Byte3">Byte3</VisibleName> </Component> </StructType> ... <Parameter ParameterId="1000" type="local:Channel4Byte"> <Attributes channel="input" download="true" functional="false" offlineaccess="write" onlineaccess="readwrite" /> <Default>0</Default> <Name name="local:DWIN">DWORD Input</Name> <Description name="local:DWIN.Desc">DWORD Input</Description> </Parameter> Arrays IEC: abyBuffer : ARRAY [0..10] OF BYTE; DevDesc: <ArrayType name="myArray" basetype="std:BYTE"> <FirstDimension> <LowerBorder>0</LowerBorder> <UpperBorder>10</UpperBorder> </FirstDimension> </ArrayType> Driver Interface Please always start with a template, when you start writing a driver. This will give you a good skeleton of your driver. Anyway, this chapter contains a few basic informations about specific interface functions. IoDrvUpdateConfiguration This is always the first entry point when you start writing a driver. It is called when a program is loaded, and gives all drivers the chance to register itself for specific connectors of the device tree. Additionally it gives the driver the chance to prepare itself and to configure the I/O system. This function is called with pConnectorList set to 0, when the application is deleted or reseted. So all drivers need to handle that. IF (pConnectorList = 0) THEN RETURN; END_IF Registering for a connector Check the template for details. In general you search for a connector by its "Module ID" . Then you register your base interface pointer at this connector. This is enough, that you are called by the I/O manager for every I/O update. IF m_pConnector^.hIoDrv = 0 THEN m_pConnector^.hIoDrv := m_hInterface; Reading Configuration You get the whole built time configuration of the application passed to this function. Read those values with IoMgrConfigReadParameter to configure your subsystem. Preparing I/O channels The I/O channels are also just a special type of parameter, and can be read in this early stage. You can use the value "dwDriverSpecific" to prepare your I/Os in a way that you can quickly access them later on. The best way is to store a pointer to the I/O data there. Those can be perfectly used with IoMgrCopy... in IoDrvReadInputs/-WriteOutputs. FOR i:=0 TO 7 DO pParameter := IoMgrConfigGetParameter(m_pConnector, 1000 + i); IF (pParameter <> 0) THEN pParameter^.dwDriverSpecific := ADR(_MCP3008.auiValue[i]); END_IF END_FOR IoDrvReadInputs / Iodrvwriteoutputs CODESYS reduces the updates of the I/O to the bare minimum. The compiler detects already which I/O is used in which task, and calls the drivers from those task contexts in which they are used. To allow the driver to update only the necessary I/O variables, you get a list of mappings passes to those functions. FOR i:=0 TO nCount - 1 DO IF (pConnectorMapList[i].dwNumOfChannels = 0) THEN CONTINUE; END_IF FOR j:= 0 TO UDINT_TO_UINT(pConnectorMapList[i].dwNumOfChannels) - 1 DO IoMgrCopyInputLE(ADR(pConnectorMapList[i].pChannelMapList[j]), pConnectorMapList[i].pChannelMapList[j].pParameter^.dwDriverSpecific); END_FOR END_FOR
Last updated: 3 days ago
Post by jonasz on TargetVisu not accepting touch inputs
CODESYS Forge
talk
(Post)
This solution did not give me peace of mind, so I decided to check other options again, especially since artifacts appear on some controls in WebVis. The solution is to enable the Multitouch handling option in the additional settings tab.
Last updated: 2 days ago
TargetVisu not accepting touch inputs
CODESYS Forge
talk
(Thread)
TargetVisu not accepting touch inputs
Last updated: 2 days ago
(no subject)
I/O Drivers
doc
(Thread)
Last updated: 3 days ago
C0077: Unknown type: 'NULL'
CODESYS Forge
talk
(Thread)
C0077: Unknown type: 'NULL'
Last updated: 2025-05-13
Home (version 1) discussion
williamjmak1
wiki
(Thread)
Home (version 1) discussion
Last updated: 2 days ago
MS SQL Compact cutting off Database name?
CODESYS Forge
talk
(Thread)
MS SQL Compact cutting off Database name?
Last updated: 2 days ago
Home (version 1) discussion
juanp
wiki
(Thread)
Home (version 1) discussion
Last updated: 2 days ago
Post by romrot on MS SQL Compact cutting off Database name?
CODESYS Forge
talk
(Post)
I'm trying to use an fbMsSQL_compact function to read data from an SQL database. It works with some databases, but there is one database that I can't seem to get it to work on. It's set up the same as the other databases that I've tested with the only difference I see is that the name of the database is cut off in the status message. I'll get a message like "User: User logged out succesfully from database: 12345" But the whole name of the database is something like 1234567.
Last updated: 2 days ago
Post by wagokurt on TargetVisu option missing in IDE
CODESYS Forge
talk
(Post)
I have installed both the Codesys Control for Linux SL 4.16.0.0 and Target Visu for Linux 4.16.0.0 packages in Codesys 3.5 SP21 Patch2. When I try to add a target visu, it is missing. I have spent hours on this and tried different machines, all behave the same way. Is this a bug?
Last updated: 1 day ago
Post by davemansell on explicit cast ENUM to UINT and vice versa
CODESYS Forge
talk
(Post)
This is from sometime ago but below might help someone else; this could be done via POINTERS. Make sure you protect for NULL pointers etc if you take this approach. // DECLARATION myEnumValue : enumMyEnum; u8 : USINT; pmyEnumValue : POINTER TO enumMyEnum; pu8 : POINTER TO USINT; // CODE pu8 := ADR(u8); pmyEnumValue := pu8; myEnumValue := pmyEnumValue^; // dereference the pointer
Last updated: 1 day ago
explicit cast ENUM to UINT and vice versa
CODESYS Forge
talk
(Thread)
explicit cast ENUM to UINT and vice versa
Last updated: 1 day ago
Post by davemansell on explicit cast ENUM to UINT and vice versa
CODESYS Forge
talk
(Post)
This is from sometime ago but below might help you could do this via POINTERS. Make sure you protect for NULL pointers etc if you take this approach. // DECLARATION myEnumValue : enumMyEnum; u8 : USINT; pmyEnumValue : POINTER TO enumMyEnum; pu8 : POINTER TO USINT; // CODE pmyEnumValue := pu8; myEnumValue := pmyEnumValue^; // dereference the pointer
Last updated: 1 day ago