-
Notifications
You must be signed in to change notification settings - Fork 129
Circuitpython
With Circuitpython it's possible to write software without compiling, simply by editing text files on the USB drive. nRF52840 has 256K RAM and 1M flash so you get about 300K free for your source code, even more with frozen modules. So you do NOT really need an external flash chip, the main issue is that Python is super slow, booting takes about 5-10 seconds, depending on your code, considering wake up after sleep is basically rebooting that may be very annoying.
Download nRFMicro .uf2 firmware here (I forked the repository but haven't submitted the PR just yet):
- adafruit-circuitpython-nrfmicro-en_US-6.0.0-beta.2-fixed.uf2 (includes precompiled BLE/HID/OLED/Neopixel modules)
- nrfmicro-circuitpython-8.0.0-alpha.1-40-gf95bd243c.uf2 (latest alpha, not recommended)
To flash .uf2 firmware to nRFMicro just press reset twice within 500 ms and copy the file to the USB drive.
- After flashing the Python firmware, just copy your Python code to the code.py file in the USB root, it runs automatically.
- To debug Python code, connect to virtual COM port at 115200, use Ctrl+C/Ctrl+D to break/reload and read the messages.
Put the main code into code.py
file on the USB drive root. All changes apply instantly after saving the file, no reset needed.
To debug you just connect to virtual COM port at 115200, and use Ctrl+C to break and/or Ctrl+D to reload and read the messages. See virtual COM port number in windows Device Manager. I use Kitty (http://www.9bis.net/kitty) as a terminal.
Copy to USB root as code.py.
import time
import board
from digitalio import DigitalInOut, Direction, Pull
led = DigitalInOut(board.P1_10) # blue led
led.direction = Direction.OUTPUT
while True:
led.value = True
time.sleep(0.5)
led.value = False
time.sleep(0.5)
You will need the font5x8.bin font file for OLED (put it to the root directory).
import adafruit_ssd1306
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
oled = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c)
oled.fill(0)
oled.text("hello world", 0, 0, 1)
oled.show()
import neopixel
import time
leds = 6
pixels = neopixel.NeoPixel(board.P0_06, leds, auto_write=False)
for i in range(leds):
pixels[i] = (0, 0, 32)
time.sleep(0.1)
pixels.show()
for i in range(leds):
pixels[i] = (0, 0, 0)
time.sleep(0.1)
pixels.show()
ONLY if you want to build your own custom python core. Use stock fimware above if you just want to run python.
Normally you don't need to build anything, just download the .uf2 binary above, but if you want to build from scratch:
git clone https://github.com/joric/circuitpython
cd circuitpython
git checkout nrfmicro
git submodule sync
git submodule update --init
sudo apt install gettext
sudo pip install mpy-cross
make -C mpy-cross
cd ports/nrf
make BOARD=nrfmicro
Binary is saved to ports/nrf/build-nrfmicro/firmware.uf2
. Configuration is in ports/nrf/boards/nrfmicro
(I added more pins and removed XTAL requirement). This binary is basically chip-agnostic for all nRF52840-based boards, all pins are available. It contains frozen modules (see below).
Adafruit CircuitPython 6.0.0-beta.2-dirty on 2020-10-14; nrfmicro with nRF52840
>>> import board
>>> dir(board)
['__class__', 'AIN0', 'AIN2', 'AIN5', 'AIN7', 'I2C', 'LED', 'MISO', 'MOSI', 'NFC1', 'NFC2', 'P0_01', 'P0_02',
'P0_03', 'P0_04', 'P0_05', 'P0_06', 'P0_07', 'P0_08', 'P0_09', 'P0_10', 'P0_11', 'P0_12', 'P0_13', 'P0_14',
'P0_15', 'P0_16', 'P0_17', 'P0_18', 'P0_19', 'P0_20', 'P0_21', 'P0_22', 'P0_23', 'P0_24', 'P0_25', 'P0_26',
'P0_28', 'P0_29', 'P0_30', 'P0_31', 'P1_00', 'P1_01', 'P1_02', 'P1_03', 'P1_04', 'P1_05', 'P1_06', 'P1_07',
'P1_08', 'P1_09', 'P1_10', 'P1_11', 'P1_12', 'P1_13', 'P1_14', 'P1_15', 'RX', 'SCK', 'SCL', 'SDA', 'SPI',
'TX', 'UART', 'VCC_OFF', 'VOLTAGE_MONITOR']
Note that nice!nano already has its own binary hosted here: https://circuitpython.org/board/nice_nano (see PR #3017). The pinout is slightly different (see Pinout) e.g. SCL and SDA pins are 17/20 not 15/17 as on nRFMicro and LED is not 1.10 but 0.15. You may compare configurations side by side in pins.c files: nice_nano/pins.c, nrfmicro/pins.c.
Adafruit CircuitPython 6.0.0-beta.2 on 2020-10-05; nice!nano with nRF52840
>>> import board
>>> dir(board)
['__class__', 'AIN0', 'AIN2', 'AIN5', 'AIN7', 'BAT_VOLT', 'I2C', 'LED', 'MISO', 'MOSI', 'NFC1', 'NFC2',
'P0_02', 'P0_04', 'P0_06', 'P0_08', 'P0_09', 'P0_10', 'P0_11', 'P0_12', 'P0_13', 'P0_15', 'P0_17', 'P0_20',
'P0_22', 'P0_24', 'P0_26', 'P0_29', 'P0_31', 'P1_00', 'P1_01', 'P1_02', 'P1_04', 'P1_06', 'P1_07', 'P1_11',
'P1_13', 'P1_15', 'RX', 'SCK', 'SCL', 'SDA', 'SPI', 'TX', 'UART', 'VCC_OFF']
It's much faster to run modules from the Circuitpython binary rather than from the filesystem. They also need less RAM.
Basically what I did is modified mpconfigboard.mk
:
USB_VID = 0x239A
USB_PID = 0x80B4
USB_PRODUCT = "nrfmicro"
USB_MANUFACTURER = "joric"
MCU_CHIP = nrf52840
INTERNAL_FLASH_FILESYSTEM = 1
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HID
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_framebuf
And added a couple of missing repositories into the /frozen folder (use git submodule add):
cd ~/circuitpython/frozen
git submodule add https://github.com/adafruit/Adafruit_CircuitPython_SSD1306
git submodule add https://github.com/adafruit/Adafruit_CircuitPython_framebuf
Then build as usual and you would have a binary that doesn't need external modules.
You can also precompile your custom keyboard-related modules this way (KMK firmware does that already).
You can use help('modules')
in the python prompt to list all the available modules.
To add external modules, download 6.x bundle from https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases and copy files to internal USB drive (you can find font5x8.bin in the examples folder). You could also use Circup.
(nRFMicro build already includes those modules, no need to add them.)
- /lib/adafruit_ble
- /lib/adafruit_hid
- /lib/adafruit_bus_device
- /lib/adafruit_framebuf.mpy
- /lib/adafruit_ssd1306.mpy
- /lib/neopixel.mpy
- /font5x8.bin
A minimal code that runs OLED, RGB, BLE, USB and key matrix and doesn't weight a ton (copy to the Python USB drive):
- https://gist.github.com/joric/327fafcf40a8a8ccb93c9b7eb631100b (/code.py)
- https://github.com/adafruit/Adafruit_CircuitPython_framebuf/raw/master/examples/font5x8.bin (/font5x8.bin)
There's a lightweight firmware that boots relatively fast:
I've made a branch that runs on nrfmicro, working on it (you can copypaste pieces from there):
There's keyboard firmware (in progress) that uses Circuitpython (no BLE support at master, just USB and UART via TRRS):
There's an experimental branch that runs wireless split keyboards:
You can just copy .py files along with the KMK folder to disk but It's better to compile KMK to save some RAM. Use make compile
to build it (you will need circuitpython compiler from circuitpython package, built in the make -C mpy-cross stage). If you see maximum recursion depth exceeded
that means you ran out of RAM. Try editing boot.py or something (e.g 4096+2048).
You can also try the following (you will need OLED libraries in your /lib directory, see examples above).
- kmk - put (compiled) kmk firmware files from the repository to the kmk folder
- main.py - put to the root folder
- jorne.py - put to the root folder
The result looks like this (matrix works, keymap works):
Looks like Circuitpython boot time is affected by the startup LED that just stalls the entire startup for 700 ms to blink.
- https://github.com/adafruit/circuitpython/blob/main/supervisor/shared/safe_mode.c#L70 (code in question)
-
https://github.com/adafruit/circuitpython/blob/main/main.c#L463 (comment out the line with
wait_for_safe_mode_reset
) - https://youtu.be/RkmZ7mdLf14 (video)
Use this build to boot faster (I've just commented out wait_for_safe_mode_reset
call in main.c
), it's near instant now:
Also make sure your python has no gc.collect()
calls and modules are either compiled or built (frozen) into the .uf2 file.
Looks like it's still a feature in progress. Micropython has pyb module that you can use in boot.py
to hide MSC:
import pyb
#pyb.usb_mode('VCP+MSC') # act as a serial and a storage device
pyb.usb_mode('VCP+HID') # act as a serial device and a mouse
Circuitpython has no pyb or machine modules. This feature while useful is not critical because wireless keyboards are usually not connected to USB.
- https://github.com/adafruit/circuitpython/issues/1015 (enable/disable msc on boot, in progress)
- https://learn.adafruit.com/circuitpython-essentials/circuitpython-storage (how to use boot.py)
- https://forums.adafruit.com/viewtopic.php?f=60&t=163825 (how to hide msc on boot)