Are you new to using CircuitPython? No worries, there is a full getting started guide here.
Adafruit suggests using the Mu editor to edit your code and have an interactive REPL in CircuitPython. You can learn about Mu and installation in this tutorial.
Download the Project Bundle
Your project will use a specific set of CircuitPython libraries and the code.py file. In order to get the libraries you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Drag the contents of the uncompressed bundle directory onto your board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries # # SPDX-License-Identifier: MIT import board import displayio import keypad import adafruit_displayio_sh1107 from adafruit_hid.keyboard import Keyboard from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS from adafruit_display_text import label from adafruit_bitmap_font import bitmap_font try: import usb_hid except ImportError: usb_hid = None K_SQ = "√" K_CL = "<clear>" K_FN = "<fn>" K_PA = "<paste>" KEYMAP0 = [ K_CL, K_FN, '%', '/', '7', '8', '9', '*', '4', '5', '6', '-', '1', '2', '3', '+', '0', '.', K_SQ, '=' ] KEYMAP1 = [ K_CL, None, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', K_PA, ] keymaps = { 0: KEYMAP0, 1: KEYMAP1, } # pylint: disable=redefined-outer-name def lookup(layer, key_number): while layer >= 0: key = keymaps[layer][key_number] if key is not None: return key layer -= 1 return None displayio.release_displays() # oled_reset = board.D9 # Use for I2C i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller display_bus = displayio.I2CDisplay(i2c, device_address=0x3C) # SH1107 is vertically oriented 64x128 WIDTH = 128 HEIGHT = 64 display = adafruit_displayio_sh1107.SH1107(display_bus, width=WIDTH, height=HEIGHT, rotation=180) display.auto_refresh = False font = bitmap_font.load_font("/digit-16px.pcf") text_area = label.Label(font, text=" ", line_spacing=0.95) text_area.y = 8 display.root_group = text_area N = float unary = { K_SQ: lambda a: a**.5, } binary = { '+': (lambda a, b: a+b, lambda a, b: a * (1+b/100)), '-': (lambda a, b: a-b, lambda a, b: a * (1-b/100)), '*': (lambda a, b: a*b, lambda a, b: a * (b/100)), '/': (lambda a, b: a/b, lambda a, b: a / (b/100)), } class Calculator: def __init__(self): self._number1 = N("0") self._number2 = None self.trail = ["Ready."] self.entry = "" self.op = None self.keyboard = None self.keyboard_layout = None def paste(self, text): if self.keyboard is None: if usb_hid: self.keyboard = Keyboard(usb_hid.devices) self.keyboard_layout = KeyboardLayoutUS(self.keyboard) else: return if self.keyboard_layout is None: self.add_trail("No USB") else: text = str(text) self.keyboard_layout.write(text) self.add_trail(f"Pasted {text}") def add_trail(self, msg): self.trail = self.trail[-3:] + [str(msg).upper()] @property def number1(self): return self._number1 @number1.setter def number1(self, number): self._number1 = number self.add_trail(number) @property def number2(self): if self.entry == "": if self._number2 is not None: return self._number2 return None return N(self.entry) @number2.setter def number2(self, number): self._number2 = number self.entry = '' def clear(self): self.number1 = N("0") def clear_entry(self): self.number2 = None def key_pressed(self, k): # pylint: disable=too-many-branches if k == K_CL: if self.entry: self.entry = self.entry[:-1] elif self.op: print("clear op") self.op = None elif self.number2 is None: self.clear() else: print("clear entry - op = ", self.op) self.clear_entry() if len(k) == 1 and k in "0123456789": self.entry = self.entry + k if k == "." and not "." in self.entry: if self.entry == "": self.entry = "0" self.entry = self.entry + k if k == K_PA: if self.number2 is not None: self.paste(self.number2) else: self.paste(self.number1) if k == "=": self.do_binary_op(0) if k == "%": self.do_binary_op(1) op = unary.get(k) if op: self.do_unary_op(op) if k in binary: if self.number2 is not None: if self.op: self.do_binary_op(0) else: self.number1 = self.number2 self.clear_entry() self.op = k def do_unary_op(self, op): if self.number2 is not None: self.number2 = op(self.number2) else: self.number1 = op(self.number1) def do_binary_op(self, i): if self.op and self.number2 is not None: op = binary[self.op][i] self.op = None self.number1 = op(self.number1, self.number2) self.clear_entry() def show(self): rows = [""] * 4 trail = self.trail if len(trail) > 0: rows[2] = trail[-1] if len(trail) > 1: rows[1] = trail[-2] if len(trail) > 2: rows[0] = trail[-3] entry_or_number = self.entry or self.number2 cursor = ' :' if (self.number2 is None or self.entry != "") else "" op = self.op or '' op = 'd' if op == '/' else op rows[-1] = f"{op}{entry_or_number or ''}{cursor}" for r in rows: print(r) text_area.text = "\n".join(rows) km=keypad.KeyMatrix( row_pins=(board.A2, board.A1, board.A3, board.A0, board.D0), column_pins=(board.D25, board.D11, board.D12, board.D24)) calculator=Calculator() calculator.show() layer = 0 while True: ev = km.events.get() if ev: key = lookup(layer, ev.key_number) if ev.pressed: if key == K_FN: layer = 1 try: calculator.key_pressed(key) except Exception as e: # pylint: disable=broad-except calculator.add_trail(e) calculator.show() elif ev.released: if key == K_FN: layer = 0 else: display.refresh()
Adapting to other Adafruit Feather boards
The code can be adapted to most Adafruit Feather boards that support CircuitPython, but the Feather RP2040 board names some pins differently than most boards, so you'll need to make modifications. Find the portion of the code that creates the KeyMatrix and change D24/D25 to A4/A5 for most other Feathers:
km=keypad.KeyMatrix( row_pins=(board.A2, board.A1, board.A3, board.A0, board.D0), column_pins=(board.A5, board.D11, board.D12, board.A4))
Text editor powered by tinymce.