Best way to route an input to 7 possible outputs?

Ok, it's making a little more sense to me now.

Simplest "solution" I can think of, and this might not fulfill all your requirements, would be that each hexagon would contain only 6x ws2812 LEDs. One side would be the input, perhaps with a 3-pin male connector of some kind. The other 5 sides would be outputs with 3-pin female connectors. The 5 outputs would be identical/equivalent/connected in parallel. Only the "source module" would contain an Arduino and have perhaps only 1 but potentially 6 female 3-pin output connectors, but these could be independant outputs if required.

This would give full independant control of every led in every hexagon in a single "route" leading back to the source module. Only if there was a branch at some point, where 2 or more modules have their male pins connected to the same neighbour's female pins, would the full independance be lost, because both/all of the branches would become copies of each other from that point onwards.

Thoughts?

Having only the LEDs in each cell would of course be easiest and by far cheapest, but would also dramatically increase the steps needed to be taken by the user. Also as you said, branches are a problem. The best way I can think of to overcome that is to have one input and output port on the backside of each cell, and have the user take sort of a "patch cable" approach, attaching 3-pin cables to daisy chain the cells together in a custom order. In theory that would give the desired end result to a T, but not the steps getting there - Things I'd be sacrificing with that:

  • If one-way connectors are on sides of cells, they'd now have a "right-side-up" and can't be put in any orientation. A minor detail but just one more thing the user needs to know
  • If patch cables are used, the user has to figure out the best way to route them, which some people might find difficult with bigger complex layouts. Also the assembly now can't be flush against a wall without squishing the wires
  • No more automatic model detection; this is the big one, the user would now have to manually tell the source cell exactly where and how each cell is connected in the assembly and rely on themselves to match it
  • No more changing the model while live; I could be wrong but I believe you'd need to turn it off and on again for the strips to re-initialize with a new length whenever the model changes. Combined with the above point, this would make experimenting with arrangements very tedious for the user.

Btw, the way I was originally planning to connect the cells electrically was with terminal dots on the sides, so as you slide them into place the terminals make contact with the adjacent cell. Since the opposite side would be rotated 180°, this allows for natural RX/TX connection, as well as same side VCC/GND by simply having a copy of that pin mirrored, if that makes sense.

Not sure what you mean by "right-side-up". This is a 2 dimensional thing designed to hang on a wall? Each hexagon can only have 6 possible orientations, can't it? Why can't all of them be used?

Why is that important? This is an ornament, an art work showing interesting patterns of colour. How will knowing that information change how it behaves?

Generally speaking, circuits should not be reconfigured while live. Those that can have been specifically and expertly designed to allow that without damaging those circuits, for example USB or HDMI.

Sorry, by "right-side-up" I meant like it would be important which orientation you attach it, because you'd be limiting the data flow to one possible direction.

To have specific animations, it needs to know where each pixel is in physical space so it can be appropriately mapped. Essentially what I'm making can be thought of as a modular LED display.

True, and maybe this would be best at first, but the dream at least is to have a system where the user can experiment with different arrangements by simply snapping a new cell on and everything updates instantly, which doesn't seem like it'd be too much to ask. For the patch cable approach, essentially all I'd be doing is plugging in (or unplugging) another daisy chained LED strip while it's live, which I don't think would cause any issues, and if I can re-initialize the strip in the code at will it'd never need to be turned off during the process.

To achieve all those things, I think you are going to need some intelligence in each hexagon. If each hexagon needs to communicate with up to 6 neighbors, I think it will need 6 hardware UARTs. I don't think using multiplexers will be good enough, because any neighbor might try to send to the hexagon at any instant, and without 6 hardware UARTs chances are most of that data will be lost.

A different idea would be that each hexagon only communicates with the source module, in a master-slave arrangement. Some kind of multi-drop serial protocol might work, such as rs-485. In this protocol, a single pair of serial lines acts as a bus. Any slave can be connected anywhere on the bus and can communicate to the master directly. Rs-485 is the only one I know of, but there could be a more appropriate one out there for this application.

Each hexagon still needs to somehow figure out its orientation and position, so that it can send this to the source module. I can't think of a simple solution for that at the moment.

imho you will need a chain of modules, abstracted from your hexagonal layout.
Each node can have two full RX/TX interfaces, one labeled IN and one labeld OUT.
You daisy chain your Nodes - route wires from Out to the next IN

First the Master needs to ask all Nodes one by one to figure out the their order:

Each Node has a Unique ID (A, B, C, D)
on startup master sends out a "Is there anyone?"
the first device Unique ID A responds on its IN Interface to the master
the Master sends the a NodeNumber 1 to Node A
Node A receives NodeNumber 1
Master sends out a "Is there anyone?" - again
The first Node (A) has already got 1, therefore it forwards the request to the OUT interface.
The second Node (let's asume D) receives this request on its IN Interface and responds with it's ID D
The first Node (A) forwards the information from its OUT interface to its IN Interface to the Master
The Master now assigns the Number 2 to the Node D.
Master sends the Information for NodeD to NodeA.
Node A forwards the Information to Node D.
Node D is now Number 2.
Master sends out a "Is there anyone?" - again
Node A .. as already a Number, forwards to Node D
Node D ... has already a Number, forwards to its Out Interface
Node C ... receives the request from Node D, and respons with its Node ID C
Node D ... forwards to Node A
Node A... forwards to Master
Master assigns Number3
...

and so on. In the end, the Master knows the order of each Node: A->D->C
You could add the orientation of the Nodes - so the Master should be able to address each LED exactly.

if you don't like daisy chaining, google for "wired mesh network" and have fun :wink:

Another idea. Each hexagon has, as part of its circuit, a pair of resistors forming a voltage divider. The values of those resistors are unique to each hexagon, giving a unique output voltage. The MCU in the hexagon can use one of its analog inputs to read it's own unique voltage and then knows its identity, which it can communicate to the master/source module. It also makes that unique voltage available on a connector on each side.

Another connector on each side allows the hexagon's MCU to measure the voltage it is receiving from the neighbour on that side using 6 more analog inputs. A high value pull-down resistor would need to be attached to each analog input to prevent it floating and read zero if no neighbour is connected on that side, but not significantly affect the voltage if a neighbor is connected. Each hexagon can communicate the voltages it reads on each side to the source module.

The source module would have it's own resistor pair and would output is own unique voltage so that any hexagon connected to it would measure and report that back to the source module. The source module would read it's own voltage divider, just like the hexagons do, so it could recognise it's own value when a hexagon reports that back. It would also use additional analog input(s) to detect connected hexagon(s).

The source module could then build a map in its memory of where on a grid each hexagon is located, and it's orientation. It can start by requesting data from the hexagon(s) connected directly to it. It will learn of other hexagon's IDs from that, requesting data from each in turn.

Love the brainstorming!

Yeah, that's what I was thinking originally with the serial thing. I'm with you on the idea of having one serial bus and then possibly using a simple GPIO sequence to figure out position/orientation, but haven't thought about a specific algorithm for that yet either. I'll check out rs-485 when I get a chance, sounds interesting.

Ok, sounds like you're suggesting kind of a hybrid between the "smart" method and the daisy chained patch cable approach? So my question with that is, how would it know the position and orientation of the cells? If you still have to put that in manually, then what's the point of knowing the order since you could just specify that as well, and not need to bother with a microcontroller in each cell? Overall your algorithm looks similar to mine with the pure serial approach:

  • Each cell has an independent serial RX/TX for each side, as well as a global bus
  • Source boots up and waits for a message
  • Cell boots up and immediately sends "hey something changed, we need to update the virtual model" to global serial bus (eventually I'd make a way for running cells to also detect if adjacent cells are removed, and send this same message)
  • All other cells receive that and, if they were already running, pause. Source receives that and starts the model update sequence by sending "ok, we're updating the model" along with the data that makes up the model so far (currently just itself, at x=0 y=0 angle=0)
  • First cell receives that and sends back "ok got that, moving on", then adds its own data to the model and sends that to the first of its 6 sides. Let's assume nothing's there, so it times out and moves on to the 2nd side. Let's say a cell receives that and sends back "got that, moving on", so the first cell knows to pause there.
  • This continues until it reaches a dead end (meaning a cell times out on all 6 sides), and then that cell sends "Ok this branch is complete, here's the model" back through the side it received from. If that cell has more sides to try, it'll move on with that updated model, and so on.
  • Eventually all branches will be calculated, and the first cell sends that complete model back to where it first received from - the source cell. When the source receives that, it knows "ok this must be everything", and then sends the complete model to the global bus so every cell has a copy and knows to start listening for frames.
  • That's the algorithm I have working in my Processing simulation as of now

I had a similar idea but didn't bother to mention it because I didn't think it would work and it wasn't really formed in my head, but you might be onto something. One issue though, are you saying each cell would have to be manufactured with a unique resistor value? If so, this might be impractical not only because of the non-uniformity, but also because with only 1023 possible values (generous considering the variance in analogRead), at scale it'd be likely that someone could by chance end up with 2 or more identical cells. In general I like the idea of things being theoretically infintely expandable, but I realize sometimes that's not practical so I'm open to ideas.

THIS! This is more the idea I had. If each cell could have an identical SET of different-valued resistors for each side, could there be a way to get the whole model just from that, without an MCU in each cell?? Of course then even if there is, we still have the issue of how to route LED data...

Man this seemed so much simpler in my head :joy: I love the collaborative problem-solving though!

No, I was imagining the bare PCB would be manufactured and you would be soldering the components on, so you could choose a resistor pair for each board. Alternatively, you could have the PCBs manufactured with components soldered on, except the resistor pair, which you could solder on later. That way, you could use surface mount components for most things but have spaces for 2 thru-hole resistors.

There is a small chip you would need: max485 or equivalent. However, there seems to be a limit of 32 devices on a rs-485 bus, so if no way can be found to extend that, you may need to look for some other solution.

Hmm I see. Is there a reason I wouldn't be able to just have one regular serial connection tied together between all cells? Like connect all the TX and RX so when one cell sends something, everything else receives it (including itself but it could filter that out)? I guess that might break if multiple cells try to send something at once... But considering the chances of a cell initiating a message at the EXACT same time as another are next to zero, I could have some kind of handshake where if a cell receives something right before it was about to send, it waits and reads that first

  1. You can't connect more than one TX pin together. As soon as one tries to transmit, it would short-circuit with the other, certainly garbling the message, possibly damaging both chips. You could solve that problem with diodes, but then you would also need to pull up the line with a resistor, and this would limit the baud rate you could use because the line is only pulled high passively and fairly weakly, rather than pulled high actively and strongly by the TX pin as it normally would.

  2. You can only connect a limited number of Rx pins (maybe 10~20) to a single TX pin before the excessive "fan-out" garbles the signal, or, again, limits you to slower baud rates.

Solutions to the above could be to use buffer chips on the Rx and TX pins, to boost the signals, and enable the TX pins to be "tri-stated" so they don't interfere with each other and cause short-circuits. A perfect chip exists for that purpose: the max485 chip I mentioned before.

The question is, why is there still a limit of 32 devices? My guess would be that fan-out problem again. Perhaps the limit can be extended with extra buffer chips. More research needed!

The way to avoid that is to have a single master device (the source module in your case) and multiple slaves (the hexagons). Slaves only speak when spoken to, and although the master broadcasts requests to all slaves at once, the request includes the ID of the slave that should respond, so all but one slave ignores the message.

This does present a problem for your idea of allowing the user to add new hexagons at any time. The new hexagon would need to announce its arrival somehow. Perhaps it would be acceptable for the user to hit a reset button on the source module after adding one or more hexagons? Then the source module could discover the new hexagons in the manner I described before.

To update a WS2812 interrupts needs to be disabled causing a device to be deaf. Having multiple devices communicating with eachother where devices can be deaf might be a challange for the protocol. I think I would go with 2 buses. One for the pixeldata which daisychains through all devices and one for the configuration.

Each hexagon has 6 sides and one led stripe. Resulting in 7 data inputs and 7 data outputs where any input needs to connect to any output. I think I would use some crossbar switch. Maybe the MT8809 works for this.

To configure the routing the source module can send two commands to a device:

  • resetRouting -> Clears the routing configuration
  • addRoute -> Adds a side to the signal path

E.g.: Having the source module connected to the first device on side A the following commands would be issued:

  • 1, resetRouting
  • 1, addRoute, A

Resulting in this loop:
SM -> 1A -> 1LED -> 1A -> SM

Assuming a second device on side C additional the following commands would be send:

  • 1, addRoute, C
  • 2, resetRouting
  • 2, addRoute, x (where x is the side connected to device 1)

Resulting in this loop:
SM -> 1A -> 1LED -> 1C -> 2x -> 2LED -> 2x -> 1C -> 1A -> SM

=============

For the device discovery I think I need to address a device with:

  • b (Broadcast, sending a message to all devices)
  • s (Select, pulling a devices selectIn low)
  • (address) (Using a configured address, e.g. 1-100)

and probably the following commands:

  • resetAddress (Sets configured address to 0, send as broadcast)
  • setAddress (Set the configured address)
  • getAddress (Selected device responds with its address)
  • resetSelect (Sets all selectOut high)
  • selectSide (Pulls selectOut to low)

To discover the first device the following commands are send:

  • b, resetAddress
  • SM, selectSide x
  • s, setAddress 1
  • SM, resetSelect

Getting the orientation:

  • Loop over A-F -> x
  • 1, selectSide x
  • s, getAddress
  • When address is SM -> orientation found

Discover a second device:

  • Loop ver A-F -> x
  • 1, selectSide x
  • s, getAddress
  • -> No response -> Open side
  • -> Response with address == 0 -> New unconfigured device -> Continue with setting address and discover orientation
  • -> Response with address >= 1 -> Configured device -> ignore

=============

For the electrical connection I suggest 12 pins on each side of the hexagon.

  • Pin L3(x) and R3(x) (where (x) is the side) connect to the crossbar switch
  • Pin L4 to the Arduino
  • Pin R4(x) to the Arduino (where (x) is the side, resulting in 6 GPIO pins used)
  • Pin 5 and 6 to the UART (Some thoughs need to go into preventing 2 devices driving this TX to different levels)

The source module has RX and TX swapped to allow communication SM -> device and device -> SM
But no communication between device -> device.

Sounds like there are "better' versions of the MAX485 chip, which allow more devices on the bus. MAX487 allows up to 128, for example.

Ahh true. Ok, scrap that idea. I'll check out the max485, or possibly 487 as you mentioned later.

Ok, idea: what if instead of announcing new/removed cells, it just... Always checks? Like it starts the sequence to update the model, and while that's going it continues to use the global bus to send frames to the current model. When the updated model comes back, it simply swaps that in and then starts checking again. That way new cells could simply wait to be discovered by the next sweep, and removed cells would be noticed as well.

Huh, didn't know these existed. A quick Google tells me they look really useful, I'll do more research on that. Clearly you put a lot of time into the following suggestions which I appreciate, I'll definitely study that closer and see if I can get something out of it. The picture with terminal configuration is pretty much what I had in mind in the first place.

I guess that could work, yes. If it uses too much of the global bus' bandwidth, it could be done more slowly, so that the data is rebuilt over a number of update rounds.