Скетч для ардуино. Позже будут внесены правки для более точного позиционирования индикации в торцевых стенках (после того, как они будут изготовлены), с учётом того, что на одной из них индикаторы расположены не в ряд, а один над другим. Оба приведенных варианта скетчей рабочие. Профессиональные программисты - сильно не пинайте...
Вариант 1. Здесь часы постоянно при выполнении основного цикла забирают время с RTC и также за каждый шаг происходит обновление дисплеев. Интервальные таймеры до первого срабатывания и при превышении значения 9:59 показывают только точку. Повторное срабатывание датчика возможно только через 10 секунд. В этом варианте могут наблюдаться расхождения значений часов RTC и таймеров из-за разных методов их вычисления.
Код:
//-------Макет станции Петербургского метрополитена "Садовая"--------------------------//-------Масштаб 1:120 (TT)------------------------------------------------------------
//-------Скетч визуализации индикаторов времени и интервальных таймеров 1 и 2 пути-----
#include <Wire.h> //Подключаем библиотеку для работы с интерфейсом I²C
#include <Adafruit_SSD1306.h> //Подключаем библиотеку для работы с OLED-дисплеями
#include "RTClib.h" //Подключаем библиотеку для работы с часами реального времени
#define OLED_RESET LED_BUILTIN //Видимо для инициализации нужен ресет-пин, хотя в I²C-версии такого сигнала нет (только в SPI).
//В референс-примере библиотеки Adafruit_SSD1306 тут был пин 4, но он нам самим пригодится. Поэтому поставим
//тут онбоард-светодиод (13 пин)...
Adafruit_SSD1306 Display1(OLED_RESET); //Дисплей -1
Adafruit_SSD1306 Display2(OLED_RESET); //Дисплей -2
RTC_DS1307 rtc; // часы реального времени
//-------определяем необходимые переменные:--------------------------------------------
bool timer1enable; //Вкл-Выкл для таймера 1
int minit1; //минуты для таймера 1
int sec1; //секунды для таймера 1
unsigned long timer_one; //вычисление миллис для таймера 1
bool timer2enable; //Вкл-Выкл для таймера 2
int minit2; //минуты для таймера 2
int sec2; //секунды для таймера 2
unsigned long timer_two; //вычисление миллис для таймера 2
String rtchour; //строка для считывания часов из RTC
String rtcmin; //строка для считывания минут из RTC
String rtcsec; //строка для считывания секунд из RTC
int clkhour; //Время в INT для расчётов незначащего нуля Часов
int clkmin; //Время в INT для расчётов незначащего нуля Минут
int clksec; //Время в INT для расчётов незначащего нуля Секунд
bool datchik1; //флаг срабатывания датчика 1
bool datchik2; //флаг срабатывания датчика 2
//-------Функция установки:---------------------------------------------------------------
void setup() {
pinMode(4, INPUT); //Устанавливаем пин 4 на вход (не обязательно, по умолчанию он и так настроен на считывание)
pinMode(5, INPUT); //Устанавливаем пин 5 на вход (не обязательно, по умолчанию он и так настроен на считывание)
//На эти пины (4 и 5) будем принимать сигналы с ИФК-датчиков контроля ухода поезда в туннель
Display1.begin(SSD1306_SWITCHCAPVCC, 0x3C); //I²C адрес первого дисплея
Display2.begin(SSD1306_SWITCHCAPVCC, 0x3D); //I²C адрес второго дисплея
Display1.setRotation(2); //поворачиваем первый дисплей на 180°
Display2.setRotation(2); //поворачиваем второй дисплей на 180°
Display1.clearDisplay(); //очищаем первый дисплей
Display2.clearDisplay(); //очищаем второй дисплей
Display1.setTextColor(1); //установка "цвета" текста первого дисплея (светится на чёрном)
Display2.setTextColor(1); //установка "цвета" текста второго дисплея
Display1.setTextSize(1); //установка размера шрифта первого дисплея
Display2.setTextSize(1); //установка размера шрифта второго дисплея
Display1.setTextWrap(0); //запрещаем пере-
Display2.setTextWrap(0); //нос слов
Display1.ssd1306_command(SSD1306_SETCONTRAST); // устанавливаем интенсивность свечения первого дисплея, используя установку контрастности
Display1.ssd1306_command(5); // 0-255
Display2.ssd1306_command(SSD1306_SETCONTRAST); // устанавливаем интенсивность свечения второго дисплея, используя установку контрастности
Display2.ssd1306_command(5); // 0-255
//-------Инициализируем необходимые переменные:--------------------------------------------
minit1=0; //Сбрасываем значения
sec1=0; //времени таймера 1 в ноль
timer_one=0; //Начальный millis таймера 1 для сравнения (не обязательно, по умолчанию равно нулю)
timer1enable=false; //До ухода первого поезда в туннель интервальный таймер 1 отключен
minit2=0; //Сбрасываем значения
sec2=0; //времени таймера 2 в ноль
timer_two=0; //Начальный millis таймера 2 для сравнения (не обязательно, по умолчанию равно нулю)
timer2enable=false; //До ухода первого поезда в туннель интервальный таймер 2 отключен
datchik1=false; //Флаг состояния датчика 1 сброшен
datchik2=false; //Флаг состояния датчика 2 сброшен
}
//-------Конец функции установки-------------------------------------------------------------
//-------Начало фунуции главного цикла-------------------------------------------------------
void loop() {
DateTime now = rtc.now(); //берём время с RTC
rtchour=now.hour();
rtcmin=now.minute();
rtcsec=now.second();
clkhour=rtchour.toInt(); //конвертируем время в int для расчёта незначащего нуля
clkmin=rtcmin.toInt();
clksec=rtcsec.toInt();
Display1.clearDisplay(); //очищаем первый дисплей
if (timer1enable==true) //если таймер 1 включен, то показываем его цифры...(если нет - то ничего не показываем)
{
Display1.setCursor(0,57); //устанавливаем курсор в позицию отображения минут таймера 1
Display1.print(minit1); //отображаем минуты таймера 1
Display1.setCursor(8,57); //устанавливаем курсор в позицию отображения секунд таймера 1
if (sec1 < 10) Display1.print("0"); //если нужно, то отображаем незначащий ноль секунд таймера 1
Display1.print(sec1); //отображаем секунды таймера 1
};
Display1.drawPixel(6,63,1); //децимальная точка таймера 1 горит всегда
Display1.setCursor(38,57); //Теперь часы. Устанавливаем курсор в позицию отображения часов часов реального времени
if (clkhour < 10) Display1.print("0"); //Если нужно, то отображаем незначащий ноль в часах
Display1.print(clkhour); //Отображаем значение часов часов реального времени
Display1.drawPixel(50,63,1); //Рисуем точку
Display1.setCursor(52,57); //Устанавливаем курсор в позицию отображения минут часов реального времени
if (clkmin < 10) Display1.print("0"); //Если нужно, то отображаем незначащий ноль в минутах
Display1.print(clkmin); //Отображаем значение минут часов реального времени
Display1.drawPixel(64,63,1); //Рисуем точку
Display1.setCursor(66,57); //Устанавливаем курсор в позицию отображения секунд часов реального времени
if (clksec < 10) Display1.print("0"); //Если нужно, то отображаем незначащий ноль в секундах
Display1.print(clksec); //Отображаем значение секунд часов реального времени
Display1.display(); //Ву-а-ля.....
Display2.clearDisplay(); // очищаем второй дисплей... И то же самое, что для первого повторяем для дисплея 2
if (timer2enable==true) //если таймер 2 включен, то...
{
Display2.setCursor(0,57);
Display2.print(minit2);
Display2.setCursor(8,57);
if (sec2 < 10) Display2.print("0");
Display2.print(sec2);
};
Display2.drawPixel(6,63,1);
Display2.setCursor(38,57);
if (clkhour < 10) Display2.print("0");
Display2.print(clkhour);
Display2.drawPixel(50,63,1);
Display2.setCursor(52,57);
if (clkmin < 10) Display2.print("0");
Display2.print(clkmin);
Display2.drawPixel(64,63,1);
Display2.setCursor(66,57);
if (clksec < 10) Display2.print("0");
Display2.print(clksec);
Display2.display();
//-------Таймер1----(можно определить в отдельную функцию, но не будем)-------------
if (millis()-timer_one>1000 && timer1enable==true)
{
timer_one=millis();
sec1=sec1+1;
if (sec1>59)
{
sec1=0;
minit1=minit1+1;
if (minit1>9)
{
timer1enable=false; //При превышении 9:59 таймер 1 отключается
minit1=0;
}
}
}
//-------Таймер2---------------------------------------------------------------------
if (millis()-timer_two>1000 && timer2enable==true)
{
timer_two=millis();
sec2=sec2+1;
if (sec2>59)
{
sec2=0;
minit2=minit2+1;
if (minit2>9)
{
timer2enable=false; //При превышении 9:59 таймер 2 отключается
minit2=0;
}
}
}
//-------Управление датчиками (можно определить в отдельную функцию, но не будем)----
if (digitalRead(4)==HIGH && datchik1==false) //Если от ИФК-датчика 1 пришёл сигнал и его флаг сброшен, то...
{
datchik1=true; //Устанавливаем флаг того, что датчик 1 сработал
timer1enable=true; //Включаем таймер 1
minit1=0; //Начинаем отсчёт с ноля минут
sec1=0; //и ноля секунд
}
if (digitalRead(5)==HIGH && datchik2==false) //Если от ИФК-датчика 2 пришёл сигнал и его флаг сброшен, то...
{
datchik2=true; //Устанавливаем флаг того, что датчик 2 сработал
timer2enable=true; //Включаем таймер 2
minit2=0; //Начинаем отсчёт с ноля минут
sec2=0; //и ноля секунд
}
//запрещаем считывать данные с ИФК-датчиков в течение 10 секунд после срабатывания (убираем повторные срабатывания):
if (sec1>10)
{
datchik1=false; //Сбрасывем флаг срабатывания датчика 1 (разрешаем считывание с него данных, если прошло 10 сек с момента срабатывания)
}
if (sec2>10)
{
datchik2=false; //Сбрасывем флаг срабатывания датчика 2 (разрешаем считывание с него данных, если прошло 10 сек с момента срабатывания)
}
}
Вариант 2. Здесь часы однократно при включении синхронизируются с RTC и дальше все вычисления времени производятся за счёт внутреннего таймера микроконтроллера. Обновление дисплеев происходит только при изменении значений секунд таймеров или часов. Интервальные таймеры до первого срабатывания и при превышении значения 9:59 показывают только точку. Повторное срабатывание датчика возможно только через 10 секунд.
Код:
//-------Макет станции Петербургского метрополитена "Садовая"--------------------------//-------Масштаб 1:120 (TT)------------------------------------------------------------
//-------Скетч визуализации индикаторов времени и интервальных таймеров 1 и 2 пути-----
#include <Wire.h> //Подключаем библиотеку для работы с интерфейсом I²C
#include <Adafruit_SSD1306.h> //Подключаем библиотеку для работы с OLED-дисплеями
#include "RTClib.h" //Подключаем библиотеку для работы с часами реального времени
#define OLED_RESET LED_BUILTIN //Видимо для инициализации нужен ресет-пин, хотя в I²C-версии такого сигнала нет (только в SPI).
//В референс-примере библиотеки Adafruit_SSD1306 тут был пин 4, но он нам самим пригодится. Поэтому поставим
//тут онбоард-светодиод (13 пин)...
Adafruit_SSD1306 Display1(OLED_RESET); //Дисплей -1
Adafruit_SSD1306 Display2(OLED_RESET); //Дисплей -2
RTC_DS1307 rtc; // часы реального времени
//-------определяем необходимые переменные:--------------------------------------------
bool timer1enable; //Вкл-Выкл для таймера 1
int minit1; //минуты для таймера 1
int sec1; //секунды для таймера 1
unsigned long timer_one; //вычисление миллис для таймера 1
bool timer2enable; //Вкл-Выкл для таймера 2
int minit2; //минуты для таймера 2
int sec2; //секунды для таймера 2
unsigned long timer_two; //вычисление миллис для таймера 2
String rtchour; //строка для считывания часов из RTC
String rtcmin; //строка для считывания минут из RTC
String rtcsec; //строка для считывания секунд из RTC
int clkhour; //Время в INT для расчётов незначащего нуля Часов
int clkmin; //Время в INT для расчётов незначащего нуля Минут
int clksec; //Время в INT для расчётов незначащего нуля Секунд
bool datchik1; //флаг срабатывания датчика 1
bool datchik2; //флаг срабатывания датчика 2
unsigned long clock_millis; //вычисление миллис для часов
int oldclocksec; //переменные необходимы для вычисления ежесекундной отрисовки значений в теле основного цикла
int oldtimer1sec;
int oldtimer2sec;
//-------Функция установки:---------------------------------------------------------------
void setup() {
pinMode(4, INPUT); //Устанавливаем пин 4 на вход (не обязательно, по умолчанию он и так настроен на считывание)
pinMode(5, INPUT); //Устанавливаем пин 5 на вход (не обязательно, по умолчанию он и так настроен на считывание)
//На эти пины (4 и 5) будем принимать сигналы с ИФК-датчиков контроля ухода поезда в туннель
Display1.begin(SSD1306_SWITCHCAPVCC, 0x3C); //I²C адрес первого дисплея
Display2.begin(SSD1306_SWITCHCAPVCC, 0x3D); //I²C адрес второго дисплея
Display1.setRotation(2); //поворачиваем первый дисплей на 180°
Display2.setRotation(2); //поворачиваем второй дисплей на 180°
Display1.clearDisplay(); //очищаем первый дисплей
Display2.clearDisplay(); //очищаем второй дисплей
Display1.setTextColor(1); //установка "цвета" текста первого дисплея (светится на чёрном)
Display2.setTextColor(1); //установка "цвета" текста второго дисплея
Display1.setTextSize(1); //установка размера шрифта первого дисплея
Display2.setTextSize(1); //установка размера шрифта второго дисплея
Display1.setTextWrap(0); //запрещаем пере-
Display2.setTextWrap(0); //нос слов
Display1.ssd1306_command(SSD1306_SETCONTRAST); // устанавливаем интенсивность свечения первого дисплея, используя установку контрастности
Display1.ssd1306_command(5); // 0-255
Display2.ssd1306_command(SSD1306_SETCONTRAST); // устанавливаем интенсивность свечения второго дисплея, используя установку контрастности
Display2.ssd1306_command(5); // 0-255
//-------Инициализируем необходимые переменные:--------------------------------------------
minit1=0; //Сбрасываем значения
sec1=0; //времени таймера 1 в ноль
timer_one=0; //Начальный millis таймера 1 для сравнения (не обязательно, по умолчанию равно нулю)
timer1enable=false; //До ухода первого поезда в туннель интервальный таймер 1 отключен
minit2=0; //Сбрасываем значения
sec2=0; //времени таймера 2 в ноль
timer_two=0; //Начальный millis таймера 2 для сравнения (не обязательно, по умолчанию равно нулю)
timer2enable=false; //До ухода первого поезда в туннель интервальный таймер 2 отключен
datchik1=false; //Флаг состояния датчика 1 сброшен
datchik2=false; //Флаг состояния датчика 2 сброшен
//-------Синхронизируем время c RTC--------------------------------------------------------
DateTime now = rtc.now(); //берём время с RTC
rtchour=now.hour();
rtcmin=now.minute();
rtcsec=now.second();
clkhour=rtchour.toInt(); //конвертируем время в int для расчёта незначащего нуля
clkmin=rtcmin.toInt();
clksec=rtcsec.toInt();
}
//-------Конец функции установки-------------------------------------------------------------
//-------Определяем функцию прорисовки первого дисплея---------------------------------------
void DrawDisplay1()
{
Display1.clearDisplay(); //очищаем первый дисплей
if (timer1enable==true) //если таймер 1 включен, то показываем его цифры...(если нет - то ничего не показываем)
{
Display1.setCursor(0,57); //устанавливаем курсор в позицию отображения минут таймера 1
Display1.print(minit1); //отображаем минуты таймера 1
Display1.setCursor(8,57); //устанавливаем курсор в позицию отображения секунд таймера 1
if (sec1 < 10) Display1.print("0"); //если нужно, то отображаем незначащий ноль секунд таймера 1
Display1.print(sec1); //отображаем секунды таймера 1
};
Display1.drawPixel(6,63,1); //децимальная точка таймера 1 горит всегда
Display1.setCursor(38,57); //Теперь часы. Устанавливаем курсор в позицию отображения часов часов реального времени
if (clkhour < 10) Display1.print("0"); //Если нужно, то отображаем незначащий ноль в часах
Display1.print(clkhour); //Отображаем значение часов часов реального времени
Display1.drawPixel(50,63,1); //Рисуем точку
Display1.setCursor(52,57); //Устанавливаем курсор в позицию отображения минут часов реального времени
if (clkmin < 10) Display1.print("0"); //Если нужно, то отображаем незначащий ноль в минутах
Display1.print(clkmin); //Отображаем значение минут часов реального времени
Display1.drawPixel(64,63,1); //Рисуем точку
Display1.setCursor(66,57); //Устанавливаем курсор в позицию отображения секунд часов реального времени
if (clksec < 10) Display1.print("0"); //Если нужно, то отображаем незначащий ноль в секундах
Display1.print(clksec); //Отображаем значение секунд часов реального времени
Display1.display(); //Ву-а-ля.....
}
//-------Определяем функцию прорисовки второго дисплея---------------------------------------
void DrawDisplay2()
{
Display2.clearDisplay(); // очищаем второй дисплей... И то же самое, что для первого повторяем для дисплея 2
if (timer2enable==true) //если таймер 2 включен, то...
{
Display2.setCursor(0,57);
Display2.print(minit2);
Display2.setCursor(8,57);
if (sec2 < 10) Display2.print("0");
Display2.print(sec2);
};
Display2.drawPixel(6,63,1);
Display2.setCursor(38,57);
if (clkhour < 10) Display2.print("0");
Display2.print(clkhour);
Display2.drawPixel(50,63,1);
Display2.setCursor(52,57);
if (clkmin < 10) Display2.print("0");
Display2.print(clkmin);
Display2.drawPixel(64,63,1);
Display2.setCursor(66,57);
if (clksec < 10) Display2.print("0");
Display2.print(clksec);
Display2.display();
}
//-------Начало фунуции главного цикла-------------------------------------------------------
void loop() {
//-------Часы-------
if (millis()-clock_millis>1000)
{
clock_millis=millis();
clksec=clksec+1;
if (clksec>59)
{
clksec=0;
clkmin=clkmin+1;
if (clkmin>59)
{
clkmin=0;
clkhour=clkhour+1;
if (clkhour>23)
{
clkhour=0;
}
}
}
}
//-------Таймер1----
if (millis()-timer_one>1000 && timer1enable==true)
{
timer_one=millis();
sec1=sec1+1;
if (sec1>59)
{
sec1=0;
minit1=minit1+1;
if (minit1>9)
{
timer1enable=false; //При превышении 9:59 таймер 1 отключается
minit1=0;
}
}
}
//-------Таймер2-----
if (millis()-timer_two>1000 && timer2enable==true)
{
timer_two=millis();
sec2=sec2+1;
if (sec2>59)
{
sec2=0;
minit2=minit2+1;
if (minit2>9)
{
timer2enable=false; //При превышении 9:59 таймер 2 отключается
minit2=0;
}
}
}
//-------Управление датчиками (можно определить в отдельную функцию, но не будем)----
if (digitalRead(4)==HIGH && datchik1==false) //Если от ИФК-датчика 1 пришёл сигнал и его флаг сброшен, то...
{
datchik1=true; //Устанавливаем флаг того, что датчик 1 сработал
timer1enable=true; //Включаем таймер 1
minit1=0; //Начинаем отсчёт с ноля минут
sec1=0; //и ноля секунд
}
if (digitalRead(5)==HIGH && datchik2==false) //Если от ИФК-датчика 2 пришёл сигнал и его флаг сброшен, то...
{
datchik2=true; //Устанавливаем флаг того, что датчик 2 сработал
timer2enable=true; //Включаем таймер 2
minit2=0; //Начинаем отсчёт с ноля минут
sec2=0; //и ноля секунд
}
//запрещаем считывать данные с ИФК-датчиков в течение 10 секунд после срабатывания (убираем повторные срабатывания):
if (sec1>10)
{
datchik1=false; //Сбрасывем флаг срабатывания датчика 1 (разрешаем считывание с него данных, если прошло 10 сек с момента срабатывания)
}
if (sec2>10)
{
datchik2=false; //Сбрасывем флаг срабатывания датчика 2 (разрешаем считывание с него данных, если прошло 10 сек с момента срабатывания)
}
//-------отображаем всё на дисплеях в момент изменения секунд RTC, секунд таймера1 или секунд таймера2 (чтобы не дёргать дисплеи каждый проход цикла) ---------
if (oldclocksec!=clksec || oldtimer1sec!=sec1 || oldtimer2sec!=sec2)
{
oldclocksec=clksec;
oldtimer1sec=sec1;
oldtimer2sec=sec2;
DrawDisplay1();
DrawDisplay2();
}
}
//-------конец основного цикла--------