Преобразование задержек в миллиметры для более плавной работы
Мне нужна помощь в преобразовании моего кода, чтобы заменить команду 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); // зуммер выключен
}
}
@Noel, 👍0
Обсуждение2 ответа
Ваши задержки делают программу прерывистой, поэтому вы должны избавиться от них. :)
У меня есть продолжительное обсуждение этого на моем собственном сайте, но в основном вам нужно перестроиться так, чтобы вы использовали "конечный автомат" и вместо того, чтобы откладывать, отмечали текущее время (используя функцию 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);
}
}
Это не будет однократным изменением. Вам нужно переосмыслить, как вы справляетесь с временными задержками.
Некоторое время назад я написал ответ на другой вопрос о неблокирующем программировании, используя функции "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, позволяя вам свободно сосредоточиться на том, для чего предназначена ваша программа.
- 5 В MCP2515 в 3.3V Teensy. Как уменьшить напряжение MISO
- Можно ли сделать FastLED быстрее?
- Подключение семисегментного дисплея с общим анодом к сдвиговому регистру
- Как использовать SPI на Arduino?
- Как решить проблему «avrdude: stk500_recv(): programmer is not responding»?
- Как создать несколько запущенных потоков?
- Как подключиться к Arduino с помощью WiFi?
- avrdude ser_open() can't set com-state
В чем именно заключается ваша проблема при этом? Вы поняли, как используется функция
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