Twincat 3: How to convert 4 HEX array to Float? - plc

we are receiving (via UDP datagram) a float value codified by 4 bytes hex array.
We need to convert from 4 hex bytes to a float.
udp_data[0] = 'BE';
udp_data[1] = '7A';
udp_data[2] = 'E0';
udp_data[3] = 'F4';
In the given example, the correct equivalence, after transformation, udp_data is equivalent to -0.24499:
What is the optimal conversion in Twincat 3 PLC? maybe some library? We need to perform 52 transformation at once of this type.
I attached an example with an example taken from an online calculator:
Thanks!!

You can use a UNION type, which will at the same address hold a byte array (like the one you get from your UDP communication) and the real var which you want to convert to.
When you change the byte array, the real automatically reflects it. The conversion works the other way around also, in fact.
TYPE U_Convert :
UNION
arrUDP_Data: ARRAY [0 .. 3] OF BYTE; // Array must start with LSB
rReal : REAL;
END_UNION
END_TYPE
In MAIN you can declare the following var.
VAR
uConvert: U_Convert;
fValue : REAL;
END_VAR
And in the body of MAIN, update the byte array to requested values.
// Here we update the byte array
uConvert.arrUDP_Data[0] := 16#F4; // LSB
uConvert.arrUDP_Data[1] := 16#E0;
uConvert.arrUDP_Data[2] := 16#7A;
uConvert.arrUDP_Data[3] := 16#BE; // MSB
// Here we 'use' the converted value
fValue := uConvert.rReal;

I assume you have an array of bytes.
Header (put this in own function block if you want):
PROGRAM MAIN
VAR
aByteArray : ARRAY[1..4] OF BYTE := [16#F4, 16#E0, 16#7A, 16#BE];
pt : POINTER TO REAL;
fRealValue : REAL;
END_VAR
Body:
pt := ADR(aByteArray);
fRealValue := pt^;
Will give you the desired result:

Related

Declare values of array of structures in POU using PLC ST

I have structure declared like this:
TYPE board:
STRUCT
number: INT;
color: DWORD;
END_STRUCT
END_TYPE
And i want to declare array of these structures with starting values in POU. I do it like this:
Program PLC_PRG
VAR
arr1: ARRAY[1..61] OF board;
board: board;
arr1[1].color := 16#FF0000;
END_VAR
But i get an error which say: "Error 4024: PLC_PRG(10): Expecting ':' before '['".
Do anybody know how to solve this problem?
You didn't specify a platform but I'll assume TwinCAT. To initialize an array of structures, you have to do it all in the variable declaration line.
arr1: ARRAY[1..61] OF board := [(number:=7, color:=16#FF0000), (number:=5, color:=16#FF0001)];
This example will initialize elements 1 and 2 of the array. To my knowledge you can't selectively initialize individual array elements as you are maybe wanting to do. You could use statements like arr1[17].color := 16#FF0000 of course but it would have to be outside of your variable declaration block.

Multi-variable assignment to an array in TwinCAT

How can assign a new variable to an array in TwinCAT?
in TwinCAT you can initialize all your array's argument directly for example for array a we can use:
a : ARRAY [1..3] OF INT := [3(0)];
or
a : ARRAY [1..3] OF INT := [0,0,0];
but if you want to assign the array in the main program(not initializing part) for example
a:=[2,8,5];
you will face this error tip: Unexpected array initialisation.
any help would be appreciated.
You cannot directly initialize arrays inside the program part.
That beeing said, the best option is porbably to define a constant containing the desired initialization values and then assigning the value of that constant to your array:
VAR
aiMyArray : ARRAY [1..2] OF INT;
END_VAR
VAR CONSTANT
aiInitializerMyArrayOptionA : ARRAY [1..2] OF INT := [1,2];
aiInitializerMyArrayOptionB : ARRAY [1..2] OF INT := [3,4];
END_VAR
IF bCondition THEN
aiMyArray := aiInitializerMyArrayOptionA;
ELSE
aiMyArray := aiInitializerMyArrayOptionB;
END_IF
The other option would be to manually initialize each index one by one which easily gets impracticale with decent array sizes:
IF bCondition THEN
aiMyArray[1] := 1;
aiMyArray[2] := 2;
ELSE
aiMyArray[1] := 3;
aiMyArray[2] := 4;
END_IF
Looping thorugh all elements and assigning values might be an option too. But that would only be usefull when the values are no arbitrary constatns but you are able to calculate the values from some formula.

Initialize Array of Custom Types in Structured Text Syntax

In my project I have a type like:
TYPE myDataStruct :
STRUCT
A : UINT;
B : WORD;
C : REAL;
D : Custom_Obj;
END_STRUCT
END_TYPE
And I need to keep an array of this type for persistent memory. I can't just use VAR RETAIN because this particular piece of memory needs to persist through a download. The controller I am using has a way to do this but in order for it to work I need to be able to set the array equal to an initial value. So if I have declared
myarray := ARRAY[0..20] OF myDataStruct;
How do I then initialize this array to a blank array? What is the equivalent of new in other languages?
I have guessed
myarray := [21(A := 0,
B := '',
C := 0.0,
D := ??? )];
But that doesn't appear to be right. It could be simplified if there were only one level deep of custom structs and for this application I could do that. However, I still don't think I have the syntax right.
What is the equivalent of new in other languages?
The analog of this is
VAR
EmptyArray : ARRAY[0..20] OF myDataStruct;
END_VAR
If you want to pre-populate it with default values
VAR
EmptyArray : ARRAY[0..20] OF myDataStruct := [
(A := 100, B := 200, С := 0.0, D := ???),
(A := 34, B := 45, С := 0.1, D := ???),
..... etc
];
END_VAR
For CoDeSys 2.3 delete [ and ].
What you have to understand that EmptyArray is not a prototype of data you need but already initialized variable.
There is no way to initialize it in "x = new struct()" way. You also can't assign the whole array in the code with something like myarray = [1, 2, 3] etc, as far as I know.
If you just want to set it empty with values like 0, '', etc, then there are two ways that I would use:
1. Use MEMSET function to set all bytes to 0
Link to the online help
//Something like
MemSet(
pbyBuffer := ADR(myarray), //Address of the variable
byValue := 0, //Byte that address is filled with
dwSize := SIZEOF(myarray) //How many bytes? (variable size)
)
2. Create a dummy variable and assign it to the myarray
The variable is always initialized to zeros, so EmptyArray values are all 0/empty etc.
VAR
EmptyArray : ARRAY[0..20] OF myDataStruct;
END_VAR
//In the code
myarray := EmptyArray;
I hope I understood your question correctly.

Passing Delphi 2D dynamic array to C DLL

I am trying to use a DLL written in C, wrapping a Matlab DLL.
The function is defined in C as:
int wmlfLevel1(double* input2D, int size, char* message, double** output2d)
and my Delphi function is defined as:
func: function (input2d: pDouble; size: integer; messag: PAnsiChar; output2d: ppDouble): integer; cdecl;
I have defined these types to pass a matrix to the DLL:
type
TDynArrayOfDouble = array of double;
type
T2DDynArrayOfDouble = array of TDynArrayOfDouble;
type
ppDouble = ^pDouble;
and am calling the DLL function like this:
var
in2d: T2DDynArrayOfDouble;
size: integer;
msg: PAnsiChar;
out2d: T2DDynArrayOfDouble;
begin
size:= 3;
SetLength(in2d, size, size);
SetLength(out2d, size, size);
in2d[0][0]:= 1; ... // init matrix values
func(pDouble(in2d), size, msg, ppdouble(out2d));
The problem is the output matrix contains a huge amount of incorrect values (should contains the input matrix multiplied by 2).
What have I missed?
I can successfully call this DLL function using a static array with the following code:
type
T2DArrayOfDouble = array [0..2, 0..2] of double;
type
pT2DArrayOfDouble = ^T2DArrayOfDouble;
type
ppT2DArrayOfDouble = ^pT2DArrayOfDouble;
func: function (input2d: pT2DArrayOfDouble; size: integer; messag: PAnsiChar; output2d: ppT2DArrayOfDouble): integer; cdecl;
...
var
in2d: T2DArrayOfDouble;
size: integer;
msg: PAnsiChar;
out2d: pT2DArrayOfDouble;
begin
size:= High(in2d) + 1;
in2d[0][0]:= 1; ... // init matrix values
func(#in2d, size, msg, #out2d);
double* input2D
That's a pointer to an array of double. But you pass it
in2d: T2DArrayOfDouble
which is a pointer to an array of pointer to array of double.
In other words you are passing double** to a parameter that expects double*.
Solve the problem by changing
in2d: T2DArrayOfDouble
to
in2d: TDynArrayOfDouble
Obviously you'll have to change the SetLength to match, and the code that populated the array. Although one wonders if there isn't a deeper problem given that the argument name suggests that, contrary to the type, it is a two dimensional array.

MATLAB wchar_t Equivalent Type

The C-Code is stored in a DLL. I can load the DLL in MATLAB with the loadlibrary function. I am having trouble passing the wchar_t*[] parameter to the function. I do not know how to create this data type in MATLAB. Does anyone know how to create this type to pass to the calllib function?
MATLAB Code:
loadlibrary('test.dll', 'test.h');
str = '0';
ptr = libpoiner('voidPtrPtr', [int8(str) 0])
calllib('test.dll', 'testFunction', ptr) %this parameter does not match the wchar*[] type
outVal = ptr.Value
C-Code:
void testFunction(wchar_t* str[])
{
str[0] = L"test";
}
Output:
MATLAB allows the function to complete. The outVal variable is filled with garbage values.
If you are able to modify the C header files, you may try the following:
Adjust the header file to convert all wchar_t * to unsigned short *.
On the MATLAB side, the corresponding type would then be a uint16 array.
You could then typecast the uint16 array to char.
I figured it out. I changed the MATLAB code to the following:
loadlibrary('test.dll', 'test.h');
str = '0';
ptr = libpoiner('voidPtrPtr', [uint16(str) 0])
calllib('test.dll', 'testFunction', ptr) %this parameter does not match the wchar*[] type
outVal = ptr.Value
expectedOutput = char(outVal); %convert to ASCII
It outputs the values in decimal which confused me. When I converted them to ASCII, everything made sense.