## Step 1 - Install CircuitPython

This guide requires CircuitPython be installed, click the button below to learn how to do that and install the latest version of CircuitPython.

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.

Make sure you use CircuitPython 6.0.0-rc.0 or newer for this project.

## Step 2 - Install Libraries

First make sure you are running the latest version of Adafruit CircuitPython for the Feather nRF52840. You'll also need to install several libraries on your Feather:

• jepler_udecimal (from the Community Bundle)

Carefully follow the steps to find and install these libraries from Adafruit's CircuitPython Library Bundle. Our CircuitPython starter guide has a great page on how to install libraries from the bundle.

## Step 3 - Install code

Use the "Project Zip" link to download the rest of the files needed for the calculator, then unzip it inside the CIRCUITPY drive.

```# SPDX-FileCopyrightText: 2020 Jeff Epler for Adafruit Industries
#

import gc
import time

from jepler_udecimal import Decimal, getcontext, localcontext
import jepler_udecimal.utrig  # Needed for trig functions in Decimal
import board
import digitalio
import displayio
import framebufferio
import microcontroller
import sharpdisplay
import terminalio

try:
import usb_hid
except ImportError:
usb_hid = None

# Initialize the display, cleaning up after a display from the previous
# run if necessary
displayio.release_displays()
framebuffer = sharpdisplay.SharpMemoryFramebuffer(board.SPI(), board.RX, 400, 240)
display = framebufferio.FramebufferDisplay(framebuffer, auto_refresh=False)

def inner(fn):
def wrapper(*args, **kw):
with localcontext() as ctx:
ctx.prec = ctx.prec + add + (ctx.prec * num + den - 1) // den
result = fn(*args, **kw)
return +result
return wrapper
return inner

class AngleConvert:
def __init__(self):
self.state = 0

def next_state(self):
self.state = (self.state + 1) % 3

def __str__(self):
return "DRG"[self.state]

@property
def factor(self):
return [360, None, 400][self.state]

def from_user(self, x):
factor = self.factor
if factor is None:
return x
x = x.remainder_near(factor)
pi_4 = Decimal("1.0").atan()
return x * pi_4 * 8 / factor

def to_user(self, x):
factor = self.factor
if factor is None:
return x
pi_4 = Decimal("1.0").atan()
return x * factor / pi_4 / 8

@extraprec(num=1)
def cos(self, x):
return self.from_user(x).cos()

@extraprec(num=1)
def sin(self, x):
return self.from_user(x).sin()

@extraprec(num=1)
def tan(self, x):
return self.from_user(x).tan()

@extraprec(num=1)
def acos(self, x):
return self.to_user(x.acos())

@extraprec(num=1)
def asin(self, x):
return self.to_user(x.asin())

@extraprec(num=1)
def atan(self, x):
return self.to_user(x.atan())

getcontext().prec = 14
getcontext().Emax = 99
getcontext().Emin = -99

def get_pin(x):
if isinstance(x, microcontroller.Pin):
return digitalio.DigitalInOut(x)
return x

def __init__(self, row_pins, col_pins):
self.row_pins = [get_pin(p) for p in row_pins]
self.col_pins = [get_pin(p) for p in col_pins]
self.old_state = set()
self.state = set()

for r in self.row_pins:
r.switch_to_input(digitalio.Pull.UP)
for c in self.col_pins:
c.switch_to_output(False)

def scan(self):
self.old_state = self.state
state = set()
for c, cp in enumerate(self.col_pins):
cp.switch_to_output(False)
for r, rp in enumerate(self.row_pins):
if not rp.value:
cp.switch_to_input()
self.state = state
return state

def rising(self):
old_state = self.old_state
new_state = self.state

return new_state - old_state

class LayerSelect:
def __init__(self, idx=1, next_layer=None):
self.idx = idx
self.next_layer = next_layer or self

LL0 = LayerSelect(0)
LL1 = LayerSelect(1)
LS1 = LayerSelect(1, LL0)

def __init__(self, row_pins, col_pins, layers):
self.layers = layers
self.layer = LL0
self.pending = []

def getch(self):
if not self.pending:
self.base.scan()
for r, c in self.base.rising():
op = self.layers[self.layer.idx][r][c]
if isinstance(op, LayerSelect):
self.layer = op
else:
self.pending.extend(op)
self.layer = self.layer.next_layer

if self.pending:
return self.pending.pop(0)

return None

col_pins = (board.D10, board.D9, board.D6, board.TX)
row_pins = (board.A0, board.A1, board.A2, board.A3, board.A4, board.A5)

BS = '\x7f'
CR = '\n'

layers = (
(
('^', 'l', 'r', LS1),
('s', 'c', 't', '/'),
('7', '8', '9', '*'),
('4', '5', '6', '-'),
('1', '2', '3', '+'),
('0', '.',  BS,  CR)
),

(
('v', 'L', 'R', LL0),
('S', 'C', 'T', 'N'),
( '',  '',  '',  ''),
( '',  '',  '', 'n'),
( '',  '',  '',  ''),
('=', '@',  BS, '~')
),
)

class Impl:
def __init__(self):

# outgoing keypresses
self.keyboard = None
self.keyboard_layout = None

g = displayio.Group()

self.labels = labels = []
labels.append(Label(terminalio.FONT, scale=2, color=0))
labels.append(Label(terminalio.FONT, scale=3, color=0))
labels.append(Label(terminalio.FONT, scale=3, color=0))
labels.append(Label(terminalio.FONT, scale=3, color=0))
labels.append(Label(terminalio.FONT, scale=3, color=0))
labels.append(Label(terminalio.FONT, scale=3, color=0))

for li in labels:
g.append(li)

bitmap = displayio.Bitmap((display.width + 126)//127, (display.height + 126)//127, 1)
palette = displayio.Palette(1)
palette[0] = 0xffffff

bg = displayio.Group(scale=127)
bg.append(tile_grid)

g.insert(0, bg)

display.show(g)

def getch(self):
while True:
time.sleep(.02)
if c is not None:
return c

def setline(self, i, text):
li = self.labels[i]
text = text[:31] or " "
if text == li.text:
return
li.text = text
li.anchor_point = (0,0)
li.anchored_position = (1, max(1, 41 * i - 7) + 6)

def refresh(self):
pass

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:
raise ValueError("USB HID not available")
text = str(text)
self.keyboard_layout.write(text)
raise RuntimeError("Pasted")

def start_redraw(self):
display.auto_refresh = False

def end_redraw(self):
display.auto_refresh = True

def end(self):
pass
impl = Impl()

stack = []
entry = []

def do_op(arity, fun):
if arity > len(stack):
return "underflow"
res = fun(*stack[-arity:][::-1])
del stack[-arity:]
if isinstance(res, list):
stack.extend(res)
elif res is not None:
stack.append(res)
return None
angleconvert = AngleConvert()

def roll():
stack[:] = stack[1:] + stack[:1]

def rroll():
stack[:] = stack[-1:] + stack[:-1]

def swap():
stack[-2:] = [stack[-1], stack[-2]]

ops = {
'\'': (1, lambda x: -x),
'\\': (2, lambda x, y: x/y),
'#': (2, lambda x, y: y**(1/x)),
'*': (2, lambda x, y: y*x),
'+': (2, lambda x, y: y+x),
'-': (2, lambda x, y: y-x),
'/': (2, lambda x, y: y/x),
'^': (2, lambda x, y: y**x),
'v': (2, lambda x, y: y**(1/x)),
'_': (2, lambda x, y: x-y),
'@': angleconvert.next_state,
'C': (1, angleconvert.acos),
'c': (1, angleconvert.cos),
'L': (1, Decimal.exp),
'l': (1, Decimal.ln),
'q': (1, lambda x: x**.5),
'r': roll,
'R': rroll,
'S': (1, angleconvert.asin),
's': (1, angleconvert.sin),
'~': swap,
'T': (1, angleconvert.atan),
't': (1, angleconvert.tan),
'n': (1, lambda x: -x),
'N': (1, lambda x: 1/x),
'=': (1, impl.paste)
}

def pstack(msg):
impl.setline(0, f'[{angleconvert}] {msg}')

for i, reg in enumerate("TZYX"):
if len(stack) > 3-i:
val = stack[-4+i]
else:
val = ""
impl.setline(1+i, f"{reg} {val}")

def loop():
impl.start_redraw()
pstack(f'{gc.mem_free()} RPN bytes free')
impl.setline(5, "> " + "".join(entry) + "_")
impl.refresh()
impl.end_redraw()

while True:
do_pstack = False
do_pentry = False
message = ''

c = impl.getch()
if c in '\x7f\x08':
if entry:
entry.pop()
do_pentry = True
elif stack:
stack.pop()
do_pstack = True
if c == '\x1b':
del entry[:]
do_pentry = True
elif c in '0123456789.eE':
if c == '.' and '.' in entry:
c = 'e'
entry.append(c)
do_pentry = True
elif c == '\x04':
break
elif c in ' \n':
if entry:
try:
stack.append(Decimal("".join(entry)))
except Exception as e:
message = str(e)
del entry[:]
elif c == '\n' and stack:
stack.append(stack[-1])
do_pstack = True
elif c in ops:
if entry:
try:
stack.append(Decimal("".join(entry)))
except Exception as e:
message = str(e)
del entry[:]
op = ops.get(c)
try:
if callable(op):
message = op() or ''
else:
message = do_op(*op) or ''
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
message = str(e)
do_pstack = True

impl.start_redraw()

if do_pstack:
pstack(message)
do_pentry = True

if do_pentry:
impl.setline(5, "> " + "".join(entry) + "_")

if do_pentry or do_pstack:
impl.refresh()

impl.end_redraw()

try:
loop()
finally:
impl.end()
```

This guide was first published on Oct 21, 2020. It was last updated on Oct 21, 2020.

This page (Installing the Code) was last updated on Jun 05, 2022.