Skip to content
Snippets Groups Projects
Commit 267c8f11 authored by moon2's avatar moon2 :speech_balloon: Committed by q3k
Browse files

positional captouch output

parent 3989dc46
No related branches found
No related tags found
No related merge requests found
......@@ -26,7 +26,7 @@ Please transfer all .py files in python_payload/ for using the demo payload.
$ cd ~
$ git clone https://github.com/espressif/esp-idf.git
$ cd esp-idf
$ git checkout v4.4
$ git checkout v4.4.4
$ git submodule update --init --recursive
$ cd esp-idf
......
This diff is collapsed.
......@@ -8,6 +8,7 @@
#include "esp_log.h"
#include "driver/i2c.h"
#include "driver/spi_master.h"
#include <freertos/timers.h>
#include <stdio.h>
#include <string.h>
......@@ -33,6 +34,9 @@ static const char *TAG = "espan";
#error "i2c not implemented for this badge generation"
#endif
static QueueHandle_t i2c_queue = NULL;
static uint8_t dummy_data;
static esp_err_t i2c_master_init(void)
{
int i2c_master_port = I2C_MASTER_NUM;
......@@ -54,6 +58,18 @@ static esp_err_t i2c_master_init(void)
#define CAPTOUCH_POLLING_PERIOD 10
static uint8_t hw_init_done = 0;
void i2c_timer(TimerHandle_t data){
xQueueSend(i2c_queue, &dummy_data, 0);
}
void i2c_task(void * data){
while(1){
xQueueReceive(i2c_queue, &dummy_data, portMAX_DELAY);
captouch_read_cycle();
update_button_state();
}
}
void os_app_main(void)
{
ESP_LOGI(TAG, "Starting on %s...", badge23_hw_name);
......@@ -68,21 +84,20 @@ void os_app_main(void)
//vTaskDelay(2000 / portTICK_PERIOD_MS);
set_global_vol_dB(0);
captouch_force_calibration();
display_init();
hw_init_done = 1;
while(1) {
manual_captouch_readout(1);
vTaskDelay((CAPTOUCH_POLLING_PERIOD) / portTICK_PERIOD_MS);
manual_captouch_readout(0);
vTaskDelay((CAPTOUCH_POLLING_PERIOD) / portTICK_PERIOD_MS);
update_button_state();
vTaskDelay((CAPTOUCH_POLLING_PERIOD) / portTICK_PERIOD_MS);
//display_draw_scope();
}
ESP_ERROR_CHECK(i2c_driver_delete(I2C_MASTER_NUM));
ESP_LOGI(TAG, "I2C de-initialized successfully");
i2c_queue = xQueueCreate(1,1);
TaskHandle_t i2c_task_handle;
//xTaskCreate(&i2c_task, "I2C task", 4096, NULL, configMAX_PRIORITIES , &i2c_task_handle);
xTaskCreatePinnedToCore(&i2c_task, "I2C task", 4096, NULL, configMAX_PRIORITIES-1, &i2c_task_handle, 0);
TimerHandle_t i2c_timer_handle = xTimerCreate("I2C timer", pdMS_TO_TICKS(CAPTOUCH_POLLING_PERIOD), pdTRUE, (void *) 0, *i2c_timer);
if( xTimerStart(i2c_timer_handle, 0 ) != pdPASS) ESP_LOGI(TAG, "I2C timer initialization failed");
hw_init_done = 1;
}
uint8_t hardware_is_initialized(){
......
#pragma once
#include <stdint.h>
/* GENERAL INFORMATION
*
* petal index: 0 is the top petal above the USB-C jack, increases ccw so that
* bottom petals are uneven and top petals even.
* TODO: LEDs are indexed differently, this should be harmonized
* in the impending API refactor.
*
* captouch data: full uint16_t range per stage, higher values indicate touch.
* pad index: defined in captouch.c
*
*
* IOU: the internals are still subject to major restructuring and are not
* documented as of yet. will do once the data structures actually make sense
* and are not duct tape upon duct tape.
*/
/* polls data from both captouch chips and processes it, either by updating
* the captouch data exposed to the user or running a calibration cycle.
*
* the captouch chips has updated their registers every 9.2ms, so the fn
* should be called every 10ms or so.
*
* this will be replaced someday by an interrupt event triggered system,
* but this would ideally already implement configuration switching to
* optimize latency by grouping pads and to expose the unused pad which
* is a nontrivial task for another day.
*/
void captouch_read_cycle(void);
/* the captouch chip can generate an "afe" offset in the analog domain before
* the ADC to optimize the readout range. according to the datasheet this should
* be in the middle of the 16bit delta sigma ADC range (without much reasoning
* supplied), however we found that having a higher range is beneficial.
*
* the calibration cycle is optimizing the afe coefficients so that the output of
* the "untouched" pads is close to the target set with this with this function/
*
* default target: 6000, manufacturer recommendation: 32676
*/
void captouch_set_calibration_afe_target(uint16_t target);
/* starts a a calibration cycle which is intended to run when the captouch
* pads are not being touched. the cycle consists of two parts:
*
* 1) optimize the afe coefficients (see captouch_set_calibration_afe_target)
*
* 2) with the new afe coefficients applied, average the readings in the untouched
* state into a software calibration list which is normally subtracted from the
* captouch outputs. this makes up for the limited resolution of the of the afe
* coefficient calibration.
*/
void captouch_force_calibration();
/* indicates if a calibration cycle is currently active. the readings for
* captouch_read_cycle and captouch_get_petal_* during a calibration cycle.
*
* 1: calibration cycle active
* 0: calibration cycle inactive
*/
uint8_t captouch_calibration_active();
/* 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
* calibration value plus their threshold (see captouch_set_petal_pad_threshold)
*/
uint16_t read_captouch(void);
/* sets threshold for a petal pad above which read_captouch considers a pad as
* touched.
*
* petal: petal index
* pad: pad index
* thres: threshold value
*/
void captouch_set_petal_pad_threshold(uint8_t petal, uint8_t pad, uint16_t thres);
/* returns last read captouch value from a petal pad without subtracting its
* calibration reference. typically only needed for debugging.
*
* petal: petal index
* pad: pad index
*/
uint16_t captouch_get_petal_pad_raw(uint8_t petal, uint8_t pad);
/* returns calibration reference for a petal pad.
*
* petal: petal index
* pad: pad index
*/
uint16_t captouch_get_petal_pad_calib_ref(uint8_t petal, uint8_t pad);
/* returns calibrated value from petal. clamps below 0.
*
* petal: petal index
* pad: pad index
*/
uint16_t captouch_get_petal_pad(uint8_t petal, uint8_t pad);
/* estimates the azimuthal finger position on a petal in arbitrary units.
* returns 0 if hardware doesn't support this.
*
* petal: petal index
* pad: pad index
*/
int32_t captouch_get_petal_phi(uint8_t petal);
/* estimates the radial finger position on a petal in arbitrary units.
* returns 0 if hardware doesn't support this.
*
* petal: petal index
* pad: pad index
*/
int32_t captouch_get_petal_rad(uint8_t petal);
/* configures the captouch chips, prefills internal structs etc.
*/
void captouch_init(void);
/* TODO: didn't look into what it does, never used it, just copied it from
* the hardware verification firmware along with the rest. didn't break it
* _intentionally_ but never tested it either.
*/
void captouch_print_debug_info(void);
void gpio_event_handler(void * arg);
void manual_captouch_readout(uint8_t top);
void captouch_get_cross(int paddle, int * x, int * y);
void captouch_force_calibration();
uint16_t read_captouch();
......@@ -12,19 +12,10 @@ INSTRUMENT 1, CHORD ORGAN: all LEDs are lit up in the same color. each top petal
INSTRUMENT 2, MELODY PLAYER: the 3 most down-pointing petal LEDs are lit in pink. these 3 petals select between 3 different octaves. depending on octave selection, the remaining petals are lit in blue (low octave), cyan (mid octave) and green (high octave). these "playing petals" provide tones in a major scale. it is a single oscillator system, so pressing more than one "playing petal" results in the oscillator jumping between pitches rapidly, providing a HIDDEN NOISE MODE. the screen is entirely black. author's note: this one didn't turn out well at all and could use a bunch more love. we find it valuable to provide people with a simple instrument to try to play their favorite melodies on, but this ain't it yet.
### current captouch issues
INSTRUMENT 3, WORMS: worms
the current firmware is built around AD7147 captouch controllers and uses their simplified output, where the controller autocalibrates and guesses whether a pad is being touched or not. since the controller was designed to have several mm of material in between (as for a stovetop), it sometimes is wayyyy to sensitive.
INSTRUMENT 4, CAP TOUCH DEBUG: shows 10 dots corresponding to the 10 petals. each gets bigger if the captouch threshold is exceeded (i.e., a touch is registered) and moves with the current position on the petal (1d for 2-segment petals, 2d for 3-segment petals). note that on p3/p4 the inner pad of the bottom petal opposed to the usb c port is currently not registered for technical reasons.
the captouch controller can be forced into recalibration by holding the right shoulder button down while in the menu, however it does keep some internal state and sometimes turning the badge off and on again is the only way to get acceptable captouch performance.
### captouch calibration:
in general, for best performance we recommend the following flow:
- hold the badge as you would hold it for playing (i.e., give it a reasonable initial calibration baseline), then plug in the USB cable on the other side to power up the badge
- wait for the menu to show
- play
- if issues occur or you switch hand position, do a manual recalibration and hope for the best
- if hope is lost powercycle
the new captouch driver takes raw data from the captouch controller and processes it on the esp32. it is currently being worked on in the raw_captouch branch, but sadly only for p4 badges.
also some individual pads on some revisions are not responding. the new driver is intended to fix that as well.
the captouch controller can be recalibrated by pressing the left shoulder button while in the menu. the screen shows the text "CAL" while in calibration. the screen background is teal for 0.5s to give the user time to remove their hands, then turns plum for the actual calibration. calibration is intended to get a baseline for pads that are not being touched at the moment, it is recommeded to lie the badge flat on the able while recalibrating. calibration is also performed at startup.
import hardware
import utils
import cmath
import math
import time
def init():
pass
def run():
hardware.display_fill(0)
time.sleep_ms(30)
for i in range(10):
size = (hardware.get_captouch(i) * 4) + 4
size += int(max(0, sum([hardware.captouch_get_petal_pad(i, x) for x in range(0, 3+1)]) / 8000))
x = 70 + (hardware.captouch_get_petal_rad(i)/1000)
x += (hardware.captouch_get_petal_phi(i)/600)*1j
rot = cmath.exp(2j *math.pi * i / 10)
x = x * rot
col = 0b1111100000011111
if i%2:
col = 0b0000011111111111
utils.draw_rect(int(x.imag+120-(size/2)),int(x.real+120-(size/2)),size,size,col)
hardware.display_update()
def foreground():
pass
......@@ -22,7 +22,6 @@ def set_chord(i):
hue = int(72*(i+0.5)) % 360
set_led_hsv(j, hue, 1, 0.2)
chord = chords[i]
print("set chord " +str(i))
update_leds()
......@@ -39,7 +38,6 @@ def run():
k = int(i/2)
synths[k].tone(chord[k])
synths[k].start()
print("synth " +str(k))
def init():
global chord_index
......
......@@ -4,11 +4,13 @@ import time
import harmonic_demo
import melodic_demo
import demo_worms
import cap_touch_demo
MODULES = [
harmonic_demo,
melodic_demo,
demo_worms,
cap_touch_demo,
]
CURRENT_APP_RUN = None
......@@ -50,6 +52,7 @@ def foreground_menu():
utils.highlight_bottom_petal(0,0,55,55);
utils.highlight_bottom_petal(1,55,0,55);
utils.highlight_bottom_petal(2,55,55,0);
utils.highlight_bottom_petal(3,0,110,0);
display_fill(BACKGROUND_COLOR)
display_update()
......@@ -67,18 +70,36 @@ def set_rel_volume(vol):
set_global_volume_dB(VOLUME)
time.sleep_ms(100)
def captouch_cal():
global ctx
display_fill(0b0000000111100111)
ctx.move_to(0,0).rgb(0,255,0).text("cal")
display_update()
time.sleep_ms(500)
display_fill(0b0011100000000111)
ctx.move_to(0,0).rgb(0,255,0).text("cal")
captouch_autocalib()
while(captouch_calibration_active()):
pass
display_fill(0)
display_update()
def main():
global CURRENT_APP_RUN
global ctx
while not init_done():
pass
captouch_autocalib()
captouch_autocalib() # dry run
while(captouch_calibration_active()):
pass
ctx = get_ctx()
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
captouch_cal()
for module in MODULES:
module.init()
......@@ -88,7 +109,7 @@ def main():
while True:
if((get_button(1) == 2) and (CURRENT_APP_RUN == run_menu)):
captouch_autocalib()
captouch_cal()
foreground_menu()
else:
if(get_button(0) == 2):
......
......@@ -22,16 +22,21 @@
mp_obj_t mp_ctx_from_ctx(Ctx *ctx);
mp_obj_t mp_ctx = NULL;
STATIC mp_obj_t mp_init_done(size_t n_args, const mp_obj_t *args) {
STATIC mp_obj_t mp_init_done(void) {
return mp_obj_new_int(hardware_is_initialized());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_init_done_obj, 0, 1, mp_init_done);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_init_done_obj, mp_init_done);
STATIC mp_obj_t mp_display_update(size_t n_args, const mp_obj_t *args) {
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);
STATIC mp_obj_t mp_display_update(void) {
display_update();
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_display_update_obj, 0, 1, mp_display_update);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_display_update_obj, mp_display_update);
STATIC mp_obj_t mp_display_draw_pixel(size_t n_args, const mp_obj_t *args) {
uint16_t x = mp_obj_get_int(args[0]);
......@@ -65,11 +70,60 @@ STATIC mp_obj_t mp_get_captouch(size_t n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_get_captouch_obj, 1, 2, mp_get_captouch);
STATIC mp_obj_t mp_captouch_autocalib(size_t n_args, const mp_obj_t *args) {
STATIC mp_obj_t mp_captouch_get_petal_pad_raw(mp_obj_t petal_in, mp_obj_t pad_in) {
uint8_t petal = mp_obj_get_int(petal_in);
uint8_t pad = mp_obj_get_int(pad_in);
uint16_t output = captouch_get_petal_pad_raw(petal, pad);
return mp_obj_new_int(output);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_captouch_get_petal_pad_raw_obj, mp_captouch_get_petal_pad_raw);
STATIC mp_obj_t mp_captouch_get_petal_pad(mp_obj_t petal_in, mp_obj_t pad_in) {
uint8_t petal = mp_obj_get_int(petal_in);
uint8_t pad = mp_obj_get_int(pad_in);
return mp_obj_new_int(captouch_get_petal_pad(petal, pad));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_captouch_get_petal_pad_obj, mp_captouch_get_petal_pad);
STATIC mp_obj_t mp_captouch_get_petal_rad(mp_obj_t petal_in) {
uint8_t petal = mp_obj_get_int(petal_in);
int32_t ret = captouch_get_petal_rad(petal);
return mp_obj_new_int(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_captouch_get_petal_rad_obj, mp_captouch_get_petal_rad);
STATIC mp_obj_t mp_captouch_get_petal_phi(mp_obj_t petal_in) {
uint8_t petal = mp_obj_get_int(petal_in);
int32_t ret = captouch_get_petal_phi(petal);
return mp_obj_new_int(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_captouch_get_petal_phi_obj, mp_captouch_get_petal_phi);
STATIC mp_obj_t mp_captouch_set_petal_pad_threshold(mp_obj_t petal_in, mp_obj_t pad_in, mp_obj_t thres_in) {
uint8_t petal = mp_obj_get_int(petal_in);
uint8_t pad = mp_obj_get_int(pad_in);
uint16_t thres = mp_obj_get_int(thres_in);
captouch_set_petal_pad_threshold(petal, pad, thres);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(mp_captouch_set_petal_pad_threshold_obj, mp_captouch_set_petal_pad_threshold);
STATIC mp_obj_t mp_captouch_autocalib(void) {
captouch_force_calibration();
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_captouch_autocalib_obj, 0, 2, mp_captouch_autocalib);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_captouch_autocalib_obj, mp_captouch_autocalib);
STATIC mp_obj_t mp_captouch_set_calibration_afe_target(mp_obj_t target_in) {
uint16_t target = mp_obj_get_int(target_in);
captouch_set_calibration_afe_target(target);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_captouch_set_calibration_afe_target_obj, mp_captouch_set_calibration_afe_target);
STATIC mp_obj_t mp_get_button(size_t n_args, const mp_obj_t *args) {
uint8_t leftbutton = mp_obj_get_int(args[0]);
......@@ -151,8 +205,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_reset_ctx_obj, 0, 0, mp_reset_ctx)
STATIC const mp_rom_map_elem_t mp_module_hardware_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_badge_audio) },
{ MP_ROM_QSTR(MP_QSTR_init_done), MP_ROM_PTR(&mp_init_done_obj) },
{ MP_ROM_QSTR(MP_QSTR_captouch_calibration_active), MP_ROM_PTR(&mp_captouch_calibration_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_captouch), MP_ROM_PTR(&mp_get_captouch_obj) },
{ MP_ROM_QSTR(MP_QSTR_captouch_get_petal_pad_raw), MP_ROM_PTR(&mp_captouch_get_petal_pad_raw_obj) },
{ MP_ROM_QSTR(MP_QSTR_captouch_get_petal_pad), MP_ROM_PTR(&mp_captouch_get_petal_pad_obj) },
{ MP_ROM_QSTR(MP_QSTR_captouch_get_petal_rad), MP_ROM_PTR(&mp_captouch_get_petal_rad_obj) },
{ MP_ROM_QSTR(MP_QSTR_captouch_get_petal_phi), MP_ROM_PTR(&mp_captouch_get_petal_phi_obj) },
{ MP_ROM_QSTR(MP_QSTR_captouch_set_petal_pad_threshold), MP_ROM_PTR(&mp_captouch_set_petal_pad_threshold_obj) },
{ MP_ROM_QSTR(MP_QSTR_captouch_autocalib), MP_ROM_PTR(&mp_captouch_autocalib_obj) },
{ MP_ROM_QSTR(MP_QSTR_captouch_set_calibration_afe_target), MP_ROM_PTR(&mp_captouch_set_calibration_afe_target_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_button), MP_ROM_PTR(&mp_get_button_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_global_volume_dB), MP_ROM_PTR(&mp_set_global_volume_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_count_sources), MP_ROM_PTR(&mp_count_sources_obj) },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment