управление 2 датчиками и 3 насосами с помощью millis

я делаю 3 насоса для вывода, и я использую millis для этого, я хочу остановиться, если rach intervalm сейчас в моем коде все еще зацикливается, а не останавливается для него thx

int pump1 = 5;//pin D5
int pump2 = 6;//pin D6
int pump3 = 7;//pin D7
float pompa1;
float pompa2;
float pompa3;
unsigned long prev_pump1    = 0;
unsigned long prev_pump2    = 0;
unsigned long prev_pump3    = 0;
unsigned long initiate_start = 0;
int pump_state1 = false;
int pump_state2 = false;
int pump_state3 = false;

и после этого это моя программа цикла

  //----------- АЛГОРИТМ НАКАЧКИ--------------------
  long int pompa01 = pompa1*1000;//интервал pump1
  long int pompa02 = pompa2*1000;//интервал pump2
  long int pompa03 = pompa3*1000;//интервал pump2

  unsigned long initiate_start = millis();

  //помпа1
  if(intiate_start-prev_pump1 > pompa01)
  {
    pump_state1 = !pump_state1;
    
    if(pump_state1)
    {
      digitalWrite(pump1, HIGH);
    }
    else
    {
      digitalWrite(pump1, LOW);
    }
    prev_pump1 = initiate_start;
  }
  
    //помпа2
  if(intiate_start-prev_pump2 > pompa02)
  {
    pump_state2 = !pump_state2;
    
    if(pump_state2)
    {
      digitalWrite(pump2, HIGH);
    }
    else
    {
      digitalWrite(pump2, LOW);
    }
    prev_pump2 = initiate_start;
  }

    //помпа3
  if(intiate_start-prev_pump3 > pompa03)
  {
    pump_state3 = !pump_state3;
    
    if(pump_state3)
    {
      digitalWrite(pump3, HIGH);
    }
    else
    {
      digitalWrite(pump3, LOW);
    }
    prev_pump3 = initiate_start;
  }

, 👍1

Обсуждение

Пожалуйста, отредактируйте свой вопрос: составьте полные предложения и обратите внимание на грамматику и пунктуацию. В нынешнем виде это действительно трудно расшифровать., @Edgar Bonet

Примечание: имена переменных initiate_start и intiate_start слишком похожи, что делает код запутанным., @Edgar Bonet


2 ответа


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

1

Это подразумевается как дополнение к ответу Мишеля Кейзерса. Я полностью согласен с его оценкой: дублирования кода следует избегать, так как это сильно вредит ремонтопригодности. И я сочувствую ему, чувствуя желание реализовать это как правильный класс. ;-)

Однако я бы сказал, что между упрощенной версией кода OP и полностью объектно-ориентированной версией этого ответа есть промежуточный шаг, на который стоит ориентироваться как на следующую вещь, которую нужно изучить. Я имею в виду массивы и циклы.

Многие случаи дублирования кода следуют примерно такой схеме:

// В верхней части скетча:
some_type data0 = some_value;
some_type data1 = some_other_value;
// и так далее.

// Внутри функции:
do_lots_of_stuff(data0);
do_lots_of_stuff(data1);
// и так далее.

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

Простое решение такого рода дублирования кода состоит в том, чтобы поместить переменные в массив и обработать их, прокручивая массив:

// В верхней части скетча:
const int data_count = ...;
some_type[data_count] = {some_value, some_other_value, ...};

// Внутри функции:
for (int i = 0; i < data_count; i++)
    do_lots_of_stuff(data[i]);

Вот моя попытка применить эту технику к коду OP. Обратите внимание, что, поскольку для каждого насоса существует несколько переменных, нам нужно несколько массивов:

const int pump_count = 3;
const uint8_t pump_pins[pump_count] = {5, 6, 7};
const uint32_t pump_half_periods[pump_count] = {1000, 2500, 3200};

uint8_t pump_states[pump_count];  // НИЗКИЙ или ВЫСОКИЙ
uint32_t pump_last_actuations[pump_count];  // время в мс

void setup() {
    for (int i = 0; i < pump_count; i++) {
        pinMode(pump_pins[i], OUTPUT);
    }
}

void loop() {
    unsigned long now = millis();
    for (int i = 0; i < pump_count; i++) {
        if (now - pump_last_actuations[i] >= pump_half_periods[i]) {
            pump_states[i] = (pump_states[i] == HIGH) ? LOW : HIGH;
            digitalWrite(pump_pins[i], pump_states[i]);
            pump_last_actuations[i] = now;
        }
    }
}

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

Как только вы освоитесь с этой концепцией, я бы сказал, что следующим шагом с точки зрения построения абстракций будет объединение всех данных относительно одного насоса в структуру. Тогда у нас был бы один массив (элементами которого являются структуры) вместо набора массивов:

// Структура данных, описывающая насос.
struct Pump {
    const uint8_t pin;
    const uint32_t half_period;  // полупериод в мс
    uint8_t state;               // НИЗКОЕ или ВЫСОКОЕ
    uint32_t last_actuation;     // время в мс
};

const int pump_count = 3;

Pump pumps[pump_count] = {
// pin, half-T, state, last_actuation
    {5, 1000, LOW, 0},
    {6, 2500, LOW, 0},
    {7, 3200, LOW, 0}
};

Тело setup() и loop() будет очень похоже на предыдущую версию, но с pump_pins[i] заменено pumps[i].pin и так далее.

Третьим шагом на лестнице абстракции было бы встраивание кода, который обрабатывает структуру данных, с самими данными в классе, как показано в ответе Мишеля Кейзерса. При правильном определении методов основной скетч можно свести примерно к следующему:

#include "Pump.h"

Pump pumps[] = {
    Pump(5, 1000),
    Pump(6, 2500),
    Pump(7, 3200)
};

void setup() {
    for (Pump &pump : pumps)
        pump.begin();
}

void loop() {
    for (Pump &pump : pumps)
        pump.process();
}

Я перепробовал все эти методы, и оказалось, что первый (с коллекцией массивов) использует наименьшее количество строк кода, а последний (с классом) - наибольшее. Тогда можно было бы задаться вопросом, зачем использовать более продвинутые абстракции, если они увеличивают объем кода, который нужно ввести. Причина в том, что эти абстракции становятся полезными, когда программа усложняется. Вы можете собрать весь код, который имеет дело с насосами, в одном месте (возможно, в отдельном файле), весь код , который имеет дело с датчиками, в другом месте... и главный скетч, который объединяет все части вместе, остается простым.

,

что такое "полупериод в мс"? это время штата или что, @daffa faiz

@daffafaiz: “Период” - это время, необходимое сигналу для повторения. “Полупериод” - это половина периода. “мс” - это символ “миллисекунд”, единицы измерения времени., @Edgar Bonet

спасибо за это, сэр, @daffa faiz

и, сэр, что, если я сотру const, он не сможет редактировать isn?, @daffa faiz

@daffafaiz: Прошу прощения? Можете ли вы составить полное предложение?, @Edgar Bonet

является ли const перед uint32_t для исправления данных, которые больше не могут измениться?, @daffa faiz

@daffafaiz: Да, это означает, что мы не собираемся менять это значение. Если вы планируете изменить его, то удалите определитель const., @Edgar Bonet


0

Примечание: это не ответ (моя предыдущая версия ответа содержала проблему, спасибо Эдгару Бонету за уведомление и исправление оставшейся части ответа). Однако я не хочу удалять этот ответ, так как он может помочь удалить много дублирования. Я взял на себя смелость превратить его в класс.

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

Скетч:

#include  "Pump.h"

const int NR_OF_PUMPS = 3;

Pump pumps[NR_OF_PUMPS] = 
{
  Pump(5, 1.0),
  Pump(6, 2.5),
  Pump(7, 3.2) 
};

void setup() 
{
}

void loop() 
{
  process();
}

void process()
{
  for (int pumpIndex = 0; pumpIndex < NR_OF_PUMPS; pumpIndex++)
  {
    pumps[pumpIndex].Process(millis());
  }
}

Насос.h:

class Pump
{
public:
  Pump::Pump(int pin, float interval);
  void Process(uint32_t currentTime);

private:
  int _pin;
  float _interval;
  uint32_t _prevTime;
  bool _state;
};

Pump.cpp :

#include "Arduino.h"
#include "Pump.h"

Pump::Pump(int pin, float interval)
:
  _pin(pin),
  _interval(interval * 1000.0),
  _prevTime(0),
  _state(false)
{
}
    
void Pump::Process(uint32_t currentTime)
{
  if (currentTime - _prevTime > _interval)
  {
    _state = !_state;
    digitalWrite(_pin, _state ? HIGH : LOW);
    _prevTime = currentTime;
  }
}
,

Re “_сматривает, что текущее время всегда вводится в предыдущие переменные_”: обновление предыдущих переменных находится в пределах условия if, которое управляет приведением в действие насоса, что нормально. Кстати, вы забыли обновить _prevTime в приведенном вами примере., @Edgar Bonet

@EdgarBonet Хороший улов, спасибо за улучшения., @Michel Keijzers

prev_pump3 = initiate_star делает цикл навсегда? Или что ?, @daffa faiz

Насколько я знаю, нет, однако у вас, вероятно, проблема с опечаткой в переменной intiate / initiate., @Michel Keijzers

Я решил эту опечатку, однако она все еще обрезается: (, @daffa faiz

Добавьте несколько инструкций печати, чтобы увидеть, что не так, это самый простой способ отладить эти проблемы, @Michel Keijzers

Re “_prevTime(millis())”: глобальные конструкторы вызываются перед инициализацией ядра Arduino. Я бы не стал вызывать основные функции в это время, даже если millis() может быть безвредным. Безопасно инициализировать _prevTime равным нулю, что в любом случае является начальным значением millis()., @Edgar Bonet

@EdgarBonet Я действительно думал об этом. Я бы подумал, что время начала в любом случае наступит быстро, но это может иметь значение. Первоначально я инициализировал _prevTime с помощью отдельного метода во время setup, но это показалось немного преувеличенным, так как в любом случае это приложение не критично по времени., @Michel Keijzers

Хорошо, позвольте мне добавить немного println для этого thx за помощь мне, @daffa faiz