A handy way to code interactions in a fairly complex environment is to use C++ objects. The idea is that each object has a setup() and a loop() method. In your setup, you call the setup method of each, and in your loop, you call the loop method of each. Each object in its loop method does whatever it has to do - potentially by calling methods on other objects to signal changes of state. These other objects potentially deal with the change right then, or they store something internally to be handled when their own loop() method gets called.
It can be a bit ugly to call your time-slice method on each object. One way to deal with this is to create a superclass with virtual setup() and loop() methods, and to initialize a table of pointers to these objects. Bu there is a neater way.
In the sketch below, the Runnable abstract class builds a linked list of all runnable objects using its constructor. The main setup() and loop() methods iterate through this list. Below that is the actual business end of this demo. The demo creates four objects. Three blinkers (on pins 8, 9, and 10) and an object that reads analog 0 and sets the blink rate of the blinker on pin 10 accordingly.
The objects are wired to their pins and to the other objects they interact with in their constructors. Note the use of C++ references to wire together the BlinkerSpeedShifter to the Blinker that it controls.
The result is a network of objects, each with a role and each with its own internal state and set of variables.