Skip to content

Commit

Permalink
Merge pull request #216 from brilliantlabsAR/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
siliconwitch authored May 30, 2023
2 parents 2778c03 + 783fda9 commit cd03a7e
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 104 deletions.
2 changes: 1 addition & 1 deletion micropython
Submodule micropython updated 273 files
5 changes: 2 additions & 3 deletions modules/_splashscreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
if fpga.read(1, 4) == b"Mncl":
t = display.Text("MONOCLE", 320, 200, display.WHITE, justify=display.MIDDLE_CENTER)
display.show(t)
del t

del t
del fpga
del display
del fpga, display
64 changes: 37 additions & 27 deletions modules/_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,23 @@ def device_module():


def display_module():
# Text: randomly placed text strings (or no change if overlaps occurs)
for i in range(0, 100):
__test("display.WIDTH", 640)
__test("display.HEIGHT", 400)
__test("display.FONT_HEIGHT", 48)
__test("display.FONT_WIDTH", 24)
__test("display.FONT_WIDTH", True)
__test("display.TOP_LEFT > 0", True)
__test("display.MIDDLE_LEFT > 0", True)
__test("display.BOTTOM_LEFT > 0", True)
__test("display.TOP_CENTER > 0", True)
__test("display.BOTTOM_CENTER > 0", True)
__test("display.TOP_RIGHT > 0", True)
__test("display.MIDDLE_CENTER > 0", True)
__test("display.MIDDLE_RIGHT > 0", True)
__test("display.BOTTOM_RIGHT > 0", True)

print("Text: randomly placed text strings (or no change if overlaps occurs)")
for i in range(0, 200):
l = []
for i in range(0, 15):
x = random.randint(-display.WIDTH, display.WIDTH * 2)
Expand All @@ -73,10 +88,10 @@ def display_module():
display.show(l)
except display.TextOverlapError:
pass
time.sleep_ms(200)
time.sleep_ms(100)
time.sleep(1)

# Text: print a string in each corner to check if alignment is still fine
print("Text: print a string in each corner to check if alignment is still fine")
l = []
w = display.WIDTH - 1
h = display.HEIGHT - 1
Expand All @@ -85,9 +100,9 @@ def display_module():
l.append(display.Text("BOTTOM LEFT", 0, h, 0x007700, justify=display.BOTTOM_LEFT))
l.append(display.Text("BOTTOM RIGHT", w, h, 0x0077FF, justify=display.BOTTOM_RIGHT))
display.show(l)
time.sleep(2)
time.sleep(3)

# Line: spinning animation
print("Line: spinning animation")
scale = 200
x_offset = display.WIDTH // 2
y_offset = display.HEIGHT // 2
Expand All @@ -103,7 +118,7 @@ def display_module():
time.sleep_ms(10)
time.sleep(1)

# Line: rectangle around the display edges
print("Line: rectangle around the display edges")
t = 10
h = display.HEIGHT - t
w = display.WIDTH - t
Expand All @@ -115,7 +130,7 @@ def display_module():
)
time.sleep(1)

# Rectangle: growing rectangle, getting larger than the display
print("Rectangle: growing rectangle, getting larger than the display")
x_offset = display.WIDTH // 2
y_offset = display.HEIGHT // 2
for i in range(0, display.WIDTH // 2 + 100, 10):
Expand All @@ -127,47 +142,42 @@ def display_module():
time.sleep_ms(10)
time.sleep(1)

# Rectangle: test with ((x1,y1), (x2,y2)) with x1 > x2 or y1 > y2
print("Rectangle: test with ((x1,y1), (x2,y2)) with x1 > x2 or y1 > y2")
r = display.Rectangle(550, 300, 100, 100, display.GREEN)
display.show(r)
time.sleep(1)

# Text: test the edge cases for clipping on the right side of the display
print("Text: test the edge cases for clipping on the right side of the display")
for i in range(display.FONT_WIDTH * 2):
s = "0....:....0....:....0....:"
display.show(display.Text(s, 0 + i, 0, display.WHITE))
time.sleep_ms(100)
time.sleep(1)
time.sleep(2)

# Text: the "hello" spinning around the "world"
print("Text: the 'hello' spinning around the 'world'")
t1 = display.Text("hello", 200, 100, display.WHITE)
t2 = display.Text("world", 400, 20, display.WHITE)
for i in range(0, 130):
_ = t2.move(0, +1)
for i in range(0, 13):
_ = t2.move(0, +10)
display.show(t1, t2)
for i in range(0, 350):
_ = t2.move(-1, 0)
for i in range(0, 35):
_ = t2.move(-10, 0)
display.show(t1, t2)
for i in range(0, 130):
_ = t2.move(0, -1)
for i in range(0, 13):
_ = t2.move(0, -10)
display.show(t1, t2)
for i in range(0, 350):
_ = t2.move(+1, 0)
for i in range(0, 35):
_ = t2.move(+10, 0)
display.show(t1, t2)
time.sleep(1)

# Wrappers: test correct forwarding to other classes
print("Wrappers: test correct forwarding to other classes")
f = display.Fill(display.RED)
hl = display.HLine(10, 100, 620, display.WHITE, thickness=18)
vl = display.VLine(10, 100, 200, display.WHITE, thickness=18)
display.show(f, hl, vl)
time.sleep(1)
display.clear()

# Test constants
__test("display.WIDTH", 640)
__test("display.HEIGHT", 400)


def camera_module():
print("TODO camera module")
Expand Down Expand Up @@ -335,6 +345,7 @@ def update_module():


def all():
device_module()
display_module()
camera_module()
microphone_module()
Expand All @@ -343,5 +354,4 @@ def all():
fpga_module()
bluetooth_module()
time_module()
device_module()
update_module()
11 changes: 11 additions & 0 deletions modules/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ STATIC mp_obj_t device_force_sleep(void)
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(device_force_sleep_obj, device_force_sleep);

STATIC mp_obj_t device_is_charging(void)
{
// Get the CHG value from STAT_CHG_B
i2c_response_t charging_response = monocle_i2c_read(PMIC_I2C_ADDRESS, 0x03, 0x0C);
app_err(charging_response.fail);

return charging_response.value ? mp_const_true : mp_const_false;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(device_is_charging_obj, device_is_charging);

extern const struct _mp_obj_type_t device_storage_type;

STATIC const mp_rom_map_elem_t device_module_globals_table[] = {
Expand All @@ -161,6 +171,7 @@ STATIC const mp_rom_map_elem_t device_module_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&device_reset_cause_obj)},
{MP_ROM_QSTR(MP_QSTR_prevent_sleep), MP_ROM_PTR(&device_prevent_sleep_obj)},
{MP_ROM_QSTR(MP_QSTR_force_sleep), MP_ROM_PTR(&device_force_sleep_obj)},
{MP_ROM_QSTR(MP_QSTR_is_charging), MP_ROM_PTR(&device_is_charging_obj)},
{MP_ROM_QSTR(MP_QSTR_Storage), MP_ROM_PTR(&device_storage_type)},
};
STATIC MP_DEFINE_CONST_DICT(device_module_globals, device_module_globals_table);
Expand Down
54 changes: 34 additions & 20 deletions modules/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

import vgr2d
import fpga
import struct
import time
from _display import *

WIDTH = 640
Expand Down Expand Up @@ -62,6 +64,11 @@
GRAY8 = 0xE2E2E2


FBTEXT_PAGE_SIZE = 1024
FBTEXT_NUM_PAGES = 2
fbtext_addr = 0


class Colored:
def color(self, color_rgb):
self.color_rgb = color_rgb
Expand Down Expand Up @@ -281,23 +288,22 @@ def color(*args):
arg.color(args[-1])


def text_check_collision_y(l):
if len(l) <= 1:
def text_get_overlapping_y(l):
if len(l) == 0:
return
l = sorted(l, key=lambda obj: obj.y)
prev = l[0]
for obj in l[1:]:
if obj.y < prev.y + FONT_HEIGHT:
raise TextOverlapError(f"{obj} overlaps with {prev}")
return (prev, obj)
prev = obj


def text_check_collision_xy(l):
if len(l) <= 1:
def text_get_overlapping_xy(base, l):
if len(l) == 0:
return
base = l[0]
sub = [l[0]]
for obj in l[1:]:
sub = [base]
for obj in l:
if obj.x < base.x + base.width(base.string):
# Some overlapping on x coordinates, accumulate the row
sub.append(obj)
Expand All @@ -306,12 +312,14 @@ def text_check_collision_xy(l):
break

# now also check the y coordinate for all the potential clashes
text_check_collision_y(sub)
return text_get_overlapping_y(sub)


def text_check_collision(l):
def text_get_overlapping(l):
for i in range(len(l)):
text_check_collision_xy(l[i:])
overlapping = text_get_overlapping_xy(l[i], l[i + 1:])
if overlapping is not None:
return overlapping


def update_colors(addr, l):
Expand Down Expand Up @@ -343,34 +351,40 @@ def update_colors(addr, l):
fpga.write(addr, buffer)


def show_text(l):
def show_fbtext(l):
global fbtext_addr

update_colors(0x4502, l)
# Text has no wrapper, we implement it locally.
# See https://streamlogic.io/docs/reify/nodes/#fbtext
buffer = bytearray(2) # address 0x0000
l = [obj for obj in l if hasattr(obj, "fbtext")]
l = sorted(l, key=lambda obj: obj.y)
buffer = bytearray(struct.pack(">H", fbtext_addr))
l = sorted(l, key=lambda obj: obj.x)
text_check_collision(l)
overlapping = text_get_overlapping(l)
if overlapping is not None:
raise TextOverlapError(f"{overlapping[0]} overlaps with {overlapping[1]}")
for obj in l:
obj.fbtext(buffer)
if len(buffer) > 0:
addr = bytearray(struct.pack(">H", fbtext_addr))
fpga.write(0x4501, addr)
fpga.write(0x4503, buffer + b"\xFF\xFF\xFF")
fbtext_addr += FBTEXT_PAGE_SIZE
fbtext_addr %= FBTEXT_PAGE_SIZE * FBTEXT_NUM_PAGES
time.sleep_ms(20) # ensure the buffer swap has happened


def show_vgr2d(l):
update_colors(0x4402, l)
# 0 is the address of the frame in the framebuffer in use.
# See https://streamlogic.io/docs/reify/nodes/#fbgraphics
# Offset: active display offset in buffer used if double buffering
l = [obj.vgr2d() for obj in l if hasattr(obj, "vgr2d")]
vgr2d.display2d(0, l, WIDTH, HEIGHT)


def show(*args):
l = flatten(args)
show_vgr2d(l)
show_text(l)
args = flatten(args)
show_vgr2d([obj for obj in args if hasattr(obj, "vgr2d")])
show_fbtext([obj for obj in args if hasattr(obj, "fbtext")])


def clear():
Expand Down
59 changes: 29 additions & 30 deletions modules/microphone.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,32 @@

import fpga

__microphone_opened = False


class Microphone:
def __init__(self, callback, sample_rate):
global __microphone_opened
if callable(callback) == False:
raise TypeError("callback must be a callable function")
if __microphone_opened:
raise RuntimeError("microphone is already started")
__microphone_opened = True
self.callback = callback
self.sample_rate = sample_rate

def read(self, samples=-1):
## TODO fpga.read limited to 256
available = int.from_bytes(fpga.read(0x1C01, 2), "big")
if samples == -1:
data = fpga.read(0x1C02, available)
else:
data = fpga.read(0x1C02, min(samples, available))
return data

def close(self):
global __microphone_opened
__microphone_opened = False


def open(callback, sample_rate=16000):
return Microphone(callback, sample_rate)

def record(sample_rate=16000):
# TODO pass sample rate to FPGA
fpga.write(0x1C04, int.to_bytes(sample_rate, 2, "big"))

# TODO send flush buffer command
fpga.write(0x1C03, b"\x01")

# TODO send record command
fpga.write(0x1C04, b"\x02")


def read(samples=-1):
available = int.from_bytes(fpga.read(0x1C01, 2), "big")
available = min(available, 255)

if samples == -1:
data = fpga.read(0x1C02, available)
else:
data = fpga.read(0x1C02, min(samples, available))
return data


def stop():
# TODO send stop command
fpga.write(0x1C04, b"\x03")


# TODO add callback handler when keyword detection is available
2 changes: 2 additions & 0 deletions monocle-core/monocle-critical.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ void monocle_critical_startup(void)
if (resp.fail || resp.value != 0x02)
{
not_real_hardware_flag = true;
NRFX_LOG("PMIC not found");
}

// Turn off the FPGA, flash, display and camera rails
Expand Down Expand Up @@ -232,6 +233,7 @@ void monocle_critical_startup(void)
if (resp.fail || resp.value != 0x41)
{
app_err(resp.value);
app_err(resp.fail);
}

app_err(monocle_i2c_write(TOUCH_I2C_ADDRESS, 0xD0, 0x60, 0x60).fail); // Ack resets and enable event mode
Expand Down
Loading

0 comments on commit cd03a7e

Please sign in to comment.