Currently only one wrapper is provided for Trill Craft, which simply exposes the sensor's 30 touch readings. This is largely adapted from Edu Meneses' implementation for a previous version of the T-Stick firmware.
#pragma once
#include <limits>
#include <cstdint>
#include "sygah-metadata.hpp"
#include "sygah-endpoints.hpp"
namespace sygaldry { namespace sygsa {
struct TrillCraft
: name_<"Trill Craft">
, description_<"A capacitive touch sensor with 30 electrodes to be connected by "
"the user. Operates in DIFF mode only. See "
"https://learn.bela.io/using-trill/settings-and-sensitivity/ "
"for more information on configuration."
>
, author_<"Edu Meneses, Travis J. West">
, copyright_<"Copyright 2023 Sygaldry Contributors">
, license_<"SPDX-License-Identifier: MIT">
, version_<"0.0.0">
{
static constexpr unsigned int channels = 30;
struct inputs_t {
slider_message<"prescaler", "measurement gain adjustment"
, uint8_t, 1, 8, 1
, tag_session_data
> prescaler;
slider_message<"noise threshold", "threshold for ignoring low-level noise"
, uint8_t, 0, 255, 0
, tag_session_data
> noise_threshold;
slider_message<"sampling speed"
, "0 - ultra fast (57 to 2800 us per sensor), "
"3 - slow (205 to 22000 us per sensor). "
"Broad sampling rate adjustment; see also resolution"
, uint8_t, 0, 3, 0
, tag_session_data
> speed;
slider_message<"resolution"
, "measurement resolution in bits; "
"higher resolutions reduce effective sampling rate"
, uint8_t, 9, 16, 9
, tag_session_data
> resolution;
bng<"update baseline"
, "reset the baseline capacitance based on the current sensor state."
"Avoid touching the sensor while updating the baseline reading."
> update_baseline;
array<"map", channels
, "mapping from raw channel to normalized channel, "
"e.g. if the 0th element in the map is 5, then the 0th raw"
"channel will be written to the normalized channel with index 5."
, int, 0, 29, 0
, tag_session_data
> map;
} inputs;
struct outputs_t {
toggle<"running", "indicates successful connection and polling status"> running;
text_message<"error status", "indicates error conditions"> error_status;
array<"raw", channels
, "unprocessed difference in capacitance with respect to baseline"
, int, 0, std::numeric_limits<int>::max(), 0
> raw;
toggle<"any", "indicates presence of any touch"> any;
array<"max seen", channels, "the maximum reading seen"
, int, 0, decltype(raw)::max(), 0
> max_seen;
array<"normalized", channels
, "normalized capacitance reading"
> normalized;
array<"mask", channels, "indicates which electrodes are touched"
, char, 0, 1, 0
> mask;
slider<"instant max", "the maximum normalized reading currently"
, float
> instant_max;
} outputs;
void * pimpl = nullptr;
void init();
void main();
};
} }
#include "sygsa-trill_craft.hpp"
#include <algorithm>
#include <Trill.h>
namespace sygaldry { namespace sygsa {
void TrillCraft::init()
{
auto trill = new Trill();
pimpl = static_cast<void*>(trill);
bool initialize_map = false;
std::array<bool, channels> channel_indexed{0};
for (std::size_t i = 0; i < channels && !initialize_map; ++i)
{
if (inputs.map[i] < 30)
{
if (channel_indexed[inputs.map[i]]) initialize_map = true;
else channel_indexed[inputs.map[i]] = true;
}
else
{
initialize_map = true;
}
}
if (initialize_map) for (std::size_t i = 0; i < channels; ++i) inputs.map[i] = i;
int setup_return_code = trill->setup(Trill::TRILL_CRAFT);
if (0 != setup_return_code)
{
outputs.running = false;
switch (setup_return_code)
{
case -2:
outputs.error_status = "unknown default address";
break;
case 2:
outputs.error_status = "unable to identify device";
break;
case -3:
outputs.error_status = "found unexpected device type";
break;
case -1:
outputs.error_status = "invalid device mode";
break;
default:
outputs.error_status = "unknown error";
}
return;
}
else outputs.running = true;
trill->setMode(Trill::RAW);
trill->setScanSettings(inputs.speed, inputs.resolution);
delay(trill->interCommandDelay);
trill->setNoiseThreshold(inputs.noise_threshold);
delay(trill->interCommandDelay);
trill->setPrescaler(inputs.prescaler);
delay(trill->interCommandDelay);
trill->updateBaseline();
delay(trill->interCommandDelay);
}
void TrillCraft::main()
{
if (not outputs.running) return;
auto trill = static_cast<Trill*>(pimpl);
trill->requestRawData();
for (int i=0; i<30; i++) {
if (trill->rawDataAvailable() > 0) {
outputs.raw[i] = trill->rawDataRead();
}
}
for (int i = 0; i < channels; i++) {
if (outputs.raw[i] != 0) {
outputs.max_seen[i] = std::max(outputs.max_seen[i], outputs.raw[i]);
outputs.normalized[inputs.map[i]] = static_cast<float>(outputs.raw[i])
/ static_cast<float>(outputs.max_seen[i]);
outputs.mask[inputs.map[i]] = true;
} else {
outputs.normalized[inputs.map[i]] = 0.0f;
outputs.mask[inputs.map[i]] = false;
}
}
outputs.instant_max = *std::max_element( outputs.normalized.value.data()
, outputs.normalized.value.data()+channels
);
if (outputs.instant_max == 0.0f) {
outputs.any = 0;
} else {
outputs.any = 1;
}
if (inputs.speed.updated || inputs.resolution.updated)
{
trill->setScanSettings(inputs.speed, inputs.resolution);
delay(trill->interCommandDelay);
}
if (inputs.noise_threshold.updated)
{
trill->setNoiseThreshold(inputs.noise_threshold);
delay(trill->interCommandDelay);
}
if (inputs.prescaler.updated)
{
trill->setPrescaler(inputs.prescaler);
delay(trill->interCommandDelay);
}
if (inputs.resolution.updated || inputs.prescaler.updated || inputs.update_baseline)
{
for (auto& max : outputs.max_seen.value) max = 0;
trill->updateBaseline();
delay(trill->interCommandDelay);
}
}
} }
void delay(unsigned long ms)
Definition sygsa-delay.cpp:14
TODO: write tests. And documentation...