#define     LED_PIN        LED_BUILTIN  // пин встроенного светодиода
#define     BLINK_INTERVAL 1000         // чачтота мигания

void setup() {                         // начальное состояние встроенного светодиода
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(115200);
}

void loop()
{
  uint8_t delay_break_flag = 0; // внутренний флаг для delay с выходом, нужно вынести в глобальные переменные, если используется не только в loop()
//        76543210  
//        |||||||| 
//        |||||||+---  0: произошел выход из delay() не дождавшись окончания интервала 
//        ||||||+----  1: Flag 1
//        |||||+-----  2: Flag 2
//        ||||+------  3: Flag 3
//        |||+-------  4: Flag 4
//        ||+--------  5: Flag 5
//        |+---------  6: Flag 6
//        +----------  7: внутренний флаг для передачи в my_yield, что delay стартовала, можно проинициализировать какие то переменные

  Serial.println("loop_begin------");

  Serial.print("delay(30 000) start: ");
  Serial.println(millis());
  
  delay(30000);    // обычный delay() на 30сек но блокировки не происходит, светодиод моргает
  
  Serial.print("delay(30 000) end: ");
  Serial.println(millis());

  Serial.print("delay(60 000) start: ");
  Serial.println(millis());

  delay(600000, &delay_break_flag);  // delay() с возможностью выти из него недожидаясь окончания интервала по какому то событию
  
  Serial.print("delay(60 000) end: ");
  Serial.println(millis());
  
  if (delay_break_flag & 1)  // если вышли из delay() раньше времени
    Serial.println("Delay break");
  
  if (delay_break_flag & 2)  // обработчиком событий был взведен какой то флаг например смены стсояния. состояние смотрим в глобальной переменной, ее в примере нет
    Serial.println("Flag detected");
  
  Serial.println("loop_end--------\n");
}


void delay(uint32_t ms, uint8_t *ptr) // собственно чуть подправленный delay с возможностью прерывания
{
    uint32_t start = micros();
    
    if (ptr)              // усли тлько зашли в new delay()
      *ptr = 0x80;       // взведем флаг начала delay()
    
    while (ms > 0) {
 
        my_yield(ptr);

        if (ptr && *ptr & 1)  // взведен флаг выхода из delay в my_yield()  
          break;

        while ( ms > 0 && (micros() - start) >= 1000) {
            ms--;
            start += 1000;
        }
    }
}

void yield() // если кто то вызвал yeld (не новый delay) вызываем my_yield() с нулевым указателем в параметре
{
  my_yield(NULL);  // вызовем my_yield() с нулевым указателем
}

void my_yield(uint8_t *ptr)
{
  uint16_t          currentMillis = (uint16_t) (millis() & 0xFFFFL);  // у меня короткие интервалы 
  static uint16_t   previousMillis = 0;       // время последнего обновления состояния сетодиода - мигания
  static int        counter = 0;
  static uint8_t    ledState = LOW;          // состояние светодиода


  if (ptr && *ptr & 0x80)  // это первый вход в my_yield() из delay(ххх,с указателем на флаг)(можно проинициализировать какие то внутренние переменные - например счетчик counter=0;
  {
    *ptr = 0;   // сбросим флаг  первого входа
    counter=0;  // проинициализируем переменные
  }
  
  if (currentMillis - previousMillis >= BLINK_INTERVAL) // --------------------------обработка каких то событий
  {
    previousMillis = currentMillis;

    // мигаем светодиодом, счтаем мигания для примера
    
    ledState =! ledState;
    if (ledState == HIGH) {
      Serial.print("Tic");
    } else {
      Serial.println("-tac");
      counter++;
    }
    digitalWrite(LED_PIN, ledState);

    if (counter == 10) // на 10 мигании
    {
      counter=0; // сбросим счетчик
      if (ptr)   // если указатель не нулевой, попали из delay с выходом
        *ptr |= (uint8_t) 1;    // взведем флаг выхода
        *ptr |= (uint8_t) 2;    // для примера взведем флаг изменения состояния, состояние передаем через глобальную переменную
    }
  }                                               // --------------------------обработка каких то событий
  
}