Как мне запустить конечный автомат один раз в цикле Arduino

#arduino #state-machine #multitasking

#arduino #конечный автомат #многозадачность

Вопрос:

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

Порядок работы должен быть следующим

  1. Откройте затвор камеры (камера находится в режиме лампы)
  2. Одновременно капните две капли воды с двух соленоидов.
  3. Запустите flash
  4. Закройте затвор фотокамеры

Моя платформа — Atmega 2560. Вот код, который у меня есть на данный момент,

 int relayCam=A0;  //pin to relay to trigger camera
int relayFlash=A7 ;  //pin to relay to fire flash
int button1Read;
//button read delays (settling time)
int button1DT=250;
int button1Pin=8;  //pin for 2 drop button read
int DelayCamOpen=100;  //wait time to trigger camera
int DelayCamClose=1000;  //wait time to close camera shutter
//Drop start
int DropStart=500;  //time to start first drop
//Flash timing
int DelayFlashFire1=350;
int DelayFlashOff=50;  //turn flash off

bool runOnce = false;

class Solenoid
{
 int SolenoidPin;
 long OnTime;
 long OffTime;
 int SolenoidState;
 unsigned long previousMillis;
  
public:
Solenoid(int pin, long on, long off)
{
SolenoidPin = pin;
pinMode(SolenoidPin, OUTPUT);
OnTime = on;
OffTime = off;
SolenoidState = HIGH;
previousMillis = 0;
}
void update()
{
unsigned long currentMillis = millis();
if((SolenoidState == HIGH) amp;amp; (currentMillis - previousMillis >= OnTime))
{
SolenoidState = HIGH;
previousMillis = currentMillis;
digitalWrite(SolenoidPin, SolenoidState);  
}
else if ((SolenoidState == LOW) amp;amp; (currentMillis - previousMillis >= OffTime))
{
SolenoidState = LOW;
previousMillis = currentMillis;
digitalWrite(SolenoidPin, SolenoidState);  
}
}
};

Solenoid drop1(A1, 55, 55);
Solenoid drop2(A2, 55, 55);

void setup()
{
pinMode(relayCam, OUTPUT);
pinMode(relayFlash, OUTPUT);
pinMode(button1Pin, INPUT);
}
void loop()
{
//This part works
button1Read=digitalRead(button1Pin);
delay (button1DT);
if(button1Read==0) {
delay (DelayCamOpen);  //wait time after button is pushed to start sequence
digitalWrite (relayCam,LOW);  //opens the camera shutter
delay (DropStart);  //wait time after shutter opens ro start first drop
//This part does not execute
if (runOnce == false)
{runOnce = true;
drop1.update();
drop2.update();
}
//This part works
delay (DelayFlashFire1);  //wait time to fire flash
digitalWrite (relayFlash,LOW);  //fire flash
delay (DelayFlashOff);  //time to keep flash open (very short time)
digitalWrite (relayFlash,HIGH);  //close flash relay
delay  (DelayCamClose);  //wait time to close camera shutter
digitalWrite (relayCam,HIGH);  //close camera shutter
} 
else if(button1Read==1) 
{
 digitalWrite (drop1,HIGH);  //no signal to waterdrop
 digitalWrite (drop2,HIGH);  //no signal to waterdrop
 digitalWrite (relayCam,HIGH);  //no signal to camera
 digitalWrite (relayFlash,HIGH);  //no signal to flash
}
}
 

Камера и вспышка работают, а соленоиды — нет. Я даже не уверен, правильно ли я это делаю. Я знаю, что задержки при запуске двух соленоидов не дают мне желаемых результатов, поэтому я пытаюсь использовать метод конечного автомата. Любая помощь / предложения будут высоко оценены. Я новичок в написании кода и во всем, что связано с Arduino.

Это код, который работает с одним соленоидом.

 //Pin assignments
int relayCam=A0;  //pin to relay to trigger camera
int relayFlash=A7 ;  //pin to relay to fire flash
int relayWD1=A1;  //pin to relay to trigger water drop 1
int button1Read;
//button read delays (settling time)
int button1DT=250;

int button1Pin=8;  //pin for 2 drop button read
//drop sizes
int dropSize1=55;  //time to hold relay for 1st drop
int dropSize2=45;  //time to hold relay for 2nd drop

//delays betwween drops
int Delay1=45;  //time between 1st and 2nd drops
//camera shutter timings
int DelayCamOpen=100;  //wait time to trigger camera
int DelayCamClose=1000;  //wait time to close camera shutter
//Drop start
int DropStart=500;  //time to start first drop
//Flash timing
int DelayFlashFire1=350;
int DelayFlashOff=50;  //turn flash off

void setup() {
pinMode(relayCam, OUTPUT);  
pinMode(relayWD1, OUTPUT);
pinMode(relayFlash, OUTPUT);
pinMode(button1Pin, INPUT);
}

void loop() {
button1Read=digitalRead(button1Pin);
delay (button1DT);
if(button1Read==0) {
delay (DelayCamOpen);  //wait time after button is pushed to start sequence
digitalWrite (relayCam,LOW);  //opens the camera shutter
delay (DropStart);  //wait time after shutter opens ro start first drop  
  
digitalWrite (relayWD1,LOW);  //starts first drop
delay (dropSize1);  //duration of water flow
digitalWrite (relayWD1,HIGH);  //stops first drop
  
delay (Delay1);  //wait time for second drop to start
  
digitalWrite (relayWD1,LOW);  //start of second drop
delay (dropSize2);  //duration of waterflow
digitalWrite (relayWD1,HIGH);  //stops second drop

delay (DelayFlashFire1);  //wait time to fire flash
digitalWrite (relayFlash,LOW);  //fire flash
delay (DelayFlashOff);  //time to keep flash open (very short time)
digitalWrite (relayFlash,HIGH);  //close flash relay
delay  (DelayCamClose);  //wait time to close camera shutter
digitalWrite (relayCam,HIGH);  //close camera shutter
} 
else if(button1Read==1) 
{digitalWrite (relayWD1,HIGH);  //no signal to waterdrop
 digitalWrite (relayCam,HIGH);  //no signal to camera
 digitalWrite (relayFlash,HIGH);  //no signal to flash
}
}
 

Комментарии:

1. Можете ли вы объяснить свое намерение runOnce ? Собираетесь ли вы иметь внутренний цикл, который вызывается update() до завершения удаления? Или это связано с внешним циклом так, что вы хотите удалить только для первого вызова loop() ?

2. Я намерен runOnce запускать соленоиды только один раз внутри цикла в зависимости от состояния кнопки. Кнопка используется для запуска последовательности, и соленоиды должны активироваться только для двух капель каждый. Одно нажатие кнопки запускает цикл один раз. Цикл переключает состояния после отпускания кнопки. Кнопка read = 0 запускает последовательность. Как только первая последовательность завершается и кнопка отпущена, она считывает вторую часть цикла, отключая все. Проблема, с которой я столкнулся сейчас, заключается в том, что соленоиды работают непрерывно. Не уверен, как заставить их остановиться после отпускания кнопки.

Ответ №1:

Абстракция, представленная вашим update() методом, меня мучает и сбивает с толку. Мое предложение — update() заменить двумя методами startDrop() и stopDrop() . startDrop() записывает a startTimestamp (как вы делали с currentMillis переменной-членом класса) и открывает соленоид. stopDrop() сравнивает прошедшее время и возвращает false, если прошло недостаточно времени, а соленоид остается открытым, или возвращает true, если прошло достаточно времени и соленоид закрыт.

 class Solenoid
{
  ...
  
  private unsigned long startTimestamp;

  void startDrop()
  {
    startTimestamp = millis();
    digitalWrite(SolenoidPin, LOW);  
  }
  
  bool stopDrop()
  {
    if ((millis() - startTimestamp) >= OffTime)
    {
      digitalWrite(SolenoidPin, HIGH);
      return true;
    }
    return false;
  }
  
  ...
}


void loop()
{
  ...
  
  drop1.startDrop();
  drop2.startDrop();
  while (drop1.stopDrop() == false);
  while (drop2.stopDrop() == false);
  
  ...
}
 

Это всего лишь черновик. Возможно, вы захотите добавить некоторую проверку ошибок, чтобы убедиться, что, например, соленоид был закрыт раньше startDrop() или startDrop() был вызван раньше stopDrop() .

Комментарии:

1. Спасибо, что нашли время для рассмотрения этого. Я замучен и смущен многим из этого. Я понимаю механику кода с одним соленоидом. Добавление второго соленоида и синхронизация его с первым довольно сбивают с толку. Я попытался подключить соленоиды параллельно, но мне нужно настроить время сброса независимо. Я реализовал приведенный выше код, но получил сообщение об ошибке, что startDrop и stopDrop были private . Я их public создал, и теперь они работают непрерывно, независимо от buttonState того, что я также должен был объявить currentMillis .

2. Упс, мой currentMillis должен быть millis() . Я отредактирую ответ.

3. @brichardson Я понял, что мой код ответа не будет работать правильно, если drop2.onTime < drop1.onTime потому, что он не пытается остановить drop2 до тех пор, пока drop1 не будет остановлен. Поэтому необходимы некоторые улучшения, чтобы быть более гибкими в зависимости от времени включения.

4. Еще раз спасибо за обзор. Я не уверен на 100%, что конечный автомат является лучшим решением для этого. Я рассмотрю другие варианты. Может быть, Arduino просто не может делать то, что я хочу???