Запуск Arduino с 2 выходами и 2 входами
Совершенно новый для Arduino, 3 недели назад!
Я делаю проект с 2 входами (2 ультразвуковых датчика) и 2 выходами (зуммер и отправка SMS)
Код для запуска зуммера и смс отлично работает по отдельности. Звуковой сигнал перестанет звучать примерно через 5 секунд.
Мне бы хотелось, чтобы они работали одновременно, но когда я загружал код на плату, это не сработало, как планировалось.
Я читал на форумах об использовании millis / blinkwithoutdelay, но не знаю, как включить эти функции в свой режим, поскольку примеры, опубликованные в Интернете, отличаются от моего проекта.
Ниже приведен код, который я создал.
Был бы признателен, если бы мне помогли опытные люди.
#define TRIG A0 //Выводы модуля
#define ECHO A1
#define TRIG1 A2 //Выводы модуля
#define ECHO1 A3
#define Buzzerpin 13
int ctn = 0;
void setup() {
Serial.begin(9600); // Последовательный мониторинг
pinMode(TRIG, OUTPUT); // Инициализация триггерного выхода и эхо-ввода
pinMode(ECHO, INPUT_PULLUP);
pinMode(TRIG1, OUTPUT); // Инициализация триггерного выхода и эхо-ввода
pinMode(ECHO1, INPUT_PULLUP);
pinMode(Buzzerpin, OUTPUT);
}
void loop() {
digitalWrite(TRIG, LOW); // Установить вывод триггера в низкий уровень на 2 мкс
delayMicroseconds(2);
digitalWrite(TRIG, HIGH); // Отправляем максимум 10 мкс, чтобы активировать ранжирование в соответствии со спецификациями
delayMicroseconds(20);
digitalWrite(TRIG, LOW); // Снова отправляем низкий пин
delayMicroseconds(2);
digitalWrite(TRIG1, LOW); // Установите триггерный контакт в низкий уровень на 2 мкс. Перед этим подайте короткий НИЗКИЙ импульс, чтобы обеспечить чистый ВЫСОКИЙ импульс.
delayMicroseconds(2);
digitalWrite(TRIG1, HIGH); // Отправляем максимум 10 мкс, чтобы активировать ранжирование в соответствии со спецификациями
delayMicroseconds(20);
digitalWrite(TRIG1, LOW); // Снова отправляем низкий пин
delayMicroseconds(2);
int distance = pulseIn(ECHO, HIGH, 26000); // Чтение импульса времени
int distance1 = pulseIn(ECHO1, HIGH, 26000); // Чтение импульса времени
distance = distance/58;
distance1 = distance1/58;
if (distance < 25) {
Serial.print("\r");
delay(1000);
Serial.print("AT+CMGF=1\r");
delay(1000);
/*Replace XXXXXXXXXX to 10 digit mobile number & ZZ to 2 digit country code*/
Serial.print("AT+CMGS=\"+YYXXXX\"\r"); // YY — код страны. XXX — номер.
delay(1000); //
//Текст сообщения для отправки.
Serial.print("HELLO There");
delay(1000);
Serial.write(0x1A);
delay(1000);
}
else {}
if (distance1 < 25 && ctn < 150) {
ctn += 1;
digitalWrite(Buzzerpin, HIGH);
}
else {
digitalWrite(Buzzerpin, LOW);
}
if (distance1 > 25 && ctn >= 150) {
ctn = 0;
digitalWrite(Buzzerpin, HIGH);
}
}
@Zac, 👍1
2 ответа
Первое: вы должны вызвать pulseIn()
сразу после отправки TRIG.
пульс. Если вы будете ждать слишком долго, вы пропустите начало эхо-импульса.
Например, здесь:
int distance = pulseIn(ECHO, HIGH, 26000);
int distance1 = pulseIn(ECHO1, HIGH, 26000);
второй pulseIn()
запускается слишком поздно из-за затраченного времени
первым.
Далее, общий подход к ведению дел — неблокирующий способ. (и, таким образом, иметь возможность обрабатывать параллельные задачи) заключается в программировании каждой задачи в виде конечного автомата. Соедините это с метод синхронизации, используемый в руководстве по Arduino «Мигание без задержки», если вам нужны действия по времени.
Здесь я бы использовал один конечный автомат для отправки SMS, а другой для зуммера. Первый самый сложный:
Правка. Я подробно рассказал, как спроектировал этот конечный автомат.
В канонической реализации конечного автомата каждое действие связанный с переходом состояния. Затем у нас есть переход для каждого команда, отправленная на модуль GSM, и мы можем пометить состояния в соответствии с какие команды уже были отправлены, например:
SENT_NOTHING → SENT_CR → SENT_CMGF → SENT_CMGS → SENT_MSG → SENT_ALL.
Все вышеперечисленные переходы отправляют строку на модуль GSM. Первый
один (SENT_NOTHING → SENT_CR) запускается обнаруженным расстоянием
быть меньше 25 см. Следующие запускаются по времени
второй после предыдущего. Мы должны добавить один последний переход
(SENT_ALL → SENT_NOTHING), который не выполняет никаких действий, но позволяет системе
«забыть» о том, что он отправил SMS, и, таким образом, подготовить его к отправке нового.
Этот переход также будет инициирован по времени после потенциально долгого
задержка (как часто пользователь хотел бы получать эти напоминания?). В
код ниже, эта последняя задержка составляет одну секунду, что я считаю слишком
короткий, но соответствует последнему delay(1000)
в опубликованном вами коде.
реализация будет такой:
static enum {
SENT_NOTHING, SENT_CR, SENT_CMGF, SENT_CMGS, SENT_MSG, SENT_ALL
} sms_state = SENT_NOTHING;
static uint32_t time_last_command_sent;
uint32_t now = millis();
switch (sms_state) {
case SENT_NOTHING:
if (distance < 25) {
Serial.print("\r");
time_last_command_sent = now;
sms_state = SENT_CR;
}
break;
case SENT_CR:
if (now - time_last_command_sent >= 1000) {
Serial.print("AT+CMGF=1\r");
time_last_command_sent = now;
sms_state = SENT_CMGF;
}
break;
case SENT_CMGF:
if (now - time_last_command_sent >= 1000) {
Serial.print("AT+CMGS=\"+YYXXXX\"\r");
time_last_command_sent = now;
sms_state = SENT_CMGS;
}
break;
// и так далее для случаев SENT_CMGS и SENT_MSG...
case SENT_ALL:
if (now - time_last_command_sent >= 1000) {
sms_state = SENT_NOTHING; // забываем, что отправили смс
}
break;
}
Однако у меня есть проблема с этой реализацией: она слишком
повторяющиеся, как случаи SENT_CR, SENT_CMGF, SENT_CMGS и SENT_MSG
по сути являются копиями одного и того же кода. Для того, чтобы сделать код
сушилка, я предпочитаю объединять их в одно состояние и разделять
информацию о состоянии в две переменные: sms_state
и
отправлено_команд
. Новые возможные значения для sms_state
теперь следующие:
- SMS_READY: код готов к отправке сообщения, как только оно будет требуется обнаруженным расстоянием.
- SMS_SENDING: он начал отправлять команды модулю GSM, но это еще не сделано.
- SMS_DONE: SMS-сообщение было отправлено, но никакое другое не будет отправлено, пока прошло некоторое время, после чего он перейдет в SMS_READY.
Тогда сопоставление с «полными» состояниями машины:
full state │ sms_state commands_sent
───────────────┼───────────────────────────
SENT_NOTHING │ SMS_READY 0
SENT_CR │ SMS_SENDING 1
SENT_CMGF │ SMS_SENDING 2
SENT_CMGS │ SMS_SENDING 3
SENT_MSG │ SMS_SENDING 4
SENT_ALL │ SMS_DONE 5
Можно возразить, что sms_state
является избыточным, поскольку все
информация содержится в commands_sent
. Я все еще хотел сохранить
sms_state
, чтобы легко переключиться
на него. Это не единственный
однако возможный подход, и было бы совершенно разумно написать
что-то вроде
if (commands_sent == 0) {
// обрабатываем случай SMS_READY
} else if (commands_sent < command_count) {
// обрабатываем случай SMS_SENDING
} else {
// обрабатываем случай SMS_DONE
}
У зуммера есть только два состояния: BUZZER_OFF и BUZZER_ON. Переходы обусловлены измеренным расстоянием и для ВКЛ. → OFF переход, а также к моменту включения.
Вот моя предварительная, непроверенная реализация этого подхода:
#define TRIG A0 //Выводы модуля
#define ECHO A1
#define TRIG1 A2 //Выводы модуля
#define ECHO1 A3
#define Buzzerpin 13
// Команды для отправки модулю GSM.
const int command_count = 5;
const char * const commands[command_count] = {
"\r",
"AT+CMGF=1\r",
"AT+CMGS=\"+YYXXXX\"\r",
"HELLO There",
"\x1a"
};
void setup() {
Serial.begin(9600);
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT_PULLUP);
pinMode(TRIG1, OUTPUT);
pinMode(ECHO1, INPUT_PULLUP);
pinMode(Buzzerpin, OUTPUT);
}
void loop() {
// Измеряем оба расстояния.
digitalWrite(TRIG, LOW);
digitalWrite(TRIG, HIGH);
delayMicroseconds(20);
digitalWrite(TRIG, LOW);
int distance = pulseIn(ECHO, HIGH, 26000) / 58;
digitalWrite(TRIG1, HIGH);
delayMicroseconds(20);
digitalWrite(TRIG1, LOW);
int distance1 = pulseIn(ECHO1, HIGH, 26000) / 58;
// Время для обоих конечных автоматов.
uint32_t now = millis();
// Отправляем СМС.
static enum {SMS_READY, SMS_SENDING, SMS_DONE} sms_state;
static uint32_t time_last_command_sent;
static int commands_sent = 0;
switch (sms_state) {
case SMS_READY:
if (distance < 25) {
Serial.print(commands[commands_sent++]);
time_last_command_sent = now;
sms_state = SMS_SENDING;
}
break;
case SMS_SENDING:
if (now - time_last_command_sent >= 1000) {
Serial.print(commands[commands_sent++]);
time_last_command_sent = now;
if (commands_sent >= command_count) {
sms_state = SMS_DONE;
}
}
break;
case SMS_DONE:
if (now - time_last_command_sent >= 1000) {
commands_sent = 0;
sms_state = SMS_READY;
}
break;
}
// Включите зуммер.
static enum {BUZZER_OFF, BUZZER_ON} buzzer_state;
static uint32_t time_buzzer_started;
if (buzzer_state == BUZZER_OFF && distance1 < 25) {
digitalWrite(Buzzerpin, HIGH);
buzzer_state = BUZZER_ON;
time_buzzer_started = now;
} else if (buzzer_state == BUZZER_ON && distance1 >= 25
&& now - time_buzzer_started >= 2000) {
digitalWrite(Buzzerpin, LOW);
buzzer_state = BUZZER_OFF;
}
}
Мигание без задержки включает не функции, а стиль кодирования.
Идея состоит в том, что вы пишете код цикла так, чтобы он проходил без каких-либо задержек или с очень небольшими задержками. При каждом проходе по циклу вы видите, сколько времени прошло, и если с момента запуска зуммера прошло 5 секунд, вы выключите зуммер.
Вероятно, вы можете избежать микросекундных задержек, которые вы используете в своем коде ультразвукового датчика. Эти задержки настолько малы, что вызывают только очень короткие паузы в вашем приложении.
Вызовы delay(1000)
в вашем цикле не будут работать. Каждый из них заставляет ВСЕ останавливаться на целую секунду. Вы не будете проверять ультразвуковой датчик, вы не измените состояние зуммера, вы не будете делать НИЧЕГО в течение 5 секунд, как только вы войдете в тело этого оператора if
. Эти вызовы с задержкой должны быть прекращены.
- Как справиться с rollover millis()?
- Использование millis() и micros() внутри процедуры прерывания
- ардуино - миллисекунды ()
- Кнопка с таймером переключения и функцией сброса времени + светодиод обратной связи
- Использовать timer0, не влияя на millis() и micros().
- Торговый автомат Arduino для мониторинга ввода монет в слот во время ожидания ввода пользователя
- Влияет ли `millis()` на длинные ISR?
- nodeMCU — Millis() — Простой счетчик — Как долго горит светодиод?
Ух ты. Вы серьезно переписали код OP. Я решил относиться к этому как к ситуации «дайте человеку рыбу/научите человека ловить рыбу» и вместо этого предложил общее руководство., @Duncan C
@DuncanC, важнее то, что это хороший пример техники, @Juraj
Согласитесь, это элегантная переработка кода OP., @Duncan C
Привет Эдгар, большое спасибо за код. Могу ли я проверить, могу ли я изменить время в 'if (сейчас - time_last_command_sent >= 1000) {' как для SMS_SENDING, так и для SMS_DONE? Нарушит ли это поток цепи?, @Zac
@Zac: Я призываю вас изучить этот код (начните со ссылки на «конечный автомат», которую я предоставил), понять его (задайте вопросы, если необходимо), а затем измените _все, что хотите_. На самом деле я считаю, что вам действительно нужно поиграть с ним, чтобы полностью понять его работу., @Edgar Bonet