ESP8266 alarm clock - initial release
[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   utf8rus(str, msgBuf, 255);
137   Panel->displayClear();
138   Panel->setFont(RomanCyrillic);
139   Panel->displayScroll(msgBuf, PA_CENTER, PA_SCROLL_LEFT, panelSpeed());
140   screenModeRequested = mMessage;
141   screenMode = mMessage;
142 }
143
144 void message(const __FlashStringHelper*  str, int priority) {
145   if (priority > currentPriority) return;
146   char buf[256];
147   strncpy_P(buf, (PGM_P)str, 255);
148   utf8rus(buf, msgBuf, 255);
149   Panel->displayClear();
150   Panel->setFont(RomanCyrillic);
151   Panel->displayScroll(msgBuf, PA_CENTER, PA_SCROLL_LEFT, panelSpeed());
152   screenModeRequested = mMessage;
153   screenMode = mMessage;
154 }
155
156 void messageModal(const char* str) {
157   message(str);
158   while (!(Panel->displayAnimate())) { delay(50); }
159 }
160
161 void messageModal(const __FlashStringHelper*  str) {
162   message(str);
163   while (!(Panel->displayAnimate())) { delay(50); }
164 }
165
166 void scroll(const char* str, bool force) {
167   utf8rus(str, scrollBuf, 255);
168   if (force && currentPriority>5) {
169     drawScroll();
170     screenModeRequested = mWeather;
171   }
172 }
173
174 const char* templateSZ PROGMEM = "%02d%c%02d%c%02d";
175 const char* templateSNZ PROGMEM = "%2d%c%02d%c%02d";
176 const char* templateMZ PROGMEM = "%02d%c%02d";
177 const char* templateMNZ PROGMEM = "%2d%c%02d";
178
179 void displayTime(int hh, int mi, int ss, int dw, int dd, int mm, int yy, bool dots) {
180   char divider;
181   char templateStr[32];
182   Panel->setFont(fonts[
183      (cfg.getBoolValue(F("panel_seconds")) && (cfg.getIntValue(F("led_modules"))<6))
184      ?FONT_NARROW:cfg.getIntValue(F("panel_font")) ]);
185   if (screenMode !=mTime) {
186     screenModeRequested = mTime;
187     screenMode = mTime;
188   }
189   if (dots || !cfg.getBoolValue(F("flash_dots"))) {
190     divider = ':';
191   } else {
192     divider = ' ';
193   }
194   if (cfg.getBoolValue(F("panel_seconds"))) {
195     if (cfg.getBoolValue(F("panel_zero"))) {
196       strcpy_P(templateStr,templateSZ);
197     } else {
198       strcpy_P(templateStr,templateSNZ);
199     }
200   } else {
201     if (cfg.getBoolValue(F("panel_zero"))) {
202       strcpy_P(templateStr,templateMZ);
203     } else {
204       strcpy_P(templateStr,templateMNZ);
205     }
206   }
207   snprintf(msgBuf, 255, templateStr, hh, divider, mi, divider, ss);
208   Panel->displayText(msgBuf, PA_CENTER,100,0,PA_NO_EFFECT,PA_NO_EFFECT);
209   Panel->displayAnimate();
210   scrollFinished = true;
211 }
212
213 void displayTime() {
214
215   static bool newsec = false;
216   static bool newsec06 = false;
217   static int  prevss;
218   static unsigned long prevmilli;
219
220   if (ss != prevss) {
221     prevmilli=millis();
222     prevss = ss;
223     newsec = true;
224     newsec06 = true;
225   }
226
227   if (newsec) {
228     displayTime(hh,mi,ss,dw,dd,mm,yy,true);
229     newsec = false;
230   } else {
231     int delta = millis() - prevmilli;
232     if (delta>600 && newsec06) {
233       displayTime(hh,mi,ss,dw,dd,mm,yy,false);
234       newsec06 = false;
235     }
236   }
237
238 }
239
240 void displayDate(int dd, int mm, int yy) {
241
242   String msg;
243
244   if (screenMode !=mDate) {
245     screenMode = mDate;
246
247     PGM_P day = days[dw];
248     PGM_P month = months[mm];
249
250     sprintf(msgBuf,"%s, %02d %s %04d г",day, dd, month, yy);
251     utf8rus(msgBuf, msgBuf, 255);
252     Panel->displayClear();
253     Panel->setFont(RomanCyrillic);
254
255     Panel->displayScroll(msgBuf, PA_CENTER, PA_SCROLL_LEFT, panelSpeed());
256   }
257 }
258
259 void displayDate() {
260   displayDate(dd,mm,yy);
261 }
262
263 void tickPanel() {
264   if (screenMode == mTime || screenModeRequested != screenMode) { // needs to redraw
265     if (now<1668332133) { message(F("Время еще неизвестно...")); }
266     switch (screenModeRequested) {
267       case mDefault:
268       case mBarrier:
269       case mLast:
270       case mTime:
271         displayTime();
272         break;
273       case mDate:
274         displayDate();
275         break;
276       case mWeather:
277         drawScroll();
278         break;
279       case mMessage:
280         break;
281     }
282   } else if (Panel->displayAnimate()) {
283     screenModeRequested = mTime;
284     currentPriority = 255;
285   }
286 }
287
288