Merge branch 'main' of github.com:rvbglas/esp_clock
[esp-clock.git] / panel.cpp
1 #include "Clock.h"
2 #include <MD_Parola.h>
3 #include <MD_MAX72xx.h>
4 #include "fonts.h"
5
6 #define FONT_NARROW 2
7
8 int screenMode = mTime;
9 int screenModeRequested = mTime;
10 int currentPriority = 255;
11 bool scrollFinished = true;
12 char msgBuf[256];   // one-time messages
13 char scrollBuf[256] = "- - - - -"; // retained
14
15 const char mn1[] PROGMEM = "января";
16 const char mn2[] PROGMEM = "февраля";
17 const char mn3[] PROGMEM = "марта";
18 const char mn4[] PROGMEM = "апреля";
19 const char mn5[] PROGMEM = "мая";
20 const char mn6[] PROGMEM = "июня";
21 const char mn7[] PROGMEM = "июля";
22 const char mn8[] PROGMEM = "августа";
23 const char mn9[] PROGMEM = "сентября";
24 const char mn10[] PROGMEM = "октября";
25 const char mn11[] PROGMEM = "ноября";
26 const char mn12[] PROGMEM = "декабря";
27
28 const char *const months[] PROGMEM = {
29   mn1, mn2, mn3, mn4, mn5, mn6, mn7, mn8, mn9, mn10, mn11, mn12
30 };
31
32 const char day0[] PROGMEM = "Воскресенье";
33 const char day1[] PROGMEM = "Понедельник";
34 const char day2[] PROGMEM = "Вторник";
35 const char day3[] PROGMEM = "Среда";
36 const char day4[] PROGMEM = "Четверг";
37 const char day5[] PROGMEM = "Пятница";
38 const char day6[] PROGMEM = "Суббота";
39
40 const char* const days[] PROGMEM = {
41   day0, day1, day2, day3, day4, day5, day6
42 };
43
44 MD_Parola* Panel = nullptr;
45
46 void utf8rus(const char* source, char* target, int maxLen) {
47   int i = 0, k , idx =0;
48   unsigned char n;
49   k = strlen(source);
50   while (i < k) {
51     n = source[i]; i++;
52     if (n >= 0xC0) {
53       switch (n) {
54         case 0xD0: {
55           n = source[i]; i++;
56           if (n == 0x81) { n = 0xA8; break; }
57           if (n >= 0x90 && n <= 0xBF) n = n + 0x30;
58           break;
59         }
60         case 0xD1: {
61           n = source[i]; i++;
62           if (n == 0x91) { n = 0xB8; break; }
63           if (n >= 0x80 && n <= 0x8F) n = n + 0x70;
64           break;
65         }
66         case 0xC2: {
67           n = source[i]; i++;
68           if (n == 0xb0) { n = 0xf; break; } // degree sign, hack
69           break;
70         }
71         default: {
72           n = source[i]; i++;
73         }
74       }
75     }
76     target[idx++] = n;
77     if (idx>=maxLen) break;
78   }
79   target[idx] = 0;
80 }
81
82 void setPanelBrightness() {
83   if (Panel) {
84     int br;
85     if (isNight()) {
86       br = cfg.getIntValue(F("panel_brightness_night"));
87     } else {
88       br = cfg.getIntValue(F("panel_brightness_day"));
89     }
90     Panel->setIntensity(br);
91   }
92 }
93
94 void setupPanel() {
95   if (Panel) {
96     Panel->displayClear();
97     delete Panel;
98     Panel=nullptr;
99   }
100   int pin_din = cfg.getIntValue(F("pin_din"));
101   int pin_clk = cfg.getIntValue(F("pin_clk"));
102   int pin_cs = cfg.getIntValue(F("pin_cs"));
103   if (!pin_din || !pin_clk || !pin_cs) {
104     pin_din = D6;
105     pin_clk = D8;
106     pin_cs = D7;
107   }
108   int led_modules = cfg.getIntValue(F("led_modules"));
109   if (!led_modules) led_modules = 4;
110   Panel = new MD_Parola(MD_MAX72XX::FC16_HW,pin_din,pin_clk,pin_cs,led_modules);
111   Panel->begin();
112   Panel->setFont(RomanCyrillic);
113   unregisterTimeHandler(F("brightness"));
114   registerTimeHandler(F("brightness"),'h',setPanelBrightness);
115 }
116
117 int panelSpeed() {
118   int panel_speed = cfg.getIntValue(F("panel_speed"));
119   if (panel_speed>0 && panel_speed<=20) {
120     panel_speed = 500/panel_speed;
121   } else  {
122     panel_speed = 50;
123   }
124   return panel_speed;
125 }
126
127 void drawScroll() {
128   Panel->displayClear();
129   Panel->setFont(RomanCyrillic);
130   Panel->displayScroll(scrollBuf, PA_CENTER, PA_SCROLL_LEFT, panelSpeed());
131   screenMode = mWeather;
132 }
133
134 void message(const char* str, int priority) {
135   if (priority > currentPriority) return;
136   currentPriority = priority;
137   utf8rus(str, msgBuf, 255);
138   Panel->displayClear();
139   Panel->setFont(RomanCyrillic);
140   Panel->displayScroll(msgBuf, PA_CENTER, PA_SCROLL_LEFT, panelSpeed());
141   screenModeRequested = mMessage;
142   screenMode = mMessage;
143 }
144
145 void message(const __FlashStringHelper*  str, int priority) {
146   if (priority > currentPriority) return;
147   currentPriority = priority;
148   char buf[256];
149   strncpy_P(buf, (PGM_P)str, 255);
150   utf8rus(buf, msgBuf, 255);
151   Panel->displayClear();
152   Panel->setFont(RomanCyrillic);
153   Panel->displayScroll(msgBuf, PA_CENTER, PA_SCROLL_LEFT, panelSpeed());
154   screenModeRequested = mMessage;
155   screenMode = mMessage;
156 }
157
158 void messageModal(const char* str) {
159   message(str);
160   while (!(Panel->displayAnimate())) { delay(10); }
161 }
162
163 void messageModal(const __FlashStringHelper*  str) {
164   message(str);
165   while (!(Panel->displayAnimate())) { delay(10); }
166 }
167
168 void scroll(const char* str, bool force) {
169   utf8rus(str, scrollBuf, 255);
170   if (force && currentPriority>5) {
171     drawScroll();
172     screenModeRequested = mWeather;
173   }
174 }
175
176 const char* templateSZ PROGMEM = "%02d%c%02d%c%02d";
177 const char* templateSNZ PROGMEM = "%2d%c%02d%c%02d";
178 const char* templateMZ PROGMEM = "%02d%c%02d";
179 const char* templateMNZ PROGMEM = "%2d%c%02d";
180
181 void displayTime(int hh, int mi, int ss, int dw, int dd, int mm, int yy, bool dots) {
182   char divider;
183   char templateStr[32];
184   Panel->setFont(fonts[
185      (cfg.getBoolValue(F("panel_seconds")) && (cfg.getIntValue(F("led_modules"))<6))
186      ?FONT_NARROW:cfg.getIntValue(F("panel_font")) ]);
187   if (screenMode !=mTime) {
188     screenModeRequested = mTime;
189     screenMode = mTime;
190   }
191   if (dots || !cfg.getBoolValue(F("flash_dots"))) {
192     divider = ':';
193   } else {
194     divider = ' ';
195   }
196   if (cfg.getBoolValue(F("panel_seconds"))) {
197     if (cfg.getBoolValue(F("panel_zero"))) {
198       strcpy_P(templateStr,templateSZ);
199     } else {
200       strcpy_P(templateStr,templateSNZ);
201     }
202   } else {
203     if (cfg.getBoolValue(F("panel_zero"))) {
204       strcpy_P(templateStr,templateMZ);
205     } else {
206       strcpy_P(templateStr,templateMNZ);
207     }
208   }
209   snprintf(msgBuf, 255, templateStr, hh, divider, mi, divider, ss);
210   Panel->displayText(msgBuf, PA_CENTER,100,0,PA_NO_EFFECT,PA_NO_EFFECT);
211   Panel->displayAnimate();
212   scrollFinished = true;
213 }
214
215 void displayTime() {
216
217   static bool newsec = false;
218   static bool newsec06 = false;
219   static int  prevss;
220   static unsigned long prevmilli;
221
222   if (ss != prevss) {
223     prevmilli=millis();
224     prevss = ss;
225     newsec = true;
226     newsec06 = true;
227   }
228
229   if (newsec) {
230     displayTime(hh,mi,ss,dw,dd,mm,yy,true);
231     newsec = false;
232   } else {
233     int delta = millis() - prevmilli;
234     if (delta>600 && newsec06) {
235       displayTime(hh,mi,ss,dw,dd,mm,yy,false);
236       newsec06 = false;
237     }
238   }
239
240 }
241
242 void displayDate(int dd, int mm, int yy) {
243
244   String msg;
245
246   if (screenMode !=mDate) {
247     screenMode = mDate;
248
249     PGM_P day = days[dw];
250     PGM_P month = months[mm];
251
252     sprintf(msgBuf,"%s, %02d %s %04d г",day, dd, month, yy);
253     utf8rus(msgBuf, msgBuf, 255);
254     Panel->displayClear();
255     Panel->setFont(RomanCyrillic);
256
257     Panel->displayScroll(msgBuf, PA_CENTER, PA_SCROLL_LEFT, panelSpeed());
258   }
259 }
260
261 void displayDate() {
262   displayDate(dd,mm,yy);
263 }
264
265 void tickPanel() {
266   if (screenMode == mTime || screenModeRequested != screenMode) { // needs to redraw
267     if (now<1668332133) { message(F("Время еще неизвестно...")); }
268     switch (screenModeRequested) {
269       case mDefault:
270       case mBarrier:
271       case mLast:
272       case mTime:
273         displayTime();
274         break;
275       case mDate:
276         displayDate();
277         break;
278       case mWeather:
279         drawScroll();
280         break;
281       case mMessage:
282         break;
283     }
284   } else if (Panel->displayAnimate()) {
285     screenModeRequested = mTime;
286     currentPriority = 255;
287   }
288 }
289
290