xdrv_13_display.ino 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103
  1. /*
  2. xdrv_13_display.ino - Display support for Sonoff-Tasmota
  3. Copyright (C) 2018 Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #if defined(USE_I2C) || defined(USE_SPI)
  16. #ifdef USE_DISPLAY
  17. #define XDRV_13 13
  18. #define DISPLAY_MAX_DRIVERS 16 // Max number of display drivers/models supported by xdsp_interface.ino
  19. #define DISPLAY_MAX_COLS 44 // Max number of columns allowed with command DisplayCols
  20. #define DISPLAY_MAX_ROWS 32 // Max number of lines allowed with command DisplayRows
  21. #define DISPLAY_LOG_ROWS 32 // Number of lines in display log buffer
  22. #define D_CMND_DISPLAY "Display"
  23. #define D_CMND_DISP_ADDRESS "Address"
  24. #define D_CMND_DISP_COLS "Cols"
  25. #define D_CMND_DISP_DIMMER "Dimmer"
  26. #define D_CMND_DISP_MODE "Mode"
  27. #define D_CMND_DISP_MODEL "Model"
  28. #define D_CMND_DISP_REFRESH "Refresh"
  29. #define D_CMND_DISP_ROWS "Rows"
  30. #define D_CMND_DISP_SIZE "Size"
  31. #define D_CMND_DISP_FONT "Font"
  32. #define D_CMND_DISP_ROTATE "Rotate"
  33. #define D_CMND_DISP_TEXT "Text"
  34. enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND,
  35. FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER,
  36. FUNC_DISPLAY_CLEAR, FUNC_DISPLAY_DRAW_FRAME,
  37. FUNC_DISPLAY_DRAW_HLINE, FUNC_DISPLAY_DRAW_VLINE, FUNC_DISPLAY_DRAW_LINE,
  38. FUNC_DISPLAY_DRAW_CIRCLE, FUNC_DISPLAY_FILL_CIRCLE,
  39. FUNC_DISPLAY_DRAW_RECTANGLE, FUNC_DISPLAY_FILL_RECTANGLE,
  40. FUNC_DISPLAY_TEXT_SIZE, FUNC_DISPLAY_FONT_SIZE, FUNC_DISPLAY_ROTATION, FUNC_DISPLAY_DRAW_STRING, FUNC_DISPLAY_ONOFF };
  41. enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL };
  42. enum DisplayCommands { CMND_DISPLAY, CMND_DISP_MODEL, CMND_DISP_MODE, CMND_DISP_REFRESH, CMND_DISP_DIMMER, CMND_DISP_COLS, CMND_DISP_ROWS,
  43. CMND_DISP_SIZE, CMND_DISP_FONT, CMND_DISP_ROTATE, CMND_DISP_TEXT, CMND_DISP_ADDRESS };
  44. const char kDisplayCommands[] PROGMEM =
  45. "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|"
  46. D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS ;
  47. const char S_JSON_DISPLAY_COMMAND_VALUE[] PROGMEM = "{\"" D_CMND_DISPLAY "%s\":\"%s\"}";
  48. const char S_JSON_DISPLAY_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_DISPLAY "%s\":%d}";
  49. const char S_JSON_DISPLAY_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DISPLAY "%s%d\":%d}";
  50. uint8_t disp_power = 0;
  51. uint8_t disp_device = 0;
  52. uint8_t disp_refresh = 1;
  53. int16_t disp_xpos = 0;
  54. int16_t disp_ypos = 0;
  55. uint8_t disp_autodraw = 1;
  56. uint8_t dsp_init;
  57. uint8_t dsp_font;
  58. uint8_t dsp_flag;
  59. uint8_t dsp_on;
  60. uint16_t dsp_x;
  61. uint16_t dsp_y;
  62. uint16_t dsp_x2;
  63. uint16_t dsp_y2;
  64. uint16_t dsp_rad;
  65. uint16_t dsp_color;
  66. int16_t dsp_len;
  67. char *dsp_str;
  68. #ifdef USE_DISPLAY_MODES1TO5
  69. char disp_temp[2]; // C or F
  70. uint8_t disp_subscribed = 0;
  71. char **disp_log_buffer;
  72. uint8_t disp_log_buffer_cols = 0;
  73. uint8_t disp_log_buffer_idx = 0;
  74. uint8_t disp_log_buffer_ptr = 0;
  75. char **disp_screen_buffer;
  76. uint8_t disp_screen_buffer_cols = 0;
  77. uint8_t disp_screen_buffer_rows = 0;
  78. #endif // USE_DISPLAY_MODES1TO5
  79. /*********************************************************************************************/
  80. void DisplayInit(uint8_t mode)
  81. {
  82. dsp_init = mode;
  83. XdspCall(FUNC_DISPLAY_INIT);
  84. }
  85. void DisplayClear(void)
  86. {
  87. XdspCall(FUNC_DISPLAY_CLEAR);
  88. }
  89. void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color)
  90. {
  91. dsp_x = x;
  92. dsp_y = y;
  93. dsp_len = len;
  94. dsp_color = color;
  95. XdspCall(FUNC_DISPLAY_DRAW_HLINE);
  96. }
  97. void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color)
  98. {
  99. dsp_x = x;
  100. dsp_y = y;
  101. dsp_len = len;
  102. dsp_color = color;
  103. XdspCall(FUNC_DISPLAY_DRAW_VLINE);
  104. }
  105. void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color)
  106. {
  107. dsp_x = x;
  108. dsp_y = y;
  109. dsp_x2 = x2;
  110. dsp_y2 = y2;
  111. dsp_color = color;
  112. XdspCall(FUNC_DISPLAY_DRAW_LINE);
  113. }
  114. void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color)
  115. {
  116. dsp_x = x;
  117. dsp_y = y;
  118. dsp_rad = rad;
  119. dsp_color = color;
  120. XdspCall(FUNC_DISPLAY_DRAW_CIRCLE);
  121. }
  122. void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color)
  123. {
  124. dsp_x = x;
  125. dsp_y = y;
  126. dsp_rad = rad;
  127. dsp_color = color;
  128. XdspCall(FUNC_DISPLAY_FILL_CIRCLE);
  129. }
  130. void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color)
  131. {
  132. dsp_x = x;
  133. dsp_y = y;
  134. dsp_x2 = x2;
  135. dsp_y2 = y2;
  136. dsp_color = color;
  137. XdspCall(FUNC_DISPLAY_DRAW_RECTANGLE);
  138. }
  139. void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color)
  140. {
  141. dsp_x = x;
  142. dsp_y = y;
  143. dsp_x2 = x2;
  144. dsp_y2 = y2;
  145. dsp_color = color;
  146. XdspCall(FUNC_DISPLAY_FILL_RECTANGLE);
  147. }
  148. void DisplayDrawFrame(void)
  149. {
  150. XdspCall(FUNC_DISPLAY_DRAW_FRAME);
  151. }
  152. void DisplaySetSize(uint8_t size)
  153. {
  154. Settings.display_size = size &3;
  155. XdspCall(FUNC_DISPLAY_TEXT_SIZE);
  156. }
  157. void DisplaySetFont(uint8_t font)
  158. {
  159. Settings.display_font = font &3;
  160. XdspCall(FUNC_DISPLAY_FONT_SIZE);
  161. }
  162. void DisplaySetRotation(uint8_t rotation)
  163. {
  164. Settings.display_rotate = rotation &3;
  165. XdspCall(FUNC_DISPLAY_ROTATION);
  166. }
  167. void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag)
  168. {
  169. dsp_x = x;
  170. dsp_y = y;
  171. dsp_str = str;
  172. dsp_color = color;
  173. dsp_flag = flag;
  174. XdspCall(FUNC_DISPLAY_DRAW_STRING);
  175. }
  176. void DisplayOnOff(uint8_t on)
  177. {
  178. dsp_on = on;
  179. XdspCall(FUNC_DISPLAY_ONOFF);
  180. }
  181. /*-------------------------------------------------------------------------------------------*/
  182. // get asci number until delimiter and return asci number lenght and value
  183. uint8_t atoiv(char *cp, int16_t *res)
  184. {
  185. uint8_t index = 0;
  186. *res = atoi(cp);
  187. while (*cp) {
  188. if ((*cp>='0' && *cp<='9') || (*cp=='-')) {
  189. cp++;
  190. index++;
  191. } else {
  192. break;
  193. }
  194. }
  195. return index;
  196. }
  197. // get asci number until delimiter and return asci number lenght and value
  198. uint8_t atoiV(char *cp, uint16_t *res)
  199. {
  200. uint8_t index = 0;
  201. *res = atoi(cp);
  202. while (*cp) {
  203. if (*cp>='0' && *cp<='9') {
  204. cp++;
  205. index++;
  206. } else {
  207. break;
  208. }
  209. }
  210. return index;
  211. }
  212. /*-------------------------------------------------------------------------------------------*/
  213. #define DISPLAY_BUFFER_COLS 128 // Max number of characters in linebuf
  214. void DisplayText(void)
  215. {
  216. uint8_t lpos;
  217. uint8_t escape = 0;
  218. uint8_t var;
  219. uint8_t font_x = 6;
  220. uint8_t font_y = 8;
  221. uint8_t fontnumber = 1;
  222. int16_t lin = 0;
  223. int16_t col = 0;
  224. int16_t fill = 0;
  225. int16_t temp;
  226. int16_t temp1;
  227. uint16_t color = 0;
  228. char linebuf[DISPLAY_BUFFER_COLS];
  229. char *dp = linebuf;
  230. char *cp = XdrvMailbox.data;
  231. memset(linebuf, ' ', sizeof(linebuf));
  232. linebuf[sizeof(linebuf)-1] = 0;
  233. *dp = 0;
  234. while (*cp) {
  235. if (!escape) {
  236. // check for escape
  237. if (*cp == '[') {
  238. escape = 1;
  239. cp++;
  240. // if string in buffer print it
  241. if ((uint32_t)dp - (uint32_t)linebuf) {
  242. if (!fill) { *dp = 0; }
  243. if (col > 0 && lin > 0) {
  244. // use col and lin
  245. DisplayDrawStringAt(col, lin, linebuf, color, 1);
  246. } else {
  247. // use disp_xpos, disp_ypos
  248. DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, color, 0);
  249. }
  250. memset(linebuf, ' ', sizeof(linebuf));
  251. linebuf[sizeof(linebuf)-1] = 0;
  252. dp = linebuf;
  253. }
  254. } else {
  255. // copy chars
  256. if (dp < (linebuf + DISPLAY_BUFFER_COLS)) { *dp++ = *cp++; }
  257. }
  258. } else {
  259. // check escapes
  260. if (*cp == ']') {
  261. escape = 0;
  262. cp++;
  263. } else {
  264. // analyze escapes
  265. switch (*cp++) {
  266. case 'z':
  267. // clear display
  268. DisplayClear();
  269. disp_xpos = 0;
  270. disp_ypos = 0;
  271. col = 0;
  272. lin = 0;
  273. break;
  274. case 'i':
  275. // init display with partial update
  276. DisplayInit(DISPLAY_INIT_PARTIAL);
  277. break;
  278. case 'I':
  279. // init display with full refresh
  280. DisplayInit(DISPLAY_INIT_FULL);
  281. break;
  282. case 'o':
  283. DisplayOnOff(0);
  284. break;
  285. case 'O':
  286. DisplayOnOff(1);
  287. break;
  288. case 'x':
  289. // set disp_xpos
  290. var = atoiv(cp, &disp_xpos);
  291. cp += var;
  292. break;
  293. case 'y':
  294. // set disp_ypos
  295. var = atoiv(cp, &disp_ypos);
  296. cp += var;
  297. break;
  298. case 'l':
  299. // text line lxx
  300. var = atoiv(cp, &lin);
  301. cp += var;
  302. //display.setCursor(display.getCursorX(),(lin-1)*font_y*txtsize);
  303. break;
  304. case 'c':
  305. // text column cxx
  306. var = atoiv(cp, &col);
  307. cp += var;
  308. //display.setCursor((col-1)*font_x*txtsize,display.getCursorY());
  309. break;
  310. case 'C':
  311. // text color cxx
  312. var = atoiV(cp, &color);
  313. cp += var;
  314. break;
  315. case 'p':
  316. // pad field with spaces fxx
  317. var = atoiv(cp, &fill);
  318. cp += var;
  319. linebuf[fill] = 0;
  320. break;
  321. case 'h':
  322. // hor line to
  323. var = atoiv(cp, &temp);
  324. cp += var;
  325. if (temp < 0) {
  326. DisplayDrawHLine(disp_xpos + temp, disp_ypos, -temp, color);
  327. } else {
  328. DisplayDrawHLine(disp_xpos, disp_ypos, temp, color);
  329. }
  330. disp_xpos += temp;
  331. break;
  332. case 'v':
  333. // vert line to
  334. var = atoiv(cp, &temp);
  335. cp += var;
  336. if (temp < 0) {
  337. DisplayDrawVLine(disp_xpos, disp_ypos + temp, -temp, color);
  338. } else {
  339. DisplayDrawVLine(disp_xpos, disp_ypos, temp, color);
  340. }
  341. disp_ypos += temp;
  342. break;
  343. case 'L':
  344. // any line to
  345. var = atoiv(cp, &temp);
  346. cp += var;
  347. cp++;
  348. var = atoiv(cp, &temp1);
  349. cp += var;
  350. DisplayDrawLine(disp_xpos, disp_ypos, temp, temp1, color);
  351. disp_xpos += temp;
  352. disp_ypos += temp1;
  353. break;
  354. case 'k':
  355. // circle
  356. var = atoiv(cp, &temp);
  357. cp += var;
  358. DisplayDrawCircle(disp_xpos, disp_ypos, temp, color);
  359. break;
  360. case 'K':
  361. // filled circle
  362. var = atoiv(cp, &temp);
  363. cp += var;
  364. DisplayDrawFilledCircle(disp_xpos, disp_ypos, temp, color);
  365. break;
  366. case 'r':
  367. // rectangle
  368. var = atoiv(cp, &temp);
  369. cp += var;
  370. cp++;
  371. var = atoiv(cp, &temp1);
  372. cp += var;
  373. DisplayDrawRectangle(disp_xpos, disp_ypos, temp, temp1, color);
  374. break;
  375. case 'R':
  376. // filled rectangle
  377. var = atoiv(cp, &temp);
  378. cp += var;
  379. cp++;
  380. var = atoiv(cp, &temp1);
  381. cp += var;
  382. DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, color);
  383. break;
  384. case 't': {
  385. if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) {
  386. snprintf_P(dp, 5, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute);
  387. dp += 5;
  388. }
  389. break;
  390. }
  391. case 'd':
  392. // force draw grafics buffer
  393. DisplayDrawFrame();
  394. break;
  395. case 'D':
  396. // set auto draw mode
  397. disp_autodraw = *cp&1;
  398. cp += 1;
  399. break;
  400. case 's':
  401. // size sx
  402. DisplaySetSize(*cp&3);
  403. cp += 1;
  404. break;
  405. case 'f':
  406. // font sx
  407. DisplaySetFont(*cp&3);
  408. cp += 1;
  409. break;
  410. case 'a':
  411. // rotation angle
  412. DisplaySetRotation(*cp&3);
  413. cp+=1;
  414. break;
  415. default:
  416. // unknown escape
  417. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("Unknown Escape"));
  418. goto exit;
  419. break;
  420. }
  421. }
  422. }
  423. }
  424. exit:
  425. // now draw buffer
  426. if ((uint32_t)dp - (uint32_t)linebuf) {
  427. if (!fill) { *dp = 0; }
  428. if (col > 0 && lin > 0) {
  429. // use col and lin
  430. DisplayDrawStringAt(col, lin, linebuf, color, 1);
  431. } else {
  432. // use disp_xpos, disp_ypos
  433. DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, color, 0);
  434. }
  435. }
  436. // draw buffer
  437. if (disp_autodraw) { DisplayDrawFrame(); }
  438. }
  439. /*********************************************************************************************/
  440. #ifdef USE_DISPLAY_MODES1TO5
  441. void DisplayClearScreenBuffer(void)
  442. {
  443. if (disp_screen_buffer_cols) {
  444. for (byte i = 0; i < disp_screen_buffer_rows; i++) {
  445. memset(disp_screen_buffer[i], 0, disp_screen_buffer_cols);
  446. }
  447. }
  448. }
  449. void DisplayFreeScreenBuffer(void)
  450. {
  451. if (disp_screen_buffer != NULL) {
  452. for (byte i = 0; i < disp_screen_buffer_rows; i++) {
  453. if (disp_screen_buffer[i] != NULL) { free(disp_screen_buffer[i]); }
  454. }
  455. free(disp_screen_buffer);
  456. disp_screen_buffer_cols = 0;
  457. disp_screen_buffer_rows = 0;
  458. }
  459. }
  460. void DisplayAllocScreenBuffer(void)
  461. {
  462. if (!disp_screen_buffer_cols) {
  463. disp_screen_buffer_rows = Settings.display_rows;
  464. disp_screen_buffer = (char**)malloc(sizeof(*disp_screen_buffer) * disp_screen_buffer_rows);
  465. if (disp_screen_buffer != NULL) {
  466. for (byte i = 0; i < disp_screen_buffer_rows; i++) {
  467. disp_screen_buffer[i] = (char*)malloc(sizeof(*disp_screen_buffer[i]) * (Settings.display_cols[0] +1));
  468. if (disp_screen_buffer[i] == NULL) {
  469. DisplayFreeScreenBuffer();
  470. break;
  471. }
  472. }
  473. }
  474. if (disp_screen_buffer != NULL) {
  475. disp_screen_buffer_cols = Settings.display_cols[0] +1;
  476. DisplayClearScreenBuffer();
  477. }
  478. }
  479. }
  480. void DisplayReAllocScreenBuffer(void)
  481. {
  482. DisplayFreeScreenBuffer();
  483. DisplayAllocScreenBuffer();
  484. }
  485. void DisplayFillScreen(uint8_t line)
  486. {
  487. byte len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]);
  488. if (len) {
  489. memset(disp_screen_buffer[line] + strlen(disp_screen_buffer[line]), 0x20, len);
  490. disp_screen_buffer[line][disp_screen_buffer_cols -1] = 0;
  491. }
  492. }
  493. /*-------------------------------------------------------------------------------------------*/
  494. void DisplayClearLogBuffer(void)
  495. {
  496. if (disp_log_buffer_cols) {
  497. for (byte i = 0; i < DISPLAY_LOG_ROWS; i++) {
  498. memset(disp_log_buffer[i], 0, disp_log_buffer_cols);
  499. }
  500. }
  501. }
  502. void DisplayFreeLogBuffer(void)
  503. {
  504. if (disp_log_buffer != NULL) {
  505. for (byte i = 0; i < DISPLAY_LOG_ROWS; i++) {
  506. if (disp_log_buffer[i] != NULL) { free(disp_log_buffer[i]); }
  507. }
  508. free(disp_log_buffer);
  509. disp_log_buffer_cols = 0;
  510. }
  511. }
  512. void DisplayAllocLogBuffer(void)
  513. {
  514. if (!disp_log_buffer_cols) {
  515. disp_log_buffer = (char**)malloc(sizeof(*disp_log_buffer) * DISPLAY_LOG_ROWS);
  516. if (disp_log_buffer != NULL) {
  517. for (byte i = 0; i < DISPLAY_LOG_ROWS; i++) {
  518. disp_log_buffer[i] = (char*)malloc(sizeof(*disp_log_buffer[i]) * (Settings.display_cols[0] +1));
  519. if (disp_log_buffer[i] == NULL) {
  520. DisplayFreeLogBuffer();
  521. break;
  522. }
  523. }
  524. }
  525. if (disp_log_buffer != NULL) {
  526. disp_log_buffer_cols = Settings.display_cols[0] +1;
  527. DisplayClearLogBuffer();
  528. }
  529. }
  530. }
  531. void DisplayReAllocLogBuffer(void)
  532. {
  533. DisplayFreeLogBuffer();
  534. DisplayAllocLogBuffer();
  535. }
  536. void DisplayLogBufferAdd(char* txt)
  537. {
  538. if (disp_log_buffer_cols) {
  539. strlcpy(disp_log_buffer[disp_log_buffer_idx], txt, disp_log_buffer_cols); // This preserves the % sign where printf won't
  540. disp_log_buffer_idx++;
  541. if (DISPLAY_LOG_ROWS == disp_log_buffer_idx) { disp_log_buffer_idx = 0; }
  542. }
  543. }
  544. char* DisplayLogBuffer(char temp_code)
  545. {
  546. char* result = NULL;
  547. if (disp_log_buffer_cols) {
  548. if (disp_log_buffer_idx != disp_log_buffer_ptr) {
  549. result = disp_log_buffer[disp_log_buffer_ptr];
  550. disp_log_buffer_ptr++;
  551. if (DISPLAY_LOG_ROWS == disp_log_buffer_ptr) { disp_log_buffer_ptr = 0; }
  552. char *pch = strchr(result, '~'); // = 0x7E (~) Replace degrees character (276 octal)
  553. if (pch != NULL) { result[pch - result] = temp_code; }
  554. }
  555. }
  556. return result;
  557. }
  558. void DisplayLogBufferInit(void)
  559. {
  560. if (Settings.display_mode) {
  561. disp_log_buffer_idx = 0;
  562. disp_log_buffer_ptr = 0;
  563. disp_refresh = Settings.display_refresh;
  564. snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%c"), TempUnit());
  565. DisplayReAllocLogBuffer();
  566. char buffer[40];
  567. snprintf_P(buffer, sizeof(buffer), PSTR(D_VERSION " %s%s"), my_version, my_image);
  568. DisplayLogBufferAdd(buffer);
  569. snprintf_P(buffer, sizeof(buffer), PSTR("Display mode %d"), Settings.display_mode);
  570. DisplayLogBufferAdd(buffer);
  571. snprintf_P(buffer, sizeof(buffer), PSTR(D_CMND_HOSTNAME " %s"), my_hostname);
  572. DisplayLogBufferAdd(buffer);
  573. snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), Settings.sta_ssid[Settings.sta_active]);
  574. DisplayLogBufferAdd(buffer);
  575. snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_MAC " %s"), WiFi.macAddress().c_str());
  576. DisplayLogBufferAdd(buffer);
  577. if (!global_state.wifi_down) {
  578. snprintf_P(buffer, sizeof(buffer), PSTR("IP %s"), WiFi.localIP().toString().c_str());
  579. DisplayLogBufferAdd(buffer);
  580. snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_RSSI " %d%%"), WifiGetRssiAsQuality(WiFi.RSSI()));
  581. DisplayLogBufferAdd(buffer);
  582. }
  583. }
  584. }
  585. /*********************************************************************************************\
  586. * Sensors
  587. \*********************************************************************************************/
  588. enum SensorQuantity {
  589. JSON_TEMPERATURE,
  590. JSON_HUMIDITY, JSON_LIGHT, JSON_NOISE, JSON_AIRQUALITY,
  591. JSON_PRESSURE, JSON_PRESSUREATSEALEVEL,
  592. JSON_ILLUMINANCE,
  593. JSON_GAS,
  594. JSON_YESTERDAY, JSON_TOTAL, JSON_TODAY,
  595. JSON_PERIOD,
  596. JSON_POWERFACTOR, JSON_COUNTER, JSON_ANALOG_INPUT, JSON_UV_LEVEL,
  597. JSON_CURRENT,
  598. JSON_VOLTAGE,
  599. JSON_POWERUSAGE,
  600. JSON_CO2,
  601. JSON_FREQUENCY };
  602. const char kSensorQuantity[] PROGMEM =
  603. D_JSON_TEMPERATURE "|" // degrees
  604. D_JSON_HUMIDITY "|" D_JSON_LIGHT "|" D_JSON_NOISE "|" D_JSON_AIRQUALITY "|" // percentage
  605. D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" // hPa
  606. D_JSON_ILLUMINANCE "|" // lx
  607. D_JSON_GAS "|" // kOhm
  608. D_JSON_YESTERDAY "|" D_JSON_TOTAL "|" D_JSON_TODAY "|" // kWh
  609. D_JSON_PERIOD "|" // Wh
  610. D_JSON_POWERFACTOR "|" D_JSON_COUNTER "|" D_JSON_ANALOG_INPUT "|" D_JSON_UV_LEVEL "|" // No unit
  611. D_JSON_CURRENT "|" // Ampere
  612. D_JSON_VOLTAGE "|" // Volt
  613. D_JSON_POWERUSAGE "|" // Watt
  614. D_JSON_CO2 "|" // ppm
  615. D_JSON_FREQUENCY ; // Hz
  616. void DisplayJsonValue(const char *topic, const char* device, const char* mkey, const char* value)
  617. {
  618. char quantity[TOPSZ];
  619. char buffer[Settings.display_cols[0] +1];
  620. char spaces[Settings.display_cols[0]];
  621. char source[Settings.display_cols[0] - Settings.display_cols[1]];
  622. char svalue[Settings.display_cols[1] +1];
  623. ShowFreeMem(PSTR("DisplayJsonValue"));
  624. memset(spaces, 0x20, sizeof(spaces));
  625. spaces[sizeof(spaces) -1] = '\0';
  626. snprintf_P(source, sizeof(source), PSTR("%s/%s%s"), topic, mkey, spaces); // pow1/Voltage
  627. int quantity_code = GetCommandCode(quantity, sizeof(quantity), mkey, kSensorQuantity);
  628. if ((-1 == quantity_code) || !strcmp_P(mkey, S_RSLT_POWER)) { // Ok: Power, Not ok: POWER
  629. return;
  630. }
  631. if (JSON_TEMPERATURE == quantity_code) {
  632. snprintf_P(svalue, sizeof(svalue), PSTR("%s~%s"), value, disp_temp);
  633. }
  634. else if ((quantity_code >= JSON_HUMIDITY) && (quantity_code <= JSON_AIRQUALITY)) {
  635. snprintf_P(svalue, sizeof(svalue), PSTR("%s%%"), value);
  636. }
  637. else if ((quantity_code >= JSON_PRESSURE) && (quantity_code <= JSON_PRESSUREATSEALEVEL)) {
  638. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_PRESSURE), value);
  639. }
  640. else if (JSON_ILLUMINANCE == quantity_code) {
  641. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_LUX), value);
  642. }
  643. else if (JSON_GAS == quantity_code) {
  644. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOOHM), value);
  645. }
  646. else if ((quantity_code >= JSON_YESTERDAY) && (quantity_code <= JSON_TODAY)) {
  647. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOWATTHOUR), value);
  648. }
  649. else if (JSON_PERIOD == quantity_code) {
  650. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATTHOUR), value);
  651. }
  652. else if ((quantity_code >= JSON_POWERFACTOR) && (quantity_code <= JSON_UV_LEVEL)) {
  653. snprintf_P(svalue, sizeof(svalue), PSTR("%s"), value);
  654. }
  655. else if (JSON_CURRENT == quantity_code) {
  656. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_AMPERE), value);
  657. }
  658. else if (JSON_VOLTAGE == quantity_code) {
  659. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_VOLT), value);
  660. }
  661. else if (JSON_POWERUSAGE == quantity_code) {
  662. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATT), value);
  663. }
  664. else if (JSON_CO2 == quantity_code) {
  665. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_PARTS_PER_MILLION), value);
  666. }
  667. else if (JSON_FREQUENCY == quantity_code) {
  668. snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_HERTZ), value);
  669. }
  670. snprintf_P(buffer, sizeof(buffer), PSTR("%s %s"), source, svalue);
  671. // snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "mkey [%s], source [%s], value [%s], quantity_code %d, log_buffer [%s]"), mkey, source, value, quantity_code, buffer);
  672. // AddLog(LOG_LEVEL_DEBUG);
  673. DisplayLogBufferAdd(buffer);
  674. }
  675. void DisplayAnalyzeJson(char *topic, char *json)
  676. {
  677. // //tele/pow2/STATE {"Time":"2017-09-20T11:53:03", "Uptime":10, "Vcc":3.123, "POWER":"ON", "Wifi":{"AP":2, "SSId":"indebuurt2", "RSSI":68, "APMac":"00:22:6B:FE:8E:20"}}
  678. // //tele/pow2/ENERGY {"Time":"2017-09-20T11:53:03", "Total":6.522, "Yesterday":0.150, "Today":0.073, "Period":0.5, "Power":12.1, "Factor":0.56, "Voltage":210.1, "Current":0.102}
  679. // tele/pow1/SENSOR = {"Time":"2018-01-02T17:13:17","ENERGY":{"Total":13.091,"Yesterday":0.060,"Today":0.046,"Period":0.2,"Power":9.8,"Factor":0.49,"Voltage":206.8,"Current":0.096}}
  680. // tele/dual/STATE {"Time":"2017-09-20T11:53:03","Uptime":25,"Vcc":3.178,"POWER1":"OFF","POWER2":"OFF","Wifi":{"AP":2,"SSId":"indebuurt2","RSSI":100,"APMac":"00:22:6B:FE:8E:20"}}
  681. // tele/sc/SENSOR {"Time":"2017-09-20T11:53:09","Temperature":24.0,"Humidity":16.0,"Light":30,"Noise":20,"AirQuality":100,"TempUnit":"C"}
  682. // tele/rf1/SENSOR {"Time":"2017-09-20T11:53:23","BH1750":{"Illuminance":57}}
  683. // tele/wemos5/SENSOR {"Time":"2017-09-20T11:53:53","SHT1X":{"Temperature":20.1,"Humidity":58.9},"HTU21":{"Temperature":20.7,"Humidity":58.5},"BMP280":{"Temperature":21.6,"Pressure":1020.3},"TempUnit":"C"}
  684. // tele/th1/SENSOR {"Time":"2017-09-20T11:54:48","DS18B20":{"Temperature":49.7},"TempUnit":"C"}
  685. const char *tempunit;
  686. // char jsonStr[MESSZ];
  687. // strlcpy(jsonStr, json, sizeof(jsonStr)); // Save original before destruction by JsonObject
  688. String jsonStr = json; // Move from stack to heap to fix watchdogs (20180626)
  689. StaticJsonBuffer<1024> jsonBuf;
  690. JsonObject &root = jsonBuf.parseObject(jsonStr);
  691. if (root.success()) {
  692. tempunit = root[D_JSON_TEMPERATURE_UNIT];
  693. if (tempunit) {
  694. snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), tempunit);
  695. // snprintf_P(log_data, sizeof(log_data), disp_temp);
  696. // AddLog(LOG_LEVEL_DEBUG);
  697. }
  698. for (JsonObject::iterator it = root.begin(); it != root.end(); ++it) {
  699. JsonVariant value = it->value;
  700. if (value.is<JsonObject>()) {
  701. JsonObject& Object2 = value;
  702. for (JsonObject::iterator it2 = Object2.begin(); it2 != Object2.end(); ++it2) {
  703. JsonVariant value2 = it2->value;
  704. if (value2.is<JsonObject>()) {
  705. JsonObject& Object3 = value2;
  706. for (JsonObject::iterator it3 = Object3.begin(); it3 != Object3.end(); ++it3) {
  707. DisplayJsonValue(topic, it->key, it3->key, it3->value.as<const char*>()); // Sensor 56%
  708. }
  709. } else {
  710. DisplayJsonValue(topic, it->key, it2->key, it2->value.as<const char*>()); // Sensor 56%
  711. }
  712. }
  713. } else {
  714. DisplayJsonValue(topic, it->key, it->key, it->value.as<const char*>()); // Topic 56%
  715. }
  716. }
  717. }
  718. }
  719. void DisplayMqttSubscribe(void)
  720. {
  721. /* Subscribe to tele messages only
  722. * Supports the following FullTopic formats
  723. * - %prefix%/%topic%
  724. * - home/%prefix%/%topic%
  725. * - home/level2/%prefix%/%topic% etc.
  726. */
  727. // if (Settings.display_mode &0x04) {
  728. if (Settings.display_model) {
  729. char stopic[TOPSZ];
  730. char ntopic[TOPSZ];
  731. ntopic[0] = '\0';
  732. strlcpy(stopic, Settings.mqtt_fulltopic, sizeof(stopic));
  733. char *tp = strtok(stopic, "/");
  734. while (tp != NULL) {
  735. if (!strcmp_P(tp, PSTR(MQTT_TOKEN_PREFIX))) {
  736. break;
  737. }
  738. strncat_P(ntopic, PSTR("+/"), sizeof(ntopic) - strlen(ntopic) -1); // Add single-level wildcards
  739. tp = strtok(NULL, "/");
  740. }
  741. strncat(ntopic, Settings.mqtt_prefix[2], sizeof(ntopic) - strlen(ntopic) -1); // Subscribe to tele messages
  742. strncat_P(ntopic, PSTR("/#"), sizeof(ntopic) - strlen(ntopic) -1); // Add multi-level wildcard
  743. MqttSubscribe(ntopic);
  744. disp_subscribed = 1;
  745. } else {
  746. disp_subscribed = 0;
  747. }
  748. }
  749. boolean DisplayMqttData(void)
  750. {
  751. if (disp_subscribed) {
  752. char stopic[TOPSZ];
  753. snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), Settings.mqtt_prefix[2]); // tele/
  754. char *tp = strstr(XdrvMailbox.topic, stopic);
  755. if (tp) { // tele/sonoff/SENSOR
  756. if (Settings.display_mode &0x04) {
  757. tp = tp + strlen(stopic); // sonoff/SENSOR
  758. char *topic = strtok(tp, "/"); // sonoff
  759. DisplayAnalyzeJson(topic, XdrvMailbox.data);
  760. }
  761. return true;
  762. }
  763. }
  764. return false;
  765. }
  766. void DisplayLocalSensor(void)
  767. {
  768. if ((Settings.display_mode &0x02) && (0 == tele_period)) {
  769. DisplayAnalyzeJson(mqtt_topic, mqtt_data);
  770. }
  771. }
  772. #endif // USE_DISPLAY_MODES1TO5
  773. /*********************************************************************************************\
  774. * Public
  775. \*********************************************************************************************/
  776. void DisplayInitDriver(void)
  777. {
  778. XdspCall(FUNC_DISPLAY_INIT_DRIVER);
  779. // snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model);
  780. // AddLog(LOG_LEVEL_DEBUG);
  781. if (Settings.display_model) {
  782. devices_present++;
  783. disp_device = devices_present;
  784. #ifndef USE_DISPLAY_MODES1TO5
  785. Settings.display_mode = 0;
  786. #else
  787. DisplayLogBufferInit();
  788. #endif // USE_DISPLAY_MODES1TO5
  789. }
  790. }
  791. void DisplaySetPower(void)
  792. {
  793. disp_power = bitRead(XdrvMailbox.index, disp_device -1);
  794. if (Settings.display_model) {
  795. XdspCall(FUNC_DISPLAY_POWER);
  796. }
  797. }
  798. /*********************************************************************************************\
  799. * Commands
  800. \*********************************************************************************************/
  801. boolean DisplayCommand(void)
  802. {
  803. char command [CMDSZ];
  804. boolean serviced = true;
  805. uint8_t disp_len = strlen(D_CMND_DISPLAY); // Prep for string length change
  806. if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_DISPLAY), disp_len)) { // Prefix
  807. int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic +disp_len, kDisplayCommands);
  808. if (-1 == command_code) {
  809. serviced = false; // Unknown command
  810. }
  811. else if (CMND_DISPLAY == command_code) {
  812. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\""
  813. D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"),
  814. Settings.display_model, Settings.display_mode, Settings.display_dimmer, Settings.display_size, Settings.display_font, Settings.display_rotate, Settings.display_refresh,
  815. Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows);
  816. }
  817. else if (CMND_DISP_MODEL == command_code) {
  818. if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) {
  819. uint8_t last_display_model = Settings.display_model;
  820. Settings.display_model = XdrvMailbox.payload;
  821. if (XdspCall(FUNC_DISPLAY_MODEL)) {
  822. restart_flag = 2; // Restart to re-init interface and add/Remove MQTT subscribe
  823. } else {
  824. Settings.display_model = last_display_model;
  825. }
  826. }
  827. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_model);
  828. }
  829. else if (CMND_DISP_MODE == command_code) {
  830. #ifdef USE_DISPLAY_MODES1TO5
  831. /* Matrix LCD / Oled TFT
  832. * 1 = Text up and time Time
  833. * 2 = Date Local sensors Local sensors
  834. * 3 = Day Local sensors and time Local sensors and time
  835. * 4 = Mqtt left and time Mqtt (incl local) sensors Mqtt (incl local) sensors
  836. * 5 = Mqtt up and time Mqtt (incl local) sensors and time Mqtt (incl local) sensors and time
  837. */
  838. if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) {
  839. uint8_t last_display_mode = Settings.display_mode;
  840. Settings.display_mode = XdrvMailbox.payload;
  841. if (!disp_subscribed) {
  842. restart_flag = 2; // Restart to Add/Remove MQTT subscribe
  843. } else {
  844. if (last_display_mode && !Settings.display_mode) { // Switch to mode 0
  845. DisplayInit(DISPLAY_INIT_MODE);
  846. DisplayClear();
  847. } else {
  848. // if (!last_display_mode && Settings.display_mode) { // Switch to non mode 0
  849. DisplayLogBufferInit();
  850. DisplayInit(DISPLAY_INIT_MODE);
  851. }
  852. }
  853. }
  854. #endif // USE_DISPLAY_MODES1TO5
  855. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_mode);
  856. }
  857. else if (CMND_DISP_DIMMER == command_code) {
  858. if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
  859. Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; // Correction for Domoticz (0 - 15)
  860. if (Settings.display_dimmer && !(disp_power)) {
  861. ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY);
  862. }
  863. else if (!Settings.display_dimmer && disp_power) {
  864. ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY);
  865. }
  866. }
  867. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_dimmer);
  868. }
  869. else if (CMND_DISP_SIZE == command_code) {
  870. if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) {
  871. Settings.display_size = XdrvMailbox.payload;
  872. }
  873. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_size);
  874. }
  875. else if (CMND_DISP_FONT == command_code) {
  876. if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) {
  877. Settings.display_font = XdrvMailbox.payload;
  878. }
  879. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_font);
  880. }
  881. else if (CMND_DISP_ROTATE == command_code) {
  882. if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) {
  883. if (Settings.display_rotate != XdrvMailbox.payload) {
  884. /*
  885. // Needs font info regarding height and width
  886. if ((Settings.display_rotate &1) != (XdrvMailbox.payload &1)) {
  887. uint8_t temp_rows = Settings.display_rows;
  888. Settings.display_rows = Settings.display_cols[0];
  889. Settings.display_cols[0] = temp_rows;
  890. #ifdef USE_DISPLAY_MODES1TO5
  891. DisplayReAllocScreenBuffer();
  892. #endif // USE_DISPLAY_MODES1TO5
  893. }
  894. */
  895. Settings.display_rotate = XdrvMailbox.payload;
  896. DisplayInit(DISPLAY_INIT_MODE);
  897. #ifdef USE_DISPLAY_MODES1TO5
  898. DisplayLogBufferInit();
  899. #endif // USE_DISPLAY_MODES1TO5
  900. }
  901. }
  902. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_rotate);
  903. }
  904. else if (CMND_DISP_TEXT == command_code) {
  905. mqtt_data[0] = '\0';
  906. if (disp_device && XdrvMailbox.data_len > 0) {
  907. #ifndef USE_DISPLAY_MODES1TO5
  908. DisplayText();
  909. #else
  910. if (!Settings.display_mode) {
  911. DisplayText();
  912. } else {
  913. DisplayLogBufferAdd(XdrvMailbox.data);
  914. }
  915. #endif // USE_DISPLAY_MODES1TO5
  916. } else {
  917. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("No Text"));
  918. }
  919. if (mqtt_data[0] == '\0') {
  920. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_VALUE, command, XdrvMailbox.data);
  921. }
  922. }
  923. else if ((CMND_DISP_ADDRESS == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) {
  924. if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) {
  925. Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload;
  926. }
  927. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, Settings.display_address[XdrvMailbox.index -1]);
  928. }
  929. else if (CMND_DISP_REFRESH == command_code) {
  930. if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) {
  931. Settings.display_refresh = XdrvMailbox.payload;
  932. }
  933. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_refresh);
  934. }
  935. else if ((CMND_DISP_COLS == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) {
  936. if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) {
  937. Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload;
  938. #ifdef USE_DISPLAY_MODES1TO5
  939. if (1 == XdrvMailbox.index) {
  940. DisplayLogBufferInit();
  941. DisplayReAllocScreenBuffer();
  942. }
  943. #endif // USE_DISPLAY_MODES1TO5
  944. }
  945. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, Settings.display_cols[XdrvMailbox.index -1]);
  946. }
  947. else if (CMND_DISP_ROWS == command_code) {
  948. if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) {
  949. Settings.display_rows = XdrvMailbox.payload;
  950. #ifdef USE_DISPLAY_MODES1TO5
  951. DisplayLogBufferInit();
  952. DisplayReAllocScreenBuffer();
  953. #endif // USE_DISPLAY_MODES1TO5
  954. }
  955. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_rows);
  956. }
  957. else serviced = false; // Unknown command
  958. }
  959. else serviced = false; // Unknown command
  960. return serviced;
  961. }
  962. /*********************************************************************************************\
  963. * Interface
  964. \*********************************************************************************************/
  965. boolean Xdrv13(byte function)
  966. {
  967. boolean result = false;
  968. if ((i2c_flg || spi_flg || soft_spi_flg) && XdspPresent()) {
  969. switch (function) {
  970. case FUNC_PRE_INIT:
  971. DisplayInitDriver();
  972. break;
  973. case FUNC_EVERY_50_MSECOND:
  974. if (Settings.display_model) { XdspCall(FUNC_DISPLAY_EVERY_50_MSECOND); }
  975. break;
  976. case FUNC_COMMAND:
  977. result = DisplayCommand();
  978. break;
  979. case FUNC_SET_POWER:
  980. DisplaySetPower();
  981. break;
  982. #ifdef USE_DISPLAY_MODES1TO5
  983. case FUNC_EVERY_SECOND:
  984. if (Settings.display_model && Settings.display_mode) { XdspCall(FUNC_DISPLAY_EVERY_SECOND); }
  985. break;
  986. case FUNC_MQTT_SUBSCRIBE:
  987. DisplayMqttSubscribe();
  988. break;
  989. case FUNC_MQTT_DATA:
  990. result = DisplayMqttData();
  991. break;
  992. case FUNC_SHOW_SENSOR:
  993. DisplayLocalSensor();
  994. break;
  995. #endif // USE_DISPLAY_MODES1TO5
  996. }
  997. }
  998. return result;
  999. }
  1000. #endif // USE_DISPLAY
  1001. #endif // USE_I2C or USE_SPI