#arduino #state-machine #multitasking
#arduino #конечный автомат #многозадачность
Вопрос:
Я пытаюсь сфотографировать столкновения капель воды с помощью двух соленоидов. Я успешно сделал это, используя один соленоид. При этом использовались задержки настройки в коде для определения времени срабатывания затвора камеры, капель воды и вспышки. Я хотел бы запустить два соленоида одновременно с одинаковыми задержками, но не уверен, как это сделать.
Порядок работы должен быть следующим
- Откройте затвор камеры (камера находится в режиме лампы)
- Одновременно капните две капли воды с двух соленоидов.
- Запустите flash
- Закройте затвор фотокамеры
Моя платформа — 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 просто не может делать то, что я хочу???