/ 3D_Printed_NeoPixel_Ring_Hair_Dress / code.py
code.py
1 # SPDX-FileCopyrightText: 2014 HerrRausB https://github.com/HerrRausB 2 # SPDX-FileCopyrightText: 2017 Mikey Sklar for Adafruit Industries 3 # 4 # SPDX-License-Identifier: LGPL-3.0-or-later 5 # 6 # 3D_Printed_NeoPixel_Ring_Hair_Dress.py 7 # 8 # this was ported to CircuitPython from the 'Gemma Hoop Animator' 9 # 10 # https://github.com/HerrRausB/GemmaHoopAnimator 11 # 12 # unless you # don't like the preset animations or find a 13 # major bug, you don't need tochange anything here 14 # 15 import time 16 17 import board 18 import neopixel 19 20 try: 21 import urandom as random # for v1.0 API support 22 except ImportError: 23 import random 24 from analogio import AnalogIn 25 26 # available actions 27 ACT_NOP = 0x00 # all leds off, do nothing 28 ACT_SIMPLE_RING = 0x01 # all leds on 29 ACT_CYCLING_RING_ACLK = 0x02 # anti clockwise cycling colors 30 ACT_CYCLING_RING_CLKW = 0x04 # clockwise cycling colors 31 ACT_WHEEL_ACLK = 0x08 # anti clockwise spinning wheel 32 ACT_WHEEL_CLKW = 0x10 # clockwise spinning wheel 33 ACT_SPARKLING_RING = 0x20 # sparkling effect 34 35 numpix = 16 # total number of NeoPixels 36 pixel_output = board.D0 # pin where NeoPixels are connected 37 analog_input = board.A0 # needed to seed the random generator 38 strip = neopixel.NeoPixel(pixel_output, numpix, 39 brightness=.3, auto_write=False) 40 41 # available color generation methods 42 COL_RANDOM = 0x40 # colors will be generated randomly 43 COL_SPECTRUM = 0x80 # colors will be set as cyclic spectral wipe 44 45 # specifiyng the action list 46 # the action's overall duration in milliseconds (be careful not 47 action_duration = 0 48 # to use values > 2^16-1 - roughly one minute :-) 49 50 action_and_color_gen = 1 # the color generation method 51 52 # the duration of each action step rsp. the delay of the main 53 action_step_duration = 2 54 # loop in milliseconds - thus, controls the action speed (be 55 # careful not to use values > 2^16-1 - roughly one minute :-) 56 57 color_granularity = 3 # controls the increment of the R, G, and B 58 # portions of the rsp. color. 1 means the increment is 0,1,2,3,..., 59 # 10 means the increment is 0,10,20,... don't use values > 255, and 60 # note that even values > 127 wouldn't make much sense... 61 62 # controls the speed of color changing independently from action 63 color_interval = 4 64 65 # general global variables 66 color = 0 67 color_timer = 0 68 action_timer = 0 69 action_step_timer = 0 70 color_idx = 0 71 curr_color_interval = 0 72 curr_action_step_duration = 0 73 curr_action_duration = 0 74 curr_action = 0 75 curr_color_gen = COL_RANDOM 76 idx = 0 77 offset = 0 78 number_of_actions = 31 79 curr_action_idx = 0 80 curr_color_granularity = 1 81 spectrum_part = 0 82 83 # defining the animation actions by simply initializing the array of actions 84 # this array variable must be called theactionlist !!! 85 # 86 # valid actions are: 87 # ACT_NOP simply do nothing and switch everything off 88 # ACT_SIMPLE_RING all leds on 89 # ACT_CYCLING_RING_ACLK anti clockwise cycling colors 90 # ACT_CYCLING_RING_CLKW clockwise cycling colors acording 91 # ACT_WHEEL_ACLK anti clockwise spinning wheel 92 # ACT_WHEEL_CLKW clockwise spinning wheel 93 # ACT_SPARKLING_RING sparkling effect 94 # 95 # valid color options are: 96 # COL_RANDOM colors will be selected randomly, which might 97 # be not very sufficient due to well known 98 # limitations of the random generation algorithm 99 # COL_SPECTRUM colors will be set as cyclic spectral wipe 100 # R -> G -> B -> R -> G -> B -> R -> ... 101 102 # action action name & action step color color change 103 # duration color generation method duration granularity interval 104 theactionlist = [ 105 [5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1], 106 [2, ACT_CYCLING_RING_CLKW | COL_RANDOM, 107 0.02, 1, 0.005], 108 [5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1], 109 [2, ACT_CYCLING_RING_ACLK | COL_RANDOM, 110 0.02, 1, 0.005], 111 [5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1], 112 [2.5, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 113 0.25, 20, 0.020], 114 [1, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 115 0.50, 1, 0.020], 116 [.750, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 117 0.075, 1, 0.020], 118 [.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 119 0.100, 1, 0.020], 120 [.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 121 0.125, 1, 0.020], 122 [.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 123 0.150, 1, 0.050], 124 [.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 125 0.175, 1, 0.100], 126 [.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 127 0.200, 1, 0.200], 128 [.750, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 129 0.225, 1, 0.250], 130 [1, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 131 0.250, 1, 0.350], 132 [30, ACT_SIMPLE_RING | COL_SPECTRUM, 133 0.050, 1, 0.010], 134 [2.5, ACT_WHEEL_ACLK | COL_SPECTRUM, 135 0.010, 1, 0.010], 136 [2.5, ACT_WHEEL_ACLK | COL_SPECTRUM, 137 0.015, 1, 0.020], 138 [2, ACT_WHEEL_ACLK | COL_SPECTRUM, 139 0.025, 1, 0.030], 140 [1, ACT_WHEEL_ACLK | COL_SPECTRUM, 141 0.050, 1, 0.040], 142 [1, ACT_WHEEL_ACLK | COL_SPECTRUM, 143 0.075, 1, 0.040], 144 [1, ACT_WHEEL_ACLK | COL_SPECTRUM, 145 0.100, 1, 0.050], 146 [.500, ACT_WHEEL_ACLK | COL_SPECTRUM, 147 0.125, 1, 0.060], 148 [.500, ACT_WHEEL_CLKW | COL_SPECTRUM, 149 0.125, 5, 0.050], 150 [1, ACT_WHEEL_CLKW | COL_SPECTRUM, 151 0.100, 10, 0.040], 152 [1.5, ACT_WHEEL_CLKW | COL_SPECTRUM, 153 0.075, 15, 0.030], 154 [2, ACT_WHEEL_CLKW | COL_SPECTRUM, 155 0.050, 20, 0.020], 156 [2.5, ACT_WHEEL_CLKW | COL_SPECTRUM, 157 0.025, 25, 0.010], 158 [3, ACT_WHEEL_CLKW | COL_SPECTRUM, 159 0.010, 30, 0.005], 160 [5, ACT_SPARKLING_RING | COL_RANDOM, 0.010, 25, 1], 161 [5, ACT_NOP, 0, 0, 0] 162 ] 163 164 # pylint: disable=global-statement 165 def nextspectrumcolor(): 166 global spectrum_part, color_idx, curr_color_granularity, color 167 168 # spectral wipe from green to red 169 if spectrum_part == 2: 170 color = (color_idx, 0, 255-color_idx) 171 color_idx += curr_color_granularity 172 if color_idx > 255: 173 spectrum_part = 0 174 color_idx = 0 175 176 # spectral wipe from blue to green 177 elif spectrum_part == 1: 178 color = (0, 255 - color_idx, color_idx) 179 color_idx += curr_color_granularity 180 if color_idx > 255: 181 spectrum_part = 2 182 color_idx = 0 183 184 # spectral wipe from red to blue 185 elif spectrum_part == 0: 186 color = (255 - color_idx, color_idx, 0) 187 color_idx += curr_color_granularity 188 if color_idx > 255: 189 spectrum_part = 1 190 color_idx = 0 191 192 193 def nextrandomcolor(): 194 global color 195 196 # granularity = 1 --> [0 .. 255] * 1 --> 0,1,2,3 ... 255 197 # granularity = 10 --> [0 .. 25] * 10 --> 0,10,20,30 ... 250 198 # granularity = 100 --> [0 .. 2] * 100 --> 0,100, 200 (boaring...) 199 random_red = random.randint(0, int(256 / curr_color_granularity)) 200 random_red *= curr_color_granularity 201 202 random_green = random.randint(0, int(256 / curr_color_granularity)) 203 random_green *= curr_color_granularity 204 205 random_blue = random.randint(0, int(256 / curr_color_granularity)) 206 random_blue *= curr_color_granularity 207 208 color = (random_red, random_green, random_blue) 209 210 211 def nextcolor(): 212 # save some RAM for more animation actions 213 if curr_color_gen & COL_RANDOM: 214 nextrandomcolor() 215 else: 216 nextspectrumcolor() 217 218 219 def setup(): 220 # fingers corssed, the seeding makes sense to really get random colors... 221 apin = AnalogIn(analog_input) 222 random.seed(apin.value) 223 apin.deinit() 224 225 # let's go! 226 nextcolor() 227 strip.write() 228 229 230 setup() 231 232 while True: # Loop forever... 233 234 # do we need to load the next action? 235 if (time.monotonic() - action_timer) > curr_action_duration: 236 current_action = theactionlist[curr_action_idx] 237 238 curr_action_duration = current_action[action_duration] 239 curr_action = current_action[action_and_color_gen] & 0x3F 240 curr_action_step_duration = current_action[action_step_duration] 241 curr_color_gen = current_action[action_and_color_gen] & 0xC0 242 curr_color_granularity = current_action[color_granularity] 243 curr_color_interval = current_action[color_interval] 244 curr_action_idx += 1 245 246 # take care to rotate the action list! 247 curr_action_idx %= number_of_actions 248 action_timer = time.monotonic() 249 250 # do we need to change to the next color? 251 if (time.monotonic() - color_timer) > curr_color_interval: 252 nextcolor() 253 color_timer = time.monotonic() 254 255 # do we need to step up the current action? 256 if (time.monotonic() - action_step_timer) > curr_action_step_duration: 257 258 if curr_action: 259 260 is_act_cycling = (ACT_CYCLING_RING_ACLK or ACT_CYCLING_RING_CLKW) 261 262 if curr_action == ACT_NOP: 263 # rather trivial even tho this will be repeated as long as the 264 # NOP continues - i could have prevented it from repeating 265 # unnecessarily, but that would mean more code and less 266 # space for more actions within the animation 267 for i in range(0, numpix): 268 strip[i] = (0, 0, 0) 269 270 elif curr_action == ACT_SIMPLE_RING: 271 # even more trivial - just set the new color, if there is one 272 for i in range(0, numpix): 273 strip[i] = color 274 275 elif curr_action == is_act_cycling: 276 # spin the ring clockwise or anti clockwise 277 if curr_action == ACT_CYCLING_RING_ACLK: 278 idx += 1 279 else: 280 idx -= 1 281 282 # prevent overflows or underflows 283 idx %= numpix 284 285 # set the new color, if there is one 286 strip[idx] = color 287 288 elif curr_action == ACT_WHEEL_ACLK or ACT_WHEEL_CLKW: 289 # switch on / off the appropriate pixels according to 290 # the current offset 291 for idx in range(0, numpix): 292 if ((offset + idx) & 7) < 2: 293 strip[idx] = color 294 else: 295 strip[idx] = (0, 0, 0) 296 297 # advance the offset and thus, spin the wheel 298 if curr_action == ACT_WHEEL_CLKW: 299 offset += 1 300 else: 301 offset -= 1 302 303 # prevent overflows or underflows 304 offset %= numpix 305 306 elif curr_action == ACT_SPARKLING_RING: 307 # switch current pixel off 308 strip[idx] = (0, 0, 0) 309 # pick a new pixel 310 idx = random.randint(0, numpix) 311 # set new pixel to the current color 312 strip[idx] = color 313 314 strip.write() 315 action_step_timer = time.monotonic()