Как выполнять многозадачность с помощью arduino, например, обновлять данные, а также проверять состояние?
Здесь данные обновляются каждые 2 секунды. Но когда условие истинно, тогда запускается процесс клапана и двигателя, и данные обновляются только после завершения этой части (т. Е. Данные обновляются через 25 секунд). Я хочу, чтобы обновление данных и проверка состояния выполнялись одновременно.
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define ONE_WIRE_BUS 4
#define TEMPERATURE_PRECISION 9
#define motor 6
#define valve 7
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
const float thld = 35;
#define SCREEN_WIDTH 128 //ширина OLED-дисплея, в пикселях
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin (115200);
pinMode(motor, OUTPUT);
pinMode(valve, OUTPUT);
delay(10);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Адрес 0x3D для 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000);
display.setTextColor(WHITE);
}
void loop() {
sensors.requestTemperatures();
float temp = sensors.getTempCByIndex(0);
Serial.println(temp);
Serial.println((char)176);
Serial.println("C | ");
if ( temp > thld)
{
digitalWrite (valve, LOW);
delay (5000);
digitalWrite (motor, LOW);
delay (10000);
digitalWrite (motor, HIGH);
delay (6000);
digitalWrite (valve, HIGH);
delay (2000);
}
else
{
digitalWrite (motor, HIGH);
digitalWrite (valve, HIGH);
}
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.println("Temperature");
display.setCursor(0, 30);
display.println("Soil & Water Engg.");
display.setTextSize(2);
display.setCursor(0,10);
display.println(temp);
display.setCursor(75, 10);
display.println("C");
display.setCursor(0,40);
display.display();
delay (2000);
}
@Jashanjot Kaur, 👍3
Обсуждение3 ответа
Вам нужно подумать об этом с помощью реального аналога.
Мне нравится использовать вареное яйцо...
Есть три способа сварить яйцо:
Блокирующая операция
- Положите яйцо в кипящую воду
- Стойте и смотрите на него минуты три или около того
- Достаньте яйцо из воды и съешьте его
Работа таймера
- Положите яйцо в кипящую воду
- Установите таймер на 3 минуты
- Уходи и займись чем-нибудь другим
- Когда зазвонит будильник достаньте яйцо и съешьте его
Опрос времени
- Положите яйцо в кипящую воду и запишите текущее время
- Идите и займитесь чем-нибудь другим, время от времени поглядывая на часы и сравнивая время с тем, когда вы начали
- По истечении трех минут достаньте яйцо и съешьте его
В настоящее время вы используете операцию блокировки, когда вы стоите, уставившись на яйцо, пока не придет время что-то делать. Вам нужно изменить свой код, чтобы использовать один из других методов.
В большинстве случаев метод опроса по времени достаточно хорош. У вас может возникнуть некоторое дрожание, когда все, что вы делаете, вызывает задержку, прежде чем вы взглянете на часы, но это редко является проблемой. Для большей точности (лучше для проверки с более высокой частотой) вы используете таймер, который запускает прерывание, чтобы вы всегда выполняли действия с более точными интервалами.
В IDE есть пример под названием BlinkWithoutDelay
, который реализует опрос времени очень упрощенным способом - запишите время, затем сравните это время и текущее время, а по истечении времени измените состояние светодиода, записав новое время.
Общий подход к неблокирующему коду заключается в использовании millis() и сравнении длительностей:
bool active = false;
unsigned long start = 0;
const unsigned long duration = 25000; // 25 секунд
void loop() {
unsigned long now = millis();
if (start_condition) {
active = true;
start = now;
turn_on();
}
if (active && now - start >= duration) {
active = false;
turn_off();
}
}
Правильно написанный неблокирующий код никогда не использует delay(), а вместо этого записывает временные метки и сравнивает длительности, чтобы понять, когда что-то делать.
Одна ловушка для новичков: никогда не сравнивайте время, только продолжительность! Поскольку millis() обтекается (через 2 ^ 32 мс, что составляет ~ 49,7 дней), у вас может быть время начала , которое больше текущего времени, но если вычесть 2 раза, это всегда дает правильную продолжительность (если продолжительность сама по себе меньше 50 дней)
Таким образом, единственный правильный способ проверки времени с помощью millis - это:
(later_time - earlier_time) compared-with duration
Мне нравится библиотека, которая делает все это так просто: "Тикер".
- https://www.arduino.cc/reference/en/libraries/ticker /
- Документация: https://github.com/sstaub/Ticker
Одной из тонкостей Ticker является разрешение - оно может использовать миллисекунды или микросекунды, а время может быть указано в миллисекундах или микросекундах.
По умолчанию используется микросекундное разрешение с миллисекундной спецификацией:
Ticker x(callback, time_in_millisec); // повтор по умолчанию равен 0 (непрерывный), разрешение равно микронам
Это работает только для продолжительности до 70 минут, для более длительных периодов вы должны использовать миллисекунды:
Ticker x(callback, time_in_millisec, /*repeat=*/0, /*resolution=*/MILLIS);
При указании обратного вызова для ticker вы можете использовать функцию, которая не принимает аргументов и не возвращает значения:
void mycallback() {dostuff();}
Ticker x(mycallback, ....);
Или вы можете использовать лямбда-выражение C ++ для создания безымянной функции, которая делает то же самое.
Ticker x([](){dostuff();}, ....);
Вот ваш код, переписанный с помощью Ticker с некоторыми предположениями с моей стороны о том, что вы намереваетесь, вы должны быть в состоянии адаптировать идеи
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Ticker.h>
#define ONE_WIRE_BUS 4
#define TEMPERATURE_PRECISION 9
#define motor 6
#define valve 7
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// используйте пороговое значение separatethreshold для включения / выключения, чтобы задать некоторый гистерезис
const float on_thld = 36;
const float off_thld = 35;
#define SCREEN_WIDTH 128 //ширина OLED-дисплея, в пикселях
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void check_sensors();
// Этот тикер будет вызывать check_sensors каждые 2 секунды
Ticker ticker_sensors(check_sensors, 2000, 0);
// Когда мы включим клапаны, мы будем использовать это, чтобы запланировать включение двигателя на 5 секунд позже (событие 1 раз при каждом запуске ())
Ticker ticker_motor_start([](){ digitalWrite(motor, LOW); }, 5000, 1);
// если вам не нравится этот синтаксис, вы могли бы использовать:
// аннулировать turn_on_motor() {digitalWrite(двигатель, НИЗКИЙ);}
// Бегущая строка ticker_motor_start(turn_on_motor, 500, 1);
// Когда мы выключим двигатель, мы будем использовать это, чтобы запланировать отключение клапана на 5 секунд позже (событие 1 раз при каждом запуске ())
Ticker ticker_valve_off([](){ digitalWrite(valve, HIGH); }, 5000, 1);
void check_sensors() {
sensors.requestTemperatures();
float temp = sensors.getTempCByIndex(0);
Serial.println(temp);
Serial.println((char)176);
Serial.println("C | ");
if (temp > on_thld) {
if (ticker_motor_start.state() != RUNNING) {
// отмена любого ожидающего отключения клапана
ticker_valve_off.stop();
// включите клапан и запланируйте цифровую запись включения
digitalWrite(valve, HIGH);
ticker_motor_start.start();
}
} else if (temp < off_thld) {
if (ticker_motor_start.state() == RUNNING) {
// выключите двигатель и запланируйте клапаны, чтобы отключить
digitalWrite(motor, HIGH);
ticker_valve_off.start();
}
}
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.println("Temperature");
display.setCursor(0, 30);
display.println("Soil & Water Engg.");
display.setTextSize(2);
display.setCursor(0,10);
display.println(temp);
display.setCursor(75, 10);
display.println("C");
display.setCursor(0,40);
display.display();
}
void setup() {
Serial.begin(115200);
digitalWrite(motor, HIGH); // установите значение перед включением вывода
digitalWrite(valve, HIGH); // установите значение перед включением вывода
pinMode(motor, OUTPUT);
pinMode(valve, OUTPUT);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Адрес 0x3D для 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.setTextColor(WHITE);
ticker_sensors.start();
}
void loop() {
// обновите все тикеры - если они не запущены, это не работает
// Они только проверяют время и решают, когда вызывать свои обратные вызовы во время update()
ticker_sensors.update();
ticker_motor_start.update();
ticker_valve_off.update();
}
Не все процессы основаны на времени, поэтому для полноты картины
loop:
read the time;
if time to run process1,
process1();
if time to run process2,
process2();
poll external event1;
if event1 completed,
external1();
poll external event2;
if event2 completed,
external2();
end;
- Управление регулятором вентилятора от Arduino
- Реле с NodeMCU не работает
- 2-ходовой переключатель с Arduino?
- Частое включение и выключение в SSR
- HC-05 bluetooth-модуль малой дальности действия
- Система интеллектуального освещения на основе esp поверх Alexa работает со сбоями
- Наиболее эффективная и экономичная установка домашней автоматизации
- Воспроизведение смоделированных температурных данных с помощью Arduino
Не используйте
delay()
...?, @MajenkoДля этого есть учебное пособие: [Моргайте без задержки] (https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay )., @Edgar Bonet