├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── images ├── beats.png ├── color.png ├── get_led.png ├── get_led_selectors.png ├── get_sections.png ├── headerimage.jpg ├── light_up.png ├── moving_dot.png ├── off.png ├── on.png ├── patreon_button.svg ├── rainbow.png ├── test_animations.png ├── transition.png └── write.png ├── neopixel_plus ├── __init__.py ├── animations │ ├── __init__.py │ ├── beats_up_and_down.py │ ├── light_up.py │ ├── moving_dot.py │ ├── rainbow.py │ └── transition.py ├── helper │ ├── __init__.py │ └── colors.py └── neopixel_plus.py └── setup.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: glowingkitty # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: marcoEDU # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | pyvenv/ 9 | .vscode 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | .dmypy.json 128 | dmypy.json 129 | 130 | # Pyre type checker 131 | .pyre/ 132 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Marco 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![NeoPixelPlus](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/headerimage.jpg "NeoPixelPlus") 2 | 3 | The NeoPixel library plus animations and terminal testing mode - so you can see how your LEDs would behave directly in the terminal, without any microcontroller. 4 | 5 | Want to support the development and stay updated? 6 | 7 | Become a Patreon Donate using Liberapay 8 | 9 | ## Overview 10 | 1. [Installation](#installation) 11 | 12 | 2. [Example](#example) 13 | 14 | 3. [NeoPixel class](#neopixel-class) 15 | 16 | - [pin](#neopixel_pin) 17 | 18 | - [n](#neopixel_n) 19 | 20 | - [start_led](#neopixel_start_led) 21 | 22 | - [test](#neopixel_test) 23 | 24 | - [overwrite_line](#neopixel_overwrite_line) 25 | 26 | - [debug](#neopixel_debug) 27 | 28 | - [target](#neopixel_target) 29 | 30 | 31 | 4. [NeoPixel functions (animations)](#neopixel-functions-animations) 32 | 33 | 4.1 [rainbow_animation()](#rainbow_animation) 34 | 35 | - [brightness](#rainbow_animation_brightness) 36 | 37 | - [loop_limit](#rainbow_animation_loop_limit) 38 | 39 | - [duration_ms](#rainbow_animation_duration_ms) 40 | 41 | - [pause_ms](#rainbow_animation_pause_ms) 42 | 43 | - [customization_json](#rainbow_animation_customization_json) 44 | 45 | 4.2 [beats()](#beats) 46 | 47 | - [brightness](#beats_brightness) 48 | 49 | - [brightness_fixed](#beats_brightness_fixed) 50 | 51 | - [loop_limit](#beats_loop_limit) 52 | 53 | - [duration_ms](#beats_duration_ms) 54 | 55 | - [pause_ms](#beats_pause_ms) 56 | 57 | - [start](#beats_start) 58 | 59 | - [rgb_colors](#beats_rgb_colors) 60 | 61 | - [num_random_colors](#beats_num_random_colors) 62 | 63 | - [max_height](#beats_num_max_height) 64 | 65 | - [customization_json](#beats_customization_json) 66 | 67 | 4.3 [moving_dot()](#moving_dot) 68 | 69 | - [brightness](#moving_dot_brightness) 70 | 71 | - [loop_limit](#moving_dot_loop_limit) 72 | 73 | - [duration_ms](#moving_dot_duration_ms) 74 | 75 | - [pause_a_ms](#moving_dot_pause_a_ms) 76 | 77 | - [pause_b_ms](#moving_dot_pause_a_ms) 78 | 79 | - [start](#moving_dot_start) 80 | 81 | - [rgb_colors](#moving_dot_rgb_colors) 82 | 83 | - [num_random_colors](#moving_dot_num_random_colors) 84 | 85 | - [customization_json](#moving_dot_customization_json) 86 | 87 | 4.4 [light_up()](#light_up) 88 | 89 | - [brightness](#light_up_brightness) 90 | 91 | - [loop_limit](#light_up_loop_limit) 92 | 93 | - [duration_ms](#light_up_duration_ms) 94 | 95 | - [pause_ms](#light_up_pause_ms) 96 | 97 | - [sections](#light_up_start) 98 | 99 | - [rgb_colors](#light_up_rgb_colors) 100 | 101 | - [num_random_colors](#light_up_num_random_colors) 102 | 103 | - [customization_json](#light_up_customization_json) 104 | 105 | 4.5 [transition()](#transition) 106 | 107 | - [brightness](#transition_brightness) 108 | 109 | - [loop_limit](#transition_loop_limit) 110 | 111 | - [duration_ms](#transition_duration_ms) 112 | 113 | - [pause_ms](#transition_pause_ms) 114 | 115 | - [sections](#transition_start) 116 | 117 | - [rgb_colors](#transition_rgb_colors) 118 | 119 | - [num_random_colors](#transition_num_random_colors) 120 | 121 | - [customization_json](#transition_customization_json) 122 | 123 | 5. [NeoPixel functions (other)](#neopixel-functions-other) 124 | 125 | 5.1 [get_sections()](#get_sections) 126 | 127 | 5.2 [get_led_selectors()](#get_led_selectors) 128 | 129 | - [sections](#get_led_selectors_sections) 130 | 131 | 5.3 [write()](#write) 132 | 133 | - [s_after_wait](#write_s_after_wait) 134 | 135 | 5.4 [get_led()](#get_led) 136 | 137 | - [i](#get_led_i) 138 | 139 | - [start](#get_led_start) 140 | 141 | 5.5 [off()](#off) 142 | 143 | 5.6 [on()](#on) 144 | 145 | - [num](#on_num) 146 | 147 | 5.7 [color()](#color) 148 | 149 | - [rgb_color](#color_rgb_color) 150 | 151 | - [customization_json](#color_customization_json) 152 | 153 | 5.8 [test_animations()](#test_animations) 154 | 155 | 5.9 [get_pin()](#get_pin) 156 | 157 | 6. [Terminal commands](#terminal-commands) 158 | 159 | ## Installation 160 | Make sure Python 3 is installed. 161 | 162 | `Recommended: always create a Python Virtual Environment for your project and install neopixel_plus in that environment.` 163 | ``` 164 | pip install neopixel_plus 165 | ``` 166 | 167 | ## Example 168 | 169 | IMPORTANT: 170 | 171 | To use NeoPixel+ on Raspberry Pi (using target='adafruit'), you need to make sure you execute python with sudo. 172 | For example: 173 | ``` 174 | sudo python 175 | ``` 176 | or from a virtual environment 177 | ``` 178 | sudo ./pyvenv/bin/python 179 | ``` 180 | 181 | 182 | ``` 183 | from neopixel_plus import NeoPixel 184 | 185 | # Example 1 - Changing the color of a physical LED 186 | pixel = NeoPixel(pin=5, n=30) 187 | pixel.leds[0] = (219,100,222) 188 | pixel.write() 189 | 190 | # Example 2 - Testing a rainbow animation in the terminal 191 | NeoPixel(test=True).rainbow_animation() 192 | 193 | # Example 3 - Playing a rainbow animation on physical LEDs 194 | NeoPixel(pin=5, n=30).rainbow_animation() 195 | 196 | ``` 197 | ## NeoPixel class 198 | 199 | #### Input: 200 | 201 | ##### NeoPixel(pin=...) 202 | ```python 203 | type = int 204 | default = 10 205 | purpose = 'The GPIO pin the data wire of the LED strip is connected to' 206 | ``` 207 | 208 | ##### NeoPixel(n=...) 209 | ```python 210 | type = int 211 | default = 30 212 | purpose = 'The number of RGB LEDs on your LED strip' 213 | ``` 214 | 215 | ##### NeoPixel(start_led=...) 216 | ```python 217 | type = int 218 | default = 0 219 | purpose = 'With which LED should the animation start' 220 | ``` 221 | 222 | ##### NeoPixel(test=...) 223 | ```python 224 | type = bool 225 | default = False 226 | purpose = 'If True: show LED simulation in terminal output. If False: connect to real LED strip and play animation.' 227 | ``` 228 | 229 | ##### NeoPixel(overwrite_line=...) 230 | ```python 231 | type = bool 232 | default = True 233 | purpose = 'If False: show all steps of LED animation in terminal ouput. Useful for debugging.' 234 | ``` 235 | 236 | ##### NeoPixel(debug=...) 237 | ```python 238 | type = bool 239 | default = False 240 | purpose = 'If True: prints all function calls and their input variables, for better debugging.' 241 | ``` 242 | 243 | ##### NeoPixel(target=...) 244 | ```python 245 | type = str 246 | default = 'micropython' 247 | options = ['micropython','adafruit'] 248 | purpose = 'Defines what kind of NeoPixel library is targeted: the default micropython NeoPixel or adafruits NeoPixel for Raspberry Pi.' 249 | ``` 250 | 251 | 252 | ## NeoPixel functions (animations) 253 | 254 | ### rainbow_animation() 255 | ![rainbow](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/rainbow.png "rainbow") 256 | 257 | #### Input: 258 | 259 | ##### rainbow_animation(brightness=...) 260 | ```python 261 | type = float 262 | default = 1.0 263 | purpose = 'Set the maximum brightness of the LEDs. 0 == off, 1.0 == 100%' 264 | ``` 265 | 266 | ##### rainbow_animation(loop_limit=...) 267 | ```python 268 | type = int 269 | default = None 270 | purpose = 'If set, defines how often animation should repeat, else: animation runs in infinite loop.' 271 | ``` 272 | 273 | ##### rainbow_animation(duration_ms=...) 274 | ```python 275 | type = int 276 | default = 1000 277 | purpose = 'Defines many ms should the animation last' 278 | ``` 279 | 280 | ##### rainbow_animation(pause_ms=...) 281 | ```python 282 | type = int 283 | default = None 284 | purpose = 'If set, defines if a pause should be made after animation and how long that lasts.' 285 | ``` 286 | 287 | ##### rainbow_animation(customization_json=...) 288 | ```python 289 | type = dict 290 | default = {} 291 | purpose = 'If you like, you can also give the customization options via a dict as an imput. Example: {"duration_ms":2000}' 292 | ``` 293 | 294 | 295 | ### beats() 296 | ![beats](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/beats.png "beats") 297 | 298 | #### Input: 299 | 300 | ##### beats(brightness=...) 301 | ```python 302 | type = float 303 | default = 1.0 304 | purpose = 'Set the maximum brightness of the LEDs. 0 == off, 1.0 == 100%' 305 | ``` 306 | 307 | ##### beats(brightness_fixed=...) 308 | ```python 309 | type = bool 310 | default = False 311 | purpose = 'If False: the brightness will start low and become high at the end. If True: the brightness stays the same during the animation, for all LEDs.' 312 | ``` 313 | 314 | ##### beats(loop_limit=...) 315 | ```python 316 | type = int 317 | default = None 318 | purpose = 'If set, defines how often animation should repeat, else: animation runs in infinite loop.' 319 | ``` 320 | 321 | ##### beats(duration_ms=...) 322 | ```python 323 | type = int 324 | default = 200 325 | purpose = 'Defines many ms should the animation last' 326 | ``` 327 | 328 | ##### beats(pause_ms=...) 329 | ```python 330 | type = int 331 | default = 300 332 | purpose = 'If set, defines if a pause should be made after animation and how long that lasts.' 333 | ``` 334 | 335 | ##### beats(start=...) 336 | ```python 337 | type = str 338 | default = 'start' 339 | options = ['start','end','start + end','center'] 340 | purpose = 'Defines from where the animation should start' 341 | ``` 342 | 343 | ##### beats(rgb_colors=...) 344 | ```python 345 | type = list 346 | default = list of randomly selected RGB colors (example: [[140,140,144],[240,100,0]]) 347 | purpose = 'Define what RGB colors the animation will use' 348 | ``` 349 | 350 | ##### beats(num_random_colors=...) 351 | ```python 352 | type = int 353 | default = 5 354 | purpose = 'Defines how many random RGB colors the animation will switch between, if no RGB colors are manually defined' 355 | ``` 356 | 357 | ##### beats(max_height=...) 358 | ```python 359 | type = float 360 | default = 1.0 361 | purpose = 'Defines how high the beat animation can go. 1.0 == 100% of all LEDs, 0 == no LEDs.' 362 | ``` 363 | 364 | ##### beats(customization_json=...) 365 | ```python 366 | type = dict 367 | default = {} 368 | purpose = 'If you like, you can also give the customization options via a dict as an imput. Example: {"duration_ms":2000}' 369 | ``` 370 | 371 | 372 | ### moving_dot() 373 | ![moving_dot](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/moving_dot.png "moving_dot") 374 | 375 | #### Input: 376 | 377 | ##### moving_dot(brightness=...) 378 | ```python 379 | type = float 380 | default = 1.0 381 | purpose = 'Set the maximum brightness of the LEDs. 0 == off, 1.0 == 100%' 382 | ``` 383 | 384 | 385 | ##### moving_dot(loop_limit=...) 386 | ```python 387 | type = int 388 | default = None 389 | purpose = 'If set, defines how often animation should repeat, else: animation runs in infinite loop.' 390 | ``` 391 | 392 | ##### moving_dot(duration_ms=...) 393 | ```python 394 | type = int 395 | default = 200 396 | purpose = 'Defines many ms should the animation last' 397 | ``` 398 | 399 | ##### moving_dot(pause_a_ms=...) 400 | ```python 401 | type = int 402 | default = 0 403 | purpose = 'Defines if pause A should be made and how long that lasts.' 404 | ``` 405 | 406 | ##### moving_dot(pause_b_ms=...) 407 | ```python 408 | type = int 409 | default = 300 410 | purpose = 'Defines if pause B should be made and how long that lasts.' 411 | ``` 412 | 413 | ##### moving_dot(start=...) 414 | ```python 415 | type = str 416 | default = 'start' 417 | options = ['start','end'] 418 | purpose = 'Defines from where the animation should start' 419 | ``` 420 | 421 | ##### moving_dot(rgb_colors=...) 422 | ```python 423 | type = list 424 | default = list of randomly selected RGB colors (example: [[140,140,144],[240,100,0]]) 425 | purpose = 'Define what RGB colors the animation will use' 426 | ``` 427 | 428 | ##### moving_dot(num_random_colors=...) 429 | ```python 430 | type = int 431 | default = 5 432 | purpose = 'Defines how many random RGB colors the animation will switch between, if no RGB colors are manually defined' 433 | ``` 434 | 435 | ##### moving_dot(customization_json=...) 436 | ```python 437 | type = dict 438 | default = {} 439 | purpose = 'If you like, you can also give the customization options via a dict as an imput. Example: {"duration_ms":2000}' 440 | ``` 441 | 442 | ### light_up() 443 | ![light_up](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/light_up.png "light_up") 444 | 445 | #### Input: 446 | 447 | ##### light_up(brightness=...) 448 | ```python 449 | type = float 450 | default = 1.0 451 | purpose = 'Set the maximum brightness of the LEDs. 0 == off, 1.0 == 100%' 452 | ``` 453 | 454 | 455 | ##### light_up(loop_limit=...) 456 | ```python 457 | type = int 458 | default = None 459 | purpose = 'If set, defines how often animation should repeat, else: animation runs in infinite loop.' 460 | ``` 461 | 462 | ##### light_up(duration_ms=...) 463 | ```python 464 | type = int 465 | default = 200 466 | purpose = 'Defines many ms should the animation last' 467 | ``` 468 | 469 | ##### light_up(pause_ms=...) 470 | ```python 471 | type = int 472 | default = 200 473 | purpose = 'Defines if pause should be made and how long that lasts.' 474 | ``` 475 | 476 | ##### light_up(sections=...) 477 | ```python 478 | type = str or list 479 | default = 'all' 480 | options = ['all',0,1,2,3] 481 | purpose = 'Defines what sections of the LED strip should glow up (one section is 15 LEDs).' 482 | ``` 483 | 484 | ##### light_up(rgb_colors=...) 485 | ```python 486 | type = list 487 | default = list of randomly selected RGB colors (example: [[140,140,144],[240,100,0]]) 488 | purpose = 'Define what RGB colors the animation will use' 489 | ``` 490 | 491 | ##### light_up(num_random_colors=...) 492 | ```python 493 | type = int 494 | default = 5 495 | purpose = 'Defines how many random RGB colors the animation will switch between, if no RGB colors are manually defined' 496 | ``` 497 | 498 | ##### light_up(customization_json=...) 499 | ```python 500 | type = dict 501 | default = {} 502 | purpose = 'If you like, you can also give the customization options via a dict as an imput. Example: {"duration_ms":2000}' 503 | ``` 504 | 505 | ### transition() 506 | ![transition](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/transition.png "transition") 507 | 508 | #### Input: 509 | 510 | ##### transition(brightness=...) 511 | ```python 512 | type = float 513 | default = 1.0 514 | purpose = 'Set the maximum brightness of the LEDs. 0 == off, 1.0 == 100%' 515 | ``` 516 | 517 | ##### transition(loop_limit=...) 518 | ```python 519 | type = int 520 | default = None 521 | purpose = 'If set, defines how often animation should repeat, else: animation runs in infinite loop.' 522 | ``` 523 | 524 | ##### transition(duration_ms=...) 525 | ```python 526 | type = int 527 | default = 200 528 | purpose = 'Defines many ms should the animation last' 529 | ``` 530 | 531 | ##### transition(pause_ms=...) 532 | ```python 533 | type = int 534 | default = 200 535 | purpose = 'Defines if pause should be made and how long that lasts.' 536 | ``` 537 | 538 | ##### transition(sections=...) 539 | ```python 540 | type = str or list 541 | default = 'all' 542 | options = ['all',0,1,2,3] 543 | purpose = 'Defines what sections of the LED strip should glow up (one section is 15 LEDs).' 544 | ``` 545 | 546 | ##### transition(rgb_colors=...) 547 | ```python 548 | type = list 549 | default = list of randomly selected RGB colors (example: [[140,140,144],[240,100,0]]) 550 | purpose = 'Define what RGB colors the animation will use' 551 | ``` 552 | 553 | ##### transition(num_random_colors=...) 554 | ```python 555 | type = int 556 | default = 5 557 | purpose = 'Defines how many random RGB colors the animation will switch between, if no RGB colors are manually defined' 558 | ``` 559 | 560 | ##### transition(customization_json=...) 561 | ```python 562 | type = dict 563 | default = {} 564 | purpose = 'If you like, you can also give the customization options via a dict as an imput. Example: {"duration_ms":2000}' 565 | ``` 566 | 567 | ## NeoPixel functions (other) 568 | 569 | ### get_sections() 570 | ![get_sections](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/get_sections.png "get_sections") 571 | Returns a list of all the LED strip sections (length: 15 LEDs per section). 572 | 573 | ### get_led_selectors() 574 | ![get_led_selectors](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/get_led_selectors.png "get_led_selectors") 575 | Returns a list of all the selector numbers to select specific LEDs in the strip. 576 | 577 | #### Input: 578 | 579 | ##### get_led_selectors(sections=...) 580 | ```python 581 | type = str or list 582 | default = 'all' 583 | options = ['all','random',0,1,2,3] 584 | purpose = 'Defines what sections should be returned.' 585 | ``` 586 | 587 | ### write() 588 | ![write](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/write.png "write") 589 | Makes the LEDs glow in the color you defined. If test==True: simulates how the LEDs would glow. 590 | 591 | #### Input: 592 | 593 | ##### write(s_after_wait=...) 594 | ```python 595 | type = float 596 | default = 1.0/36.0 597 | purpose = 'Defines how many seconds the code should wait after writing the LED status.' 598 | ``` 599 | 600 | ### get_led() 601 | ![get_led](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/get_led.png "get_led") 602 | Get the number of an LED, to select it for changing its color. 603 | 604 | #### Input: 605 | 606 | ##### get_led(i=...) 607 | ```python 608 | type = int 609 | purpose = 'Defines which LED you want to get. If <0: LED from the end will be selected.' 610 | ``` 611 | 612 | ##### get_led(start=...) 613 | ```python 614 | type = str 615 | default = None 616 | purpose = 'Defines from where your animation starts. If start==end: LEDs will be counted from the end of the LED strip.' 617 | ``` 618 | 619 | ### off() 620 | ![off](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/off.png "off") 621 | Turns all LEDs off. 622 | 623 | ### on() 624 | ![on](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/on.png "on") 625 | Turns all or specific LEDs on (make them glow white, 100% brightness). 626 | 627 | #### Input: 628 | 629 | ##### on(num=...) 630 | ```python 631 | type = int 632 | default = None 633 | purpose = 'Turn on only one specific LED.' 634 | ``` 635 | 636 | ### color() 637 | ![color](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/color.png "color") 638 | Turn on all LEDs in a specific RGB color. 639 | 640 | #### Input: 641 | 642 | ##### color(rgb_color=...) 643 | ```python 644 | type = list 645 | purpose = 'Define an [r,g,b] list with the red, green and blue values (from 0-255).' 646 | ``` 647 | 648 | ##### color(customization_json=...) 649 | ```python 650 | type = dict 651 | default = {} 652 | purpose = 'If you like, you can also give the customization options via a dict as an imput. Example: {"rgb_color":[100,200,200]}' 653 | ``` 654 | 655 | ### test_animations() 656 | ![test_animations](https://raw.githubusercontent.com/marcoEDU/NeoPixelPlus/master/images/test_animations.png "test_animations") 657 | Run all the different LED animations from NeoPixel+. 658 | 659 | ### get_pin() 660 | Returns the class object for the GPIO pin (micropython's and adafruit's NeoPixel use different classes for that). 661 | 662 | ## Terminal commands 663 | You can also start an animation by calling the neopixel_plus.py file directly via your terminal. 664 | 665 | Example: 666 | ``` 667 | python3 neopixel_plus.py -t -d -n -a 668 | ``` -------------------------------------------------------------------------------- /images/beats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/beats.png -------------------------------------------------------------------------------- /images/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/color.png -------------------------------------------------------------------------------- /images/get_led.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/get_led.png -------------------------------------------------------------------------------- /images/get_led_selectors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/get_led_selectors.png -------------------------------------------------------------------------------- /images/get_sections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/get_sections.png -------------------------------------------------------------------------------- /images/headerimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/headerimage.jpg -------------------------------------------------------------------------------- /images/light_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/light_up.png -------------------------------------------------------------------------------- /images/moving_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/moving_dot.png -------------------------------------------------------------------------------- /images/off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/off.png -------------------------------------------------------------------------------- /images/on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/on.png -------------------------------------------------------------------------------- /images/patreon_button.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 31 | 52 | 55 | 62 | 68 | 74 | 80 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /images/rainbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/rainbow.png -------------------------------------------------------------------------------- /images/test_animations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/test_animations.png -------------------------------------------------------------------------------- /images/transition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/transition.png -------------------------------------------------------------------------------- /images/write.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glowingkitty/NeoPixelPlus/e3ef4c916b4cb368f890e56128dbdf298a148e58/images/write.png -------------------------------------------------------------------------------- /neopixel_plus/__init__.py: -------------------------------------------------------------------------------- 1 | from .neopixel_plus import NeoPixel 2 | -------------------------------------------------------------------------------- /neopixel_plus/animations/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from neopixel_plus.animations.beats_up_and_down import BeatsUpAndDown 3 | from neopixel_plus.animations.light_up import LightUp 4 | from neopixel_plus.animations.moving_dot import MovingDot 5 | from neopixel_plus.animations.rainbow import RainbowAnimation 6 | from neopixel_plus.animations.transition import Transition 7 | except ImportError: 8 | from animations.beats_up_and_down import BeatsUpAndDown 9 | from animations.light_up import LightUp 10 | from animations.moving_dot import MovingDot 11 | from animations.rainbow import RainbowAnimation 12 | from animations.transition import Transition 13 | -------------------------------------------------------------------------------- /neopixel_plus/animations/beats_up_and_down.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | try: 4 | from neopixel_plus.helper import Color 5 | except ImportError: 6 | from helper import Color 7 | 8 | 9 | class BeatsUpAndDown: 10 | def __init__(self, 11 | led_strip, 12 | loop_limit=None, 13 | duration_ms=200, 14 | pause_ms=300, 15 | start='start', 16 | rgb_colors=None, 17 | brightness=1, 18 | brightness_fixed=False, 19 | max_height=1, 20 | num_random_colors=5, 21 | ): 22 | self.led_strip = led_strip 23 | self.loop_limit = loop_limit 24 | self.loops = 0 25 | self.duration_ms = duration_ms 26 | self.pause_ms = pause_ms 27 | self.start = start 28 | self.max_height = max_height 29 | 30 | self.write_wait_time = ( 31 | self.duration_ms/2/self.led_strip.addressable_strip_length)/1000 32 | 33 | self.colors = Color( 34 | rgb_colors=rgb_colors, 35 | brightness=brightness, 36 | brightness_fixed=brightness_fixed, 37 | num_random_colors=num_random_colors 38 | ) 39 | 40 | if self.start == 'start' or self.start == 'end': 41 | self.led_strip.addressable_strip_length = round( 42 | self.led_strip.strip_length*self.max_height) 43 | self.selected_leds = range(self.led_strip.addressable_strip_length) 44 | self.selected_leds_counter_up = self.led_strip.addressable_strip_length 45 | self.selected_leds_counter_down = round(self.led_strip.addressable_strip_length - 46 | self.selected_leds_counter_up) 47 | else: 48 | self.led_strip.addressable_strip_length = self.led_strip.strip_length 49 | self.selected_leds = range( 50 | round(self.led_strip.addressable_strip_length/2)) 51 | self.selected_leds_counter_up = round(self.max_height * 52 | round(self.led_strip.addressable_strip_length/2)) 53 | self.selected_leds_counter_down = round(round( 54 | self.led_strip.addressable_strip_length/2)-self.selected_leds_counter_up) 55 | 56 | def color_leds(self): 57 | if self.led_strip.debug: 58 | print('BeatsUpAndDown().color_leds()') 59 | 60 | # color LEDs 61 | for i in self.selected_leds[:self.selected_leds_counter_up]: 62 | # if brightness_fixed==False: set brightness depending on what led is glowing up 63 | if self.colors.brightness_fixed == False: 64 | # led 1: 30% of self.brightness_max 65 | # led 2: 30% of max + (i * 70%/self.led_strip.strip_length) 66 | # last LED: 100% * self.brightness_max 67 | if self.start == 'start + end' or self.start == 'center': 68 | self.colors.brightness = round((0.3*self.colors.brightness_max) + 69 | ((i+1)*(0.7/((self.led_strip.addressable_strip_length*self.max_height)/2))), 2) 70 | else: 71 | self.colors.brightness = round((0.3*self.colors.brightness_max) + 72 | ((i+1)*(0.7/self.led_strip.addressable_strip_length)), 2) 73 | self.colors.correct() 74 | 75 | # "start" option "start & end" and "center" 76 | if self.start == 'end': 77 | i = [-(i+1)] 78 | elif self.start == 'start': 79 | i = [i] 80 | elif self.start == 'start + end': 81 | i = [i, -(i+1)] 82 | elif self.start == 'center': 83 | i = [i+round(self.led_strip.addressable_strip_length/2), - 84 | (i+1)+round(self.led_strip.addressable_strip_length/2)] 85 | 86 | for led in i: 87 | led = self.led_strip.get_led(led, self.start) 88 | self.led_strip.leds[led] = self.colors.selected 89 | 90 | self.led_strip.write(s_after_wait=self.write_wait_time) 91 | 92 | def make_leds_black(self): 93 | if self.led_strip.debug: 94 | print('BeatsUpAndDown().make_leds_black()') 95 | 96 | # then make them black 97 | for i in self.selected_leds[self.selected_leds_counter_down:]: 98 | if self.start == 'start': 99 | i = [-(i+1)] 100 | elif self.start == 'end': 101 | i = [i] 102 | elif self.start == 'start + end': 103 | i = [-(i+1)+round(self.led_strip.addressable_strip_length/2), 104 | i+round(self.led_strip.addressable_strip_length/2)] 105 | elif self.start == 'center': 106 | i = [i, -(i+1)] 107 | 108 | for led in i: 109 | led = self.led_strip.get_led(led, self.start) 110 | self.led_strip.leds[led] = self.colors.black 111 | 112 | self.led_strip.write(s_after_wait=self.write_wait_time) 113 | 114 | def glow(self): 115 | if self.led_strip.debug: 116 | print('BeatsUpAndDown().glow()') 117 | 118 | print('Beats up and down:') 119 | try: 120 | # make sure leds are off 121 | self.led_strip.off() 122 | 123 | while True: 124 | # update color if brightness different 125 | if self.colors.brightness != 1 and self.colors.brightness_fixed: 126 | self.colors.correct() 127 | 128 | self.color_leds() 129 | self.make_leds_black() 130 | 131 | # change to next color 132 | self.colors.next() 133 | 134 | if self.pause_ms: 135 | time.sleep(self.pause_ms/1000) 136 | 137 | self.loops += 1 138 | if self.loop_limit and self.loop_limit == self.loops: 139 | print() 140 | break 141 | except KeyboardInterrupt: 142 | self.led_strip.fadeout() 143 | 144 | import sys 145 | print() 146 | sys.exit(0) 147 | -------------------------------------------------------------------------------- /neopixel_plus/animations/light_up.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | try: 4 | from neopixel_plus.helper import Color 5 | except ImportError: 6 | from helper import Color 7 | 8 | 9 | class LightUp: 10 | def __init__(self, 11 | led_strip, 12 | rgb_colors=None, 13 | brightness=1, 14 | loop_limit=None, 15 | duration_ms=200, 16 | pause_ms=200, 17 | num_random_colors=5, 18 | sections='all'): 19 | self.led_strip = led_strip 20 | self.loop_limit = loop_limit 21 | self.loops = 0 22 | self.duration_ms = duration_ms 23 | self.pause_ms = pause_ms 24 | self.sections = sections 25 | self.colors = Color( 26 | rgb_colors=rgb_colors, 27 | brightness=brightness, 28 | num_random_colors=num_random_colors 29 | ) 30 | 31 | self.write_wait_time = ( 32 | self.duration_ms/((round(self.colors.brightness_max, 1)/0.1)*2)/1000) 33 | 34 | def glow(self): 35 | print('Light up:') 36 | try: 37 | # make sure leds are off 38 | self.led_strip.off() 39 | 40 | while True: 41 | # go over levels of brightness from 0 to 1 and 42 | self.colors.brightness = 0 43 | self.colors.correct() 44 | 45 | self.selected_leds = self.led_strip.get_led_selectors( 46 | self.sections) 47 | 48 | # light up in 10 steps 49 | steps = 0 50 | step_height = round(self.colors.brightness_max/10, 2) 51 | while steps < 10: 52 | for i in self.selected_leds: 53 | self.led_strip.leds[i] = self.colors.selected 54 | self.led_strip.write(s_after_wait=self.write_wait_time) 55 | 56 | # update color 57 | self.colors.brightness += step_height 58 | self.colors.correct() 59 | steps += 1 60 | 61 | steps = 0 62 | while steps < 10: 63 | for i in self.selected_leds: 64 | self.led_strip.leds[i] = self.colors.selected 65 | self.led_strip.write(s_after_wait=self.write_wait_time) 66 | 67 | # update color 68 | self.colors.brightness -= step_height 69 | self.colors.correct() 70 | steps += 1 71 | 72 | # change to next color 73 | self.colors.next() 74 | 75 | if self.pause_ms: 76 | time.sleep(self.pause_ms/1000) 77 | 78 | self.loops += 1 79 | if self.loop_limit and self.loop_limit == self.loops: 80 | print() 81 | break 82 | 83 | except KeyboardInterrupt: 84 | self.led_strip.fadeout() 85 | 86 | import sys 87 | print() 88 | sys.exit(0) 89 | -------------------------------------------------------------------------------- /neopixel_plus/animations/moving_dot.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | try: 4 | from neopixel_plus.helper import Color 5 | except ImportError: 6 | from helper import Color 7 | 8 | 9 | class MovingDot: 10 | def __init__(self, 11 | led_strip, 12 | loop_limit=None, 13 | duration_ms=200, 14 | pause_a_ms=0, 15 | pause_b_ms=300, 16 | start='start', 17 | rgb_colors=None, 18 | brightness=1, 19 | num_random_colors=5): 20 | self.led_strip = led_strip 21 | self.loop_limit = loop_limit 22 | self.loops = 0 23 | self.duration_ms = duration_ms 24 | self.pause_a_ms = pause_a_ms 25 | self.pause_b_ms = pause_b_ms 26 | self.start = start 27 | self.selector = { 28 | "start": [-1, -2, -3, -4, -5], 29 | "end": [0, 1, 2, 3, 4] 30 | } 31 | 32 | self.colors = Color( 33 | rgb_colors=rgb_colors, 34 | brightness=brightness, 35 | num_random_colors=num_random_colors 36 | ) 37 | 38 | self.write_wait_time = ( 39 | self.duration_ms/2/self.led_strip.strip_length)/1000 40 | 41 | def create_dot(self): 42 | if self.led_strip.debug: 43 | print('MovingDot().create_dot()') 44 | 45 | self.dot = [self.colors.black]*5 46 | 47 | counter = 0 48 | for selected in self.selector[self.start]: 49 | self.colors.brightness = ( 50 | 1-(counter*0.225)) * self.colors.brightness_max 51 | self.colors.correct() 52 | self.dot[selected] = self.colors.selected 53 | counter += 1 54 | 55 | def change_direction(self): 56 | if self.led_strip.debug: 57 | print('MovingDot().change_direction()') 58 | 59 | if self.start == 'start': 60 | self.start = 'end' 61 | else: 62 | self.start = 'start' 63 | 64 | def move_dot(self): 65 | if self.led_strip.debug: 66 | print('MovingDot().move_dot()') 67 | 68 | # move dot into view 69 | for selected in self.selector[self.start]: 70 | if self.start == 'start': 71 | self.led_strip.insert_led(0, self.dot[selected]) 72 | else: 73 | self.led_strip.append_led(self.dot[selected]) 74 | self.led_strip.write(s_after_wait=self.write_wait_time) 75 | 76 | # add black led to front and remove last led, to move dot 77 | while True: 78 | if self.start == 'start': 79 | self.led_strip.insert_led() 80 | else: 81 | self.led_strip.append_led() 82 | self.led_strip.write(s_after_wait=self.write_wait_time) 83 | 84 | # if all leds black, exit loop 85 | for led in self.led_strip.leds: 86 | if led[0] != 0 or led[1] != 0 or led[2] != 0: 87 | break 88 | else: 89 | break 90 | 91 | def glow(self): 92 | if self.led_strip.debug: 93 | print('MovingDot().glow()') 94 | 95 | print('Moving dot:') 96 | try: 97 | # make sure leds are off 98 | self.led_strip.off() 99 | 100 | while True: 101 | # make sure duration is correct 102 | # create dot with tail 103 | self.create_dot() 104 | 105 | # move dot with tail and write 106 | self.move_dot() 107 | 108 | # once dot disappeared at the end: pause_a 109 | time.sleep(self.pause_a_ms/1000) 110 | 111 | # create new dot with tail and move in opposit start 112 | self.change_direction() 113 | self.create_dot() 114 | self.move_dot() 115 | self.change_direction() 116 | 117 | # once dot disappeared at the end: pause_b 118 | time.sleep(self.pause_b_ms/1000) 119 | 120 | # change to next color 121 | self.colors.next() 122 | 123 | self.loops += 1 124 | if self.loop_limit and self.loop_limit == self.loops: 125 | print() 126 | break 127 | 128 | except KeyboardInterrupt: 129 | self.led_strip.fadeout() 130 | 131 | import sys 132 | print() 133 | sys.exit(0) 134 | -------------------------------------------------------------------------------- /neopixel_plus/animations/rainbow.py: -------------------------------------------------------------------------------- 1 | import math 2 | import time 3 | 4 | # TODO add Color() and simplify code (create rainbow once, then move it) 5 | 6 | 7 | class RainbowAnimation: 8 | def __init__(self, 9 | led_strip, 10 | brightness=1, 11 | loop_limit=None, 12 | duration_ms=1000, 13 | pause_ms=None): 14 | self.led_strip = led_strip 15 | self.brightness_max = brightness 16 | self.loop_limit = loop_limit 17 | self.duration_ms = duration_ms 18 | self.pause_ms = pause_ms 19 | 20 | self.time_passed_ms = 0 21 | 22 | def set_time_passed_ms(self): 23 | if self.led_strip.debug: 24 | print('RainbowAnimation().set_time_passed_ms()') 25 | 26 | # if duration 1000 ms = 17 loops * 60ms 27 | # if duration 500 ms = 8.5 loops 120ms 28 | # if duration 250 ms = 4.25 loops 240ms 29 | # if duration 100 ms = 1.7 loops 600ms 30 | full_duration = 1000 31 | default_rate = 60 32 | 33 | added_ms = (full_duration/self.duration_ms)*default_rate 34 | 35 | self.time_passed_ms += added_ms 36 | 37 | def set_brightness(self, counter, max_counter): 38 | if self.led_strip.debug: 39 | print('RainbowAnimation().set_brightness(counter={},max_counter={})'.format( 40 | counter, max_counter)) 41 | 42 | # if counter == 1 => brightness 0.3 43 | # if counter == 2 => brightness 0.6 44 | # if counter == max_counter-1 => brightness 0.3 45 | # if counter == max_counter-2 => brightness 0.6 46 | if self.duration_ms and self.pause_ms and counter == 0: 47 | self.brightness = 0.3*self.brightness_max 48 | elif self.duration_ms and self.pause_ms and counter == 1: 49 | self.brightness = 0.6*self.brightness_max 50 | elif self.duration_ms and self.pause_ms and counter == (max_counter-2): 51 | self.brightness = 0.6*self.brightness_max 52 | elif self.duration_ms and self.pause_ms and counter == (max_counter-1): 53 | self.brightness = 0.3*self.brightness_max 54 | else: 55 | self.brightness = 1*self.brightness_max 56 | 57 | def get_max_counter(self): 58 | if self.led_strip.debug: 59 | print('RainbowAnimation().get_max_counter()') 60 | 61 | # if duration 1000 ms = 17 loops * 60ms 62 | # if duration 500 ms = 8.5 loops 120ms 63 | # if duration 250 ms = 4.25 loops 240ms 64 | # if duration 100 ms = 1.7 loops 600ms 65 | full_duration = 1000 66 | loops = 17 67 | 68 | return round((self.duration_ms/full_duration)*loops) 69 | 70 | def glow(self): 71 | if self.led_strip.debug: 72 | print('RainbowAnimation().glow()') 73 | 74 | print('Rainbow:') 75 | try: 76 | # if duration, need to adapt time_passed to make one full color loop (and then pause if pause set) 77 | # turn LEDs rainbow 78 | counter = 0 79 | loops = 0 80 | max_counter = self.get_max_counter() 81 | while True: 82 | if loops < 10: 83 | self.brightness = (0.1+(loops*0.1))*self.brightness_max 84 | else: 85 | self.set_brightness(counter, max_counter) 86 | 87 | # turn LEDs black (off) for duration of pause 88 | if counter == max_counter: 89 | if self.duration_ms and self.pause_ms: 90 | self.led_strip.off() 91 | time.sleep((self.pause_ms-10)/1000) 92 | 93 | counter = 0 94 | loops += 1 95 | 96 | if self.loop_limit and self.loop_limit == loops: 97 | print() 98 | break 99 | 100 | else: 101 | 102 | self.set_time_passed_ms() 103 | for i in range(self.led_strip.strip_length): 104 | i = self.led_strip.get_led(i) 105 | color = self.rainbow_color(self.time_passed_ms, i, 106 | self.brightness) 107 | self.led_strip.leds[i] = color 108 | 109 | self.led_strip.write() 110 | 111 | counter += 1 112 | loops += 1 113 | except KeyboardInterrupt: 114 | self.led_strip.fadeout() 115 | 116 | import sys 117 | print() 118 | sys.exit(0) 119 | 120 | def rainbow_color(self, t, i, brightness): 121 | if self.led_strip.debug: 122 | print('RainbowAnimation().rainbow_color(t={},i={},brightness={})'.format( 123 | t, i, brightness)) 124 | 125 | t = t/1000 126 | 127 | k = t + 0.05 * i 128 | 129 | r = 0.5 + 0.5 * math.cos(6.28318 * (1.0 * k + 0.00)) 130 | g = 0.5 + 0.5 * math.cos(6.28318 * (1.0 * k + 0.33)) 131 | b = 0.5 + 0.5 * math.cos(6.28318 * (1.0 * k + 0.67)) 132 | 133 | r = int(255.0 * r * brightness) 134 | g = int(255.0 * g * brightness) 135 | b = int(255.0 * b * brightness) 136 | 137 | r = r if r < 255 and r > 0 else 255 if r >= 255 else 0 138 | g = g if g < 255 and g > 0 else 255 if g >= 255 else 0 139 | b = b if b < 255 and b > 0 else 255 if b >= 255 else 0 140 | return (r, g, b) 141 | -------------------------------------------------------------------------------- /neopixel_plus/animations/transition.py: -------------------------------------------------------------------------------- 1 | import time 2 | from os import name 3 | 4 | try: 5 | from neopixel_plus.helper import Color 6 | except ImportError: 7 | from helper import Color 8 | 9 | 10 | class Transition: 11 | def __init__(self, 12 | led_strip, 13 | rgb_colors=None, 14 | brightness=1, 15 | loop_limit=None, 16 | duration_ms=200, 17 | pause_ms=200, 18 | num_random_colors=5, 19 | sections='all'): 20 | self.led_strip = led_strip 21 | self.loop_limit = loop_limit 22 | self.loops = 0 23 | self.duration_ms = duration_ms 24 | self.pause_ms = pause_ms 25 | self.sections = sections 26 | self.colors = Color( 27 | rgb_colors=rgb_colors, 28 | brightness=brightness, 29 | num_random_colors=num_random_colors 30 | ) 31 | 32 | self.transition_steps = 20 33 | self.write_wait_time = (self.duration_ms/self.transition_steps)/1000 34 | 35 | self.selected_leds = self.led_strip.get_led_selectors( 36 | self.sections) 37 | 38 | def glow(self): 39 | if self.led_strip.debug: 40 | print('Transition().glow()') 41 | 42 | print('Transition:') 43 | try: 44 | # make sure leds are off 45 | self.led_strip.off() 46 | 47 | while True: 48 | # add or substract difference between rgb values and update color - to make transition 49 | 50 | steps_counter = 0 51 | target_color = self.colors.selected 52 | difference = [ 53 | target_color[0] - 54 | self.led_strip.leds[self.selected_leds[0]][0], 55 | target_color[1] - 56 | self.led_strip.leds[self.selected_leds[0]][1], 57 | target_color[2] - 58 | self.led_strip.leds[self.selected_leds[0]][2], 59 | ] 60 | 61 | difference_per_step = [ 62 | round(difference[0]/self.transition_steps), 63 | round(difference[1]/self.transition_steps), 64 | round(difference[2]/self.transition_steps), 65 | ] 66 | 67 | while steps_counter != self.transition_steps: 68 | 69 | self.colors.selected = [ 70 | self.led_strip.leds[self.selected_leds[0] 71 | ][0]+difference_per_step[0], 72 | self.led_strip.leds[self.selected_leds[0] 73 | ][1]+difference_per_step[1], 74 | self.led_strip.leds[self.selected_leds[0] 75 | ][2]+difference_per_step[2], 76 | ] 77 | 78 | # make to not select invalid RGB values 79 | new_list = [] 80 | for number in self.colors.selected: 81 | if number < 0: 82 | new_list.append(0) 83 | elif number > 255: 84 | new_list.append(255) 85 | else: 86 | new_list.append(number) 87 | self.colors.selected = new_list 88 | 89 | for i in self.selected_leds: 90 | self.led_strip.leds[i] = self.colors.selected 91 | self.led_strip.write(s_after_wait=self.write_wait_time) 92 | 93 | steps_counter += 1 94 | 95 | # change to next color 96 | self.colors.next() 97 | 98 | if self.pause_ms: 99 | time.sleep(self.pause_ms/1000) 100 | 101 | self.loops += 1 102 | if self.loop_limit and self.loop_limit == self.loops: 103 | print() 104 | break 105 | 106 | except KeyboardInterrupt: 107 | self.led_strip.fadeout() 108 | 109 | import sys 110 | print() 111 | sys.exit(0) 112 | -------------------------------------------------------------------------------- /neopixel_plus/helper/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | try: 3 | from neopixel_plus.helper.colors import Color 4 | except ImportError: 5 | from helper.colors import Color 6 | -------------------------------------------------------------------------------- /neopixel_plus/helper/colors.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | class Color: 5 | def __init__(self, rgb_colors, brightness=1, brightness_fixed=False, num_random_colors=5): 6 | self.rgb_colors = rgb_colors if rgb_colors else self.random_set( 7 | num_random_colors) 8 | self.selected_num = 0 9 | self.selected_max = len(self.rgb_colors)-1 10 | # make sure self.rgb_colors is a list and self.rgb_colors[self.selected_num] is also a list 11 | if type(self.rgb_colors) == list and type(self.rgb_colors[self.selected_num]) == list: 12 | self.base_color = self.rgb_colors[self.selected_num] 13 | else: 14 | print( 15 | 'self.rgb_colors and self.rgb_colors[self.selected_num] must be lists. Currently:') 16 | print('self.rgb_colors: {}'.format(self.rgb_colors)) 17 | print('self.rgb_colors[self.selected_num]: {}'.format( 18 | self.rgb_colors[self.selected_num])) 19 | raise 20 | self.brightness = brightness 21 | self.brightness_max = brightness 22 | self.brightness_fixed = brightness_fixed 23 | 24 | self.correct() 25 | 26 | @property 27 | def black(self): 28 | return [0, 0, 0] 29 | 30 | @property 31 | def white(self): 32 | return [255, 255, 255] 33 | 34 | def random(self): 35 | return [random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)] 36 | 37 | def random_set(self, how_many): 38 | array = [0]*how_many 39 | return [self.random() for x in array] 40 | 41 | def correct(self): 42 | r = int(self.base_color[0] * self.brightness) 43 | g = int(self.base_color[1] * self.brightness) 44 | b = int(self.base_color[2] * self.brightness) 45 | 46 | self.selected = [r if r < 255 else 255, g if g < 47 | 255 else 255, b if b < 255 else 255] 48 | 49 | def next(self): 50 | # select next color from self.rgb_colors, with correct brightness 51 | if self.selected_num == self.selected_max: 52 | self.selected_num = 0 53 | else: 54 | self.selected_num += 1 55 | self.base_color = self.rgb_colors[self.selected_num] 56 | self.correct() 57 | -------------------------------------------------------------------------------- /neopixel_plus/neopixel_plus.py: -------------------------------------------------------------------------------- 1 | import getopt 2 | import math 3 | import random 4 | import sys 5 | import time 6 | from random import randint 7 | 8 | try: 9 | from neopixel_plus.animations import * 10 | except ImportError: 11 | from animations import * 12 | 13 | 14 | class NeoPixel: 15 | def __init__(self, 16 | pin_num=None, 17 | n=30, 18 | start_led=0, 19 | test=False, 20 | overwrite_line=True, 21 | debug=False, 22 | target='micropython' # or 'adafruit' 23 | ): 24 | self.debug = debug 25 | self.target = target 26 | self.strip_length = n 27 | self.addressable_strip_length = n 28 | self.start_led = start_led 29 | self.test = test 30 | self.pin_num = pin_num if pin_num else 10 if self.target == 'micropython' else 18 if self.target == 'adafruit' else None 31 | self.overwrite_line = overwrite_line 32 | self.sections = self.get_sections() 33 | 34 | if self.test: 35 | self.leds = [[0, 0, 0] for x in range(self.strip_length)] 36 | else: 37 | from neopixel import NeoPixel as NeoPixelOriginal 38 | if self.target == 'micropython': 39 | self.leds = NeoPixelOriginal( 40 | self.get_pin(), 41 | self.strip_length) 42 | elif self.target == 'adafruit': 43 | self.leds = NeoPixelOriginal( 44 | self.get_pin(), 45 | self.strip_length, 46 | auto_write=False) 47 | 48 | def get_pin(self): 49 | if self.target == 'micropython': 50 | from machine import Pin 51 | return Pin(self.pin_num, Pin.OUT) 52 | 53 | elif self.target == 'adafruit': 54 | import board 55 | if self.pin_num == 18: 56 | return board.D18 57 | elif self.pin_num == 23: 58 | return board.D23 59 | elif self.pin_num == 24: 60 | return board.D24 61 | elif self.pin_num == 24: 62 | return board.D24 63 | elif self.pin_num == 25: 64 | return board.D25 65 | elif self.pin_num == 12: 66 | return board.D12 67 | elif self.pin_num == 16: 68 | return board.D16 69 | elif self.pin_num == 4: 70 | return board.D4 71 | elif self.pin_num == 17: 72 | return board.D17 73 | elif self.pin_num == 27: 74 | return board.D27 75 | elif self.pin_num == 22: 76 | return board.D22 77 | elif self.pin_num == 5: 78 | return board.D5 79 | elif self.pin_num == 6: 80 | return board.D6 81 | elif self.pin_num == 13: 82 | return board.D13 83 | elif self.pin_num == 26: 84 | return board.D26 85 | 86 | def get_sections(self): 87 | if self.debug: 88 | print('NeoPixel().get_sections()') 89 | 90 | sections_length = 15 91 | sections = [] 92 | counter = 0 93 | while counter < self.addressable_strip_length: 94 | sections.append( 95 | [x for x in range(counter, counter+sections_length)]) 96 | counter += sections_length 97 | return sections 98 | 99 | def get_led_selectors(self, sections='all'): 100 | if self.debug: 101 | print('NeoPixel().get_led_selectors(sections={})'.format(sections)) 102 | 103 | if type(sections) == str: 104 | if sections == 'all': 105 | return range(self.addressable_strip_length) 106 | elif sections == 'random': 107 | return self.sections[randint(0, len(self.sections)-1)] 108 | else: 109 | selected_leds = [] 110 | 111 | # if "sections" is a list of strings, first convert them to counter numbers (0,1,2,3) instead of "Section 1","Section 2" etc. 112 | if type(sections[0]) == str: 113 | sections = [int(x.lower().replace(' ', '').split( 114 | 'section')[-1])-1 for x in sections] 115 | 116 | for entry in sections: 117 | selected_leds += self.sections[entry] 118 | 119 | return selected_leds 120 | 121 | def write(self, s_after_wait=1.0/36.0): 122 | if self.debug: 123 | print('NeoPixel().write(s_after_wait={})'.format(s_after_wait)) 124 | 125 | if self.test: 126 | from colr import color 127 | print( 128 | ''.join(color(' ', back=(x[0], x[1], x[2])) for x in self.leds), end='\r' if self.overwrite_line and not self.debug else '\n') 129 | else: 130 | if self.target == 'micropython': 131 | self.leds.write() 132 | elif self.target == 'adafruit': 133 | self.leds.show() 134 | time.sleep(s_after_wait) 135 | 136 | def insert_led(self, position=0, rgb=[0, 0, 0]): 137 | if self.debug: 138 | print('NeoPixel().insert_led(position={},rgb={})'.format(position, rgb)) 139 | # save state of all leds as list, insert LED at position, then write LEDs 140 | leds = [[x[0], x[1], x[2]] for x in self.leds] 141 | leds.insert(position, rgb) 142 | leds = leds[:-1] 143 | for i in range(len(leds)): 144 | self.leds[i] = leds[i] 145 | 146 | def append_led(self, rgb=[0, 0, 0]): 147 | if self.debug: 148 | print('NeoPixel().append_led(rgb={})'.format(rgb)) 149 | # save state of all leds as list, append LED at the end, then write LEDs 150 | leds = [[x[0], x[1], x[2]] for x in self.leds] 151 | leds.append(rgb) 152 | leds = leds[1:] 153 | for i in range(len(leds)): 154 | self.leds[i] = leds[i] 155 | 156 | def fadeout(self): 157 | # get current colors of leds and make them darker step by step 158 | brightness = 0.9 159 | while brightness >= 0: 160 | for x in range(self.strip_length): 161 | r = round(self.leds[x][0]*brightness) 162 | g = round(self.leds[x][1]*brightness) 163 | b = round(self.leds[x][2]*brightness) 164 | self.leds[x] = [ 165 | r if r <= 255 and r >= 0 else 255 if r > 255 else 0, 166 | g if g <= 255 and g >= 0 else 255 if g > 255 else 0, 167 | b if b <= 255 and b >= 0 else 255 if b > 255 else 0, 168 | ] 169 | brightness -= 0.1 170 | self.write(0.001) 171 | 172 | def get_led(self, i, start=None): 173 | if self.debug: 174 | print('NeoPixel().get_led(i={},start={}'.format(i, start)) 175 | i = i+self.start_led 176 | if i < 0: 177 | i += self.addressable_strip_length 178 | if start and start == 'end': 179 | i += (self.strip_length-self.addressable_strip_length) 180 | 181 | return i 182 | 183 | def off(self): 184 | if self.debug: 185 | print('NeoPixel().off()') 186 | for i in range(self.strip_length): 187 | self.leds[i] = (0, 0, 0) 188 | self.write() 189 | 190 | def on(self, num=None): 191 | if self.debug: 192 | print('NeoPixel().on(num={})'.format(num)) 193 | if type(num) == int: 194 | num = self.get_led(num) 195 | self.leds[num] = (255, 255, 255) 196 | else: 197 | for i in range(self.strip_length): 198 | self.leds[i] = (255, 255, 255) 199 | self.write() 200 | 201 | def test_animations(self): 202 | # run all the animations for testing 203 | print('Start testing animations...') 204 | while True: 205 | self.rainbow_animation(loop_limit=2) 206 | 207 | self.beats(loop_limit=3) 208 | self.beats(loop_limit=3, start='end') 209 | self.beats(loop_limit=3, start='start + end') 210 | self.beats(loop_limit=3, start='center', brightness=0.5) 211 | 212 | self.moving_dot(loop_limit=3) 213 | self.moving_dot(loop_limit=3, start='end', brightness=0.5) 214 | 215 | self.light_up(loop_limit=3) 216 | self.light_up(loop_limit=3, sections='random') 217 | self.light_up(loop_limit=3, sections=[0]) 218 | 219 | self.transition(loop_limit=3) 220 | self.transition(loop_limit=3, sections=[0]) 221 | 222 | def color(self, 223 | rgb_colors=[randint(0, 255), randint(0, 255), randint(0, 255)], 224 | customization_json={} 225 | ): 226 | try: 227 | print('color:') 228 | original_r = customization_json['rgb_colors'][0][ 229 | 0] if customization_json and 'rgb_colors' in customization_json else rgb_colors[0][0] 230 | original_g = customization_json['rgb_colors'][0][ 231 | 1] if customization_json and 'rgb_colors' in customization_json else rgb_colors[0][1] 232 | original_b = customization_json['rgb_colors'][0][ 233 | 2] if customization_json and 'rgb_colors' in customization_json else rgb_colors[0][2] 234 | 235 | brightness = 0.1 236 | while brightness <= 1: 237 | for i in range(self.strip_length): 238 | 239 | r = round(original_r*brightness) 240 | g = round(original_g*brightness) 241 | b = round(original_b*brightness) 242 | i = self.get_led(i) 243 | self.leds[i] = [ 244 | r if r <= 255 and r >= 0 else 255 if r > 255 else 0, 245 | g if g <= 255 and g >= 0 else 255 if g > 255 else 0, 246 | b if b <= 255 and b >= 0 else 255 if b > 255 else 0, 247 | ] 248 | self.write() 249 | brightness += 0.1 250 | 251 | while True: 252 | # await keyboard interrupt 253 | time.sleep(10) 254 | except KeyboardInterrupt: 255 | self.fadeout() 256 | 257 | import sys 258 | print() 259 | sys.exit(0) 260 | 261 | def rainbow_animation(self, 262 | loop_limit=None, 263 | brightness=1, 264 | duration_ms=1000, 265 | pause_ms=None, 266 | customization_json={} 267 | ): 268 | 269 | RainbowAnimation( 270 | led_strip=self, 271 | loop_limit=customization_json['loop_limit'] if customization_json and 'loop_limit' in customization_json else loop_limit, 272 | brightness=customization_json['brightness'] if customization_json and 'brightness' in customization_json else brightness, 273 | duration_ms=customization_json['duration_ms'] if customization_json and 'duration_ms' in customization_json else duration_ms, 274 | pause_ms=customization_json['pause_ms'] if customization_json and 'pause_ms' in customization_json else pause_ms 275 | ).glow() 276 | 277 | def beats(self, 278 | rgb_colors=None, 279 | brightness=1, 280 | brightness_fixed=False, 281 | max_height=1, 282 | loop_limit=None, 283 | duration_ms=200, 284 | pause_ms=300, 285 | start='start', 286 | num_random_colors=5, 287 | customization_json={} 288 | ): 289 | 290 | BeatsUpAndDown( 291 | led_strip=self, 292 | rgb_colors=customization_json['rgb_colors'] if customization_json and 'rgb_colors' in customization_json else rgb_colors, 293 | brightness=customization_json['brightness'] if customization_json and 'brightness' in customization_json else brightness, 294 | brightness_fixed=customization_json['brightness_fixed'] if customization_json and 'brightness_fixed' in customization_json else brightness_fixed, 295 | max_height=customization_json['max_height'] if customization_json and 'max_height' in customization_json else max_height, 296 | loop_limit=customization_json['loop_limit'] if customization_json and 'loop_limit' in customization_json else loop_limit, 297 | duration_ms=customization_json['duration_ms'] if customization_json and 'duration_ms' in customization_json else duration_ms, 298 | pause_ms=customization_json['pause_ms'] if customization_json and 'pause_ms' in customization_json else pause_ms, 299 | start=customization_json['start'] if customization_json and 'start' in customization_json else start, 300 | num_random_colors=customization_json['num_random_colors'] if customization_json and 'num_random_colors' in customization_json else num_random_colors 301 | ).glow() 302 | 303 | def moving_dot(self, 304 | rgb_colors=None, 305 | brightness=1, 306 | loop_limit=None, 307 | duration_ms=200, 308 | pause_a_ms=0, 309 | pause_b_ms=300, 310 | start='start', 311 | num_random_colors=5, 312 | customization_json={} 313 | ): 314 | 315 | MovingDot( 316 | led_strip=self, 317 | rgb_colors=customization_json['rgb_colors'] if customization_json and 'rgb_colors' in customization_json else rgb_colors, 318 | brightness=customization_json['brightness'] if customization_json and 'brightness' in customization_json else brightness, 319 | loop_limit=customization_json['loop_limit'] if customization_json and 'loop_limit' in customization_json else loop_limit, 320 | duration_ms=customization_json['duration_ms'] if customization_json and 'duration_ms' in customization_json else duration_ms, 321 | pause_a_ms=customization_json['pause_a_ms'] if customization_json and 'pause_a_ms' in customization_json else pause_a_ms, 322 | pause_b_ms=customization_json['pause_b_ms'] if customization_json and 'pause_b_ms' in customization_json else pause_b_ms, 323 | start=customization_json['start'] if customization_json and 'start' in customization_json else start, 324 | num_random_colors=customization_json['num_random_colors'] if customization_json and 'num_random_colors' in customization_json else num_random_colors 325 | ).glow() 326 | 327 | def light_up(self, 328 | rgb_colors=None, 329 | brightness=1, 330 | loop_limit=None, 331 | duration_ms=200, 332 | pause_ms=200, 333 | num_random_colors=5, 334 | sections='all', 335 | customization_json={} 336 | ): 337 | 338 | LightUp( 339 | led_strip=self, 340 | rgb_colors=customization_json['rgb_colors'] if customization_json and 'rgb_colors' in customization_json else rgb_colors, 341 | brightness=customization_json['brightness'] if customization_json and 'brightness' in customization_json else brightness, 342 | loop_limit=customization_json['loop_limit'] if customization_json and 'loop_limit' in customization_json else loop_limit, 343 | duration_ms=customization_json['duration_ms'] if customization_json and 'duration_ms' in customization_json else duration_ms, 344 | pause_ms=customization_json['pause_ms'] if customization_json and 'pause_ms' in customization_json else pause_ms, 345 | num_random_colors=customization_json['num_random_colors'] if customization_json and 'num_random_colors' in customization_json else num_random_colors, 346 | sections=customization_json['sections'] if customization_json and 'sections' in customization_json else sections 347 | ).glow() 348 | 349 | def transition(self, 350 | rgb_colors=None, 351 | brightness=1, 352 | loop_limit=None, 353 | duration_ms=200, 354 | pause_ms=200, 355 | num_random_colors=5, 356 | sections='all', 357 | customization_json={} 358 | ): 359 | 360 | Transition( 361 | led_strip=self, 362 | rgb_colors=customization_json['rgb_colors'] if customization_json and 'rgb_colors' in customization_json else rgb_colors, 363 | brightness=customization_json['brightness'] if customization_json and 'brightness' in customization_json else brightness, 364 | loop_limit=customization_json['loop_limit'] if customization_json and 'loop_limit' in customization_json else loop_limit, 365 | duration_ms=customization_json['duration_ms'] if customization_json and 'duration_ms' in customization_json else duration_ms, 366 | pause_ms=customization_json['pause_ms'] if customization_json and 'pause_ms' in customization_json else pause_ms, 367 | num_random_colors=customization_json['num_random_colors'] if customization_json and 'num_random_colors' in customization_json else num_random_colors, 368 | sections=customization_json['sections'] if customization_json and 'sections' in customization_json else sections 369 | ).glow() 370 | 371 | 372 | if __name__ == "__main__": 373 | test = True 374 | target = None 375 | n = 60 376 | animation = 'rainbow_animation' 377 | customization = {} 378 | try: 379 | opts, args = getopt.getopt(sys.argv[1:], "ht:d:n:a:c:", [ 380 | "test=", "target=", "n=", "animation=", "customization="]) 381 | except getopt.GetoptError: 382 | print('neopixel_plus.py -t -d -n -a -c ') 383 | sys.exit(2) 384 | for opt, arg in opts: 385 | if opt in ("-t", "--test"): 386 | test = arg 387 | elif opt in ("-d", "--target"): 388 | target = arg 389 | elif opt in ("-n", "--n"): 390 | n = int(arg) 391 | elif opt in ("-a", "--animation"): 392 | animation = arg 393 | elif opt in ("-c", "--customization"): 394 | customization = eval(arg) 395 | 396 | if target: 397 | test = False 398 | 399 | # get parameters and function call from shell commands 400 | getattr(NeoPixel(test=test, target=target, n=n), animation)( 401 | customization_json=customization) 402 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="neopixel_plus", # Replace with your own username 8 | version="1.7", 9 | author="Marco", 10 | author_email=None, 11 | description="The NeoPixel library plus animations and terminal testing mode - so you can see how your LEDs would behave directly in the terminal, without any microcontroller.", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/glowingkitty/NeoPixelPlus", 15 | packages=setuptools.find_packages(), 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | ], 21 | python_requires='>=3.6', 22 | install_requires=[ 23 | 'colr', 24 | 'rpi_ws281x', 25 | 'adafruit-circuitpython-neopixel' 26 | ] 27 | ) 28 | --------------------------------------------------------------------------------