Fork of the espurna firmware for `mhsw` switches
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.

662 lines
23 KiB

  1. /*
  2. GARLAND MODULE
  3. Copyright (C) 2020 by Dmitry Blinov <dblinov76 at gmail dot com>
  4. Inspired by https://github.com/Vasil-Pahomov/ArWs2812 (currently https://github.com/Vasil-Pahomov/Liana)
  5. Tested on 300 led strip.
  6. The most time consuming operation is actually showing leds by Adafruit Neopixel. It take about 1870 mcs.
  7. More long strip can take more time to show.
  8. Currently animation calculation, brightness calculation/transition and showing makes in one loop cycle.
  9. Debug output shows timings. Overal timing should be not more that 3000 ms.
  10. MQTT control:
  11. "command:["immediate", "queue", "sequence", "reset"]
  12. "enable":["true", "false"]
  13. "brightness":[0-255]
  14. "speed":[30-60]
  15. "animation":["PixieDust", "Sparkr", "Run", "Stars", "Spread", "R"andCyc", "Fly", "Comets", "Assemble", "Dolphins", "Salut"]
  16. "palette":["RGB", "Rainbow", "Stripe", "Party", "Heat", Fire", "Blue", "Sun", "Lime", "Pastel"]
  17. "duration":5000
  18. */
  19. #include "garland.h"
  20. #if GARLAND_SUPPORT
  21. #include <Adafruit_NeoPixel.h>
  22. #include <vector>
  23. #include "garland/color.h"
  24. #include "garland/palette.h"
  25. #include "garland/scene.h"
  26. #include "mqtt.h"
  27. #include "ws.h"
  28. const char* NAME_GARLAND_ENABLED = "garlandEnabled";
  29. const char* NAME_GARLAND_BRIGHTNESS = "garlandBrightness";
  30. const char* NAME_GARLAND_SPEED = "garlandSpeed";
  31. const char* NAME_GARLAND_SWITCH = "garland_switch";
  32. const char* NAME_GARLAND_SET_BRIGHTNESS = "garland_set_brightness";
  33. const char* NAME_GARLAND_SET_SPEED = "garland_set_speed";
  34. const char* NAME_GARLAND_SET_DEFAULT = "garland_set_default";
  35. const char* MQTT_TOPIC_GARLAND = "garland";
  36. const char* MQTT_PAYLOAD_COMMAND = "command";
  37. const char* MQTT_PAYLOAD_ENABLE = "enable";
  38. const char* MQTT_PAYLOAD_BRIGHTNESS = "brightness";
  39. const char* MQTT_PAYLOAD_ANIM_SPEED = "speed";
  40. const char* MQTT_PAYLOAD_ANIMATION = "animation";
  41. const char* MQTT_PAYLOAD_PALETTE = "palette";
  42. const char* MQTT_PAYLOAD_DURATION = "duration";
  43. const char* MQTT_COMMAND_IMMEDIATE = "immediate";
  44. const char* MQTT_COMMAND_RESET = "reset"; // reset queue
  45. const char* MQTT_COMMAND_QUEUE = "queue"; // enqueue command payload
  46. #define EFFECT_UPDATE_INTERVAL_MIN 7000 // 5 sec
  47. #define EFFECT_UPDATE_INTERVAL_MAX 12000 // 10 sec
  48. #define NUMLEDS_CAN_CAUSE_WDT_RESET 100
  49. bool _garland_enabled = true;
  50. unsigned long _lastTimeUpdate = 0;
  51. unsigned long _currentAnimDuration = ULONG_MAX;
  52. unsigned int _currentAnimInd = 0;
  53. unsigned int _currentPaletteInd = 0;
  54. String _immediate_command;
  55. std::queue<String> _commands;
  56. // Palette should
  57. Palette pals[] = {
  58. // palettes below are taken from http://www.color-hex.com/color-palettes/ (and modified)
  59. // RGB: Red,Green,Blue sequence
  60. Palette("RGB", {0xFF0000, 0x00FF00, 0x0000FF}),
  61. // Rainbow: Rainbow colors
  62. Palette("Rainbow", {0xFF0000, 0xAB5500, 0xABAB00, 0x00FF00, 0x00AB55, 0x0000FF, 0x5500AB, 0xAB0055}),
  63. // RainbowStripe: Rainbow colors with alternating stripes of black
  64. Palette("Stripe", {0xFF0000, 0x000000, 0xAB5500, 0x000000, 0xABAB00, 0x000000, 0x00FF00, 0x000000,
  65. 0x00AB55, 0x000000, 0x0000FF, 0x000000, 0x5500AB, 0x000000, 0xAB0055, 0x000000}),
  66. // Party: Blue purple ping red orange yellow (and back). Basically, everything but the greens.
  67. // This palette is good for lighting at a club or party.
  68. Palette("Party", {0x5500AB, 0x84007C, 0xB5004B, 0xE5001B, 0xE81700, 0xB84700, 0xAB7700, 0xABAB00,
  69. 0xAB5500, 0xDD2200, 0xF2000E, 0xC2003E, 0x8F0071, 0x5F00A1, 0x2F00D0, 0x0007F9}),
  70. // Heat: Approximate "black body radiation" palette, akin to the FastLED 'HeatColor' function.
  71. // Recommend that you use values 0-240 rather than the usual 0-255, as the last 15 colors will be
  72. // 'wrapping around' from the hot end to the cold end, which looks wrong.
  73. Palette("Heat", {0x700070, 0xFF0000, 0xFFFF00, 0xFFFFCC}),
  74. // Fire:
  75. Palette("Fire", {0x000000, 0x220000, 0x880000, 0xFF0000, 0xFF6600, 0xFFCC00}),
  76. // Blue:
  77. Palette("Blue", {0xffffff, 0x0000ff, 0x00ffff}),
  78. // Sun: Slice Of The Sun
  79. Palette("Sun", {0xfff95b, 0xffe048, 0xffc635, 0xffad22, 0xff930f}),
  80. // Lime: yellow green mix
  81. Palette("Lime", {0x51f000, 0x6fff00, 0x96ff00, 0xc9ff00, 0xf0ff00}),
  82. // Pastel: Pastel Fruity Mixture
  83. Palette("Pastel", {0x75aa68, 0x5960ae, 0xe4be6c, 0xca5959, 0x8366ac})};
  84. constexpr size_t palsSize() { return sizeof(pals)/sizeof(pals[0]); }
  85. Adafruit_NeoPixel pixels = Adafruit_NeoPixel(GARLAND_LEDS, GARLAND_D_PIN, NEO_GRB + NEO_KHZ800);
  86. Scene scene(&pixels);
  87. Anim* anims[] = {new AnimStart(), new AnimPixieDust(), new AnimSparkr(), new AnimRun(), new AnimStars(), new AnimSpread(),
  88. new AnimRandCyc(), new AnimFly(), new AnimComets(), new AnimAssemble(), new AnimDolphins(), new AnimSalut()};
  89. constexpr size_t animsSize() { return sizeof(anims)/sizeof(anims[0]); }
  90. //------------------------------------------------------------------------------
  91. void garlandDisable() {
  92. pixels.clear();
  93. }
  94. //------------------------------------------------------------------------------
  95. void garlandEnabled(bool enabled) {
  96. _garland_enabled = enabled;
  97. setSetting(NAME_GARLAND_ENABLED, _garland_enabled);
  98. if (!_garland_enabled) {
  99. schedule_function([]() {
  100. pixels.clear();
  101. pixels.show();
  102. });
  103. }
  104. #if WEB_SUPPORT
  105. char buffer[128];
  106. snprintf_P(buffer, sizeof(buffer), PSTR("{\"garlandEnabled\": %s}"), enabled ? "true" : "false");
  107. wsSend(buffer);
  108. #endif
  109. }
  110. //------------------------------------------------------------------------------
  111. bool garlandEnabled() {
  112. return _garland_enabled;
  113. }
  114. //------------------------------------------------------------------------------
  115. // Setup
  116. //------------------------------------------------------------------------------
  117. void _garlandConfigure() {
  118. _garland_enabled = getSetting(NAME_GARLAND_ENABLED, true);
  119. DEBUG_MSG_P(PSTR("[GARLAND] _garland_enabled = %d\n"), _garland_enabled);
  120. byte brightness = getSetting(NAME_GARLAND_BRIGHTNESS, 255);
  121. scene.setBrightness(brightness);
  122. DEBUG_MSG_P(PSTR("[GARLAND] brightness = %d\n"), brightness);
  123. float speed = getSetting(NAME_GARLAND_SPEED, 50);
  124. scene.setSpeed(speed);
  125. }
  126. //------------------------------------------------------------------------------
  127. void _garlandReload() {
  128. _garlandConfigure();
  129. }
  130. //------------------------------------------------------------------------------
  131. void setDefault() {
  132. scene.setDefault();
  133. byte brightness = scene.getBrightness();
  134. setSetting(NAME_GARLAND_BRIGHTNESS, brightness);
  135. byte speed = scene.getSpeed();
  136. setSetting(NAME_GARLAND_SPEED, speed);
  137. #if WEB_SUPPORT
  138. char buffer[128];
  139. snprintf_P(buffer, sizeof(buffer), PSTR("{\"garlandBrightness\": %d, \"garlandSpeed\": %d}"), brightness, speed);
  140. wsSend(buffer);
  141. #endif
  142. }
  143. #if WEB_SUPPORT
  144. //------------------------------------------------------------------------------
  145. void _garlandWebSocketOnConnected(JsonObject& root) {
  146. root[NAME_GARLAND_ENABLED] = garlandEnabled();
  147. root[NAME_GARLAND_BRIGHTNESS] = scene.getBrightness();
  148. root[NAME_GARLAND_SPEED] = scene.getSpeed();
  149. root["garlandVisible"] = 1;
  150. }
  151. //------------------------------------------------------------------------------
  152. bool _garlandWebSocketOnKeyCheck(const char* key, JsonVariant& value) {
  153. if (strncmp(key, NAME_GARLAND_ENABLED, strlen(NAME_GARLAND_ENABLED)) == 0) return true;
  154. if (strncmp(key, NAME_GARLAND_BRIGHTNESS, strlen(NAME_GARLAND_BRIGHTNESS)) == 0) return true;
  155. if (strncmp(key, NAME_GARLAND_SPEED, strlen(NAME_GARLAND_SPEED)) == 0) return true;
  156. return false;
  157. }
  158. //------------------------------------------------------------------------------
  159. void _garlandWebSocketOnAction(uint32_t client_id, const char* action, JsonObject& data) {
  160. if (strcmp(action, NAME_GARLAND_SWITCH) == 0) {
  161. if (data.containsKey("status") && data.is<int>("status")) {
  162. garlandEnabled(1 == data["status"].as<int>());
  163. }
  164. }
  165. if (strcmp(action, NAME_GARLAND_SET_BRIGHTNESS) == 0) {
  166. if (data.containsKey("brightness")) {
  167. byte new_brightness = data.get<byte>("brightness");
  168. DEBUG_MSG_P(PSTR("[GARLAND] new brightness = %d\n"), new_brightness);
  169. setSetting(NAME_GARLAND_BRIGHTNESS, new_brightness);
  170. scene.setBrightness(new_brightness);
  171. }
  172. }
  173. if (strcmp(action, NAME_GARLAND_SET_SPEED) == 0) {
  174. if (data.containsKey("speed")) {
  175. byte new_speed = data.get<byte>("speed");
  176. DEBUG_MSG_P(PSTR("[GARLAND] new speed = %d\n"), new_speed);
  177. setSetting(NAME_GARLAND_SPEED, new_speed);
  178. scene.setSpeed(new_speed);
  179. }
  180. }
  181. if (strcmp(action, NAME_GARLAND_SET_DEFAULT) == 0) {
  182. setDefault();
  183. }
  184. }
  185. #endif
  186. //------------------------------------------------------------------------------
  187. void setupScene(unsigned int anim_ind, unsigned int palette_ind, unsigned long duration) {
  188. unsigned long currentAnimRunTime = millis() - _lastTimeUpdate;
  189. _lastTimeUpdate = millis();
  190. _currentAnimDuration = duration;
  191. int prevAnimInd = _currentAnimInd;
  192. _currentAnimInd = anim_ind;
  193. int prevPalInd = _currentPaletteInd;
  194. _currentPaletteInd = palette_ind;
  195. int numShows = scene.getNumShows();
  196. int frameRate = currentAnimRunTime > 0 ? numShows * 1000 / currentAnimRunTime : 0;
  197. DEBUG_MSG_P(PSTR("[GARLAND] Anim: %-10s Pal: %-8s timings: calc: %4d pixl: %3d show: %4d frate: %d\n"),
  198. anims[prevAnimInd]->name(), pals[prevPalInd].name(),
  199. scene.getAvgCalcTime(), scene.getAvgPixlTime(), scene.getAvgShowTime(), frameRate);
  200. DEBUG_MSG_P(PSTR("[GARLAND] Anim: %-10s Pal: %-8s Inter: %d\n"),
  201. anims[_currentAnimInd]->name(), pals[_currentPaletteInd].name(), _currentAnimDuration);
  202. scene.setAnim(anims[_currentAnimInd]);
  203. scene.setPalette(&pals[_currentPaletteInd]);
  204. scene.setup();
  205. }
  206. //------------------------------------------------------------------------------
  207. void executeCommand(const String& command) {
  208. DEBUG_MSG_P(PSTR("[GARLAND] Executing command \"%s\"\n"), command.c_str());
  209. // Parse JSON input
  210. DynamicJsonBuffer jsonBuffer;
  211. JsonObject& root = jsonBuffer.parseObject(command);
  212. if (!root.success()) {
  213. DEBUG_MSG_P(PSTR("[GARLAND] Error parsing command\n"));
  214. return;
  215. }
  216. bool scene_setup_required = false;
  217. if (root.containsKey(MQTT_PAYLOAD_ENABLE)) {
  218. auto enable = root[MQTT_PAYLOAD_ENABLE].as<String>();
  219. garlandEnabled(enable != "false");
  220. }
  221. if (root.containsKey(MQTT_PAYLOAD_BRIGHTNESS)) {
  222. auto brightness = root[MQTT_PAYLOAD_BRIGHTNESS].as<byte>();
  223. scene.setBrightness(brightness);
  224. }
  225. if (root.containsKey(MQTT_PAYLOAD_ANIM_SPEED)) {
  226. auto speed = root[MQTT_PAYLOAD_ANIM_SPEED].as<byte>();
  227. scene.setSpeed(speed);
  228. }
  229. unsigned int newAnimInd = _currentAnimInd;
  230. if (root.containsKey(MQTT_PAYLOAD_ANIMATION)) {
  231. auto animation = root[MQTT_PAYLOAD_ANIMATION].as<const char*>();
  232. for (size_t i = 0; i < animsSize(); ++i) {
  233. auto anim_name = anims[i]->name();
  234. if (strcmp(animation, anim_name) == 0) {
  235. newAnimInd = i;
  236. scene_setup_required = true;
  237. break;
  238. }
  239. }
  240. }
  241. unsigned int newPalInd = _currentPaletteInd;
  242. if (root.containsKey(MQTT_PAYLOAD_PALETTE)) {
  243. auto palette = root[MQTT_PAYLOAD_PALETTE].as<const char*>();
  244. for (size_t i = 0; i < palsSize(); ++i) {
  245. auto pal_name = pals[i].name();
  246. if (strcmp(palette, pal_name) == 0) {
  247. newPalInd = i;
  248. scene_setup_required = true;
  249. break;
  250. }
  251. }
  252. }
  253. unsigned long newAnimDuration = LONG_MAX;
  254. if (root.containsKey(MQTT_PAYLOAD_DURATION)) {
  255. newAnimDuration = root[MQTT_PAYLOAD_DURATION].as<unsigned long>();
  256. scene_setup_required = true;
  257. }
  258. if (scene_setup_required) {
  259. setupScene(newAnimInd, newPalInd, newAnimDuration);
  260. }
  261. }
  262. //------------------------------------------------------------------------------
  263. // Loop
  264. //------------------------------------------------------------------------------
  265. void garlandLoop(void) {
  266. if (!_immediate_command.isEmpty()) {
  267. executeCommand(_immediate_command);
  268. _immediate_command.clear();
  269. }
  270. if (!garlandEnabled())
  271. return;
  272. scene.run();
  273. unsigned long currentAnimRunTime = millis() - _lastTimeUpdate;
  274. if (currentAnimRunTime > _currentAnimDuration && scene.finishedAnimCycle()) {
  275. if (!_commands.empty()) {
  276. executeCommand(_commands.front());
  277. _commands.pop();
  278. } else {
  279. unsigned int newAnimInd = _currentAnimInd;
  280. while (newAnimInd == _currentAnimInd) newAnimInd = secureRandom(1, animsSize());
  281. unsigned int newPalInd = _currentPaletteInd;
  282. while (newPalInd == _currentPaletteInd) newPalInd = secureRandom(palsSize());
  283. unsigned long newAnimDuration = secureRandom(EFFECT_UPDATE_INTERVAL_MIN, EFFECT_UPDATE_INTERVAL_MAX);
  284. setupScene(newAnimInd, newPalInd, newAnimDuration);
  285. }
  286. }
  287. }
  288. //------------------------------------------------------------------------------
  289. void garlandMqttCallback(unsigned int type, const char * topic, const char * payload) {
  290. if (type == MQTT_CONNECT_EVENT) {
  291. mqttSubscribe(MQTT_TOPIC_GARLAND);
  292. }
  293. if (type == MQTT_MESSAGE_EVENT) {
  294. // Match topic
  295. String t = mqttMagnitude((char*)topic);
  296. if (t.equals(MQTT_TOPIC_GARLAND)) {
  297. // Parse JSON input
  298. DynamicJsonBuffer jsonBuffer;
  299. JsonObject& root = jsonBuffer.parseObject(payload);
  300. if (!root.success()) {
  301. DEBUG_MSG_P(PSTR("[GARLAND] Error parsing mqtt data\n"));
  302. return;
  303. }
  304. String command = MQTT_COMMAND_IMMEDIATE;
  305. if (root.containsKey(MQTT_PAYLOAD_COMMAND)) {
  306. command = root[MQTT_PAYLOAD_COMMAND].as<String>();
  307. }
  308. if (command == MQTT_COMMAND_IMMEDIATE) {
  309. _immediate_command = payload;
  310. } else if (command == MQTT_COMMAND_RESET) {
  311. std::queue<String> empty;
  312. std::swap( _commands, empty );
  313. _immediate_command.clear();
  314. _currentAnimDuration = 0;
  315. setDefault();
  316. garlandEnabled(true);
  317. } else if (command == MQTT_COMMAND_QUEUE) {
  318. _commands.push(payload);
  319. }
  320. }
  321. }
  322. }
  323. //------------------------------------------------------------------------------
  324. void garlandSetup() {
  325. _garlandConfigure();
  326. mqttRegister(garlandMqttCallback);
  327. // Websockets
  328. #if WEB_SUPPORT
  329. wsRegister()
  330. .onConnected(_garlandWebSocketOnConnected)
  331. .onKeyCheck(_garlandWebSocketOnKeyCheck)
  332. .onAction(_garlandWebSocketOnAction);
  333. #endif
  334. espurnaRegisterLoop(garlandLoop);
  335. espurnaRegisterReload(_garlandReload);
  336. pixels.begin();
  337. scene.setAnim(anims[0]);
  338. scene.setPalette(&pals[0]);
  339. scene.setup();
  340. _currentAnimDuration = secureRandom(EFFECT_UPDATE_INTERVAL_MIN, EFFECT_UPDATE_INTERVAL_MAX);
  341. }
  342. /*#######################################################################
  343. _____
  344. / ____|
  345. | (___ ___ ___ _ __ ___
  346. \___ \ / __| / _ \ | '_ \ / _ \
  347. ____) | | (__ | __/ | | | | | __/
  348. |_____/ \___| \___| |_| |_| \___|
  349. #######################################################################*/
  350. #define GARLAND_SCENE_TRANSITION_MS 1000 // transition time between animations, ms
  351. #define GARLAND_SCENE_SPEED_MAX 70
  352. #define GARLAND_SCENE_SPEED_FACTOR 10
  353. #define GARLAND_SCENE_DEFAULT_SPEED 50
  354. #define GARLAND_SCENE_DEFAULT_BRIGHTNESS 255
  355. Scene::Scene(Adafruit_NeoPixel* pixels)
  356. : _pixels(pixels),
  357. _numLeds(pixels->numPixels()),
  358. _leds1(_numLeds),
  359. _leds2(_numLeds),
  360. _ledstmp(_numLeds),
  361. _seq(_numLeds) {
  362. }
  363. void Scene::setPalette(Palette* palette) {
  364. _palette = palette;
  365. if (setUpOnPalChange) {
  366. setupImpl();
  367. }
  368. }
  369. void Scene::setBrightness(byte brightness) {
  370. DEBUG_MSG_P(PSTR("[GARLAND] Scene::setBrightness = %d\n"), brightness);
  371. this->brightness = brightness;
  372. }
  373. byte Scene::getBrightness() {
  374. DEBUG_MSG_P(PSTR("[GARLAND] Scene::getBrightness = %d\n"), brightness);
  375. return brightness;
  376. }
  377. // Speed is reverse to cycleFactor and 10x
  378. void Scene::setSpeed(byte speed) {
  379. this->speed = speed;
  380. cycleFactor = (float)(GARLAND_SCENE_SPEED_MAX - speed) / GARLAND_SCENE_SPEED_FACTOR;
  381. DEBUG_MSG_P(PSTR("[GARLAND] Scene::setSpeed %d cycleFactor = %d\n"), speed, (int)(cycleFactor * 1000));
  382. }
  383. byte Scene::getSpeed() {
  384. DEBUG_MSG_P(PSTR("[GARLAND] Scene::getSpeed %d cycleFactor = %d\n"), speed, (int)(cycleFactor * 1000));
  385. return speed;
  386. }
  387. void Scene::setDefault() {
  388. speed = GARLAND_SCENE_DEFAULT_SPEED;
  389. cycleFactor = (float)(GARLAND_SCENE_SPEED_MAX - speed) / GARLAND_SCENE_SPEED_FACTOR;
  390. brightness = GARLAND_SCENE_DEFAULT_BRIGHTNESS;
  391. DEBUG_MSG_P(PSTR("[GARLAND] Scene::setDefault speed = %d cycleFactor = %d brightness = %d\n"), speed, (int)(cycleFactor * 1000), brightness);
  392. }
  393. void Scene::run() {
  394. unsigned long iteration_start_time = micros();
  395. if (state == Calculate || cyclesRemain < 1) {
  396. // Calculate number of cycles for this animation iteration
  397. float cycleSum = cycleFactor * (_anim ? _anim->getCycleFactor() : 1.0) + cycleTail;
  398. cyclesRemain = cycleSum;
  399. if (cyclesRemain < 1) {
  400. cyclesRemain = 1;
  401. cycleSum = 0;
  402. cycleTail = 0;
  403. } else {
  404. cycleTail = cycleSum - cyclesRemain;
  405. }
  406. if (_anim) {
  407. _anim->Run();
  408. }
  409. sum_calc_time += (micros() - iteration_start_time);
  410. iteration_start_time = micros();
  411. ++calc_num;
  412. state = Transition;
  413. }
  414. if (state == Transition && cyclesRemain < 3) {
  415. // transition coef, if within 0..1 - transition is active
  416. // changes from 1 to 0 during transition, so we interpolate from current
  417. // color to previous
  418. float transc = (float)((long)transms - (long)millis()) / GARLAND_SCENE_TRANSITION_MS;
  419. Color* leds_prev = (_leds == &_leds1[0]) ? &_leds2[0] : &_leds1[0];
  420. if (transc > 0) {
  421. for (int i = 0; i < _numLeds; i++) {
  422. // transition is in progress
  423. Color c = _leds[i].interpolate(leds_prev[i], transc);
  424. byte r = (int)(bri_lvl[c.r]) * brightness / 256;
  425. byte g = (int)(bri_lvl[c.g]) * brightness / 256;
  426. byte b = (int)(bri_lvl[c.b]) * brightness / 256;
  427. _pixels->setPixelColor(i, _pixels->Color(r, g, b));
  428. }
  429. } else {
  430. for (int i = 0; i < _numLeds; i++) {
  431. // regular operation
  432. byte r = (int)(bri_lvl[_leds[i].r]) * brightness / 256;
  433. byte g = (int)(bri_lvl[_leds[i].g]) * brightness / 256;
  434. byte b = (int)(bri_lvl[_leds[i].b]) * brightness / 256;
  435. _pixels->setPixelColor(i, _pixels->Color(r, g, b));
  436. }
  437. }
  438. sum_pixl_time += (micros() - iteration_start_time);
  439. iteration_start_time = micros();
  440. ++pixl_num;
  441. state = Show;
  442. }
  443. if (state == Show && cyclesRemain < 2) {
  444. /* Showing pixels (actually transmitting their RGB data) is most time consuming operation in the
  445. garland workflow. Using 800 kHz gives 1.25 μs per bit. -> 30 μs (0.03 ms) per RGB LED.
  446. So for example 3 ms for 100 LEDs. Unfortunately it can't be postponed and resumed later as it
  447. will lead to reseting the transmition operation. From other hand, long operation can cause
  448. Soft WDT reset. To avoid wdt reset we need to switch soft wdt off for long strips.
  449. It is not best practice, but assuming that it is only garland, it can be acceptable.
  450. Tested up to 300 leds. */
  451. if (_numLeds > NUMLEDS_CAN_CAUSE_WDT_RESET) {
  452. ESP.wdtDisable();
  453. }
  454. _pixels->show();
  455. if (_numLeds > NUMLEDS_CAN_CAUSE_WDT_RESET) {
  456. ESP.wdtEnable(5000);
  457. }
  458. sum_show_time += (micros() - iteration_start_time);
  459. ++show_num;
  460. state = Calculate;
  461. ++numShows;
  462. }
  463. --cyclesRemain;
  464. }
  465. void Scene::setupImpl() {
  466. transms = millis() + GARLAND_SCENE_TRANSITION_MS;
  467. // switch operation buffers (for transition to operate)
  468. if (_leds == &_leds1[0]) {
  469. _leds = &_leds2[0];
  470. } else {
  471. _leds = &_leds1[0];
  472. }
  473. if (_anim) {
  474. _anim->Setup(_palette, _numLeds, _leds, &_ledstmp[0], &_seq[0]);
  475. }
  476. }
  477. void Scene::setup() {
  478. sum_calc_time = 0;
  479. sum_pixl_time = 0;
  480. sum_show_time = 0;
  481. calc_num = 0;
  482. pixl_num = 0;
  483. show_num = 0;
  484. numShows = 0;
  485. if (!setUpOnPalChange) {
  486. setupImpl();
  487. }
  488. }
  489. unsigned long Scene::getAvgCalcTime() { return sum_calc_time / calc_num; }
  490. unsigned long Scene::getAvgPixlTime() { return sum_pixl_time / pixl_num; }
  491. unsigned long Scene::getAvgShowTime() { return sum_show_time / show_num; }
  492. /*#######################################################################
  493. _ _ _
  494. /\ (_) | | (_)
  495. / \ _ __ _ _ __ ___ __ _ | |_ _ ___ _ __
  496. / /\ \ | '_ \ | | | '_ ` _ \ / _` | | __| | | / _ \ | '_ \
  497. / ____ \ | | | | | | | | | | | | | (_| | | |_ | | | (_) | | | | |
  498. /_/ \_\ |_| |_| |_| |_| |_| |_| \__,_| \__| |_| \___/ |_| |_|
  499. #######################################################################*/
  500. Anim::Anim(const char* name) : _name(name) {}
  501. void Anim::Setup(Palette* palette, uint16_t numLeds, Color* leds, Color* ledstmp, byte* seq) {
  502. this->palette = palette;
  503. this->numLeds = numLeds;
  504. this->leds = leds;
  505. this->ledstmp = ledstmp;
  506. this->seq = seq;
  507. SetupImpl();
  508. }
  509. void Anim::initSeq() {
  510. for (int i = 0; i < numLeds; ++i)
  511. seq[i] = i;
  512. }
  513. void Anim::shuffleSeq() {
  514. for (int i = 0; i < numLeds; ++i) {
  515. byte ind = (unsigned int)(rngb() * numLeds / 256);
  516. if (ind != i) {
  517. std::swap(seq[ind], seq[i]);
  518. }
  519. }
  520. }
  521. void Anim::glowSetUp() {
  522. braPhaseSpd = secureRandom(4, 13);
  523. if (braPhaseSpd > 8) {
  524. braPhaseSpd = braPhaseSpd - 17;
  525. }
  526. braFreq = secureRandom(20, 60);
  527. }
  528. void Anim::glowForEachLed(int i) {
  529. int8 bra = braPhase + i * braFreq;
  530. bra = BRA_OFFSET + (abs(bra) >> BRA_AMP_SHIFT);
  531. leds[i] = leds[i].brightness(bra);
  532. }
  533. void Anim::glowRun() { braPhase += braPhaseSpd; }
  534. bool operator== (const Color &c1, const Color &c2)
  535. {
  536. return (c1.r == c2.r && c1.g == c2.g && c1.b == c2.b);
  537. }
  538. unsigned int rng() {
  539. static unsigned int y = 0;
  540. y += micros(); // seeded with changing number
  541. y ^= y << 2;
  542. y ^= y >> 7;
  543. y ^= y << 7;
  544. return (y);
  545. }
  546. // Ranom numbers generator in byte range (256) much faster than secureRandom.
  547. // For usage in time-critical places.
  548. byte rngb() { return (byte)rng(); }
  549. #endif // GARLAND_SUPPORT