Преобразование задержек в миллиметры для более плавной работы

Мне нужна помощь в преобразовании моего кода, чтобы заменить команду delay() на команду millis() для более плавной работы.

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

Это мои коды

#define aref_voltage 3.3
#include <Servo.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 //ширина OLED-дисплея, в пикселях
#define SCREEN_HEIGHT 32 //высота OLED-дисплея, в пикселях
// Объявление для дисплея SSD1306, подключенного к I2C (контакты SDA, SCL)
#define OLED_RESET     4 // Сброс контакта # (или -1, если используется общий вывод сброса Arduino)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Servo Servo1;
int led = 13;
int sw = 3;
int bz = 14;
int ldr = A1;
int lv = 0;
int rly = 7;
int buttonState = 0;
int servoPin = 8;
// процедура настройки запускается один раз, когда вы нажимаете сброс:
void setup() 
{
  // инициализируйте цифровой вывод в качестве выходного сигнала.
  pinMode(led, OUTPUT);
  pinMode(sw, INPUT);
  pinMode(ldr, INPUT);
  pinMode(bz, OUTPUT);
  pinMode(rly, OUTPUT);
  digitalWrite(rly, LOW);
   Servo1.attach(servoPin);
  analogReference(EXTERNAL);
  Serial.begin(115200);

   if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Адрес 0x3D для 128x64
   Serial.println(F("SSD1306 allocation failed"));
   for(;;);
  }
}
void loop() 
  {
  lv = analogRead(ldr); // считывает значение из LDR
  Serial.print("LDR Value is: ");
  Serial.println(lv);
  delay(100);
  
  buttonState = digitalRead(sw); // Проверьте переключатель 1 или 0
  Serial.println(buttonState);   //Отображение состояния кнопки при
  delay(100);
  
    while ( (lv >= 150) && (buttonState == HIGH) )
{
    delay(1000);
    display.clearDisplay();
    display.setTextSize(2); 
    display.setTextColor(WHITE);
    display.setCursor(0,0);
    display.println("Relay On! ");      // здесь сообщение находится внутри "" 
    delay(1000);
    display.display();

    digitalWrite(rly, HIGH);//реле с
    delay(6000);
    tone(bz, 1500);// зуммер включен с
    delay(3000); 
    digitalWrite(led, HIGH);// светодиодная вспышка с
    delay(3000);
    digitalWrite(led, LOW);
    delay(3000);

    digitalWrite(led, HIGH); // светодиодный 2 кГц
    delay(3000);
    digitalWrite(led, LOW);
    delay(3000);
    tone(bz, 2000); // зуммер с
    delay(3000);
    noTone(bz);

    delay(2000);
    display.clearDisplay();
    display.setTextSize(2); 
    display.setTextColor(WHITE);
    display.setCursor(0,0);
    display.println("Tank 2 Filled! ");      // здесь сообщение находится внутри "" 
    delay(1000);
    display.display();
    
    digitalWrite(rly, LOW); // реле низкого
    digitalWrite(led, HIGH); 
    delay(5000);            // светодиодная вспышка 4 Гц
    digitalWrite(led, LOW);
    delay(5000);

   Servo1.write(45); // Заставить сервопривод перейти на
   delay(1000);
   
   Servo1.write(135); // Заставить сервопривод перейти на 135 градусов
   delay(1000);
   
   Servo1.write(0); // Заставить сервопривод перейти на 0 градусов
   delay(1000);
    break;
}
   if (buttonState == LOW)
  {
    digitalWrite(rly, LOW); // реле выключения
    digitalWrite(led, LOW); //
    delay(500); 
   Servo1.write(0);     // Заставить сервопривод перейти на 0 градусов
   delay(1000);
    noTone(bz); // зуммер выключен

  }
  }

, 👍0

Обсуждение

В чем именно заключается ваша проблема при этом? Вы поняли, как используется функция millis ()? Знаете ли вы, что такое FSM (конечный автомат)? Если нет, то вам действительно следует поискать его, поскольку эта концепция будет невероятно полезна в реализации того, что вы хотите., @chrisl

Коды перемещаются только после истечения задержки, я бы хотел, чтобы коды выполнялись одновременно., @Noel

Да, это очень общее описание того, чего вы хотите. Но это не объясняет, в чем именно заключается проблема. Что вы уже пробовали до сих пор? Есть ли что-то, чего вы не понимаете в концепции неблокирующего кода, как в примере BlinkWithoutDelay (это пример, объясняющий millis ())? В Интернете уже есть множество руководств по этому вопросу, и нам нужно знать, как мы можем помочь вам лучше, чем эти., @chrisl

Вы поставили цель, а не вопрос.Этот сайт не является бесплатным сервисом кодирования. Вам следует выполнить поиск в Google по запросу "Arduino blink без промедления", прочитать одно из различных руководств, которые находит поиск, а затем попытаться самостоятельно выполнить рефакторинг вашего кода., @Duncan C

хорошо, спасибо за информацию, @Noel

ваш код загроможден бесполезными комментариями ... например, `// светодиодная вспышка с частотой 1 Гц" комментирует раздел кода, который не работает с частотой 1 Гц ... то же самое со многими другими комментариями, @jsotola

вам нужно четко продумать , как работает ваш код ... в нынешнем виде он наполнен глупыми командами (прошу прощения за выбор слов, но я не могу придумать никакого другого слова)... пример в псевдокоде: реле ВКЛЮЧЕНО", "подождите 6 секунд", "включен звуковой сигнал", "подождите 3 секунды", "светодиод ВКЛЮЧЕН", "подождите 3 секунды", "светодиод ВЫКЛЮЧЕН", подождите 3 секунды и т.д. И т.п. ..... более простым кодом было бы реле ВКЛЮЧЕНО", "зуммер ВКЛЮЧЕН, светодиод ВКЛЮЧЕН, подождите 3 секунды, светодиод выключен, подождите 3 секунды` и т.д. и т.п., @jsotola

@DuncanC его цель - избавиться от задержек и обеспечить бесперебойную работу кода, я думал, он это объяснил., @Nick Gammon


2 ответа


1

Ваши задержки делают программу прерывистой, поэтому вы должны избавиться от них. :)

У меня есть продолжительное обсуждение этого на моем собственном сайте, но в основном вам нужно перестроиться так, чтобы вы использовали "конечный автомат" и вместо того, чтобы откладывать, отмечали текущее время (используя функцию millis() ), а затем выполняли следующий шаг, когда время истекло.

Например, когда вы печете пирог, вы не прекращаете заниматься им на час и не смотрите в духовку, пока он готовится, не так ли? Вы записываете время и возвращаетесь, когда время истекает.

В одном из руководств по Arduino Blink без задержки есть пример этого.

Пример кода таков (первоначальные комментарии удалены):

// константы не изменятся. Используется здесь для установки контакта:
const int ledPin =  LED_BUILTIN;// номер вывода светодиода

// Переменные изменятся:
int ledState = LOW;             // ledState используется для установки светодиода

// Как правило, вы должны использовать "unsigned long" для переменных, которые удерживают время
// Значение быстро станет слишком большим, чтобы int мог хранить
unsigned long previousMillis = 0;        // сохранит время последнего обновления светодиода

// константы не изменятся:
const long interval = 1000;           // интервал, с которым нужно мигать (миллисекунды)

void setup() {
  // установите цифровой вывод в качестве выходного:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // вот где вы должны поместить код, который должен выполняться постоянно.

  // проверьте, пришло ли время мигать светодиоду; то есть, если разница
  // между текущим временем и последним миганием светодиод больше, чем
  // интервал, с которым вы хотите мигать светодиодом.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // сохраните время последнего мигания светодиода
    previousMillis = currentMillis;

    // если индикатор не горит, включите его и наоборот:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // установите светодиод с помощью значения ledState переменной:
    digitalWrite(ledPin, ledState);
  }
}

Это не будет однократным изменением. Вам нужно переосмыслить, как вы справляетесь с временными задержками.

,

1

Некоторое время назад я написал ответ на другой вопрос о неблокирующем программировании, используя функции "maybe-do" - функции, которые, возможно, что-то делают, если выполняются условия для этого, обычно по истечении временного интервала. отличная статья @nickgammon (ссылка в его ответе на этот вопрос) показывает аналогичную структуру, которая оценивает условие внутри loop(), а не внутри функции maybe-do / action, как я сделал в своем ответе. Оба способа работают одинаково хорошо.

Хорошим упражнением будет поиграть с примером скетча BlinkWithoutDelay и даже написать пару собственных скетчей, которые соответствуют этому общему формату. Но скоро вы устанете писать все эти выражения if( (millis() - previousMills) >= someInterval){ и бухгалтерию различных предыдущихмиллисекунд, которые к ним прилагаются, не говоря уже об отладке результатов неизбежных опечаток! )-:

Как только вы освоитесь с:

is (current time - time when we previously acted) >= action interval{
   do the action;
   save the current time as the previous time;
}
// опять же, для следующего действия
// еще раз, для каждого оставшегося действия

, рассмотрите возможность использования библиотеки таймеров, такой как SimpleTimer. Его задача состоит в том, чтобы вести всю эту бухгалтерию за вас и вызывать ваши функции действий в нужное время.

Вам нужно знать несколько вещей, чтобы использовать его - некоторые из тех же немногих вещей, которые вам нужно было знать, чтобы писать код предыдущим способом, но более утомительные части будут сделаны за вас:

  • Как сделать набор таймеров;
  • Как создать функцию действия (называемую "функцией обратного вызова");
  • Как установить интервал (одноразовый или повторяющийся), через который будет вызываться функция действия или обратного вызова.
  • Как запустить таймеры, чтобы они могли выполнять свою работу.

Как сделать таймеры:

  • Во-первых, вам нужна библиотека: посетите эту страницу github и нажмите зеленую кнопку "Загрузить код" (на самом деле это стрелка вниз, а не слово "загрузить"). В появившемся меню нажмите "Загрузить ZIP".
  • Откройте файл, который вы получаете, "Arduino-SimpleTimer-master.zip " (как вы это сделаете, зависит от операционной системы вашего компьютера). В ZIP-файле вы найдете следующую структуру папок:
    Arduino-SimpleTimer-master --- SimpleTimer --- (некоторые папки и файлы)
    Перетащите папку "SimpleTimer" в папку "библиотеки" в папке Arduino sketch. Отныне эта библиотека может обслуживать каждый набросок, который вы напишете.
  • Добавьте следующий включаемый файл в верхнюю часть вашего скетча: #включить <SimpleTimer.h>
  • Создайте глобальный набор таймеров: SimpleTimer myTimers; // создает 10 таймеров и делает их доступными для использования.

Как создать функцию обратного вызова - функцию, которая запускает ваш светодиод, шаговый двигатель или любое другое действие, которое вам нужно выполнить в определенное время:

  • Функция обратного вызова делает все, что необходимо для выполнения временной задачи, и ничего больше. Например, он может выключить светодиод, если он включен, или включить, если он выключен.
  • Функция действия не принимает аргументов и не возвращает значения. Он должен иметь следующий тип:
    void переключенный (void);
    Конечно, вы можете называть свои функции так, как вам нравится.

Как установить интервал. Существует несколько способов, но вы, вероятно, будете использовать эти два почти для всего - повторяющихся или одноразовых:

  • myTimers.setTimeout(time, toggleLED);
    устанавливает одноразовый таймер, который будет вызывать мою функцию `toggleLED ()' только один раз, через миллисекунды "time". Он возвращает номер таймера, который будет использоваться. В основном вы можете пока игнорировать это значение.
  • myTimers.setInterval(time, toggleLED);
    устанавливает повторяющийся таймер, который будет продолжать вызывать мою функцию toggleLED() каждые "временные" миллисекунды, пока выполняется скетч. Он также возвращает номер таймера, и опять же, на данный момент вы можете игнорировать это значение.

Как запустить таймеры.

  • Библиотеку таймеров необходимо вызывать часто, чтобы ваши функции вызывались с достаточно точными интервалами. Ваша функция loop() должна вызывать myTimers.run() при каждом запуске.
  • loop() не должен делать ничего, что занимает очень много времени, и не должен вызывать какую-либо функцию, которая может занять очень много времени. Длительные задачи будут нарушать точность времени, с которым вызываются ваши функции обратного вызова.
  • Ваша функция цикла должна выглядеть следующим образом:
void loop(){
   myTimers.run();
   // может быть, какой-нибудь другой быстрый код здесь
}

Функция .run() проверяет каждый из ваших таймеров и для каждого, время которого истекло, вызывает свою функцию обратного вызова (ту, которую вы указали при вызове .setInterval() или .setTimeout()) . Вот как библиотека таймеров выполняет свою работу и почему вам нужно часто вызывать ее.



Теперь давайте посмотрим, как собрать все это вместе в примере программы для мигания 2 светодиодов с разными интервалами, используя SimpleTimers:

// Демонстрационная программа SimpleTimer
// Мигайте 2 светодиода с разной частотой в течение 15 секунд, затем выключите оба светодиода и остановитесь.

#include <Arduino.h>
#include <SimpleTimer.h>

/*****< Definitions >*****/
#define LED1    13  // встроенный светодиод
#define LED2    12  // внешний светодиод



/*****< External and Global Declarations >*****/
SimpleTimer myTimers;       // определяет набор таймеров
bool doHalt = false;            // флаг, указывающий loop() на остановку


/*****< Local Function Prototypes >*****/
void toggleLED1(void);      // функция для переключения состояния LED1
void toggleLED2(void);      // функция для переключения состояния LED2
void setHalt(void);         // функция для установки флага остановки

void setup()
{
    // Установите режимы вывода для наших двух светодиодов:
   pinMode(LED1, OUTPUT);
   pinMode(LED2, OUTPUT);

   // Выключите светодиоды:
   digitalWrite(LED1, LOW);
   digitalWrite(LED2, LOW);

    // Настройка интервалов мигания
    myTimers.setInterval(800L, toggleLED1); // запуск таймера для медленного мигания светодиода1
    myTimers.setInterval(300L, toggleLED2); // запуск таймера для быстрого мигания светодиода2

    // Остановите программу через 30 секунд.
    myTimers.setTimeout(15*1000L, setHalt); // запустить таймер для остановки программы
}


void loop() {
    myTimers.run();                 // продолжайте вызывать таймеры как можно чаще

    if( doHalt ){                       // если установлен флаг остановки,
        // Выключите светодиоды:
       digitalWrite(LED1, LOW); // выключите светодиоды,
    digitalWrite(LED2, LOW);

    // Висеть здесь, ничего не делая
        while(1)                            // и зависнуть здесь навсегда.
            ;
    }
}

// Переключить состояние светодиода1
void toggleLED1(void){
    bool isOn = digitalRead(LED1);
    
    digitalWrite(LED1, !isOn);
}


// Переключить состояние светодиода2
void toggleLED2(void){
    bool isOn = digitalRead(LED2);
    
    digitalWrite(LED2, !isOn);
}


// Установите флаг остановки на stop 'loop()'
void setHalt(void){
    doHalt = true;
}

/***  end  ***/

Эта программа делает все, что делает BlinkWithoutDelay, но без учета и механики вызова ваших функций действия. Обо всем этом заботится библиотека SimpleTimer, позволяя вам свободно сосредоточиться на том, для чего предназначена ваша программа.

,