An ESP32-based Redis-watcher and info-displayer https://rpjios.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
10 KiB

  1. #include "zw_displays.h"
  2. #include "zw_logging.h"
  3. #include "zw_common.h"
  4. #include "zw_provision.h"
  5. // TODO: get rid of these externs! (and associated includes!)
  6. extern unsigned long immediateLatency;
  7. extern unsigned long gUDRA;
  8. extern ZWAppConfig gConfig;
  9. #include "zw_redis.h"
  10. extern ZWRedis *gRedis;
  11. #include <ArduinoJson.h>
  12. extern StaticJsonBuffer<1024> jsonBuf;
  13. struct AnimStep
  14. {
  15. int digit;
  16. int bits;
  17. };
  18. AnimStep full_loop[] = {{0, 1}, {1, 1}, {2, 1}, {3, 1}, {3, 3}, {3, 7}, {3, 15}, {2, 9}, {1, 9}, {0, 9}, {0, 25}, {0, 57}, {-1, -1}};
  19. AnimStep light_loop[] = {{0, 1}, {1, 1}, {2, 1}, {3, 1}, {3, 2}, {3, 4}, {3, 8}, {2, 8}, {1, 8}, {0, 8}, {0, 16}, {0, 32}, {-1, -1}};
  20. void __runAnimation(TM1637Display *d, AnimStep *anim, bool cE = false, int s = 0)
  21. {
  22. if (gConfig.deepSleepMode)
  23. return;
  24. uint8_t v[] = {0, 0, 0, 0};
  25. for (AnimStep *w = anim; w->bits != -1 && w->digit != -1; w++)
  26. {
  27. if (cE)
  28. bzero(v, 4);
  29. v[w->digit] = w->bits;
  30. d->setSegments(v);
  31. if (s)
  32. delay(s);
  33. }
  34. }
  35. String __dispSpecNameComp(DisplaySpec* d, int cLimit, int lenLimit = 4)
  36. {
  37. auto sName = String(d->spec.listKey);
  38. sName.replace(":.list", "");
  39. auto cIdx = -1;
  40. for (int i = 0; i < cLimit; i++)
  41. cIdx = sName.indexOf(":", cIdx + 1);
  42. auto retVal = sName.substring(cIdx + 1);
  43. if (retVal.length() > lenLimit)
  44. retVal = retVal.substring(0, lenLimit);
  45. return retVal;
  46. }
  47. String getDispSpecShortName(DisplaySpec* d)
  48. {
  49. return __dispSpecNameComp(d, 3);
  50. }
  51. String getDispSpecSensorName(DisplaySpec* d)
  52. {
  53. return __dispSpecNameComp(d, 2, 3);
  54. }
  55. static uint8_t degFSegs[] = {99, 113};
  56. static uint8_t fSeg[] = {113};
  57. static uint8_t prcntSeg[] = {99, 92};
  58. int noop(int a) { return a; }
  59. void d_def(DisplaySpec *d)
  60. {
  61. #if M5STACKC
  62. M5.Lcd.setTextColor(DARKGREY, BLACK);
  63. M5.Lcd.printf("%s: ", getDispSpecShortName(d).c_str());
  64. M5.Lcd.setTextColor(WHITE, BLACK);
  65. M5.Lcd.printf("%d\n", d->spec.lastVal);
  66. #else
  67. d->disp->showNumberDec(d->spec.lastVal);
  68. #endif
  69. }
  70. void d_tempf(DisplaySpec *d)
  71. {
  72. #if M5STACKC
  73. float curVal = d->spec.lastVal / 100.0;
  74. uint16_t tempColor = curVal > 100 ? RED : (curVal > 95.0 ? ORANGE :
  75. (curVal > 85.0 ? YELLOW : (curVal > 65.0 ? GREEN :
  76. (curVal > 55.0 ? CYAN : (curVal > 40.0 ? BLUE : PURPLE)))));
  77. M5.Lcd.setTextColor(DARKGREY, BLACK);
  78. M5.Lcd.printf("%s: ", getDispSpecSensorName(d).c_str());
  79. M5.Lcd.setTextColor(tempColor, BLACK);
  80. M5.Lcd.printf("%.1fF\n", curVal);
  81. M5.Lcd.setTextColor(WHITE, BLACK);
  82. #else
  83. if (d->spec.lastVal < 10000)
  84. {
  85. d->disp->showNumberDecEx(d->spec.lastVal, 0, false);
  86. d->disp->setSegments(degFSegs, 2, 2);
  87. }
  88. else
  89. {
  90. d->disp->showNumberDecEx(d->spec.lastVal / 100, 0, false, 3);
  91. d->disp->setSegments(fSeg, 1, 3);
  92. }
  93. #endif
  94. }
  95. void d_humidPercent(DisplaySpec *d)
  96. {
  97. #if M5STACKC
  98. M5.Lcd.setTextColor(DARKGREY, BLACK);
  99. M5.Lcd.printf("%s: ", getDispSpecSensorName(d).c_str());
  100. M5.Lcd.setTextColor(WHITE, BLACK);
  101. M5.Lcd.printf("%.1f%%\n", (float)d->spec.lastVal / 100.0);
  102. #else
  103. d->disp->showNumberDecEx(d->spec.lastVal, 0, false);
  104. d->disp->setSegments(prcntSeg, 2, 2);
  105. #endif
  106. }
  107. DisplaySpec gDisplays_AMINI[] = {
  108. {33, 32, nullptr, {"zero:sensor:BME280:temperature:.list", 0, 11, 0.0, 0, noop, d_tempf}},
  109. {26, 25, nullptr, {"zero:sensor:BME280:humidity:.list", 0, 11, 0.0, 0, noop, d_humidPercent}},
  110. {18, 19, nullptr, {"zero:sensor:BME280:pressure:.list", 0, 5, 0.0, 0, [](int i) { return i / 100; }, d_def}},
  111. {-1, -1, nullptr, {nullptr, -1, -1, -1.0, -1, noop, d_def}}};
  112. DisplaySpec gDisplays_EZERO[] = {
  113. {33, 32, nullptr, {"zero:sensor:BME280:pressure:.list", 0, 5, 0.0, 0, [](int i) { return i / 100; }, d_def}},
  114. {18, 19, nullptr, {"zero:sensor:BME280:temperature:.list", 0, 11, 0.0, 0, noop, d_tempf}},
  115. {26, 25, nullptr, {"zed:sensor:SPS30:mc_2p5:.list", 0, 5, 0.0, 0, [](int i) { return i / 100; }, d_def}},
  116. {13, 14, nullptr, {"zero:sensor:BME280:humidity:.list", 0, 11, 0.0, 0, noop, d_humidPercent}},
  117. {-1, -1, nullptr, {nullptr, -1, -1, -1.0, -1, noop, d_def}}};
  118. DisplaySpec gDisplays_ETEST[] = {
  119. {26, 25, nullptr, {"zero:sensor:DHTXX:temperature_fahrenheit:.list", 0, 11, 0.0, 0, noop, d_tempf}},
  120. {33, 32, nullptr, {"zero:sensor:DHTXX:relative_humidity:.list", 0, 5, 0.0, 0, noop, d_humidPercent}},
  121. {-1, -1, nullptr, {nullptr, -1, -1, -1.0, -1, noop, d_def}}};
  122. // pin numbers are just sentinels in M5STACKC definitions: (-1, -1) is the sentinel value
  123. DisplaySpec gDisplays_M5STICKC[] = {
  124. {18, 19, nullptr, {"zero:sensor:BME280:temperature:.list", 0, 11, 0.0, 0, noop, d_tempf}},
  125. {26, 25, nullptr, {"zero:sensor:DHTXX:temperature_fahrenheit:.list", 0, 11, 0.0, 0, noop, d_tempf}},
  126. {13, 14, nullptr, {"zero:sensor:BME280:humidity:.list", 0, 11, 0.0, 0, noop, d_humidPercent}},
  127. {33, 32, nullptr, {"zero:sensor:DHTXX:relative_humidity:.list", 0, 5, 0.0, 0, noop, d_humidPercent}},
  128. {18, 19, nullptr, {"zed:sensor:BME280:temperature:.list", 0, 11, 0.0, 0, noop, d_tempf}},
  129. {13, 14, nullptr, {"zed:sensor:BME280:humidity:.list", 0, 11, 0.0, 0, noop, d_humidPercent}},
  130. {33, 32, nullptr, {"zed:sensor:BME280:pressure:.list", 0, 5, 0.0, 0, [](int i) { return i / 100; }, d_def}},
  131. {26, 25, nullptr, {"zed:sensor:SPS30:mc_2p5:.list", 0, 5, 0.0, 0, [](int i) { return i / 100; }, d_def}},
  132. {-1, -1, nullptr, {nullptr, -1, -1, -1.0, -1, noop, d_def}}};
  133. DisplaySpec gDisplays_NULLSPEC[] = {
  134. {-1, -1, nullptr, {nullptr, -1, -1, -1.0, -1, noop, d_def}}
  135. };
  136. DisplaySpec *zwdisplayInit(String &hostname)
  137. {
  138. DisplaySpec *retSpec = NULL;
  139. if (hostname.equals("ezero"))
  140. {
  141. retSpec = gDisplays_EZERO;
  142. }
  143. else if (hostname.equals("amini"))
  144. {
  145. retSpec = gDisplays_AMINI;
  146. }
  147. else if (hostname.equals("etest"))
  148. {
  149. retSpec = gDisplays_ETEST;
  150. }
  151. else if (hostname.startsWith("stack"))
  152. {
  153. retSpec = gDisplays_M5STICKC;
  154. }
  155. else
  156. {
  157. retSpec = gDisplays_NULLSPEC;
  158. }
  159. if (retSpec)
  160. {
  161. #if M5STACKC
  162. dprint("M5StickC display init\n");
  163. M5.Lcd.setRotation(3);
  164. M5.Lcd.fillScreen(TFT_BLACK);
  165. M5.Axp.ScreenBreath(gConfig.brightness + 7);
  166. #else
  167. zlog("Initializing displays with brightness level %d\n", gConfig.brightness);
  168. auto spec = retSpec;
  169. for (; spec->clockPin != -1 && spec->dioPin != -1; spec++)
  170. {
  171. zlog("Setting up display #%d with clock=%d DIO=%d\n",
  172. (int)(spec - retSpec), spec->clockPin, spec->dioPin);
  173. spec->disp = new TM1637Display(spec->clockPin, spec->dioPin);
  174. if (!gConfig.deepSleepMode)
  175. spec->disp->clear();
  176. spec->disp->setBrightness(gConfig.brightness);
  177. __runAnimation(spec->disp, full_loop, false, 5);
  178. }
  179. if (gConfig.debug && !gConfig.deepSleepMode)
  180. {
  181. EXEC_ALL_DISPS(retSpec, showNumberDecEx((int)(walk - retSpec), 0,
  182. false, 4 - (int)(walk - retSpec), (int)(walk - retSpec)));
  183. delay(2000);
  184. }
  185. #endif
  186. }
  187. return retSpec;
  188. }
  189. void updateDisplay(DisplaySpec *disp)
  190. {
  191. if (gConfig.debug)
  192. __runAnimation(disp->disp, full_loop);
  193. auto __s = LAT_FUNC();
  194. auto lrVec = gRedis->getRange(disp->spec.listKey, disp->spec.startIdx, disp->spec.endIdx);
  195. immediateLatency = LAT_FUNC() - __s;
  196. auto newUDRA = gUDRA == 0 ? immediateLatency : (gUDRA + immediateLatency) / 2;
  197. auto deltaUDRA = newUDRA - gUDRA;
  198. gUDRA = newUDRA;
  199. if (lrVec.size())
  200. {
  201. double acc = 0.0;
  202. for (auto lrStr : lrVec)
  203. {
  204. if (lrStr.length() < 256)
  205. {
  206. jsonBuf.clear();
  207. JsonArray &jsRoot = jsonBuf.parseArray(lrStr.c_str());
  208. disp->spec.lastTs = (double)jsRoot[0];
  209. acc += (double)jsRoot[1];
  210. }
  211. }
  212. if (gConfig.debug)
  213. __runAnimation(disp->disp, light_loop, true);
  214. disp->spec.lastVal = disp->spec.adjFunc((int)((acc * 100.0) / lrVec.size()));
  215. disp->spec.dispFunc(disp);
  216. zlog("[%s] count %d val %d immLat %lu gUDRA %lu (delta %ld)\n",
  217. disp->spec.listKey, lrVec.size(), disp->spec.lastVal, immediateLatency, gUDRA, deltaUDRA);
  218. }
  219. }
  220. void blink(int d)
  221. {
  222. digitalWrite(LED_BLTIN, LED_BLTIN_H);
  223. delay(d);
  224. digitalWrite(LED_BLTIN, LED_BLTIN_L);
  225. delay(d);
  226. digitalWrite(LED_BLTIN, LED_BLTIN_H);
  227. }
  228. void runAnimation(TM1637Display *d, String animation, bool cE, int s)
  229. {
  230. AnimStep *anim = NULL;
  231. if (animation.equals("full_loop"))
  232. {
  233. anim = full_loop;
  234. }
  235. else if (animation.equals("light_loop"))
  236. {
  237. anim = light_loop;
  238. }
  239. else
  240. {
  241. zlog("No animation defined for '%s'!\n", animation.c_str());
  242. return;
  243. }
  244. __runAnimation(d, anim, cE, s);
  245. }
  246. #define EXEC_WITH_EACH_DISP(DISPLIST_START, EFUNC) \
  247. do \
  248. { \
  249. for (DisplaySpec *walk = DISPLIST_START; walk->clockPin != -1 && walk->dioPin != -1; walk++) \
  250. EFUNC(walk->disp); \
  251. } while (0)
  252. void demoForDisp(TM1637Display *disp)
  253. {
  254. __runAnimation(disp, full_loop);
  255. __runAnimation(disp, light_loop);
  256. __runAnimation(disp, full_loop);
  257. __runAnimation(disp, light_loop);
  258. __runAnimation(disp, full_loop);
  259. }
  260. void demoMode(DisplaySpec *displayListStart)
  261. {
  262. dprint("Demo! Bleep bloop!\n");
  263. zlog("Demo! Bleep bloop!\n");
  264. EXEC_WITH_EACH_DISP(displayListStart, demoForDisp);
  265. }
  266. String displayConfigAsJson(DisplaySpec* displayListStart)
  267. {
  268. #define BUFLEN 512
  269. static char _buf[BUFLEN];
  270. bzero(_buf, BUFLEN);
  271. String build = "[";
  272. for (DisplaySpec *walk = displayListStart; walk->clockPin != -1 && walk->dioPin != -1; walk++)
  273. {
  274. snprintf(_buf, BUFLEN,
  275. "{\"clockPin\":%d,\"dioPin\":%d,\"listKey\":\"%s\",\"startIdx\":%d,\"endIdx\":%d}",
  276. walk->clockPin, walk->dioPin, walk->spec.listKey, walk->spec.startIdx, walk->spec.endIdx);
  277. build += String(_buf) + ((walk+1)->clockPin != -1 ? "," : "");
  278. }
  279. build += "]";
  280. return build;
  281. }