EmbeddedRelated.com
Forums

Master/slaves network protocol

Started by pozz November 30, 2014
I have a "smart device" SD that is the master on a RS485 master/slaves 
half-duplex network.  The user interact with a "dumb device" DD (slave) 
and he should have the illusion to interact directly with the SD.

All the relevant (for the user) info (status and settings) about the SD 
must be spread among the DDs, through a suitable protocol on the RS485 
network.  Some variables are read-only: they changes autonomously over 
time and can't be changed by the user (for example, a temperature). 
Other variables are read-write and can be changed only by the user (they 
don't change autonomously).

One simple strategy is to send all the current status and settings to 
all the slaves every 1 second.  If the data to be spread are big, the 
number of slaves is high and most of the data changes slowly (as it 
happens in my application), a lot of network bandwith will be wasted to 
send always the same data.
I could alleviate this problem by sending data through a broadcast 
message on the network, but I don't like it.  The master should be sure 
if a slave is present and has well received the status, such that it 
could generate an alarm if a slave is failed and can't communicate on 
the network.  So the master must poll each slave and waits for an answer 
from it (this can't be done with broadcast messages).

Another strategy is to send by difference: only if one piece of info (a 
variable, a byte or a group of variables) changes, the master sends all 
the data to each slave.
The problem with this technique is the amount of memory needed in the 
master (it could be a small 8-bit microcontroller with modest size of 
RAM).  It should allocate a data buffer for *each* slave with the last 
values sent to it over the network.  Every 1 second, a function compare 
the actual values against the last sent values for each slave and send 
new values if they differ.
If the data is 100 bytes and slaves are 10, the master needs 1KB of RAM 
only for that.
This strategy could be changed to decrease the network bandwith needed 
to refresh the new status.  If only one variable is changed, only that 
(or some other related variables) can be sent over the network.

Another strategy is similar to the previous, but saves a lot of memory: 
the master set only a flag that triggers a status refresh to all the 
slaves.  The network protocol task routine checks for this flag and if 
it is set, sends current values to all the slaves.
The problem with this strategy is in the "core" code that manages the 
status variables.  Every time in the code a variable is written in the 
RAM, a comparison with the old value must be made and, if there's a 
difference, the refresh flag must be set.  I could try to change the 
"core" code in order to manage the refresh flag for the network, but I 
don't like it for several reasons.
The "core" code is already written and tested.
In the future, I'm sure I'll forget to correctly manage the refresh flag.
The "core" code would be strongly coupled with other completely 
different modules (network protocol).
The "core" code would be more dirty with network refresh flag 
management: every time I have to set a variable

   status.temperature = sensor_read_temperature();

I need to make a comparison with the old value:

   new_temperature = sensor_read_temperature();
   if (new_temperature != status.temperature) {
     status.temperature = new_temperature;
     network.refresh_flag = 1;
   }

One possibility to alleviate those problems is to create an intermediate 
module that manages the status.  This module provides getter and setter 
functions.

   status_temperature_set(sensor_read_temperature());

   void status_temperature_set(int new_temperature) {
     if (status.temperature != new_temperature) {
       status.temperature = new_temperature;
       network.refresh_flag = 1;
     }
   }

Even this strategy has some inefficiencies.  The module should manage 
the status that is relevant for the slaves (and for the protocol).  The 
"core" code should call status module setters for some variables and 
should use normal assignment for other variables (that aren't related to 
network protocol).  In this case too, the "core" code is coupled with 
another, completely logically different, module.


Do you have other better strategies to propose?
pozz wrote:

> I have a "smart device" SD that is the master on a RS485 master/slaves > half-duplex network. The user interact with a "dumb device" DD (slave) > and he should have the illusion to interact directly with the SD. > > All the relevant (for the user) info (status and settings) about the SD > must be spread among the DDs, through a suitable protocol on the RS485 > network. Some variables are read-only: they changes autonomously over > time and can't be changed by the user (for example, a temperature). > Other variables are read-write and can be changed only by the user (they > don't change autonomously). > > One simple strategy is to send all the current status and settings to > all the slaves every 1 second. If the data to be spread are big, the > number of slaves is high and most of the data changes slowly (as it > happens in my application), a lot of network bandwith will be wasted to > send always the same data. > I could alleviate this problem by sending data through a broadcast > message on the network, but I don't like it. The master should be sure > if a slave is present and has well received the status, such that it > could generate an alarm if a slave is failed and can't communicate on > the network. So the master must poll each slave and waits for an answer > from it (this can't be done with broadcast messages). > > Another strategy is to send by difference: only if one piece of info (a > variable, a byte or a group of variables) changes, the master sends all > the data to each slave. > The problem with this technique is the amount of memory needed in the > master (it could be a small 8-bit microcontroller with modest size of > RAM). It should allocate a data buffer for *each* slave with the last > values sent to it over the network. Every 1 second, a function compare > the actual values against the last sent values for each slave and send > new values if they differ. > If the data is 100 bytes and slaves are 10, the master needs 1KB of RAM > only for that. > This strategy could be changed to decrease the network bandwith needed > to refresh the new status. If only one variable is changed, only that > (or some other related variables) can be sent over the network. > > Another strategy is similar to the previous, but saves a lot of memory: > the master set only a flag that triggers a status refresh to all the > slaves. The network protocol task routine checks for this flag and if > it is set, sends current values to all the slaves. > The problem with this strategy is in the "core" code that manages the > status variables. Every time in the code a variable is written in the > RAM, a comparison with the old value must be made and, if there's a > difference, the refresh flag must be set. I could try to change the > "core" code in order to manage the refresh flag for the network, but I > don't like it for several reasons. > The "core" code is already written and tested. > In the future, I'm sure I'll forget to correctly manage the refresh flag. > The "core" code would be strongly coupled with other completely > different modules (network protocol). > The "core" code would be more dirty with network refresh flag > management: every time I have to set a variable > > status.temperature = sensor_read_temperature(); > > I need to make a comparison with the old value: > > new_temperature = sensor_read_temperature(); > if (new_temperature != status.temperature) { > status.temperature = new_temperature; > network.refresh_flag = 1; > } > > One possibility to alleviate those problems is to create an intermediate > module that manages the status. This module provides getter and setter > functions. > > status_temperature_set(sensor_read_temperature()); > > void status_temperature_set(int new_temperature) { > if (status.temperature != new_temperature) { > status.temperature = new_temperature; > network.refresh_flag = 1; > } > } > > Even this strategy has some inefficiencies. The module should manage > the status that is relevant for the slaves (and for the protocol). The > "core" code should call status module setters for some variables and > should use normal assignment for other variables (that aren't related to > network protocol). In this case too, the "core" code is coupled with > another, completely logically different, module. > > > Do you have other better strategies to propose?
There are plenty of strategies but which will be most suitable for you rather depends on the end use of your system. Is this a commercial product or just something you are playing with? There are things you could do to help yourself, without busting your available memory or comms bandwidth budget, but end use of the information is something I would want to know before I suggest anything as there may be Safety Integrity Implications that you have not yet stated anything about. To make assumptions otherwise would be wrong. -- ******************************************************************** Paul E. Bennett IEng MIET.....<email://Paul_E.Bennett@topmail.co.uk> Forth based HIDECS Consultancy.............<http://www.hidecs.co.uk> Mob: +44 (0)7811-639972 Tel: +44 (0)1235-510979 Going Forth Safely ..... EBA. www.electric-boat-association.org.uk.. ********************************************************************
On Sun, 30 Nov 2014 16:27:37 +0100, pozz <pozzugno@gmail.com> wrote:

>I have a "smart device" SD that is the master on a RS485 master/slaves >half-duplex network.
In any half duplex network, the relative throughput at higher speeds drops quite rapidly due to latencies at both, including software delays as well as transceivers turn around times. So even if you can run RS-485 at 115k2 to 1000 m or Profibus-DP at 12 Mbit/s for 100 m, the actual throughput might not be what you expected. Using long message frames and avoiding too much pol/ack sequences will help.
>The user interact with a "dumb device" DD (slave) >and he should have the illusion to interact directly with the SD. > >All the relevant (for the user) info (status and settings) about the SD >must be spread among the DDs, through a suitable protocol on the RS485 >network. Some variables are read-only: they changes autonomously over >time and can't be changed by the user (for example, a temperature). >Other variables are read-write and can be changed only by the user (they >don't change autonomously).
So you must poll each DD to check if it has something to send.
>One simple strategy is to send all the current status and settings to >all the slaves every 1 second. If the data to be spread are big, the >number of slaves is high and most of the data changes slowly (as it >happens in my application), a lot of network bandwith will be wasted to >send always the same data. >I could alleviate this problem by sending data through a broadcast >message on the network, but I don't like it. The master should be sure >if a slave is present and has well received the status, such that it >could generate an alarm if a slave is failed and can't communicate on >the network. So the master must poll each slave and waits for an answer >from it (this can't be done with broadcast messages).
If the same data needs to be sent to all slaves, broadcast would be the natural solution. Also sending a few small different values to all slaves could be done with a broadcast message, just locate the value for a specific slave at a predefined location in the frame. The broadcast frame should also contain a common revision number. Since you are going to poll the slaves anyway for input data, include the most recent successfully received broadcast frame revision number in the poll response, thus the master will know if there are old data in some slaves and repeat the broadcast. This should quite quickly remove any transient errors and all slaves will return the most recent broadcast frame revision number and there should not be a need to repeat the periodic updates, until some data on the master side has changed and a new revision number assigned. Of course, if the slave has some real data to send, the poll response should contain the broadcast frame revision number as well as the data to be sent to the master, thus minimizing the unproductive message exchanges. Such practices have been used at least since the HDLC days.
pozz wrote:
> I have a "smart device" SD that is the master on a RS485 master/slaves > half-duplex network. The user interact with a "dumb device" DD (slave) > and he should have the illusion to interact directly with the SD. > > All the relevant (for the user) info (status and settings) about the SD > must be spread among the DDs, through a suitable protocol on the RS485 > network. Some variables are read-only: they changes autonomously over > time and can't be changed by the user (for example, a temperature). > Other variables are read-write and can be changed only by the user (they > don't change autonomously). > > One simple strategy is to send all the current status and settings to > all the slaves every 1 second. If the data to be spread are big, the > number of slaves is high and most of the data changes slowly (as it > happens in my application), a lot of network bandwith will be wasted to > send always the same data. > I could alleviate this problem by sending data through a broadcast > message on the network, but I don't like it. The master should be sure > if a slave is present and has well received the status, such that it > could generate an alarm if a slave is failed and can't communicate on > the network. So the master must poll each slave and waits for an answer > from it (this can't be done with broadcast messages). > > Another strategy is to send by difference: only if one piece of info (a > variable, a byte or a group of variables) changes, the master sends all > the data to each slave. > The problem with this technique is the amount of memory needed in the > master (it could be a small 8-bit microcontroller with modest size of > RAM). It should allocate a data buffer for *each* slave with the last > values sent to it over the network. Every 1 second, a function compare > the actual values against the last sent values for each slave and send > new values if they differ. > If the data is 100 bytes and slaves are 10, the master needs 1KB of RAM > only for that. > This strategy could be changed to decrease the network bandwith needed > to refresh the new status. If only one variable is changed, only that > (or some other related variables) can be sent over the network. > > Another strategy is similar to the previous, but saves a lot of memory: > the master set only a flag that triggers a status refresh to all the > slaves. The network protocol task routine checks for this flag and if > it is set, sends current values to all the slaves. > The problem with this strategy is in the "core" code that manages the > status variables. Every time in the code a variable is written in the > RAM, a comparison with the old value must be made and, if there's a > difference, the refresh flag must be set. I could try to change the > "core" code in order to manage the refresh flag for the network, but I > don't like it for several reasons. > The "core" code is already written and tested. > In the future, I'm sure I'll forget to correctly manage the refresh flag. > The "core" code would be strongly coupled with other completely > different modules (network protocol). > The "core" code would be more dirty with network refresh flag > management: every time I have to set a variable > > status.temperature = sensor_read_temperature(); > > I need to make a comparison with the old value: > > new_temperature = sensor_read_temperature(); > if (new_temperature != status.temperature) { > status.temperature = new_temperature; > network.refresh_flag = 1; > } > > One possibility to alleviate those problems is to create an intermediate > module that manages the status. This module provides getter and setter > functions. > > status_temperature_set(sensor_read_temperature()); > > void status_temperature_set(int new_temperature) { > if (status.temperature != new_temperature) { > status.temperature = new_temperature; > network.refresh_flag = 1; > } > } > > Even this strategy has some inefficiencies. The module should manage > the status that is relevant for the slaves (and for the protocol). The > "core" code should call status module setters for some variables and > should use normal assignment for other variables (that aren't related to > network protocol). In this case too, the "core" code is coupled with > another, completely logically different, module. > > > Do you have other better strategies to propose?
You're reinventing MODBUS RTU. It kinda sucks. You can make it work if you have a low enough number of "registers" on a small enough set of "nodes". You trade for latency and the only noise management strategy is retransmission of requests by the master node. -- Les Cargill
Il 30/11/2014 17:46, Paul E Bennett ha scritto:
> pozz wrote:
>
> [...] >> Do you have other better strategies to propose? > > There are plenty of strategies but which will be most suitable for you > rather depends on the end use of your system. Is this a commercial product > or just something you are playing with?
I'm just playing with this gadget at the moment, but I hope to sell some piece. Anyway I try to use "professional" methods even in projects I develop during spare time for hobby.
> There are things you could do to help yourself, without busting your > available memory or comms bandwidth budget, but end use of the information > is something I would want to know before I suggest anything as there may be > Safety Integrity Implications that you have not yet stated anything about. > To make assumptions otherwise would be wrong.
It's not a critical application (such as aerospace, avionics, medical or safety). This is for sure.
Il 30/11/2014 18:32, upsidedown@downunder.com ha scritto:
> On Sun, 30 Nov 2014 16:27:37 +0100, pozz <pozzugno@gmail.com> wrote: > >> I have a "smart device" SD that is the master on a RS485 master/slaves >> half-duplex network. > > In any half duplex network, the relative throughput at higher speeds > drops quite rapidly due to latencies at both, including software > delays as well as transceivers turn around times. So even if you can > run RS-485 at 115k2 to 1000 m or Profibus-DP at 12 Mbit/s for 100 m, > the actual throughput might not be what you expected. > > Using long message frames and avoiding too much pol/ack sequences will > help.
This is an interesting comment. I tend to use lower bitrate, in the range 9600-57600bps, even if the distance is small (maximum 100 meters). I agree with you about the delay that is introduced by software, mainly the delay for the slave to answer to the master (1ms delay corresponds to about 4 bytes at 38400bps), but the transceivers turn around time is very small at those speeds and can be safely ignored.
>> The user interact with a "dumb device" DD (slave) >> and he should have the illusion to interact directly with the SD. >> >> All the relevant (for the user) info (status and settings) about the SD >> must be spread among the DDs, through a suitable protocol on the RS485 >> network. Some variables are read-only: they changes autonomously over >> time and can't be changed by the user (for example, a temperature). >> Other variables are read-write and can be changed only by the user (they >> don't change autonomously). > > So you must poll each DD to check if it has something to send.
Yes, it is what I'm doing.
>> One simple strategy is to send all the current status and settings to >> all the slaves every 1 second. If the data to be spread are big, the >> number of slaves is high and most of the data changes slowly (as it >> happens in my application), a lot of network bandwith will be wasted to >> send always the same data. >> I could alleviate this problem by sending data through a broadcast >> message on the network, but I don't like it. The master should be sure >> if a slave is present and has well received the status, such that it >> could generate an alarm if a slave is failed and can't communicate on >> the network. So the master must poll each slave and waits for an answer >>from it (this can't be done with broadcast messages). > > If the same data needs to be sent to all slaves, broadcast would be > the natural solution. Also sending a few small different values to all > slaves could be done with a broadcast message, just locate the value > for a specific slave at a predefined location in the frame. > > The broadcast frame should also contain a common revision number. > > Since you are going to poll the slaves anyway for input data, include > the most recent successfully received broadcast frame revision number > in the poll response, thus the master will know if there are old data > in some slaves and repeat the broadcast. This should quite quickly > remove any transient errors and all slaves will return the most recent > broadcast frame revision number and there should not be a need to > repeat the periodic updates, until some data on the master side has > changed and a new revision number assigned. > > Of course, if the slave has some real data to send, the poll response > should contain the broadcast frame revision number as well as the data > to be sent to the master, thus minimizing the unproductive message > exchanges.
I don't think there's a great difference between (M=Master, S=Slave): M: Hey all, this is message number 13 and my current state is this M: Hey Slave 1, nothing to say? S1: Last frame received from you is 13 and nothing to say. M: Hey Slave 2, nothing to say? S2: Last frame received from you is 12(!!!?) and nothing to say and M: Hey Slave 1, this is my current state. Nothing to say? S1: Ok, nothing to say M: Hey Salve 2, this is my current state. Nothing to say? S2: Ok, the user wants to switch off garden lamp ...
> Such practices have been used at least since the HDLC days.
Indeed my protocol (at the link level) is inspired to HDLC. The master manages a session for each slave and, for each session, there are two numbers: the sequence number of last frame send and the sequence number of last frame received. This is a good approach to identify failed slaves and failed transmissions to avoid retransmissions with side effetcs. But this is at the link level. I was discussing about the protol at the application level on top of link level.
Il 30/11/2014 19:14, Les Cargill ha scritto:
 > [...]
> You're reinventing MODBUS RTU. It kinda sucks. You can make it work if > you have a low enough number of "registers" on a small enough set of > "nodes". You trade for latency and the only noise management strategy > is retransmission of requests by the master node.
I know the MODBUS RTU, but I don't know how it is the answer to my question. Should the Master (implementing Modbus protocol) sends all the registers values every 1 second, even if they don't change, or should it send only values that change? What is the approach in Modbus network?
pozzugno wrote:
> Il 30/11/2014 19:14, Les Cargill ha scritto: > > [...] >> You're reinventing MODBUS RTU. It kinda sucks. You can make it work if >> you have a low enough number of "registers" on a small enough set of >> "nodes". You trade for latency and the only noise management strategy >> is retransmission of requests by the master node. > > I know the MODBUS RTU, but I don't know how it is the answer to my > question. >
It's an attempt to name a very similar protocol which might trigger some strategies to cope amongst the participants. I think the comparison to RTU it does help. You're bandwidth constrained and will have to trade latency for bandwidth.
> Should the Master (implementing Modbus protocol) sends all the registers > values every 1 second, even if they don't change, or should it send only > values that change? What is the approach in Modbus network? >
I am very sure that this varies. I can't give you "should"; I don't have enough information. But there's usually no reason to send information you don't have to over a bandwidth constrained network. Usually. The ugly part of MODBUS RTU is having to poll everything for state changes. Since you're half duplex, I doubt you can have asynchronous events fired from edge nodes. You'd have to poll for them. -- Les Cargill
pozzugno wrote:

> Il 30/11/2014 17:46, Paul E Bennett ha scritto: >> pozz wrote: > > >> [...] >>> Do you have other better strategies to propose? >> >> There are plenty of strategies but which will be most suitable for you >> rather depends on the end use of your system. Is this a commercial >> product or just something you are playing with? > > I'm just playing with this gadget at the moment, but I hope to sell some > piece. Anyway I try to use "professional" methods even in projects I > develop during spare time for hobby. > > >> There are things you could do to help yourself, without busting your >> available memory or comms bandwidth budget, but end use of the >> information is something I would want to know before I suggest anything >> as there may be Safety Integrity Implications that you have not yet >> stated anything about. To make assumptions otherwise would be wrong. > > It's not a critical application (such as aerospace, avionics, medical or > safety). This is for sure.
OK. Does a slave node need information generated by another slave in order to operate sensibly? This could complicate matters slightly. However, there are schemes and I have seen others mention RTU's which is where this need seemd like it could find a solution. You should probably form a definite pattern for sending and receiving data that includes the slave node addresses. Whether such addresses are provided as hard-wired switches or are part of a discovery scheme is something you may need to think about. In the end, all your slaves will need a unique node address. There are schemes like CEC (part of the HDMI specification and used for consumer device control). Some standards exist like ModBus, IEC60870, DNP3 and IEC61850. The HDMI CEC bit is in <http://www.microprocessor.org/HDMISpecification.pdf>. You should probably keep all communicable data in specific memory blocks to aid easy transfer (one for TX and one for RX). Keep the size of these blocks small and include some form of communications heartbeat function within the protocol you select (this should be regular enough and distinct enough to know the link status in both directions). The master station should always be the commanding node and slaves should obey. You can include acknowledgements so that the master knows that the commands are received and acted upon. Regular status enquiries could collect the receivable data in response or just a status byte that states the slave is healthy and there is data to transfer (something you will have to decide) Keep the protocol you select as simple as will suit your needs as possible. -- ******************************************************************** Paul E. Bennett IEng MIET.....<email://Paul_E.Bennett@topmail.co.uk> Forth based HIDECS Consultancy.............<http://www.hidecs.co.uk> Mob: +44 (0)7811-639972 Tel: +44 (0)1235-510979 Going Forth Safely ..... EBA. www.electric-boat-association.org.uk.. ********************************************************************
Il 01/12/2014 13:56, Paul E Bennett ha scritto:
 >
> Does a slave node need information generated by another slave in order to > operate sensibly? This could complicate matters slightly. However, there are > schemes and I have seen others mention RTU's which is where this need seemd > like it could find a solution.
No, it's not important.
> You should probably form a definite pattern for sending and receiving data > that includes the slave node addresses. Whether such addresses are provided > as hard-wired switches or are part of a discovery scheme is something you > may need to think about. In the end, all your slaves will need a unique node > address.
Of course, I already developed a discovery mechanism that is based on a unique 32-bit serial number hard-wired in each slave. During discovery phase, the master assign a 8-bit addres to each slave. This 8-bit address is used during normal communications.
> There are schemes like CEC (part of the HDMI specification and used > for consumer device control). Some standards exist like ModBus, IEC60870, > DNP3 and IEC61850. The HDMI CEC bit is in > <http://www.microprocessor.org/HDMISpecification.pdf>.
The link level of the protocol is inspired to HDLC. Now I have to develop the application-level protocol.
> You should probably keep all communicable data in specific memory blocks to > aid easy transfer (one for TX and one for RX). Keep the size of these blocks > small and include some form of communications heartbeat function within the > protocol you select (this should be regular enough and distinct enough to > know the link status in both directions). > > The master station should always be the commanding node and slaves should > obey. You can include acknowledgements so that the master knows that the > commands are received and acted upon. Regular status enquiries could collect > the receivable data in response or just a status byte that states the slave > is healthy and there is data to transfer (something you will have to decide) > > Keep the protocol you select as simple as will suit your needs as possible.
Everything you have written is already implemented in the link-layer of the protocol.