pho ramen soba

Monday, 10 June 2024

moving from circuit python to micro python

I've made a technical decision to move away from using Adafruit's CircuitPython to the upstream MicroPython. The reason, in a word, is stability of the implementation. I find, for my uses, that MicroPython is more stable for embedded unattended execu…
Read on blog or Reader
Site logo image Arcane Science Lab Read on blog or Reader

moving from circuit python to micro python

whbeebe

June 10

MicroPython IoT Prototype

I've made a technical decision to move away from using Adafruit's CircuitPython to the upstream MicroPython. The reason, in a word, is stability of the implementation. I find, for my uses, that MicroPython is more stable for embedded unattended execution than CircuitPython is. The developer ecosystem seems to be richer for CircuitPython, but for long term development I can write or modify what's out there for my needs if the underlying embedded Python implementation is stable enough.

Another feature that MicroPython has that CircuitPython doesn't is the ability to get a microcontroller's unique ID. I use that characteristic extensively because I need it for embedded devices that are part of a larger wireless network to manage them all. For just one example I use the last four characters of the unique ID to synthesis an SSID for devices that will be part of my local home WiFi network. You can see an example in the photo above, in the third line of text (RP2-9633).

While a lot has been made of flashing a Raspberry Pi Pico/W with CircuitPython, flashing with MicroPython is very easy, as it's the same with either. Make sure you have the UF2 file for MicroPython, then with the device unplugged, hold down the BOOTSEL button, plug in the USB cable, then release the BOOTSEL button. The Raspberry Pi Pico will come up in bootloader mode looking like a flash drive, allowing you to drag and drop the MicroPython UF2 file onto the device. Once flashed, the Pico will disappear from your file system, and you'll need a tool such as Thonny to communicate with, and program the device.

  MPY: soft reboot     MEM FREE: 183,584 BYTES   FS TOTAL: 868,352 BYTES   FS  FREE: 847,872 BYTES   PLATFORM: MicroPython-1.23.0-arm--with-newlib4.3.0        UID: E6614C775B969633       SSID: RP2-9633   CPU FREQ: 125,000,000 Hz        I2C: SoftI2C(scl=27, sda=26, freq=500000)        I2C: DEVICES FOUND: ['0x3d']    ssid: Dashmeister              rssi: -82  ssid: ESP32S3-5F50             rssi: -21  ssid: GuestNetwork             rssi: -61  ssid: NETGEAR04                rssi: -90  ssid: NotTheWiFiYoureLookingFor rssi: -91  ssid: SmartLife-EEFB           rssi: -68    MicroPython v1.23.0 on 2024-06-02; Raspberry Pi Pico W with RP2040  Type "help()" for more information.  

What follows is the code running on this particular Raspberry Pi Pico W. It is in two parts, the main followed by the OLED display class.

  print()    import gc  print(f" MEM FREE: {gc.mem_free():,} BYTES")    import os  UNAME = os.uname().sysname.upper()  stat_vfs = os.statvfs('/')  print(f" FS TOTAL: {stat_vfs[0] * stat_vfs[2]:,} BYTES")  print(f" FS  FREE: {stat_vfs[0] * stat_vfs[3]:,} BYTES")    import platform  print(f" PLATFORM: {platform.platform()}")    import binascii  import machine as ma  UNIQUE_ID = binascii.hexlify(ma.unique_id()).decode('ascii').upper()  print(f"      UID: {UNIQUE_ID}")  SSID = UNAME + '-' + UNIQUE_ID[-4:]  print(f"     SSID: {SSID}")  print(f" CPU FREQ: {ma.freq():,} Hz")    # Scan I2C bus for devices  #  # I2C pins for Raspberry Pi Pico W, device I2C1  import SSD1306  SDA_PIN = 26  SCL_PIN = 27  SOFT_I2C = ma.SoftI2C(scl=ma.Pin(SCL_PIN), sda=ma.Pin(SDA_PIN))  print(f"      I2C: {SOFT_I2C}")  print("      I2C: DEVICES FOUND:", [hex(device_address)      for device_address in SOFT_I2C.scan()])    # Display the Micropython logo on the SSD1306 OLED display.  #  display = SSD1306.SSD1306_I2C(SOFT_I2C)  display.fill(0)  display.framebuf.fill_rect(0, 0, 32, 32, 1)  display.framebuf.fill_rect(2, 2, 28, 28, 0)  display.framebuf.vline(9, 8, 22, 1)  display.framebuf.vline(16, 2, 22, 1)  display.framebuf.vline(23, 8, 22, 1)  display.framebuf.fill_rect(26, 24, 2, 4, 1)  display.text('MicroPython', 40, 0, 1)  display.text('-'.join(platform.platform().split('-')[1:3]), 40, 12, 1)  display.text(SSID, 40, 24, 1)  display.show()    print()    import network  wifi = network.WLAN(network.STA_IF)  wifi.active(True)  access_points = wifi.scan()  networks = {}    for network in access_points:      if len(network[0]) > 0 and bytearray(network[0])[0] != 0:          ssid = network[0].decode('utf-8')          networks[ssid] = network[3]    for ssid in sorted(networks.keys()):      print(f"ssid: {ssid:24} rssi: {networks[ssid]}")    print()  

Inside of main, lines 19 and 19 find the board's unique ID and then use the last four characters of the unique ID to synthesize the board's SSID. The block of code from line 23 to line 47 enable the OLED display and display the MicroPython logo as well as identifying information about that specific Raspberry Pi Pico W. You can see what it produces in the photo at the top of the post.

  # MicroPython SSD1306 OLED driver, I2C interface  #  # Originally written by Adafruit  #  # Adafruit has deprecated this code, and is now devoting  # development time and resources for the version that  # works with Circuit Python.    import time  import framebuf  from micropython import const    # 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)    # display definitions  OLED_WIDTH    = const(128)  OLED_HEIGHT   = const(64)  OLED_LINE_MAX = const(6)  OLED_ADDR     = const(0x3D)    class SSD1306():      def __init__(self, width, height, external_vcc):          self.width = width          self.height = height          self.external_vcc = external_vcc          self.pages = self.height // 8          # Note the subclass must initialize self.framebuf to a framebuffer.          # This is necessary because the underlying data buffer is different          # between I2C and SPI implementations (I2C needs an extra byte).          self.poweron()          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.height == 32 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 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_framebuf()        def fill(self, col):          self.framebuf.fill(col)        def pixel(self, x, y, col):          self.framebuf.pixel(x, y, col)        def scroll(self, dx, dy):          self.framebuf.scroll(dx, dy)        def text(self, string, x, y, col=1):          self.framebuf.text(string, x, y, col)      class SSD1306_I2C(SSD1306):      def __init__(self, i2c, width=OLED_WIDTH, height=OLED_HEIGHT, addr=0x3D, external_vcc=False):          self.i2c = i2c          self.addr = addr          self.temp = bytearray(2)          # Add an extra byte to the data buffer to hold an I2C data/command byte          # to use hardware-compatible I2C transactions.  A memoryview of the          # buffer is used to mask this byte from the framebuffer operations          # (without a major memory hit as memoryview doesn't copy to a separate          # buffer).          self.buffer = bytearray(((height // 8) * width) + 1)          self.buffer[0] = 0x40  # Set first byte of data buffer to Co=0, D/C=1          self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height)          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_framebuf(self):          # Blast out the frame buffer using a single I2C transaction to support          # hardware I2C interfaces.          self.i2c.writeto(self.addr, self.buffer)        def poweron(self):          pass        # A convenience method to print by line number unlike the text() method.      # This assumes that you are using a 128 x 64 pixel OLED display.      # Line numbers are 1-6 inclusive. There is no line 0.      #      def line(self, string, line_number):          if line_number > 0 and line_number <= OLED_LINE_MAX:              self.text(string,0,(line_number - 1)*10)        # A way to test a 128 x 64 pixel OLED display.      #      def test_oled(self):          for i in range(1, OLED_LINE_MAX + 1):              self.fill(0)              self.line('LINE {} ----+----'.format(i), i)              self.show()              time.sleep_ms(500)            self.fill(0)          self.show()  

The code in the SSD1306 class was originally created by Adafruit, but they abandoned it when they decided to only support their CircuitPython fork of MicroPython. That's fine, and you an find the original source on GitHub. I cleaned up some things, removed bits I didn't need, and added some bits I found handy.

I'm quite comfortable moving towards MicroPython. The solid reliability of MicroPython trumps all other concerns.

Comment
Like
You can also reply to this email to leave a comment.

Arcane Science Lab © 2024. Manage your email settings or unsubscribe.

WordPress.com and Jetpack Logos

Get the Jetpack app

Subscribe, bookmark, and get real-time notifications - all from one app!

Download Jetpack on Google Play Download Jetpack from the App Store
WordPress.com Logo and Wordmark title=

Automattic, Inc. - 60 29th St. #343, San Francisco, CA 94110  

at June 10, 2024
Email ThisBlogThis!Share to XShare to FacebookShare to Pinterest

No comments:

Post a Comment

Newer Post Older Post Home
Subscribe to: Post Comments (Atom)

A Delicious Vegan Thanksgiving Menu 🍁

Weekly menu inspo, with a holiday twist! A delicious, satisfying Thanksgiving menu with appetizers, sides, mains, ...

  • LA COURONNE LYONNAISE, TWO WAYS
    This bread originates in Lyon, and is shaped as a crown, therefore the name ...
  • [New post] This tried-and-true Irish pub-inspired soup is creamy and thick with chunks of potatoes and leeks throughout.
    Emily Morgan posted: "This tried-and-true Irish pub-inspired soup is creamy and thick with chunks of potatoes and leeks thr...
  • Keto Chicken Pot Pie Casserole (Gluten-Free)
    INGREDIENTS US CustomaryMetric▢4 cups cooked chicken breast (roasted, rotisserie...

Search This Blog

  • Home

About Me

phoo, ramen, soba
View my complete profile

Report Abuse

Blog Archive

  • November 2025 (15)
  • October 2025 (21)
  • September 2025 (19)
  • August 2025 (28)
  • July 2025 (25)
  • June 2025 (28)
  • May 2025 (34)
  • April 2025 (36)
  • March 2025 (39)
  • February 2025 (36)
  • January 2025 (43)
  • December 2024 (46)
  • November 2024 (51)
  • October 2024 (44)
  • September 2024 (1172)
  • August 2024 (1572)
  • July 2024 (1413)
  • June 2024 (1289)
  • May 2024 (1362)
  • April 2024 (1472)
  • March 2024 (1827)
  • February 2024 (2413)
  • January 2024 (2936)
  • December 2023 (2135)
  • November 2023 (1639)
  • October 2023 (1285)
  • September 2023 (918)
  • August 2023 (864)
  • July 2023 (795)
  • June 2023 (800)
  • May 2023 (796)
  • April 2023 (754)
  • March 2023 (649)
  • February 2023 (736)
  • January 2023 (1159)
  • December 2022 (968)
  • November 2022 (921)
  • October 2022 (852)
  • September 2022 (708)
  • August 2022 (766)
  • July 2022 (877)
  • June 2022 (684)
  • May 2022 (716)
  • April 2022 (698)
  • March 2022 (781)
  • February 2022 (734)
  • January 2022 (955)
  • December 2021 (1387)
  • November 2021 (3002)
  • October 2021 (3213)
  • September 2021 (3188)
  • August 2021 (3232)
  • July 2021 (1697)
Powered by Blogger.