Правильный способ завершения цикла при необходимости?

Я новичок в Arduinos. В настоящее время я пытаюсь изменить скрипт здесь, который в данный момент просто включает или выключает светодиод в зависимости от того, какой HTTP-запрос GET он получает. Однако я хочу изменить это так, чтобы светодиод модулировал включение/выключение при получении запроса On HTTP GET, а затем просто выключался полностью при получении запроса Off HTTP GET. Однако я нашел два противоречивых метода, которые позволяют этого добиться; первый из них:

int value = LOW;
    if (request.indexOf("/LED=ON") != -1)  {
      value = HIGH;
      do
      {
      digitalWrite(ledPin, HIGH);
      delay(3000);
      digitalWrite(ledPin, LOW);
      delay(5000);
      value = HIGH;
      } while (value = HIGH);
    }
    if (request.indexOf("/LED=OFF") != -1)  {
      digitalWrite(ledPin, LOW);
      value = LOW;
    }

В то время как другой:

  int value = LOW;
    if (request.indexOf("/LED=ON") != -1)  {
      value = HIGH;
      void loop() {
      digitalWrite(ledPin, HIGH);
      delay(3000);
      digitalWrite(ledPin, LOW);
      delay(5000);
      value = HIGH;
      }
    }
    if (request.indexOf("/LED=OFF") != -1)  {
      digitalWrite(ledPin, LOW);
      value = LOW;
    }

Как вы можете видеть, первый использует do...while, а второй использует loop(). У меня есть ощущение, что первый из двух скриптов был бы более подходящим, так как он должен выключаться, как только value устанавливается в LOW (т. е. когда запрашивается выключение)

Однако я был бы очень признателен, если бы кто-нибудь мог просмотреть оба скрипта и сказать мне, какой из них является правильным способом добиться такого рода отключения при запросе HTTP GET Off (если хотя бы один из них является правильным способом сделать это!).

Заранее спасибо за помощь,

С наилучшими пожеланиями, Том

, 👍3

Обсуждение

К сожалению, они оба совершенно неверны. Вместо этого используйте конечный автомат., @Ignacio Vazquez-Abrams

@IgnacioVazquez-Abrams Спасибо за ваш ответ. Это позор, как бы я это сделал?, @Tom

как происходит выход из цикла do...while в первом фрагменте?, @jsotola

Я предположил, что когда выполняется запрос off get, вы можете видеть из скрипта, что «value» устанавливается в LOW, и когда это условие выполняется, цикл должен выйти., @Tom

изучите примеры кодов, которые являются частью Arduino IDE .... обратите внимание на использование setup() и loop() ....... очень внимательно посмотрите на скетч flashWithoutDelay ..... в своей программе установите флаг при получении HTTP-запроса ..... затем в коде, который следует за проверкой HTTP, замигайте светодиодом, если флаг установлен .... выключите светодиод, если флаг не установлен, @jsotola

также посмотрите, как отформатирован исходный код... отступы и расположение фигурных скобок {} ..... затем посмотрите на свой код... ваш код плохо отформатирован, поэтому его труднее читать (трудно следить за ходом программы), @jsotola

С do...while value никогда не меняется на LOW, так как код, который его изменяет, находится вне do...while и не выполняется, пока value равно HIGH. попробуйте переместить int value... за пределы функции цикла (т. е. глобальной переменной) и удалить do...while (оставить только код внутри него). Не уверен, что проверка http-запросов только каждые 8 секунд сработает., @Gerben

@jsotola Спасибо за ваш ответ. Это звучит как отличное решение для этой проблемы. Я искал этот пример скетча и не смог его найти. Если это возможно и если это не слишком большая просьба, не могли бы вы показать, как я это сделаю в формате ответа, так как у меня очень мало опыта программирования с Arduino IDE. Спасибо, @Tom

@Gerben Спасибо за ваш комментарий. Я понимаю решение, которое вы пытаетесь предложить, но я согласен, что я считаю, что проверка запроса каждые 8 секунд не сработает, поскольку сам запрос должен быть сделан точно в нужное время. Можете ли вы придумать какое-либо другое решение?, @Tom

Сетевые компоненты работают параллельно с вашим кодом, так что это может сработать., @Gerben

@Gerben Я попробую это немного позже. Может быть, лучше иметь два отдельных цикла, работающих одновременно, если это вообще возможно? То есть один цикл, который постоянно готов к новому запросу get, и другой, который может обрабатывать цикл do...while, поскольку первый цикл затем может свободно изменять "значение" в любое время, что может остановить второй цикл, если вы понимаете, о чем я?, @Tom

Я не знаю, можно ли (просто) сделать многопоточность на ESP. Лучшим решением будет сделать мигание на основе значения миллис. Таким образом, вам не нужны никакие задержки. Смотрите страницу Arduino «мигание без задержки»., @Gerben


1 ответ


Лучший ответ:

3

Как отметили Игнасио Васкес-Абрамс и jsotola, вам нужно использовать конечный автомат, как в Мигание без задержки Учебник по Arduino. Ваш будет немного сложнее, потому что вы нужен способ включать и выключать его, и поскольку вы используете разные кнопки включения и выключения периоды выключения. Вот пример кода, который абстрагирует логику мигания на три функции:

  • update_led() отвечает за мигание, и его нужно вызывать довольно часто, чтобы добиться плавного мигания
  • start_blinking() и stop_blinking(), как следует из их названий, являются используется для включения и выключения мигающего конечного автомата.
const uint32_t ON_TIME  = 3000;
const uint32_t OFF_TIME = 5000;

bool led_blinking;     // мигаем ли мы светодиодом?
bool led_on;           // светодиод в данный момент горит?
uint32_t last_toggle;  // последний раз переключался во время мигания

void update_led() {
    if (!led_blinking) return;
    uint32_t now = millis();
    if (led_on && now - last_toggle >= ON_TIME) {
        digitalWrite(ledPin, LOW);
        led_on = false;
        last_toggle = now;
    }
    if (!led_on && now - last_toggle >= OFF_TIME) {
        digitalWrite(ledPin, HIGH);
        led_on = true;
        last_toggle = now;
    }
}

void start_blinking() {
    digitalWrite(ledPin, HIGH);
    led_blinking = true;
    led_on = true;
    last_toggle = millis();
}

void stop_blinking() {
    digitalWrite(ledPin, LOW);
    led_blinking = false;
    led_on = false;
}

Затем вам нужно вызвать эти функции из loop() следующим образом:

void loop() {
    // При необходимости оставьте светодиод мигать.
    update_led();
    ...
    if (request.indexOf("/LED=ON") != -1)  {
        start_blinking();
    }
    if (request.indexOf("/LED=OFF") != -1)  {
        stop_blinking();
    }
    ...
}

Изменить, в ответ на комментарии jsotola:

Отвечая на вопрос, я изначально хотел сделать BlinkingLed класс, с функциями в качестве его открытого интерфейса и переменные частные. Я сопротивлялся искушению на том основании, что, учитывая очевидный уровень знаний C++ у OP, который может оказаться хорошим над его головой. Вместо этого я использовал документацию как неформальный способ разделение публичного и личного: вы должны использовать только что документировано, т.е. функции. Вы не «срезаете путь» ваша программа и очистите led_blinking напрямую”.

Вы, конечно, можете сделать переменную led_blinking частью публичный интерфейс. Это может быть даже хорошим упрощением, так как вы не больше не нужна функция старт/стоп. Но тогда вы теряете контроль над начальная фаза мигания. Это может и не быть проблемой, но если вы хотите, чтобы светодиод включился сразу после установки led_blinking в значение true, то вам придется сделать что-то вроде

void update_led() {
    uint32_t now = millis();
    if (!led_blinking) {
        digitalWrite(ledPin, LOW);
        led_on = false;
        last_toggle = now - OFF_TIME;  // исправить начальную фазу
        return;
    }
    ...
}

Обратите внимание, что вычитание может привести к переполнению, но это не проблема, потому что он следует правилам модульной арифметики.

Мне на самом деле нравится этот вариант, вся логика в одном функция. Таким образом, переменные led_on и last_toggle могут быть сделаны статический локальный по отношению к функции, который обеспечивает настоящую инкапсуляцию без класс. Однако, с педагогической точки зрения, я думаю, что мой первоначальный предложение легче понять.

,

Большое спасибо за ваш ответ! Это именно то, что я искал! Я ценю всю помощь, которую мне оказали люди в комментариях, но я действительно ценю тот факт, что вы смогли разбить проблему на понятные части и предоставить хорошо структурированный ответ. Спасибо!, @Tom

измените эту строку if (!led_blinking) return; так, чтобы она выключила светодиод, а затем вернулась, @jsotola

@jsotola Я не понимаю, что вы имеете в виду. Вы хотите сказать, что эта строка, которую он написал, неверна? Если так, то как должна выглядеть эта строка?, @Tom

нет, я не говорю, что это было написано неправильно... светодиод, безусловно, перестанет мигать, когда led_blinking==false ..... если led_blinking очищается (устанавливается в false) функцией stop_blinking(), то светодиод будет выключен ..... однако, если вы сделаете сокращение в своей программе и очистите led_blinking напрямую, без вызова stop_blinking(), то светодиод перестанет мигать, но может быть включен, @jsotola

@jsotala А, понятно, это имеет смысл. Как именно мне адаптировать этот код, чтобы при led_blinking==false светодиод полностью выключался, даже если он в данный момент включен? Не могли бы вы опубликовать код, который может помочь решить эту проблему? Большое спасибо :), @Tom

я не понимаю, почему вы просите код... у вас уже есть строка if (!led_blinking), которую вам дал Эдгар Бонет... и вы ранее написали код выключения в том, что вы опубликовали... посмотрите на второй оператор if... просто вставьте код для выключения светодиода перед командой return, @jsotola

@jsotola: Я отвечаю на ваши комментарии в редактировании своего ответа., @Edgar Bonet