mirror of
https://github.com/skogaby/OpeNITHM.git
synced 2026-09-22 22:58:16 +03:00
The slider *finally* calibrates itself, probably needs a bit of tweaking to figure out the proper values for tuning sensitivity
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
#define USE_WS2812SERIAL
|
||||
#include <FastLED.h>
|
||||
|
||||
#define AIR_LED_DELAY 165
|
||||
#define AIR_LED_DELAY 135
|
||||
#define AIR_INPUT_DETECTION 0.65
|
||||
#define AIR_INPUT_THRESHOLD 0.25
|
||||
#define CALIBRATION_FLAG 0xF6
|
||||
|
||||
@@ -23,184 +23,89 @@ void AutoTouchboard::scan()
|
||||
}
|
||||
}
|
||||
|
||||
void AutoTouchboard::loadConfig()
|
||||
void AutoTouchboard::calibrateKeys()
|
||||
{
|
||||
for (int i = 0; i < NUM_SENSORS; i++)
|
||||
// Flash every key red a few times so they know to let go of the slider while we self-calibrate
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
EEPROM.get(i * 2, thresholds[i]);
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
#ifndef KEY_DIVIDERS
|
||||
leds[j] = CRGB::Red;
|
||||
#else
|
||||
leds[j*2] = CRGB::Red;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AutoTouchboard::saveConfig()
|
||||
{
|
||||
for (int i = 0; i < NUM_SENSORS; i++)
|
||||
{
|
||||
EEPROM.put(i * 2, thresholds[i]);
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
#ifndef KEY_DIVIDERS
|
||||
leds[j] = CRGB::Purple;
|
||||
#else
|
||||
leds[j*2] = CRGB::Purple;
|
||||
#endif
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
}
|
||||
|
||||
EEPROM.put(64, (byte) CALIBRATION_FLAG);
|
||||
}
|
||||
|
||||
void AutoTouchboard::calibrateKeys(bool forceCalibrate = false)
|
||||
{
|
||||
// we'll only calibrate if:
|
||||
// * the user is holding the last 4 keys
|
||||
// * the calibration flag is not set in EEPROM
|
||||
bool needsCalibration = forceCalibrate;
|
||||
// Take the initial baseline readings so we can populate our delta buffer
|
||||
int value;
|
||||
|
||||
byte calibrationFlag;
|
||||
EEPROM.get(64, calibrationFlag);
|
||||
|
||||
// only check the last 4 keys if we've calibrated at least once
|
||||
if (calibrationFlag == CALIBRATION_FLAG)
|
||||
{
|
||||
loadConfig();
|
||||
|
||||
int touched = 0;
|
||||
for (int r = 0; r < NUM_READINGS; r++) {
|
||||
scan();
|
||||
|
||||
for (int i = 24; i < 31; i += 2)
|
||||
{
|
||||
if (update(i) || update(i + 1)) touched++;
|
||||
}
|
||||
for (int i = 0; i < NUM_SENSORS; i++) {
|
||||
value = key_values[i];
|
||||
|
||||
// force re-calibration if the last 4 keys are being held
|
||||
needsCalibration = (touched == 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
needsCalibration = true;
|
||||
// calculate the newest delta if it's not the first reading
|
||||
if (r > 0) {
|
||||
deltas[i]->addValue(value - lastReadings[i]);
|
||||
}
|
||||
|
||||
lastReadings[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsCalibration)
|
||||
// Flash the slider green for a couple of seconds to show success
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
uint16_t baselines[NUM_SENSORS];
|
||||
uint16_t maxReadings[NUM_SENSORS];
|
||||
|
||||
// Reset calibration data for all keys
|
||||
for (int i = 0; i < NUM_SENSORS; i++)
|
||||
{
|
||||
key_values[i] = 0;
|
||||
thresholds[i] = 0xFFFF;
|
||||
maxReadings[i] = 0;
|
||||
}
|
||||
|
||||
// Flash every key red a few times so they know to let go of the slider
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
#ifndef KEY_DIVIDERS
|
||||
leds[j] = CRGB::Red;
|
||||
leds[j] = CRGB::Green;
|
||||
#else
|
||||
leds[j*2] = CRGB::Red;
|
||||
leds[j * 2] = CRGB::Green;
|
||||
#endif
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
#ifndef KEY_DIVIDERS
|
||||
leds[j] = CRGB::Purple;
|
||||
#else
|
||||
leds[j*2] = CRGB::Purple;
|
||||
#endif
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
delay(500);
|
||||
}
|
||||
|
||||
// Figure out the baselines for each key
|
||||
for (int i = 0; i < CALIBRATION_SAMPLES; i++)
|
||||
{
|
||||
scan();
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_SENSORS; i++)
|
||||
{
|
||||
baselines[i] = key_values[i];
|
||||
}
|
||||
|
||||
// Iterate over each key and have the user single press to calculate
|
||||
// our threshold windows. Since each finger raises the value by roughly
|
||||
// the same amount, we have the single double press each key, obtain the
|
||||
// average readout, then use the baselines to calculate a window for singles
|
||||
// and doubles
|
||||
for (int i = 0; i < NUM_SENSORS; i++)
|
||||
{
|
||||
// In 16-key mode, the key stays red until we detect a touch, then turns silver while the readings
|
||||
// are being taken, then turns green. In 32-key mode, the key stays red until it detects the top
|
||||
// sensor touched, then the key turns purple during readings for the top sensor, then turns yellow.
|
||||
// Once it's yellow, it performs the same process for the bottom sensor, then turns green and moves
|
||||
// to the next key.
|
||||
CRGB color = (i % 2 == 0) ? CRGB::Red : CRGB::Yellow;
|
||||
int lightIndex = i / 2;
|
||||
|
||||
#ifndef KEY_DIVIDERS
|
||||
leds[lightIndex] = color;
|
||||
#else
|
||||
leds[lightIndex * 2] = color;
|
||||
#endif
|
||||
|
||||
FastLED.show();
|
||||
|
||||
// wait until we detect a touch, then start measuring to determine the average
|
||||
while (key_values[i] < baselines[i] + CALIBRATION_DETECTION_THRESHOLD)
|
||||
{
|
||||
scan();
|
||||
}
|
||||
|
||||
#ifndef KEY_DIVIDERS
|
||||
leds[lightIndex] = CRGB::Silver;
|
||||
#else
|
||||
leds[lightIndex * 2] = CRGB::Silver;
|
||||
#endif
|
||||
|
||||
FastLED.show();
|
||||
|
||||
for (int j = 0; j < CALIBRATION_SAMPLES; j++)
|
||||
{
|
||||
scan();
|
||||
maxReadings[i] = max(maxReadings[i], key_values[i]);
|
||||
}
|
||||
|
||||
color = (i % 2 == 0) ? CRGB::Yellow : CRGB::Green;
|
||||
|
||||
#ifndef KEY_DIVIDERS
|
||||
leds[lightIndex] = color;
|
||||
#else
|
||||
leds[lightIndex * 2] = color;
|
||||
#endif
|
||||
|
||||
FastLED.show();
|
||||
|
||||
uint16_t window = (maxReadings[i] - baselines[i]) * TOUCH_INPUT_THRESHOLD;
|
||||
thresholds[i] = baselines[i] + window;
|
||||
}
|
||||
|
||||
saveConfig();
|
||||
}
|
||||
else
|
||||
{
|
||||
// just set the keys green
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
#ifndef KEY_DIVIDERS
|
||||
leds[i] = CRGB::Green;
|
||||
#else
|
||||
leds[i * 2] = CRGB::Green;
|
||||
#endif
|
||||
FastLED.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AutoTouchboard::update(int key)
|
||||
{
|
||||
return (key_values[key] > thresholds[key]);
|
||||
// check if the button is pressed
|
||||
// keep a running record of the last X deltas and the latest readings
|
||||
int value = key_values[key];
|
||||
deltas[key]->addValue(value - lastReadings[key]);
|
||||
lastReadings[key] = value;
|
||||
|
||||
// check the readout data and the running sum of the deltas;
|
||||
// if running sum of deltas goes above our defined threshold,
|
||||
// trigger a press and set new lower bounds for release,
|
||||
// otherwise, check for a release
|
||||
if (!states[key] && (deltas[key]->getSum() > deltaThreshold)) {
|
||||
// set a new release threshold based on the reading at the time we detected
|
||||
// the press (minus the threshold + a small hysteresis factor)
|
||||
releaseThresholds[key] = value - deltaThreshold + releaseHysteresis;
|
||||
states[key] = true;
|
||||
} else if (states[key] && (value < releaseThresholds[key])) {
|
||||
// new release
|
||||
states[key] = false;
|
||||
releaseThresholds[key] = 0;
|
||||
}
|
||||
|
||||
return states[key];
|
||||
}
|
||||
|
||||
uint16_t AutoTouchboard::getRawValue(int key)
|
||||
@@ -214,5 +119,10 @@ AutoTouchboard::AutoTouchboard()
|
||||
pinMode(MUX_1, OUTPUT);
|
||||
pinMode(MUX_2, OUTPUT);
|
||||
|
||||
for (int i = 0; i < NUM_SENSORS; i++) {
|
||||
deltas[i] = new RunningSum(NUM_READINGS);
|
||||
states[i] = false;
|
||||
}
|
||||
|
||||
calibrateKeys();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "Config.h"
|
||||
#include "PinConfig.h"
|
||||
#include "RunningSum.h"
|
||||
#include "USBOutput.h"
|
||||
|
||||
#include <EEPROM.h>
|
||||
@@ -12,10 +13,8 @@
|
||||
#define USE_WS2812SERIAL
|
||||
#include <FastLED.h>
|
||||
|
||||
#define CALIBRATION_SAMPLES 80
|
||||
#define CALIBRATION_DETECTION_THRESHOLD 6
|
||||
#define CALIBRATION_FLAG 0xA2
|
||||
#define TOUCH_INPUT_THRESHOLD 0.85
|
||||
// for hit detection, we keep a running record of the last X number
|
||||
// of arrow readings and the deltas from their last readings
|
||||
|
||||
#ifndef KEY_DIVIDERS
|
||||
extern CRGB leds[16];
|
||||
@@ -23,20 +22,28 @@ extern CRGB leds[16];
|
||||
extern CRGB leds[31];
|
||||
#endif
|
||||
|
||||
// the size of the buffer of deltas to keep for each key
|
||||
#define NUM_READINGS 10
|
||||
|
||||
class AutoTouchboard
|
||||
{
|
||||
private:
|
||||
// these will be tunable / need to be experimented ith
|
||||
int deltaThreshold = 5;
|
||||
int releaseHysteresis = 2;
|
||||
|
||||
uint16_t key_values[NUM_SENSORS];
|
||||
uint16_t thresholds[NUM_SENSORS];
|
||||
bool states[NUM_SENSORS];
|
||||
int releaseThresholds[NUM_SENSORS];
|
||||
int lastReadings[NUM_SENSORS];
|
||||
RunningSum* deltas[NUM_SENSORS];
|
||||
|
||||
public:
|
||||
AutoTouchboard();
|
||||
void scan();
|
||||
void loadConfig();
|
||||
void saveConfig();
|
||||
bool update(int key);
|
||||
uint16_t getRawValue(int key);
|
||||
void calibrateKeys(bool forceCalibrate = false);
|
||||
void calibrateKeys();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -90,13 +90,13 @@ void initializeController() {
|
||||
led = CRGB::Orange;
|
||||
|
||||
FastLED.show();
|
||||
delay(1000);
|
||||
delay(500);
|
||||
|
||||
for (CRGB& led : leds)
|
||||
led = CRGB::Black;
|
||||
|
||||
FastLED.show();
|
||||
delay(1000);
|
||||
delay(500);
|
||||
}
|
||||
|
||||
// Initialize and calibrate touch sensors
|
||||
@@ -124,7 +124,7 @@ void initializeController() {
|
||||
|
||||
// Initialize air sensor
|
||||
if (sensor != NULL) delete sensor;
|
||||
sensor = new AirSensor(1000, 200);
|
||||
sensor = new AirSensor(400, 200);
|
||||
|
||||
// Display the number of air sensors that were calibrated
|
||||
for (CRGB& led : leds)
|
||||
@@ -237,7 +237,7 @@ void loop() {
|
||||
lightIntensity[index] -= 0.05f;
|
||||
|
||||
// If the key is currently being held, set its color to the on color
|
||||
if (stateTop || stateTop)
|
||||
if (stateTop || stateBot)
|
||||
{
|
||||
lightIntensity[index] = 1.0f;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ void SerialProcessor::processConfigCommand(uint8_t* buf)
|
||||
serialLeds->saveLights();
|
||||
break;
|
||||
case CMD_CALIBRATE_SLIDER:
|
||||
touchboard->calibrateKeys(true);
|
||||
touchboard->calibrateKeys();
|
||||
break;
|
||||
case CMD_CALIBRATE_AIR_SENSORS:
|
||||
sensor->recalibrate();
|
||||
|
||||
Reference in New Issue
Block a user