C++ versus Step7 -> Add variables to string

wdejong

Member
Join Date
Apr 2007
Location
Teramo
Posts
6
Hello Guys,



Some time ago I wrote an ASCII protocol in C++ on a master PC which is sending a char string to a partner PC. The string contains a lot a formatted integer and floating point values.



A part of the code:



static char outStr[256] , TotalStr[256]

// Declaration of a 256 char String



sprintf(outStr,"%04d%02d%02d\t%02d%02d%02d\t%d\t", wYear, wMonth, wDay, wHour, wMinute, wSecond, StateValue);

// variable outStr is filled with the values behind de format (“… “ )
// place holder.
// so %04d will be filled with the decimal value of wYear and has a fix length

// of 4 positions. Between every value an ASCII <TAB> is added (\t).



strcat(TotalStr,outStr);

// add outStr to TotalStr



sprintf(outStr,"%.2f\t%.2f\t%.2f\t%.2f\t%.1f\t%.2f\t", FloatVal1, FloatVal2, FloatVal3, FloatVal4, FloatVal5, FloatVal6);

// %.2f = floating point value with 2 decimals, no fixed length.



strcat(TotalStr,outStr)

// etc……..





As you see C++ is very powerful of building those kinds of strings.

Now I have to do the same project with a S7 315-2DP CPU including a CP341.

After a few days of studying this stuff, I did not find a solution to build such a string in step7. Probably I’m trying to hard to find the short way which isn’t there.



Does anybody know how to ad formatted integers and floating points to a char string?

If not, how can I make a floating point to string (char) conversion with 1 or two decimals? I have enough experience with programming in step7, so you can leave the basics away.



Thanks for any help.
 
There are numerous string handling routines supplied as part of Simatic manager (FC5 is one IIRC) - look in the Step 7 supplied library projects.
 
Do you have Step 7 Pro? If so you could use SCL (Pascal) I have not worked with strings in SCL but I think it would be very easy with your C++ skills.
 
Personally, I don't use Siemens strings at all. I think you will get much further ahead if you use CHAR arrays instead. That way, you can have (practically) unlimited string lengths and you can easily monitor the string (char) data online.

In any event, let's look at your data. How is it being used? Will it be displayed on an HMI? I would check to see it it's even necessary to do the data conversion. A "string" (or char array) doesn't necessarily need to contain ASCII values, and the destination device might be able to do the conversion better or at least handle the raw data.

If it is absolutely necessary to convert the data, then I would build my own routine that is called while looping through the string. INT and DINT values are easy, but REAL numbers will require a brute force method.
 
Here's a limited function sprintf function implemented in STL. This function uses FC30 and FC38 from the Siemens library to convert the floating point number to a string and the resulting string exponent back to an integer. It will only process positive exponents and does nothing with the number of digits parameter and produces a 28 character string for the number and simply block copies the resultant string to a character array in DB2 for easy monitoring. This code has been tested in the simulator.

Code:
DATA_BLOCK DB 2
TITLE =
VERSION : 0.1

  STRUCT  
   aChar : ARRAY  [1 .. 28 ] OF //Temporary placeholder variable
   CHAR ; 
  END_STRUCT ; 
BEGIN
   aChar[1] := ' '; 
   aChar[2] := ' '; 
   aChar[3] := ' '; 
   aChar[4] := ' '; 
   aChar[5] := ' '; 
   aChar[6] := ' '; 
   aChar[7] := ' '; 
   aChar[8] := ' '; 
   aChar[9] := ' '; 
   aChar[10] := ' '; 
   aChar[11] := ' '; 
   aChar[12] := ' '; 
   aChar[13] := ' '; 
   aChar[14] := ' '; 
   aChar[15] := ' '; 
   aChar[16] := ' '; 
   aChar[17] := ' '; 
   aChar[18] := ' '; 
   aChar[19] := ' '; 
   aChar[20] := ' '; 
   aChar[21] := ' '; 
   aChar[22] := ' '; 
   aChar[23] := ' '; 
   aChar[24] := ' '; 
   aChar[25] := ' '; 
   aChar[26] := ' '; 
   aChar[27] := ' '; 
   aChar[28] := ' '; 
END_DATA_BLOCK
FUNCTION FC 1 : VOID
TITLE =sprintf
VERSION : 0.1

VAR_INPUT
  rValue : REAL ; 
  iDecimals : INT ; 
END_VAR
VAR_TEMP
  aFloat : ARRAY  [1 .. 28 ] OF CHAR ; 
  sFloat : STRING  [14 ]; 
  sExponent : STRING  [14 ]; 
  aExponent : ARRAY  [1 .. 14 ] OF CHAR ; 
  bExponentSign : BOOL ; 
  iExponent : INT ; 
  aResult : ARRAY  [1 .. 28 ] OF CHAR ; 
  cZero : CHAR ; 
  iSFC21Return : INT ; 
  iLoopCount : INT ; 
  iSFC20Return : INT ; 
  aStrippedResult : ARRAY  [1 .. 28 ] OF CHAR ; 
  bIsAplus : BOOL ; 
  bIsAMinus : BOOL ; 
  bIsADigit : BOOL ; 
  bIsAZero : BOOL ; 
END_VAR
BEGIN
NETWORK
TITLE =Init vars
	  L	 '0'; 
	  T	 #cZero; 
	  LAR1  P##sFloat; 
	  L	 14; 
	  T	 B [AR1,P#0.0]; 
	  T	 B [AR1,P#1.0]; 
NETWORK
TITLE =fill with zeros
 
	  CALL SFC   21 (
		   BVAL					 := #cZero,
		   RET_VAL				  := #iSFC21Return,
		   BLK					  := #aFloat);
	  NOP   0; 
NETWORK
TITLE =convert to float
	  CALL FC	30 (
		   IN					   := #rValue,
		   RET_VAL				  := #sFloat);
	  NOP   0; 
NETWORK
TITLE =copy mantissa result only
	  LAR1  P##sFloat; 
	  +AR1  P#2.0; 
	  L	 B [AR1,P#0.0]; 
	  T	 #aFloat[1]; 
	  L	 B [AR1,P#1.0]; 
	  T	 #aFloat[2]; 
	  L	 B [AR1,P#2.0]; 
	  T	 #aFloat[3]; 
	  L	 B [AR1,P#3.0]; 
	  T	 #aFloat[4]; 
	  L	 B [AR1,P#4.0]; 
	  T	 #aFloat[5]; 
	  L	 B [AR1,P#5.0]; 
	  T	 #aFloat[6]; 
	  L	 B [AR1,P#6.0]; 
	  T	 #aFloat[7]; 
	  L	 B [AR1,P#7.0]; 
	  T	 #aFloat[8]; 
	  L	 B [AR1,P#8.0]; 
	  T	 #aFloat[9]; 
	  L	 B [AR1,P#9.0]; 
	  T	 #aFloat[10]; 
 

NETWORK
TITLE =exponent without the E
	  L	 B [AR1,P#11.0]; 
	  L	 '+'; 
	  ==I   ; 
	  =	 #bExponentSign; 
	  L	 W [AR1,P#12.0]; 
	  LAR1  P##sExponent; 
	  T	 W [AR1,P#3.0]; 
	  L	 3; 
	  T	 B [AR1,P#0.0]; 
	  T	 B [AR1,P#1.0]; 
	  L	 '+'; 
	  T	 B [AR1,P#2.0]; 
NETWORK
TITLE =get the exponent
 
	  CALL FC	38 (
		   S						:= #sExponent,
		   RET_VAL				  := #iExponent);
	  NOP   0; 
NETWORK
TITLE =fill intermediate result with zeros
	  CALL SFC   21 (
		   BVAL					 := #cZero,
		   RET_VAL				  := #iSFC21Return,
		   BLK					  := #aResult);
	  NOP   0; 
NETWORK
TITLE =fill stripped result with zeros
	  CALL SFC   21 (
		   BVAL					 := #cZero,
		   RET_VAL				  := #iSFC21Return,
		   BLK					  := #aStrippedResult);
	  NOP   0; 
NETWORK
TITLE =shift decimal point over
	  L	 #iExponent; 
	  L	 0; 
	  ==I   ; 
	  JC	npee; 
	  A	 #bExponentSign; 
	  JCN   npee; 
	  LAR1  P##aFloat; 
	  LAR2  P##aResult; 
	  L	 B [AR1,P#0.0]; //copy sign
	  T	 B [AR2,P#0.0]; 
	  +AR1  P#1.0; 
	  +AR2  P#1.0; 
	  L	 B [AR1,P#0.0]; //copy first digit
	  T	 B [AR2,P#0.0]; 
	  +AR1  P#2.0; //skip past decimal point
	  +AR2  P#1.0; 

	  L	 26; 
Lop:  T	 #iLoopCount; 
	  L	 #iExponent; 
	  L	 0; 
	  ==I   ; 
	  JCN   nopo; 
	  L	 '.'; 
	  T	 B [AR2,P#0.0]; 
	  +AR2  P#1.0; 
nopo: L	 #iExponent; 
	  +	 -1; 
	  T	 #iExponent; 
	  L	 B [AR1,P#0.0]; //copy digit
	  T	 B [AR2,P#0.0]; 
	  +AR1  P#1.0; 
	  +AR2  P#1.0; 
	  L	 #iLoopCount; 
	  LOOP  Lop; 
npee: NOP   0; 
//	  deal with non positve exponent here
NETWORK
TITLE =strip off leading zeros
	  LAR1  P##aResult; 
	  LAR2  P##aStrippedResult; 
	  L	 B [AR1,P#0.0]; //copy sign
	  T	 B [AR2,P#0.0]; 
	  L	 27; 
lopx: T	 #iLoopCount; 
	  L	 B [AR1,P#0.0]; 
	  L	 '+'; 
	  ==I   ; 
	  =	 #bIsAplus; 
	  TAK   ; 
	  L	 '-'; 
	  ==I   ; 
	  =	 #bIsAMinus; 
	  L	 B [AR1,P#2.0]; 
	  A(	; 
	  L	 '0'; 
	  >I	; 
	  )	 ; 
	  A(	; 
	  TAK   ; 
	  L	 '9'; 
	  <=I   ; 
	  )	 ; 
	  =	 #bIsADigit; 
	  L	 B [AR1,P#1.0]; 
	  L	 '0'; 
	  ==I   ; 
	  =	 #bIsAZero; 
	  A(	; 
	  A	 #bIsAplus; 
	  O	 #bIsAMinus; 
	  )	 ; 
	  A	 #bIsADigit; 
	  A	 #bIsAZero; 
	  JCN   nost; 
	  +AR1  P#2.0; //skip past leading zero
	  +AR2  P#1.0; 
	  JU	more; 
nost: L	 B [AR1,P#0.0]; //copy digit
	  T	 B [AR2,P#0.0]; 
	  +AR1  P#1.0; 
	  +AR2  P#1.0; 
more: L	 #iLoopCount; 
	  LOOP  lopx; 
	  NOP   0; 
NETWORK
TITLE =block copy result to DB for monitoring
	  CALL SFC   20 (
		   SRCBLK				   := #aStrippedResult,
		   RET_VAL				  := #iSFC20Return,
		   DSTBLK				   := DB2.aChar);
	  NOP   0; 
END_FUNCTION
 
Thank you guys for your quick response.
It's really amazing.

CarlesM: I have Step7 Pro and working with SCL, but i could not find a possibility for adding formatted variables to a text string.

S7Guy: The char string will be send to a PC and must be readable with hyper terminal in ASCII format. The data really need to be converted.

L D[AR2,P#0.0]: Thanks for the code. i'm starting directly working on it. Let you know if it works.
 
In the online help for SCL, go and find Standard Functions of S7-SCL .. Functions for processing strings ..
There is a host of manipulating and conversion functions.
You probably have to use REAL_TO_STRING and CONCAT to add a floating point value to a string.

Edit: From the looks of what you want to do, and what SCL can do, I would say that it is very easy to implement with SCL.
 
Last edited:
I see that you want to start the string with date and time.

You need to retrieve the CPU date and time. You set and read the CPU clock with SFC0 SET_CLK and SFC1 READ_CLK.
The value will be returned as a DATE_AND_TIME formatted complex data type.
You have to retrieve the parts you need (year, month, day, hour, minute, second, millisecond), and place each part in its own address.
Here is the meaning of each byte of the DATE_AND_TIME format:

Byte, meaning, example:
0 Year B#16#93
1 Month B#16#12
2 Day B#16#25
3 Hour B#16#08
4 Minute B#16#12
5 Second B#16#34
6 Two most significant digits of MSEC B#16#56
7(4MSB)Two least significant digits of MSEC B#16#
7(4LSB)Day of week
1 = Sunday
2 = Monday
...
7 = Saturday B#16#_5

You have to convert from BCD to INT (BTI instruction in STL) and then store the result in individual addresses. And with INTs you can easily convert to Strings.
All of the above is best done in STL.

In SCL you can then easily pick each of the components and put them in the string (after proper conversion).
 
Last edited:
Jesper:
Unfortunately is it not as easy as you think it is.
The REAL_TO_STRING conversion will result in a value with an exponent.
A real value of 2.3467 will be converted to "+0.2346700E+01". Since i need a value like "+2.35" there is still some work to do.
Right now i'm looking if i can use the code from "L D[AR2,P#0.0]", but still looking forward having a sulution in SCL.

Thanks anyway.
 
There was a discussion recently about converting REALs to INTs in the format "xx.yy" like you want it. Try to locate it. It was relatively easy as far as I remember.
 
Here is the thread:
http://www.plctalk.net/qanda/showthread.php?t=28900

From that, you should be able to do like this:

//load REAL value x.y
L DB1.DBD0
L 100.0
*R
RND
L 100
/I
T DB1.DBW4 //xx part
SRD 16
T DB1.DBW6 //yy part

I havent tested the above code.
The real value must not be greater than +327.67 or less than -327.68.
You may have to modify the code if you have values outside this.
 
Last edited:
Jesper: The thread you gave me is more about that you didn't know about there is no rounding up with integer division ;-). Nothing about reals!!
 
No it isnt.
The thread is about how to split a value like 123456 representing 1234.56 into two values 1234 and 56.

Therefore:
Multiply your REAL with 100.
Convert to DINT
Divide by 100.
Place quotient and remainder in separate addresses.
That is what the STL code does.

edit: It is true that it the previous thread does not talk about how to convert from REAL to INT, but that is the least part of it.
 
Last edited:
Attached is the completed sprintf function as source code. If you insert the source file into a new project containing FC30 (R_STRNG) and FC38 (STRNG_I) from the Standard Library, IEC functions, you can compile the source code. It will produce DB1, FC1, FC2 and OB1 which you download to the simulator. The resultant string is created in DB1 with tab delimiters.
 
L D[AR2 said:
Attached is the completed sprintf function as source code. If you insert the source file into a new project containing FC30 (R_STRNG) and FC38 (STRNG_I) from the Standard Library, IEC functions, you can compile the source code. It will produce DB1, FC1, FC2 and OB1 which you download to the simulator. The resultant string is created in DB1 with tab delimiters.

Thank you very much for the code. Looking at your posts i saw you bring in a lot of solutions over here. You must have a lot of spare time, or you are very fast, or a day in the UK has more then 24 hours. Tommorow i will do some tests with the code and let you know my results.
 

Similar Topics

I have a client who periodically experiences network communication issues. Sometimes when I VPN into the site, their SCADA systems will flash comm...
Replies
2
Views
169
We have been using a "koyo" dl06 and communicating to it well over Modbus TCP, we read inputs and set outputs at a high rate, 4-10 times a second...
Replies
7
Views
643
Hi all, I am working on a project that needs to detect when a user is putting in effort into the system (this is a robotic gait trainer that...
Replies
49
Views
8,897
General Question: My trial version of TIA Portal v16 expires in 5 days and I'll be buying the full licence (Basic). I spoke to a chap at Siemens...
Replies
5
Views
1,298
Hi, I have recently gone full in on TIA after holding on to Simatic Manager. One thing which I find more difficult is the filter function for...
Replies
6
Views
1,725
Back
Top Bottom