Those who have seen the introduction of circuitpython generally covet the new modules of circuitpython. For example, py can directly drive the camera, support MP3 soft solution, numpy library, matrix LED driver, etc. But the ideal is very full, the reality is very skinny, and the skinny feeling is that what you think is there is actually not (or in another way).
Although circuitpython is said to be based on micropython, it is actually very different from micropython. Don’t naively think that the code that micropython can run can run with circuitpython. Here is a record of the painful points and the corresponding countermeasures:
1) The basic modules are different, there is no machine, and the microcontroller is used instead, as are other modules, which makes the code of micropython almost impossible to run in circuitpython. What is this called? Based on micropython, it has been completely revamped. . .
2) The PIN interface is different, and the PIN cannot be dynamically specified according to the parameters. I wrote a piece of code to match the int value with the PIN according to the name:
import sys import gc CIRCUIT: bool = (sys.implementation.name == 'circuitpython') # Whether circuitpython if CIRCUIT: # IO mapping of circuitpython import microcontroller iomapper: dict[int, microcontroller. Pin] = {} for pin_str in dir(microcontroller.pin): pin = getattr(microcontroller. pin, pin_str) if isinstance(pin, microcontroller. Pin): iomapper[int(pin_str[4:])] = pin
Then iomapper[10] gets IO10
3) IO reading and writing, SPI, and I2C are used differently:
Use two other modules busio and digitalio
busio provides SPI, and digitalio provides IO level reading and setting.
IO writing is similar to the following: Note that mpy uses 0 and 1 to indicate high and low levels, while cpy uses True and False
def digital_write(self, pin: Pin | digitalio. DigitalInOut, value): if CIRCUIT: pin.value = (value > 0) else: pin. value(value)
4) The keyboard module is different. Although a simple package is made, the interrupted interface is completely shielded and packaged. After repackaging, it can barely be used. The code after the previous keyboard module integrates circuitpython:
from lib.epui import * import lib.time as time if CIRCUIT: from microcontroller import Pin else: from machine import Pin """ micropython switch check module By @Jim 2023-2025 """ class BaseSwitchButton: """ button interface Key detection module that supports anti-shake and continuous pressing """ def check_continuous(self): """ Check if the button is pressed continuously :return: """ pass def pull_state(self) -> tuple(bool, bool): """ pull status :return: (whether it was pressed during the period, whether it was pressed continuously) """ return (None, None) if CIRCUIT: import lib.adafruit_ticks as adafruit_ticks import keypad class SwitchButton(BaseSwitchButton): """Switch Button Class Circuitpython, a key detection module that supports anti-shake and continuous pressing """ continuous_period = 1000 # How many seconds to press continuously to enter continuous trigger pins: list[Pin] = [] buttons = [] def __init__(self, pin_num: int): self.__pressed = False self.__continuous = False self.__press_tick = 0 self.__stat_on = False # Whether to press the status self.pin = iomapper[pin_num] if self.pin is None: raise ValueError(f'pin number {pin_num} not available') self.key_number = len(SwitchButton.pins) SwitchButton.pins.append(self.pin) SwitchButton. buttons. append(self) @classmethod def init_keypad(cls): SwitchButton.keys = keypad.Keys(tuple(SwitchButton.pins), value_when_pressed=False, pull=True) @classmethod def sync_button(cls): while True: # Pull all states event = SwitchButton.keys.events.get() if event is None: break button = SwitchButton.buttons[event.key_number] if event.pressed: button.__pressed = True # record has been pressed button.__press_tick = event.timestamp button.__stat_on = True if event.released: button.__stat_on = False def check_continuous(self): if self.__stat_on: # pressed if adafruit_ticks.ticks_diff(adafruit_ticks.ticks_ms(), self.__press_tick) > SwitchButton.continuous_period: self.__continuous = True self.__pressed = True def pull_state(self) -> tuple(bool, bool): result = (self.__pressed, self.__continuous) self.__pressed = False self.__continuous = False return result else: #micropython class SwitchButton(BaseSwitchButton): """Switch Button Class Micropython, a button detection module that supports anti-shake and continuous pressing """ continuous_period = 1000 # How many seconds to press continuously to enter continuous trigger throttle_ms = 400 # cheating will trigger multiple times, do some throttling def __init__(self, pin_num: int): """ :param pin_num: """ self.__pin = Pin(pin_num, Pin.IN, Pin.PULL_UP) self.__pin.irq(handler=self.__switch_change, trigger=Pin.IRQ_FALLING) # Falling edge trigger self.__pressed = False self.__continuous = False self.__press_tick = 0 def __switch_change(self, _): if time.ticks_diff(time.ticks_ms(), self.__press_tick) < SwitchButton.throttle_ms: # throttle return self.__pressed = True self.__press_tick = time.ticks_ms() self.__continuous = False def check_continuous(self): if self.__pin.value() == 0: # press if time.ticks_diff(time.ticks_ms(), self.__press_tick) > SwitchButton.continuous_period: self.__continuous = True self.__pressed = True def pull_state(self) -> tuple(bool, bool): result = (self.__pressed, self.__continuous) self.__pressed = False self.__continuous = False return result def sleep(self): # sleep irq self.__pin.irq(trigger=0) def awake(self): self.__pin.irq(handler=self.__switch_change, trigger=Pin.IRQ_FALLING) # Falling edge trigger if __name__ == '__main__': my_switch = SwitchButton(12 if CIRCUIT else 2) if CIRCUIT: SwitchButton.init_keypad() else: from machine import disable_irq, enable_irq print('start') while True: if CIRCUIT: SwitchButton. sync_button() my_switch. check_continuous() pressed = continuous = False if not CIRCUIT: irq_state = disable_irq() [pressed, continuous] = my_switch. pull_state() if CIRCUIT: time. sleep(0.1) else: enable_irq(irq_state) time. sleep_ms(100) if pressed: print(f'OK is {"continuously pressed" if continuous else "pressed"}')
4) There is no coroutine module. This is not packaged in the firmware, but the py code is provided separately. After reading it, it seems to be barely usable (by the way, Micropython’s coroutine is also a bunch of py packaged):
https://github.com/adafruit/Adafruit_CircuitPython_asyncio/tree/main
5) If drawing generally starts from framebuffer, but there is no framebuffer, only a highly encapsulated framebufferio. This is not packaged in the firmware, there is also a separate py code
GitHub – adafruit/Adafruit_CircuitPython_framebuf: CircuitPython framebuf module, based on the Python framebuf module
But… the above module is re-implemented in python, and blit is not implemented, and the block copy that can improve performance the most is not there. . . OMG, the solution is to transplant. I have transplanted the version of framebuffer1.20 to circuitpython (firmware has been provided). Framebuffer1.20 has many more good things than 1.19. For example, drawing ellipses (including circles and rounded rectangles becomes Simple), polygon filling, this solves the problem of drawing
Well, it doesn’t matter if you don’t have it. At worst, let’s move from micropython. Please see the related article “Compiling circuitpython with smartconfig and framebuffer”
6) zlib only has decompress, no DecompIO
CircuitPython does drop some good things, decompress puts the decompressed data into memory. And DecompIO can achieve streaming decompression. For example, if you want to process a 10M text for streaming retrieval, then DecompIO can process the loss, but decompress is unable to do it. For example, you can put a binary tree index list into the compressed file. . . etc.
In the end, it can only be transplanted. I have successfully completed the transplantation of the corresponding function from micropython to circuitpython.
7) The root partition is mounted read-only by default, which means that all write operations will fail. Solution: Add in code.py:
import storage storage.remount("/", False) #Crap, the default mount is readonly
Then unplug and restart
8) The time module is different. There is an extra useless -1 in the tuple. I wrote a time module myself to smooth the difference between the two systems:
""" Compatible with both micropython and circuitpython time modules This smooths out platform differences @Jim 2023-07 """ from lib.epui import * if CIRCUIT: from lib.adafruit_ticks import * from time import * def sleep_ms(ms: int): sleep(ms/1000) def sleep_us(us: int): sleep(us/1000000) wrap_mktime = mktime wrap_localtime = localtime def mktime(t: struct_time | tuple) -> int: if isinstance(t, tuple) and len(t) == 8: l = list(t) l.append(-1) return wrap_mktime(tuple(l)) else: return wrap_mktime(t) def localtime(secs: int = None) -> tuple: s_t = wrap_localtime(secs) s_t = list(s_t) del s_t[-1] return tuple(s_t) else: from utime import *
The following is the transplanted version of the module compiled by me:
CircuitPython module overview
Wi-Fi: off | REPL | 8.2.0-dirty\ Adafruit CircuitPython 8.2.0-dirty on 2023-07-22; S2Mini with ESP32S2-S2FN4R2 >>> help('modules') __future__ countio msgpack sys __main__ digitalio neopixel terminalio _asyncio displayio neopixel_write time _pixelmap dualbank nvm touchio adafruit_bus_device errno onewireio traceback adafruit_bus_device.i2c_device espidf os ulab adafruit_bus_device.spi_device espnow paralleldisplay ulab.numpy adafruit_pixelbuf espulp ps2io ulab.numpy.fft aesio fontio pulseio ulab.numpy.linalg alarm framebuf pwmio ulab.scipy analogbufio framebufferio rainbowio ulab.scipy.linalg analogio frequencyio random ulab.scipy.optimize array gc re ulab.scipy.signal atexit getpass rgbmatrix ulab.scipy.special audiobusio hashlib rotaryio ulab.utils audiocore i2cperipheral rtc usb_cdc audiomixer i2ctarget sdcardio usb_hid audiomp3 io select usb_midi binascii ipaddress sharpdisplay useselect bitbangio json smartconfig vectorio bitmaptools keypad socketpool watchdog board math ssl wifi builtins mdns storage zlib busio memorymap struct canio microcontroller supervisor collections micropython synthio Plus any modules on the filesystem
Download https://download.csdn.net/download/applebomb/88074245