Часы для региональной выставки

Всем здраствуйте! Меня зовут Николай и я хожу в кружок робототехнки и програмирования в школе. У нас скоро региональная выставка проектов, и я хочу показать на ней часы, но у меня возникают с ними проблемы, а мой учитель кружка говорит лучше сделай светофор. Мне не нравится светофор это глупо. Помогите с проектом пожалуйста.

Что я хочу от часов.
Часы отображают время, точка номер 2 мигает. При нажатии кнопки энкодера, переходит в режим настройки времени, где поворот ручки меняет текущее время - по одной минуте за поворот, вперед или назад. Еще один клик переводит в режим будильника, где настраивается будильник таким же образом. Во время настройки будильника мигает так же 4 точка в углу правом. Точка сохраняется при отображении времени вне режима настройки, если будильник установлен. что бы отключить или включить будильник, нужно задержать кнопку энкодера в нормальном режиме и точка пропадет, выключив будильник. Так же будильник активируется, зажав кнопку. Третий режим - это почасовой сигнал. Поворотом энокдера меняется значение On и Off. Почасовой сигнал пищит каждый раз когда начинается новый час. Его режим активности отображается горящей точкой в левом углу.

Какие проблемы.
Будильник не работает
Почасовой сигнал не работает

Я использую дисплей 4401as-1 (0.40-inch Emitting color: Red Mode: Common-Cathode (CC) Digit: 4-Digit Category: LED 7-Segment Display) потому что он был самый дешовый, энкодер я выпаял из сломоного блока питания (без резьбы крепежнной) без платы просто голый, и модуль RTC Ds1302 тоже самый дешевый.

Вот мой код

#include <GyverSegment.h>
#include <Ds1302.h>
#include <EncButton.h>

// ------------------ Настройка дисплея ------------------
uint8_t segPins[] = {2, 3, 4, 5, 6, 7, 8, 9};
uint8_t digPins[] = {10, 11, 12, 13};
DispBare<4, true, false> disp(digPins, segPins);

// ------------------ Настройка DS1302 ------------------
#define DS1302_CE   A2
#define DS1302_CLK  A0
#define DS1302_IO   A1
Ds1302 rtc(DS1302_CE, DS1302_CLK, DS1302_IO);

// ------------------ Настройка энкодера и кнопки ------------------
EncButton eb(A3, A4, A5, INPUT_PULLUP, INPUT_PULLUP, LOW);

// ------------------ Настройка буззера ------------------
#define BUZZER_PIN  A6

// ------------------ Режимы работы ------------------
enum Mode {
  MODE_NORMAL,
  MODE_SET_TIME,
  MODE_SET_ALARM,
  MODE_SET_HOURLY
};
Mode currentMode = MODE_NORMAL;

// Флаги и параметры
bool alarmEnabled = false;
bool hourlyChimeEnabled = false;
int alarmHour = 7;
int alarmMinute = 0;

// Переменные для настройки времени
int tempHour = 0;
int tempMinute = 0;

// ------------------ Переменные для мигания ------------------
unsigned long blinkTimer = 0;
bool blinkState = false;
const unsigned long BLINK_INTERVAL = 500;

// Для почасового сигнала
int lastDisplayedHour = -1;

// ------------------ Прототипы функций ------------------
void updateBlink();
void updateDisplay(int dispHour, int dispMinute);
void playBeep();

// ------------------ setup() ------------------
void setup() {
  pinMode(BUZZER_PIN, OUTPUT);
  
  rtc.init();
  if (rtc.isHalted()) {
    Ds1302::DateTime dt = {22, 1, 1, 12, 0, 0, 6};
    rtc.setDateTime(&dt);
  }
  
  disp.clear();
  disp.update();
  
  eb.setBtnLevel(LOW);
  eb.counter = 0;
  blinkTimer = millis();
}

// ------------------ loop() ------------------
void loop() {
  disp.tick();  // Обновление дисплея
  eb.tick();    // Обновление энкодера и кнопки
  updateBlink();
  
  static Ds1302::DateTime now;
  if (currentMode == MODE_NORMAL) rtc.getDateTime(&now);
  
  switch (currentMode) {
    case MODE_NORMAL:
      updateDisplay(now.hour, now.minute);
      
      // Проверка будильника
      if (alarmEnabled && now.hour == alarmHour && now.minute == alarmMinute && now.second == 0) {
        playBeep();
      }
      
      // Проверка почасового сигнала
      if (hourlyChimeEnabled && now.minute == 0 && now.second == 0 && now.hour != lastDisplayedHour) {
        playBeep();
        lastDisplayedHour = now.hour;
      }
      if (now.minute != 0) lastDisplayedHour = -1;
      break;
      
    case MODE_SET_TIME:
      if (eb.counter != 0) {
        int totalMinutes = tempHour * 60 + tempMinute + eb.counter;
        totalMinutes = (totalMinutes + 1440) % 1440;
        tempHour = totalMinutes / 60;
        tempMinute = totalMinutes % 60;
        eb.counter = 0;
      }
      updateDisplay(tempHour, tempMinute);
      break;
      
    case MODE_SET_ALARM:
      if (eb.counter != 0) {
        int totalMinutes = tempHour * 60 + tempMinute + eb.counter;
        totalMinutes = (totalMinutes + 1440) % 1440;
        tempHour = totalMinutes / 60;
        tempMinute = totalMinutes % 60;
        eb.counter = 0;
      }
      updateDisplay(tempHour, tempMinute);
      break;
      
    case MODE_SET_HOURLY:
      if (eb.turn()) {
        // Переключение между ON и OFF при повороте энкодера
        hourlyChimeEnabled = !hourlyChimeEnabled;
      }
      // Отображение ON или OFF на дисплее
      updateDisplay(hourlyChimeEnabled ? 1 : 0, 0);
      break;
  }

  // Долгое нажатие для включения/выключения будильника
  if (currentMode == MODE_NORMAL && eb.hold()) {
    alarmEnabled = !alarmEnabled;
    tone(BUZZER_PIN, 1000, 200);
  }

  // Короткое нажатие для переключения режимов
  if (eb.click()) {
    switch (currentMode) {
      case MODE_NORMAL:
        rtc.getDateTime(&now);
        tempHour = now.hour;
        tempMinute = now.minute;
        currentMode = MODE_SET_TIME;
        break;
        
      case MODE_SET_TIME: {
        Ds1302::DateTime now;
        rtc.getDateTime(&now);
        Ds1302::DateTime newTime = {now.year, now.month, now.day, tempHour, tempMinute, now.second, now.dow};
        rtc.setDateTime(&newTime);
        tempHour = alarmHour;
        tempMinute = alarmMinute;
        currentMode = MODE_SET_ALARM;
        break;
      }
        
      case MODE_SET_ALARM:
        alarmHour = tempHour;
        alarmMinute = tempMinute;
        currentMode = MODE_SET_HOURLY;
        break;
        
      case MODE_SET_HOURLY:
        currentMode = MODE_NORMAL;
        break;
    }
  }
  
  disp.delay(10);  // Задержка для обновления дисплея
}

void updateBlink() {
  if (millis() - blinkTimer >= BLINK_INTERVAL) {
    blinkTimer = millis();
    blinkState = !blinkState;
  }
}

void updateDisplay(int dispHour, int dispMinute) {
  disp.clear();
  
  if (currentMode == MODE_SET_HOURLY) {
    // В режиме настройки почасового сигнала отображаем ON или OFF
    if (hourlyChimeEnabled) {
      disp.print("ON");
      disp.setCursor(0);
    } else {
      disp.print("OFF");
      disp.setCursor(0);
    }
  } else {
    // В остальных режимах отображаем время
    char buf[5];
    sprintf(buf, "%02d%02d", dispHour, dispMinute);
    disp.setCursor(0);
    disp.print(buf);
    
    if (currentMode == MODE_NORMAL) {
      disp.buffer[1] = blinkState ? (disp.buffer[1] | 0x80) : (disp.buffer[1] & 0x7F);
      if (alarmEnabled) disp.buffer[3] |= 0x80;
      if (hourlyChimeEnabled) disp.buffer[0] |= 0x80;
    }
  }
  
  disp.update();  // Обновление дисплея
}

void playBeep() {
  tone(BUZZER_PIN, 1000, 200); // Воспроизведение звукового сигнала
}

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

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

Я ничего не рисовал просто кодил и много раз правил код .

А сам бипер работает? Например, будучи вызванным из setup().

Проверил таким кодом и не рабоатет он. МОжет нога А6 плохая у меня? Не понимаю. Сам бипер рабочий проверил на нескольких.

const int buzzer = A6;  //

void setup() {
  pinMode(buzzer, OUTPUT);
}

void loop() {
  digitalWrite(buzzer, HIGH);
  delay(500);
  digitalWrite(buzzer, LOW);
  delay(500);
}

Отлаживайтесь…
Например, если в строку 87 не попадаем, то и надо выяснять - почему не попадаем. Взяли и перед 86 строкой вывели в сериал все переменные, которые в ней будут проверяться. Вот прямо все! Ну и так далее.
Если реально бипер не дохлый! :slight_smile:

Если МК - 328p, то A6 на нём - только вход.

1 лайк

да у меня нано, а какой можно использовать?

Как выход - любой, кроме A6 и A7.

2 лайка

твой код неверный, не может быть источником информации о том, что ты хочешь сделать. нужно формализовать твои желания в алгоритм, такие квадратики, ромбики и прочие стрелочки, у вас такое учили?

А у меня кроме А6 и А7 выоходов не осталось. А что значит выход. Вот у меня RTC еще подключен к А0, 1, 2, можно их заменить на А6-7?

Совершена ошибка проектирования.

Выход - это, упрощенно, пин, на который воздействует digitalWrite(). Технически - внутри стоят два транзистора, один из которых умеет пин замыкать на VCC, а другой на GND. Для A6 и А7 таковых транзисторов внутри микроконтроллера нет.

Что можно сделать:

  1. малой кровью - кнопку перекинуть на A6 и работать с ней, как с подключенной к аналоговому входу;
  2. посложнее - взять i2c модуль часов, освободив один нормальный пин;
  3. самый простой и дорогой - построить всё на Mega2560.

Нет. А6 и А7 могут использоваться только как аналоговые входы

Можете пожалуйста объяснить как это сделать. Я перекинул кнопку на А6, а бузер на А5. Теперь бузер работает, а вот кнопка энкодера нет.

https://www.google.com/search?q=arduino+кнопка+на+аналоговом+входе&oq=arduino+кнопка+на+аналоговом+входе&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTE1NTU3ajBqN6gCD7ACAQ&client=ms-unknown&sourceid=chrome-mobile&ie=UTF-8#sbfbu=1&pi=arduino%20кнопка%20на%20аналоговом%20входе

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

Хотя, наверное, можно текущую библиотеку нацелить на A7, пусть мучается. А настоящую кнопку с A6 читать другой бибоиотекой.

1 лайк

Опрашивай кнопку энкодера отдельно. Если она замыкается на GND, то ей нужно сделать внешнюю подтяжку (с пина А6 на VCC резистор 10кОм подключи) и тогда читать состояние этой кнопки можно так:

if (analogRead(A6) < 200) {  // если напряжение на пине А6 (что с кнопкой) упало сильно ниже VCC (VCC = 1023)
  // то кнопку нажали, можем тут обработать это событие
} else {  // иначе кнопку отпустили
  // тут можно обработать отпускание кнопки, если это нужно
} 
1 лайк

Спасибо большое! Все работает. Использовал стороннюю библиотеку.

Это тоже попробую, спасибо!