Торговый автомат Arduino для мониторинга ввода монет в слот во время ожидания ввода пользователя
У меня есть проект под названием «Торговый автомат с луковой шкуркой». Функция должна быть такой: когда монета вставлена, импульсы от монетоприемника (что иногда неточно, поэтому я не использовал AttachInterrupt) записываются Arduino и отображаются пользователю как кредиты. затем, ожидая ввода монет, пользователю будет предложено выбрать продукт.
ЭКС. Сценарий 1: Пользователь бросает монету номиналом 1 цент, затем машина покажет пользователю свой кредит в 1 цент и попросит нажать кнопку. пользователь нажмет кнопку, затем машина включит двигатель на (X) секунд, чтобы выдать продукт, скажет спасибо, а затем снова перезагрузится.
ЭКС. Сценарий 2: Пользователь бросает монету номиналом 5 центов, затем машина покажет пользователю свой кредит в размере 5 центов и попросит нажать кнопку. затем пользователь снова бросит монету достоинством в 1 цент, поэтому машина отобразит свой кредит как 6 центов и снова попросит нажать кнопку. пользователь нажмет кнопку, затем машина включит двигатель на (6X) секунд, чтобы выдать продукт, скажет спасибо, а затем снова перезагрузится.
Вот код, который я написал до сих пор, но проблема в том, что при активации он записывает только первый цент/импульс, а затем продолжает ждать ввода пользователя для нажатия кнопки, даже если я брошу больше монет, он не засчитается вставлены новые монеты, всегда отображается только 1 цент / 1 кредит.
ОТРЕДАКТИРОВАНО: Целевого кредита нет, подойдет 1 цент. но если пользователь сбросит 2 цента, то после выбора пользователем продукта должны быть выданы 2 набора продуктов. Итак, цель заключается в том, что Arduino должен ждать ввода монет в слот и одновременно ожидать ввода пользователя.
#include <Wire.h> // Поставляется с Arduino IDE, требуется для I2C
#include <LiquidCrystal_I2C.h> //Вызов библиотеки LCD I2C
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Устанавливаем адрес I2C ЖК-дисплея. Настраиваем передачу данных от микроконтроллера к ЖК-дисплею.
int coinPin = 2; //Определяется как приемный штифт от монетоприемника.
int coinState = 0; //Определяем статус coinPin
int credits = 0; //Определим переменную для отображения кредита.
int delayTime = 0; //Определить переменную задержки для количества продолжительность подачи листа луковой шкурки
int resetPin = 12; //Определяем вывод сброса для сброса после завершения функции
int sensorPin = A0; // выбираем входной контакт для LDR
int sensorValue = 0; // переменная для хранения значения, поступающего с датчика
const int laserPin = 6;
const int shortSizeFeedTime = 5000; //Продолжительность прохождения 2 штук луковой шкурки через устройство подачи
const int longSizeFeedTime = 7500; //Продолжительность прохождения 3 штук луковой шкурки через устройство подачи
const int ledpin = 13; //определяем вывод светодиода на выводе 13
const int ButtonS = 8; //Определить кнопку для короткой луковой шкурки
const int ButtonL = 7; //Определить кнопку для длинной луковицы
const int shortSize = 9; //Определяем вывод для вывода двигателя shortSize
const int longSize = 10; //Определяем вывод для вывода двигателя longSize
void setup()
{
digitalWrite(resetPin, HIGH);
delay(200);
pinMode(coinPin, INPUT); //Определяем функцию контакта кнопки и входного контакта.
pinMode(resetPin, OUTPUT); //Устанавливаем PIN-код сброса как ВЫХОД
pinMode(shortSize, OUTPUT);
pinMode(longSize, OUTPUT);
pinMode(laserPin, OUTPUT); //Лазерный PIN-код
/*
lcd.begin(16, 2); //Планирование столбцов и строк. ЖК-дисплей
lcd.setCursor(4,0); //Устанавливаем начальную позицию курсора в позиции (4,0)
lcd.print("WELCOME"); //Отображение текста на ЖК-дисплее
lcd.setCursor(0,1); //Устанавливаем начальную позицию курсора в позиции (0,1)
lcd.print("PLZ INSERT COIN"); //Отображение текста на ЖК-дисплее
*/
Serial.begin(9600); // Начинаем последовательное соединение с ПК
lcd.begin(16, 2); // инициализируем ЖК на 16 символов 2 строки, включаем подсветку
pinMode(ButtonS, INPUT_PULLUP);
pinMode(ButtonL, INPUT_PULLUP);
digitalWrite(laserPin, HIGH);// используется в качестве лазерного датчика натяжного троса
start(); //ПЕРЕЙТИ К ЗАПУСКУ ФУНКЦИИ
//задержка(8000);
// Подождите, а затем сообщите пользователю, что он может запустить Serial Monitor и ввести символы для
// Отображать. (Установите для параметра «Последовательный монитор» значение «Без окончания строки»)
//ЖК.очистить();
//lcd.setCursor(0,0); //Начинаем с символа 0 в строке 0
}
void start()
{
//-------- Напишем символы на дисплее ------------------
// ПРИМЕЧАНИЕ. Позиция курсора: (CHAR, LINE) начинается с 0.
lcd.clear();
lcd.setCursor(4, 0); //Начинаем с символа 4 в строке 0
lcd.print("WELCOME!");
delay(1000);
lcd.setCursor(0, 1);
lcd.print(" ->Insert Coin<-");
loop();
}
void loop()
{
sensorValue = analogRead(sensorPin);//считываем статус LDR, подключенного к аналоговому выводу для лазерного растяжного троса
coinState = digitalRead(coinPin); //значение coinPin в coinState
lcd.setCursor(0, 0); //Устанавливаем начальную позицию курсора в позиции (0,0)
while (analogRead(sensorPin) < 1000)
{
lcd.clear();
lcd.print("PLS. REMOVE ANY");
lcd.setCursor(0, 1);
lcd.print("OBJECT FRM TRAY!");
delay(2000);
start();
}
if (coinState == LOW) //Условие
{
credits += 1; //Вычисляем значение переменной. С каждым разом оно будет увеличиваться. 5
delay(500); //Расписание задержки 500мс.
lcd.clear(); //Очистить весь ЖК-экран команды
lcd.print("CREDIT:"); //Отображение сообщения на ЖК-дисплее
lcd.setCursor(8, 0); //Устанавливаем начальную позицию курсора в позиции (8,0)
lcd.print(credits); //Отображает значение переменной «кредит» на ЖК-экране.
lcd.setCursor(12, 0); //Устанавливаем начальную позицию курсора в позиции (8,0)
lcd.print("PHP"); //Отображение текста на ЖК-дисплее
lcd.setCursor(0, 1);
lcd.print("PRESS TO PROCEED");
//Ждем ввода кнопки от пользователя
while (digitalRead(ButtonS) == HIGH || digitalRead(ButtonL) == HIGH) {
//---------Сервопривод A----------------
if (digitalRead(ButtonS) == LOW && sensorValue > 1000) {
lcd.clear();
lcd.setCursor(4, 0);
lcd.print("VENDING:");
lcd.setCursor(0, 1);
lcd.print("LONG ONIONSKIN");
delayTime = credits * longSizeFeedTime;
digitalWrite(longSize, HIGH);//Включаем двигатель питателя
delay(delayTime); //время расчета для 2шт луковой шкурки
digitalWrite(longSize, LOW);//Выключаем двигатель питателя
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("THANK YOU!");
lcd.setCursor(0, 1);
lcd.print("PLS. COME AGAIN!");
credits = 0;//обнуляем кредиты
delay(3000);
digitalWrite(resetPin, LOW);//необязательно для активации PIN-кода сброса оборудования
start();//Перейти к запуску функции, своего рода сброс
break;//на самом деле такого никогда не происходит :)
} // когда предмет выдан, выходим из цикла и возвращаемся к ожиданию монеты
//---------Сервопривод B-------------
else if (digitalRead(ButtonL) == LOW && sensorValue > 1000) {
lcd.clear();
lcd.setCursor(4, 0);
lcd.print("VENDING:");
lcd.setCursor(0, 1);
lcd.print("SHORT ONIONSKIN");
delayTime = credits * shortSizeFeedTime;//время расчета для 2шт луковой шкурки
digitalWrite(shortSize, HIGH);//Включаем двигатель питателя
delay(delayTime);
digitalWrite(shortSize, LOW);//Выключаем двигатель питателя
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("THANK YOU!");
lcd.setCursor(0, 1);
lcd.print("PLS. COME AGAIN!");
credits = 0;
delay(3000);
digitalWrite(resetPin, LOW);//необязательно для активации PIN-кода сброса оборудования
start();//Перейти к запуску функции, своего рода сброс
break;//Опять же, такого никогда не бывает :)
}
}//Завершить цикл while
}//Конец цикла обнаружения монет
}//Завершить цикл void()
PS: Я где-то читал, что использование миллиса может помочь решить проблему двойного выполнения или использование правильного цикла while или switch-case. Я мало что знаю о том, как работают циклы while, do- while, for, switch.
@EngrAbbas, 👍1
Обсуждение2 ответа
Лучший ответ:
После нескольких исследований здесь представлен окончательный рабочий код и решение проблемы.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Константы
bool userSelect = false;
const int coinPin = 2; // Определяется как приемный штифт от монетоприемника.
int coinState = 0; // Определяем статус coinPin
volatile int credits = 0; // Определим переменную для отображения кредита.
int delayTime = 0; // Определить переменную задержки для количества продолжительности подачи луковой шкурки
int resetPin = 12; // Определить вывод сброса для сброса после завершения функции
int sensorPin = A0; // выбираем входной контакт для LDR
int sensorValue = 0; // переменная для хранения значения, поступающего с датчика
const int laserPin = 6;
const int shortSizeFeedTime = 5000; // Длительность прохождения 2pc OnionSkin через фидер
const int longSizeFeedTime = 7500; // Длительность прохождения 3pc OnionSkin через фидер
const int ledpin = 13; // Определить вывод светодиода на выводе 13
const int ButtonS = 4; // Определить кнопку для короткого размера OnionSkin
const int ButtonL = 5; // Определить кнопку для длинного размера OnionSkin
const int shortSize = 6; // Определить вывод для вывода двигателя shortSize
const int longSize = 7; // Определить вывод для вывода двигателя longSize
//Объявление контактов I2C для ЖК-дисплея
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
//Функция счетчика CoinInterrupt
void coinInterrupt() {
// Каждый раз, когда монетоприемник посылает импульс, прерываем основной цикл, чтобы добавить 1 цент, и включаем светодиод
credits += 1;
digitalWrite(ledpin, HIGH);
digitalWrite(ledpin, LOW);
}
//Функция Long OnionSkin Feeder
void longSkinServe() {
lcd.clear();
lcd.setCursor(4, 0);
lcd.print("VENDING:");
lcd.setCursor(0, 1);
lcd.print("LONG ONION SKIN");
delayTime = credits * longSizeFeedTime;
digitalWrite(longSize, HIGH);
delay(delayTime);
digitalWrite(longSize, LOW);
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("THANK YOU!");
lcd.setCursor(0, 1);
lcd.print("PLS. COME AGAIN!");
delay(3000);
digitalWrite(resetPin, LOW);
credits = 0;
userSelect = false;
}
//Короткая функция OnionSkin Feeder
void shortSkinServe() {
lcd.clear();
lcd.setCursor(4, 0);
lcd.print("VENDING:");
lcd.setCursor(0, 1);
lcd.print("SHORT ONION SKIN");
delayTime = credits * shortSizeFeedTime;
digitalWrite(shortSize, HIGH);
delay(delayTime);
digitalWrite(shortSize, LOW);
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("THANK YOU!");
lcd.setCursor(0, 1);
lcd.print("PLS. COME AGAIN!");
delay(3000);
digitalWrite(resetPin, LOW);
credits = 0;
userSelect = false;
}
void setup() {
Serial.begin(9600); // Начинаем последовательное соединение с ПК
// устанавливаем количество столбцов и строк ЖК-дисплея:
lcd.begin(16, 2); // инициализируем ЖК на 16 символов 2 строки, включаем подсветку
lcd.print("STARTING UP...");
attachInterrupt(digitalPinToInterrupt(coinPin), coinInterrupt, RISING);
pinMode(ledpin, OUTPUT);
pinMode(resetPin, OUTPUT); //Устанавливаем PIN-код сброса как ВЫХОД
pinMode(shortSize, OUTPUT);
pinMode(longSize, OUTPUT);
// pinMode(laserPin, OUTPUT); //Лазерный PIN-код
pinMode(ButtonS, INPUT_PULLUP);
pinMode(ButtonL, INPUT_PULLUP);
delay(2000);
}
// Основной цикл
void loop() {
if (credits == 0) {//еще не запущено, кредитов еще нет
lcd.clear();
lcd.print("ONION SKIN VENDO");
lcd.setCursor(0, 1);
lcd.write("-[INSERT COINS]-");
delay(3000);
lcd.clear();
lcd.print("-[INSERT COINS]-");
lcd.setCursor(1, 1);
lcd.write("P1 or P5 ONLY");
Serial.println("No Coin Inserted Yet...Waiting");
delay (3000);
//---Ждем монеты/кредита-----------
while (credits > 0 && userSelect == false) { //если монета вставлена, остается в цикле включения сервопривода
lcd.clear(); //Очистить весь ЖК-экран команды
lcd.print("CREDITS:"); //Отображение сообщения на ЖК-дисплее
lcd.setCursor(9, 0); //Устанавливаем начальную позицию курсора в позиции (8,0)
lcd.print(credits); //Отображает значение переменной «кредит» на ЖК-экране.
lcd.setCursor(12, 0); //Устанавливаем начальную позицию курсора в позиции (8,0)
lcd.print("PHP"); //Отображение текста на ЖК-дисплее
lcd.setCursor(0, 1);
lcd.print("PRESS TO PROCEED");
Serial.println("Credits:");
Serial.print(credits);
if (digitalRead(ButtonS) == HIGH || digitalRead(ButtonL) == HIGH) {
if (digitalRead(ButtonS) == LOW) {
//выполняем двигатель короткого размера
userSelect = true;
shortSkinServe();
}
else if (digitalRead(ButtonL) == LOW)
{
userSelect = true;
longSkinServe();
}
}
delay (80); // для цикла while управление скоростью обновления ЖК-дисплея
}
}
}
Также вот принципиальная схема.
Некоторые могут сказать, что кодирование неэффективно по сравнению с использованием нескольких операторов if else..if, но это решает проблему. Ура!
Сделайте это конечным автоматом:
Исходное состояние: режим ожидания, в лотке ничего нет, кредитов нет
Если вводится монета, то она подсчитывает кредиты и отображает ее. (Всегда должно быть правдой)
Если нажата кнопка, то что-то будет продано (и кредит вычтен) только в том случае, если на балансе достаточно средств и лоток пуст. В противном случае в течение нескольких секунд будет отображаться ошибка.
Отображение ошибки можно выполнить, установив глобальное значение, а также время начала и продолжительность:
long credit;
enum{noError, insufficientCredit, trayError, thankyou};
int error;
unsigned long messageTimestamp;
unsigned long messageDuration;
void updateLCD(){
if(millis() - messageTimestamp > messageDuration)
error=noError;
switch(error){
case noError: break;
case insufficientCredit:
//показать ошибку недостаточного кредита
break;
case trayError:
//показать ошибку лотка
break;
case thankyou:
//показываем сообщение с благодарностью
break;
}
if(credit == 0){
//показать режим привлечения
}else{
//показать кредит
}
}
Вы можете сделать что-то подобное для двигателя:
bool motorOn;
unsigned long motorTimestamp;
unsigned long motorDuration;
if(motorOn && millis() - motorTimestamp> motorDuration){
motorOn = false;
digitalWrite(shortSize, LOW);//Выключаем двигатель питателя
}
Таким образом вы также можете предотвратить повторный запуск торгового автомата при работающем двигателе, выполнив дополнительную проверку motorOn
.
Я согласен с идеей государственной машины. Нарисуйте сетку, показывающую все возможные состояния (например, нет денег, есть деньги, ожидание кнопки, выдача товара и т. д.). Затем все возможные действия (например, добавление денег, нажатие кнопки) и определение того, что нужно сделать в каждом состоянии для каждого возможного действия., @Nick Gammon
На самом деле суммы целевого кредита нет, базовый кредит составляет 1 цент. 1 цента достаточно для запуска двигателя подачи/выдачи продукта. если вы добавите 2 цента, то у вас будет 2 кредита и получится 2 набора продуктов. проблема в коде, который я сделал, распознается/подсчитывается только 1 цент, это похоже на то, когда он обнаруживает импульс на coinPin, он подсчитывает первый кредит, а затем переходит к следующей функции, чтобы дождаться ввода пользователя. мне нужно, чтобы он все еще ждал обнаружения импульса монеты, одновременно ожидая ввода пользователя., @EngrAbbas
@ratchet Если вы также любезно включите модификацию/исправление в основной код. Как я уже сказал, я мало знаком с программированием на C++. Я также не знаю, в какой части кода вызывать функцию updateLCD(), а также где вставить/заменить опубликованное вами решение., @EngrAbbas
@ratchet Freak Привет, есть новости? Я все еще жду и любопытствую :) Спасибо., @EngrAbbas
@EngrAbbas, если вы хотите масштабировать количество выдаваемого продукта, вы можете соответствующим образом масштабировать продолжительность двигателя., @ratchet freak
@ratchetfreak Я изменил код, но он все равно не работает. вот это я сделал. https://pastebin.com/YPR48f7s Я думаю, проблема в том, что я не знаю, куда вставить ваш код. У меня нет никаких знаний о том, как работает millis(), я только что прочитал в Интернете, что это то, что вы используете для многозадачности/делания двух разных вещей одновременно. пожалуйста помоги, @EngrAbbas
- Та же кнопка одним кликом и двойным кликом
- Как запустить 4 светодиода последовательно на основе кнопочного входа?
- Как использовать Multipile millis()
- Как справиться с rollover millis()?
- Печать string and integer LCD
- Почему мои часы реального времени показывают неверное время с моего ПК?
- Arduino uno + cnc Shield v3 + драйвер шагового двигателя A4988 + AccelStepper?
- Отправьте несколько значений int из Python в Arduino, используя pySerial
эти два сценария одинаковы, @jsotola
@jsotola для каждого сценария выпадала разная монета. расчет продукта также отличался из-за вставленной суммы, @EngrAbbas
Аббас и ребята, мне интересно, можно ли удалить код кнопки, затем заставить код ждать несколько секунд, затем он проверит, сбросил ли клиент 1 цент или 2 цента, а затем запустит конкретную команду для каждого значения? я только начал изучать программирование на ардуино. надеюсь скоро услышать от вас, ребята, @polu