This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

RS485 signal collision
#1
Hi !
I appreciate if you could give me any idea because I cannnot come up with a good one.

I use LM RS485 port to communicate with the slave and the following is what I do:
- [event script] send the request to the slave to open/close the water valve
- [event script] send the request to the slave to open/close the drain valve
- [resident script] send a read request to the slave to get the slave's current status every 1 minute

The complicated thing is that I need to leave 0.5sec in between each every command.
Is there a way to wait 0.5 sec between each event script and the resident script?

Thank you for your help in advance!
Reply
#2
Everything should work in single resident script. You can have only one open connection at the time.
------------------------------
Ctrl+F5
Reply
#3
Thank you Daniel!

I am wondering how to open/close whenever I want in a single resident script... does that mean I call a resident script from a event script ?
(In my case, the group address 14/2/0 is for opening/closing the water valve, 14/2/1 for opening/closing the drain valve)
Reply
#4
You can set this script to run with 0 interval only make sure everything is on change of values. No need for event script.
------------------------------
Ctrl+F5
Reply
#5
Oh I get it !

So I create a resident script with 0 interval and listen to the group address event for 14/2/0 , 14/2/1
I will try this way, thank you Daniel (: !
Reply
#6
Dear Daniel 

I tried this in a resident script with 0 interval like this but it seems this script does not get executed every second...
When I remove the last line "res, err = port:read(17)" it gets executed every second.
Could you tell me if I am doing anything wrong?

Code:
if not port then
  require('serial')
  port =  serial.open('/dev/RS485-1', {
  baudrate = 19200,
  databits = 8,
  stopbits = 1,
  parity = 'none',
  duplex = 'half'
})
end

log('executed')


if storage.get('coldBathAutoStartCommand') ~= nil  then
port:write(storage.get('coldBathAutoStartCommand'))
os.sleep(1)
port:write(':RR\r\n')
storage.set('coldBathAutoStartCommand', nil)
end


port:flush()
res, err = port:read(17)
Reply
#7
port:read(17) is a blocking read. It won't return until 17 bytes are read.
For one-way communication you can use UDP for data transport between scripts:
Code:
if not server then
  require('socket')
  server = socket.udp()
  server:setsockname('127.0.0.1', 5485)
  server:settimeout(60)
end

if not port then
  require('serial')
  port = serial.open('/dev/RS485-1', {
    baudrate = 19200,
    databits = 8,
    stopbits = 1,
    parity = 'none',
    duplex = 'half'
  })
end

data = server:receive()
if data then
  port:write(data)
  os.sleep(1)
  port:write(':RR\r\n')
end

Send commands like this:
Code:
data = '123456'

sock = require('socket').udp()
sock:sendto(data, '127.0.0.1', 5485)
sock:close()
Reply
#8
Thank you Admin!

So I write sock : sendto(data, '127.0.0.1', 5485) in the event script and receive them in resident script.

port:write(':RR\r\n') is a command to request the slave to give LM the status so it is not one-way communication....can I include "port:read(17)" in the same resident script?
Reply
#9
Then you will need something like this. It will request the status after each write or socket read timeout so it can happen faster than once every 60 seconds. But I don't think that it will be an issue.
Code:
if not server then
  require('socket')
  server = socket.udp()
  server:setsockname('127.0.0.1', 5485)
  server:settimeout(60)
end

if not port then
  require('serial')
  port = serial.open('/dev/RS485-1', {
    baudrate = 19200,
    databits = 8,
    stopbits = 1,
    parity = 'none',
    duplex = 'half'
  })
end

data = server:receive()
if data then
  port:write(data)
  os.sleep(0.5)
end

port:write(':RR\r\n')
data = port:read(17, 1) -- read 17 bytes for up to 1 second
if data and #data == 17 then
  -- parse data response here
end
Reply
#10
Dear admin
I really appreciate your solution !
This looks so beautiful Smile
I will try this in the site Smile
Reply
#11
Dear admin,

I am running the code in the site, and I want to add 2 features....could you tell me how I do these?

1. Is there a way to execute port:write(':RR\r\n') every 60 sec even if port:read(17) does not return anything? As you said before, port:read(17) is blocking the script. If there's no response from the slave, the script is not going to be executed anymore  Sad

2. I want to alart the system admin if there's no response from the slave(port:read(17)) for 5 min....can I do that in this script?

Code:
if not server then
  require('socket')
  server = socket.udp()
  server:setsockname('127.0.0.1', 5485)
  server:settimeout(60)
end

if not port then
  require('serial')
  port = serial.open('/dev/RS485-1', {
    baudrate = 19200,
    databits = 8,
    stopbits = 1,
    parity = 'none',
    duplex = 'half'
  })
end

data = server:receive()
if data then
  port:write(data)
  os.sleep(0.5)
end

port:flush()
port:write(':RR\r\n')
data, err = port:read(17, 1) -- read 17 bytes for up to 1 second
if data and #data == 17 then
  -- parse data response here
end
Reply
#12
Do you have a description of the protocol? Is the response fixed to 17 chars or can it be different? Apart from the status request does the device send something back when a command is written to it?
Reply
#13
Dear admin,
Thank you for your response!
It's a special protocol that we(the bath system builder and we) made to meet the control requirement for this project.
Response lengh is fixed to 17 ASCII chars for sure, and apart from the status request the device does not send anything back to LM. Smile
Reply
#14
Ok then the script is already more or less correct. Use this to add an alert if 5 timeouts in a row happen.
Code:
if not server then
  require('socket')
  server = socket.udp()
  server:setsockname('127.0.0.1', 5485)
  server:settimeout(60)
end

if not port then
  require('serial')
  port = serial.open('/dev/RS485-1', {
    baudrate = 19200,
    databits = 8,
    stopbits = 1,
    parity = 'none',
    duplex = 'half'
  })

  timeouts = 0
end

data = server:receive()
if data then
  port:write(data)
  os.sleep(0.5)
end

port:flush()
port:write(':RR\r\n')
data, err = port:read(17, 1) -- read 17 bytes for up to 1 second

if data and #data == 17 then
  timeouts = 0
  -- parse data response here
else
  timeouts = timeouts + 1

  if timeouts == 5 then
    -- send alert
  end
end
Reply
#15
Dear admin,
Thank you for providing the code, it's working on the site!

However there was one problem we could not solve on the site.
Even though this script intend to wait at least for 0.5 sec between each command,
when the bath system builder checked their command reception log on the slave side,
it seems that sometimes LM sends commmand with 0 sec in between (between the actual command port:write(data) and the status read port:write(':RR\r\n')).
It does not happen every time, it happened just 3 times in a day.


I just changed the timeout to 20 from 1 but everything else is same with your last script.
Code:
data, err = port:read(17, 20) -- read 17 bytes for up to 20 second


Could you think of any reason why this happen ?

Thank you !
Reply
#16
Try increasing the sleep time after writing a command from 0.5 seconds to a larger value. You can also add a short sleep after a successful status read.
Reply
#17
Thank you admin! We will try both of your suggestion Smile
Reply


Forum Jump: