Sign in to follow this  
Andreschrosa

Modbus Read Slave ID

Recommended Posts

I'm writing a sequence to scan my network switching between IPs and sending modbus function 17 (Report Slave ID) to discover where the equipments are located.

To run modbus function 17 I took some code from the forum, but there you used modbus function 07, it should be diferent to get the parameters I need from 17, like equipment ID.

global string ip

ip = '10.50.8.79'

device.Mod.Address = ip
private string listequip[] = 0

for (private.j = 1, j<=255, j++)


   private string datain
// lock port so regular driver doesn't interfere
if (device.Mod.lockport())
   try
	  // clear buffer
	  device.Mod.purge()
	  // write output string
	  device.Mod.write(chra({i,0x11,0xC0,0x2C}))
	  // read 5 char response
	  datain = device.Mod.read(15) 
	  // confirm valid response
	  if (mid(datain,1,1) == chr(7)) 
		 // put result into channel		 
		 MyChan.AddValue(asc(mid(datain,2,1))) 
		 i++

	  endif
   catch()
   endcatch
   // release port
   device.Mod.unlockport()
endif
endfor

Share this post


Link to post
Share on other sites

global string ip

ip = '10.50.8.79'

device.Mod.Address = ip
private string listequip[] = 0
private i = 0
for (private.j = 1, j<255, j++)


   private string datain
// lock port so regular driver doesn't interfere
if (device.Mod.lockport())
   try
	  // clear buffer
	  device.Mod.purge()
	  // write output string
	  device.Mod.write(chra({j,0x11,0xC0,0x2C}))
	  // read 5 char response
	  datain = device.Mod.read(15) 
	  // confirm valid response
	  if (mid(datain,1,1) == chr(15)) 
		 // put result into channel		 
		 listequip[j] = mid(datain,11,1)+mid(datain,12,1)
		 //MyChan.AddValue(asc(mid(datain,2,1)))		  

	  endif
   catch()
   endcatch
   // release port
   device.Mod.unlockport()
endif
endfor

However, this seens to not be writing anything to listequip...

Share this post


Link to post
Share on other sites

I can't tell you. You'd have to look at the comm monitor to see what the device is returning. My guess is that the second character is not chr(15) as you expect. I would also consider putting a breakpoint in so you can actually see what datain is, or just put a ? statement using the showhidden function:

? showhidden(datain)

Put this right after the device.mod.read(15)

Share this post


Link to post
Share on other sites

somehow when I enter the j variable in the watch window, I see it starts out at some random number (like 62,30,90) then it begins incrementing correctly and goes up to 255 in the 'for'.

datain is not returning anything.

any thoughts?

global string ip

ip = '10.50.8.79'

device.Mod.Address = ip
private string listequip[] = 0
global int j = 0
for (j=1, j<255, j++)

   global datain
// lock port so regular driver doesn't interfere
if (device.Mod.lockport())
   try
	  // clear buffer
	  device.Mod.purge()
	  // write output string
	  device.Mod.write(chra({j,0x11,0xC2,0xEC}))
	  // read 15 char response
	  datain = device.Mod.read(asc(15))	 
	  ? showhidden(datain)

	  // confirm valid response
	  if (mid(datain,1,1) == chr(11))		  
		 // put result into channel		 
		 listequip[j] = concat(mid(datain,11,1),mid(datain,12,1))	  

	  endif
   catch()
   endcatch
   // release port
   device.Mod.unlockport()
endif
endfor

Share this post


Link to post
Share on other sites

Sorry, I didn't look at your code close enough the first time. You can't put a variable inside of {} notation. Only constants. So, you have device.mod.write(chra({j,0x11...}) which is not allowed. You should do: device.mod.write(chr(j) + chra({0x11,0xc2,0xec}))

Share this post


Link to post
Share on other sites

Thanks for your replys, they are helping me get the hang of DAQFactory fast :rolleyes:

I changed some code and now I see some response for function 17, but there is still something wrong.

I'm using mobdubs RTU, by the way.

The response I get from the command/alert window:

051710?\142v\2000018\214\13500H\241\245

the response I get from comm monitor window:

Rx: \x05\x11\x0A?\x8Ev\xC8\x00\x12\xD6\x87\x00H\xF1\xF5

the expected response from the device:

05 11 0A 3F 8E 76 C8 00 12 D6 87 00 48 F1 F5

it's right on almost every bit, except 5 (wich disappears), 11 and 12 (the ones I need).

Any thoughts on how to fix this will be appreciated.

Code:

   j =5
   global string datain

// lock port so regular driver doesn't interfere
if (device.Mod.lockport())
   try
	  // clear buffer
	  device.Mod.purge()
	  // write output string
	  device.Mod.write(chr(j) + chra({0x11,0xC2,0xEC}))
	  // read 15 char response
	  datain = device.Mod.Read(chr(15))
	  ? showhidden(datain)
	  // confirm valid response
	  if (mid(datain,1,1) == chr(11))
		 // put result into channel		 
		 listequip[j] = mid(datain,11,2)
		 ? showhidden(listequip[5])

	  endif
   catch()
   endcatch
   // release port
  device.Mod.unlockport()
endif

Share this post


Link to post
Share on other sites

When working with binary protocols you should select "Display all chars as ASCII Codes" in the monitor window, otherwise, DAQFactory will display chars that are displayable characters (i.e. ascii codes 32 to 127) as the character instead of in \xxx notation. It gets pretty confusing otherwise. Sorry, I know I said use ShowHidden(), but I wasn't really thinking and instead you should either use the monitor or use the asca() function, so:

? asca(datain)

Asca() takes a string and returns an array of ascii codes.

Share this post


Link to post
Share on other sites

That worked but now I come back to read slave ID to finish implementing it to actualy get the serial number, that would be [7][8][9][10] from the response frame, stored in the long format. The function like wrote bellow worked cause I previously used it to get [11][12] from the response, which is equipment type. This only returned a number on [12].

Now I must get then all [7]-[12]; and [7]-[10] must be concatenated like 0012D687 in hexa so that in they represent 1234567 in decimal (for exemple).

However, the way the code is now, I only get the array {0,18,214,135} those are in ASCII of course, but I didnt figure out yet how to do this.

function function17(end)
   global string input
   private string output
   // clear buffer
   device.Mod.purge()
   output = chr(end) + chr(0x11)
   output = output + calcCRC(output)
   ?AscA(output)
   // write output string
   device.Mod.write(output)
   // read 15 char response
   input = device.Mod.Read(15)
   ?AscA(input)
   // confirm valid response
   if (mid(input,1,1) == chra(17))				  
	  private string serial = mid(input,7,4) // + AscA(mid(input,11,1) + mid(input,12,1))	  
	  return (AscA(serial))
   endif

Share this post


Link to post
Share on other sites

I think you want To.ULong() which will take an array of 4 values like {0,18,214,135} and make them into a single unsigned long. There are several other versions that affect byte ordering like To.urwLong() and To.urbLong()

Share this post


Link to post
Share on other sites

In trying to thinker with the to.ulong function, I'm running into a few problems:

if I enter the numbers manually, it works fine, ie. to.ulong({{135,214,18,0}}) it returns 1234567.

However if I set it directly from the function, it dosent, ie. to.ulong({{fun[3,2,1,0]}}) it returns 66051.

function function17(end)
   global string input
   private string output
   // clear buffer
   device.Mod.purge()
   output = chr(end) + chr(0x11)
   output = output + calcCRC(output)
   ?AscA(output)
   // write output string
   device.Mod.write(output)
   // read 15 char response
   input = device.Mod.Read(15)
   // confirm valid response
   if (mid(input,1,1) == chra(17))
	  private string serial = mid(input,7,4)
	  return (asca(serial))
   endif

device.Mod.LockPort()
global string fun = function17(5)
device.Mod.UnlockPort()

:o

Share this post


Link to post
Share on other sites

Hmm, I'm not sure why this always gets people. You can't put non-scalar values inside {}. No functions, no variables. If a function returns an array as yours does, there is no reason to use {} at all. You just do: to.ulong(function17(5)).

Share this post


Link to post
Share on other sites

I cant use it like that, I must get from function17 response array and put bytes [7][8][9][10] to long, and then put bytes [11][12] into other variable. That's why I'm trying to do it like that.

Share this post


Link to post
Share on other sites

But function17 returns an array of 4 bytes. So you have two issues. One, you aren't getting 11 and 12, and two, you are then putting that in a string, "fun", not a numeric variable.

I think you want to change function17 to return asca(mid(input,7,6)) which will return an array of 6 values. Then take the first 4 and use to.ulong. So:

global fun = function17(5)

global var1 = to.ulong(fun[0,3])

global var2 = fun[4,5]

Share this post


Link to post
Share on other sites

I have not been successful trying to communicate with my device.

So, I have been trying to use function 17 to find the device address. Then I read this from the manual:

"The DX100 (M) inherits its Modbus address from the gas detector that it is connected to.

Until the gas detector has its digital address configured, it defaults to an address of 00 which

gives the DX100 (M) a Modbus address of 99. With this default address of 99 the Termination

Unit ignores all attempts to communicate with it.

Thus when initially installed and powered-up the DX100 (M) assumes this default Modbus

address of 99 and ignores all attempts at communication.

The gas detectors digital address must be changed from the default to enable Modbus

communications via the DX100 (M).

The address is configured using the SHC1 Handheld Interrogator. To set the address....."

I thought I could just use the default address of 00, or 99 to talk to my device, but like I said, I haven't been

successfull.

Is there a way to "assign", FORCE, an address to my device using the modbus rtu protocol? Apparently, untill the default address of 00, for my device is changed, it does not allow communication. This appears to be a manufacturer's security thing, I assume. :blink:

Thanks for all your help.

Share this post


Link to post
Share on other sites

Yeah, it sounds like you'll need to adjust the address of the gas detector. Until that is done you are going to get no where. You cannot use address 0 because that is a broadcast address for Modbus. There is nothing in the Modbus spec that allows you to change the ID. Some devices map the ID to a holding register, but you'd still have to be able to communicate with it using the existing ID first. With no valid ID, you can't use Modbus.

Share this post


Link to post
Share on other sites

That explains the address of 0. My Device uses HART PROTOCOL, which alas, DF doesn't provide for. :blink:

I have polled using HART OPC, from the HART people, but it still can't find the device. It's looking like I need

an ,um expensive, "communicator" to talk to a "smart" HART Device. Or else, recalibrations from the factory.(also expensive)..

HART is super-imposed on top of RS-485.

Can you provide any help with talking, using the HART PROTOCOL??

Thanks

Share this post


Link to post
Share on other sites

Yes , and it turns out Hart Protocol requires, nothing new I suppose, yet its own special Modem to talk to the device. One is on order for me.

An RS232 to RS485, converter, wouldn't/won't work, with Hart Protocol.

Hart Protocol appears to be anything but an DOT ORG organization. They are after your wallet , if ever an Hierarchy ever was. :blink:

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this