//-----------------
// part_#01
//
#include <Wire.h>
#include <SSD1306Ascii.h>
#include <SSD1306AsciiWire.h>
#include <TimeLib.h> // БИБЛИОТЕКА ЖИВОГО ВРЕМЕНИ
// --- ПИНЫ ---
#define DATA_PIN 2
#define BUTTON_PIN 3
#define PWR_SWITCH_PIN 4
// Закомментируй эту строку, когда перейдешь на CR1/3N!
#define USE_18650
// ==========================================
// КАСТОМНАЯ ГРАФИКА ПО ЧЕРТЕЖАМ (6 байт заголовка)
// ==========================================
const uint8_t customGlyphs[] PROGMEM = {
0x00, 0x01, // Индексы 0 и 1: Тип шрифта (00 00 = граница 1 px // 00 01 = граница 0 px)
0x05, // Индекс 2: Ширина (5)
0x08, // Индекс 3: Высота (8)
32, // Индекс 4: Стартовый символ (ASCII 32 = 'SPACE')
39, // Индекс 5: Количество символов в массиве (31 шт.)
// --- ДАЛЬШЕ ИДУТ ДАННЫЕ (с индекса 6) ---
// 32 - space
0x00, 0x00, 0x00, 0x00, 0x00,
// Код 33 ('!') - Залитый блок (Кирпич) 5*7
0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
// Код 34 ('"') - Пустая рамка 5*7
0x7F, 0x41, 0x41, 0x41, 0x7F,
// Код 35 ('#') - Дымчатый сегмент (Шахматка) 5*7
0x55, 0x2A, 0x55, 0x2A, 0x55,
// Код 36 ('$') - Граница верх/низ (пустая шкала) 5*7
0x41, 0x41, 0x41, 0x41, 0x41,
// Код 37 ('%') - Граница право (правая граница шкалы) 5*7
0x00, 0x00, 0x00, 0x00, 0x7F,
// Код 38 ('&') - Граница лево (левая граница шкалы) 5*7
0x7F, 0x00, 0x00, 0x00, 0x00,
// глазки 39 - Залитый блок (Кирпич) 5*8
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// глазки 40 - "зрачёк влево" 5*8
0xFF, 0x80, 0x80, 0x80, 0x80,
// глазки 41 - "зрачёк вправо" 5*8
0x80, 0x80, 0x80, 0x80, 0xFF,
// 42 - Дымчатый сегмент (Шахматка) 5*8
0x55, 0xAA, 0x55, 0xAA, 0x55,
// 43 - Граница верх/низ (пустая шкала) 5*8
0x81, 0x81, 0x81, 0x81, 0x81,
// 44 - Граница лево (левая граница шкалы) 5*8
0xFF, 0x00, 0x00, 0x00, 0x00,
// 45 - QUAL 1/3
0x00, 0x47, 0x7D, 0x47, 0x40,
// 46 - QUAL 2/3
0x47, 0x40, 0x0F, 0x00, 0x1F,
// 47 - QUAL 3/3
0x00, 0x3F, 0x00, 0x7F, 0x00,
// 48 - BATT 1/3
0x00, 0x7F, 0x7F, 0x41, 0x41,
// 49 - BATT 2/3
0x7F, 0x41, 0x41, 0x7F, 0x41,
// 50 - BATT 3/3
0x41, 0x7F, 0x3E, 0x3E, 0x00,
// 51 - RAMF 1/3
0x00, 0x3C, 0xBD, 0x3C, 0xBD,
// 52 - RAMF 2/3
0x3C, 0xBD, 0x3C, 0xBD, 0x3C,
// 53 - RAMF 3/3
0xBD, 0x3C, 0xBD, 0x3C, 0x00,
// 54 - FLOW 1/3
0x08, 0x09, 0x09, 0xFF, 0x88,
// 55 - FLOW 2/3
0x88, 0x88, 0xFF, 0x09, 0x09,
// 56 - FLOW 3/3
0x09, 0xFF, 0x88, 0x88, 0x08,
// 57 - ------------------------
0x08, 0x08, 0x08, 0x08, 0x08,
// 58 - граница лево/верх 5*8
0xFF, 0x01, 0x01, 0x01, 0x01,
// 59 - CEST 1/6
0x80, 0x82, 0x04, 0xC0, 0xF0,
// 60 - CEST 2/6
0xF0, 0xF8, 0xFB, 0xF8, 0xF0,
// 61 - CEST 3/6
0xF0, 0xC0, 0x04, 0x82, 0x80,
// 62 - CEST 4/6
0x00, 0x20, 0x10, 0x01, 0x07,
// 63 - CEST 5/6
0x07, 0x0F, 0x6F, 0x0F, 0x07,
// 64 - CEST 6/6
0x07, 0x01, 0x10, 0x20, 0x00,
// 65 - CET 1/6
0x85, 0xA2, 0xC5, 0xA8, 0x90,
// 66 - CET 2/6
0xAA, 0x44, 0xBF, 0x44, 0xAA,
// 67 - CET 3/6
0x90, 0xA8, 0xC5, 0xA2, 0x85,
// 68 - CET 4/6
0x50, 0x22, 0x51, 0x0A, 0x04,
// 69 - CET 5/6
0x2A, 0x11, 0x7E, 0x11, 0x2A,
// 70 - CET 6/6
0x04, 0x0A, 0x51, 0x22, 0x50
};
const uint8_t customNumber[] PROGMEM = { //big numbers for TIME/DATE
0x00, 0x00,
0x05,
0x08,
45,
14,
// 45 - "-"
0x00, 0x08, 0x08, 0x08, 0x00,
// 46 - "."
0x00, 0x00, 0xC0, 0x00, 0x00,
// 47 - "/"
0x00, 0xC0, 0x3C, 0x03, 0x00,
// 48 - "0"
0x7E, 0x81, 0x81, 0x81, 0x7E,
// 49 - "1"
0x00, 0x81, 0xFF, 0x80, 0x00,
// 50 - "2"
0xF3, 0x91, 0x91, 0x91, 0x9F,
// 51 - "3"
0x42, 0x81, 0x89, 0x89, 0x76,
// 52 - "4"
0x38, 0x24, 0x22, 0x21, 0xFF,
// 53 - "5"
0xCF, 0x89, 0x89, 0x89, 0xF9,
// 54 - "6"
0xFF, 0x89, 0x89, 0x89, 0xF8,
// 55 - "7"
0x03, 0xC1, 0x21, 0x11, 0x0F,
// 56 - "8"
0x76, 0x89, 0x89, 0x89, 0x76,
// 57 - "9"
0x1F, 0x91, 0x91, 0x91, 0xFF,
// 58 - ":"
0x00, 0x00, 0xCC, 0x00, 0x00
};
const uint8_t customMeandr[] PROGMEM = { //Meandr for FLOW
0x00, 0x01,
0x05,
0x08,
48,
13,
// 48 - "0" __
0x01, 0xFF, 0x80, 0x80, 0xFF,
// 49 - "1"_|||
0x01, 0x01, 0x01, 0x01, 0x01,
// 50 - "2" error_e_
0x01, 0xAA, 0x55, 0xAA, 0x01,
// 51 - "3" minutes flag -| |-
0x01, 0x7F, 0x00, 0x00, 0x7F,
//глифы для эквалайзера на service2 5*8
// 52 - space
0x00, 0x00, 0x00, 0x00, 0x00,
// 53 - 1 пиксель снизу
0x00, 0x80, 0x80, 0x80, 0x80,
// 54 - 2 пикселя снизу
0x00, 0xC0, 0xC0, 0xC0, 0xC0,
// 55 - 3 пикселя снизу
0x00, 0xE0, 0xE0, 0xE0, 0xE0,
// 56 - 4 пикселя снизу
0x00, 0xF0, 0xF0, 0xF0, 0xF0,
// 57 - 5 пикселей снизу
0x00, 0xF8, 0xF8, 0xF8, 0xF8,
// 58 - 6 пикселей снизу
0x00, 0xFC, 0xFC, 0xFC, 0xFC,
// 59 - 7 пикселей снизу
0x00, 0xFE, 0xFE, 0xFE, 0xFE,
// 60 - 8 пикселей снизу
0x00, 0xFF, 0xFF, 0xFF, 0xFF
};
SSD1306AsciiWire oled;
// --- ПЕРЕМЕННЫЕ ДЛЯ ПРЕРЫВАНИЙ ---
volatile unsigned long pulseStartTime = 0;
volatile unsigned long pulseDuration = 0;
volatile bool pulseReady = false;
volatile bool lastState = HIGH;
// --- ПЕРЕМЕННЫЕ UI ---
byte currentScreen = 10;
int batteryPercent = 0.0;
float batteryVoltage = 0.00;
float qualityPercent = 0.0;
float sdramPercent = 0.0;
int rawADC = 0;
// ДИНАМИЧЕСКИЕ ПЕРЕМЕННЫЕ ВРЕМЕНИ
int dcfHour = 0, dcfMinute = 0, dcfSecond = 0;
int dcfDay = 1, dcfMonth = 1, dcfYear = 26;
// --- АНИМАЦИИ ---
const char* robotAnim[] = {"O_o ", "o_O "};
byte RobotAnimIndex = 0;
unsigned long lastRobotAnimTime = 0;
char spinnerAnim[] = {'|', '/', '-', '\\'};
byte SpinnerAnimIndex = 0;
// --- МОНИТОРИНГ FLOW ---
char flowBuf[25] = " ";
// --- ТЕРМИНАЛ ---
char termData[8][22];
char* term[8];
byte termCol = 0;
int getFreeMemory() {
extern int __heap_start, *__brkval;
int v;
return (int)((size_t)&v - (__brkval == 0 ? (size_t)&__heap_start : (size_t)__brkval));
}
// ФУНКЦИЯ АВТОМАТИЧЕСКОЙ СИНХРОНИЗАЦИИ С ПК ПРИ СТАРТЕ
void syncToCompileTime() {
char monthStr[4];
int day, year, hour, minute, second;
int month = 1;
sscanf(__DATE__, "%3s %d %d", monthStr, &day, &year);
sscanf(__TIME__, "%d:%d:%d", &hour, &minute, &second);
const char *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
for (int i = 0; i < 12; i++) {
if (strncmp(monthStr, &months[i * 3], 3) == 0) {
month = i + 1;
break;
}
}
setTime(hour, minute, second, day, month, year);
}
void addTerminalChar(char c) {
memmove(flowBuf, flowBuf + 1, 23);
flowBuf[23] = c;
if (termCol >= 21) {
char* temp = term[0];
for (byte i = 0; i < 7; i++) {
term[i] = term[i + 1];
}
term[7] = temp;
memset(term[7], ' ', 21);
termCol = 0;
}
term[7][termCol] = c;
termCol++;
}
void handlePulse() {
unsigned long now = millis();
bool state = !digitalRead(DATA_PIN); //! - инверсия сигнала на D2, после ВС547B
digitalWrite(LED_BUILTIN, !state);
if (state != lastState) {
static unsigned long lastChangeTime = 0;
if (now - lastChangeTime > 30) {
if (state == HIGH) { //HIGH - с инверсие , LOW - без инверсии
pulseStartTime = now;
} else {
pulseDuration = now - pulseStartTime;
pulseReady = true;
}
lastState = state;
lastChangeTime = now;
}
}
}
void setup() {
// Внутренний ИОН 1.1V. Жизненно важно для батарейного питания:
// Опорное напряжение не "плывет" при разряде аккумулятора (в отличие от VCC).
analogReference(INTERNAL); //reference voltage 1.1V
// СИНХРОНИЗИРУЕМ ВНУТРЕННИЕ ЧАСЫ С ВРЕМЕНЕМ КОМПИЛЯЦИИ
syncToCompileTime();
for (byte i = 0; i < 8; i++) {
memset(termData[i], ' ', 21);
termData[i][21] = '\0';
term[i] = termData[i];
}
// Настройка пинов
pinMode(PWR_SWITCH_PIN, INPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(DATA_PIN, INPUT_PULLUP);
// Прерывание висит на CHANGE. Сама функция handlePulse() отрабатывает быстро,
// что отлично подходит для съема сигнала с приемника.
attachInterrupt(digitalPinToInterrupt(DATA_PIN), handlePulse, CHANGE);
Wire.begin();
// Fast I2C (400kHz). Обязательно для OLED при работе с точными таймингами.
// Сокращает время блокировки процессора на отправку кадров дисплею.
Wire.setClock(400000L);
oled.begin(&Adafruit128x64, 0x3C); // для 0,96" SSD1306
// oled.begin(&SH1106_128x64, 0x3C); // для 1,3" SH1106
oled.setFont(System5x7);
// Расчет свободной памяти
sdramPercent = 100 - ((getFreeMemory() * 100L) / 2048L);
// Глазки
oled.setFont(customGlyphs);
oled.set2X();
delay(1000); oled.clear();
oled.setCursor(0, 2); oled.write(33); oled.write(33); oled.write(33); oled.print(" "); oled.write(33); oled.write(33); oled.write(33);
delay(1000); oled.clear();
oled.setCursor(0, 0); oled.write(39); oled.write(39); oled.write(39); oled.print(" "); oled.write(39); oled.write(39); oled.write(39);
oled.setCursor(0, 2); oled.write(39); oled.write(39); oled.write(39); oled.print(" "); oled.write(39); oled.write(39); oled.write(39);
oled.setCursor(0, 4); oled.write(40); oled.write(39); oled.write(39); oled.print(" "); oled.write(40); oled.write(39); oled.write(39);
delay(1000);
oled.clear();
oled.setCursor(0, 2); oled.write(33); oled.write(33); oled.write(33); oled.print(" "); oled.write(33); oled.write(33); oled.write(33);
delay(1000); oled.clear();
oled.setCursor(0, 0); oled.write(39); oled.write(39); oled.write(39); oled.print(" "); oled.write(39); oled.write(39); oled.write(39);
oled.setCursor(0, 2); oled.write(39); oled.write(39); oled.write(39); oled.print(" "); oled.write(39); oled.write(39); oled.write(39);
oled.setCursor(0, 4); oled.write(39); oled.write(39); oled.write(41); oled.print(" "); oled.write(39); oled.write(39); oled.write(41);
delay(1000); oled.clear(); delay(1000);
oled.setFont(System5x7);
runBootSequence();
updateBatteryStatus();
}
void loop() {
dcfHour = hour();
dcfMinute = minute();
dcfSecond = second();
dcfDay = day();
dcfMonth = month();
dcfYear = year() % 100;
handleButton();
// --- ОБРАБОТКА ЭФИРА ---
static unsigned long lastValidPulseTime = 0;
static bool minuteMarkLogged = false;
if (pulseReady) {
noInterrupts();
unsigned long dur = pulseDuration;
pulseReady = false;
interrupts();
char newBit = '?';
float pulseScore = 0.0;
// Считаем отклонение от идеальных 100 мс и 200 мс
float d0 = dur - 100.0;
float d1 = dur - 200.0;
// Вычисляем Гауссиану (колокол) для нуля и единицы
float score0 = 100.0 * exp(-(d0 * d0) / 1250.0);
float score1 = 100.0 * exp(-(d1 * d1) / 1250.0);
// Сравниваем, на что больше похож импульс, с порогом отсечения мусора 15%
if (score0 > score1 && score0 > 15.0) {
newBit = '0';
pulseScore = score0;
lastValidPulseTime = millis();
minuteMarkLogged = false;
}
else if (score1 > score0 && score1 > 15.0) {
newBit = '1';
pulseScore = score1;
lastValidPulseTime = millis();
minuteMarkLogged = false;
}
else if (dur < 50) {
// Игнорируем короткие всплески (скорее всего дребезг или ВЧ-наводка)
}
else {
// Все, что не попало на вершину колокола — это битый эфир (помеха)
newBit = '2';
pulseScore = 0.0;
}
if (newBit != '?') {
// Скользящая средняя качества плавно стремится к Гауссовой оценке текущего бита
qualityPercent = ((qualityPercent * 7.0) + pulseScore) / 8.0;
if (qualityPercent > 99.9) qualityPercent = 99.9;
addTerminalChar(newBit);
}
}
//
////
// --- ЛОВИМ МИНУТНУЮ МЕТКУ ---
if (!minuteMarkLogged && digitalRead(DATA_PIN) == HIGH && lastValidPulseTime > 0) {
if (millis() - lastValidPulseTime > 1500) {
addTerminalChar('3');
qualityPercent = ((qualityPercent * 7.0) + 100.0) / 8.0;
if (qualityPercent > 99.9) qualityPercent = 99.9; // Жесткий ограничитель
minuteMarkLogged = true;
}
}
static unsigned long lastBatTime = 0;
if (millis() - lastBatTime >= 30000) {
lastBatTime = millis();
updateBatteryStatus();
}
if (millis() - lastRobotAnimTime >= 500) {
lastRobotAnimTime = millis();
RobotAnimIndex = (RobotAnimIndex + 1) % 2;
}
static unsigned long lastDrawTime = 0;
if (millis() - lastDrawTime >= 100) {
lastDrawTime = millis();
SpinnerAnimIndex = (SpinnerAnimIndex + 1) % 4;
switch (currentScreen) {
case 0: drawMonitoring(); break;
case 1: drawService1(); break;
case 2: drawService2(); break;
case 3: drawWordTime1(); break;
case 4: drawWordTime2(); break;
case 5: drawWordTime3(); break;
case 6: drawWordTime4(); break;
case 7: drawWordTime5(); break;
case 8: drawWordTime6(); break;
case 9: drawWordTime7(); break;
case 10: drawTerminal(); break;
}
}
}
void handleButton() {
static bool btnPressed = false;
bool reading = !digitalRead(BUTTON_PIN);
if (reading && !btnPressed) {
btnPressed = true;
delay(50);
} else if (!reading && btnPressed) {
btnPressed = false;
currentScreen = (currentScreen + 1) % 11;
oled.clear();
}
}
void updateBatteryStatus() {
// Переводим пин в OUTPUT и прижимаем к земле, замыкая цепь делителя
pinMode(PWR_SWITCH_PIN, OUTPUT);
digitalWrite(PWR_SWITCH_PIN, LOW);
// Даем время на стабилизацию напряжения в делителе (заряд емкости пина АЦП)
delay(10);
// Холостое чтение для переключения мультиплексора АЦП на нужный канал
analogRead(A0);
long sum = 0;
for (byte i = 0; i < 8; i++) {
sum += analogRead(A0);
delay(1);
}
rawADC = sum / 8;
// Размыкаем цепь делителя, переводя пин в высокоимпедансное состояние (INPUT),
// чтобы остановить утечку тока через резисторы.
pinMode(PWR_SWITCH_PIN, INPUT);
// Коэффициент = (V_max_батареи / V_max_на_АЦП) * (1.1V / 1023)
// При текущем делителе 1 единица АЦП = ~1.075 мВ на входе.
// k = RealBattery Voltage / rawADC // calibration
// float k = 0.0111143271; //18650;
float k = 0.00538; //wokwi
batteryVoltage = rawADC * k;
int voltX100 = batteryVoltage * 100;
#ifdef USE_18650
// Для Li-Ion: 4.2V (100%) - 3.2V (0%)
batteryPercent = constrain(map(voltX100, 320, 420, 0, 100), 0, 99);
#else
// Для CR1/3N (Li-MnO2): Новое напряжение ~3.0V - 3.2V, разряжается до ~2.0V
// Желательно скорректировать пороги под даташит конкретной батарейки.
batteryPercent = constrain(map(voltX100, 210, 300, 0, 100), 0, 99);
#endif
}
//-----------------
// part_#02
//
void drawMonitoring() {
oled.setFont(customNumber); oled.set2X(); oled.setCursor(0, 0);
print2Digits(dcfHour); oled.print(millis() % 1000 < 500 ? ':' : ' '); print2Digits(dcfMinute); oled.print(millis() % 1000 < 500 ? ':' : ' '); print2Digits(dcfSecond);
oled.setFont(customGlyphs); oled.set1X();
uint8_t x_offset = 106;
bool isSummerTime = digitalRead(2);
oled.setCursor(x_offset, 0);
if (isSummerTime) {
oled.write(59); oled.write(60); oled.write(61);
} else {
oled.write(65); oled.write(66); oled.write(67);
}
oled.setCursor(x_offset, 1);
if (!isSummerTime) {
oled.write(68); oled.write(69); oled.write(70);
} else {
oled.write(62); oled.write(63); oled.write(64);
}
oled.set1X(); oled.setCursor(0, 2);
for (int i = 0; i < 25; i++) {
oled.setFont(customGlyphs);
oled.write(57);
}
oled.setFont(customNumber); oled.set2X(); oled.setCursor(0, 3);
print2Digits(dcfDay); oled.print('.'); print2Digits(dcfMonth); oled.print('.'); oled.print(2000 + dcfYear);
oled.set1X(); oled.setCursor(0, 5);
for (int i = 0; i < 25; i++) {
oled.setFont(customGlyphs);
oled.write(57);
}
oled.set1X();
uint8_t x1_offset = 0;
oled.setCursor(x1_offset, 7); oled.setFont(customGlyphs);
oled.write(54); oled.write(55); oled.write(56); oled.write(32); // Иконка FLOW
oled.setCursor(x1_offset, 6); oled.setFont(customGlyphs);
oled.write(32); oled.write(32); oled.write(32); oled.write(32);
// рисуем меандр, ошибки, минутный флаг
oled.set2X(); oled.setFont(customMeandr); oled.print(&flowBuf[12]);
oled.setFont(System5x7);
}
void drawService2() {
oled.setFont(customGlyphs); oled.set2X(); oled.setCursor(0, 1);
oled.write(48); oled.write(49); oled.write(50); oled.write(32);
oled.setFont(System5x7);
oled.print(batteryVoltage, 2); oled.print('V');
oled.set1X(); oled.print(rawADC);
oled.set1X(); oled.setCursor(0, 3); oled.setFont(customGlyphs);
oled.write(33); drawBar(batteryPercent, 24); oled.setFont(customGlyphs); oled.write(38);
oled.setFont(customGlyphs); oled.set2X(); oled.setCursor(0, 6);
oled.write(51); oled.write(52); oled.write(53); oled.write(32); oled.write(32); oled.write(32);
oled.setFont(System5x7);
if (sdramPercent < 10) oled.print('0'); oled.print(sdramPercent, 1); oled.print('%');
oled.set1X(); oled.setCursor(0, 5); oled.setFont(customGlyphs);
oled.write(33); drawBar(sdramPercent, 24); oled.setFont(customGlyphs); oled.write(38);
oled.setFont(System5x7);
}
// Форма Гауссова колокола для 25 столбцов (макс. высота 40 пикселей)
const byte bellShape[25] = {1, 2, 3, 4, 5, 8, 13, 18, 24, 30, 35, 39, 40, 39, 35, 30, 24, 18, 13, 8, 5, 4, 3, 2, 1};
void drawService1() {
oled.set2X(); oled.setCursor(0, 0); oled.setFont(customGlyphs);
oled.write(45); oled.write(46); oled.write(32); oled.write(32); oled.write(32); oled.write(32);
oled.setFont(System5x7);
if (qualityPercent < 10) oled.print('0');
oled.print(qualityPercent, 1); oled.print('%');
oled.set1X(); oled.setCursor(0, 2); oled.setFont(customGlyphs);
oled.write(33); drawBar(qualityPercent >= 99.9 ? 100 : (int)qualityPercent, 24); oled.setFont(customGlyphs); oled.write(38);
// --- ОТРИСОВКА МОНОЛИТНОГО КОЛОКОЛА (Строки с 3 по 7, 25 столбцов) ---
oled.setFont(customMeandr); // Включаем шрифт с глифами эквалайзера
for (int r = 0; r < 5; r++) {
oled.set1X();
oled.setCursor(0, 3 + r);
for (int col = 0; col < 25; col++) {
// Вычисляем целевую высоту конкретного столбца (масштабируем от 0 до 100%)
int targetHeight = (bellShape[col] * (int)qualityPercent) / 100;
// Вычисляем базовую высоту текущей строки (строка 4 - это низ = 0, строка 0 - это верх = 32)
int rowBase = (4 - r) * 8;
// Сколько пикселей гистограммы попадает в текущую строку?
int pixelsInThisRow = targetHeight - rowBase;
if (pixelsInThisRow <= 0) {
// Столбец до этой строки не дорос (пустота)
oled.write(52);
} else if (pixelsInThisRow >= 8) {
// Столбец пробивает эту строку насквозь (полный монолит)
oled.write(60);
} else {
// Столбец заканчивается внутри этой строки (частичное заполнение)
// Так как символы идут по порядку: 53 (0px) + pixelsInThisRow дает нужный символ
oled.write(52 + pixelsInThisRow);
}
}
}
oled.setFont(System5x7); // Возврат к стандартному шрифту
}
//
void drawWordTime1() {
drawGenericWordTime(-2, F("LONDON.UTC "));
}
void drawWordTime2() {
drawGenericWordTime(2, F(" DUBAI "));
}
void drawWordTime3() {
drawGenericWordTime(3, F(" MOCKBA "));
}
void drawWordTime4() {
drawGenericWordTime(8, F(" BEIJING "));
}
void drawWordTime5() {
drawGenericWordTime(9, F(" TOKYO "));
}
void drawWordTime6() {
drawGenericWordTime(10, F(" SYDNEY "));
}
void drawWordTime7() {
drawGenericWordTime(-5, F(" NEW-YORK "));
}
void drawGenericWordTime(int offset, const __FlashStringHelper* city) {
int displayHour = (dcfHour + offset + 24) % 24;
oled.set2X(); oled.setCursor(0, 0); oled.print(' '); oled.setFont(customNumber); print2Digits(displayHour); oled.print(millis() % 1000 < 500 ? ':' : ' '); print2Digits(dcfMinute); oled.print(millis() % 1000 < 500 ? ':' : ' '); print2Digits(dcfSecond);
oled.set1X(); oled.setCursor(0, 2);
for (int i = 0; i < 25; i++) {
oled.setFont(customGlyphs);
oled.write(57);
}
oled.set2X(); oled.setCursor(0, 3); oled.setFont(customNumber); print2Digits(dcfDay); oled.print('.'); print2Digits(dcfMonth); oled.print('.'); oled.print(2000 + dcfYear);
oled.set1X(); oled.setCursor(0, 5);
for (int i = 0; i < 25; i++) {
oled.setFont(customGlyphs);
oled.write(57);
}
oled.set2X(); oled.setCursor(0, 6); oled.setFont(System5x7); oled.print(city);
oled.set1X(); oled.setFont(System5x7);
}
void drawTerminal() {
for (byte i = 0; i < 8; i++) {
oled.setCursor(0, i);
oled.print(term[i]);
}
oled.setFont(System5x7);
if (termCol < 21) {
oled.setCursor(termCol * 6, 7);
oled.print(spinnerAnim[SpinnerAnimIndex]);
}
}
void print2Digits(int n) {
if (n < 10) oled.print('0');
oled.print(n);
}
void drawBar(int val, int maxChars) {
int numFilled = map(val, 0, 100, 0, maxChars);
oled.setFont(customGlyphs);
for (int i = 0; i < maxChars; i++) {
oled.write(i < numFilled ? 33 : 36);
}
oled.setFont(System5x7);
}
void runBootSequence() {
oled.clear();
oled.set2X(); oled.setCursor(0, 1); oled.println(F("RD&P Lab "));
oled.set1X(); oled.setCursor(0, 3); oled.println(F(" with"));
oled.set2X(); oled.setCursor(0, 5); oled.println(F(" Gemini"));
oled.set1X(); oled.setCursor(0, 7); oled.println(F(" [O_o]")); delay(1000);
oled.setCursor(0, 7); oled.println(F(" [o_O]")); delay(1000);
oled.setCursor(0, 7); oled.println(F(" [O_o]")); delay(1000);
oled.clear();
oled.set1X(); oled.setCursor(0, 0); oled.println(F("v.0008_real_hw"));
oled.set2X(); oled.setCursor(0, 2); oled.println(F("W-MONITOR"));
oled.set1X(); oled.println(F(" DCF77"));
oled.setCursor(0, 6);
for (int i = 0; i < 25; i++) {
oled.setFont(customGlyphs);
oled.write(42);
delay(40);
}
oled.setCursor(0, 6);
for (int i = 0; i < 25; i++) {
oled.write(39);
delay(40);
}
delay(500); oled.clear();
oled.setFont(System5x7);
}
//-----------------