Low data rate transmission over long distance using MCUs
I'd suggest that any configuration requiring the nodes/units (the MCUs) be programmed with individual ID# would be extremely hard to manage.
- Each unit has to be individually programmed with an ID#
- Most bus based protocols are not able to be position sensitive
- Units become order sensitive and difficult to use in large numbers
With any Bus based system it is almost impossible to tell the relative positions or physical connection order of the slaves. I2C is a great example of this restriction; there is no way to tell the physical connection architecture without additional logic and status lines.
I would suggest the best possible architecture for your application would be to use a SoftUART in each of your ATTinys and a very simple half duplex async command protocol.
Consider a configuration like this:

simulate this circuit – Schematic created using CircuitLab
Each ATTiny is Non-unique (no initial ID#'s) and is configured by the protocol reset. ID#'s are allocated based on connection order.
A simple protocol might be as follows:
- All units Rx a character string starting with "#" and ending with a cr or cr/lf sequence from the left source on their Rx input, decode and send to the next unit using the right Tx. The string is fully received decoded and then sent to the next unit downstream. In this way you only require half duplex operation in each node.
- Character strings can be modified by each unit if required. This allows either incrementing elements such as ID#'s or allows command feedback.
- The character string is returned to the Host unit after passing through ALL physical units in the string.
Command strings might be as follows:
Reset: #Ra cr ….// #=Start of string.... "R"=reset command …. "a"=start ID# …. cr= cr or cr/lf sequence
The host sends "#Ra" cr to the first downstream node, which resets, and copies the "a" to it's ID# storage. The string start ID# is incremented (from 'a" to "b") and sent to the next downstream node. If there were 26 nodes in series, then string returned to the Host would be "#Rz" cr" and each node would now hold a unique ID# of "a" through "z".
Trigger: #T? cr ….// #=Start of string ….."T"= Trigger command …."?"=node ID# "a" through "z".
Passthrough: #P? cr ….// #=Start of string ….."P"= Passthrough command …."?"=node ID# return value.
The node receiving this command would simply copy this to it's downstream neighbor taking no other action. This would be a simple way to return a status byte from a node. For example, if a node received a Trigger command, then it could alter the string sent to the next node to a Passthrough command, adding whatever status byte it required. All other nodes would simply pass this string to their neighbor and eventually back to the Host.
With the protocol above a string is 4 characters long. If the baud rate was set at 9600 baud, then it takes about 4.1mS for the string to be sent. Since the string must pass through all 20 nodes then it takes about 21 * 4.1ms for the string to return to the Host, or about 84mS.
I'd suggest that when a trigger command was received, the addressed node would not send the string to it's neighbor until the Flash was complete, it could then return the status (power used, or other parameters) to the Host using Passthrough. This also means that if you have a high energy pulse, the data lines are not being used while processing the command, reducing the likelihood of transmission errors.
An architecture such as this could be useful for at least ten meters distance between nodes, and at 9600 baud easily meets your 500mS requirement.