Compare commits

...

139 Commits

Author SHA1 Message Date
d76e088c37 Remove left-over merge stuff in efects/big_clock.h
Some checks reported errors
continuous-integration/drone Build was killed
2020-11-06 23:42:04 +01:00
c9825c8f9b Give path in effects/animation.h to also work on case-insensitive file systems. 2020-11-06 23:41:38 +01:00
ae997ef802 Merge branch 'master' of https://git.schle.nz/fabian/pitrix 2020-11-06 23:34:57 +01:00
a6af2829ee Better and more SimpleEffects.
* Calculations are done using doubles for much easier code.
* Binary flags instead of boolean variables for configuration.
* New effects sineline, barbershop and zigzag.
2020-11-06 22:33:37 +00:00
0a4a62d7c8 Two more new effects: upwave and canterwave. 2020-11-04 06:32:55 +01:00
7747e38253 Moved all individual effects into a subfolder {src,include}/effects/ 2020-11-04 06:26:02 +01:00
b9cfc6568b night_clock now has bigger digits. 2020-11-04 06:16:26 +01:00
1d66f9c541 Try to connect to WiFi for a longer time. Useful when your WiFi takes a bit longer to authenticate. 2020-11-04 06:00:24 +01:00
f9e6a5ebd6 Added new effect type SimpleEffect for, well, simple effects. Added new effect slow_blinking based on this and also converted the confetti effects. 2020-11-04 05:58:00 +01:00
a96f6c79e3 monitor.rb now uses real 24-bit-colors instead of ansi colors. 2020-08-06 15:37:38 +00:00
ecf5998510 Merge branch 'master' of https://git.schle.nz/fabian/pitrix 2020-07-09 19:51:49 +02:00
0ac4f9b181 Added "blinking child icon" effect. 2020-07-09 06:42:04 +02:00
fd44b217a7 Added homeassistant auto-discovery via MQTT. 2020-07-09 06:41:21 +02:00
209140cfb7 Added new effect TPM2.NET. This is untested. 2020-07-09 06:39:53 +02:00
bcb5fdc9be Darker red for NightClock. 2020-04-30 21:56:02 +02:00
b5bb0feccf Fixed typo in macros.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2020-04-30 06:53:35 +02:00
0863380648 Effect DVD is now one of the cycling effects.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2020-04-30 06:52:31 +02:00
075823220a Added effect diamond. 2020-04-30 06:51:02 +02:00
caa86551a0 A bit less logging from mqtt.cpp. 2020-04-30 06:42:18 +02:00
10be8ef7cc There are now global instances of Window which don't have to be created and deleted all the time. 2020-04-30 06:41:30 +02:00
9de77349e8 SnakeEffect now uses a hand-crafted meandering algorithm. Still not perfect, but it works as good as the old code while looking more "natural". 2020-04-30 06:40:05 +02:00
599bcd87bc Tried using a fixed meandering path for the snake effect. Doesn't work as of now. 2020-04-30 06:20:19 +02:00
70ddba2cbc Log contents of SPIFFS on bootup.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2020-04-29 20:28:10 +02:00
3f09d9adbf Modernized platformio.ini 2020-04-29 20:27:34 +02:00
cfb25d6030 Added more AnimationEffects. 2020-04-29 20:27:16 +02:00
4762a852d8 MQTT messages can now be retained. 2020-04-29 20:25:41 +02:00
8e2d2225cb Better metrics. 2020-04-29 20:24:59 +02:00
65dd09ca0d Fixed Animation stuff by not sending so many debug messages via MQTT. 2020-04-29 20:23:57 +02:00
f014fd7cae Changed NightClock to be a special form of BigClock. 2020-04-29 20:19:26 +02:00
1707084299 Added AnimationEffect::Blinker to quickly create a blinking sprite effect from an image file. 2020-04-29 20:17:34 +02:00
402d7f5d75 Merge branch 'master' of https://git.schle.nz/fabian/pitrix 2020-04-29 09:28:22 +02:00
521e5f735d Effect lightspeed * More variable saturation for the stars
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2020-04-28 17:47:43 +02:00
0da161ccd1 Fonts * Added font_numbers3x3 2020-04-28 17:46:40 +02:00
f8b6f5cc02 Effect Matrix * Better colors 2020-04-28 17:46:23 +02:00
c6b2a8a1d0 Effect Clock * Trying to get the nightclock effect to work... 2020-04-28 17:45:29 +02:00
6ba916282b Modified IP in platformio.ini 2020-04-28 17:44:59 +02:00
79c13e760f Webserver * Increased buffer size for HTML generation in /effects. 2020-04-28 17:44:19 +02:00
47812de405 Switched from NTPClient to lwIPs internal SNTP client.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
This has the advantage of being able to set DST automatically.
2020-03-29 18:08:54 +02:00
d28dca0a4d Merge branch 'master' of https://git.schle.nz/fabian/pitrix 2020-01-22 12:50:51 +01:00
e2a56d7c29 Added a TimeEffect to show a countdown to a time given via MQTT.
All checks were successful
continuous-integration/drone/push Build is passing
2019-12-21 14:21:31 +01:00
439e2de17f effect_lightspeed: Speed changes. 2019-11-03 13:56:07 +01:00
994f4894dd effect_lightspeed: New effect.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2019-10-24 06:33:16 +02:00
b5343b59e5 Window.cpp: Fixed the line drawing code and improved it's speed. 2019-10-24 06:31:52 +02:00
66c0124072 Removed old include. 2019-10-23 12:43:59 +02:00
2a6f68cc45 Adapted recorder.rb and generate_gifs.sh to websockets. 2019-10-23 12:37:25 +02:00
f5d47fe7da Keep track of the current frame number. 2019-10-23 12:36:08 +02:00
029c93166d Extended the sebsocket's capabilities. And moved a lot of html strings to PROGMEM to free up some RAM. 2019-10-23 12:34:46 +02:00
141210a370 Switched from ESPWebServer and WebSocketsServer to ESPAsyncWebServer. 2019-10-23 06:22:53 +02:00
a902addf94 snakenet.rb: You can choose between using float or integer weights. floats seem to give better results and the speed didn't improve a lot. 2019-10-23 05:42:42 +02:00
b644006036 MQTT: Report flash size at startup. 2019-10-23 05:42:10 +02:00
dfe99408c9 Small fixes. 2019-10-23 05:41:39 +02:00
3c0e4af325 effect_blur2d: Number of blobs is configurable. 2019-10-23 05:40:44 +02:00
aa72196a07 monitor.rb now also uses websockets instead of raw UDP stuff. 2019-10-23 05:38:38 +02:00
f76354a4d5 Removed recorder, added websockets instead. 2019-10-23 05:37:55 +02:00
01c364795f Ignore snakenet/data_set.* 2019-10-23 05:36:17 +02:00
38aa654c54 Snakenet.rb now applies modifications to the weights in both directions (e.g. positive and negative), creating two new AIs.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2019-10-18 17:04:44 +02:00
1418d519d5 Better frametime calculation in pitrix.cpp 2019-10-18 17:03:48 +02:00
efe9b924ec Effect tv_static now also uses subpixel rendering. 2019-10-18 17:03:21 +02:00
7b8dabee43 Added another potentially good neural net to effect matrix. 2019-10-18 17:02:54 +02:00
783cfdae3f Implemented subpixel rendering for the line drawing methods. 2019-10-18 17:02:23 +02:00
54925dfc0e Snake effect now uses a neural net with a hidden layer; a bug in _is_free() that lead to lots of snakes committing suicide was fixed; neural net weights are now given as binary representation of a float to prevent rounding errors. 2019-10-18 06:40:09 +02:00
306f72d838 Added another, slightly better neural net to effect_snake. 2019-10-17 05:37:20 +02:00
230a1f1a91 Effect snake now uses a "proper", albeit simple neural network instead of simple decisions. 2019-10-16 12:27:58 +02:00
bd0041619a Added snakenet.rb, the tool used to train the AI for snake effect. 2019-10-16 05:56:42 +02:00
a3caaa1fef Merge branch 'master' of https://git.schle.nz/fabian/pitrix
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2019-10-15 20:06:38 +02:00
ec379c009e Effect snake is now a real snake game, with apples, dying and even a more or less smart AI. 2019-10-15 20:06:19 +02:00
8568436fc1 Ignore .vscode
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2019-10-12 23:28:19 +02:00
6eeb7488b1 Effect sinematrix now comes in additional variants with some more color palettes. 2019-10-12 23:27:49 +02:00
efa9a73cae Added switching of effects via the web interface.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2019-10-11 17:57:00 +02:00
bbdb46c04d Twirl no longer uses a masked clock. 2019-10-11 17:56:37 +02:00
4c611da6d1 Finally, big_clock is working correctly with smoothly moving seconds and stuff. Also, the spacing of the moving seconds is now configurable. 2019-10-11 17:56:18 +02:00
377ccc477f Small changes in settings.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2019-10-11 12:14:32 +02:00
efebe9adb4 The *matrix effects now are all derived from MatrixEffectBase class. Also, you can now set the amount of columns for these effects. 2019-10-11 06:21:32 +02:00
359b7a7826 Merge branch 'master' of https://git.schle.nz/fabian/pitrix; added new effect tv_static.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2019-10-10 06:50:09 +02:00
b5c1f350d2 Whitespace changes.
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2019-10-10 06:47:17 +02:00
5eba691429 Extended the webinterface to list all available effects. 2019-10-10 06:45:49 +02:00
d8fe055e3d Better default values for matrix* effects. 2019-10-10 06:44:35 +02:00
6b4f75b8bc Fixed random_matrix effect. 2019-10-10 06:43:01 +02:00
416ab46f9b Repaired font3x5_numbers_blocky: A 5 will now be shown as 5 instead of 9. m( 2019-10-09 05:59:05 +02:00
b4aa711940 Lots of settings stuff: The webinterface at /settings now lets you change the settings. Also, these changes will be saved to SPIFFS and loaded after a reboot. 2019-10-09 05:57:20 +02:00
205a0df842 Small fixes.
Some checks failed
continuous-integration/drone/push Build is failing
2019-10-04 15:58:56 +02:00
8bcee1871f Effect dvd now with subpixel rendering. 2019-10-04 15:58:24 +02:00
ef57c5ea2e Settings can now be changed:
* Via HTTP POST request to /settings, providing key=<key_to_change> and value=<new_value>.
  * Via MQTT at <MQTT_TOPIC>/settings.<key>.
2019-10-04 12:11:05 +02:00
0f1d4abe04 Settings are now properly global and can be changed. 2019-10-04 12:10:16 +02:00
2b50691067 Fixed memory leak in random_matrix and rainbow_matrix. 2019-10-04 12:08:01 +02:00
af1314632e sines effect now uses subpixel rendering. 2019-10-04 12:05:54 +02:00
2b7033b685 cycle effect now tracks the heap leakage of the running effects. Cause somewhere's a memory leak leading to restarts of the ESP every about 2 days... 2019-10-04 12:03:03 +02:00
97dd6de280 Fixes for recorder and tests. 2019-10-02 06:21:10 +02:00
54d357e6df Fixes for firework effect. 2019-10-02 06:20:39 +02:00
ac1f758b87 Fixes in blur2d effect. 2019-10-02 06:18:43 +02:00
85aee53462 More debugging output in Animation.cpp 2019-10-02 06:18:15 +02:00
f42b5e1034 Effect big_clock now show the seconds in a calmer way. Divisible-by-5 seconds are in another color. 2019-10-02 06:17:20 +02:00
083564caef Reorganized effect selection stuff: No longer a big case clause comparing CRC32. Instead an array of structs. Much nicer. Also, other code can now see which effects there are. 2019-10-02 06:16:07 +02:00
3f6d4cb0be Moved settings from preprocessor directives to variables. Also added a way to (for now only) display them via HTTP server. 2019-10-02 06:13:55 +02:00
382631d7d7 Effect#loop now gets the time since the last run of the loop in ms. This enables
the effects to show animations that stay fluid independent of the current frame rate.
2019-10-01 06:29:32 +02:00
096d13438a More effects for cycle.
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-26 06:52:33 +02:00
075f434997 Small fixes and tweaks to blur2d, clock and firework.
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-25 20:05:18 +02:00
c1024b3423 As noticed, this reverts big_clock to the pre-subsampling state. 2019-09-25 20:04:48 +02:00
2395e51e88 Added different rendering modes for the subpixel stuff. 2019-09-25 20:04:17 +02:00
1c1c3a8054 Kinda fixed big_clock effect, but it doesn't look nearly as cool as I'd hoped... So this commit includes the fix and the next commit will be a revert to the older, non-subsampled code, which just looked better. 2019-09-25 19:31:07 +02:00
937850c90a Better MQTT filtering.
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-25 18:13:56 +02:00
b0c1bcaa65 More logging for effect changes in cycle effect. 2019-09-25 18:13:39 +02:00
7cbb156b66 Attempted to make the border of big_clock use subpixel rendering. This is still work in progress. 2019-09-25 18:13:11 +02:00
0a7334e448 Fixed big_clock 2019-09-25 18:12:34 +02:00
29aa47177e Fixed dimmed colors in subpixel rendering mode. 2019-09-25 18:12:16 +02:00
7ac681002e Added effect night_clock 2019-09-25 18:11:52 +02:00
36edb94ff0 Metrics now include the current effect's name. 2019-09-25 18:11:03 +02:00
d396b68191 Added aknowledgements section to README. 2019-09-25 06:44:22 +02:00
c1cfb693d3 Added missing option EFFECT_SINES_COUNT fo congif.sample.h 2019-09-25 06:43:31 +02:00
34d5aa6496 Use more effects for cycling through! 2019-09-25 06:42:43 +02:00
781954d7f1 Added effect marquee, which will scroll a given message through the display. 2019-09-25 06:41:52 +02:00
73b8d162c7 Metrics logging now includes average time taken per frame. Also, metrics now come in a single JSON payload to /metrics. 2019-09-25 06:40:24 +02:00
62549e73bf Small fix for clock effect to work correctly with the new subpixel rendeing stuff. 2019-09-25 06:35:30 +02:00
3018bc6375 Added font5x7 and turned the letters around so they are now upside up. ;-) 2019-09-25 06:34:26 +02:00
1cd94ac4ae Effect swirl is now shown with clock. 2019-09-25 06:33:26 +02:00
b8f39410c9 Effect matrix now uses subpixel rendering for smoother movements. 2019-09-25 06:32:35 +02:00
711719921a Rewrote effect firework. Now it kinda works. 2019-09-25 06:30:03 +02:00
6ba2854a8d Effect big_dynamic will now be shown with clock. 2019-09-25 06:28:53 +02:00
aa11f5ed8a Added subpixel-rendering as well as blurring methods to Window. 2019-09-25 06:27:43 +02:00
fa5f1c8816 Added effect blur2d 2019-09-25 06:26:27 +02:00
b5c99c320b Added effect sines. 2019-09-25 06:25:16 +02:00
96e144f96b Added effect analog_clock (heavily, not-really-usable work-in-progress!). 2019-09-25 06:24:24 +02:00
792354e371 Updated recorder to only send every 2nd frame, to clear the window when selecting a new effect, doubled the resizing factor and re-enabled gifsicle. 2019-09-25 06:09:00 +02:00
571ae26c25 Updated effect gifs. 2019-09-25 06:07:17 +02:00
549bce479a gifsicle breaks the gifs. -_-
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-04 12:48:45 +02:00
5bd831cedc Removed superfluous file.
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-04 12:08:12 +02:00
642c91ba96 Nicer effect gifs and README.
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-04 12:07:30 +02:00
fb7c366ed6 Added all effects to README.
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-04 06:18:51 +02:00
2d4009075d Added a single effect to README.md
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-04 04:13:00 +00:00
f7a6a17beb Added effects as gif files.
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-04 06:08:27 +02:00
1754f49b68 Better results from memory leak effect.
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-04 06:07:28 +02:00
0b80b74be3 Added more effects to the cycle effect. 2019-09-04 06:06:47 +02:00
33c2417534 Added recorder to be able to stream the current LED data via network. That way you can create nice looking GIF images of the effects - and even develop effects without having to look at the actual LED panel. 2019-09-04 06:05:45 +02:00
bf1666fb32 Added font numbers3x5_blocky. 2019-09-04 06:02:16 +02:00
0d23e5ec6c Removed old reference to SimpleList. 2019-09-04 06:01:32 +02:00
a05931d7f9 Made twirl effect a lot more beautiful. 2019-09-04 06:01:08 +02:00
a6cd94e416 Fixed rainbow_matrix and random_matrix. 2019-09-04 06:00:18 +02:00
2ddd77eb5c Fixed big_clock effect and made it a lot nicer with moving pixels for seconds. 2019-09-04 05:59:30 +02:00
1122546853 Fixed line and circle drawing code in Window. But it looks bad ans some form of dithering is definitely required. 2019-09-04 05:58:36 +02:00
128 changed files with 4267 additions and 1183 deletions

2
.gitignore vendored
View File

@ -9,3 +9,5 @@ include/config.h
.piolibdeps .piolibdeps
.pioenvs .pioenvs
.DS_Store .DS_Store
.vscode
src/tools/snakenet/data_set.*

View File

@ -10,6 +10,35 @@ pitrix fetches the current time via NTP, is controllable via MQTT and
can be flashed over-the-air, so you don't need to disassemble your can be flashed over-the-air, so you don't need to disassemble your
nice-looking LED matrix everytime you want to update the software. nice-looking LED matrix everytime you want to update the software.
### Effects
| Name | Description | Image |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| bell | An example for a blinking sprite. Can be used for home automation, for example. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/bell.gif" /> |
| big_clock | A big clock. Seconds are moving around the edge. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/big_clock.gif" /> |
| big_dynamic | Big squares appearing in sequential colors. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/big_dynamic.gif" /> |
| clock | The small clock at the bottom. Usually used by other effects to integrate a clock. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/clock.gif" /> |
| cycle | Meta effect: Cycles through some of the other effects. | - |
| confetti | Random pixels lighting up in sequential colors and fading out again. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/confetti.gif" /> |
| dvd | The bouncing-around dvd logo we all know and love. High chance of hitting cordners because of the low resolution of the LED panel. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/dvd.gif" /> |
| fire | Fireplace-like effect. Needs more tweaking. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/fire.gif" /> |
| firework | Firework-like effect. Needs more work. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/firework.gif" /> |
| gol | Conway's Game of Life. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/gol.gif" /> |
| matrix | That green effect from The Matrix. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/matrix.gif" /> |
| multi_dynamic | Space is divided in 2x2 squares which are changing their color random-sequentially. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/multi_dynamic.gif" /> |
| pixel_clock | A clock using single pixels to display the current time. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/pixel_clock.gif" /> |
| rainbow_matrix | Like matrix, but not just green but displaying a rainbow across the columns. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/rainbow_matrix.gif" /> |
| random_matrix | Like matrix, but with random colors and random directions. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/random_matrix.gif" /> |
| sinematrix3 | Colorful pastel images, slowly moving about. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/sinematrix3.gif" /> |
| single_dynamic | 2x2 squares random-sequentially changing their color one after another. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/single_dynamic.gif" /> |
| snake | Snake. Without self-collision detection or AI. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/snake.gif" /> |
| twirl | A colorful twirl. | <img width="128" height="128" src="https://git.schle.nz/fabian/pitrix/raw/branch/master/docs/effects/twirl.gif" /> |
## How to use ## How to use
Checkout the code, rename `include/config.sample.h` to `include/config.h` Checkout the code, rename `include/config.sample.h` to `include/config.h`
@ -55,3 +84,24 @@ seconds:
If you enabled HTTP server, you can always make GET requests to `/free_heap`, `/uptime` or `/fps` to get those values. If you enabled HTTP server, you can always make GET requests to `/free_heap`, `/uptime` or `/fps` to get those values.
If you enabled `DEBUG`, log messages will be sent to `MQTT_TOPIC/log`. If you enabled `DEBUG`, log messages will be sent to `MQTT_TOPIC/log`.
## Aknowledgements
### Libraries
| Library | Author | Link |
| ------- | ------ | ---- |
| PubSubClient | knolleary | https://pubsubclient.knolleary.net/
| FastLED (with small modifications) | Daniel Garcia & Mark Kriegsman | https://fastled.io
| NTPClient (with modifications) | | https://github.com/arduino-libraries/NTPClient
| ESP8266WebServer | | https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer
| ESPAsyncTCP | me-no-dev | https://github.com/me-no-dev/ESPAsyncTCP
### Inspirations and stuff
* This whole project was inspired by
https://github.com/orithena/Arduino-LED-experiments/tree/master/Ribba16x16_v2_ESP32.
The sinematrix3 effect was also taken from there.
* The firework effect was heavily inspired (aka copied) from
https://gist.github.com/kriegsman/68929cbd1d6de4535b20
* The sines effect is based upon
https://github.com/atuline/FastLED-Demos/blob/master/sinelon/sinelon.ino

BIN
data/child.pia Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
docs/effects/bell.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
docs/effects/big_clock.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
docs/effects/clock.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
docs/effects/confetti.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
docs/effects/dvd.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
docs/effects/fire.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
docs/effects/firework.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
docs/effects/gol.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/effects/heart.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
docs/effects/matrix.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
docs/effects/snake.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/effects/twirl.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

View File

@ -1,5 +1,4 @@
#ifndef Effect_H #pragma once
#define Effect_H
#include "Window.h" #include "Window.h"
#include "config.h" #include "config.h"
@ -7,10 +6,11 @@
class Effect { class Effect {
protected: protected:
Window* window = Window::getFullWindow(); // Use a full screen window per default. Window* window = &Window::window_full; // Use a full screen window per default.
public: public:
virtual ~Effect() {}; virtual ~Effect() {};
virtual void loop() = 0; virtual void loop(uint16_t ms) = 0;
virtual String get_name() = 0;
boolean supports_window = false; boolean supports_window = false;
virtual boolean can_be_shown_with_clock() { return false; }; virtual boolean can_be_shown_with_clock() { return false; };
virtual boolean clock_as_mask() { return false; }; virtual boolean clock_as_mask() { return false; };
@ -20,4 +20,3 @@ public:
virtual void apply_option(String key, String value) {}; virtual void apply_option(String key, String value) {};
}; };
#endif

32
include/SimpleEffect.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include "Effect.h"
#include "prototypes.h"
#include <cmath>
#define SE_CYCLE_COLORS 1 // Slowly cycle through the rainbow.
#define SE_RANDOM_PIXEL_COLORS 2 // Every pixel gets a random color every frame.
#define SE_ONLY_POSITIVE 4 // Only use colors, not white. This is equivalent to running your output through abs()
#define SE_FADEOUT 8 // Fades the old image out. Returning 0 doesn't change the pixel's value.
#define SE_RANDOM_STATIC_COLOR 16 // Sets a random static color at start of the effect.
#define SE_DEBUG 32 // Prints debug messages.
class SimpleEffect : public Effect {
protected:
Window* window = &Window::window_full; // Use a full screen window per default.
uint8_t _color = 0;
uint16_t _flags;
String _name;
simple_effect_t _method;
public:
SimpleEffect(String name, uint16_t flags, simple_effect_t method): _name { name }, _method { method } {
_flags = flags;
if (_flags & SE_RANDOM_STATIC_COLOR) {
_color = random8();
_flags &= ~SE_CYCLE_COLORS & ~SE_RANDOM_PIXEL_COLORS;
}
};
void loop(uint16_t ms) override;
String get_name() { return _name; };
boolean can_be_shown_with_clock() { return true; }
};

View File

@ -3,12 +3,24 @@
#include "config.h" #include "config.h"
#include "prototypes.h" #include "prototypes.h"
enum SubpixelRenderingMode {
SUBPIXEL_RENDERING_SET,
SUBPIXEL_RENDERING_ADD,
SUBPIXEL_RENDERING_RAISE
};
class Window { class Window {
private:
void _circle_point(int x0, int y0, int x1, int y1, CRGB* color);
void _subpixel_render(uint8_t x, uint8_t y, CRGB* color, SubpixelRenderingMode m);
public: public:
static Window window_full;
static Window window_with_clock;
static Window window_clock;
const uint8_t x, y; const uint8_t x, y;
const uint8_t width, height; const uint8_t width, height;
uint16_t count; uint16_t count;
static Window* getFullWindow();
Window(): Window(0, 0, LED_WIDTH, LED_HEIGHT) {}; Window(): Window(0, 0, LED_WIDTH, LED_HEIGHT) {};
Window(uint8_t x, uint8_t y) : Window(x, y, LED_WIDTH-x, LED_HEIGHT-y) {}; Window(uint8_t x, uint8_t y) : Window(x, y, LED_WIDTH-x, LED_HEIGHT-y) {};
@ -17,15 +29,29 @@ public:
void clear(); void clear();
void clear(CRGB* color); void clear(CRGB* color);
void setPixel(uint8_t x, uint8_t y, CRGB* color); void setPixel(uint8_t x, uint8_t y, CRGB* color);
void setSubPixel(accum88 x, accum88 y, CRGB* color, SubpixelRenderingMode m = SUBPIXEL_RENDERING_ADD);
void setPixelByIndex(uint16_t index, CRGB* color); void setPixelByIndex(uint16_t index, CRGB* color);
void raisePixel(uint8_t x, uint8_t y, CRGB* color); void raisePixel(uint8_t x, uint8_t y, CRGB* color);
void line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, CRGB* color); void line(saccum78 x1, saccum78 y1, saccum78 x2, saccum78 y2, CRGB* color);
void lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t length, CRGB* color); void lineWithAngle(uint8_t x, uint8_t y, uint16_t angle, uint8_t length, CRGB* color);
void lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t startdist, uint8_t length, CRGB* color); void lineWithAngle(uint8_t x, uint8_t y, uint16_t angle, uint8_t startdist, uint8_t length, CRGB* color);
void circle(uint8_t x, uint8_t y, uint8_t r, CRGB* color); void circle(uint8_t x, uint8_t y, uint8_t r, CRGB* color);
uint16_t coordsToGlobalIndex(uint8_t x, uint8_t y); uint16_t coordsToGlobalIndex(uint8_t x, uint8_t y);
uint16_t localToGlobalIndex(uint16_t); uint16_t localToGlobalIndex(uint16_t);
void drawChar(Font* f, uint8_t x, uint8_t y, const char c, CRGB* color, bool mask=false); void drawChar(Font* f, accum88 x, accum88 y, const char c, CRGB* color, bool mask=false);
void drawText(Font* f, uint16_t x, uint16_t y, String s, CRGB* color);
void drawSubText(Font* f, accum88 x, accum88 y, String s, CRGB* color);
void addPixelColor(uint16_t index, CRGB* color); void addPixelColor(uint16_t index, CRGB* color);
void addPixelColor(uint8_t x, uint8_t y, CRGB* color);
CRGB get_pixel(uint8_t x, uint8_t y);
void fadeToBlackBy(fract8 speed); void fadeToBlackBy(fract8 speed);
void shift_down();
void shift_down_and_blur();
void clear_row(uint8_t y);
void blur(fract8 intensity);
void blur_rows(fract8 intensity);
void blur_row(uint8_t y, fract8 intensity);
void blur_columns(fract8 intensity);
void blur_column(uint8_t x, fract8 intensity);
void fill_with_checkerboard();
}; };

View File

@ -3,6 +3,7 @@
#include <Arduino.h> #include <Arduino.h>
#define FASTLED_INTERNAL #define FASTLED_INTERNAL
#include <FastLED.h> #include <FastLED.h>
#include "settings.h"
//#define DEBUG // Uncomment this to enable Debug messages via Serial and, if enabled, MQTT. //#define DEBUG // Uncomment this to enable Debug messages via Serial and, if enabled, MQTT.
//#define CONFIG_USABLE // Uncomment this by removing the // at the beginning! //#define CONFIG_USABLE // Uncomment this by removing the // at the beginning!
@ -35,6 +36,8 @@
#define MQTT_TOPIC "pitrix/" // MQTT topic to listen to. Must not start with a slash, but must end with one. #define MQTT_TOPIC "pitrix/" // MQTT topic to listen to. Must not start with a slash, but must end with one.
#define MQTT_REPORT_METRICS // Whether to report metrics via MQTT. Disable if unwanted. #define MQTT_REPORT_METRICS // Whether to report metrics via MQTT. Disable if unwanted.
#define MQTT_TOPIC_WEATHER "accuweather/pitrix/" // MQTT topic to listen for weather data. Must not start with a slash, but must end with one. #define MQTT_TOPIC_WEATHER "accuweather/pitrix/" // MQTT topic to listen for weather data. Must not start with a slash, but must end with one.
#define MQTT_TOPIC_TIMER "alexa/timer"
#define MQTT_TOPIC_HOMEASSISTANT "homeassistant"
#define HOSTNAME "pitrix-%08X" // Hostname of the ESP to use for OTA and MQTT client id. %08X will be replaced by the chip id. #define HOSTNAME "pitrix-%08X" // Hostname of the ESP to use for OTA and MQTT client id. %08X will be replaced by the chip id.
#define OTA_STARTUP_DELAY 10 // How many seconds to wait at startup. This is useful to prevent being unable to flash OTA by a bug in the code. Set to 0 to disable. #define OTA_STARTUP_DELAY 10 // How many seconds to wait at startup. This is useful to prevent being unable to flash OTA by a bug in the code. Set to 0 to disable.
@ -46,37 +49,35 @@
#define MONITOR_LOOP_TIME_THRESHOLD 500 #define MONITOR_LOOP_TIME_THRESHOLD 500
#define MONITOR_LOOP_TIME_COUNT_MAX 10 #define MONITOR_LOOP_TIME_COUNT_MAX 10
#define EFFECT_CYCLE_TIME 300 // Time in seconds between cycling effects. // settings.effects.cycle.time = 300; // Time in seconds between cycling effects.
#define EFFECT_CYCLE_RANDOM true // settings.effects.cycle.random = true;
#define EFFECT_MATRIX_LENGTH_MIN 4 // settings.effects.matrix.length_min = 4;
#define EFFECT_MATRIX_LENGTH_MAX 20 // settings.effects.matrix.length_max = 20;
#define EFFECT_MATRIX_SPEED_MIN 50 // settings.effects.matrix.speed_min = 1;
#define EFFECT_MATRIX_SPEED_MAX 135 // settings.effects.matrix.speed_max = 10;
#define EFFECT_SINGLE_DYNAMIC_LOOP_TIME 40 // .dynamic.single_loop_time = 40;
#define EFFECT_MULTI_DYNAMIC_LOOP_TIME 1400 // .dynamic.multi_loop_time = 1400;
#define EFFECT_BIG_DYNAMIC_LOOP_TIME 50 // .dynamic.big_loop_time = 50;
#define EFFECT_BIG_DYNAMIC_SIZE 3 // .dynamic.big_size = 3;
#define EFFECT_CONFETTI_PIXELS_PER_LOOP 2 // .fire.cooldown = 192;
// .fire.spark_chance = 5;
#define EFFECT_SNAKE_DIRECTION_CHANGE 10 // .firework.drag = 255;
#define EFFECT_SNAKE_SLOWDOWN 2 // .firework.bounce = 200;
// .firework.gravity = 10;
// .firework.sparks = 12;
#define EFFECT_FIRE_COOLDOWN 192 // .gol.start_percentage = 90;
#define EFFECT_FIRE_SPARK_CHANCE 5 // .gol.blend_speed = 10;
// .gol.restart_after_steps = 100;
#define EFFECT_FIREWORK_SHOT_CHANCE 200 // .sines.count = 5;
#define EFFECT_FIREWORK_BLUR 200
#define EFFECT_FIREWORK_FADEOUT_SPEED 5
#define EFFECT_GOL_START_PERCENTAGE 90 // .snake.direction_change = 5;
#define EFFECT_GOL_BLEND_SPEED 10 // .snake.slowdown = 2;
#define EFFECT_GOL_RESTART_AFTER_STEPS 100
#define EFFECT_DVD_WIDTH 3
#define EFFECT_DVD_HEIGHT 2
// Stop editing here // Stop editing here
@ -96,22 +97,18 @@
Serial.println(buffer);\ Serial.println(buffer);\
} while (0); } while (0);
#else #else
#define LOG(msg, ...) do { \ #define LOG(x, ...) Serial.printf(x, ##__VA_ARGS__);
char buffer[128]; \ #define LOGln(x, ...) { Serial.printf(x, ##__VA_ARGS__); Serial.println(); }
snprintf_P(buffer, 128, PSTR(msg), ##__VA_ARGS__);\
Serial.print(buffer);\
} while (0);
#define LOGln(msg, ...) do { \
char buffer[128]; \
snprintf_P(buffer, 128, PSTR(msg), ##__VA_ARGS__);\
Serial.println(buffer);\
} while (0);
#endif #endif
#define DBG(msg, ...) { Serial.printf(msg, ##__VA_ARGS__); Serial.println(); }
#else #else
#define LOG(msg, ...) do {} while(0); #define LOG(x) do {} while(0);
#define LOGln(msg, ...) do {} while(0); #define LOGln(x) do {} while(0);
#define DBG(msg, ...) do {} while(0);
#endif #endif
#if !defined( ESP8266 ) && !defined( ESP32 ) #if !defined( ESP8266 ) && !defined( ESP32 )
#error "Neither ESP8266 nor ESP32 are set. Maybe you are compiling this for another platform...?" #error "Neither ESP8266 nor ESP32 are set. Maybe you are compiling this for another platform...?"
#endif #endif

View File

@ -1,20 +0,0 @@
#pragma once
#include "Effect.h"
#include "prototypes.h"
#include "my_fastled.h"
#include "Animation.h"
class AnimationEffect : public Effect {
private:
Animation *animation;
uint16_t xOffset;
uint16_t yOffset;
public:
AnimationEffect(const char* name) : AnimationEffect(name, 0x000000, 0, 0) {}
AnimationEffect(const char* name, uint32_t bg_color) : AnimationEffect(name, bg_color, 0, 0) {}
AnimationEffect(const char* name, uint32_t bg_color, int x, int y);
~AnimationEffect();
AnimationEffect* setFgColor(uint32_t c);
void loop();
};

View File

@ -1,24 +0,0 @@
#ifndef effect_big_clock_H
#define effect_big_clock_H
#include "Effect.h"
class BigClockEffect : public Effect {
private:
CRGB color_h = CRGB(0xFF0000);
CRGB color_m = CRGB(0x00FF00);
CRGB color_colon = CRGB(0xFFFF00);
void drawNumber(uint8_t number, int x, int y, CRGB color);
void drawText(char *text, int x, int y, CRGB color);
const unsigned char* font_char(const unsigned char* font, char c);
void drawSprite(const unsigned char* sprite, int xOffset, int yOffset, CRGB color);
public:
void loop();
};
#endif

View File

@ -1,16 +0,0 @@
#pragma once
#include "Effect.h"
#include "prototypes.h"
#include "my_fastled.h"
#include "Window.h"
class ClockEffect : public Effect {
private:
Window* window = new Window(0, LED_HEIGHT - 6, LED_WIDTH, 6);
public:
~ClockEffect();
void loop();
void loop(boolean invert, CRGB fg_color, CRGB bg_color);
};

View File

@ -1,20 +0,0 @@
#ifndef effect_confetti_H
#define effect_confetti_H
#include "Effect.h"
#include "my_fastled.h"
class ConfettiEffect : public Effect {
protected:
virtual CRGB _getColor();
public:
void loop();
boolean can_be_shown_with_clock();
};
class RandomConfettiEffect : public ConfettiEffect {
protected:
CRGB _getColor() override;
};
#endif

View File

@ -1,7 +0,0 @@
#pragma once
#include "Effect.h"
class FireworkEffect : public Effect {
void loop();
};

View File

@ -1,27 +0,0 @@
#ifndef effect_snake_H
#define effect_snake_H
#include "Effect.h"
#include "prototypes.h"
class SnakeEffect : public Effect {
private:
Coords coords;
uint8_t direction = 1;
uint8_t hue = 0;
uint8_t run = 0;
bool is_turn_needed();
void turn_random();
bool turn_right();
bool turn_left();
bool is_direction_okay(uint8_t direction);
public:
SnakeEffect();
~SnakeEffect();
void loop();
boolean valid_position(Coords c);
Coords update_position(Coords c, uint8_t direction);
boolean can_be_shown_with_clock();
};
#endif

View File

@ -1,16 +0,0 @@
#ifndef effect_twirl_H
#define effect_twirl_H
#include "Effect.h"
#include <Arduino.h>
class TwirlEffect : public Effect {
private:
uint8_t angleOffset = 0;
double center_x = 8;
double center_y = 8;
public:
void loop();
};
#endif

View File

@ -1,18 +1,28 @@
#ifndef effects_H #pragma once
#define effects_H
#include <SimpleList.h>
#include "Effect.h" #include "Effect.h"
#include "effect_clock.h" #include "effects/clock.h"
#include "effects/timer.h"
extern const char* cycle_effects[]; #define SIMPLE_EFFECT(name, use_in_cycle, flags, ...) {name, use_in_cycle, [](){ return new SimpleEffect(name, flags, [](double t, uint16_t i, uint8_t x, uint8_t y)->double __VA_ARGS__ ); }}
extern uint8_t cycle_effects_count;
struct EffectEntry {
const char* name;
bool use_in_cycle;
std::function<Effect*()> create;
#ifdef MQTT_REPORT_METRICS
int16_t heap_change_sum;
uint16_t run_count;
#endif
};
extern EffectEntry effects[];
extern const uint8_t effects_size;
extern Effect* current_effect; extern Effect* current_effect;
extern ClockEffect effect_clock; extern ClockEffect effect_clock;
extern TimerEffect effect_timer;
Effect* select_effect(uint32_t c); Effect* select_effect(char* name);
Effect* select_effect(uint8_t id);
bool change_current_effect(String s); bool change_current_effect(String s);
void setup_effects(); void setup_effects();
#endif

View File

@ -0,0 +1,9 @@
#pragma once
#include "Effect.h"
class AnalogClockEffect : public Effect {
public:
void loop(uint16_t ms);
String get_name() override { return "analog_clock"; }
};

View File

@ -0,0 +1,24 @@
#pragma once
#include "Effect.h"
#include "prototypes.h"
#include "my_fastled.h"
#include "../Animation.h"
class AnimationEffect : public Effect {
private:
Animation *animation;
const char* name;
uint16_t xOffset;
uint16_t yOffset;
unsigned long _last_blink_at;
uint16_t _blink_freq;
public:
AnimationEffect(const char* name, uint32_t bg_color=0x000000, int x=0, int y=0);
static AnimationEffect* Blinker(const char* name, uint16_t interval, uint32_t color, uint32_t background_color=0x000000);
~AnimationEffect();
AnimationEffect* setFgColor(uint32_t c);
AnimationEffect* setBlinkFrequency(uint16_t);
void loop(uint16_t ms);
String get_name() override;
};

View File

@ -1,5 +1,4 @@
#ifndef effect_bell_H #pragma once
#define effect_bell_H
#include "Effect.h" #include "Effect.h"
#include "functions.h" #include "functions.h"
@ -10,7 +9,7 @@ private:
CRGB color_off = CRGB(0x000000); CRGB color_off = CRGB(0x000000);
boolean invert = false; boolean invert = false;
public: public:
void loop(); void loop(uint16_t ms);
String get_name() override { return "bell"; }
}; };
#endif

View File

@ -0,0 +1,21 @@
#pragma once
#include "Effect.h"
class BigClockEffect : public Effect {
protected:
CRGB _color_font = CRGB(0xAAAAAA);
CRGB _color_seconds_light = CRGB(0xFFFF00);
CRGB _color_seconds_dark = CRGB(0xAA0000);
CRGB _color_seconds_moving_light = CRGB(0x666600);
CRGB _color_seconds_moving_dark = CRGB(0x660000);
virtual CRGB _get_color_font() { return CRGB(0xAAAAAA); }
void _draw_seconds(uint8_t seconds);
virtual void _draw_border_pixel(accum88 pos, CRGB* color);
void _draw_colon(bool odd);
public:
virtual void loop(uint16_t ms);
String get_name() override { return "big_clock"; }
};

32
include/effects/blur2d.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include "prototypes.h"
#include "functions.h"
#include "Effect.h"
class Blur2DBlob {
private:
accum88 _x_freq;
accum88 _y_freq;
uint8_t _color_freq;
public:
Blur2DBlob();
void render(Window* win);
};
class Blur2DEffect : public Effect {
private:
Window* window = &Window::window_with_clock;
uint8_t _count;
Blur2DBlob* _blobs;
public:
Blur2DEffect();
~Blur2DEffect();
void _init();
void _delete();
boolean supports_window = true;
boolean can_be_shown_with_clock();
void loop(uint16_t ms);
String get_name() override { return "blur2d"; }
};

18
include/effects/clock.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "Effect.h"
#include "prototypes.h"
#include "my_fastled.h"
#include "Window.h"
class ClockEffect : public Effect {
protected:
Window* window = &Window::window_clock;
public:
virtual ~ClockEffect();
virtual void loop(uint16_t ms);
String get_name() override { return "clock"; }
void loop_with_invert(bool invert);
void loop(boolean invert, CRGB fg_color, CRGB bg_color, uint8_t y);
};

View File

@ -1,5 +1,4 @@
#ifndef effect_cycle_H #pragma once
#define effect_cycle_H
#include "Effect.h" #include "Effect.h"
#include "effects.h" #include "effects.h"
@ -9,6 +8,8 @@ private:
Effect* effect = NULL; Effect* effect = NULL;
uint16_t effect_id = -1; uint16_t effect_id = -1;
unsigned long effectSince = 0; unsigned long effectSince = 0;
uint16_t _heap_free = 0;
uint8_t _effects_count;
public: public:
CycleEffect(); CycleEffect();
~CycleEffect(); ~CycleEffect();
@ -16,8 +17,7 @@ public:
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
boolean clock_as_mask(); boolean clock_as_mask();
String get_name() override;
void loop(); void loop(uint16_t ms);
}; };
#endif

11
include/effects/diamond.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "Effect.h"
class DiamondEffect : public Effect {
private:
Window* window = &Window::window_with_clock;
public:
void loop(uint16_t ms) override;
bool can_be_shown_with_clock() override;
String get_name() override { return "diamond"; }
};

View File

@ -3,15 +3,16 @@
class DvdEffect : public Effect { class DvdEffect : public Effect {
private: private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6); Window* window = &Window::window_with_clock;
uint8_t _x = 0; saccum78 _x = 0;
uint8_t _y = 0; saccum78 _y = 0;
int8_t _x_dir = 1; int8_t _x_dir = 1;
int8_t _y_dir = 1; int8_t _y_dir = 1;
CRGB _color; CRGB _color;
public: public:
DvdEffect(); DvdEffect();
~DvdEffect(); ~DvdEffect();
void loop() override; void loop(uint16_t ms) override;
bool can_be_shown_with_clock() override; bool can_be_shown_with_clock() override;
String get_name() override { return "dvd"; }
}; };

View File

@ -13,19 +13,23 @@ public:
SingleDynamicEffect(); SingleDynamicEffect();
void init(); void init();
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
virtual void loop(); virtual void loop(uint16_t ms);
void draw(); void draw();
String get_name() override { return "single_dynamic"; }
}; };
class MultiDynamicEffect : public SingleDynamicEffect { class MultiDynamicEffect : public SingleDynamicEffect {
public: public:
void loop(); void loop(uint16_t ms);
String get_name() override { return "multi_dynamic"; }
}; };
class BigDynamicEffect : public Effect { class BigDynamicEffect : public Effect {
private: private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6); Window* window = &Window::window_with_clock;
public: public:
void loop(); void loop(uint16_t ms);
~BigDynamicEffect(); ~BigDynamicEffect();
boolean can_be_shown_with_clock() override;
String get_name() override { return "big_dynamic"; }
}; };

View File

@ -15,5 +15,6 @@ private:
public: public:
FireEffect(); FireEffect();
~FireEffect(); ~FireEffect();
void loop(); void loop(uint16_t ms);
String get_name() override { return "fire"; }
}; };

View File

@ -0,0 +1,57 @@
#pragma once
#include "prototypes.h"
#include "functions.h"
#include "Effect.h"
enum FireworkDotType { FIREWORK_DOT_NONE, FIREWORK_DOT_SHELL, FIREWORK_DOT_SPARK };
class FireworkEffect;
class FireworkEffectDot {
private:
Window* _window;
FireworkEffect* _main;
accum88 _x;
accum88 _y;
saccum78 _xv;
saccum78 _yv;
accum88 _r;
CRGB _color;
void _screenscale(accum88 a, byte n, byte& screen, byte& screenerr);
int16_t _scale15by8_local(int16_t i, fract8 scale);
public:
byte show;
FireworkDotType type;
FireworkEffectDot(Window* w, FireworkEffect* e);
void draw();
void move();
void ground_launch();
void sky_burst(accum88 basex, accum88 basey, saccum78 basedv, CRGB& basecolor);
};
class FireworkEffect : public Effect {
private:
Window* window = &Window::window_with_clock;
bool _skyburst = 0;
accum88 _burst_x;
accum88 _burst_y;
saccum78 _burst_xv;
saccum78 _burst_yv;
CRGB _burst_color;
FireworkEffectDot* _dot;
FireworkEffectDot** _sparks;
public:
FireworkEffect();
~FireworkEffect();
void skyburst(accum88 x, accum88 y, saccum78 xv, saccum78 yv, CRGB c);
boolean supports_window = true;
boolean can_be_shown_with_clock();
void loop(uint16_t ms);
String get_name() override { return "firework"; }
};

View File

@ -17,6 +17,7 @@ private:
public: public:
GolEffect(); GolEffect();
~GolEffect(); ~GolEffect();
void loop(); void loop(uint16_t ms);
bool can_be_shown_with_clock(); bool can_be_shown_with_clock();
String get_name() override { return "gol"; }
}; };

View File

@ -0,0 +1,32 @@
#pragma once
#include "Effect.h"
#include "my_fastled.h"
class LightspeedEffectStar {
private:
uint16_t _angle;
accum88 _distance;
uint16_t _speed;
uint16_t _target;
uint8_t _saturation;
void _init();
public:
LightspeedEffectStar();
void loop(Window* win);
};
class LightspeedEffect : public Effect {
private:
LightspeedEffectStar* _stars;
uint8_t _count;
void _init();
void _delete();
public:
LightspeedEffect();
~LightspeedEffect();
void loop(uint16_t ms);
boolean can_be_shown_with_clock();
String get_name() override { return "lightspeed"; }
};

19
include/effects/marquee.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include "prototypes.h"
#include "functions.h"
#include "Effect.h"
class MarqueeEffect : public Effect {
private:
Window* window = &Window::window_with_clock;
String _text = String("No text set +++ ");
saccum78 _position = (window->width<<8);
public:
boolean supports_window = true;
boolean can_be_shown_with_clock();
void loop(uint16_t ms);
void apply_option(String key, String value) override;
String get_name() override { return "marquee"; }
};

View File

@ -9,7 +9,7 @@
class MatrixEffectColumn { class MatrixEffectColumn {
protected: protected:
Window* window; Window* window;
uint8_t x, y; saccum78 x, y;
uint8_t length = 1; uint8_t length = 1;
uint8_t _direction = 2; uint8_t _direction = 2;
bool _random_direction = false; bool _random_direction = false;
@ -27,9 +27,9 @@ public:
MatrixEffectColumn(Window* win, uint8_t direction=0, bool random_direction=false); MatrixEffectColumn(Window* win, uint8_t direction=0, bool random_direction=false);
virtual ~MatrixEffectColumn() {}; virtual ~MatrixEffectColumn() {};
void advance(); void advance(uint16_t ms);
void draw(); void draw();
void loop(); void loop(uint16_t ms);
}; };
class RainbowMatrixEffectColumn : public MatrixEffectColumn { class RainbowMatrixEffectColumn : public MatrixEffectColumn {
@ -48,23 +48,58 @@ public:
RandomMatrixEffectColumn(Window* win, uint8_t dir, bool rnd=false) : MatrixEffectColumn(win, dir, rnd) {}; RandomMatrixEffectColumn(Window* win, uint8_t dir, bool rnd=false) : MatrixEffectColumn(win, dir, rnd) {};
}; };
class MatrixEffect : public Effect { class ColumnMatrixEffectColumn : public MatrixEffectColumn {
protected:
uint8_t _hue;
CRGB _getColor(uint8_t height) override;
void restart(bool completely_random) override;
public:
ColumnMatrixEffectColumn(Window* win, uint8_t dir, bool rnd=false) : MatrixEffectColumn(win, dir, rnd) {};
};
class MatrixEffectBase : public Effect {
protected: protected:
MatrixEffectColumn** _columns; MatrixEffectColumn** _columns;
virtual void _init(); uint8_t _count;
virtual uint8_t _get_count();
virtual void _delete();
void _init();
virtual void _create() = 0;
public: public:
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
MatrixEffect(); virtual ~MatrixEffectBase();
virtual ~MatrixEffect(); void loop(uint16_t ms);
void loop();
}; };
class RainbowMatrixEffect : public MatrixEffect { class MatrixEffect : public MatrixEffectBase {
private: protected:
void _init() override; void _create() override;
public:
MatrixEffect();
String get_name() override { return "matrix"; }
}; };
class RandomMatrixEffect : public MatrixEffect { class RainbowMatrixEffect : public MatrixEffectBase {
private: protected:
void _init() override; void _create() override;
public:
RainbowMatrixEffect();
String get_name() override { return "rainbow_matrix"; }
}; };
class RandomMatrixEffect : public MatrixEffectBase {
protected:
uint8_t _get_count() override;
void _create() override;
public:
RandomMatrixEffect();
String get_name() override { return "random_matrix"; }
};
class ColumnMatrixEffect : public MatrixEffectBase {
protected:
void _create() override;
public:
ColumnMatrixEffect();
String get_name() override { return "column_matrix"; }
};

View File

@ -0,0 +1,8 @@
#pragma once
#include "Effect.h"
class NightClockEffect : public Effect {
public:
virtual void loop(uint16_t ms);
String get_name() override { return "night_clock"; }
};

View File

@ -10,6 +10,7 @@ private:
public: public:
PixelClockEffect(); PixelClockEffect();
~PixelClockEffect(); ~PixelClockEffect();
void loop(); void loop(uint16_t ms);
bool can_be_shown_with_clock(); bool can_be_shown_with_clock();
String get_name() override { return "pixel_clock"; }
}; };

View File

@ -1,10 +1,15 @@
#ifndef effect_sinematrix3_H #pragma once
#define effect_sinematrix3_H
#include "prototypes.h" #include "prototypes.h"
#include "functions.h" #include "functions.h"
#include "Effect.h" #include "Effect.h"
enum SinematrixColorScheme {
SINEMATRIX_COLOR_PASTEL_RAINBOW,
SINEMATRIX_COLOR_RAINBOW,
SINEMATRIX_COLOR_PURPLEFLY,
};
class Sinematrix3Effect : public Effect { class Sinematrix3Effect : public Effect {
private: private:
double pangle = 0; double pangle = 0;
@ -26,12 +31,14 @@ private:
double fx = 1.0 / (LED_WIDTH / PI); double fx = 1.0 / (LED_WIDTH / PI);
double fy = 1.0 / (LED_HEIGHT / PI); double fy = 1.0 / (LED_HEIGHT / PI);
Matrix rotate; Matrix rotate;
SinematrixColorScheme _color_scheme;
CRGB _get_color(int value);
public: public:
Sinematrix3Effect(SinematrixColorScheme s = SINEMATRIX_COLOR_PASTEL_RAINBOW): _color_scheme(s) {};
boolean supports_window = true; boolean supports_window = true;
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
boolean clock_as_mask(); boolean clock_as_mask();
void loop(); void loop(uint16_t ms);
String get_name() override { return "sinematrix3"; }
}; };
#endif

34
include/effects/sines.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include "prototypes.h"
#include "functions.h"
#include "Effect.h"
class SinesEffectSinus {
private:
uint16_t _frequency;
uint16_t _color_frequency;
uint16_t _amplitude;
uint16_t _x;
uint16_t _offset;
Window* _window;
public:
SinesEffectSinus(Window* w);
void loop(uint16_t ms);
};
class SinesEffect : public Effect {
private:
SinesEffectSinus** _sinus;
uint8_t _count;
void _init();
void _delete();
public:
SinesEffect();
~SinesEffect();
boolean supports_window = true;
boolean can_be_shown_with_clock();
void loop(uint16_t ms);
String get_name() override { return "sines"; }
};

68
include/effects/snake.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include "Effect.h"
#include "prototypes.h"
#define SNAKE_DIR_NORTH 0
#define SNAKE_DIR_EAST 1
#define SNAKE_DIR_SOUTH 2
#define SNAKE_DIR_WEST 3
#define SNAKE_DEBUG false
class SnakeEffect : public Effect {
private:
Coords _pos;
Coords _apple;
Coords _tail;
int8_t _dir = SNAKE_DIR_NORTH;
uint8_t* _map;
uint16_t _pixels;
uint8_t _length;
unsigned long _last_apple_at;
unsigned long _last_move_at;
uint16_t _round;
// Neural net config
// These are actually float values. But in order to prevent rounding errors and stuff, they are provided
// in form of the raw binary data of the IEE754 floating point numbers.
// In _decide() there's code to memcpy()-convert them to a float.
// Round 340, 223.4 points, length 39, 36% stopped, 64% died
// const uint32_t _weights[36] = {0xbd8e626e, 0xbee2cd2c, 0x3e4d5cab, 0x3eceb8c3, 0xbed0a514, 0x3ec62438, 0x3e947ed4, 0xbe4b8bf2, 0xbf301113, 0xbf3f0a75, 0x3f1868f7, 0xbf0253ca, 0xbedca2f2, 0xbd547c6d, 0x3edd6a8a, 0xbd4b97b6, 0x3f64ec26, 0xbe5323c1, 0x3eccf87d, 0xbf2d4796, 0xbf62b6e8, 0xbf71daf6, 0xbf03f08e, 0xbf222609, 0x3e26c03c, 0xbf497837, 0xbee4d175, 0x3ec601de, 0x3e4e0695, 0x3eef2619, 0xbe849370, 0xbf18fb2b, 0x3f25bbd1, 0xbf3dcd78, 0x3f37a58d, 0x3ef4a25b};
// Round 630, 221.0 points, length 38, 36% stopped, 64% died
const uint32_t _weights[36] = {0xbd25943f, 0xbf279d81, 0x3e25d128, 0x3ec62438, 0x3f0e719c, 0x3eefbea9, 0x3e947ed4, 0xbe5323c1, 0xbf2d4796, 0xbf3f0a75, 0x3f0e45d9, 0xbf0253ca, 0xbedca2f2, 0xbd79073c, 0x3ede80ec, 0xbd4b97b6, 0x3f69a6be, 0xbe4b8bf2, 0x3eccf87d, 0xbf301113, 0xbf62b6e8, 0xbf71daf6, 0xbf204130, 0xbf222609, 0x3e26c03c, 0xbf497837, 0xbee4d175, 0x3ec601de, 0x3e4954eb, 0x3eef2619, 0xbe849370, 0xbf18fb2b, 0x3f25bbd1, 0xbf3b4e44, 0x3f484d59, 0x3edd6a8a};
// Round 193, 164.8 points, length 36, 6% stopped, 94% died
//const uint32_t _weights[36] = {0x3e872ffb, 0xbea57262, 0xbee269bf, 0x3ed790a3, 0xbf54014f, 0x3ecde0a6, 0xbf240a93, 0xbe9e4782, 0x3f205106, 0xbf4465c2, 0xbf79579a, 0xbf07f122, 0x3ed0e1bc, 0xbf7a5a09, 0xbf0fc70b, 0xbf6d1971, 0xbe0f5585, 0xbec94b12, 0x3f51f7a9, 0x3eaac42b, 0xbe6aafa6, 0x3d3e3ce3, 0xbf7c4232, 0xbe634103, 0x3f800000, 0x3eff886c, 0x3deae1e8, 0x3eea6988, 0xbf800000, 0xbf426a20, 0x3e3a0a45, 0xbe848803, 0x3e84e8c9, 0x3ef9fabc, 0xbe7733e6, 0xbecda633};
// Round 13650, 244.8 points, length 42, 34% stopped, 66% died
//const uint32_t _weights[36] = {0xbeb99de3, 0x3e6ca488, 0xbe3e9dad, 0xbed38d4e, 0x3f279fc1, 0xbd367111, 0xbf473843, 0xbf800000, 0x3f614edc, 0xbecc734f, 0xbe59b29d, 0x3d479078, 0x3efa7ca6, 0xbedc6ce6, 0x3f4626a1, 0x3e9d8c2c, 0x3f29e25c, 0x3ebde05d, 0x3e8f3e29, 0xbe8ad92c, 0xbe148f2d, 0x3d4a3ca7, 0xbf800000, 0x3d9cd634, 0x3f29802e, 0xbf2cc57e, 0xbcbfafff, 0x3e280b8a, 0x3f5ff9a3, 0xbf4e29c9, 0x3e8936d2, 0xbf49dda9, 0xbe9bf4c7, 0x3e203ea8, 0xbd4edf4d, 0xbf4e3c05};
const uint8_t _net_layout[3] = {6, 4, 3};
const uint8_t _net_layers = 3;
const uint8_t _net_total_size = 36;
uint8_t _head_rounds = 0;
uint8_t _tail_rounds = 0;
uint16_t _xy2i(uint8_t x, uint8_t y);
uint16_t _xy2i(Coords c);
Coords _i2xy(uint16_t i);
Coords _new_pos(uint8_t dir);
uint8_t _dying = 0;
bool _is_free(uint8_t dir);
uint8_t _free_spaces(uint8_t dir);
uint8_t _to_apple(uint8_t dir);
void _place_apple();
void _init();
void _decide();
uint8_t _coords_to_field_id(Coords);
int8_t _manual_decision();
void _move();
void _draw();
public:
SnakeEffect();
~SnakeEffect();
void loop(uint16_t ms);
boolean can_be_shown_with_clock();
String get_name() override { return "snake"; }
};

View File

@ -1,5 +1,4 @@
#ifndef effect_static_H #pragma once
#define effect_static_H
#include "Effect.h" #include "Effect.h"
#include "my_fastled.h" #include "my_fastled.h"
@ -10,7 +9,7 @@ private:
public: public:
StaticEffect(CRGB col); StaticEffect(CRGB col);
boolean supports_window = true; boolean supports_window = true;
void loop(); void loop(uint16_t ms);
String get_name() override { return "static"; }
}; };
#endif

16
include/effects/timer.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "Effect.h"
#include "prototypes.h"
#include "my_fastled.h"
#include "Window.h"
class TimerEffect : public Effect {
protected:
Window* window = new Window(0, 0, LED_WIDTH, 6);
public:
~TimerEffect();
void loop(uint16_t ms);
String get_name() override { return "timer"; }
};

View File

@ -0,0 +1,30 @@
#pragma once
#include "Effect.h"
#include "prototypes.h"
#include "my_fastled.h"
#include "Window.h"
#include "config.h"
#include <WiFiUdp.h>
class Tpm2NetEffect : public Effect {
protected:
Window* window = &Window::window_full;
WiFiUDP _udp;
uint16_t _pixel_index = 0;
void _parse_command(uint16_t size, uint8_t packet_number);
void _parse_data(uint16_t size, uint8_t packet_number);
void _respond(uint8_t* data, uint8_t len);
void _respond_ack();
void _respond_with_data(uint8_t* data, uint8_t len);
void _respond_unknown_command();
unsigned long _last_packet_at = 0;
public:
Tpm2NetEffect();
virtual ~Tpm2NetEffect();
virtual void loop(uint16_t ms);
bool can_be_shown_with_clock();
String get_name() override { return "tpm2.net"; }
};

View File

@ -0,0 +1,12 @@
#pragma once
#include "Effect.h"
class TvStaticEffect : public Effect {
private:
Window* _window = &Window::window_with_clock;
public:
~TvStaticEffect();
void loop(uint16_t ms) override;
bool can_be_shown_with_clock() override;
String get_name() override { return "tv_static"; }
};

18
include/effects/twirl.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "Effect.h"
#include <Arduino.h>
class TwirlEffect : public Effect {
private:
uint8_t angleOffset = 0;
uint8_t _center_offset_angle = 0;
double _real_center_x = LED_WIDTH / 2;
double _real_center_y = LED_HEIGHT / 2;
public:
void loop(uint16_t ms);
boolean can_be_shown_with_clock() override;
boolean clock_as_mask() override;
String get_name() override { return "twirl"; }
};

View File

@ -2,4 +2,7 @@
#include "prototypes.h" #include "prototypes.h"
extern Font font_numbers3x5; extern Font font_numbers3x5;
extern Font font_numbers3x3;
extern Font font_numbers3x5_blocky;
extern Font font_numbers4x7; extern Font font_numbers4x7;
extern Font font5x7;

View File

@ -5,15 +5,13 @@
#include "my_wifi.h" #include "my_wifi.h"
#include <FS.h> #include <FS.h>
#include <ESPAsyncWebServer.h>
#if defined ( ESP8266 ) extern AsyncWebServer http_server;
extern ESP8266WebServer http_server;
#elif defined ( ESP32 )
extern ESP32WebServer http_server;
#endif
extern File upload_file; extern File upload_file;
extern uint32_t monitor_client;
void http_server_setup(); void http_server_setup();
void http_server_loop(); void http_server_send_framedata();
#endif #endif

View File

@ -6,3 +6,4 @@
extern const TProgmemRGBGradientPalette_byte palette_fire[] FL_PROGMEM; extern const TProgmemRGBGradientPalette_byte palette_fire[] FL_PROGMEM;
extern const TProgmemRGBGradientPalette_byte palette_matrix[] FL_PROGMEM; extern const TProgmemRGBGradientPalette_byte palette_matrix[] FL_PROGMEM;
extern const TProgmemRGBGradientPalette_byte palette_purplefly_gp[] FL_PROGMEM;

View File

@ -17,7 +17,8 @@ void mqtt_setup();
void mqtt_loop(); void mqtt_loop();
void mqtt_publish(const char* topic, int number); void mqtt_publish(const char* topic, int number, bool retain=false);
void mqtt_publish(const char* topic, const char* message, bool retain=false);
void mqtt_log(const char* message); void mqtt_log(const char* message);
void mqtt_log(int number); void mqtt_log(int number);

View File

@ -5,13 +5,11 @@
#if defined( ESP8266 ) #if defined( ESP8266 )
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#elif defined( ESP32 ) #elif defined( ESP32 )
#include <WiFi.h> #include <WiFi.h>
#include <ESPmDNS.h> #include <ESPmDNS.h>
#include <WiFiClient.h> #include <WiFiClient.h>
#include <WiFiServer.h> #include <WiFiServer.h>
#include <ESP32WebServer.h>
#endif #endif
#include <WiFiUdp.h> #include <WiFiUdp.h>

View File

@ -1,9 +1,5 @@
#pragma once #pragma once
#include <NTPClient.h>
#include "my_wifi.h"
#include "config.h" #include "config.h"
void time_changed();
extern NTPClient ntpClient;
void updateCallback(NTPClient* c);
void ntp_setup(); void ntp_setup();

View File

@ -1,6 +1,12 @@
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include "my_fastled.h"
extern uint8_t baseHue;
extern char hostname[30];
extern uint16_t frame;
extern unsigned long timer;
typedef struct { typedef struct {
uint8_t width; uint8_t width;
@ -27,5 +33,4 @@ typedef struct {
uint16_t y; uint16_t y;
} Coords; } Coords;
extern uint8_t baseHue; typedef std::function<double(double, uint16_t, uint8_t, uint8_t)> simple_effect_t;
extern char hostname[30];

104
include/settings.h Normal file
View File

@ -0,0 +1,104 @@
#pragma once
#include <Arduino.h>
enum SettingType {
TYPE_UINT8,
TYPE_UINT16,
TYPE_BOOL
};
typedef struct {
const char* name;
uint16_t* value;
SettingType type;
} Setting;
struct Settings {
uint16_t fps = 50;
struct /* effects */ {
struct /* cycle */ {
uint16_t time = 300;
uint16_t random = 1;
} cycle ;
struct /* matrix */ {
uint16_t length_min = 4;
uint16_t length_max = 20;
uint16_t speed_min = 3;
uint16_t speed_max = 7;
uint16_t count = 16;
uint16_t random_count = 32;
} matrix;
struct /* big_clock */ {
uint16_t spacing = 5;
} big_clock;
struct /* blur2d */ {
uint16_t count = 5;
} blur2d;
struct /* confetti */ {
uint16_t pixels_per_loop = 2;
} confetti;
struct /* dvd */ {
uint16_t width = 3;
uint16_t height = 2;
uint16_t speed = 50;
} dvd;
struct /* dynamic */ {
uint16_t single_loop_time = 40;
uint16_t multi_loop_time = 1400;
uint16_t big_loop_time = 50;
uint16_t big_size = 3;
} dynamic;
struct /* fire */ {
uint16_t cooldown = 192;
uint16_t spark_chance = 5;
} fire;
struct /* firework */ {
uint16_t drag = 255;
uint16_t bounce = 200;
uint16_t gravity = 10;
uint16_t sparks = 12;
} firework;
struct /* gol */ {
uint16_t start_percentage = 90;
uint16_t blend_speed = 10;
uint16_t restart_after_steps = 100;
} gol;
struct /* lightspeed */ {
uint16_t count = 25;
} lightspeed;
struct /* sines */ {
uint16_t count = 5;
} sines;
struct /* snake */ {
uint16_t direction_change = 5;
uint16_t slowdown = 2;
} snake;
struct /* tv_static */ {
uint16_t black_bar_speed = 12;
} tv_static;
} effects;
};
extern Settings settings;
extern Setting all_settings[];
extern const uint8_t all_settings_size;
bool change_setting(const char* key, uint16_t new_value);
uint16_t setting_default(Setting* s);
bool save_settings();
bool load_settings();

View File

@ -10,31 +10,31 @@
[platformio] [platformio]
lib_dir = lib lib_dir = lib
env_default = ota default_envs = ota
[extra] [extra]
lib_deps = lib_deps =
PubSubClient PubSubClient
https://github.com/fabianonline/FastLED.git https://github.com/fabianonline/FastLED.git
https://github.com/fabianonline/NTPClient.git https://github.com/me-no-dev/ESPAsyncWebServer.git
ESP8266WebServer
ErriezCRC32
[env:ota] [env:ota]
upload_port = 10.10.2.78 upload_port = 10.10.2.78 ; .78=prod, .80=dev
upload_protocol = espota upload_protocol = espota
platform = espressif8266 platform = espressif8266
board = esp07 board = esp07
framework = arduino framework = arduino
lib_deps = ${extra.lib_deps} lib_deps = ${extra.lib_deps}
lib_ldf_mode = chain+ lib_ldf_mode = deep
build_flags = -Wl,-Teagle.flash.2m512.ld build_flags = -Wl,-Teagle.flash.2m512.ld
[env:local] [env:local]
upload_port = /dev/cu.wchusbserial1420 upload_port = /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0
platform = espressif8266 platform = espressif8266
board = esp07 board = esp07
framework = arduino framework = arduino
lib_deps = ${extra.lib_deps} lib_deps = ${extra.lib_deps}
lib_ldf_mode = chain+ lib_ldf_mode = deep
build_flags = -Wl,-Teagle.flash.2m512.ld build_flags = -Wl,-Teagle.flash.2m512.ld
monitor_filters = time, esp8266_exception_decoder
build_type = debug

View File

@ -75,7 +75,7 @@ bool Animation::_load_from_file(const char* filename) {
return false; return false;
} }
if (file.available() != size - 6) { if (file.available() < 0 || file.available() + 6 != size) {
LOGln("Animation * Expected file to have %d bytes available, but found %d bytes available.", size - 6, file.available()); LOGln("Animation * Expected file to have %d bytes available, but found %d bytes available.", size - 6, file.available());
file.close(); file.close();
return false; return false;
@ -84,16 +84,16 @@ bool Animation::_load_from_file(const char* filename) {
// Now we can be sure to have the right amount of bytes available for reading // Now we can be sure to have the right amount of bytes available for reading
_width = file.read(); _width = file.read();
LOGln("Animation * width: %d", _width); DBG("Animation * width: %d", _width);
_height = file.read(); _height = file.read();
LOGln("Animation * height: %d", _height); DBG("Animation * height: %d", _height);
_frame_count = file.read(); _frame_count = file.read();
LOGln("Animation * frame_count: %d", _frame_count); DBG("Animation * frame_count: %d", _frame_count);
_color_count = file.read(); _color_count = file.read();
LOGln("Animation * color_count: %d", _color_count); DBG("Animation * color_count: %d", _color_count);
LOGln("Animation * Loading colors..."); DBG("Animation * Loading colors...");
_colors = new CRGB*[_color_count]; _colors = new CRGB*[_color_count];
char* temp = new char[_color_count*3]; char* temp = new char[_color_count*3];
int bytes_read = file.readBytes(temp, _color_count*3); int bytes_read = file.readBytes(temp, _color_count*3);
@ -102,36 +102,33 @@ bool Animation::_load_from_file(const char* filename) {
_colors[i] = new CRGB(temp[i*3], temp[i*3+1], temp[i*3+2]); _colors[i] = new CRGB(temp[i*3], temp[i*3+1], temp[i*3+2]);
} }
delete [] temp; delete [] temp;
LOGln("Animation * Color loading done."); DBG("Animation * Color loading done.");
LOG("Animation * Loading frame times..."); DBG("Animation * Loading frame times...");
_frame_times = new uint16_t[_frame_count]; _frame_times = new uint16_t[_frame_count];
for (int i=0; i<_frame_count; i++) { for (int i=0; i<_frame_count; i++) {
_frame_times[i] = (file.read() << 8) | file.read(); _frame_times[i] = (file.read() << 8) | file.read();
} }
LOGln(" done."); DBG(" done.");
LOGln("Animation * Loading frame lengths..."); DBG("Animation * Loading frame lengths...");
_frame_data_lengths = new uint16_t[_frame_count]; _frame_data_lengths = new uint16_t[_frame_count];
temp = new char[_frame_count*2]; temp = new char[_frame_count*2];
bytes_read = file.readBytes(temp, _frame_count*2); bytes_read = file.readBytes(temp, _frame_count*2);
LOGln("Animation * Read %d bytes.", bytes_read); DBG("Animation * Read %d bytes.", bytes_read);
for (int i=0; i<_frame_count; i++) { for (int i=0; i<_frame_count; i++) {
//LOGln("Animation * Raw frame lengths: %d, %d", temp[i*2], temp[i*2+1]); //LOGln("Animation * Raw frame lengths: %d, %d", temp[i*2], temp[i*2+1]);
_frame_data_lengths[i] = (temp[i*2]<<8) | temp[i*2+1]; _frame_data_lengths[i] = (temp[i*2]<<8) | temp[i*2+1];
} }
delete [] temp; delete [] temp;
LOGln("Animation * Frame length loading done."); DBG("Animation * Frame length loading done.");
LOGln("Animation * Loading frame data..."); DBG("Animation * Loading frame data...");
_frame_data = new uint8_t*[_frame_count]; _frame_data = new uint8_t*[_frame_count];
for (int i=0; i<_frame_count; i++) { for (int i=0; i<_frame_count; i++) {
uint16_t fl = _frame_data_lengths[i]; uint16_t fl = _frame_data_lengths[i];
LOGln("Animation * Loading frame %d with %d bytes...", i, fl); DBG("Animation * Loading frame %d/%d with %d bytes...", i, _frame_count, fl);
_frame_data[i] = new uint8_t[fl]; _frame_data[i] = new uint8_t[fl];
/*for (int b=0; b<fl; b++) {
_frame_data[i][b] = file.read();
}*/
file.readBytes((char*)_frame_data[i], fl); file.readBytes((char*)_frame_data[i], fl);
} }
LOGln("Animation * Frame data loaded."); LOGln("Animation * Frame data loaded.");
@ -207,27 +204,27 @@ void Animation::setSingleFrame(uint8_t frame) {
Animation::~Animation() { Animation::~Animation() {
for (int i=0; i<_color_count; i++) delete _colors[i]; for (int i=0; i<_color_count; i++) delete _colors[i];
LOGln("Deleting _colors...");
if (_colors) delete [] _colors;
LOGln("Deleting fgColor...");
DBG("Animation * Deleting _colors...");
if (_colors) delete[] _colors;
DBG("Animation * Deleting fgColor...");
if (fgColor != NULL) delete fgColor; if (fgColor != NULL) delete fgColor;
LOGln("Deleting bgColor...");
DBG("Animation * Deleting bgColor...");
if (bgColor != NULL) delete bgColor; if (bgColor != NULL) delete bgColor;
LOGln("Deleting _frame_data_lengths...");
if (_frame_data_lengths) delete [] _frame_data_lengths; DBG("Animation * Deleting _frame_data_lengths...");
LOGln("Deleting _frame_times..."); if (_frame_data_lengths) delete[] _frame_data_lengths;
if (_frame_times) delete [] _frame_times; DBG("Animation * Deleting _frame_times...");
if (_frame_times) delete[] _frame_times;
for (int i=0; i<_frame_count; i++) { for (int i=0; i<_frame_count; i++) {
delete [] _frame_data[i]; delete[] _frame_data[i];
} }
LOGln("Deleting _frame_data..."); DBG("Animation * Deleting _frame_data...");
if (_frame_data) delete[] _frame_data;
if (_frame_data) delete [] _frame_data; LOGln("Animation * Deletion done.");
LOGln("Deleteion done.");
} }
void Animation::draw() { void Animation::draw() {

35
src/SimpleEffect.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "SimpleEffect.h"
void SimpleEffect::loop(uint16_t ms) {
if (_flags & SE_FADEOUT) window->fadeToBlackBy(3);
double t = 0.001 * millis();
for(uint8_t x=0; x<window->width; x++) for(uint8_t y=0; y<window->height; y++) {
uint16_t i = y*window->width + x;
double r = _method(t, i, x, y);
//if (i==0) Serial.printf("t=%f i=%d x=%d y=%d => r=%f, abs(r)=%d\n", t, i, x, y, r, abs(r)*255);
if ((_flags & SE_DEBUG) && i==17) Serial.printf("t=%f i=%d x=%d y=%d => r=%f, abs(r*255)=%d\n", t, i, x, y, r, (int)abs(r*255));
if ((_flags & SE_FADEOUT) && r==0) {
continue;
}
// Clamp r between -1.0 and +1.0
if (r<-1.0) {
r = -1.0;
} else if (r>1.0) {
r = 1.0;
}
if (_flags & SE_ONLY_POSITIVE) {
r = abs(r);
}
CRGB color;
if (_flags & SE_RANDOM_PIXEL_COLORS) {
color = CHSV(random8(), 255, abs(r*255));
} else {
color = CHSV(_flags & SE_CYCLE_COLORS ? baseHue : _color, r<0?0:255, abs(r*255));
}
window->setPixel(x, y, &color);
}
}

View File

@ -1,10 +1,9 @@
#include "Window.h" #include "Window.h"
#include "functions.h" #include "functions.h"
Window* Window::getFullWindow() { Window Window::window_full = Window();
static Window win; Window Window::window_with_clock = Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
return &win; Window Window::window_clock = Window(0, LED_HEIGHT-6, LED_WIDTH, 6);
}
void Window::setPixel(uint8_t x, uint8_t y, CRGB* color) { void Window::setPixel(uint8_t x, uint8_t y, CRGB* color) {
if (x>=this->width || y>=this->height) return; if (x>=this->width || y>=this->height) return;
@ -41,7 +40,20 @@ void Window::clear(CRGB* color) {
for(int x=0; x<this->width; x++) for(int y=0; y<this->height; y++) this->setPixel(x, y, color); for(int x=0; x<this->width; x++) for(int y=0; y<this->height; y++) this->setPixel(x, y, color);
} }
void Window::drawChar(Font* font, uint8_t xPos, uint8_t yPos, const char c, CRGB* color, bool mask) { void Window::drawText(Font* font, uint16_t x, uint16_t y, String text, CRGB* color) {
for (uint16_t i=0; i<text.length(); i++) {
drawChar(font, (x+((i*(font->width + 1))<<8)), (y<<8), text[i], color);
}
}
void Window::drawSubText(Font* font, accum88 x, accum88 y, String text, CRGB* color) {
for (uint16_t i=0; i<text.length(); i++) {
drawChar(font, x+(i*((font->width + 1)<<8)), y, text[i], color);
}
}
void Window::drawChar(Font* font, accum88 xPos, accum88 yPos, const char c, CRGB* color, bool mask) {
SubpixelRenderingMode mode = mask ? SUBPIXEL_RENDERING_SET : SUBPIXEL_RENDERING_ADD;
if (!font->isCharAllowed(c)) return; if (!font->isCharAllowed(c)) return;
uint16_t position = font->getCharPosition(c); uint16_t position = font->getCharPosition(c);
uint8_t* data = new uint8_t[font->width]; uint8_t* data = new uint8_t[font->width];
@ -50,90 +62,224 @@ void Window::drawChar(Font* font, uint8_t xPos, uint8_t yPos, const char c, CRGB
for(uint8_t y=0; y<font->height; y++) for(uint8_t x=0; x<font->width; x++) { for(uint8_t y=0; y<font->height; y++) for(uint8_t x=0; x<font->width; x++) {
bool on = (data[x]>>(font->height - 1 - y) & 1) * 255; bool on = (data[x]>>(font->height - 1 - y) & 1) * 255;
if (mask) on = !on; if (mask) on = !on;
if (on) this->setPixel(x + xPos, y + yPos, color); if (on) this->setSubPixel(xPos + (x<<8), yPos + (y<<8), color, mode);
} }
free(data); free(data);
} }
void Window::clear_row(uint8_t y) {
CRGB black(0x000000);
for (uint8_t x=0; x<this->width; x++) this->setPixel(x, y, &black);
}
void Window::shift_down() {
for (uint8_t y=this->height-1; y>=1; y--) {
for (uint8_t x=0; x<this->width; x++) {
leds[coordsToGlobalIndex(x, y)] = leds[coordsToGlobalIndex(x, y-1)];
}
}
clear_row(0);
}
void Window::shift_down_and_blur() {
shift_down();
for (uint8_t y=1; y<this->height; y++) {
blur_row(y, 128);
}
}
void Window::blur(fract8 intensity) {
blur_rows(intensity);
blur_columns(intensity);
}
void Window::blur_rows(fract8 intensity) {
for (uint8_t y=0; y<this->height; y++) {
blur_row(y, intensity);
}
}
void Window::blur_columns(fract8 intensity) {
for (uint8_t x=0; x<this->width; x++) {
blur_column(x, intensity);
}
}
void Window::blur_row(uint8_t y, fract8 intensity) {
uint16_t idx1 = coordsToGlobalIndex(0, y);
uint16_t idx2 = coordsToGlobalIndex(this->width - 1, y);
blur1d(&(leds[idx1 < idx2 ? idx1 : idx2]), this->width, intensity);
}
void Window::blur_column(uint8_t x, fract8 intensity) {
// Reimplementation from FastLEDs blurColumns
uint8_t keep = 255 - intensity;
uint8_t seep = intensity >> 1;
CRGB carryover = CRGB::Black;
for (uint8_t y=0; y<this->height; y++) {
CRGB cur = leds[coordsToGlobalIndex(x, y)];
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (y>0) leds[coordsToGlobalIndex(x, y-1)] += part;
leds[coordsToGlobalIndex(x, y)] = cur;
carryover = part;
}
}
void Window::addPixelColor(uint16_t index, CRGB* color) { void Window::addPixelColor(uint16_t index, CRGB* color) {
leds[localToGlobalIndex(index)] += *color; leds[localToGlobalIndex(index)] += *color;
} }
void Window::addPixelColor(uint8_t x, uint8_t y, CRGB* color) {
leds[coordsToGlobalIndex(x, y)] += *color;
}
CRGB Window::get_pixel(uint8_t x, uint8_t y) {
return leds[coordsToGlobalIndex(x, y)];
}
void Window::fadeToBlackBy(fract8 speed) { void Window::fadeToBlackBy(fract8 speed) {
for (uint8_t x=0; x<this->width; x++) for(uint8_t y=0; y<this->height; y++) { for (uint8_t x=0; x<this->width; x++) for(uint8_t y=0; y<this->height; y++) {
leds[coordsToGlobalIndex(x, y)].nscale8(255 - speed); leds[coordsToGlobalIndex(x, y)].nscale8(255 - speed);
} }
} }
void Window::line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, CRGB* color) { void Window::line(saccum78 x1, saccum78 y1, saccum78 x2, saccum78 y2, CRGB* color) {
// Bresenham algorithm // Bresenham algorithm
int16_t dx = abs(x2-x1); const uint8_t stepsize = 64;
int16_t dy = abs(y2-y1); saccum78 dx = abs(x2 - x1);
saccum78 dy = -abs(y2 - y1);
int8_t sx = x1<x2 ? 1 : -1; int8_t sx = x1<x2 ? 1 : -1;
int8_t sy = y1<y2 ? 1 : -1; int8_t sy = y1<y2 ? 1 : -1;
int16_t err = dx + dy; saccum78 err = dx + dy;
int16_t e2; saccum78 e2;
uint8_t step = 0;
while (1) { while (1) {
setPixel(x1, y1, color); if (step == 0) setSubPixel(x1, y1, color, SUBPIXEL_RENDERING_RAISE);
if (x1==x2 && y1==y2) break; if (++step >= stepsize) step=0;
if (x1>>8==x2>>8 && y1>>8==y2>>8) break;
e2 = 2*err; e2 = 2*err;
if (e2 > dy) { err += dy; x1 += sx; } if (e2 > dy) {
if (e2 < dx) { err += dx; y1 += sy; } err += dy;
x1 += sx;
}
if (e2 < dx) {
err += dx;
y1 += sy;
}
} }
} }
void Window::_circle_point(int x0, int y0, int x1, int y1, CRGB* color) {
setPixel(x0+x1, y0+y1, color);
setPixel(x0-x1, y0+y1, color);
setPixel(x0+x1, y0-y1, color);
setPixel(x0-x1, y0-y1, color);
setPixel(x0+y1, y0+x1, color);
setPixel(x0-y1, y0+x1, color);
setPixel(x0+y1, y0-x1, color);
setPixel(x0-y1, y0-x1, color);
}
void Window::circle(uint8_t x0, uint8_t y0, uint8_t radius, CRGB* color) { void Window::circle(uint8_t x0, uint8_t y0, uint8_t radius, CRGB* color) {
// Again, Bresenham // Again, Bresenham
uint8_t f = 1 - radius; int x=0, y=radius;
int16_t ddF_x = 0; int d=3 - 2*radius;
int16_t ddF_y = -2 * radius; _circle_point(x0, y0, x, y, color);
uint8_t x = 0; while (y >= x) {
uint8_t y = radius;
setPixel(x0, y0 + radius, color);
setPixel(x0, y0 - radius, color);
setPixel(x0 + radius, y0, color);
setPixel(x0 - radius, y0, color);
while (x < y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++; x++;
ddF_x += 2; if (d>0) {
f += ddF_x + 1; y--;
d = d + 4*(x-y) + 10;
setPixel(x0 + x, y0 + y, color); } else {
setPixel(x0 - x, y0 + y, color); d = d + 4*x + 6;
setPixel(x0 + x, y0 - y, color); }
setPixel(x0 - x, y0 - y, color); _circle_point(x0, y0, x, y, color);
setPixel(x0 + y, y0 + x, color);
setPixel(x0 - y, y0 + x, color);
setPixel(x0 + y, y0 - x, color);
setPixel(x0 - y, y0 - x, color);
} }
} }
void Window::lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t length, CRGB* color) { void Window::lineWithAngle(uint8_t x, uint8_t y, uint16_t angle, uint8_t length, CRGB* color) {
lineWithAngle(x, y, angle, 0, length, color); lineWithAngle(x, y, angle, 0, length, color);
} }
void Window::lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t startdist, uint8_t length, CRGB* color) { void Window::lineWithAngle(uint8_t x, uint8_t y, uint16_t angle, uint8_t startdist, uint8_t length, CRGB* color) {
int16_t x1 = x; //LOGln("lineWithAngle called. x: %d.%03d, y: %d.%03d, angle: %d", x>>8, x&0xFF, y>>8, y&0xFF, angle);
int16_t y1 = y; saccum78 x1 = x<<8;
saccum78 y1 = y<<8;
if (startdist > 0) { if (startdist > 0) {
x1 = x + scale8(startdist, cos8(angle)); x1 = (x<<8) + (startdist<<8) * cos16(angle) / 0x10000;
y1 = y + scale8(startdist, sin8(angle)); y1 = (y<<8) + (startdist<<8) * sin16(angle) / 0x10000;
}
if (length==0) {
setSubPixel(x1, y1, color);
return;
} }
int16_t x2 = x + scale8(startdist + length, cos8(angle)); saccum78 x2 = (x<<8) + ((startdist + length)<<8) * cos16(angle) / 0x10000;
int16_t y2 = y + scale8(startdist + length, sin8(angle)); saccum78 y2 = (y<<8) + ((startdist + length)<<8) * sin16(angle) / 0x10000;
//LOGln("x1: %d.%03d, y1: %d.%03d, x2: %d.%03d, y2: %d.%03d", x1>>8, x1&0xFF, y1>>8, y1&0xFF, x2>>8, x2&0xFF, y2>>8, y2&0xFF);
line(x1, y1, x2, y2, color); line(x1, y1, x2, y2, color);
} }
void Window::setSubPixel(accum88 x, accum88 y, CRGB* color, SubpixelRenderingMode mode) {
uint8_t px, py;
// px = Part of next pixel to fill
// x = "Origin pixel"
px = x & 0xFF;
py = y & 0xFF;
x = x >> 8;
y = y >> 8;
CRGB c;
c = CRGB(scale8( scale8( color->r, 255-py), 255-px),
scale8( scale8( color->g, 255-py), 255-px),
scale8( scale8( color->b, 255-py), 255-px));
_subpixel_render(x, y, &c, mode);
if (px) {
c = CRGB(scale8( scale8( color->r, 255-py), px),
scale8( scale8( color->g, 255-py), px),
scale8( scale8( color->b, 255-py), px));
_subpixel_render(x+1, y, &c, mode);
}
if (py) {
c = CRGB(scale8( scale8( color->r, py), 255-px),
scale8( scale8( color->g, py), 255-px),
scale8( scale8( color->b, py), 255-px));
_subpixel_render(x, y+1, &c, mode);
}
if (px || py) {
c = CRGB(scale8( scale8( color->r, py), px),
scale8( scale8( color->g, py), px),
scale8( scale8( color->b, py), px));
_subpixel_render(x+1, y+1, &c, mode);
}
}
void Window::_subpixel_render(uint8_t x, uint8_t y, CRGB* color, SubpixelRenderingMode mode) {
switch(mode) {
case SUBPIXEL_RENDERING_ADD: addPixelColor(x, y, color); break;
case SUBPIXEL_RENDERING_RAISE: raisePixel(x, y, color); break;
case SUBPIXEL_RENDERING_SET: setPixel(x, y, color); break;
}
}
void Window::fill_with_checkerboard() {
CRGB pink(0xFF00FF);
CRGB black(0x000000);
uint8_t s = (uint8_t)(millis() / 1000);
for(int x=0; x<this->width; x++) {
for(int y=0; y<this->height; y++) {
this->setPixel(x, y, ((x+y+s) % 2)?&pink:&black);
}
}
}

View File

@ -9,3 +9,14 @@ __attribute__ ((aligned(4))) extern const TProgmemRGBGradientPalette_byte palett
__attribute__ ((aligned(4))) extern const TProgmemRGBGradientPalette_byte palette_matrix[] FL_PROGMEM = { __attribute__ ((aligned(4))) extern const TProgmemRGBGradientPalette_byte palette_matrix[] FL_PROGMEM = {
0, 0, 0, 0, // black 0, 0, 0, 0, // black
255, 0,255, 0 }; // green 255, 0,255, 0 }; // green
// Gradient palette "purplefly_gp", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/rc/tn/purplefly.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
// Size: 16 bytes of program space.
__attribute__ ((aligned(4))) extern const TProgmemRGBGradientPalette_byte palette_purplefly_gp[] FL_PROGMEM = {
0, 0, 0, 0,
63, 239, 0,122,
191, 252,255, 78,
255, 0, 0, 0};

View File

@ -1,25 +0,0 @@
#include "effect_animation.h"
#include "functions.h"
AnimationEffect::AnimationEffect(const char* name, uint32_t bg, int x, int y) {
this->xOffset = x;
this->yOffset = y;
this->animation = new Animation(name, window);
this->animation->setBgColor(bg);
this->animation->setOffsets(this->xOffset, this->yOffset);
}
AnimationEffect* AnimationEffect::setFgColor(uint32_t c) {
animation->setFgColor(c);
return this;
}
AnimationEffect::~AnimationEffect() {
delete this->animation;
}
void AnimationEffect::loop() {
this->animation->drawFrame();
this->animation->advance();
}

View File

@ -1,23 +0,0 @@
#include "Effect.h"
#include "effect_big_clock.h"
#include "fonts.h"
#include "ntp.h"
void BigClockEffect::drawNumber(uint8_t number, int x, int y, CRGB color) {
char buffer[7];
sprintf(buffer, "%02d", number);
drawText(buffer, x, y, color);
}
void BigClockEffect::drawText(char *text, int x, int y, CRGB color) {
for (uint8_t i = 0; i < strlen(text); i++) {
window->drawChar(&font_numbers4x7, text[i], x + i * 4, y, &color);
}
}
void BigClockEffect::loop() {
window->clear();
drawNumber(ntpClient.getHours(), 0, 0, color_h);
drawNumber(ntpClient.getMinutes(), 8, 0, color_m);
drawNumber(ntpClient.getSeconds(), 8, 8, color_colon);
}

View File

@ -1,46 +0,0 @@
#include "effect_clock.h"
#include <FastLED.h>
#include "functions.h"
#include "fonts.h"
#include "ntp.h"
void ClockEffect::loop() {
loop(false, CRGB(0xFFFFFF), CRGB(0x000000));
}
void ClockEffect::loop(boolean invert, CRGB fg_color, CRGB bg_color) {
if (!invert) {
window->clear(&bg_color);
} else {
// Manually clear the needed parts
for(int i=0; i<window->width; i++) window->setPixel(i, 0, &bg_color);
for(int y=0; y<6; y++) {
window->setPixel(3, y, &bg_color);
if (y!=2 && y!=4) {
window->setPixel(7, y, &bg_color);
}
window->setPixel(8, y, &bg_color);
window->setPixel(12, y, &bg_color);
}
fg_color = bg_color;
}
if (ntpClient.isTimeSet()==false && (ntpClient.getSeconds() & 1)==0) {
window->clear(&bg_color);
return;
}
int h = ntpClient.getHours();
//void drawChar(Font f, uint8_t x, uint8_t y, const char c, CRGB* color, bool mask=false);
window->drawChar(&font_numbers3x5, 0, 1, '0' + (h / 10), &fg_color, invert);
window->drawChar(&font_numbers3x5, 4, 1, '0' + (h % 10), &fg_color, invert);
int m = ntpClient.getMinutes();
window->drawChar(&font_numbers3x5, 9, 1, '0' + (m / 10), &fg_color, invert);
window->drawChar(&font_numbers3x5, 13, 1, '0' + (m % 10), &fg_color, invert);
if (ntpClient.getSeconds() & 1) {
window->setPixel(7, 2, &fg_color);
window->setPixel(7, 4, &fg_color);
}
}
ClockEffect::~ClockEffect() {
delete window;
}

View File

@ -1,22 +0,0 @@
#include "effect_confetti.h"
#include "config.h"
#include "functions.h"
#include "prototypes.h"
void ConfettiEffect::loop() {
window->fadeToBlackBy(3);
for (int i=0; i<EFFECT_CONFETTI_PIXELS_PER_LOOP; i++) {
CRGB color = _getColor();
window->addPixelColor(random16(LED_COUNT), &color);
}
}
CRGB ConfettiEffect::_getColor() {
return CHSV(baseHue + random8(64), 255, 255);
}
CRGB RandomConfettiEffect::_getColor() {
return CHSV(random8(), 255, 255);
}
boolean ConfettiEffect::can_be_shown_with_clock() { return true; };

View File

@ -1,49 +0,0 @@
#include "effect_cycle.h"
#include "effects.h"
#include <ErriezCRC32.h>
CycleEffect::CycleEffect() {
changeEffect();
}
CycleEffect::~CycleEffect() {
delete effect;
}
void CycleEffect::changeEffect() {
int new_id;
if (EFFECT_CYCLE_RANDOM) {
do {
new_id = random8(cycle_effects_count);
} while (new_id == effect_id);
} else {
new_id = (effect_id + 1) % cycle_effects_count;
}
LOGln("CycleEffect * Changing effect from #%d to #%d", effect_id, new_id);
delay(25);
if (effect) delete effect;
LOGln("CycleEffect * Searching for new effect '%s'", cycle_effects[new_id]);
delay(25);
effect = select_effect( crc32String(cycle_effects[new_id]) );
effect_id = new_id;
effectSince = millis();
}
boolean CycleEffect::can_be_shown_with_clock() {
return effect->can_be_shown_with_clock();
};
boolean CycleEffect::clock_as_mask() {
return effect->clock_as_mask();
};
void CycleEffect::loop() {
if (!effect) changeEffect(); // If this is the first run, we have to select an effect first!
effect->loop();
// Don't use EVERY_N_SECONDS(config_effect_cycle_time) here because that function isn't relly made
// to be used with changing values.
EVERY_N_SECONDS(1) {
if (effectSince + EFFECT_CYCLE_TIME*1000 < millis()) {
changeEffect();
}
}
}

View File

@ -1,37 +0,0 @@
#include "effect_dvd.h"
#include "my_fastled.h"
void DvdEffect::loop() {
bool dir_changed = false;
EVERY_N_MILLISECONDS( 250 ) {
_x += _x_dir;
_y += _y_dir;
if (_x == 0 || _x + EFFECT_DVD_WIDTH >= window->width) {
_x_dir = -_x_dir;
dir_changed = true;
}
if (_y == 0 || _y + EFFECT_DVD_HEIGHT >= window->height) {
_y_dir = -_y_dir;
dir_changed = true;
}
}
window->clear();
for (int x=0; x<EFFECT_DVD_WIDTH; x++) for (int y=0; y<EFFECT_DVD_HEIGHT; y++) {
window->setPixel(_x + x, _y + y, (CRGB*)&_color);
}
if (dir_changed) _color = (CRGB)CHSV(random8(), 255, 255);
}
bool DvdEffect::can_be_shown_with_clock() { return true; }
DvdEffect::DvdEffect() {
_color = CHSV(random8(), 255, 255);
}
DvdEffect::~DvdEffect() {
delete window;
}

View File

@ -1,61 +0,0 @@
#include "effect_dynamic.h"
#include "functions.h"
#include "config.h"
SingleDynamicEffect::SingleDynamicEffect() {
init();
}
void SingleDynamicEffect::init() {
for (int i=0; i<tile_count; i++) tiles[i] = CHSV(baseHue + random8(64), 180, 255);
}
void SingleDynamicEffect::loop() {
EVERY_N_MILLISECONDS( EFFECT_SINGLE_DYNAMIC_LOOP_TIME ) {
tiles[random8(tile_count)] = CHSV(baseHue + random8(64), 180, 255);
}
this->draw();
}
void SingleDynamicEffect::draw() {
for (int x=0; x<window->width; x++) for (int y=0; y<window->height; y++) {
int index = y/2 * window->width/2 + x/2;
window->setPixel(x, y, &tiles[index]);
}
}
boolean SingleDynamicEffect::can_be_shown_with_clock() {
return true;
}
void MultiDynamicEffect::loop() {
EVERY_N_MILLISECONDS( EFFECT_MULTI_DYNAMIC_LOOP_TIME ) {
for (int i=0; i<tile_count; i++) tiles[i] = CHSV(baseHue + random8(64), 180, 255);
}
this->draw();
}
BigDynamicEffect::~BigDynamicEffect() {
delete window;
}
void BigDynamicEffect::loop() {
EVERY_N_MILLISECONDS( EFFECT_BIG_DYNAMIC_LOOP_TIME ) {
uint8_t x = random8(0, window->width - EFFECT_BIG_DYNAMIC_SIZE + 1);
uint8_t y = random8(0, window->height - EFFECT_BIG_DYNAMIC_SIZE + 1);
CRGB color = CHSV(random8(), 255, 255);
CRGB black(0x000000);
for (uint8_t ix=0; ix<EFFECT_BIG_DYNAMIC_SIZE; ix++) for (uint8_t iy=0; iy<EFFECT_BIG_DYNAMIC_SIZE; iy++) {
window->setPixel(x+ix, y+iy, &color);
}
/*for (uint8_t ix=0; ix<EFFECT_BIG_DYNAMIC_SIZE+2; ix++) {
window->setPixel(x-1+ix, y-1, &black);
window->setPixel(x-1+ix, y+EFFECT_BIG_DYNAMIC_SIZE+1, &black);
}
for (uint8_t iy=0; iy<EFFECT_BIG_DYNAMIC_SIZE+2; iy++) {
window->setPixel(x-1, y-1+iy, &black);
window->setPixel(x+EFFECT_BIG_DYNAMIC_SIZE+1, y-1+iy, &black);
}*/
}
}

View File

@ -1,13 +0,0 @@
#include "effect_firework.h"
#include "my_fastled.h"
#include "functions.h"
#include "config.h"
void FireworkEffect::loop() {
blur(EFFECT_FIREWORK_BLUR);
fadeToBlackBy(leds, LED_COUNT, EFFECT_FIREWORK_FADEOUT_SPEED);
if (random8(EFFECT_FIREWORK_SHOT_CHANCE)==0) {
leds[random16(LED_COUNT)] = CHSV(random8(), 255, 255);
}
}

View File

@ -1,172 +0,0 @@
#include "effect_matrix.h"
#include "my_color_palettes.h"
#include "functions.h"
MatrixEffectColumn::MatrixEffectColumn(Window* win, uint8_t dir, bool rand) {
window = win;
_direction = dir;
_random_direction = rand;
restart(true);
}
void MatrixEffectColumn::restart(bool completely_random) {
if (_random_direction) {
_direction = random8(4);
}
if (completely_random) {
x = random8(window->width);
y = random8(window->height);
} else {
switch(_direction) {
case DIR_NORTH:
x = random8(window->width);
y = window->height - 1;
break;
case DIR_EAST:
x = 0;
y = random8(window->height);
break;
case DIR_SOUTH:
x = random8(window->width);
y = 0;
break;
case DIR_WEST:
x = window->width - 1;
y = random8(window->height);
break;
}
}
length = random8(EFFECT_MATRIX_LENGTH_MIN, EFFECT_MATRIX_LENGTH_MAX);
running = true;
speed = random8(EFFECT_MATRIX_SPEED_MIN, EFFECT_MATRIX_SPEED_MAX);
}
void MatrixEffectColumn::advance() {
switch(_direction) {
case DIR_NORTH:
y--;
if (y > window->height && y + length > window->height) running=false;
break;
case DIR_EAST:
x++;
if (x - length > window->width) running=false;
break;
case DIR_SOUTH:
y++;
if (y - length > window->height) running=false;
break;
case DIR_WEST:
x--;
if (x > window->width && y + length > window->width) running=false;
break;
}
}
void MatrixEffectColumn::draw() {
int8_t xdir = 0;
int8_t ydir = 0;
switch (_direction) {
case DIR_NORTH: ydir = 1; break;
case DIR_EAST: xdir = 1; break;
case DIR_SOUTH: ydir = -1; break;
case DIR_WEST: xdir = -1; break;
}
for(int i=0; i<length; i++) {
CRGB color = _getColor(i);
window->raisePixel(x+(xdir*i), y+(ydir*i), &color);
}
}
void MatrixEffectColumn::loop() {
if (!running) {
if (random8() < 20) {
// Start the column again.
restart(false);
}
} else {
if (millis() - last_move > speed) {
advance();
last_move = millis();
}
draw();
}
}
CRGB MatrixEffectColumn::_getColor(uint8_t i) {
CRGB color;
if (i==0) {
color = CRGB(255, 255, 255);
} else {
color = CHSV(83, 255, 255 * (length - i) / length);
}
return color;
}
CRGB RainbowMatrixEffectColumn::_getColor(uint8_t i) {
CRGB color;
if (i==0) {
color = CRGB(255, 255, 255);
} else {
color = CHSV(255 * x / window->width, 255, 255 * (length - i) / length);
}
return color;
}
CRGB RandomMatrixEffectColumn::_getColor(uint8_t i) {
CRGB color;
//Serial.print("RandomMatrixEffectColumn::_getColor, hue="); Serial.println(_hue);
if (i==0) {
color = CRGB(255, 255, 255);
} else {
color = CHSV(_hue, 255, 255 * (length - i) / length);
}
return color;
}
void RandomMatrixEffectColumn::restart(bool completely_random) {
MatrixEffectColumn::restart(completely_random);
_hue = random8();
}
boolean MatrixEffect::can_be_shown_with_clock() { return true; };
MatrixEffect::MatrixEffect() {
_columns = new MatrixEffectColumn* [window->width];
_init();
}
void MatrixEffect::_init() {
for (int i=0; i<window->width; i++) _columns[i] = new MatrixEffectColumn(window, MatrixEffectColumn::DIR_SOUTH);
}
void RandomMatrixEffect::_init() {
for (int i=0; i<window->width; i++) _columns[i] = new RandomMatrixEffectColumn(window, random8(4), true);
}
void RainbowMatrixEffect::_init() {
for (int i=0; i<window->width; i++) _columns[i] = new RainbowMatrixEffectColumn(window, MatrixEffectColumn::DIR_SOUTH);
}
MatrixEffect::~MatrixEffect() {
for (int i=0; i<window->width; i++) {
delete _columns[i];
}
delete[] _columns;
}
void MatrixEffect::loop() {
window->clear();
for (int i=0; i<window->width; i++) _columns[i]->loop();
}

View File

@ -1,64 +0,0 @@
#include "effect_snake.h"
#include "functions.h"
SnakeEffect::SnakeEffect() {
this->coords = {0, 0};
this->window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
}
SnakeEffect::~SnakeEffect() {
delete window;
}
void SnakeEffect::loop() {
if (run++ % EFFECT_SNAKE_SLOWDOWN == 0) { // Change the coordinates only on every n-th run.
if (random8(EFFECT_SNAKE_DIRECTION_CHANGE)==0 || is_turn_needed()) turn_random();
this->coords = update_position(this->coords, this->direction);
}
window->fadeToBlackBy(2);
CRGB color(CHSV(hue, 200, 255));
window->setPixel(this->coords.x, this->coords.y, &color);
hue++;
}
void SnakeEffect::turn_random() {
if ((random8() & 1) == 0) {
turn_right() || turn_left();
} else {
turn_left() || turn_right();
}
}
bool SnakeEffect::turn_left() {
if (!is_direction_okay(this->direction - 1)) return false;
this->direction--;
return true;
}
bool SnakeEffect::turn_right() {
if (!is_direction_okay(this->direction + 1)) return false;
this->direction++;
return true;
}
bool SnakeEffect::is_turn_needed() {
return !is_direction_okay(this->direction);
}
bool SnakeEffect::is_direction_okay(uint8_t dir) {
Coords c = update_position(this->coords, dir);
return c.x<window->width && c.y<window->height;
}
Coords SnakeEffect::update_position(Coords original, uint8_t direction) {
direction = direction % 4;
if (direction == 0) original.y--;
else if (direction == 1) original.x++;
else if (direction == 2) original.y++;
else if (direction == 3) original.x--;
return original;
}
boolean SnakeEffect::can_be_shown_with_clock() { return true; }

View File

@ -1,12 +0,0 @@
#include "effect_twirl.h"
#include "functions.h"
void TwirlEffect::loop() {
for (int x=0; x<window->width; x++) for (int y=0; y<window->height; y++) {
uint8_t angle = (x==center_x && y==center_y) ? 0 : atan2(y - center_y, x - center_x) / M_PI * 128 + 128 + angleOffset;
uint8_t brightness = sqrt16((center_x - x) * (center_x - x) + (center_y - y) * (center_y - y)) & 0xFF;
CRGB color(CHSV(angle, (brightness<<5) & 0xFF, 255));
window->setPixel(x, y, &color);
}
angleOffset += 1;
}

View File

@ -1,58 +1,98 @@
#include "effects.h" #include "effects.h"
#include "config.h"
#include "my_fastled.h" #include "my_fastled.h"
#include <ErriezCRC32.h> #include "effects/bell.h"
#include "effect_bell.h" #include "effects/sinematrix3.h"
#include "effect_sinematrix3.h" #include "effects/big_clock.h"
#include "effect_big_clock.h" #include "effects/clock.h"
#include "effect_clock.h" #include "effects/static.h"
#include "effect_static.h" #include "effects/animation.h"
#include "effect_animation.h" #include "effects/dynamic.h"
#include "effect_dynamic.h" #include "effects/matrix.h"
#include "effect_matrix.h" #include "effects/twirl.h"
#include "effect_twirl.h" #include "effects/cycle.h"
#include "effect_cycle.h" #include "effects/snake.h"
#include "effect_confetti.h" #include "effects/fire.h"
#include "effect_snake.h" #include "effects/firework.h"
#include "effect_fire.h" #include "effects/gol.h"
#include "effect_firework.h" #include "effects/pixelclock.h"
#include "effect_gol.h" #include "effects/dvd.h"
#include "effect_pixelclock.h" #include "effects/analogclock.h"
#include "effect_dvd.h" #include "effects/sines.h"
#include "effects/marquee.h"
#include "effects/blur2d.h"
#include "effects/tv_static.h"
#include "effects/lightspeed.h"
#include "effects/diamond.h"
#include "effects/tpm2_net.h"
#include "SimpleEffect.h"
#include "effects/night_clock.h"
Effect* current_effect; Effect* current_effect;
ClockEffect effect_clock; ClockEffect effect_clock;
TimerEffect effect_timer;
Effect* select_effect(uint32_t code) { // We're using 0 instead of false to get a better visual difference between true and false.
switch (code) { EffectEntry effects[] = {
// use e.g. https://crccalc.com/ for the conversion of name to crc. /* 0 */ {"sinematrix3", true, [](){ return new Sinematrix3Effect(); }},
case 0: case 0xD682E3C8 /* sinematrix3 */ : return new Sinematrix3Effect(); /* 1 */ {"big_clock", true, [](){ return new BigClockEffect(); }},
case 1: case 0x90A887DA /* big_clock */ : return new BigClockEffect(); /* 2 */ {"clock", 0, [](){ return new ClockEffect(); }},
case 2: case 0xBE7BBE92 /* clock */ : return new ClockEffect(); /* 3 */ {"bell", 0, [](){ return AnimationEffect::Blinker("/bell.pia", 300, 0xFFFF00); }},
case 3: case 0x733BE087 /* bell */ : return new BellEffect(); //(new AnimationEffect("/bell.pia", 0x000000, 0, 0))->setFgColor(0xFFFF00); /* 4 */ {"off", 0, [](){ return new StaticEffect(0x000000); }},
case 4: case 0x2BBC5D43 /* off */ : return new StaticEffect(0x000000); /* 5 */ {"single_dynamic", true, [](){ return new SingleDynamicEffect(); }},
case 5: case 0x1D84F231 /* koopa */ : return new AnimationEffect("/koopa.pia", CRGB(0x000000), 0, 0); /* 6 */ {"multi_dynamic", true, [](){ return new MultiDynamicEffect(); }},
case 6: case 0xAC43BCF1 /* couple_rain */ : return new AnimationEffect("/couple_rain.pia", CRGB(0x000000), -8, -16); /* 7 */ {"big_dynamic", true, [](){ return new BigDynamicEffect(); }},
case 7: case 0xF1B117F7 /* single_dynamic */ : return new SingleDynamicEffect(); /* 8 */ {"matrix", true, [](){ return new MatrixEffect(); }},
case 8: case 0xF52F2804 /* multi_dynamic */ : return new MultiDynamicEffect(); /* 9 */ {"random_matrix", true, [](){ return new RandomMatrixEffect(); }},
case 9: case 0xF83341CF /* matrix */ : return new MatrixEffect(); /* 10 */ {"rainbow_matrix", true, [](){ return new RainbowMatrixEffect(); }},
case 10: case 0xD2B79DD0 /* rainbow_matrix */ : return new RainbowMatrixEffect(); /* 11 */ {"cycle", 0, [](){ return new CycleEffect(); }},
case 11: case 0xE8DD3433 /* random_matrix */ : return new RandomMatrixEffect(); /* 12 */ {"twirl", true, [](){ return new TwirlEffect(); }},
case 12: case 0xB086D193 /* cycle */ : return new CycleEffect(); /* 13 */ SIMPLE_EFFECT("confetti", true, SE_CYCLE_COLORS | SE_FADEOUT, {return random8()>252?1:0;}),
case 13: case 0x2293EF9F /* twirl */ : return new TwirlEffect(); /* 14 */ SIMPLE_EFFECT("rainbow_confetti", true, SE_RANDOM_PIXEL_COLORS | SE_FADEOUT, {return random8()>252?1:0;}),
case 14: case 0x60ECC3E6 /* heart */ : return new AnimationEffect("/heart.pia", CRGB(0x000000), 0, 0); /* 15 */ {"snake", true, [](){ return new SnakeEffect(); }},
case 15: case 0x42090A49 /* confetti */ : return new ConfettiEffect(); /* 16 */ {"firework", true, [](){ return new FireworkEffect(); }},
case 16: case 0x516D6B9E /* snake */ : return new SnakeEffect(); /* 17 */ {"gol", true, [](){ return new GolEffect(); }},
case 17: case 0x58DE09CF /* fire */ : return new FireEffect(); /* 18 */ {"pixel_clock", 0, [](){ return new PixelClockEffect(); }},
case 18: case 0x08BA9C08 /* firework */ : return new FireworkEffect(); /* 19 */ {"dvd", true, [](){ return new DvdEffect(); }},
case 19: case 0x14B85EAC /* gol */ : return new GolEffect(); /* 20 */ {"analog_clock", 0, [](){ return new AnalogClockEffect(); }},
case 20: case 0xFA13015D /* cake */ : return new AnimationEffect("/cake.pia", CRGB(0x000000), 0, 0); /* 21 */ {"sines", true, [](){ return new SinesEffect(); }},
case 21: case 0xA2B0D68B /* pixel_clock */ : return new PixelClockEffect(); /* 22 */ {"blur2d", true, [](){ return new Blur2DEffect(); }},
case 22: case 0x2C0E6962 /* big_dynamic */ : return new BigDynamicEffect(); /* 23 */ {"marquee", 0, [](){ return new MarqueeEffect(); }},
case 23: case 0xDA6F31A5 /* random_confetti */ : return new RandomConfettiEffect(); /* 24 */ {"night_clock", 0, [](){ return new NightClockEffect(); }},
case 24: case 0x8325C1DF /* dvd */ : return new DvdEffect(); /* 25 */ {"tv_static", 0, [](){ return new TvStaticEffect(); }},
default : return NULL; /* 26 */ {"sinematrix3_rainbow", true, [](){ return new Sinematrix3Effect(SINEMATRIX_COLOR_RAINBOW); }},
}; /* 27 */ {"sinematrix3_purplefly", true, [](){ return new Sinematrix3Effect(SINEMATRIX_COLOR_PURPLEFLY); }},
/* 28 */ {"lightspeed", true, [](){ return new LightspeedEffect(); }},
/* 29 */ {"koopa", 0, [](){ return new AnimationEffect("/koopa.pia"); }},
/* 30 */ {"cake", 0, [](){ return new AnimationEffect("/cake.pia"); }},
/* 31 */ {"child", 0, [](){ return AnimationEffect::Blinker("/child.pia", 300, 0xFFFF00); }},
/* 32 */ {"diamond", true, [](){ return new DiamondEffect(); }},
/* 33 */ {"tpm2.net", 0, [](){ return new Tpm2NetEffect(); }},
/* 34 */ SIMPLE_EFFECT("slow_blinking", true, SE_CYCLE_COLORS, {return sin(t + (x+1)*(y+1)*i);} ),
/* 35 */ SIMPLE_EFFECT("upwave", true, SE_CYCLE_COLORS, {return (cos(t+y/2));} ),
/* 36 */ SIMPLE_EFFECT("centerwave", true, SE_CYCLE_COLORS, {return sin(t*2 - sqrt((x-4)*(x-4) + (y-7)*(y-7)));} ),
/* 37 */ SIMPLE_EFFECT("sineline", true, SE_RANDOM_STATIC_COLOR, {return sin(x/2)-sin(x-t)-y+6;} ),
/* 38 */ SIMPLE_EFFECT("barbershop", true, SE_RANDOM_STATIC_COLOR, {return 1*cos(0.8*i-t*5);} ),
/* 39 */ SIMPLE_EFFECT("zigzag", true, SE_CYCLE_COLORS, { return cos(cos(x+y)-y*cos(t/8+x/16));} ),
};
const uint8_t effects_size = 40;
Effect* select_effect(const char* name) {
for(int i=0; i<effects_size; i++) {
if (strcmp(effects[i].name, name)==0) {
return effects[i].create();
}
}
return NULL;
}
Effect* select_effect(uint8_t id) {
if (id < effects_size) {
return effects[id].create();
}
return NULL;
} }
bool change_current_effect(String payload) { bool change_current_effect(String payload) {
@ -65,11 +105,12 @@ bool change_current_effect(String payload) {
LOGln("Effects * Cleaned effect name: %s", payload.c_str()); LOGln("Effects * Cleaned effect name: %s", payload.c_str());
} }
Effect* new_effect = select_effect( crc32String(payload.c_str()) ); Effect* new_effect = select_effect( payload.c_str() );
if (new_effect == NULL) { if (new_effect == NULL) {
LOGln("Effects * Could not find effect with name %s", payload.c_str()); LOGln("Effects * Could not find effect with name %s", payload.c_str());
return false; return false;
} }
LOGln("Effects * Switching to effect %s", payload.c_str());
delete current_effect; delete current_effect;
current_effect = new_effect; current_effect = new_effect;
@ -91,9 +132,6 @@ bool change_current_effect(String payload) {
return true; return true;
} }
const char* cycle_effects[] = {"sinematrix3", "multi_dynamic", "matrix", "confetti", "single_dynamic", "snake", "gol"};
uint8_t cycle_effects_count = 7;
void setup_effects() { void setup_effects() {
current_effect = new CycleEffect(); current_effect = new CycleEffect();
} }

View File

@ -0,0 +1,33 @@
#include "effects/analogclock.h"
#include "my_fastled.h"
#include "ntp.h"
#include <time.h>
void AnalogClockEffect::loop(uint16_t ms) {
window->clear();
CRGB white(0xFFFFFF);
CRGB red(0xFF0000);
window->circle(8, 8, 7, &white);
time_t now;
tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
uint16_t seconds = timeinfo.tm_sec * 1000 + (millis()%1000);
uint16_t angle = seconds * 0x10000 / 60000;
window->lineWithAngle(8, 8, angle, 12, &red);
//window->line(0<<8, 0<<8, 7<<8, 7<<8, &white);
//window->line(15<<8, 0<<8, 8<<8, 7<<8, &red);
//window->line(0<<8, 15<<8, 7<<8, 8<<8, &blue);
//window->line(15<<8, 15<<8, 8<<8, 8<<8, &green);
/*for (uint8_t i=0; i<=12; i++) {
window->lineWithAngle(8, 8, 255/12*i, 5, 2, &white);
}
uint8_t minutes = ntpClient.getMinutes();
uint8_t hours = ntpClient.getHours();
window->lineWithAngle(8, 8, 255/60*minutes, 6, &white);
window->lineWithAngle(8, 8, 255/12*(hours % 12), 4, &white);*/
}

53
src/effects/animation.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "effects/animation.h"
#include "functions.h"
AnimationEffect::AnimationEffect(const char* name, uint32_t bg, int x, int y) {
this->name = name;
this->xOffset = x;
this->yOffset = y;
this->animation = new Animation(name, window);
this->animation->setBgColor(bg);
this->animation->setOffsets(this->xOffset, this->yOffset);
_last_blink_at = millis();
}
AnimationEffect* AnimationEffect::setFgColor(uint32_t c) {
animation->setFgColor(c);
return this;
}
AnimationEffect* AnimationEffect::setBlinkFrequency(uint16_t ms) {
_blink_freq = ms;
return this;
}
AnimationEffect::~AnimationEffect() {
delete this->animation;
}
void AnimationEffect::loop(uint16_t ms) {
if (_blink_freq > 0) {
unsigned long mil = millis();
if (mil < _last_blink_at || _last_blink_at + _blink_freq <= mil) {
this->animation->invert();
_last_blink_at = mil;
}
}
this->animation->drawFrame();
this->animation->advance();
}
String AnimationEffect::get_name() {
String s = "animation/";
s += this->name;
return s;
}
AnimationEffect* AnimationEffect::Blinker(const char* name, uint16_t interval, uint32_t color, uint32_t background_color) {
AnimationEffect* anim = new AnimationEffect(name, background_color);
anim->setFgColor(color);
anim->setBlinkFrequency(interval);
return anim;
}

View File

@ -1,9 +1,9 @@
#include "Effect.h" #include "Effect.h"
#include "functions.h" #include "functions.h"
#include "effect_bell.h" #include "effects/bell.h"
#include "sprites.h" #include "sprites.h"
void BellEffect::loop() { void BellEffect::loop(uint16_t ms) {
Serial.println("This is Bell.loop()"); Serial.println("This is Bell.loop()");
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int x = 0; x < 2; x++) { for (int x = 0; x < 2; x++) {

74
src/effects/big_clock.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "Effect.h"
#include "effects/big_clock.h"
#include "fonts.h"
#include <time.h>
#include "settings.h"
void BigClockEffect::loop(uint16_t ms) {
window->clear();
time_t now;
tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
uint8_t h = timeinfo.tm_hour;
CRGB color = _get_color_font();
window->drawChar(&font_numbers3x5_blocky, 6<<8, 2<<8, '0' + (h / 10), &color);
window->drawChar(&font_numbers3x5_blocky, 11<<8, 2<<8, '0' + (h % 10), &color);
uint8_t m = timeinfo.tm_min;
window->drawChar(&font_numbers3x5_blocky, 6<<8, 9<<8, '0' + (m / 10), &color);
window->drawChar(&font_numbers3x5_blocky, 11<<8, 9<<8, '0' + (m % 10), &color);
uint8_t s = timeinfo.tm_sec;
_draw_colon(s & 1);
_draw_seconds(timeinfo.tm_sec);
}
void BigClockEffect::_draw_colon(bool odd) {
if (odd) {
CRGB color = _get_color_font();
window->setPixel(3, 10, &color);
window->setPixel(3, 12, &color);
}
}
void BigClockEffect::_draw_seconds(uint8_t seconds) {
for (int i=1; i<=seconds; i++) {
_draw_border_pixel(i<<8, (i%5==0) ? &_color_seconds_light : &_color_seconds_dark);
}
/*timeval tv;
gettimeofday(&tv, nullptr);
uint16_t mil = (tv.tv_usec / 1000) % 1000;
accum88 pos = (seconds<<8) + ((settings.effects.big_clock.spacing-1)<<8) * (1000 - mil) / 1000 + (1<<8);
uint8_t sec = seconds + 1;
while (pos < (60<<8)) {
_draw_border_pixel(pos, sec%5==0 ? &_color_seconds_moving_light : &_color_seconds_moving_dark);
pos += settings.effects.big_clock.spacing<<8;
sec++;
}*/
}
void BigClockEffect::_draw_border_pixel(accum88 i, CRGB* color) {
accum88 x, y;
if (i<(8<<8)) {
x = i + (7<<8);
y = 0;
} else if (i<(23<<8)) {
x = 15<<8;
y = i - (8<<8);
} else if (i<(38<<8)) {
x = (38<<8) - i;
y = 15<<8;
} else if (i<(53<<8)) {
x = 0;
y = (53<<8) - i;
} else if (i<=(60<<8)) {
x = i - (53<<8);
y = 0;
} else {
return;
}
window->setSubPixel(x, y, color);
}

48
src/effects/blur2d.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "effects/blur2d.h"
Blur2DBlob::Blur2DBlob() {
_x_freq = random16(6<<8, 15<<8);
_y_freq = random16(6<<8, 15<<8);
_color_freq = random8(25, 80);
}
void Blur2DBlob::render(Window* window) {
uint8_t x = beatsin16(_x_freq, 0, window->width-1);
uint8_t y = beatsin16(_y_freq, 0, window->height-1);
CRGB c = CHSV(millis() / _color_freq, 200, 255);
window->addPixelColor(x, y, &c);
}
boolean Blur2DEffect::can_be_shown_with_clock() {
return true;
}
void Blur2DEffect::loop(uint16_t ms) {
if (_count != settings.effects.blur2d.count) {
_delete();
_init();
}
uint8_t blur_amount = dim8_raw(beatsin8(3, 128, 224));
window->blur(blur_amount);
for (int i=0; i<_count; i++) {
_blobs[i].render(window);
}
}
Blur2DEffect::Blur2DEffect() {
_init();
}
void Blur2DEffect::_init() {
_count = settings.effects.blur2d.count;
_blobs = new Blur2DBlob[_count];
}
void Blur2DEffect::_delete() {
delete[] _blobs;
}
Blur2DEffect::~Blur2DEffect() {
_delete();
}

55
src/effects/clock.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "effects/clock.h"
#include <FastLED.h>
#include "functions.h"
#include "fonts.h"
#include "ntp.h"
void ClockEffect::loop(uint16_t ms) {
loop_with_invert(false);
}
void ClockEffect::loop_with_invert(bool invert) {
// Clear the first line to have a bit of space to the other stuff on screen
CRGB bg_color(0x000000);
for(int i=0; i<window->width; i++) window->setPixel(i, 0, &bg_color);
loop(invert, CRGB(0xFFFFFF), bg_color, 1);
}
void ClockEffect::loop(boolean invert, CRGB fg_color, CRGB bg_color, uint8_t yPos) {
if (!invert) {
window->clear(&bg_color);
} else {
// Manually clear the needed parts
for(int y=0; y<6; y++) {
window->setPixel(3, yPos+y, &bg_color);
if (y!=1 && y!=3) {
window->setPixel(7, yPos+y, &bg_color);
}
window->setPixel(8, yPos+y, &bg_color);
window->setPixel(12, yPos+y, &bg_color);
}
fg_color = bg_color;
}
/*if (ntpClient.isTimeSet()==false && (ntpClient.getSeconds() & 1)==0) {
window->clear(&bg_color);
return;
}*/
time_t now;
tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
int h = timeinfo.tm_hour;
//void drawChar(Font f, uint8_t x, uint8_t y, const char c, CRGB* color, bool mask=false);
window->drawChar(&font_numbers3x5, 0<<8, yPos<<8, '0' + (h / 10), &fg_color, invert);
window->drawChar(&font_numbers3x5, 4<<8, yPos<<8, '0' + (h % 10), &fg_color, invert);
int m = timeinfo.tm_min;
window->drawChar(&font_numbers3x5, 9<<8, yPos<<8, '0' + (m / 10), &fg_color, invert);
window->drawChar(&font_numbers3x5, 13<<8, yPos<<8, '0' + (m % 10), &fg_color, invert);
if (timeinfo.tm_sec & 1) {
window->setPixel(7, yPos+1, &fg_color);
window->setPixel(7, yPos+3, &fg_color);
}
}
ClockEffect::~ClockEffect() {
}

100
src/effects/cycle.cpp Normal file
View File

@ -0,0 +1,100 @@
#include "effects/cycle.h"
#include "effects.h"
CycleEffect::CycleEffect() {
_effects_count = 0;
for (uint8_t i=0; i<effects_size; i++) {
if (effects[i].use_in_cycle) _effects_count++;
}
LOGln("Cycle * Found %d effects to use in cycle.", _effects_count);
changeEffect();
}
CycleEffect::~CycleEffect() {
delete effect;
}
void CycleEffect::changeEffect() {
uint8_t new_id;
if (settings.effects.cycle.random && _effects_count>1) {
do {
new_id = random8(_effects_count);
} while (new_id == effect_id);
} else {
new_id = (effect_id + 1) % _effects_count;
}
LOGln("CycleEffect * Changing effect from #%d to #%d", effect_id, new_id);
delay(25);
String old_effect_name = String("UNKNOWN");
if (effect) {
old_effect_name = effect->get_name();
delete effect;
}
int16_t diff = 0;
uint16_t old_heap = _heap_free;
_heap_free = ESP.getFreeHeap();
if (old_heap) {
// diff positive = More heap used (baad)
// diff negative = Less heap used (good-ish)
diff = old_heap - _heap_free;
LOGln("CycleEffect * Heap usage: #%d,%s,%d,%+d", effect_id, old_effect_name.c_str(), _heap_free, diff);
}
delay(25);
LOGln("CycleEffect * Searching for new effect #%d", new_id);
uint8_t count = 0;
EffectEntry* e = nullptr;
for (uint8_t i=0; i<effects_size; i++) {
if (effects[i].use_in_cycle) {
if (count == new_id) {
e = &effects[i];
effect = e->create();
break;
}
count++;
}
}
if (e) {
#ifdef MQTT_REPORT_METRICS
e->heap_change_sum += diff;
e->run_count++;
LOGln("CycleEffect * Last effect stats: name:%s, runs:%d, total_change:%d", old_effect_name.c_str(), e->run_count, e->heap_change_sum);
String topic = "metrics/effects/";
topic.concat(old_effect_name);
String message = String("runs:") + e->run_count + ", total_heap_change:" + e->heap_change_sum;
mqtt_publish(topic.c_str(), message.c_str(), true);
#endif
effect_id = new_id;
effectSince = millis();
LOGln("CycleEffect * Effect %s found", effect->get_name().c_str());
} else {
LOGln("CycleEffect * Effect NOT found");
}
}
boolean CycleEffect::can_be_shown_with_clock() {
return effect->can_be_shown_with_clock();
};
boolean CycleEffect::clock_as_mask() {
return effect->clock_as_mask();
};
void CycleEffect::loop(uint16_t ms) {
if (!effect) changeEffect(); // If this is the first run, we have to select an effect first!
effect->loop(ms);
// Don't use EVERY_N_SECONDS(config_effect_cycle_time) here because that function isn't relly made
// to be used with changing values.
EVERY_N_SECONDS(1) {
if (effectSince + settings.effects.cycle.time*1000 < millis()) {
changeEffect();
}
}
}
String CycleEffect::get_name() {
String s = "cycle/";
s += effect->get_name();
return s;
}

14
src/effects/diamond.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "effects/diamond.h"
#include "my_fastled.h"
void DiamondEffect::loop(uint16_t ms) {
for (int x=0; x<window->width; x++) {
for (int y=0; y<window->height; y++) {
uint8_t distance = abs(window->height/2 - y) + abs(window->width/2 - x);
CRGB col = CHSV(distance*8 - (millis()>>5)%255, 255, 255);
window->setPixel(x, y, &col);
}
}
}
bool DiamondEffect::can_be_shown_with_clock() { return true; }

50
src/effects/dvd.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "effects/dvd.h"
#include "my_fastled.h"
void DvdEffect::loop(uint16_t ms) {
bool dir_changed = false;
_x += _x_dir * settings.effects.dvd.speed;
_y += _y_dir * settings.effects.dvd.speed;
if (_x <= 0) {
_x = -_x;
_x_dir = -_x_dir;
dir_changed = true;
//LOGln("speed: %d", settings.effects.dvd.speed);
} else if (_x + (settings.effects.dvd.width << 8) >= (window->width << 8)) {
_x -= 2*settings.effects.dvd.speed;
_x_dir = -_x_dir;
dir_changed = true;
}
if (_y <= 0) {
_y = -_y;
_y_dir = -_y_dir;
dir_changed = true;
} else if (_y + (settings.effects.dvd.height << 8) >= (window->height << 8)) {
_y -= 2*settings.effects.dvd.speed;
_y_dir = -_y_dir;
dir_changed = true;
}
window->clear();
if (dir_changed) _color = (CRGB)CHSV(random8(), 255, 255);
for (int x=0; x<settings.effects.dvd.width; x++) for (int y=0; y<settings.effects.dvd.height; y++) {
window->setSubPixel(_x + (x<<8), _y + (y<<8), (CRGB*)&_color);
}
for (int x=1; x<settings.effects.dvd.width; x++) for (int y=1; y<settings.effects.dvd.height; y++) {
window->setPixel((_x>>8) + x, (_y>>8) + y, (CRGB*)&_color);
}
}
bool DvdEffect::can_be_shown_with_clock() { return true; }
DvdEffect::DvdEffect() {
_color = CHSV(random8(), 255, 255);
}
DvdEffect::~DvdEffect() {
}

56
src/effects/dynamic.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "effects/dynamic.h"
#include "functions.h"
#include "config.h"
SingleDynamicEffect::SingleDynamicEffect() {
init();
}
void SingleDynamicEffect::init() {
for (int i=0; i<tile_count; i++) tiles[i] = CHSV(baseHue + random8(64), 180, 255);
}
void SingleDynamicEffect::loop(uint16_t ms) {
EVERY_N_MILLISECONDS( settings.effects.dynamic.single_loop_time ) {
tiles[random8(tile_count)] = CHSV(baseHue + random8(64), 180, 255);
}
this->draw();
}
void SingleDynamicEffect::draw() {
for (int x=0; x<window->width; x++) for (int y=0; y<window->height; y++) {
int index = y/2 * window->width/2 + x/2;
window->setPixel(x, y, &tiles[index]);
}
}
boolean SingleDynamicEffect::can_be_shown_with_clock() {
return true;
}
void MultiDynamicEffect::loop(uint16_t ms) {
EVERY_N_MILLISECONDS( settings.effects.dynamic.multi_loop_time ) {
for (int i=0; i<tile_count; i++) tiles[i] = CHSV(baseHue + random8(64), 180, 255);
}
this->draw();
}
BigDynamicEffect::~BigDynamicEffect() {
}
void BigDynamicEffect::loop(uint16_t ms) {
EVERY_N_MILLISECONDS( settings.effects.dynamic.big_loop_time ) {
uint8_t x = random8(0, window->width - settings.effects.dynamic.big_size + 1);
uint8_t y = random8(0, window->height - settings.effects.dynamic.big_size + 1);
CRGB color = CHSV(random8(), 255, 255);
CRGB black(0x000000);
for (uint8_t ix=0; ix<settings.effects.dynamic.big_size; ix++) for (uint8_t iy=0; iy<settings.effects.dynamic.big_size; iy++) {
window->setPixel(x+ix, y+iy, &color);
}
}
}
boolean BigDynamicEffect::can_be_shown_with_clock() {
return true;
}

View File

@ -1,4 +1,4 @@
#include "effect_fire.h" #include "effects/fire.h"
#include "my_color_palettes.h" #include "my_color_palettes.h"
#include "config.h" #include "config.h"
#include "my_fastled.h" #include "my_fastled.h"
@ -14,7 +14,7 @@ FireEffect::~FireEffect() {
delete [] this->data; delete [] this->data;
} }
void FireEffect::loop() { void FireEffect::loop(uint16_t ms) {
cooldown(); cooldown();
spark(); spark();
propagate(); propagate();
@ -22,11 +22,11 @@ void FireEffect::loop() {
} }
void FireEffect::cooldown() { void FireEffect::cooldown() {
for(int i=0; i<(this->window->width * this->window->height); i++) this->data[i] = scale8(this->data[i], EFFECT_FIRE_COOLDOWN); // 240 or something for(int i=0; i<(this->window->width * this->window->height); i++) this->data[i] = scale8(this->data[i], settings.effects.fire.cooldown);
} }
void FireEffect::spark() { void FireEffect::spark() {
for(int x=0; x<this->window->width; x++) if (random8(EFFECT_FIRE_SPARK_CHANCE)==0) this->data[x] = this->spark_temp(); for(int x=0; x<this->window->width; x++) if (random8(settings.effects.fire.spark_chance)==0) this->data[x] = this->spark_temp();
} }
inline uint8_t FireEffect::spark_temp() { inline uint8_t FireEffect::spark_temp() {

183
src/effects/firework.cpp Normal file
View File

@ -0,0 +1,183 @@
// Based on https://gist.github.com/kriegsman/68929cbd1d6de4535b20
#include "effects/firework.h"
FireworkEffectDot::FireworkEffectDot(Window* w, FireworkEffect* e) {
_window = w;
_main = e;
show = 0;
type = FIREWORK_DOT_NONE;
_x = 0;
_y = _window->height - 1;
_xv = 0;
_yv = 0;
_r = 0;
_color.setRGB(0, 0, 0);
}
void FireworkEffectDot::_screenscale(accum88 a, byte n, byte& screen, byte& screenerr) {
byte ia = a >> 8;
screen = scale8(ia, n);
byte m = screen * (256 / n);
screenerr = (ia - m) * scale8(255, n);
}
int16_t FireworkEffectDot::_scale15by8_local(int16_t i, fract8 scale) {
int16_t result;
result = (int32_t)((int32_t) i*scale)/256;
return result;
}
void FireworkEffectDot::draw() {
if (!show) return;
byte ix, xe, xc;
byte iy, ye, yc;
_screenscale(_x, _window->width, ix, xe);
_screenscale(_y, _window->height, iy, ye);
xc = 255 - xe;
yc = 255 - ye;
CRGB c00 = CRGB(dim8_video( scale8( scale8( _color.r, yc), xc)),
dim8_video( scale8( scale8( _color.g, yc), xc)),
dim8_video( scale8( scale8( _color.b, yc), xc)));
CRGB c01 = CRGB(dim8_video( scale8( scale8( _color.r, ye), xc)),
dim8_video( scale8( scale8( _color.g, ye), xc)),
dim8_video( scale8( scale8( _color.b, ye), xc)));
CRGB c10 = CRGB(dim8_video( scale8( scale8( _color.r, yc), xe)),
dim8_video( scale8( scale8( _color.g, yc), xe)),
dim8_video( scale8( scale8( _color.b, yc), xe)));
CRGB c11 = CRGB(dim8_video( scale8( scale8( _color.r, ye), xe)),
dim8_video( scale8( scale8( _color.g, ye), xe)),
dim8_video( scale8( scale8( _color.b, ye), xe)));
_window->addPixelColor(ix, iy, &c00);
_window->addPixelColor(ix, iy-1, &c01);
_window->addPixelColor(ix+1, iy, &c10);
_window->addPixelColor(ix+1, iy-1, &c11);
}
void FireworkEffectDot::move() {
if (!show) return;
_yv -= settings.effects.firework.gravity;
_xv = _scale15by8_local(_xv, settings.effects.firework.drag);
_yv = _scale15by8_local(_yv, settings.effects.firework.drag);
if (type == FIREWORK_DOT_SPARK) {
_xv = _scale15by8_local(_xv, settings.effects.firework.drag);
_yv = _scale15by8_local(_yv, settings.effects.firework.drag);
_color.nscale8(255);
if (!_color) {
show = 0;
}
}
// Bounce if we hit the ground
if (_yv < 0 && _y - _window->height < (-_yv)) {
if (type == FIREWORK_DOT_SPARK) {
show = 0;
} else {
_yv = -_yv;
_yv = _scale15by8_local(_yv, settings.effects.firework.bounce);
if (_yv < 500) {
show = 0;
}
}
}
if (_yv < 300) {
if (type == FIREWORK_DOT_SHELL) {
if (_y < (uint16_t)0x8000) {
// boom
CRGB white(0xFFFFFF);
_window->clear(&white);
}
show = 0;
_main->skyburst(_x, _y, _xv, _yv, _color);
}
}
if (type == FIREWORK_DOT_SPARK) {
if ((_xv > 0 && _x>_xv) || (_xv < 0 && _x<(0xFFFF+_xv))) {
_x += _xv;
} else {
show = 0;
}
} else {
_x += _xv;
}
_y -= _yv;
}
void FireworkEffectDot::ground_launch() {
_xv = (int16_t)random16(600) - (int16_t)300;
_yv = 600 + random16(300 + (25 * _window->height));
_x = 0x8000;
_y = 0;
hsv2rgb_rainbow(CHSV(random8(), 240, 200), _color);
show = 1;
}
void FireworkEffectDot::sky_burst(accum88 basex, accum88 basey, saccum78 basedv, CRGB& basecolor) {
_xv = basedv + (int16_t)random16(2000) - (int16_t)1000;
_yv = (int16_t)random16(1500) - (int16_t)500;
_x = basex;
_y = basey;
_color = basecolor;
_color *= 4;
type = FIREWORK_DOT_SPARK;
show = 1;
}
void FireworkEffect::skyburst(accum88 x, accum88 y, saccum78 xv, saccum78 yv, CRGB c) {
_skyburst = 1;
_burst_x = x;
_burst_y = y;
_burst_xv = xv;
_burst_yv = yv;
_burst_color = c;
}
boolean FireworkEffect::can_be_shown_with_clock() {
return true;
}
void FireworkEffect::loop(uint16_t ms) {
window->clear();
_dot->move();
_dot->draw();
for (int i=0; i<settings.effects.firework.sparks; i++) {
_sparks[i]->move();
_sparks[i]->draw();
}
static uint16_t launch_countdown = 0;
if (_dot->show == 0) {
if (launch_countdown == 0) {
_dot->ground_launch();
_dot->type = FIREWORK_DOT_SHELL;
launch_countdown = random16(350) + 1;
} else {
launch_countdown--;
}
}
if (_skyburst) {
int nsparks = random8(settings.effects.firework.sparks / 2, settings.effects.firework.sparks + 1);
for (int i=0; i<nsparks; i++) {
_sparks[i]->sky_burst(_burst_x, _burst_y, _burst_yv, _burst_color);
_skyburst = 0;
}
}
}
FireworkEffect::FireworkEffect() {
_dot = new FireworkEffectDot(window, this);
_sparks = new FireworkEffectDot*[settings.effects.firework.sparks];
for (int i=0; i<settings.effects.firework.sparks; i++) {
_sparks[i] = new FireworkEffectDot(window, this);
}
}
FireworkEffect::~FireworkEffect() {
for (int i=0; i<settings.effects.firework.sparks; i++) {
delete _sparks[i];
}
delete _dot;
}

View File

@ -1,8 +1,8 @@
#include "effect_gol.h" #include "effects/gol.h"
#include "my_fastled.h" #include "my_fastled.h"
GolEffect::GolEffect() { GolEffect::GolEffect() {
this->window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6); this->window = &Window::window_with_clock;
_data = new uint8_t[this->window->count]; _data = new uint8_t[this->window->count];
_old = new uint8_t[this->window->count]; _old = new uint8_t[this->window->count];
@ -16,7 +16,7 @@ bool GolEffect::can_be_shown_with_clock() { return true; }
void GolEffect::_initialize() { void GolEffect::_initialize() {
for(uint16_t i=0; i<this->window->count; i++) { for(uint16_t i=0; i<this->window->count; i++) {
_data[i] = random8() < EFFECT_GOL_START_PERCENTAGE ? 1 : 0; _data[i] = random8() < settings.effects.gol.start_percentage ? 1 : 0;
} }
_old_hue = _hue; _old_hue = _hue;
_hue = random8(); _hue = random8();
@ -26,15 +26,14 @@ void GolEffect::_initialize() {
GolEffect::~GolEffect() { GolEffect::~GolEffect() {
delete[] _data; delete[] _data;
delete[] _old; delete[] _old;
delete window;
} }
void GolEffect::loop() { void GolEffect::loop(uint16_t ms) {
if (EFFECT_GOL_BLEND_SPEED + _blend > 255) { if (settings.effects.gol.blend_speed + _blend > 255) {
_blend = 0; _blend = 0;
_advance(); _advance();
} else { } else {
_blend += EFFECT_GOL_BLEND_SPEED; _blend += settings.effects.gol.blend_speed;
} }
_draw(); _draw();
@ -43,7 +42,7 @@ void GolEffect::loop() {
void GolEffect::_advance() { void GolEffect::_advance() {
_step++; _step++;
_old_hue = _hue; _old_hue = _hue;
if (_step >= EFFECT_GOL_RESTART_AFTER_STEPS) { if (_step >= settings.effects.gol.restart_after_steps) {
_initialize(); _initialize();
} else { } else {
for(uint16_t i=0; i<this->window->count; i++) { for(uint16_t i=0; i<this->window->count; i++) {

Some files were not shown because too many files have changed in this diff Show More