#include <LiquidCrystal.h>
#include <Servo.h>
#include <FastLED.h>
const int LCD_RS = 12, LCD_E = 11, LCD_D4 = 10, LCD_D5 = 9, LCD_D6 = 8, LCD_D7 = 7;
const int POT_PIN = A1;
const int SERVO_PIN = 6;
const int LED_PIN = 5;
const int BUTTON_LEFT_PIN = 2;
const int BUTTON_RIGHT_PIN = 3;
const int TRIG_PIN = 13;
const int ECHO_PIN = 4;
#define NUM_LEDS 4
CRGB leds[NUM_LEDS];
// Инициализация объектов
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
Servo servo;
// Состояние системы
volatile bool resetRequested = false;
volatile int confirmPresses = 0;
unsigned long resetStartTime = 0;
int totalAmount = 0;
bool isFull = false;
int selectedCoinValue = 0;
bool waitingForZero = true; // Начинаем с ожидания нулевого положения
unsigned long stableStartTime = 0;
int lastStablePotValue = -1;
const int POT_STABLE_THRESHOLD = 5; // Допустимое отклонение для стабильности
int lastDisplayedCoinValue = 0; // Последний отображаемый номинал
// Диапазоны потенциометра для разных монет (0-1023)
const int COIN_RANGES[4][2] = {
{50, 256}, // 1 рубль (0-1/4)
{256, 512}, // 2 рубля (1/4-2/4)
{512, 768}, // 5 рублей (2/4-3/4)
{768, 1024} // 10 рублей (3/4-4/4)
};
const int POT_ZERO_THRESHOLD = 20; // Порог для определения "0"
void setup() {
// Инициализация компонентов
lcd.begin(16, 2);
servo.attach(SERVO_PIN);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
pinMode(BUTTON_LEFT_PIN, INPUT_PULLUP);
pinMode(BUTTON_RIGHT_PIN, INPUT_PULLUP);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
// Настройка прерываний для кнопок
attachInterrupt(digitalPinToInterrupt(BUTTON_LEFT_PIN), handleLeftButton, FALLING);
attachInterrupt(digitalPinToInterrupt(BUTTON_RIGHT_PIN), handleRightButton, FALLING);
// Инициализация сервопривода
servo.write(0);
delay(500);
// Приветственное сообщение
lcd.print("kopilka ready");
delay(2000);
updateDisplay();
}
void loop() {
// Проверка наполненности копилки
checkPiggyBankLevel();
if (!isFull) {
// Если копилка не полная, обрабатываем выбор монеты
processCoinSelection();
}
// Обработка запроса на сброс
if (resetRequested) {
handleResetSequence();
}
delay(50); // Уменьшенная задержка для более точного отслеживания
}
void processCoinSelection() {
int potValue = analogRead(POT_PIN);
// Если ждем возврата в нулевое положение
if (waitingForZero) {
if (potValue < POT_ZERO_THRESHOLD) {
waitingForZero = false;
lcd.setCursor(0, 1);
lcd.print("vstav");
lastDisplayedCoinValue = 0; // Сбрасываем отображаемый номинал
}
return;
}
// Определяем текущий номинал на основе положения потенциометра
int currentCoinValue = 0;
if (potValue >= COIN_RANGES[0][0] && potValue < COIN_RANGES[0][1]) currentCoinValue = 1;
else if (potValue >= COIN_RANGES[1][0] && potValue < COIN_RANGES[1][1]) currentCoinValue = 2;
else if (potValue >= COIN_RANGES[2][0] && potValue < COIN_RANGES[2][1]) currentCoinValue = 5;
else if (potValue >= COIN_RANGES[3][0] && potValue < COIN_RANGES[3][1]) currentCoinValue = 10;
// Если номинал изменился, обновляем дисплей
if (currentCoinValue != lastDisplayedCoinValue) {
lastDisplayedCoinValue = currentCoinValue;
updateSelectionDisplay(currentCoinValue);
}
// Проверка стабильности положения потенциометра
if (abs(potValue - lastStablePotValue) <= POT_STABLE_THRESHOLD) {
if (millis() - stableStartTime >= 1000) {
// Положение стабильно 1 секунду - подтверждаем выбор
if (currentCoinValue > 0) {
selectedCoinValue = currentCoinValue;
addCoin(selectedCoinValue);
}
}
} else {
// Положение изменилось - сбрасываем таймер
lastStablePotValue = potValue;
stableStartTime = millis();
}
}
void updateSelectionDisplay(int coinValue) {
lcd.setCursor(0, 1);
if (coinValue > 0) {
lcd.print("vybrano: ");
lcd.print(coinValue);
lcd.print(" rub ");
lcd.print(" "); // Очистка оставшихся символов
} else {
lcd.print("poverni! ");
}
}
void addCoin(int value) {
if (value > 0) {
// Добавляем монету
totalAmount += value;
updateDisplay();
// Активируем сервопривод
servo.write(180);
delay(1000);
servo.write(0);
delay(500);
// Требуем вернуть в нулевое положение
waitingForZero = true;
lcd.setCursor(0, 1);
lcd.print("next pls ");
}
}
void checkPiggyBankLevel() {
// Измерение расстояния ультразвуковым датчиком
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH);
int distance = duration * 0.034 / 2;
// Определение уровня наполненности
int maxDistance = 400; // Максимальное расстояние (пустая копилка)
int minDistance = 5; // Минимальное расстояние (полная копилка)
int level = map(distance, minDistance, maxDistance, 4, 0);
level = constrain(level, 0, 4);
// Обновление светодиодной ленты
updateLEDs(level);
// Проверка, полная ли копилка
isFull = (level >= 4);
}
void updateLEDs(int level) {
FastLED.clear();
for (int i = 0; i < level; i++) {
if (i < 1) leds[i] = CRGB::Green;
else if (i < 3) leds[i] = CRGB::Yellow;
else leds[i] = CRGB::Red;
}
FastLED.show();
}
void updateDisplay() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("summa: ");
lcd.print(totalAmount);
lcd.print(" rub ");
}
void handleLeftButton() {
if (!resetRequested) {
resetRequested = true;
confirmPresses = 0;
resetStartTime = millis();
}
}
void handleRightButton() {
if (resetRequested) {
confirmPresses++;
}
}
void handleResetSequence() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("sbros?");
int countdown = 10 - (millis() - resetStartTime) / 1000;
if (countdown >= 0) {
lcd.setCursor(0, 9);
lcd.print("zhal: ");
lcd.setCursor(0, 15);
lcd.print(confirmPresses);
lcd.setCursor(0, 1);
lcd.print("zhmi 3x!! ");
lcd.print(countdown);
lcd.print(" sec");
if (confirmPresses >= 3) {
// Сброс суммы
totalAmount = 0;
updateDisplay();
resetRequested = false;
waitingForZero = true;
lastDisplayedCoinValue = 0;
}
} else {
// Время вышло, сброс не выполнен
updateDisplay();
resetRequested = false;
}
}