Help - Search - Members - Calendar
Full Version: HELP!
Forums.MrPLC.com > PLCs and Supporting Devices > Allen Bradley
John Croson
I am not a PLC person, but I am a VBA person. I can create a hot link in Excel to an SLC503+ and see my data. But if I try to use the following code, I get a Type Mismatch error. This usually tells me that I'm using an invalid variable type to hold the data, but I'm using a Variant type!!


'assign PLC bit values to VB variant varibles
varDump = DDERequest(RSIChan, "C5:2.ACC")

This space is supposed to hold a number from 0 to 25. If I do a Copy DDE link from RSLinx (Single Node), I get this: [ENDRES]C5:2.ACC,L1,C1

Can anyone tell me why I get this error?

Thanks!
TechJunki
I do not see any problem with that line, I use similar lines of code all the time. Have you tried getting a different address such as an integer ex N7:0 . Are you initiating the DDE link first, what other lines of code do you have before this line?? Might need some more info.

JJ
John Croson
Thanks for the reply. Here is the top of the sub:

Dim lngRow As Long
Dim varDump As Variant
Dim sngWeight As Single
Dim varResults As Variant
Dim intVar As Integer, intCount As Integer, _
intData As Integer, strData As String
On Error GoTo Error


'opens a COLD DDE link
RSIChan = DDEInitiate("RSLINX", "ENDRES")

'assign PLC bit values to VB variant varibles
varDump = DDERequest(RSIChan, "C5:2.ACC")

'close COLD DDE link
DDETerminate (RSIChan)

'check to see if anything has been dumped
If varDump > 0 Then

Thanks again!
Chris Elston
Boy....I am confused too. I am like teckjunkie, that should work.

Do you have a DDE topic created in RS Linx Single Node?

That topic name needs to be "ENDRES" based on your code.

See attached JPG.
John Croson
Thanks, but I do have a topic set up.
Chris Elston
I guess I have to agree with TechJunki. Have you tried using another address? Like N7:0?

Address C5:2 should be a default address that should be accessable in the SLC memory without having to create that address.

It does seem odd that you can create a "HOT" topic to that address.

I cruised AB's KB, and found this tech doc. It kind of points in the direction of creating your own data type. I've never done that before but see if you can use this code snipplet.

http://domino.automation.rockwell.com/appl...D8?OpenDocument

Or click Here

Actually they don't even DIM the "data" varible, which I believe is suppose to make "data" a Variant anyway if you don't DIM it if I recall my VB rules... but it looks like they are trying to detrimine the data type in the next line of code with the mytype varible.

John Croson
COOL! Thanks for the pointer. Bad form using Variants, but so be it...

The Example:


CODE

Sub Word_Read()

     Dim mytype As String
     Dim test As Integer
     Dim junk As Tag

     Set junk = gTagDb.GetTag("analog")

     'open DDE link: testsol=DDE Topic
     RSIchan = DDEInitiate("RSLinx", "testsol")

     'get data and store in data variable
     data = DDERequest(RSIchan, "N7:0")

     'shows the variable type for data
     'use the TypeName function and the Locals Window to determine
     'the data type for the variable 'data' to determine it is an array.
     'the line is not necessary for the script to run.
     mytype = TypeName(data)

     'store the value from data into variable and tag
     test = data(1)
     junk.Value = data(1)

     'close dde link
     DDETerminate (RSIchan)

     'release memory for tag
     Set junk = Nothing

End Sub



The thing I can't understand is why I'm setting the junk variable.

Is this simply used for a test?

I am using the example this way:


CODE

Sub Start()

Dim lngRow As Long
Dim varResults As Variant
Dim strTestType As String
Dim intDump As Integer
Dim junk As Tag
Dim intVar As Integer, intCount As Integer, _
   intData As Integer, strData As String
On Error GoTo Error
   
Set junk = gTagDb.GetTag("test")


   'opens a COLD DDE link
   RSIChan = DDEInitiate("RSLINX", "ENDRES")
   
   'assign PLC bit values to VB variant varibles
   dumpqty = DDERequest(RSIChan, "C5:2.ACC")
   
   
'shows the variable type for data
'use the TypeName function and the Locals Window to determine
'the data type for the variable 'data' to determine it is an array.
'the line is not necessary for the script to run.
   strTestType = TypeName(dumpqty)
   
   ' Convert our variable into something usable
   intDump = dumpqty(1)
   junk.Value = dumpqty(1)
   
   'close COLD DDE link
   DDETerminate (RSIChan)
   
   'check to see if anything has been dumped
   If intDump > 0 Then

For intCount = 0 To intDump

       'starts at row 3 of sheet
       lngRow = 3
       
       If Range("INDATA!A3").Value > 3 Then
           'look up last cell and change position
           lngRow = Range("INDATA!A3").Value
       End If
       
       'check until end of sheet
       For lngRow = lngRow To 65500
           'look for next empty cell
           If Cells(lngRow, 1) = "" Then Exit For
           
           'write current cell location to sheet INDATA
           'rather than writing a loop to search on
           'every cycle, by the time the log is at row 21,500
           'it could take a long time to search the rows...
           Range("INDATA!A3").Value = lngRow + 1
                       
       'add 1 to row "x" to check next row
       Next
         
       'opens a COLD DDE link
       RSIChan = DDEInitiate("RSLINX", "ENDRES")
       
       'there might be a better way to do this like
       'using this somehow ???????? but I don't know how
       'data = DDERequest(RSIchan, "f11:0,L7,C7")
       'Range("[ENDRES.xls]LOG!R[x]C1:R[x]C7").Value = data
                                                       
    For intCount = 0 To intDump
   
    ' Fill in the TYPE Column
       intData = DDERequest(RSIChan, "N24:" & intVar)
           Select Case intData
           Case Is = 1
               strData = "BREAD"
           Case Is = 2
               strData = "PRETZEL"
       End Select
       Cells(lngRow, 1).Value = strData
       'increment our var
       intVar = intVar + 1
       'zero our data
       intData = 0
       
   ' Fill in the Room Column
       intData = DDERequest(RSIChan, "N24:" & intVar)
           Select Case intData
               Case Is = 1
           strData = "Mixing Room"
               Case Is = 2
           strData = "Package Room"
               Case Is = 3
           strData = "Oven Room"
       End Select
       Cells(lngRow, 2).Value = strData
       'increment our var
       intVar = intVar + 1
       'zero our data
       intData = 0
       
       
   ' Fill in the Line
       intData = DDERequest(RSIChan, "N24:" & intVar)
           Select Case intData
           Case Is = 5
               strData = "Other"
           Case Else
               strData = intData
       End Select
       Cells(lngRow, 3).Value = strData
       'increment our var
       intVar = intVar + 1
       'zero our data
       intData = 0
       
       
   ' Fill in the Result Of Column
       intData = DDERequest(RSIChan, "N24:" & intVar)
           Select Case intData
           Case Is = 1
               strData = "From Startup"
           Case Is = 2
               strData = "From Shutdown"
           Case Is = 3
               strData = "Normal Production"
           Case Is = 4
               strData = "From R&D"
           Case Is = 5
               strData = "From Change Over"
           Case Is = 6
               strData = "Other"
       End Select
       Cells(lngRow, 4).Value = strData
       'increment our var
       intVar = intVar + 1
       'zero our data
       intData = 0
   
   
   
    Next intCount
       
     
       'close COLD DDE link
       DDETerminate (RSIChan)


The installer of the panel supplied these instructions:

Word 1 ( N24:0 ) = TOTE WEIGHT
Word 2 ( N24:1 ) = PRODUCT TYPE, 1=bread / 2=pretzel
Word 3 ( N24:2 ) = ROOM, 1=mixing / 2=packaging / 3=oven
Word 4 ( N24:3 ) = LINE, 1,2,3,4,5
Word 5 ( N24:4 ) = RESULT OF, 1=startup / 2=shutdown / 3=normal 4=r&d / 5=change over / 6=other


Then it starts over, counting Word 1 from N24:5, the weight.

The rest of the code has been snipped, but I just want to check for an accumulated value in C5:2.ACC, and if it is more than 0, grab all the data in the "array" and push it into my spreadsheet.

It seems that I have to do a var check for all of the PLC types of data?

Thanks for looking!
Chris Elston
What happens now with your example that you snagged from AB? Do you still get a TYPE MIS-MATCH even now without DIMing your variable?

PS: Glad people are using my example, looks like you've got some of my orginal code I did when I first tackled this DDE and Excel stuff with this download:

http://forums.mrplc.com/index.php?act=Downl...&CODE=02&id=134


Going back to your orginal code, try this:

Putting an L1, meaning read one word then a carriage return after the first word.

CODE

Dim lngRow As Long
Dim sngWeight As Single
Dim varResults As Variant
Dim intVar As Integer, intCount As Integer, _
intData As Integer, strData As String
On Error GoTo Error


'opens a COLD DDE link
RSIChan = DDEInitiate("RSLINX", "ENDRES")

'assign PLC bit values to VB variant varibles
varDump = DDERequest(RSIChan, "C5:2.ACC,L1,C1")

'close COLD DDE link
DDETerminate (RSIChan)

'check to see if anything has been dumped
If varDump > 0 Then



I think what the problem is, is that that counter is considered an array when taking about DDE much like a timer value. Like C5:2.DN, C5:2.EN,C5:2.PRE,C5:2.ACC...so we gotta handle that array of data somehow....

What would be EASIER at this point is to get your PLC guru to copy C5:2.ACC into a N7:xx memory location. N7:xx memory location will not be an array of data that you need to deal with and you can move on with your project.




Here is a list of all those stupid things you don't know about DDE and data type formatting.

BTY..... Take note to the "X" identifier...

Regardless of the client application used with RSLinx, the address of the DDE item specified must be of the following form:

[address]<,L##><,C##><R##>{,N}{,D}{,E}{,DH}{,EH}(,T)(,B)(,W)(,X)(,M) (,SA##)(,SC##)(,SP##)(,SS##)(,SA##)

where:

[ ]
indicates a required field (address) that is suitable for the PLC type specified within the topic.

< >
indicates an optional block read or block formatting command.

L##
indicates the total length (block size), or number of items to be read.

C##
indicates the number of columns in which to format the data. This is for display purposes in the client application.

R##
indicates the number or rows in which to format the data. This is for display purposes in the client application.

##
indicates a numeric value that must be placed after the optional specifier.


(Specifying C## without a corresponding L## specifier has no effect. However, specifying an L## without a C## results in a display as if L##,C1.)

{ }
indicates an optional data type specifier that is only used with a PLC-2. If none of these specifiers is used then the default type is N, 16-bit signed integer.

M:
used for specifying a Motorola byte ordering, which causes the conversion routines to swap bytes before interpreting the data. This was mainly intended to fix a problem when reading strings with unsolicited messages.

N:
16-bit signed integer

D:
12-bit, 3-digit BCD number

E:
16-bit, 4-digit BCD number

DH:
12-bit, 3-digit BCD number, displays the hexadecimal value

EH:
16-bit, 4-digit BCD number, displays the hexadecimal value

(T)
indicates a special specifier used only with unsolicited messages when the source of the data table is a PLC-5 timer accumulated value and the destination address is something other than another timer accumulated value. In most situations this specifier is not needed.

(B)
indicates a special specifier used only with the Allen-Bradley PLC data types that are 16-bits. This specifier causes the data to be displayed as a string of 1's and 0's instead of an unsigned integer.

(W)
indicates a special specifier used only with the Allen-Bradley PLC data types. This specifier is used when specifying an array of bits. It causes data to be read from consecutive elements. For example in a structured data type (timers, counters, etc.), T4:0.DN,L10,C1,W reads T4:0.DN, T4:1.DN, T4:2.DN, etc. For a non-structured type (binary, integer, etc.), B3/0,L10,C1,W reads B3:0/1, B3:1/1, B3:2/1, etc.

(X)
indicates a special specifier used only with the Allen-Bradley PLC data types. This specifier is used when specifying an array of bits. It causes data to be read from consecutive bits. For example in a structured data type (timers, counters, etc.), T4:0.DN,L3,C1,X reads T4:0.DN, T4:0.TT, T4:0.EN. For a non-structured type (binary, integer, etc.), B3/0,L10,C1,X reads B3:0/1, B3:0/2, B3:0/3, etc.

(SA##)
sends all updates to the client RSLinx receives even if they have not changed.

(SC##)
indicates a special specifier used only with the Allen-Bradley Integer (N) or ASCII (A) PLC data types for C style strings.

(SP##)
indicates a special specifier used only with the Allen-Bradley Integer (N) or ASCII (A) PLC data types for Pascal style strings.

(SS##)
indicates a special specifier used only with the Allen-Bradley Integer (N) or ASCII (A) PLC data types for space-padded strings.
John Croson
Thanks! Yes, I did hack up your original example, and AB's. I'm more of a vb guy, never having grabbed data from a PLC before, so it threw me off, not declaring variables, and then passing the var's into int's, etc.

The only issue I'm having now is DDEPoke into B3/100 to reset the array. It doesn't seem to work.

My code:

CODE
Sub DDE_Write()
RSIChan = DDEInitiate("RSLINX", "ENDRES")

'write data thru channel

DDEPoke RSIChan, "B3:100", Range("OUTDATA!A11").Value
DDEPoke RSIChan, "B3:100", Range("OUTDATA!A10").Value


'close dde channel
DDETerminate (RSIChan)


End Sub


Thanks!
TechJunki
Try substituting in the following code, substituting Book1 and Sheet1 with the correct names in your project.

You do also realize that you are writing to the same address twice B3:100, are you possibly canceling out the data you are sending here??

CODE

Sub DDE_Write()
RSIChan = DDEInitiate("RSLINX", "ENDRES")

'write data thru channel

DDEPoke RSIchan, "B3:100", Range("[Book1.XLS]Sheet1!A11")
DDEPoke RSIchan, "B3:100", Range("[Book1.XLS]Sheet1!A10")


'close dde channel
DDETerminate (RSIChan)


End Sub
Chris Elston
Since I don't know what you got in your spreadsheet at:

OUTDATA!A11

You have two different things going on there.

B3/100 is a "BIT" in the PLC. You would only be able to write a "0" or a "1" to that address and would format your code like:

DDEPoke RSIChan, "B3/100", Range("OUTDATA!A11").Value


B3:100 is a whole "WORD", or 16 bits. You could write more than just a "0" or "1" to that word.

Which one are you trying to do?

John Croson
Sorry for my incomplete post!

This cell contains a "1"

DDEPoke RSIChan, "B3:100", Range("OUTDATA!A11").Value



This cell contains a "0"

DDEPoke RSIChan, "B3:100", Range("OUTDATA!A10").Value

I was told that if I set the bit in this b3/100 to 1 and then back to 0, it would reset my n24 integer bits.

Thanks again! You guys are great!
OkiePC
You're not giving the PLC enough time to recognize the setting of the bit if you immediately turn it back off. The PLC scan time is going to be at least 10msec and the communication cycle rate is a factor as well. I would just set the bit, and let the PLC turn it back off after it has reset the array counter.

Your format will write a "1" to the 16 bit word B3:100. It will clear 15 bits and set bit 0. You could just write to a single bit using the / delimiter "B3:100/0" B3/100 IS NOT IN THE SAME WORD AS B3:100! B3/100 IS THE SAME AS B3:6/4. Using the "/" delimiter like this "B3/100" refers to the 100th bit in the FILE B3.

Now, if your VB code sets the CORRECT bit, and lets the PLC RESet it, then you could read the same bit in a DO...LOOP and make sure it is zero as a handshake before ending the routine. (be sure to put in a time limit to escape your loop if comms fail!)

I'm not a VB guy so I don't have examples, but I think my general suggestion will get you where you need to be. Make sure you get the correct bit address and get away from the word reference. If you use the whole word, it will work, but some poor fool will pull all his hair out later when he tries to use one of those other bits in the PLC that won't stay on!
OkiePC
John Croson
This is what I'll end up using. This should set the bit to 1, wait a second, and then set it back to 0. Could I have also just have used something like this?

DDEPoke RSIChan, "B3/100", 1
Pause 1
DDEPoke RSIChan, "B3/100", 0

Should I have those number in quotes?

CODE

Sub DDE_Write()
Dim intSet As Integer
RSIChan = DDEInitiate("RSLINX", "ENDRES")
'write data thru channel
'set the bit to 1
DDEPoke RSIChan, "B3/100", Range("OUTDATA!A11").Value
Pause 1
'and set it back to 0
DDEPoke RSIChan, "B3/100", Range("OUTDATA!A10").Value
'close dde channel
DDETerminate (RSIChan)
End Sub



Public Sub Pause(ByVal pSng_Secs As Single)
'Wait for the number of seconds given by pSng_Secs
Dim lSng_Start As Single
Dim lSng_End As Single
On Error GoTo Err_Pause
   lSng_Start = Timer
   lSng_End = Timer + pSng_Secs
   Do While Timer < lSng_End
   '' Correction if the timer moves over to a new day (midnight)
   '' 86400-num of secs in a day
       If Timer < lSng_Start Then lSng_End = lSng_End - 86400
   Loop
Err_Pause:
   Exit Sub
   
End Sub


Thanks.
OkiePC
No, your constants should be outside of the quotation marks, but I'm not sure if the DDEPoke will write to a single bit or to a whole word. Be sure that the next sixteen bits (b3/100 - b3/115) are not used in the PLC before you try this. The one second delay should be plenty of time, but may need to be increased if using DH485 with a lot of traffic. I still think it would be more bullet proof if you let the PLC unlatch (reset) the bit, and then in your VB code make sure it's zero before you examine the accumulator to read another batch of data. Modifying the SLC code online is really safe and painless and won't even require the process to be stopped. I wish I had more experience so I could tell you exactly what will happen bit by bit...I'll look on ab.com and see what's been written on the subject.
OkiePC
I did some experimenting of my own and found that it would not work with literals or local variables. I found that I had to use a cell reference to get the data to change in the PLC. I found that if your PLC address refers to a single bit, it will be set to a "1" if the cell reference is non-zero. It does not disturb adjacent bits.
This is my oversimplified example that did work:

Sub Word_Write()

'open dde link: testsol=DDE Topic

RSIchan = DDEInitiate("RSLinx", "BS6")

'write data thru channel

DDEPoke RSIchan, "B14:4/0", Range("Sheet1!A8")

'close dde channel

DDETerminate (RSIchan)

End Sub


Hope this helps.
John Croson
THANKS! I'm trying it myself, now.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2010 Invision Power Services, Inc.