We are having issues right now, and I just stumbled on that post. It seems to me you are saying your Linux ARM control is RPi 5-compatible, but the one you sell specifically for RPi is not. The fix you are proposing requires customers to re-purchase a CODESYS licence. Is there a time frame for that to be fixed?
https://us.store.codesys.com/codesys-control-for-raspberry-pi-sl.html Quoted from the "additional requirements" section : The operating system Raspberry Pi OS (32-bit)
4.11 appears to solve the problem. Thanks for your help. I had first installed the 64-bit version of RPiOS, but looking at various posts and the product's page on the store which only lists 32-bit compatibility, I downgraded to the 32-bit version. I did this before trying 4.11, and it did not solve the problem. Thus my working system is 32-bit, and I am wondering if I can go back to 64-bit or not. An 64-bit OS is not listed as compatible on the store's web page, but when installing there actually...
I have a Raspberry Pi 4 we have been using for years to run CODESYS for development purposes. I recently have had to re-install the OS (Raspberry OS lite). The only change to the default configuration is not creating the "pi" user during install. I (apparently successfully) installed the 4.10.0.0 control on the Raspberry Pi. We have licences on a dongle. I am able to start the control. systemctl status shows it as running for a little while (exactly, precisely 30 seconds as per my tests, every time),...
In the past, we have routinely used the "add all instance paths" function to automatically add variables to VAR_CONFIG lists. My recollection is that compiling a project did not require VAR_CONFIG to be fully populated, and, once successfully compiled, instance paths could be added. Errors related to missing declarations, if I recall correctly, were thrown when downloading to the PLC, they did not prevent a successful compile. At some point, it stopped working. Missing declarations in a VAR_CONFIG...
If the relationship between level and volume is amenable to it, one thing you can do is to use software that can perform a regression to get a a formula that approximates volume as a function of the level (Excel graphs can do that). Then, instead of a lookup table, you just perform a direct calculation. It makes for more concise code. Obviously, you need a different formula for each tank geometry, but the same is true with lookup tables (or at least their content).
I am the guy from the Stackoverflow answer. Let me attempt to give you a few more breadcrumbs. You are likely to want to achieve any combination of these goals. Make your control logic independent from your hardware Avoid hard-coded hardware addressing. Allow several instances of whichever device you control (e.g. motor) Make your code portable (for me, this means CODESYS and TwinCAT). I have found only one sane way to check all these boxes. This way involves not trying to achieve all these goals...
I am the guy from the Stackoverflow answer. Let me attempt to give you a few more breadcrumbs. You are likely to want to achieve any combination of these goals. Make your control logic independent from your hardware Avoid hard-coded hardware addressing. Allow several instances of whichever device you control (e.g. motor) Make your code portable (for me, this means CODESYS and TwinCAT). I have found only one sane way to check all these boxes. This way involves not trying to achieve all these goals...
Are you trying to achieve the equivalent of generics (i.e. have a "container" function block that contains elements of an arbitrary type, but all elements of the same type for a given instance of the container) or mixed types (i.e. elements of different types within the same container)?
Maybe I am making things look more complicated than they are. Variables defined as "REFERENCE TO" are "direct reference to the same tag or structure just with a different name". I have not used AB environments, but maybe it is not so different after all? As for the rest, what one considers "simple" is always defined in terms of what they know and prefer, as well as the problem to be solved. My "simple" is likely, for good reasons, not the same as yours.
*Perhaps in Codesys you could have the project in the version control so that it does not have any IO (only "AT %I" and "AT %Q"). * This is exactly what I was suggesting, the "project" in your version control being a library, so that it can be referenced from the actual (machine-specific) project. This library can be pre-compiled and versioned (or not). The machine-specific project contains I/O components (fieldbuses, terminals, etc.) and the mappings. That way, if you have an update to the control...
You can put your control logic in a library (i.e. using "AT %I" and "AT %Q" variables as anchor points for mapping, or whipping your own pointer-based strategy to achieve the indirection), and then use a VAR_CONFIG list to map hardware addresses to that software, or use the mapping GUI. Out of curiosity, what method in TwinCAT are you referring to? I am very eager to find out about something I do not know, for the time being we feel TwinCAT is limited compared to CODESYS, because to my (limited)...
I have not worked like you do with ladder diagrams, but here are a few ideas that may, or may not work for you. Declare and initialize REFERENCE TO variables (similar to pointers, but typically cleaner syntax) to your most used variables. For instance, you could reduce "Interface_Axis1.ProfileGeneration" to "Ax1PG". If this is an option, write your ladder diagram as a method of a function block that inherits from the manufacturer's main function block. That could allow the first level of naming to...
I have not worked like you do with ladder diagrams, but here are a few ideas that may, or may not work for you. Declare and initialize REFERENCE TO variables (similar to pointers, but typically cleaner syntax) to your most used variables. For instance, you could reduce "Interface_Axis1.ProfileGeneration" to "Ax1PG". If this is an option, write your ladder diagram as a method of a function block that inherits from the manufacturer's main function block. That could allow the first level of naming to...
As mentioned above, the assignment operator performs a copy. If the variable being copied is a pointer, then it is the pointer (a memory address) that is copied, so while it still remains a copy technically speaking, it does not create a new instance of whatever is referenced by the pointer. Using ":=" with references applies copy semantics. Using "REF=" with a reference on the left has the same semantics as using ":=" on a pointer, as no data is copied, just the reference being made to point to...
Personally, I would forego the union and individual entry structures and only use the array. It is more concise, and allows adjusting array size with a "configurable constant" (no other code change). This still allows "AT" addresses for communication. I only use structured code, maybe this blinds me to cases where arrays are not as easy to code for. However, if you prefer, or for some reason are forced, to have variables accessible in a way that does not use arrays, the above should work.
Here is what I think you are trying to achieve : Allow access to variables without the use of arrays for parts of your code that cannot deal with arrays Allow access to variables as an array for other parts of your code that are simpler (and easier to modify) done this way. Allow mapping to "AT" addresses for communications purposes Here is one way this may be done. First, I will assume you have a structure for each of your analog values that looks like this : TYPE Analog_300_A13EGA10CF001 : STRUCT...
Here is what I think you are trying to achieve : Allow access to variables without the use of arrays for parts of your code that cannot deal with arrays Allow access to variables as an array for other parts of your code that are simpler (and easier to modify) done this way. * Allow mapping to "AT" addresses for communications purposes Here is one way this may be done. First, I will assume you have a structure for each of your analog values that looks like this : TYPE Analog_300_A13EGA10CF001 : STRUCT...
I am a bit confused. The code you provide does not seem to reference your global variables list, there are no "AT" addresses. I do not understand what your intent is with the code at the bottom, or what you mean exactly by "working, too fast rewrites values".
Do you mean something like this? PROGRAM MAIN_b223d63214 VAR int_array AT %MW0: ARRAY[0..1000] OF INT; value_0, value_1: INT; END_VAR int_array[0] := 12; int_array[1] := 13; value_0 := int_array[0]; value_1 := int_array[1]; This will also work using a structure as a type rather than an "ARRAY OF", where said structure may itself recursively contain structures and arrays. Maybe you could explain a bit why you want to use %M addresses to help us understand what you are trying to achieve?
Any type is binary, in the sense that all values are represented in memory as sequences of 0 and 1. Your INT is already made up of 0 and 1, which you can directly access. For instance, if you have a variable like this : myint: INT; You can access each bit like this : least_significant_bit := myint.0; most_significant_bit := myint.15; You could also use bitwise operators (SHL, SHR, ROL, ROR) to perform bitwise manipulation of values. Getting specific bits out of a numerical value in CODESYS is therefore...
Any type is binary, in the sense that all values are represented in memory as sequences of 0 and 1. Your INT is already made up of 0 and 1, which you can directly access. For instance, if you have a variable like this : myint: INT; You can access each bit like this : first_bit := myint.0; last_bit := myint.15; You could also use bitwise operators (SHL, SHR, ROL, ROR) to perform bitwise manipulation of values. Getting specific bits out of a numerical value in CODESYS is therefore easy.
When you say "to a 4 bit binary number", what is it that you mean by "binary number"? A four-character string with each character being either 0 or 1? A numeric variable (of 8, 16 , 32 or 64 bits) with only the four less significant bits assigned (which can code numbers from 0 to 15)?
Reading the following, please note I am not an expert and have not used TCP communication between PLCs, and there may be simpler ways to deal with this that I do not know about. It is a bit tricky to diagnose byte swapping issues without access to the full system, but I will hazard a guess as to what may be happening: One of your CPU is little endian, the other is big endian. No byte swapping is actually occurring during TCP communication. You are seeing different values because when the receiving...
I think writing to inputs to give them an initial value from a FB_Init is fine in all cases. Of course, thereafter, if the inputs are cyclically fed with values and the inputs are also written to from code, there is going to be a problem. I use structured text as a "normal" programming language. I have implemented my own cyclical execution framework (my function blocks have no body, and that empty body is never called). My function blocks cannot be used in a ladder diagram, which is not a tradeoff...
Expanding on h-hermsen's final tip, consider this idea: The main program : // Main program PROGRAM MAIN VAR {attribute 'init_on_onlchange' } C_init: MVB_Set_Parameters(TRUE, 16, 3); END_VAR The helper function block : FUNCTION_BLOCK MVB_Set_Parameters VAR_INPUT Condition:BOOL; X:INT; Y:INT; END_VAR VAR_OUTPUT xError:INT; END_VAR The helper function block's FB_Init : METHOD FB_Init: BOOL VAR_INPUT bInitRetains: BOOL; bInCopyCode: BOOL; Condition:BOOL; X:INT; Y:INT; END_VAR THIS^.Condition:= Condition;...
You write : FBInit can not initialize FUNCTIONBLOCK inputs since they are INPUTS and thus they are read only and BY VALUE from point of view of method FBInit Method. Do you mean this as a recommendation, or as a statement of limitations of the compiler? I do not think the compiler enforces VAR_INPUT to be read-only from within the function block or its FB_Init. VAR_INPUT variable can also be of the "REFERENCE TO" or "POINTER TO" kinds, so inputs are not necessarily BY VALUE. Having FB_Init write...
I do not know whether the OP meant "3rd byte" as "last of 4" or "third of 4", but in the latter case, I think the mask should be "16#FF00FFFF" and the shift should be "2*8". I would probably use "OR" rather than "+", but I think in this case they are interchangeable.
I do not know whether the OP meant "3rd byte" as "last of 4" or "third of 4", but in the latter case, I think the mask should be "16#FF00FFFF" and the shift should be 2*8). I would probably use "OR" rather than "+", but I think in this case they are interchangeable.
I do not know whether the OP meant "3rd byte" as "last of 4" or "third of 4", but in the latter case, I think the mask should be "16#00FFFFFF" and the shift should be 2*8). I would probably use "OR" rather than "+", but I think in this case they are interchangeable.
I would like to understand what you mean by "that would cost processor time loading it into memory". Outside of arguments passed by value and assignments (which require the array to be copied, but probably are an atypical way to use an array), do you know of cases where a large array size causes sustained CPU load issues by itself, not related to explicit processing such as iterating over the array?
I develop everything in structured text, with no desire to make my function blocks amenable to use in the visual languages. Maybe someone working in ladder all the time has a use for passing FBs by value that I do now know of, but I could really not tell. The way I develop, I would rather have function blocks passed by reference by default. I do not remember ever passing a FB by value, or using the assignment operator on a FB (instead of REF= which I use very, very heavily). In fact, I default to...
You are looking for ways to pass arguments by reference rather than value. Google "CODESYS VAR_IN_OUT". You can also define VAR_INPUT variables in functions blocks and functions that are of type "REFERENCE TO X", where "X" is the type you want to pass by reference. Since you are passing a reference, it does not matter that it is defined as an input, you will be able to act on the "thing" that is passed as a reference, not on a copy. I do everything in structured text and do not use VAR_INPUT to visually...
I cannot say if what you are observing is to be expected, but I can share my experience. The image attached is for one library (our biggest). After a "clean all", it compiles in less than one second. That is on a quite beefy desktop with an SSD and more than enough memory, but nothing really special. Our projects, which use a decent number of mostly internal libraries, compile within a couple of seconds after a "clean all", but going online and doing a full download probably takes around 10 seconds....
For variables, maybe VAR_EXTERNAL does what you need, but I have no experience using it so cannot attest to this. For functions, I do not know of a way for a library to depend on code found inside a project. That being said, you may want to consider if what you are trying to achieve is the right design. Typically, you would probably want your project to depend on your libraries, not the other way around. This might mean putting global variables in your library or, maybe better yet, not having global...
I am not saying for sure that it is reinitialized to TRUE, but it could be if, say, the declaration of "firstScan" is located in a method rather than in the function block's body. I do not see the whole code, so it is hard to tell where your problem comes from.
Looking at your code snippet, and assuming the arrays point to valid memory areas (not overlapping other variables), it seems off the top of my mind three things may be wrong : the loop is infinite MERGE_ARRAY is being passed arguments that cause a very long processing time or memory corruption. Your code block is being called during every scan cycle, and the"firstScan" variable is local to the code block and being reinitialized to TRUE every time. It should be possible to rule of some of these easily...
The systems we design look more like 15 cars of different models with a variable (but high) number of accelerator and brake pedals each, and they are modular so we can never hard-code a single memory address anywhere in reusable function blocks. Cascading addresses in the (modular) chain of FB instances is not a realistic solution in the scenarios I deal with. I assume you can use ADR(%IB0) and survive fieldbus issues (not something I have experience with), but this is also a property of mapping...
I initially thought about using the pointer method above, but found out it had a few shortcomings. It requires that the address be valid at the time the function block is called, and if I remember correctly, this may not be the case if the data comes from a fieldbus that is in error. It also makes it hard to have nested components with I/O, because there must be an initialization method that provides each component with the required address(es). It can be done with methods, but the number of method...
FB_Init is itself a method, you can call fbControllerOne's FB_Init from within FB_TemperatureController's own FB_Init and supply it with the appropriate values along with the additional standard arguments FB_Init has but are not visible in variable declarations. Please note that this means fbControllerOne's FB_Init would be called twice, but due to the order of execution, your "manual" call would be performed last. You could also, as you hint to, create a method to set the limits and call that method...
You are right, my comment was based on an incorrect recollection of packing mode behavior.
A BOOL is a 1-byte value, but the CPU reads and writes memory as 4-byte values (32-bit) or 8-byte values (64-bit). The actual space occupied by a BOOL in a structure will depend on the packing mode. If BOOL arrays use single byte alignment, this means CODESYS performs byte-manipulation under the hood, which is certainly possible. I would like to know if this is documented somewhere, and a reliable behavior irrespective of platform.
That is a pretty big array. It has 1170000000 entries. If you use a 32-bit runtime, I assume each BOOL eats up a whole 4-byte word, so this works out to more than 4Gb of memory, which would typically be the limit a 32-bit process can address under the best circumstances. If you are using a 64-bit runtime, this doubles the memory used because memory is addressed in 8-byte words. I am not sure a 9GB+ contiguous array should be expected to fit in a machine with 16GB of memory, but if it does there sure...
I do not know if we have the exact same issue, but in cases where I experience this, I choose "Continue with this version", then perform a "Save as" (not just a "save") and overwrite the file, and the next time I open the project I do not have the message.
Just as a confirmation that the error in the OS installed was indeed the (only) problem, I was able to install and start CODESYS on the RPi after a reinstall of the 32-bit OS.
Thanks! The OS was installed by a colleague, and I did not even realize (a) there was a 64-bit version now and (b) it was not compatible with CODESYS.
I am trying to get CODESYS to run on a freshly-installed RPi (Linux raspberrypi 5.10.103-v8+), with package version 4.4.0.0, and the service is not starting. The executable is not execution, the log file says "file not found". The file that the RPi is trying to launch IS physically present (I can "cat" its contents), but it seems like it is an invalid executable. Is it possible 4.4.0.0 actually does not solve the issue?
I guess it is! Passing methods by address or using lambda expression (which is a nicer way to to something similar) also would be, if not rigorously speaking, at least from a modelling point of view. I am not a scholar, I have just been programming for decades in various languages, so there are concepts that are entirely clear in my mind for which I do not use the vocabulary from textbooks. I write code, not slides or presentations, the compiler/interpreter is my audience... :)
Although you cannot pass the address of a method and there are no lambda expressions in CODESYS, you can pass a pointer/reference of the outer function block to the inner function block, or pass the parent FB in the form of an instance of an interface the outer function block implements. Although it may not be your preferred idiom, I am not sure why you think it would not work. And here by "pass", I mean do it ahead of time (could be using FB_Init) so that the inner FB can keep that pointer/reference...
Although you cannot pass the address of a method and there are no lambda expressions in CODESYS, you can pass a pointer/reference of the outer function block to the inner function block, or pass the parent FB in the form of an instance of an interface the outer function block implements. Although it may not be your preferred idiom, I am not sure why you think it would not work.
I think I see what you mean, an before I settled on this method, I tried to achieve something along those lines. The roadblocks to a better solution that I see : The AT %Q/I* works with addresses so that is a bit low-level, but that solution is very concise. One mapping, one line. It takes a very easy solution to beat that from a complexity perspective. Mapped structures can be big, and nested. For instance, we have Profinet I/O with hundreds of bytes, which are an aggregate of several different...
I think I see what you mean, an before I settled on this method, I tried to achieve something along those lines. The roadblocks to a better solution that I see : The AT %Q/I* works with addresses so that is a bit low-level, but that solution is very concise. One mapping, one line. It takes a very easy solution to beat that from a complexity perspective. Mapped structures can be big, and nested. For instance, we have Profinet I/O with hundreds of bytes, which are an aggregate of several different...
If I understand, you would like to use a single mapping. You can do something like this. PROGRAM MAIN_b59c24a9eb VAR data: POINTER TO WORD; END_VAR data := ADR(%QW0); data[0] := 1; data[1] := 2; However, this hard codes the address in your program, so the component that deals with the addresses is not reusable. What I do is something like this : Define a structure that describes the memory block. In a (reusable) component, declare a variable with "AT %I" or "AT %Q" using this structure. Use a VAR_CONFIG...
I cannot figure out exactly what your are trying to achieve, there is too much that is missing or incorrect in your code. I am under the impression that the best way to solve your problem is fundamentally different from what you are expecting it to be. I am especially puzzled why you are trying to use a doubly-nested FOR loop in order to initialize a single structure, but maybe it is because I am inferring that from separate code blocks that are not really related. A structure is not an array of...
Sorry, I posted part of my answer first on another thread but I wanted to post it here. To achieve what you want, you do not declare a "POINTER TO ARRAY[] OF STRING". You declare a "POINTER TO STRING". You can use the array subscript notation directly with this pointer. The array subscript notation performs dereferencing, just like using "^". In fact, ptr[0] and ptr^ are equivalent as far as I can tell. PROGRAM MAIN VAR INIValues1 : ARRAY [0..1000] OF STRING; INIValues1_ptr : POINTER TO STRING; END_VAR...
To achieve what you want, you do not declare a "POINTER TO ARRAY[] OF STRING". You declare a "POINTER TO STRING". You can use the array subscript notation directly with this pointer. The array subscript notation performs dereferencing, just like using "^". In fact, ptr[0] and ptr^ are equivalent as far as I can tell. PROGRAM MAIN VAR INIValues1 : ARRAY [0..1000] OF STRING; INIValues1_ptr : POINTER TO STRING; END_VAR INIValues1_ptr := ADR(INIValues1); INIValues1_ptr[0] := 'Test';
Is there a specific reason why you want to use a file for that rather than variable access?
Y have recently had issues with multiple projects and libraries when upgrading to 3.5.17.20 (git not involved), even after updating/installing all packages. Maybe it is the same underlying issue? I would open the project/library, wait for a long time as CODESYS checked for something missing, chose whichever of the options that were presented, saved the project (normal save), and the problem was still there when attempting to open again after closing. I eventually stumbled upon the "Save as..." solution....
This is OK as an assumption if both values were created by the CPU. The endinanness check is intended for cases when a value comes from an external source (e.g. fieldbus) the endianness of which does not match the CPU. The intent of the OP seemed to assume both values were byte swapped, but obviously if they are guaranteed to share the same endianness the code becomes only a two-line pointer manipulation.
This is OK as an assumption if both values were created by the CPU. The endinanness check is intended for cases when a value comes from an external source (i.e. fieldbus) the endianness of which does not match the CPU. The intent of the OP seemed to assume both values were byte swapped, but obviously if they are guaranteed to share the same endianness the code becomes only a two-line pointer manipulation.