Skip to content
Snippets Groups Projects
Commit 5fa3bb58 authored by q3k's avatar q3k
Browse files

mpy: new captouch module

parent 7997b915
No related branches found
No related tags found
No related merge requests found
Pipeline #5729 passed
......@@ -64,14 +64,23 @@ void captouch_force_calibration();
uint8_t captouch_calibration_active();
typedef struct {
// Not all pads are present on all petals.
// Top petals have a base, cw and ccw pad.
// Bottom petlas have a base and a tip.
// Is the tip pressed down?
bool tip_pressed;
// Is the base pressed down?
bool base_pressed;
// Is the clockwise pad pressed down?
bool cw_pressed;
// Is the counter-clockwise pad pressed down?
bool ccw_pressed;
} captouch_pad_state_t;
typedef struct {
captouch_pad_state_t pads;
// Are any of the pads pressed down?
bool pressed;
} captouch_petal_state_t;
......@@ -79,9 +88,13 @@ typedef struct {
captouch_petal_state_t petals[10];
} captouch_state_t;
/* Extened/new API for reading captouch state. Allows for access to individual
* pad data.
*
* Likely to evolce into the new st3m api for captouch.
*/
void read_captouch_ex(captouch_state_t *state);
/* returns uint16_t which encodes each petal "touched" state as the bit
* corresponding to the petal index. "touched" is determined by checking if
* any of the pads belonging to the petal read a value higher than their
......
from typing import Protocol, List
class CaptouchPetalPadsState(Protocol):
"""
Current state of pads on a captouch petal.
Not all petals have all pads. Top petals have a a base, cw and ccw pad.
Bottom petals have a base and tip pad.
"""
@property
def tip(self) -> bool:
"""
True if the petals's tip is currently touched.
"""
...
@property
def base(self) -> bool:
"""
True if the petal's base is currently touched.
"""
...
@property
def cw(self) -> bool:
"""
True if the petal's clockwise pad is currently touched.
"""
...
@property
def ccw(self) -> bool:
"""
True if the petal's counter clockwise pad is currently touched.
"""
...
class CaptouchPetalState(Protocol):
@property
def pressed(self) -> bool:
"""
True if any of the petal's pads is currently touched.
"""
...
@property
def top(self) -> bool:
"""
True if this is a top petal.
"""
...
@property
def bottom(self) -> bool:
"""
True if this is a bottom petal.
"""
...
@property
def pads(self) -> CaptouchPetalPadsState:
"""
State of individual pads of the petal.
"""
...
class CaptouchState(Protocol):
"""
State of captouch sensors, captured at some time.
"""
@property
def petals(self) -> List[CaptouchPetalState]:
"""
State of individual petals.
Contains 10 elements, with the zeroth element being the pad closest to
the USB port. Then, every other pad in a counter-clockwise direction.
Pads 0, 2, 4, 6, 8 are Top pads.
Pads 1, 3, 5, 7, 9 are Bottom pads.
"""
...
def read() -> CaptouchState:
"""
Reads current captouch state from hardware and returns a snapshot in time.
"""
...
def calibration_active() -> bool:
"""
Returns true if the captouch system is current recalibrating.
"""
...
\ No newline at end of file
from typing import List
class CaptouchPetalPadsState:
def __init__(self, tip, base, cw, ccw) -> None:
self._tip = tip
self._base = base
self._cw = cw
self._ccw = ccw
@property
def tip(self) -> bool:
return self._tip
@property
def base(self) -> bool:
return self._base
@property
def cw(self) -> bool:
return self._cw
@property
def ccw(self) -> bool:
return self._ccw
class CaptouchPetalState:
def __init__(self, ix: int, pads: CaptouchPetalPadsState):
self._pads = pads
self._ix = ix
@property
def pressed(self) -> bool:
if self.top:
return self._pads.base or self._pads.ccw or self._pads.cw
else:
return self._pads.tip or self._pads.base
@property
def top(self) -> bool:
return self._ix % 2 == 0
@property
def bottom(self) -> bool:
return not self.bottom
@property
def pads(self) -> CaptouchPetalPadsState:
return self._pads
class CaptouchState:
def __init__(self, petals: List[CaptouchPetalState]):
self._petals = petals
@property
def petals(self) -> List[CaptouchPetalState]:
return self._petals
def read() -> CaptouchState:
import hardware
hardware._sim.process_events()
hardware._sim.render_gui_lazy()
petals = hardware._sim.petals
res = []
for petal in range(10):
top = petal % 2 == 0
if top:
ccw = petals.state_for_petal_pad(petal, 1)
cw = petals.state_for_petal_pad(petal, 2)
base = petals.state_for_petal_pad(petal, 3)
pads = CaptouchPetalPadsState(False, base, cw, ccw)
res.append(CaptouchPetalState(petal, pads))
else:
tip = petals.state_for_petal_pad(petal, 0)
base = petals.state_for_petal_pad(petal, 3)
pads = CaptouchPetalPadsState(tip, base, False, False)
res.append(CaptouchPetalState(petal, pads))
return CaptouchState(res)
def calibration_active() -> bool:
return False
\ No newline at end of file
import math
import os
import time
import itertools
import pygame
......@@ -42,8 +43,6 @@ class Input:
def _mouse_coords_to_id(self, mouse_x, mouse_y):
for i, (x, y) in enumerate(self.POSITIONS):
x += self.MARKER_SIZE // 2
y += self.MARKER_SIZE // 2
dx = mouse_x - x
dy = mouse_y - y
if math.sqrt(dx**2 + dy**2) < self.MARKER_SIZE // 2:
......@@ -71,8 +70,6 @@ class Input:
def render(self, surface):
s = self.state()
for i, (x, y) in enumerate(self.POSITIONS):
x += self.MARKER_SIZE // 2
y += self.MARKER_SIZE // 2
if s[i]:
pygame.draw.circle(surface, self.COLOR_HELD, (x, y), self.MARKER_SIZE//2)
elif i == self._mouse_hover:
......@@ -82,16 +79,83 @@ class Input:
class PetalsInput(Input):
# First petal is above USB-C jack, then CCW.
POSITIONS = [
(356, 122), (163, 112), (114, 302), ( 49, 477), (204, 587), (352, 696), (504, 587), (660, 477), (602, 298), (547, 117),
_petal_positions_top = [
(406, 172), (164, 352), (254, 637), (554, 637), (652, 348),
]
_petal_positions_bottom = [
(213, 162), (99, 527), (402, 746), (710, 527), (597, 167)
]
POSITIONS = list(itertools.chain(*[
[
(x + math.cos(i * -1.256 + 1.57) * 40, y + math.sin(i * -1.256 + 1.57) * 40), # base
(x + math.cos(i * -1.256 + 5.75) * 40, y + math.sin(i * -1.256 + 5.75) * 40), # cw
(x + math.cos(i * -1.256 + 3.66) * 40, y + math.sin(i * -1.256 + 3.66) * 40), # ccw
]
for i, (x, y) in enumerate(_petal_positions_top)
] + [
[
(x + math.cos(i * -1.256 - 2.20) * 40, y + math.sin(i * -1.256 - 2.20) * 40), # tip
(x + math.cos(i * -1.256 - 5.34) * 40, y + math.sin(i * -1.256 - 5.34) * 40), # base
]
for i, (x, y) in enumerate(_petal_positions_bottom)
]))
MARKER_SIZE = 40
def _index_for_petal_pad(self, petal, pad):
if petal >= 10:
raise ValueError("petal cannot be > 10")
# convert from st3m/bsp index into input state index
top = False
if petal % 2 == 0:
top = True
res = petal // 2
if top:
res *= 3
else:
res *= 2
res += 3 * 5
if top:
if pad == 1: # ccw
res += 2
elif pad == 2: # cw
res += 1
elif pad == 3: # base
res += 0
else:
raise ValueError("invalid pad number")
else:
if pad == 0: # tip
res += 0
elif pad == 3: # base
res += 1
else:
raise ValueError("invalid pad number")
return res
def state_for_petal_pad(self, petal, pad):
ix = self._index_for_petal_pad(petal, pad)
return self.state()[ix]
def state_for_petal(self, petal):
res = False
if petal % 2 == 0:
# top
res = res or self.state_for_petal_pad(petal, 1)
res = res or self.state_for_petal_pad(petal, 2)
res = res or self.state_for_petal_pad(petal, 3)
else:
# bottom
res = res or self.state_for_petal_pad(petal, 0)
res = res or self.state_for_petal_pad(petal, 3)
return res
class ButtonsInput(Input):
POSITIONS = [
( 14, 230), ( 46, 230), ( 78, 230),
(714, 230), (746, 230), (778, 230),
( 24, 240), ( 56, 240), ( 88, 240),
(724, 240), (756, 240), (788, 240),
]
MARKER_SIZE = 20
COLOR_HELD = (0x80, 0x80, 0x80, 0xff)
......@@ -394,7 +458,7 @@ def menu_button_get_left():
def get_captouch(a):
_sim.process_events()
_sim.render_gui_lazy()
return _sim.petals.state()[a]
return _sim.petals.state_for_petal(a)
#TODO(iggy/q3k do proper positional captouch)
def captouch_get_petal_rad(a):
......@@ -423,4 +487,13 @@ def scope_draw(ctx):
ctx.line_to(130, 0)
ctx.line_to(130, 130)
ctx.line_to(-130, 130)
ctx.line_to(-130, 0)
\ No newline at end of file
ctx.line_to(-130, 0)
def usb_connected():
return True
def usb_console_active():
return True
def i2c_scan():
return [16, 44, 45, 85, 109, 110]
......@@ -12,6 +12,7 @@ target_sources(usermod_badge23 INTERFACE
${CMAKE_CURRENT_LIST_DIR}/mp_badge_link.c
${CMAKE_CURRENT_LIST_DIR}/mp_kernel.c
${CMAKE_CURRENT_LIST_DIR}/mp_uctx.c
${CMAKE_CURRENT_LIST_DIR}/mp_captouch.c
)
target_include_directories(usermod_badge23 INTERFACE
......
#include "py/runtime.h"
#include "py/builtin.h"
#include "badge23/captouch.h"
#include <string.h>
STATIC mp_obj_t mp_captouch_calibration_active(void)
{
return mp_obj_new_int(captouch_calibration_active());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_captouch_calibration_active_obj, mp_captouch_calibration_active);
typedef struct {
mp_obj_base_t base;
mp_obj_t petal;
} mp_captouch_petal_pads_state_t;
const mp_obj_type_t captouch_petal_pads_state_type;
typedef struct {
mp_obj_base_t base;
mp_obj_t captouch;
mp_obj_t pads;
size_t ix;
} mp_captouch_petal_state_t;
const mp_obj_type_t captouch_petal_state_type;
typedef struct {
mp_obj_base_t base;
mp_obj_t petals;
captouch_state_t underlying;
} mp_captouch_state_t;
const mp_obj_type_t captouch_state_type;
STATIC void mp_captouch_petal_pads_state_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
mp_captouch_petal_pads_state_t *self = MP_OBJ_TO_PTR(self_in);
if (dest[0] != MP_OBJ_NULL) {
return;
}
mp_captouch_petal_state_t *petal = MP_OBJ_TO_PTR(self->petal);
mp_captouch_state_t *captouch = MP_OBJ_TO_PTR(petal->captouch);
captouch_petal_state_t *state = &captouch->underlying.petals[petal->ix];
bool top = (petal->ix % 2) == 0;
if (top) {
switch (attr) {
case MP_QSTR_base: dest[0] = mp_obj_new_bool(state->pads.base_pressed); break;
case MP_QSTR_cw: dest[0] = mp_obj_new_bool(state->pads.cw_pressed); break;
case MP_QSTR_ccw: dest[0] = mp_obj_new_bool(state->pads.ccw_pressed); break;
}
} else {
switch (attr) {
case MP_QSTR_tip: dest[0] = mp_obj_new_bool(state->pads.tip_pressed); break;
case MP_QSTR_base: dest[0] = mp_obj_new_bool(state->pads.base_pressed); break;
}
}
}
STATIC void mp_captouch_petal_state_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
mp_captouch_petal_state_t *self = MP_OBJ_TO_PTR(self_in);
if (dest[0] != MP_OBJ_NULL) {
return;
}
mp_captouch_state_t *captouch = MP_OBJ_TO_PTR(self->captouch);
captouch_petal_state_t *state = &captouch->underlying.petals[self->ix];
bool top = (self->ix % 2) == 0;
switch (attr) {
case MP_QSTR_top: dest[0] = mp_obj_new_bool(top); break;
case MP_QSTR_bottom: dest[0] = mp_obj_new_bool(!top); break;
case MP_QSTR_pressed: dest[0] = mp_obj_new_bool(state->pressed); break;
case MP_QSTR_pads: dest[0] = self->pads; break;
}
}
STATIC void mp_captouch_state_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
mp_captouch_state_t *self = MP_OBJ_TO_PTR(self_in);
if (dest[0] != MP_OBJ_NULL) {
return;
}
switch (attr) {
case MP_QSTR_petals: dest[0] = self->petals; break;
}
}
MP_DEFINE_CONST_OBJ_TYPE(
captouch_petal_pads_state_type,
MP_QSTR_CaptouchPetalPadsState,
MP_TYPE_FLAG_NONE,
attr, mp_captouch_petal_pads_state_attr
);
MP_DEFINE_CONST_OBJ_TYPE(
captouch_petal_state_type,
MP_QSTR_CaptouchPetalState,
MP_TYPE_FLAG_NONE,
attr, mp_captouch_petal_state_attr
);
MP_DEFINE_CONST_OBJ_TYPE(
captouch_state_type,
MP_QSTR_CaptouchState,
MP_TYPE_FLAG_NONE,
attr, mp_captouch_state_attr
);
STATIC mp_obj_t mp_captouch_state_new(const captouch_state_t *underlying) {
mp_captouch_state_t *captouch = m_new_obj(mp_captouch_state_t);
captouch->base.type = &captouch_state_type;
memcpy(&captouch->underlying, underlying, sizeof(captouch_state_t));
captouch->petals = mp_obj_new_list(0, NULL);
for (int i = 0; i < 10; i++) {
mp_captouch_petal_state_t *petal = m_new_obj(mp_captouch_petal_state_t);
petal->base.type = &captouch_petal_state_type;
petal->captouch = MP_OBJ_FROM_PTR(captouch);
petal->ix = i;
mp_captouch_petal_pads_state_t *pads = m_new_obj(mp_captouch_petal_pads_state_t);
pads->base.type = &captouch_petal_pads_state_type;
pads->petal = MP_OBJ_FROM_PTR(petal);
petal->pads = MP_OBJ_FROM_PTR(pads);
mp_obj_list_append(captouch->petals, MP_OBJ_FROM_PTR(petal));
}
return MP_OBJ_FROM_PTR(captouch);
}
STATIC mp_obj_t mp_captouch_read(void) {
mp_captouch_state_t st;
read_captouch_ex(&st);
return mp_captouch_state_new(&st);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_captouch_read_obj, mp_captouch_read);
STATIC const mp_rom_map_elem_t globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_captouch_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_calibration_active), MP_ROM_PTR(&mp_captouch_calibration_active_obj) }
};
STATIC MP_DEFINE_CONST_DICT(globals, globals_table);
const mp_obj_module_t mp_module_captouch_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&globals,
};
MP_REGISTER_MODULE(MP_QSTR_captouch, mp_module_captouch_user_cmodule);
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment