Посоветуйте простой планировщик задач

В общем переписал код. Пока только для 2 датчиков, остальные добавлю по аналогии.
Разбил работу с датчиками на этапы между задержками. Сами задержки вынес в основной цикл. На скорую руку получилось следующее:

#include "bmp280.h"
#include "aht20.h"

#define _I2C_CLOCK_         200000L
#define _SERIAL_BAUD_RATE_  19200

TwoWire i2c;
BMP280 bmp(&i2c, BMP280_I2C_ADDR_SEC);
AHT20 aht(&i2c, AHT20_I2C_ADDR_PRIM);

void aht_variables_init(void);
int8_t aht_begin(uint8_t &delay_in_ms);
int8_t aht_init(uint8_t &delay_in_ms);
int8_t aht_start(uint8_t &delay_in_ms);
int8_t aht_end(uint8_t &delay_in_ms);

int8_t (*aht_states[])(uint8_t &) = { aht_begin, aht_init, aht_start, aht_end };
int8_t (*aht_exec)(uint8_t &);

void bmp_variables_init(void);
int8_t bmp_begin(uint8_t &delay_in_ms);
int8_t bmp_start(uint8_t &delay_in_ms);
int8_t bmp_end(uint8_t &delay_in_ms);

int8_t (*bmp_states[])(uint8_t &) = { bmp_begin, bmp_start, bmp_end };
int8_t (*bmp_exec)(uint8_t &);

int8_t aht_status, bmp_status;
uint8_t aht_state_id, bmp_state_id;
uint8_t aht_delay_ms, bmp_delay_ms;
uint8_t aht_iteration_cnt, bmp_iteration_cnt;
uint32_t aht_start_time_ms, bmp_start_time_ms;  
value_t aht_temperature, aht_humidity;
value_t bmp_temperature, bmp_pressure;


void setup() {
  Serial.begin(_SERIAL_BAUD_RATE_);
  i2c.begin(_I2C_CLOCK_);
}

void loop() {
  uint32_t msec = millis();

  aht_variables_init();
  bmp_variables_init();

  while (aht_exec != nullptr || bmp_exec != nullptr) {
    // working with AHT20 sensor
    if (aht_exec != nullptr) {
      if (aht_delay_ms == 0)  {
        // executing the current operation
        // returns the exit code (status) and the duration of the delay
        aht_status = aht_exec(aht_delay_ms);
        if (aht_status == AHT20_OK)  {
          // iteration completed successfully
          if (++aht_state_id < sizeof(aht_states) / sizeof(aht_states[0])) {
            // next operation
            aht_exec = aht_states[aht_state_id];
          } else  {
            // all operations completed, read measured values
            aht_status = aht.read_values(aht_temperature, aht_humidity);
            // finish working with the sensor
            aht_exec = nullptr;
          }
        } else  {
          if (aht_status == AHT20_E_NEXT_ITERATION) {
            // next iteration of the current operation
            if (++aht_iteration_cnt >= AHT20_MAX_ITER_CNT)  {
              // measurement timeout exceeded, no reason to continue
              aht_status = AHT20_E_MEASUREMENT_TIMEOUT;
              aht_exec = nullptr;
            } else  {
              // delay start time
              aht_start_time_ms = millis(); 
            }
          } else  {
            // sensor error, no reason to continue
            aht_exec = nullptr;
          }
        }
      } else  {
        // delay end check
        if ((uint8_t) (millis() - aht_start_time_ms) >= aht_delay_ms)  {
          // delay completed, reset the variable
          aht_delay_ms = 0;
        }
      }
    }
    
    // working with BMP280 sensor
    if (bmp_exec != nullptr) {
      if (bmp_delay_ms == 0)  {
        // executing the current operation
        // returns the exit code (status) and the duration of the delay
        bmp_status = bmp_exec(bmp_delay_ms);
        if (bmp_status == BMP280_OK)  {
          // iteration completed successfully
          if (++bmp_state_id < sizeof(bmp_states) / sizeof(bmp_states[0])) {
            // next operation
            bmp_exec = bmp_states[bmp_state_id];
          } else  {
            // all operations completed, read measured values
            bmp_status = bmp.read_values(bmp_temperature, bmp_pressure);
            // finish working with the sensor
            bmp_exec = nullptr;
          }
        } else  {
          if (bmp_status == BMP280_E_NEXT_ITERATION) {
            // next iteration of the current operation
            if (++bmp_iteration_cnt >= BMP280_MAX_ITER_CNT)  {
              // measurement timeout exceeded, no reason to continue
              bmp_status = BMP280_E_MEASUREMENT_TIMEOUT;
              bmp_exec = nullptr;
            } else  {
              // delay start time
              bmp_start_time_ms = millis(); 
            }
          } else  {
            // sensor error, no reason to continue
            bmp_exec = nullptr;
          }
        }
      } else  {
        // delay end check
        if ((uint8_t) (millis() - bmp_start_time_ms) >= bmp_delay_ms)  {
          // delay completed, reset the variable
          bmp_delay_ms = 0;
        }
      }
    }
  }
  
  msec = millis() - msec;
  Serial.printf("Time passed: %lu msec\r\n", msec);

  if (aht_status == AHT20_OK)  {
    Serial.print("Temperature: "); Serial.print(aht_temperature.value, 2); Serial.println(" *C");
    Serial.print("Humidity: "); Serial.print(aht_humidity.value, 2); Serial.println(" %");
  } else {
    Serial.printf("AHT20 error: %d\r\n", aht_status);
  }

  if (bmp_status == BMP280_OK)  {
    Serial.print("Temperature: "); Serial.print(bmp_temperature.value, 2); Serial.println(" *C");
    Serial.print("Pressure: "); Serial.print(bmp_pressure.value, 2); Serial.println(" mmHg");
  } else {
    Serial.printf("BMP280 error: %d\r\n", bmp_status);
  }
  Serial.println();

  delay(2000);
}

void aht_variables_init(void)
{
  aht_status = 0;
  aht_state_id = 0;
  aht_delay_ms = 0;
  aht_iteration_cnt = 0;
  aht_start_time_ms = 0;  
  aht_exec = aht_states[aht_state_id];
}

void bmp_variables_init(void)
{
  bmp_status = 0;
  bmp_state_id = 0;
  bmp_delay_ms = 0;
  bmp_iteration_cnt = 0;
  bmp_start_time_ms = 0;  
  bmp_exec = bmp_states[bmp_state_id];
}

Работает, общее время опроса датчиков сократилось в разы. Всем спасибо.

Это реально жуть! Не потому. что неправильно. Я тебе вполне верю, что все нормально.
Но слышал ли ты о том, что код должен читаться?

Вот смотри, как "бездумно превращать в автомат несколько линейных потоков с delay().
Если поток нелинеен, то догадаешься сам? В каждой строке свича у тебя можно задать номер следующей инструкции и если нужно - сделать задержку. Думать о том, “как соединить два скетча с делеями??” реально не нужно. Нужно просто быть аккуратным и внимательным. И главное - это можно ПРОЧЕСТЬ! Вот так, как газету в сортире, по строчкам, сверху - вниз!

void setup() {
  Serial.begin(115200);
  // put your setup code here, to run once:

}

/*
loop1() эквивалентен полностью такому:
  {
      Serial.print("p 1 step 0 no delay; millis=");
      Serial.println(millis());
      Serial.print("p 1 step 1  delay 700; millis=");
      Serial.println(millis());
      delay(700);
      Serial.print("p 1 step 3 delay 500; millis=");
      Serial.println(millis());
      delay(500);
  }
loop2() эквивалентен полностью такому:
      Serial.print("p 2 step 0 no delay; millis=");
      Serial.println(millis());
      Serial.print("p 2 step 1  delay 1000; millis=");
      Serial.println(millis());
      delay(1000);
      Serial.print("p 2 step 3 delay 400; millis=");
      Serial.println(millis());
      delay(400);
      Serial.print("p 2 step 5 delay 600; millis=");
      Serial.println(millis());
      delay(600);
*/



void loop1() {
  static byte Step = 0;
  static uint32_t TimeMark = 0;

  switch (Step) {
    case 0:
      Serial.print("p 1 step 0 no delay; millis=");
      Serial.println(millis());
      Step ++;
      break;
    case 1:
      Serial.print("p 1 step 1  delay 700; millis=");
      Serial.println(millis());
      Step ++;
      TimeMark = millis();
      break;
    case 2:
      if (millis() - TimeMark > 700) Step++;
      break;
    case 3:
      Serial.print("p 1 step 3 delay 500; millis=");
      Serial.println(millis());
      Step ++;
      TimeMark = millis();
      break;
    case 4:
      if (millis() - TimeMark > 500) Step++;
      break;
    default: 
    Step = 0; 
    break;
  }
}

void loop2() {
  static byte Step = 0;
  static uint32_t TimeMark = 0;

  switch (Step) {
    case 0:
      Serial.print("p 2 step 0 no delay; millis=");
      Serial.println(millis());
      Step ++;
      break;
    case 1:
      Serial.print("p 2 step 1  delay 1000; millis=");
      Serial.println(millis());
      Step ++;
      TimeMark = millis();
      break;
    case 2:
      if (millis() - TimeMark > 1000) Step++;
      break;
    case 3:
      Serial.print("p 2 step 3 delay 400; millis=");
      Serial.println(millis());
      Step ++;
      TimeMark = millis();
      break;
    case 4:
      if (millis() - TimeMark > 400) Step++;
      break;
    case 5:
      Serial.print("p 2 step 5 delay 600; millis=");
      Serial.println(millis());
      Step ++;
      TimeMark = millis();
      break;
    case 6:
      if (millis() - TimeMark > 600) Step++;
      break;
    default: 
    Step = 0; 
    break;
  }
}

void loop() {

  loop1();
  loop2();

}

Согласен с замечанием, читается трудно. Перепишу на switch. Каждый датчик в отдельный цикл заверну.
Спасибо

Просто держи в голове, как “Отче Наш” или как “Шма Исраэль”, что там тебе ближе, факт:
каждый case в switch ДОЛЖЕН выполняться моментально. Каждый должен быть не блокирующим!!!
А так - это автоматом пишется по любой картинке алгоритма из “крадратиков”. Для начала - сойдет. При таком подходе очень трудно накосячить.