Sunday, January 31, 2021

Raspberry Pi Pico/MicroPython + 320x240 ILI9341 SPI Display, using jeffmer/micropython-ili9341 library

With MicroPython on Raspberry Pi Pico, this exercise show how to driver 320x240 ILI9341 SPI Display using jeffmer/micropython-ili9341 library (Micropython Driver for ILI9341 display).

Install library:

Visit https://github.com/jeffmer/micropython-ili9341, copy and save ili934xnew.py, glcdfont.py, tt14.py, tt24.py and tt32.py to Raspberry Pi Pico.

Connection:

I'm going to use the default pin assignment of SPI(0), enter the code in MicroPython REPL:

>>> import machine
>>> print(machine.SPI(0))
>>> print(machine.SPI(1))

Assign TFT pins as:




Example code:

mpyPico_ili9341.py

"""
Exercise on Raspberry Pi Pico/MicroPython
with 320x240 ILI9341 SPI Display
"""
from ili934xnew import ILI9341, color565
from machine import Pin, SPI
from micropython import const
import os
import glcdfont
import tt14
import tt24
import tt32
import time

SCR_WIDTH = const(320)
SCR_HEIGHT = const(240)
SCR_ROT = const(2)
CENTER_Y = int(SCR_WIDTH/2)
CENTER_X = int(SCR_HEIGHT/2)

print(os.uname())
TFT_CLK_PIN = const(6)
TFT_MOSI_PIN = const(7)
TFT_MISO_PIN = const(4)

TFT_CS_PIN = const(13)
TFT_RST_PIN = const(14)
TFT_DC_PIN = const(15)

fonts = [glcdfont,tt14,tt24,tt32]
text = 'Hello Raspberry Pi Pico/ili9341'

print(text)
print("fonts available:")
for f in fonts:
    print(f.__name__)

spi = SPI(
    0,
    baudrate=40000000,
    miso=Pin(TFT_MISO_PIN),
    mosi=Pin(TFT_MOSI_PIN),
    sck=Pin(TFT_CLK_PIN))
print(spi)

display = ILI9341(
    spi,
    cs=Pin(TFT_CS_PIN),
    dc=Pin(TFT_DC_PIN),
    rst=Pin(TFT_RST_PIN),
    w=SCR_WIDTH,
    h=SCR_HEIGHT,
    r=SCR_ROT)

display.erase()
display.set_pos(0,0)

for ff in fonts:
    display.set_font(ff)
    display.print(text)
    
display.set_font(tt24)
display.set_color(color565(255, 255, 0), color565(150, 150, 150))
display.print("\nThanks:")
display.print("https://github.com/jeffmer/micropython-ili9341")

time.sleep(1)

for i in range(170):
    display.scroll(1)
    time.sleep(0.01)
    
time.sleep(1)
for i in range(170):
    display.scroll(-1)
    time.sleep(0.01)
    
time.sleep(1)
for h in range(SCR_WIDTH):
    if h > SCR_HEIGHT:
        w = SCR_HEIGHT
    else:
        w = h
        
    display.fill_rectangle(0, 0, w, h, color565(0, 0, 255))
    time.sleep(0.01)

time.sleep(0.5)
display.erase()

# Helper function to draw a circle from a given position with a given radius
# This is an implementation of the midpoint circle algorithm,
# see https://en.wikipedia.org/wiki/Midpoint_circle_algorithm#C_example 
# for details
def draw_circle(xpos0, ypos0, rad, col=color565(255, 255, 255)):
    x = rad - 1
    y = 0
    dx = 1
    dy = 1
    err = dx - (rad << 1)
    while x >= y:
        display.pixel(xpos0 + x, ypos0 + y, col)
        display.pixel(xpos0 + y, ypos0 + x, col)
        display.pixel(xpos0 - y, ypos0 + x, col)
        display.pixel(xpos0 - x, ypos0 + y, col)
        display.pixel(xpos0 - x, ypos0 - y, col)
        display.pixel(xpos0 - y, ypos0 - x, col)
        display.pixel(xpos0 + y, ypos0 - x, col)
        display.pixel(xpos0 + x, ypos0 - y, col)
        if err <= 0:
            y += 1
            err += dy
            dy += 2
        if err > 0:
            x -= 1
            dx += 2
            err += dx - (rad << 1)
    
draw_circle(CENTER_X, CENTER_Y, 100)

display.set_pos(1,10)
display.print("helloraspberrypi.blogspot.com")

for c in range(99):
    draw_circle(CENTER_X, CENTER_Y, c, color565(255, 0, 0))
    
for c in range(98):
    draw_circle(CENTER_X, CENTER_Y, c, color565(0, 255, 0))
    
for c in range(97):
    draw_circle(CENTER_X, CENTER_Y, c, color565(0, 0, 255))

print("- bye-")



Updated@2021-02-27:


It's a utility font_to_py.py in the project, used to convert ttf to py can be used in this library.
This video show how I convery FreeSans font to py, and use on RPi Pico/MicroPython + ili9341 exercises.

Convert .ttf to .py
 

- visit jeffmer/micropython-ili9341 and download font_to_py.py.

Sample usage:
font_to_py.py FreeSans.ttf 23 freesans.py

To specify monospaced rendering issue:
font_to_py.py FreeSans.ttf 23 --fixed freesans.py

- search and download ttf from internet, FreeSans.ttf as a example.


- run font_to_py.py to convert font:
> python3 font_to_py.py FreeSans.ttf 48 freesans48.py

most possible you fail with error:
ModuleNotFoundError: No module named 'freetype'

- To install freetype, enter the command:
> pip3 install freetype.py

- run font_to_py.py to convert font again:
python3 font_to_py.py FreeSans.ttf 48 freesans48.py

and:
python3 font_to_py.py FreeSans.ttf 48 --fixed freesans48fixed.py

- copy freesans48.py and freesans48fixed.py to Raspberry Pi Pico

Done. you can try freesans48.py and freesans48fixed.py using my example MicroPython code on Raspberry Pi Pico.

mpyPico_ili9341_fonts.py
"""
Exercise on Raspberry Pi Pico/MicroPython
with 320x240 ILI9341 SPI Display
"""
from ili934xnew import ILI9341, color565
from machine import Pin, SPI
from micropython import const
import os
import glcdfont
import tt14
import tt24
import tt32
import utime

import freesans48
import freesans48fixed

SCR_WIDTH = const(320)
SCR_HEIGHT = const(240)
SCR_ROT = const(1)   #const(2)
CENTER_Y = int(SCR_WIDTH/2)
CENTER_X = int(SCR_HEIGHT/2)

print(os.uname())
TFT_CLK_PIN = const(6)
TFT_MOSI_PIN = const(7)
TFT_MISO_PIN = const(4)

TFT_CS_PIN = const(13)
TFT_RST_PIN = const(14)
TFT_DC_PIN = const(15)


spi = SPI(
    0,
    baudrate=40000000,
    miso=Pin(TFT_MISO_PIN),
    mosi=Pin(TFT_MOSI_PIN),
    sck=Pin(TFT_CLK_PIN))
print(spi)

display = ILI9341(
    spi,
    cs=Pin(TFT_CS_PIN),
    dc=Pin(TFT_DC_PIN),
    rst=Pin(TFT_RST_PIN),
    w=SCR_WIDTH,
    h=SCR_HEIGHT,
    r=SCR_ROT)

display.erase()
display.set_pos(0,0)

#demo freesans48
display.set_font(freesans48)
display.erase()
display.set_pos(0,0)
display.print("freesans48")
display.print("1234567890")
utime.sleep(1)
    
display.erase()
display.set_pos(0,0)
display.print("ABCDEFGHIJKLMNOPQRST")
utime.sleep(1)
display.erase()
display.set_pos(0,0)
display.print("UVWXYZ")
utime.sleep(1)
display.erase()
display.set_pos(0,0)
display.print("abcdefghijklmno")
utime.sleep(1)
display.erase()
display.set_pos(0,0)
display.print("pqrstuvwxyz")
utime.sleep(1) 

#demo freesans48fixed
display.erase()
display.set_pos(0,0)

display.set_font(freesans48fixed)

display.print("freesans48fixed")

utime.sleep(1)


for i in range(130):
    display.scroll(1)
    utime.sleep(0.01)
utime.sleep(1)
for i in range(130):
    display.scroll(-1)
    utime.sleep(0.01)
utime.sleep(1)


display.erase()
display.set_pos(0,0)
display.print("ABCDEFGHIJKLMNOPQ")
utime.sleep(1)
display.erase()
display.set_pos(0,0)
display.print("RSTUVWXYZ")
utime.sleep(1)
display.erase()
display.set_pos(0,0)
display.print("abcdefghijklmno")
utime.sleep(1)
display.erase()
display.set_pos(0,0)
display.print("pqrstuvwxyz")
utime.sleep(1)

display.erase()
display.set_pos(0,0)
display.print("1234567890")
utime.sleep(1)
display.erase()
display.set_pos(0,0)
display.print("!@#$%^&*()~_`-")
utime.sleep(1)
display.erase()
display.set_pos(0,0)
display.print("{}[]:;<>,.?/|")
utime.sleep(1)

print("- bye-")

Because the generated font files is too big (1xxx and 2xxx lines), I won't list it here. You can generate it by yourself, or try other fonts.


next example of Raspberry Pico Pico/MicroPython + 320x240 ILI9341 SPI Display:


Saturday, January 30, 2021

Raspberry Pi Pico/MicroPython: simple example of UART

MicroPython UART class implements the standard UART/USART duplex serial communications protocol. At the physical level it consists of 2 lines: RX and TX. 

To check the default setting of UART on Raspberry Pi Pico, enter the code in MicroPython REPL:

>>> import machine
>>> machine.UART(0)
>>> machine.UART(1)

It's found that the default GPIO assigned to UART(1) is tx=4, rx=5.

Mark a jump to short between GP4 (pin 6) and GP5 (pin 7).

Run the exercise code, mpyPico_uart.py
import os
import utime
import machine

#print sys info
print(os.uname())

#indicate program started visually
led_onboard = machine.Pin(25, machine.Pin.OUT)
led_onboard.value(0)     # onboard LED OFF for 0.5 sec
utime.sleep(0.5)
led_onboard.value(1)

#print uart info
uart = machine.UART(1)
print(uart)

uart.write("hello")
utime.sleep(0.1)
while uart.any():
    print(uart.read(1))

print()
print("- bye -")






Tried similar with Adafruit CircuitPython 6.2.0-beta.1 firmware installed, it report NotImplementedError: UART not yet supported.



Friday, January 29, 2021

Raspberry Pi Pico/CircuitPython + ST7789 SPI IPS LCD

With CircuitPython 6.2.0-beta.1 installed on Raspberry Pi Pico, this post show how to display on LCD display with ST7789 driver using adafruit_st7789/displayio library.

The display is a 1.14" 135x240 (RGB) IPS (SKU: MSP1141), with ST7789V driver/SPI interface, details refer to : LCDwiki - 1.14inch IPS Module.

Connection:

Library:

In this exercise, adafruit_st7789 and adafruit_display_text of Adafruit CircuitPython Library Bundle is needed.

Visit https://circuitpython.org/libraries, download the appropriate bundle for your version of CircuitPython.

Unzip the file, copy adafruit_st7789.mpy and adafruit_display_text folder to the lib folder on your CIRCUITPY drive.

Example code:

Copy below examples code to code.py on CIRCUITPY drive to run.

cpyPico_spiST7789.py
"""
Example of CircuitPython/RaspberryPi Pico
to display on 1.14" 135x240 (RGB) IPS screen
with ST7789 driver via SPI interface.

Connection between Pico and
the IPS screen, with ST7789 SPI interface.
3V3  - BLK (backlight, always on)
GP11 - CS
GP12 - DC
GP13 - RES
GP15 - SDA
GP14 - SCL
3V3  - VCC
GND  - GND
"""

import os
import board
import time
import terminalio
import displayio
import busio
from adafruit_display_text import label
#from adafruit_st7789 import ST7789
import adafruit_st7789

print("==============================")
print(os.uname())
print("Hello Raspberry Pi Pico/CircuitPython ST7789 SPI IPS Display")
print(adafruit_st7789.__name__ + " version: " + adafruit_st7789.__version__)
print()

# Release any resources currently in use for the displays
displayio.release_displays()

tft_cs = board.GP11
tft_dc = board.GP12
tft_res = board.GP13
spi_mosi = board.GP15
spi_clk = board.GP14

"""
classbusio.SPI(clock: microcontroller.Pin,
                MOSI: Optional[microcontroller.Pin] = None,
                MISO: Optional[microcontroller.Pin] = None)
"""
spi = busio.SPI(spi_clk, MOSI=spi_mosi)

display_bus = displayio.FourWire(
    spi, command=tft_dc, chip_select=tft_cs, reset=tft_res
)

#I get the parameters by guessing and trying
#display = ST7789(display_bus, width=135, height=240, rowstart=40, colstart=53)
display = adafruit_st7789.ST7789(display_bus,
                    width=135, height=240,
                    rowstart=40, colstart=53)

# Make the display context
splash = displayio.Group(max_size=10)
display.show(splash)

color_bitmap = displayio.Bitmap(135, 240, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x00FF00

bg_sprite = displayio.TileGrid(color_bitmap,
                               pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

# Draw a smaller inner rectangle
inner_bitmap = displayio.Bitmap(133, 238, 1)
inner_palette = displayio.Palette(1)
inner_palette[0] = 0x0000FF
inner_sprite = displayio.TileGrid(inner_bitmap,
                                  pixel_shader=inner_palette, x=1, y=1)
splash.append(inner_sprite)

# Draw a label
text_group1 = displayio.Group(max_size=10, scale=2, x=20, y=40)
text1 = "RPi Pico"
text_area1 = label.Label(terminalio.FONT, text=text1, color=0xFF0000)
text_group1.append(text_area1)  # Subgroup for text scaling
# Draw a label
text_group2 = displayio.Group(max_size=10, scale=1, x=20, y=60)
text2 = "CircuitPython"
text_area2 = label.Label(terminalio.FONT, text=text2, color=0xFFFFFF)
text_group2.append(text_area2)  # Subgroup for text scaling

# Draw a label
text_group3 = displayio.Group(max_size=10, scale=1, x=20, y=100)
text3 = adafruit_st7789.__name__
text_area3 = label.Label(terminalio.FONT, text=text3, color=0x0000000)
text_group3.append(text_area3)  # Subgroup for text scaling
# Draw a label
text_group4 = displayio.Group(max_size=10, scale=2, x=20, y=120)
text4 = adafruit_st7789.__version__
text_area4 = label.Label(terminalio.FONT, text=text4, color=0x000000)
text_group4.append(text_area4)  # Subgroup for text scaling

splash.append(text_group1)
splash.append(text_group2)
splash.append(text_group3)
splash.append(text_group4)

time.sleep(3.0)

rot = 0
while True:
    time.sleep(5.0)
    rot = rot + 90
    if (rot>=360):
        rot =0
    display.rotation = rot
    

cpyPico_spiST7789_bitmap.py
"""
Example of CircuitPython/Raspberry Pi Pico
to display on 1.14" 135x240 (RGB) IPS screen
with ST7789 driver via SPI interface.

Connection between Pico and
the IPS screen, with ST7789 SPI interface.
3V3  - BLK (backlight, always on)
GP11 - CS
GP12 - DC
GP13 - RES
GP15 - SDA
GP14 - SCL
3V3  - VCC
GND  - GND
"""

import os
import board
import time
import terminalio
import displayio
import busio
from adafruit_display_text import label
import adafruit_st7789

print("==============================")
print(os.uname())
print("Hello Raspberry Pi Pico/CircuitPython ST7789 SPI IPS Display")
print(adafruit_st7789.__name__ + " version: " + adafruit_st7789.__version__)
print()

# Release any resources currently in use for the displays
displayio.release_displays()

tft_cs = board.GP11
tft_dc = board.GP12
tft_res = board.GP13
spi_mosi = board.GP15
spi_clk = board.GP14

"""
classbusio.SPI(clock: microcontroller.Pin,
                MOSI: Optional[microcontroller.Pin] = None,
                MISO: Optional[microcontroller.Pin] = None)
"""
spi = busio.SPI(spi_clk, MOSI=spi_mosi)

display_bus = displayio.FourWire(
    spi, command=tft_dc, chip_select=tft_cs, reset=tft_res
)

display = adafruit_st7789.ST7789(display_bus,
                    width=135, height=240,
                    rowstart=40, colstart=53)
display.rotation = 270

group = displayio.Group(max_size=10)
display.show(group)

bitmap = displayio.Bitmap(240, 135, 135)

palette = displayio.Palette(135)
for p in range(135):
    palette[p] = (0x010000*p) + (0x0100*p) + p

for y in range(135):
    for x in range(240):
        bitmap[x,y] = y
        
tileGrid = displayio.TileGrid(bitmap, pixel_shader=palette, x=0, y=0)
group.append(tileGrid)

time.sleep(3.0)

while True:
    for p in range(135):
        palette[p] = p
    time.sleep(3.0)

    for p in range(135):
        palette[p] = 0x0100 * p
    time.sleep(3.0)

    for p in range(135):
        palette[p] = 0x010000 * p
    time.sleep(3.0)


Actually, this post repeat my previous exercise of another blog: ESP32-S2/CircuitPython: display on IPS screen (ST7789/SPI) using adafruit_st7789/displayio lib.


Related:

Thursday, January 28, 2021

Install CircuitPython firmware on Raspberry Pi Pico

As Raspberry Pi Pico released with MicroPython supported officially, CircuitPython also support Pico. It's CircuitPython 6.2.0-beta.1 currently.

To flash CircuitPython firmware on Raspberry Pi Pico:

Visit https://circuitpython.org/, search "Pico by Raspberry Pi" and download the firmware (.UF2).

Hold down Raspberry Pi Pico on-board BOOTSEL button while plugging it into USB (or pulling down the RUN/Reset pin to ground) and it will appear as a USB disk drive you can drag the firmware onto.


more exercises for Raspberry Pi Pico/CircuitPython:

Wednesday, January 27, 2021

Raspberry Pi Pico + 128x64 I2C SSD1306 OLED (MicroPython)

With MicroPython firmware flashed on Raspberry Pi Pico, it's go to add I2C SSD1306 OLED on the board.

Connection between Raspberry Pi Pico and I2C SSD1306 OLED:

Firstly, I have to check the default I2C SDA/SCL GPIO pin. Run the code on Pico.

mpyPico_I2C.py

import uos
import machine

print(uos.uname())
print("Freq: "  + str(machine.freq()) + " Hz")

i2c = machine.I2C(0)
print(i2c)

print("Available i2c devices: "+ str(i2c.scan()))

It's found that:
scl=9
sda=8

Raspberry Pi Pico Pinout (captured from https://datasheets.raspberrypi.org/pico/Pico-R3-A4-Pinout.pdf):


Then connect I2C SSD1306 OLED to Pico:


Install MicroPython SSD1306 I2C driver:

Visit https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py, copy the code to Raspberry Pi Pico, named "ssd1306.py".

For reference, I copy it here:

ssd1306.py
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces

from micropython import const
import framebuf


# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8D)

# Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
class SSD1306(framebuf.FrameBuffer):
    def __init__(self, width, height, external_vcc):
        self.width = width
        self.height = height
        self.external_vcc = external_vcc
        self.pages = self.height // 8
        self.buffer = bytearray(self.pages * self.width)
        super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
        self.init_display()

    def init_display(self):
        for cmd in (
            SET_DISP | 0x00,  # off
            # address setting
            SET_MEM_ADDR,
            0x00,  # horizontal
            # resolution and layout
            SET_DISP_START_LINE | 0x00,
            SET_SEG_REMAP | 0x01,  # column addr 127 mapped to SEG0
            SET_MUX_RATIO,
            self.height - 1,
            SET_COM_OUT_DIR | 0x08,  # scan from COM[N] to COM0
            SET_DISP_OFFSET,
            0x00,
            SET_COM_PIN_CFG,
            0x02 if self.width > 2 * self.height else 0x12,
            # timing and driving scheme
            SET_DISP_CLK_DIV,
            0x80,
            SET_PRECHARGE,
            0x22 if self.external_vcc else 0xF1,
            SET_VCOM_DESEL,
            0x30,  # 0.83*Vcc
            # display
            SET_CONTRAST,
            0xFF,  # maximum
            SET_ENTIRE_ON,  # output follows RAM contents
            SET_NORM_INV,  # not inverted
            # charge pump
            SET_CHARGE_PUMP,
            0x10 if self.external_vcc else 0x14,
            SET_DISP | 0x01,
        ):  # on
            self.write_cmd(cmd)
        self.fill(0)
        self.show()

    def poweroff(self):
        self.write_cmd(SET_DISP | 0x00)

    def poweron(self):
        self.write_cmd(SET_DISP | 0x01)

    def contrast(self, contrast):
        self.write_cmd(SET_CONTRAST)
        self.write_cmd(contrast)

    def invert(self, invert):
        self.write_cmd(SET_NORM_INV | (invert & 1))

    def show(self):
        x0 = 0
        x1 = self.width - 1
        if self.width == 64:
            # displays with width of 64 pixels are shifted by 32
            x0 += 32
            x1 += 32
        self.write_cmd(SET_COL_ADDR)
        self.write_cmd(x0)
        self.write_cmd(x1)
        self.write_cmd(SET_PAGE_ADDR)
        self.write_cmd(0)
        self.write_cmd(self.pages - 1)
        self.write_data(self.buffer)


class SSD1306_I2C(SSD1306):
    def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
        self.i2c = i2c
        self.addr = addr
        self.temp = bytearray(2)
        self.write_list = [b"\x40", None]  # Co=0, D/C#=1
        super().__init__(width, height, external_vcc)

    def write_cmd(self, cmd):
        self.temp[0] = 0x80  # Co=1, D/C#=0
        self.temp[1] = cmd
        self.i2c.writeto(self.addr, self.temp)

    def write_data(self, buf):
        self.write_list[1] = buf
        self.i2c.writevto(self.addr, self.write_list)


class SSD1306_SPI(SSD1306):
    def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
        self.rate = 10 * 1024 * 1024
        dc.init(dc.OUT, value=0)
        res.init(res.OUT, value=0)
        cs.init(cs.OUT, value=1)
        self.spi = spi
        self.dc = dc
        self.res = res
        self.cs = cs
        import time

        self.res(1)
        time.sleep_ms(1)
        self.res(0)
        time.sleep_ms(10)
        self.res(1)
        super().__init__(width, height, external_vcc)

    def write_cmd(self, cmd):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs(1)
        self.dc(0)
        self.cs(0)
        self.spi.write(bytearray([cmd]))
        self.cs(1)

    def write_data(self, buf):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs(1)
        self.dc(1)
        self.cs(0)
        self.spi.write(buf)
        self.cs(1)
Finally, test it:

Run my exercise code on Raspberry Pi Pico:

mpyPico_ssd1306.py
import ssd1306
import machine
import time
import uos
import machine

print(uos.uname())
print("Freq: "  + str(machine.freq()) + " Hz")
print("128x64 SSD1306 I2C OLED on Raspberry Pi Pico")

WIDTH = 128
HEIGHT = 64

i2c = machine.I2C(0)

print("Available i2c devices: "+ str(i2c.scan()))
oled = ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c)
oled.fill(0)

oled.text("MicroPython", 0, 0)
oled.text("OLED(ssd1306)", 0, 10)
oled.text("RPi Pico", 0, 20)
oled.show()

while True:
    time.sleep(1)
    oled.invert(1)
    time.sleep(1)
    oled.invert(0)

Tuesday, January 26, 2021

First Power up Raspberry Pi Pico and install MicroPython firmware, on Raspberry Pi.



It's the first time I plug-in the new Raspberry Pi Pico to Raspberry Pi 4B/4G, install MicroPython firmware on Raspberry Pi Pico using Thonny Python IDE, and play around MicroPython REPL.


Play around in with MicroPython REPL:
To get information:
>>> import uos
>>> uos.uname()

Get CPU frequency in hertz:
>>> import machine
>>> machine.freq()

Get memory info:
>>> import micropython
>>> micropython.mem_info()
Remark to enter boot mode:

In boot mode, the Pico will be recognized as a removable driver named "RPI-RP2". And you can install MicroPython firmware in Thonny (as shown in the video). You can also manually download firmware .uf2 and drag to "RPI-RP2", refer to "Flash the MicroPython firmware" section on MagPi - Programming Raspberry Pi Pico with Python and MicroPython.

In my first power-up of Raspberry Pi Pico, it enter boot mode automatically, no need to press and hold the on board BOOTSEL while powering up, as shown in the video. Once installed, Pico will not enter boot mode automatically. 

To enter boot mode again:
- Disconnect USB cable to Raspberry Pi Pico.
- Press and hold the onboard BOOTSEL button.
- Connect USB cable to Raspberry Pi Pico.
- Pico will be recognized as a removable driver named "RPI-RP2".


Useful Tips: 

More exercise for Raspberry Pi Pico
MicroPython:
Install/Update MicroPython firmware to Raspberry Pi Pico using Thonny Python IDE
CircuitPython:

Arduino Framework:

Sunday, January 24, 2021

BLE Notification example: Python/Raspberry Pi read notification from ESP32 BLE server to display Analog Input

It's a Python example run on Raspberry Pi, using libraries bluepy/matplotlib, connect to ESP32 BLE Server (run on ESP32-DevKitC V4), handle notification, and plot the value graphically. It's modified from last exercise of Python/Raspberry Pi handle Notification from ESP32 BLE_notify example.


pyBLE_test_waitNotification_AIN.py
# Python3 example on Raspberry Pi to handle notification from
# ESP32 BLE_notify example.
#
# To install bluepy for Python3:
# $ sudo pip3 install bluepy

from bluepy import btle
import matplotlib.pyplot as plt

value = [0]*30

plt.ylim([0, 256])
plt.plot(value)
plt.draw()
plt.pause(0.01)

class MyDelegate(btle.DefaultDelegate):
    def __init__(self):
        btle.DefaultDelegate.__init__(self)
        # ... initialise here

    def handleNotification(self, cHandle, data):
        # ... perhaps check cHandle
        # ... process 'data'
        #print(type(data))
        #print(dir(data))
        print(data)
        print(data[0])
        
        value.pop(0)
        value.append(data[0])
        plt.clf()
        plt.ylim([0, 256])
        plt.plot(value)
        plt.draw()
        plt.pause(0.01)
        
# Initialisation  -------
address = "24:0a:c4:e8:0f:9a"
service_uuid = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
char_uuid = "beb5483e-36e1-4688-b7f5-ea07361b26a8"

p = btle.Peripheral(address)
p.setDelegate(MyDelegate())

# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID(service_uuid)
ch = svc.getCharacteristics(char_uuid)[0]

"""
setup_data for bluepy noification-
"""
setup_data = b"\x01\x00"
#ch.write(setup_data)
p.writeCharacteristic(ch.valHandle + 1, setup_data)

ch_data = p.readCharacteristic(ch.valHandle + 1)
print(type(ch_data))
print(ch_data)

print("=== Main Loop ===")

while True:
    if p.waitForNotifications(1.0):
        # handleNotification() was called
        continue

    #print("Waiting...")
    # Perhaps do something else here


Wednesday, January 20, 2021

Python/Raspberry Pi handle Notification from ESP32 BLE_notify example

It's a Python3 example code (using bluepy library) run on Raspberry Pi, connect to ESP32 BLE server (programmed with BLE_notify example), and handle the Notification from ESP32.

Where address, service_uuid and char_uuid have to match with ESP32 side.

pyBLE_test_waitNotification_20210120a.py
"""
ref:
bluepy Documentation: Working with notifications
http://ianharvey.github.io/bluepy-doc/notifications.html
"""
# Python3 example on Raspberry Pi to handle notification from
# ESP32 BLE_notify example.
#
# To install bluepy for Python3:
# $ sudo pip3 install bluepy

from bluepy import btle

class MyDelegate(btle.DefaultDelegate):
    def __init__(self):
        btle.DefaultDelegate.__init__(self)
        # ... initialise here

    def handleNotification(self, cHandle, data):
        # ... perhaps check cHandle
        # ... process 'data'
        #print(type(data))
        #print(dir(data))
        print(data)
        print(data[0])


# Initialisation  -------
address = "24:0a:c4:e8:0f:9a"
service_uuid = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
char_uuid = "beb5483e-36e1-4688-b7f5-ea07361b26a8"

p = btle.Peripheral(address)
p.setDelegate(MyDelegate())

# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID(service_uuid)
ch = svc.getCharacteristics(char_uuid)[0]
"""
print(type(ch))
print(ch)
print(dir(ch))

peripheral = ch.peripheral
print(type(peripheral))
print(peripheral)

propNames = ch.propNames
print(type(propNames))
print(propNames)

properties = ch.properties
print(type(properties))
print(properties)
"""

"""
Remark for setup_data for bluepy noification-
Actually I don't understand how come setup_data = b"\x01\x00",
and ch.valHandle + 1.
Just follow suggestion by searching in internet:
https://stackoverflow.com/questions/32807781/
ble-subscribe-to-notification-using-gatttool-or-bluepy
"""
setup_data = b"\x01\x00"
#ch.write(setup_data)
p.writeCharacteristic(ch.valHandle + 1, setup_data)

ch_data = p.readCharacteristic(ch.valHandle + 1)
print(type(ch_data))
print(ch_data)

print("=== Main Loop ===")

while True:
    if p.waitForNotifications(1.0):
        # handleNotification() was called
        continue

    #print("Waiting...")
    # Perhaps do something else here

Next:

Sunday, January 17, 2021

Python on Raspberry Pi to read ESP32 BLE_server, using bluepy

This Python exercise run on Raspberry Pi using bluepy library, read ESP32 (Arduino Framework) BLE_server example (with BLE_client also).

To install bluepy for Python3, enter the command:
$ sudo pip3 install bluepy

Python code, pyBLE_test_20210117a.py

from bluepy import btle

MAC = "24:0a:c4:e8:0f:9a"
SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
CHARACTERISTIC_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8"

print("Connect to:" + MAC)
dev = btle.Peripheral(MAC)

print("\n--- dev ----------------------------")
print(type(dev))
print(dev)

print("\n--- dev.services -------------------")
for svc in dev.services:
    print(str(svc))
    
print("\n------------------------------------")
print("Get Serice By UUID: " + SERVICE_UUID)
service_uuid = btle.UUID(SERVICE_UUID)
service = dev.getServiceByUUID(service_uuid)

print(service)
print("\n--- service.getCharacteristics() ---")
print(type(service.getCharacteristics()))
print(service.getCharacteristics())

#----------------------------------------------
characteristics = dev.getCharacteristics()
print("\n--- dev.getCharacteristics() -------")
print(type(characteristics))
print(characteristics)

for char in characteristics:
    print("----------")
    print(type(char))
    print(char)
    print(char.uuid)
    if(char.uuid == CHARACTERISTIC_UUID ):
        print("=== !CHARACTERISTIC_UUID matched! ==")
        print(char)
        print(dir(char))
        print(char.getDescriptors)
        print(char.propNames)
        print(char.properties)
        print(type(char.read()))
        print(char.read())
    
#print("=== dev ============================")
#print(dir(dev))
#print("=== service ========================")
#print(dir(service))


Friday, January 1, 2021

Raspberry Pi stream camera video using raspivid in rtsp protocol, view in Android using VLC app.

Work on Raspberry Pi 4B/8G running Raspberry Pi OS (32 bit), stream video from Camera Module to port 8000 in rtsp protocol, enter the command:

$ raspivid -o - -t 0 -n -w 800 -h 600 -fps 30| cvlc -vvv stream:///dev/stdin --sout '#rtp{sdp=rtsp://:8000/}' :demux=h264

where:
-w 800 : width=800
-h 600 : height=600
-fps 30 : 30 frame per second
8000 : the port to stream video

Open VLC App, Open Network Stream
rtsp://<Raspberry Pi IP>:8000/
e.g. rtsp://192.168.1.30:8000/