modules can be interconnected at the same wiring and communicate with the
master station, one at a time obviously. Each module should have an UART.
Connect all modules TX circuits (open collector) together and RX circuits together.
If you have a limited number of modules, lets say you know the maximum possible address numbers, the master could be asking for the presence of those numbers, in sequence, and wait for a response in 50ms (example). If you have an unlimited numbers and possible combinations of addresses, then this sequencial "calling" is not practical.
One way to do that, is a colision system, where every module try to send its address byte(s) to the master right after the power on reset... it will be a mess. Each one transmit its address and wait for a master response. If the module was lucky enough to get an "empty" tx line and transmit it nicely, then the master will receive and validate it echoing it back to all the modules. The sender module will identify its address and will keep quiet until further master addressing and commands. Then the other modules will be fighting to send their address to the master and this thing goes until all modules clear ok. It sounds to be a mess, but in real the time delays involved in the distances between modules and master station would help a lot, and sooner than you think all modules will be cleared. Some techniques can be used to help this colision system, as observing the TX line before start its own transmission, it is busy or not?, or include a small different time delay between transmissions based on an algoritm that uses module's address as parameters.
Connect all modules in a long ring. Every module TX pin is connected to the following module RX pin. The Ring closes at the Master Station that makes part of the ring as any module. It means that any module can be assigned to be the master station, or even to be a backup master station if the main fails.
To implement this technique, is necessary that each module has a buffer area to store a complete or a part of the traffic message.
The master station is who initiates every transmission, at least the "topic" of the conversation, and the modules react to it. At the master's message header, the master include what is the funcion of that message (topic), so each module when receiving this message can identify what is the master intentions and then act properly.
Lets suppose a message always start with 4 sync bytes "55 55 AA C3" (so each module can identify when a message starts), followed by the master identification 2 bytes "00 01" followed by the command byte "05" (as example), followed by the lenght of the message that follows, in this case "00 00" (no message). Whenever the message length is zero it means "end of the message". The command "05" here could be the command for XID (Exchange Identification), so every module receiving this command will *include* its own address AT THE END of the message.
So exiting the master, it will be 55 55 AA C3 00 01 05 00 00
To include its address, the module will wait until the message length would be "00 00", indicating "end of message", and will include the command byte sent by the master, "05" followed by module's address, lets say "02 01" followed by a message length to follows, in this case no message, so it includes "00 00".
So exiting the first module, it will be 55 55 AA C3 00 01 05 05 02 01 00 00
The bytes in red here are the bytes inserted by the first module identifying itself via the XID command message.
This situation is dynamic, so the next module will also receive master's 05 command (XID) and will include its own 5 bytes message at the end of the message block and it goes on. If the second module address's is 07 89, then its output will be:
55 55 AA C3 00 01 05 05 02 01 05 07 89 00 00
This big block will loop back to the master RX pin quickly, telling the master all the module's addresses at once.
Other command code bytes can means different things, including addressing, polling, busy, carrier, write, read, status, etc.
The nice "Carrier Command" is a small message block sent by the master from times to times, that allows a module to send a message directly to another module, without needing to overhead master's processing time to intermediate the traffic. Example: When the module 0008 wants to send a message to module 0010, it waits the "carrier" message, then includes a sequential message number, its own address, the destination address and the message itself. Several modules can include their own inter-module messages at the same string message.
When module 0010 recognizes a message to itself in the carrier string, it complements the message number in the middle of that string and retransmit it completelly. Module 0008 seeing its message number complemented, understand it was received by the module 0010, and wipe out completely its message from the circulating string.
If the that portion of the carrier string passes module 0008 twice or more, it means that module 0010 does not received the message, so module 0008 should take an action, remove the message or keep trying, even that Master station can signal module 0008 to remove the message or if it is not done, master can force it out.
In real, IBM's Token Ring does something like that, and it is more efficient, it insert several different commands at the same circulating string.
You can think and develop your own protocol based on the above explanation, sophisticated or simple, it works very well.
The only problem is that it needs all connected modules operational. You can implement a "relay" bypass, that will propagate the signal (RX to TX) arriving to a powered off, removed or failling module. This is actually done at IBM's Token Ring.
If you have implemented anything like this, or some differente multi-stations protocol and wish to share this information with other people, I can publish here your work. Feel free to contact me about it.