While loop not advancing

I'm using I2C to link two Arduino Nano Every boards together. Both boards have a lot of work to do, so the timing of when they exchange data must be coordinated. I'm open to and would appreciate better ideas if anyone has one, but here is the solution I'm trying to implement.

I use a digital I/O line, and when the slave has completed its calculations, it sets the line to HIGH, then sits and waits for the master to send a Wire.requestFrom command. The onRequest handler sends the data, then sets a flag to indicate the data has been sent, releasing the main loop to continue.

#define SLAVE_ADDRESS   20
#define PIN_WAITING      6
bool dataSent = false;
// pin states and other initializing tasks handled in setup()

void loop()
{
    delay(2000);                          // Pretend we're doing something
    showTime("Ready");                    // This method prints the message "Ready" and the current time
                                          // in millis() to the Serial Monitor. Commenting out these
                                          // lines does not help.

    dataSent = false;                     // Set flag to indicate not data has not been sent
    digitalWrite(PIN_WAITING, LOW);       // Turn off LED to indicate data has not been sent
    digitalWrite(SLAVE_ADDRESS, HIGH);    // Put I/O line to HIGH telling master data is ready
    while (dataSent == false) {}          // Wait until data has been sent, then continue
    showTime("Done waiting");             // Tell Serial Monitor that we're moving on again
}

void requestEvent() {

    sendData();                           // Send the data - this is working because the master receives the data
    dataSent = true;                      // Set flag to indicate data was sent
    digitalWrite(PIN_WAITING, HIGH);      // Turn on LED to indicate data was sent
}

As indicated by the output to the serial monitor, the data received by the master, and the status of the LEDs, the slave puts the I/O indicator line to high and waits in the while (dataSent == false) {} loop. The master detects the signal, issues the requestFrom command, the slave responds in requestEvent, the data is sent and received, the dataSent and LED flags are both set to true/HIGH, and the I/O line goes LOW - all is working normally and as intended up to this point.

Except that the while (dataSent == false) {} loop never releases (even though dataSent goes to true) and the program hangs at that point without advancing. Through prior experiments, I know that if dataSent is true before the while (dataSent == false) {} loop starts, it moves past just fine.

To better understand what is happening, I replaced the while (dataSent == false) {} loop with the following:

waitHere = true;
while (waitHere) { 
  if (dataSent) {
      blink(LED_BUILTIN, 200, 2); }       // this blinks LED_BUILTIN 2 times for 200 ms
  else {
      blink(LED_BUILTIN, 400, 3); }       // this blinks 3 time for 400 ms
  waitHere = !dataSent; }

Result - the built-in LED turn on and stays on solid. I also tried commenting out all lines that put data to the serial monitor, but that also did not help.

If I manually trigger the I/O line HIGH by momentarily tapping it to the 5V line, the Master sends a new request, the slave responds, data is exchanged, all within the requestEvent handler, but the main loop remains frozen in place and the built-in LED never flickers. It appears that the main loop is truly frozen even though the requestEvent is still working.

Any thoughts on what is going on here or how to get past this?

I try to be selective in the code I show to keep the post concise, but here is the entire master and slave code:

MASTER:

#include <Wire.h>

#define SLAVE_1   20
short datArray[32];
byte byteArray[32];

// The setup() function runs once each time the micro-controller starts
void setup()
{
    Wire.begin();
    Serial.begin(9600);
    pinMode(LED_BUILTIN, OUTPUT);
    pinMode(SLAVE_1, INPUT);
    blink(500, 4);
}

// Add the main program code into the continuous loop() function
void loop()
{
    readWire();
    delay(1000);
}

void readWire() {
    byte a, b;
    short i, j;
    unsigned int time = 0;
    i = 0;

    Serial.println();
    Serial.println("Waiting...");
    while (digitalRead(SLAVE_1) == LOW) {}
    delay(10);

    Serial.println("Slave ready. Requesting...");
    Wire.requestFrom(SLAVE_1, 12);
    while (Wire.available()) {
        a = Wire.read();
        Serial.print(" "); Serial.print(a);
        i++;
    }
    Serial.println();
    Serial.print(i); Serial.println(" bytes received.");
    delay(200);
}

void blink(short delayTime, short numFlash) {
    for (short i = 1; i < numFlash; i++) {
        digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(delayTime);                       // wait for a second
        digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
        delay(delayTime);                       // wait for a second
    }
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(delayTime);                       // wait for a second
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
}

SLAVE

#include <Wire.h>

#define SLAVE_ADDRESS   20
#define PIN_WAITING      6

short retArray[32];
bool dataSent = false;
bool waitHere = false;
unsigned int time1 = 0;

// The setup() function runs once each time the micro-controller starts
void setup()
{
    Serial.begin(9600);
    Wire.begin(SLAVE_ADDRESS);
    Wire.onRequest(requestEvent);
    pinMode(LED_BUILTIN, OUTPUT);
    pinMode(SLAVE_ADDRESS, OUTPUT);
    pinMode(PIN_WAITING, OUTPUT);
    digitalWrite(SLAVE_ADDRESS, LOW);
    blink(LED_BUILTIN, 500, 5);
    blink(PIN_WAITING, 200, 2);
    dataSent = false;
    digitalWrite(PIN_WAITING, LOW);

    for (int i = 0; i < 6; i++) {
        retArray[i] = -29282 + i * 9999;
    }

    Serial.println("Starting...");
    showTime("Start1");
    showTime("Start2");
}

// Add the main program code into the continuous loop() function
void loop()
{
    delay(2000);
    showTime("Ready");
    // Tell master ready
    dataSent = false;
    digitalWrite(PIN_WAITING, LOW);
    digitalWrite(SLAVE_ADDRESS, HIGH);

    waitHere = true;
    while (waitHere) { 
        if (dataSent) {
            blink(LED_BUILTIN, 200, 2); }
        else {
            blink(LED_BUILTIN, 400, 3); }
        waitHere = !dataSent; }

    showTime("Done waiting");
}

void sendData() {

    short i = 0;
    byte byteArray[2];

    showTime("Sending");
    digitalWrite(SLAVE_ADDRESS, LOW);

    for (i = 0; i < 6; i++) {
        byteArray[0] = (retArray[i] >> 8) & 0xFF;
        byteArray[1] = retArray[i] & 0xFF;
        Wire.write(byteArray, 2);
    }
}

void requestEvent() {

    sendData();
    dataSent = true;
    digitalWrite(PIN_WAITING, HIGH);
    
}

void blink(short pinLED, short delayTime, short numFlash) {
    for (short i = 1; i < numFlash; i++) {
        digitalWrite(pinLED, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(delayTime);                       // wait for a second
        digitalWrite(pinLED, LOW);    // turn the LED off by making the voltage LOW
        delay(delayTime);                       // wait for a second
    }
    digitalWrite(pinLED, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(delayTime);                       // wait for a second
    digitalWrite(pinLED, LOW);    // turn the LED off by making the voltage LOW
}

void showTime(char* locName) {
    unsigned int time2 = millis();

    Serial.print(locName);
    Serial.print(" ");
    Serial.print(time2, DEC);
    Serial.print(" ");
    if (time2 < time1)
        time1 = 0;
    Serial.print(time2 - time1, DEC);
    Serial.println();
    time1 = time2;
}

You probably need to declare dataSent as volatile. The variable can be optimised into a register and teh compiler will not expect it to be changed out of order as it can be by your ISR.

That did the trick, thank you. Didn't know about volatile before, thanks for pointing me in the right direction.