XRAD'S Synchronizing two arduinos with light sensors

I have spent quite some time researching this, and unfortunately have not found answers to some challenging questions.

Project: to synchronize led flashes of two independent arduinos, each with a light sensor and an led. Board: adafriut trinket M0's , light sensor: TSL2591, led: ws2811 addressable for now (1watt later)

Concept: 1st arduino flashes an led(1sec), the 2nd arduino sees it. Conversely, the 2nd arduino flashes led and the 1st sees it. then over time, both arduino's flashes are synchronized

Rules:

  1. either #1 or #2 arduino can flash their own led if the do not receive other's flash in 10 seconds.
  2. if other's flash received, then fire own flash back
  3. attempt to get synchronized flashes for both arduino's occurring after 4 second OFF interval
  4. all outgoing flashes are always 1 sec long for either arduino

what I can do now:
i can compensate for ambient light and read other's led flash, and fire my own led flash

Problems:

  1. it seems that I will need to have a window of variablity to advance or retard millis() timing for own reply flash once the other's flash (which is also variable) is sensed. SO are the arduinos competing against each other and will never synch??
  2. I 'think' that what I would try to program for is actually the 'absence' of a flash since I would have to be blind to my own flash, which will eventaully be occurring in synch with other's flash.
  3. I am having difficulty understand HOW to create a variable milli() timer to advance or retard the led flashes.

this project seems similar to advance/retard ignition (which I have also researched) except both arduinos will be 'searching' for a balance

any project guidance or direction to info MUCH appreciated!

here is my code so far.......

/*  12/12/2021 XRAD'S FARFALE FANTASTICO (fantastic fireflies)
    the array holds four elements (light sensor readings) used to
    compute the min and max luminosity levels over ambient.  We only want
    the POSITIVE increases from MIN to MAX, and use these to calculate
    a % delta luminosity. Sometimes the array will begine with a high reading
    and they are discarded.  If delta luminosity is greater than for example
    5%, we could light our own LED flash. The LED flash is blind during self flashing
    and until a 'new' >5% delta luminosity is calculated. It is here
    before the next self flash, that the biphasic timing is adjusted
    to synch the firefly node.
    things to do:
    1) autonomous self flash
    2) variable self flash length...mayyybbbeee
    3) variable self timing...needed!
*/

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_TSL2591.h"
#include <FastLED.h>
#include "RunningAverage.h"
#include <Adafruit_DotStar.h>

#define NUMPIXELDOT 1 // Number of LEDs in strip

#define DATAPIN   7
#define CLOCKPIN   8

Adafruit_DotStar strip = Adafruit_DotStar(
                           NUMPIXELDOT, DATAPIN, CLOCKPIN, DOTSTAR_BGR);

#define NUMPIXELS 1 // Number of LEDs in strip
#define DATAPIN  3 //   LED  pin:
bool myledFLASHState = false;
unsigned long MyPreviousFlashMillis = 0;
const long MyOnFlashInterval = 1000; // led ON time ms

bool otherFlashReceived = false;
unsigned long  PreviousOtherFlashReceived = 0;
unsigned long  IntervalOtherFlashReceived = 10000;

bool   LeftFlashDetected = false;
bool   RightFlashDetected = false;

RunningAverage RIGHTEYE(4); //myArray(x) size of array
RunningAverage LEFTEYE(4);
int samplesR = 0;
int samplesL = 0;

CRGB leds[NUMPIXELS];

Adafruit_TSL2591 tsl = Adafruit_TSL2591(2591); // pass in a number for the sensor identifier (for your use later)


float MAXR = 0;
float MINR = 0;
float MAXL = 0;
float MINL = 0;

int threshold = 10;  //add this value to RIGHT/LEFT EYE element 0 to decrease unwanted noise (10 seems fine)

int TPC = 5; //threshold percent change, min required change to allow self flash to trigger

float percentChangeR = 0;// variable to hold % change of Luminosity
float percentChangeL = 0;

uint16_t lightX;
uint16_t rightEye;
uint16_t leftEye;


// Select I2C BUS
void TCA9548A(uint8_t bus) {
  Wire.beginTransmission(0x70);  // TCA9548A address is 0x70
  Wire.write(1 << bus);          // send byte to select bus
  Wire.endTransmission();
  // Serial.print("bus:  ");
  // Serial.print(bus);
}

void ledFlash() {
  for (int x = 0; x < 3; x++) {
    for (int i = 0; i < NUMPIXELS; i++ )
      leds[i].setRGB(0, 255, 0);  // grb
    FastLED.show();
    delay(250);
    for (int i = 0; i < NUMPIXELS; i++ )
      leds[i].setRGB(0, 0, 0);
    FastLED.show();
    delay(250);
  }
}

void simpleRead(void)
{
  lightX = (tsl.getLuminosity(TSL2591_VISIBLE));//used for both left and right EYEs
}


void setup(void)
{
  Serial.begin(115200);

  //Serial.begin(9600);

  FastLED.addLeds<WS2811, DATAPIN, RGB>(leds, NUMPIXELS);
  FastLED.setBrightness(100); //0-255 initial brightness
  FastLED.clear();
  FastLED.show();

  //dotstar
  strip.begin(); // Initialize pins for output
  strip.show();  // Turn off onboard dotstar

  ledFlash();//three blinks = i'm alive

  RIGHTEYE.clear(); // clear running avg loops
  LEFTEYE.clear();

  Wire.begin();// Start I2C communication with TCA9548A address 0x70

  TCA9548A(0);
  tsl.begin();//init light sensor w/fixed address 0x28 and 0x29

  TCA9548A(7);
  tsl.begin();//init light sensor w/fixed address 0x28 and 0x29
}


void loop()
{
  unsigned long MyCurrentFlashMillis = millis();//start current flash timer

  unsigned long CurrentOtherFlashReceived = millis();



  TCA9548A(0);
  //Serial.println("  RIGHT EYE");
  simpleRead();
  rightEye = lightX;

  TCA9548A(7);
  //Serial.println("  RIGHT EYE");
  simpleRead();
  leftEye = lightX;


  long rnR = rightEye;
  RIGHTEYE.addValue(rnR);//(rn * 0.001);
  samplesR++;

  long rnL = leftEye;
  LEFTEYE.addValue(rnL);//(rn * 0.001);
  samplesL++;


  /*
      Serial.print(samplesR);
      Serial.print("\t");
      Serial.print(RIGHTEYE.getMin(), 0);
      Serial.print("\t");
      Serial.print(RIGHTEYE.getAverage(), 0);
      Serial.print("\t");
      Serial.println(RIGHTEYE.getMax(), 0);

      Serial.print(samplesL);
      Serial.print("\t");
      Serial.print(LEFTEYE.getMin(), 0);
      Serial.print("\t");
      Serial.print(LEFTEYE.getAverage(), 0);
      Serial.print("\t");
      Serial.println(LEFTEYE.getMax(), 0);
  */


  if ((samplesR >= 4) || (samplesL >= 4))  {
    /*
        Serial.println("IDXR VAL 0 and 1: ");
        Serial.println(RIGHTEYE.getValue(0), 0);
        Serial.println(RIGHTEYE.getValue(4), 0);

        Serial.println("IDXL VAL 0 and 1: ");
        Serial.println(LEFTEYE.getValue(0), 0);
        Serial.println(LEFTEYE.getValue(4), 0);
    */

    //because dimming or brightening causes delta MIN/MAX = percent change,
    //read the 1st and the 4th (eg: 0,3) array elements and compare.
    //continue function if min light exceeds threshold, AND delta light NOT dimming.
    if (((RIGHTEYE.getValue(0)) + threshold) < (RIGHTEYE.getValue(3)))  {
      MAXR = RIGHTEYE.getMax();
      MINR = RIGHTEYE.getMin();
      //only do this if MAXR > MINR luminosity
      if ((MAXR) > (MINR)) {
        percentChangeR = ((MAXR - MINR) /  MINR) * 100.0;
        /*
                Serial.print("MAXR: ");
                Serial.println(MAXR);
                Serial.print("MINR: ");
                Serial.println(MINR);
        */
        if (percentChangeR >  TPC) {
          Serial.print("% change RIGHT: ");
          Serial.println(percentChangeR);
          //Serial.println("\nCNT\tMIN\tAVG\tMAX");
          samplesR = 0;
          RIGHTEYE.clear();
          RightFlashDetected = true;
        }
        else {
          RightFlashDetected = false;
        }
      }
    }

    if ((LEFTEYE.getValue(0) + threshold) < (LEFTEYE.getValue(3))) {

      MAXL = LEFTEYE.getMax();
      MINL = LEFTEYE.getMin();

      //only do this if MAXL > MINL luminosity
      if ((MAXL) > (MINL)) {
        percentChangeL = ((MAXL - MINL) /  MINL) * 100.0;
        /*
                Serial.print("MAXL: ");
                Serial.println(MAXL);
                Serial.print("MINL: ");
                Serial.println(MINL);
        */
        if (percentChangeL >  TPC) {
          //Serial.print("% change LEFT: ");
          //Serial.println(percentChangeL);
          //Serial.println("\nCNT\tMIN\tAVG\tMAX");
          samplesL = 0;
          LEFTEYE.clear();
          LeftFlashDetected = true;
        }
        else {
          LeftFlashDetected = false;
        }
      }
    }


    if ((percentChangeR >  TPC) || (percentChangeL >  TPC)) {
      if ((myledFLASHState == false)) {
        //otherFlashReceived = true;
        MyPreviousFlashMillis = MyCurrentFlashMillis;//start new timer
        for (int i = 0; i < NUMPIXELS; i++ )
          leds[i].setRGB(255, 255, 255);  // grb
        FastLED.show();
        myledFLASHState = true;
        otherFlashReceived = true;
      }

      else if (MyCurrentFlashMillis - MyPreviousFlashMillis >= MyOnFlashInterval) {//run LED for 'MyOnFlashInterval'
        if (myledFLASHState == true) {
          for (int i = 0; i < NUMPIXELS; i++ )
            leds[i].setRGB(0, 0, 0);  // grb
          FastLED.show();
          percentChangeR = 0;//this always keeps me blind to self flash until
          percentChangeL = 0;// running avg shows a "pos" % change after 4 samples
          myledFLASHState = false;
        }
      }
    }
  }

  //if firefly gets lonely, then flash on it's own!
  if ((LeftFlashDetected == false) && (RightFlashDetected == false)) {
    otherFlashReceived = false;
  }





  if ((otherFlashReceived == false) && (CurrentOtherFlashReceived - PreviousOtherFlashReceived >= IntervalOtherFlashReceived)) {

    PreviousOtherFlashReceived = CurrentOtherFlashReceived;

    if ((myledFLASHState == false)) {
      //otherFlashReceived = true;
      MyPreviousFlashMillis = MyCurrentFlashMillis;//start new timer
      for (int i = 0; i < NUMPIXELS; i++ )
        leds[i].setRGB(255, 255, + 255); // grb
      FastLED.show();
      myledFLASHState = true;
    }
  }
  else if (MyCurrentFlashMillis - MyPreviousFlashMillis >= MyOnFlashInterval) {//run LED for 'MyOnFlashInterval'
    if (myledFLASHState == true) {
      for (int i = 0; i < NUMPIXELS; i++ )
        leds[i].setRGB(0, 0, 0);  // grb
      FastLED.show();
      percentChangeR = 0;//this always keeps me blind to self flash until
      percentChangeL = 0;// running avg shows a "pos" % change after 4 samples
      myledFLASHState = false;
    }
  }


  if (samplesR >= 4) {
    samplesR = 0;
    RIGHTEYE.clear();
  }

  if (samplesL >= 4) {
    samplesL = 0;
    LEFTEYE.clear();
  }
}

The problem is that with two arduinos trying to each adjust the period then there is no master, and each affects the other.
If you set the time of firing of the first and sense this by the second A an interrupt from the light sensor then the second one will flash with only the interrupt and output 'set on' time after the first. Using an interrupt for this will minimise the delay.

Thank you! Yes, I am trying this without a master which makes it more difficult (but I have a third arduino master blinking at every 4 sec if needed). That is the challenge which is why I was thinking that the 'absence' of a flash may be the way to go. And I could code for a response delay(dwell).
Response time is fairly minimal as is if i use my 'master' blinker.......

Even if I use a master blinker...how do I adjust millis() timing to advance or retard as needed?

By changing the value of the interval variable. For example, 'MyOnFlashInterval'.

Thx Aarg. I think that if I record sensor other flash input time and compare it to expected self flash ON time(how much to, or how much after on time), and then adjust in small increments self ON time within a max range of + or - 50% of total off time between flashes, then I may be able to match OFF times. like a diminishing Sin wave crossing over a 'zero' time line....