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:
skogaby
2020-10-07 23:56:32 -05:00
parent 46b98260d4
commit d42d667c55
5 changed files with 89 additions and 172 deletions
+1 -1
View File
@@ -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
+68 -158
View File
@@ -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();
}
+15 -8
View File
@@ -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
+4 -4
View File
@@ -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;
+1 -1
View File
@@ -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();