Close
0%
0%

Playing with MicroPython on the ESP

Instant gratification on the ESP8266 and ESP-32

Similar projects worth following
This is how I setup MicroPython on my ESP32 and Wemos D1 Mini. Let the fun begins!

Installation

  1. On PC: Install Windows Driver
    Plug in the ESP and open Device Manager to make sure that the PC has proper USB to serial driver to talk to your ESP32. 
  2. On PC: Install Python3
    MicroPython runs on the ESP, but to install it on the ESP, we need to first install Python on the PC.
  3. On PC: Download MicroPython
    Open DOS Prompt and CD to the Python scripts directory.  Mine is at:
    C:\Users\hwiguna\AppData\Local\Programs\Python\Python37-32\Scripts
    Download latest ESP 32 MicroPython bin file into this scripts directory.  Detailed instructions here.
  4. On DOS Prompt: Erase the ESP by typing
    esptool.py --chip esp32 --port COM24 erase_flash
    If it keeps on trying to connect like this: "Connecting........_____....._____.." press the boot button on the ESP.
  5. On DOS Prompt: Install Micropython on ESP32
    esptool.py --chip esp32 --port COM24 --baud 460800 write_flash -z 0x1000 "esp32-bluetooth.bin"
    or for the ESP8266 Wemos D1 Mini I used:
    esptool.py --port COM21 --baud 460800 write_flash --flash_size=detect -fm dio 0 esp8266-20170108-v1.8.7.bin

To start coding:

Use any text editor. I use PyCharm, but I do not like how it ties the COM port, so I use rShell to upload to ESP and still have REPL access to the ESP.

  • Open DOS prompt: CD to the source code directory, then start rshell:
    rshell 
  • Text should be yellow now.  rshell does not automatically connect, so enter:
    connect serial COM24
  • Sync our local directory onto the ESP:
    rsync simon /pyboard
    Yes, the ESP32 is referred to as /pyboard.  simon is PC directory to sync.
    Other useful rshell commands:
    • ls /pyboard
    • cp pcfile /pyboard
  • Once synced, type repl, CTRL-D to reboot the ESP and run main.py, or use the repl as usual.  CTRL-X to exit repl back to rshell.

if you get unable to find board '' <-- it's because you have not invoke the connect serial COM24 command yet.

Tools

(in no particular order)

Ampy by AdaFruit

Rshell by Dave Hylands

uPyCraft by DFRobot

PyCharm by JetBrains

Visual Studio (Community) by Microsoft

Visual Studio Code by Microsoft

Thonny by Aivar Annamaa

ESPlorer by ???

uPyLoader by BetaRavener

Libraries

SSD1306 OLED Library is no longer supported by AdaFruit, but it worked for me.

Wemos D1 Mini Matrix LED shield driver for micropython by mactijn

Python_LCD by dhylands

io.AdaFruit.com via MQTT by Mike Teachman

  • Sierpinski Triangle

    Hari Wiguna06/23/2019 at 03:59 0 comments

    Seeing 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

    Hari Wiguna06/02/2019 at 05:34 0 comments

    Learning how to publish and subscribe on io.AdaFruit.com from Michael Teachman.

    https://github.com/miketeachman/micropython-adafruit-mqtt-esp8266

  • Chord Animated

    Hari Wiguna05/20/2019 at 00:41 0 comments

     Even 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

    Hari Wiguna05/18/2019 at 13:29 0 comments

    Learning 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 = ?

    Hari Wiguna05/16/2019 at 01:39 0 comments

    import 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

    Hari Wiguna05/13/2019 at 02:44 0 comments

    My 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

    Hari Wiguna05/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()
    ...
    Read more »

View all 7 project logs

Enjoy this project?

Share

Discussions

Does this project spark your interest?

Become a member to follow this project and never miss any updates