def setUp(self): #logging.basicConfig(level=logging.INFO) #random.seed("WSlice") self.ws = ws = WS2812(1, 64) for i in range(len(ws)): ws[i] = (i, 2 * i, 3 * i) self.p = Percolator(ws)
def setUp(self): #logging.basicConfig(level=logging.INFO) #random.seed("WSlice") self.ws = ws = WS2812(1,64) for i in range(len(ws)): ws[i] = (i, 2*i, 3*i) self.p = Percolator(ws)
def __init__(self, write_fun=None, config={}): self.config = config if write_fun is None: @coroutine def write(self, *args): print(*args) self.write = write else: self.write = write_fun self.act_led = pyb.LED(3) self.err_led = pyb.LED(1) self.pkt_led = pyb.LED(2) self.pots = [0] * 4 self.percolator = \ Percolator(WS2812(spi_bus=config['leds'].get('spi'), led_count=config['leds'].get('qty'))) self.percolator.bingo = self.bingo self.ws_rings = WS2812(2, 2 * 7 + 45) self.ring_lights = Lights(self.ws_rings) self.feed_rollers = [ Jewel7(lights=self.ring_lights[0:7]), Jewel7(lights=self.ring_lights[7:14]) ] self.rr = RingRamp(lights=self.ring_lights[14:], circumference=60, \ bottom=7, \ g=-40.0, ball_check_fun = self.ball_check) # self.zap_balls = False self.set_brightness(31)
def __init__(self, write_fun=None, config={}): self.config = config if write_fun is None: @coroutine def write(self, *args): print(*args) self.write = write else: self.write = write_fun self.act_led = pyb.LED(3) self.err_led = pyb.LED(1) self.pkt_led = pyb.LED(2) self.pots = [0] * 4 self.percolator = \ Percolator(WS2812(spi_bus=config['leds'].get('spi'), led_count=config['leds'].get('qty'))) self.percolator.bingo = self.bingo self.ws_rings = WS2812(2, 2*7 + 45) self.ring_lights = Lights(self.ws_rings) self.feed_rollers = [Jewel7(lights=self.ring_lights[0:7]), Jewel7(lights=self.ring_lights[7:14])] self.rr = RingRamp(lights=self.ring_lights[14:], circumference=60, \ bottom=7, \ g=-40.0, ball_check_fun = self.ball_check) # self.zap_balls = False self.set_brightness(31)
class Lightshow: def __init__(self, write_fun=None, config={}): self.config = config if write_fun is None: @coroutine def write(self, *args): print(*args) self.write = write else: self.write = write_fun self.act_led = pyb.LED(3) self.err_led = pyb.LED(1) self.pkt_led = pyb.LED(2) self.pots = [0] * 4 self.percolator = \ Percolator(WS2812(spi_bus=config['leds'].get('spi'), led_count=config['leds'].get('qty'))) self.percolator.bingo = self.bingo self.ws_rings = WS2812(2, 2 * 7 + 45) self.ring_lights = Lights(self.ws_rings) self.feed_rollers = [ Jewel7(lights=self.ring_lights[0:7]), Jewel7(lights=self.ring_lights[7:14]) ] self.rr = RingRamp(lights=self.ring_lights[14:], circumference=60, \ bottom=7, \ g=-40.0, ball_check_fun = self.ball_check) # self.zap_balls = False self.set_brightness(31) def set_brightness(self, v): self.brightness = self.rr.brightness = self.percolator.brightness = v self.feed_rollers[0].brightness = self.feed_rollers[1].brightness = v @coroutine def flash_LED(self, led, dur=1): led.on() yield from sleep(dur) led.off() @coroutine def fuzzle(self, quit_name): # For testing while not getattr(self, quit_name): #yield from self.flash_LED(self.err_led) # More queue-costly but let's us proceed without waiting for photons yield self.flash_LED( self.err_led) # let a coro do it so we can proceed yield from sleep(100) assert getattr(self, quit_name) setattr(self, quit_name, max(getattr(self, quit_name) - 1, 0)) @coroutine def handle_pots(self, b): for i in range(4): self.pots[i] = big_endian_int(b[2 * i:2 * (i + 1)]) yield from self.show_pots() @coroutine def show_pots(self): s = ', '.join("%d" % v for v in self.pots) yield from self.supertitle("Pots: " + s) @coroutine def supertitle(self, text): yield from self.write(b'\x1b[s\x1b[1;1H\x1b[2K') yield from self.write(text) yield from self.write('\x1b[u') @coroutine def perk(self, cli, cmd, rol): sdelay, _, scolor = str(rol, 'ASCII').partition(' ') try: delay = int(sdelay) except: delay = 100 try: color = eval(scolor) except: color = bytes((8, 0, 0)) if not hasattr(self.percolator, 'perk_quit'): self.percolator.perk_quit = 0 yield self.percolator.perk(delay, color) # Launch this and return def ball_check(self, ball, θ, ω): # Checks a Ball and possibly affects it # Return a list of balls to replace it # (e.g. just [ball] to make no changes) rv = [] # if self.zap_balls: # ball.zap = True # rv.append(ball) # if θ > π: # θ -= 2 * π # elif θ <= -2.408554 <= ball.θ: # and ball.ω >= 0: if θ <= -2.408554 <= ball.θ: # and ball.ω >= 0: # crossed off the top of the "C" # ball.zap = True # rv.append(ball) #print("θ=%f, ω=%f" % (θ, ω )) #print("zapped %r" % ball) self.loop.call_soon(self.perk_and_roll(100, ball.color)) #elif θ < 0.0 < ball.θ: # and ball.ω >= 0: #elif θ < 0.0 < ball.θ or θ > 0.0 > ball.θ : # and ball.ω >= 0: elif θ < 0.0 < ball.θ or \ θ > 0.0 > ball.θ and max(abs(θ), abs(ball.θ)) < 1.5: # and ball.ω >= 0: # Crossed the centerline of the drive rollers #print("rollered %r" % ball) #ball.ω = max(ball.ω , 2.08) # # Can't omit this ball, have to zap it for correct rendering # ball.zap = True # rv.append(ball) # Grind this ball up into primary colors color = list(iter(ball.color)) #Below does not grind up fused primaries of same color: #colors = list(v for v in [(color[0], 0, 0), # (0, color[1], 0), # (0, 0, color[2])] # if sum(v)) t = stoichiometric = self.percolator.stoichiometric stoi_primaries = [(t[0], 0, 0), (0, t[1], 0), (0, 0, t[2])] colors = [] for i in range(3): while color[i] >= stoichiometric[i]: colors.append(stoi_primaries[i]) color[i] -= stoichiometric[i] if color[i]: t = [0, 0, 0] t[i] = color[i] colors.append(tuple(t)) assert colors, colors # At least one for c in colors: rv.append(Ball(θ=0.1, ω=rand.gauss(4.5, 0.2), color=c)) #print(rv) else: rv.append(ball) return rv @coroutine def perk_and_roll(self, speed, color, i=None): out_color = yield from self.percolator.perk(speed, color, i) if out_color is not None: ball = Ball(θ=2 * π * -7 / 60, ω=0.3, color=out_color) self.rr.balls.append(ball) #else: # print("perk yielded a None") @coroutine def bingo(self): stars = list(range(7, 63, 7)) p = self.percolator lattice = p.lattice rand.shuffle(stars) for i in stars: yield from sleep(200) color = tuple(lattice[i]) p.set_color_of(i, (0, 0, 0)) i = self.percolator.down(i, rng() & 1) yield self.perk_and_roll(100, color, i) @coroutine def spin_feed_rollers(self): lower, upper = self.feed_rollers lower.center[2] = upper.center[2] = 1 v = 1.0 for i in range(len(lower.gear)): lower.gear[i] = [v, 0, 0] upper.gear[(3 - i) % len(lower.gear)] = [0, v, 0] v *= 0.3 while True: #time = self.loop.time #then = time() lower.gear.cw() upper.gear.ccw() lower.render() upper.render() #now = time() #t = 20 - (now - then) #then = now #yield from sleep(t) yield from sleep(20) @coroutine def manage_brightness(self): a = pyb.ADC(pyb.Pin('Y12')) amb = a.read() / 4096 while True: v = a.read() amb = 0.95 * amb + 0.05 * v / 4096 bv = 254 * amb + 1 self.set_brightness(bv) yield from sleep(123) @coroutine def play(self, cli, cmd, rol): yield self.percolator.play() @coroutine def stop(self, cli, cmd, rol): self.percolator.play_on = False yield @coroutine def master(self): self.loop = yield GetRunningLoop(None) yield self.manage_brightness() yield self.percolator.keep_leds_current(10) for i in range(7, 63, 7): self.percolator.set_color_of(i, self.percolator.stoichiometric) yield self.percolator.bingo() yield self.rr.integrate_continuously() yield self.spin_feed_rollers() while True: yield from self.flash_LED(self.act_led) yield from sleep(1000) def run(self): loop = get_event_loop() loop.run_until_complete(self.master())
class Lightshow: def __init__(self, write_fun=None, config={}): self.config = config if write_fun is None: @coroutine def write(self, *args): print(*args) self.write = write else: self.write = write_fun self.act_led = pyb.LED(3) self.err_led = pyb.LED(1) self.pkt_led = pyb.LED(2) self.pots = [0] * 4 self.percolator = \ Percolator(WS2812(spi_bus=config['leds'].get('spi'), led_count=config['leds'].get('qty'))) self.percolator.bingo = self.bingo self.ws_rings = WS2812(2, 2*7 + 45) self.ring_lights = Lights(self.ws_rings) self.feed_rollers = [Jewel7(lights=self.ring_lights[0:7]), Jewel7(lights=self.ring_lights[7:14])] self.rr = RingRamp(lights=self.ring_lights[14:], circumference=60, \ bottom=7, \ g=-40.0, ball_check_fun = self.ball_check) # self.zap_balls = False self.set_brightness(31) def set_brightness(self, v): self.brightness = self.rr.brightness = self.percolator.brightness = v self.feed_rollers[0].brightness = self.feed_rollers[1].brightness = v @coroutine def flash_LED(self, led, dur=1): led.on() yield from sleep(dur) led.off() @coroutine def fuzzle(self, quit_name): # For testing while not getattr(self, quit_name): #yield from self.flash_LED(self.err_led) # More queue-costly but let's us proceed without waiting for photons yield self.flash_LED(self.err_led) # let a coro do it so we can proceed yield from sleep(100) assert getattr(self, quit_name) setattr(self, quit_name, max(getattr(self, quit_name) - 1, 0)) @coroutine def handle_pots(self, b): for i in range(4): self.pots[i] = big_endian_int(b[2*i:2*(i+1)]) yield from self.show_pots() @coroutine def show_pots(self): s = ', '.join("%d" % v for v in self.pots) yield from self.supertitle("Pots: " + s) @coroutine def supertitle(self, text): yield from self.write(b'\x1b[s\x1b[1;1H\x1b[2K') yield from self.write(text) yield from self.write('\x1b[u') @coroutine def perk(self, cli, cmd, rol): sdelay, _, scolor = str(rol, 'ASCII').partition(' ') try: delay = int(sdelay) except: delay = 100 try: color = eval(scolor) except: color = bytes((8,0,0)) if not hasattr(self.percolator, 'perk_quit'): self.percolator.perk_quit = 0 yield self.percolator.perk(delay, color) # Launch this and return def ball_check(self, ball, θ, ω ): # Checks a Ball and possibly affects it # Return a list of balls to replace it # (e.g. just [ball] to make no changes) rv = [] # if self.zap_balls: # ball.zap = True # rv.append(ball) # if θ > π: # θ -= 2 * π # elif θ <= -2.408554 <= ball.θ: # and ball.ω >= 0: if θ <= -2.408554 <= ball.θ: # and ball.ω >= 0: # crossed off the top of the "C" # ball.zap = True # rv.append(ball) #print("θ=%f, ω=%f" % (θ, ω )) #print("zapped %r" % ball) self.loop.call_soon(self.perk_and_roll(100, ball.color)) #elif θ < 0.0 < ball.θ: # and ball.ω >= 0: #elif θ < 0.0 < ball.θ or θ > 0.0 > ball.θ : # and ball.ω >= 0: elif θ < 0.0 < ball.θ or \ θ > 0.0 > ball.θ and max(abs(θ), abs(ball.θ)) < 1.5: # and ball.ω >= 0: # Crossed the centerline of the drive rollers #print("rollered %r" % ball) #ball.ω = max(ball.ω , 2.08) # # Can't omit this ball, have to zap it for correct rendering # ball.zap = True # rv.append(ball) # Grind this ball up into primary colors color = list(iter(ball.color)) #Below does not grind up fused primaries of same color: #colors = list(v for v in [(color[0], 0, 0), # (0, color[1], 0), # (0, 0, color[2])] # if sum(v)) t = stoichiometric = self.percolator.stoichiometric stoi_primaries = [(t[0], 0, 0), (0, t[1], 0), (0, 0, t[2])] colors = [] for i in range(3): while color[i] >= stoichiometric[i]: colors.append(stoi_primaries[i]) color[i] -= stoichiometric[i] if color[i]: t = [0, 0, 0] t[i] = color[i] colors.append(tuple(t)) assert colors, colors # At least one for c in colors: rv.append(Ball(θ=0.1, ω =rand.gauss(4.5, 0.2), color=c)) #print(rv) else: rv.append(ball) return rv @coroutine def perk_and_roll(self, speed, color, i=None): out_color = yield from self.percolator.perk(speed, color, i) if out_color is not None: ball = Ball(θ = 2*π * -7/60, ω = 0.3, color=out_color) self.rr.balls.append(ball) #else: # print("perk yielded a None") @coroutine def bingo(self): stars = list(range(7, 63, 7)) p = self.percolator lattice = p.lattice rand.shuffle(stars) for i in stars: yield from sleep(200) color = tuple(lattice[i]) p.set_color_of(i, (0,0,0)) i = self.percolator.down(i, rng()&1) yield self.perk_and_roll(100, color, i) @coroutine def spin_feed_rollers(self): lower, upper = self.feed_rollers lower.center[2] = upper.center[2] = 1 v = 1.0 for i in range(len(lower.gear)): lower.gear[i] = [v, 0, 0] upper.gear[(3-i)%len(lower.gear)] = [0, v, 0] v *= 0.3 while True: #time = self.loop.time #then = time() lower.gear.cw() upper.gear.ccw() lower.render() upper.render() #now = time() #t = 20 - (now - then) #then = now #yield from sleep(t) yield from sleep(20) @coroutine def manage_brightness(self): a = pyb.ADC(pyb.Pin('Y12')) amb = a.read()/4096 while True: v = a.read() amb = 0.95*amb + 0.05*v/4096 bv = 254*amb + 1 self.set_brightness(bv) yield from sleep(123) @coroutine def play(self, cli, cmd, rol): yield self.percolator.play() @coroutine def stop(self, cli, cmd, rol): self.percolator.play_on = False yield @coroutine def master(self): self.loop = yield GetRunningLoop(None) yield self.manage_brightness() yield self.percolator.keep_leds_current(10) for i in range(7, 63, 7): self.percolator.set_color_of(i, self.percolator.stoichiometric) yield self.percolator.bingo() yield self.rr.integrate_continuously() yield self.spin_feed_rollers() while True: yield from self.flash_LED(self.act_led) yield from sleep(1000) def run(self): loop = get_event_loop() loop.run_until_complete(self.master())
class PercolatorTestCase(unittest.TestCase): def setUp(self): #logging.basicConfig(level=logging.INFO) #random.seed("WSlice") self.ws = ws = WS2812(1,64) for i in range(len(ws)): ws[i] = (i, 2*i, 3*i) self.p = Percolator(ws) def tearDown(self): self.ws = self.p = None gc.collect() def test_render_init(self): # A Percolator is initially in a state that renders all off self.p.render() self.assertTrue(all(sum(v)==0 for v in self.ws)) def test_render_time(self): # Rendering takes the expected amount of time n = 10 t0 = pyb.micros() for i in range(n): self.p.render() dt = pyb.elapsed_micros(t0) average_ms = dt / (n * 1000) print("%d renders average %f ms" % (n, average_ms), end='') self.assertTrue(average_ms < 15, "average render time %f ms" % (average_ms)) def test_render_at_index_0(self): # A Percolator can render itself to the backing LEDs lattice = self.p.lattice ref = (12, 34, 56) for i, v in enumerate(ref): lattice[0][i] = v self.p.render() self.assertEqual(tuple(self.ws[0]), ref) self.assertTrue(all(sum(v)==0 for v in self.ws[1:])) def test_render_at_index_0_various_brightness(self): # A Percolator can render itself to the backing LEDs with controlled brightness lattice = self.p.lattice color = (12, 34, 56) ref = [0] * 3 for brightness in (1.0, 0.5, 0.1, 0.01, 3/17, 1/254, 1/255, 1/256, 0.0): print(' %r' % brightness, end='') self.p.brightness = brightness for i, v in enumerate(color): lattice[0][i] = v ref[i] = round(brightness * v) self.p.render() self.assertEqual(tuple(self.ws[0]), tuple(ref)) self.assertTrue(all(sum(v)==0 for v in self.ws[1:])) def test_render_at_several_positions(self): # A Percolator can render itself to the backing LEDs lattice = self.p.lattice for k, g in enumerate(tg(64, 0)): for i, v in enumerate(g): lattice[k][i] = v self.p.render() for led, g in zip(self.ws, tg(64,0)): self.assertEqual(tuple(led), tuple(g))
class PercolatorTestCase(unittest.TestCase): def setUp(self): #logging.basicConfig(level=logging.INFO) #random.seed("WSlice") self.ws = ws = WS2812(1, 64) for i in range(len(ws)): ws[i] = (i, 2 * i, 3 * i) self.p = Percolator(ws) def tearDown(self): self.ws = self.p = None gc.collect() def test_render_init(self): # A Percolator is initially in a state that renders all off self.p.render() self.assertTrue(all(sum(v) == 0 for v in self.ws)) def test_render_time(self): # Rendering takes the expected amount of time n = 10 t0 = pyb.micros() for i in range(n): self.p.render() dt = pyb.elapsed_micros(t0) average_ms = dt / (n * 1000) print("%d renders average %f ms" % (n, average_ms), end='') self.assertTrue(average_ms < 15, "average render time %f ms" % (average_ms)) def test_render_at_index_0(self): # A Percolator can render itself to the backing LEDs lattice = self.p.lattice ref = (12, 34, 56) for i, v in enumerate(ref): lattice[0][i] = v self.p.render() self.assertEqual(tuple(self.ws[0]), ref) self.assertTrue(all(sum(v) == 0 for v in self.ws[1:])) def test_render_at_index_0_various_brightness(self): # A Percolator can render itself to the backing LEDs with controlled brightness lattice = self.p.lattice color = (12, 34, 56) ref = [0] * 3 for brightness in (1.0, 0.5, 0.1, 0.01, 3 / 17, 1 / 254, 1 / 255, 1 / 256, 0.0): print(' %r' % brightness, end='') self.p.brightness = brightness for i, v in enumerate(color): lattice[0][i] = v ref[i] = round(brightness * v) self.p.render() self.assertEqual(tuple(self.ws[0]), tuple(ref)) self.assertTrue(all(sum(v) == 0 for v in self.ws[1:])) def test_render_at_several_positions(self): # A Percolator can render itself to the backing LEDs lattice = self.p.lattice for k, g in enumerate(tg(64, 0)): for i, v in enumerate(g): lattice[k][i] = v self.p.render() for led, g in zip(self.ws, tg(64, 0)): self.assertEqual(tuple(led), tuple(g))