-
Sierpinski Triangle
06/23/2019 at 03:59 • 0 commentsSeeing Sierpinski Triangle on Facebook inspired me to try it on my tiny OLED.
import machine, ssd1306, math, time from machine import Pin def toInt(a): return [int(a[0]), int(a[1])] def toMid(a, b): return [(b[0]+a[0])/2, (b[1]+a[1])/2] class Sierpinski3: isWemosD1Mini = True xMax = 64 if isWemosD1Mini else 128 yMax = 48 if isWemosD1Mini else 64 sclPin = 5 if isWemosD1Mini else 18 sdaPin = 4 if isWemosD1Mini else 21 x0 = xMax / 2 y0 = yMax / 2 def oledSetup(self): i2c = machine.I2C(scl=Pin(self.sclPin), sda=Pin(self.sdaPin)) return ssd1306.SSD1306_I2C(self.xMax, self.yMax, i2c) def plot(self, oled, a, b, c): p = toInt(a) q = toInt(b) r = toInt(c) oled.line(p[0], self.yMax-1-p[1], q[0], self.yMax-1-q[1], 1) oled.line(q[0], self.yMax-1-q[1], r[0], self.yMax-1-r[1], 1) oled.line(r[0], self.yMax-1-r[1], p[0], self.yMax-1-p[1], 1) def fillTriangle(self, oled, bottomLeft, width): if width>8: bottomRight = [bottomLeft[0] + width, bottomLeft[1]] half = width / 2 y = bottomLeft[1] + math.sqrt(width**2 - half**2) top = [bottomLeft[0] + width/2, y] mid1 = toMid(bottomLeft, top) mid2 = toMid(bottomRight, top) mid3 = toMid(bottomLeft, bottomRight) self.plot(oled, mid1, mid2, mid3) self.fillTriangle(oled, bottomLeft, half) # bottom left triangle self.fillTriangle(oled, mid3, half) # bottom right triangle self.fillTriangle(oled, mid1, half) # top triangle def drawFrame(self, oled, half): oled.fill(0) width = half * 2 bottomLeft = [self.x0 - half, self.y0 - half] bottomRight = [self.x0 + half, bottomLeft[1]] top = [self.x0, bottomLeft[1] + math.sqrt(width ** 2 - half ** 2)] # Draw the first one special self.plot(oled, bottomLeft, bottomRight, top) # Draw the remaining recursively self.fillTriangle(oled, bottomLeft, width) oled.show() def main(self): oled = self.oledSetup() while True: slop = 5 for half in range(0, self.y0+slop, 3): self.drawFrame(oled, half) time.sleep(0.2) for half in range(self.y0+slop-3,0,-3): self.drawFrame(oled, half) instance = Sierpinski3() instance.main()
-
IO.AdaFruit.com Publish & Subscribe
06/02/2019 at 05:34 • 0 commentsLearning how to publish and subscribe on io.AdaFruit.com from Michael Teachman.
https://github.com/miketeachman/micropython-adafruit-mqtt-esp8266
-
Chord Animated
05/20/2019 at 00:41 • 0 commentsEven on a Wemos D1 Mini (ESP8266), MicroPython is amazingly fast!
# Chord Animated by Hari Wiguna, 2019 # ssd1306 library by AdaFruit import machine, ssd1306, math, urandom nFrames = 36 xMax = 64 yMax = 48 x0 = xMax / 2 y0 = yMax / 2 i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4)) oled = ssd1306.SSD1306_I2C(xMax, yMax, i2c) def randint(min, max): # ESP8266 does not have randint. Remove this if you're on the ESP32.' span = max - min + 1 div = 0x3fffffff // span offset = urandom.getrandbits(30) // div val = min + offset return val def draw_chord(f, xOff, yOff): node_count = 9 r_max = yMax * 2 r = f * r_max / nFrames rot = f * math.pi * 2 / nFrames x = [] y = [] for i in range(0, node_count): a = -rot + math.pi * 2 * i / node_count x.append(int(x0 + xOff + math.sin(a) * r)) y.append(int(y0 + yOff + math.cos(a) * r)) oled.fill(0) for i in range(0, node_count - 1): for j in range(i, node_count): oled.line(x[i], y[i], x[j], y[j], 1) oled.show() def zoom_in(xOff, yOff): for f in range(nFrames): draw_chord(f, xOff, yOff) def zoom_out(xOff, yOff): for f in range(nFrames-1, -1, -1): draw_chord(f, xOff, yOff) def main(): while True: xOff = randint(-x0, x0) yOff = randint(-y0, y0) zoom_in(xOff, yOff) zoom_out(xOff, yOff) main()
-
Flattest LED Cube
05/18/2019 at 13:29 • 0 commentsLearning how to do 3D to 2D projection from Daniel Shiffman of the coding train.Rotation matrix in WikiPedia
# Rotating cube on MicroPython by Hari Wiguna # Original code in P5 by Daniel Shiffman of The Coding Train # v5. Faster by using less points and computing sin cos once # v6. different rotation rate for x and z # v7. Multiple effects # v8. Rotate by small increments and put it back into the cube so we could chain effects import machine, ssd1306, math cube = [] ax = 0.2 ay = 0.2 az = 0.2 m = 70 # magnification def setup_oled(): i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4)) return ssd1306.SSD1306_I2C(64, 48, i2c) def make_cube(): n = 3 d = 1/n for z in range(n): for x in range(n): for y in range(n): cube.append([-0.5 + x*d, -0.5 + y*d, -0.5 + z*d]) def mat_mul(tx, a): out = [] for r in range(len(tx)): tot = 0 for c in range(len(tx[r])): tot += tx[r][c] * a[c] out.append(tot) return out def rotate_on_x(p3, angle): # returns 1x3 matrix sin = math.sin(angle) cos = math.cos(angle) rotx = [ [1, 0, 0], [0, cos, -sin], [0, sin, cos] ] return mat_mul(rotx, p3) def rotate_on_y(p3, angle): # returns 1x3 matrix sin = math.sin(angle) cos = math.cos(angle) roty = [ [cos, 0, sin], [0, 1, 0], [-sin, 0, cos] ] return mat_mul(roty, p3) def rotate_on_z(p3, angle): # returns 1x3 matrix sin = math.sin(angle) cos = math.cos(angle) rotz = [ [cos, -sin, 0], [sin, cos, 0], [0, 0, 1] ] return mat_mul(rotz, p3) def transform_3_to_2_with_perspective(p3): distance = 2 z = 1 / (distance - p3[2]) pmatrix = [[z, 0, 0], [0, z, 0]] return mat_mul(pmatrix, p3) def drawdot(oled, p3): p2 = transform_3_to_2_with_perspective(p3) oled.pixel(32 + int(p2[0] * m), 24 + int(p2[1] * m), 1) def spinx(oled, count): global ax, ay, az for n in range(count): oled.fill(0) for i in range(len(cube)): cube[i] = rotate_on_y(cube[i], ax) drawdot(oled, cube[i]) oled.show() def spiny(oled, count): global ax, ay, az for n in range(count): oled.fill(0) for i in range(len(cube)): cube[i] = rotate_on_y(cube[i], ay) drawdot(oled, cube[i]) oled.show() def spinz(oled, count): global ax, ay, az for n in range(count): oled.fill(0) for i in range(len(cube)): cube[i] = rotate_on_z(cube[i], az) drawdot(oled, cube[i]) oled.show() def tumble(oled, count): global ax, ay, az for n in range(count): oled.fill(0) for i in range(len(cube)): p3 = rotate_on_y(cube[i], ax) p3 = rotate_on_z(p3, az) drawdot(oled, p3) cube[i] = p3 oled.show() def projection(): oled = setup_oled() make_cube() while True: count = 10 spiny(oled, count) spinz(oled, count) tumble(oled, count)
-
OLED + MicroPython = ?
05/16/2019 at 01:39 • 0 commentsimport machine, ssd1306 import urandom def randint(min, max): # ESP8266 does not have randint. Remove this if you're on the ESP32.' span = max - min + 1 div = 0x3fffffff // span offset = urandom.getrandbits(30) // div val = min + offset return val class Vector: x = 0 y = 0 xd = 1 yd = 1 def __init__(self, x, y, xd, yd): self.x = x self.y = y self.xd = xd self.yd = yd class LearnPoint3: i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4)) oled = ssd1306.SSD1306_I2C(64, 48, i2c) n = 5 a = [] for i in range(0, n): a.append(Vector(randint(1, 64-2), randint(1, 48-2), -3, -1)) while True: oled.fill(0) for i in range(0, n): if a[i].x <= 0 or a[i].x >= 63: a[i].xd = -a[i].xd if a[i].y <= 0 or a[i].y >= 47: a[i].yd = -a[i].yd a[i].x += a[i].xd a[i].y += a[i].yd if i<(n-1): oled.line(a[i].x, a[i].y, a[i+1].x, a[i+1].y, 1) else: oled.line(a[i].x, a[i].y, a[0].x, a[0].y, 1) oled.show() LearnPoint3()
-
Chords
05/13/2019 at 02:44 • 0 commentsMy usual test program whenever I got something that can do graphics.
This is on a Wemos D1 Mini (ESP82666) with their 0.66" 64x48 pixel OLED at I2C 0x3C using GPIO 4 (SDA) and 5 (SCL)
import machine, ssd1306 import math class chord: i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4)) oled = ssd1306.SSD1306_I2C(64,48,i2c) oled.fill(0) nodeCount = 9 x0 = 64 / 2 y0 = 48 / 2 r = 48 / 2 x = [] y = [] for i in range(0, nodeCount): a = math.pi * 2 * i / nodeCount x.append(int(x0 + math.sin(a) * r)) y.append(int(y0 + math.cos(a) * r)) for i in range(0, nodeCount - 1): for j in range(i, nodeCount): oled.line(x[i], y[i], x[j], y[j], 1) oled.show()
SSD1306 OLED Library is no longer supported by AdaFruit, but it worked for me.
-
Simon Says
05/05/2019 at 01:30 • 0 comments# ======================== # SIMON SAYS # by Hari Wiguna, 2019 # ======================== from machine import Pin, TouchPad, PWM, ADC import time import urandom as random # -- Preferences -- led_pins = [32, 33, 25, 26] # EZ-SBC: [32, 33, 25, 26], Lolin: [26, 25, 33, 32] touch_pad_pins = [15, 14, 13, 12] # EZ-SBC: [15, 14, 13, 12], Lolin: [27, 14, 12, 13] speaker_pin = Pin(27) volume_level = 20 frequencies = [209, 252, 310, 415] # -- UI -- leds = [] touch_pads = [] untouched_values = [] on = 0 off = 1 # -- Game Globals -- simon = [] def set_led(index, on_or_off): leds[index].value(on_or_off) def set_all_leds(on_or_off): for i in range(4): set_led(i, on_or_off) def setup_leds(): for pinNumber in led_pins: leds.append(Pin(pinNumber, Pin.OUT)) print("led pins are {}".format(leds)) def setup_touch_pads(): for touch_pad_pin_number in touch_pad_pins: touch_pads.append(TouchPad(Pin(touch_pad_pin_number))) print("touch pads are {}".format(touch_pads)) def read_touch_pads(): touch_values = [] for index in range(4): touch_values.append(touch_pads[index].read()) return touch_values def calibrate_touch_pads(): n_times = 3 total = [0, 0, 0, 0] average = [0, 0, 0, 0] for i in range(n_times): touch_values = read_touch_pads() for index in range(4): total[index] += touch_values[index] for index in range(4): average[index] = total[index] / n_times global untouched_values untouched_values = average print("untouched_values are {}".format(untouched_values)) def indicate_game_over(): set_all_leds(on) play_game_over_tone() def play_tone(index): PWM(speaker_pin, freq=frequencies[index], duty=volume_level) def stop_tone(): p = PWM(speaker_pin) p.deinit() def play_game_over_tone(): p = PWM(speaker_pin, freq=100, duty=volume_level) time.sleep(1) p.deinit() def simon_says(): for num in simon: time.sleep(.5) # short gap between simon's sequence so same number won't run together. print("Simon says {}".format(num)) set_led(num, on) # turn on the one simon picked play_tone(num) # play appropriate tone for that number time.sleep(.5) # let human hear the tone and see his chosen led stop_tone() # enough of this cacophony set_led(num, off) # get ready for next simon sequence def wait_for_button_press(): while True: # Todo make this time out instead of waiting forever touch_values = read_touch_pads() for index in range(4): is_touched = touch_values[index] < untouched_values[index] * 3 / 4 if is_touched: print("Human pressed {}".format(index)) return index # Todo how to get out of nested loops? return -1 # -1 means human failed to press a button in time. def human_says(): for num in simon: # Loop through the current sequence length print("Simon expects {}".format(num)) button_pressed = wait_for_button_press() set_led(button_pressed, on) # User feedback, turn on the led human pressed (not necessarily correct) print("Simon said {}, Human said {}".format(num, button_pressed)) if button_pressed == num: print("Good job human!") play_tone(button_pressed) time.sleep(0.5) stop_tone() set_led(button_pressed, off) # Turn it back off to prepare for next simon sequence else: print("BAD HUMAN! GAME OVER!") return True # True = Game Over. return to get out of this function! return False # False = NOT Game over. ( human successfully repeated what Simon said) def init_random_seed(): a = ADC(Pin(34)) s = 0 while s == 0: s = a.read() time.sleep(0.1) random.seed(s) def play(): global simon simon = [] game_over = False while not game_over: new_number = random.randint(0, 3) print("\nSimon picked a new number: {}".format(new_number)) simon.append(new_number) print("Simon sequence is now: {}".format(simon)) simon_says() game_over = human_says() indicate_game_over() def wait_for_game_start(): set_all_leds(on) wait_for_button_press() set_all_leds(off) def loop(): while True: wait_for_game_start() play() def main(): print("main starts...") init_random_seed() setup_leds() setup_touch_pads() calibrate_touch_pads() loop() print("Main imported!!") main()