diff --git a/Firmware/OpeNITHM/AirSensor.cpp b/Firmware/OpeNITHM/AirSensor.cpp index ba153d5..1c78a6a 100644 --- a/Firmware/OpeNITHM/AirSensor.cpp +++ b/Firmware/OpeNITHM/AirSensor.cpp @@ -117,10 +117,6 @@ int AirSensor::getValue(int sensor, bool light) AirSensor::AirSensor(int requiredSamples, int skippedSamples) : thresholds{ 10000, 10000, 10000, 10000, 10000, 10000 }, calibrationSamples{ 0, 0, 0, 0, 0, 0 }, skippedSamples{ 0, 0, 0, 0, 0, 0 }, samplesToAcquire(requiredSamples), samplesToSkip(skippedSamples), calibrated{ 0, 0, 0, 0, 0, 0 }, allCalibrated(false) { - // Load config values - EEPROM.get(68, deadzone); - EEPROM.get(72, alpha); - #ifdef IR_SENSOR_ANALOG digitalMode = false; #else @@ -138,6 +134,8 @@ AirSensor::AirSensor(int requiredSamples, int skippedSamples) : thresholds{ 1000 calibrated[i] = getValue(i, true); } } + + analogSensitivity = 40; } // Check if all IR sensors are calibrated. If they are, set a flag to not need to re-check it @@ -159,45 +157,46 @@ bool AirSensor::isCalibrated() #endif } -bool AirSensor::getSensorState(int sensor) -{ +bool AirSensor::getSensorState(int sensor) { // Flash the LED and read the IR sensor int value = getValue(sensor, true); turnOffLight(); - if (digitalMode) - return value == LOW ? true : false; - else + if (digitalMode) { - - // If the sensor is calibrated, Store its current filtered value. - // We are using an exponential moving average to filter out environmental noise. Setting alpha to 1 disables it. - if (allCalibrated || calibrated[sensor]) { - sensorValues[sensor] = (float)value * alpha + sensorValues[sensor] * (1 - alpha); - return sensorValues[sensor] < thresholds[sensor]; - } - else + return value == LOW ? true : false; + } + else + { + if (allCalibrated || calibrated[sensor]) { - // If it is not calibrated, perform calibration: - // Skip the first few samples. This might not be required, but improved performance in my case. - // This might be due to wiring mistakes I made - I'm leaving the code in either way as it can't hurt. - if (skippedSamples[sensor] > samplesToSkip) + sensorValues[sensor] = value; + return sensorValues[sensor] < thresholds[sensor]; + } + else + { + // If it is not calibrated, perform calibration + // Skip the first few samples + if (skippedSamples[sensor] > samplesToSkip) { // Keep the minimum value seen by the sensor - if (value < thresholds[sensor]) thresholds[sensor] = value; + if (value < thresholds[sensor]) + thresholds[sensor] = value; + // If we have enough samples: - if (++calibrationSamples[sensor] > samplesToAcquire) + if (++calibrationSamples[sensor] > samplesToAcquire) { // Consider the sensor calibrated. Finalize calibration for this sensor. sensorValues[sensor] = value; calibrated[sensor] = true; - thresholds[sensor] -= deadzone; - }; - } - else + thresholds[sensor] *= (analogSensitivity / 100); + } + } + else { skippedSamples[sensor]++; } + return false; } } @@ -238,26 +237,9 @@ bool AirSensor::getSensorCalibrated(int i) return calibrated[i]; } -void AirSensor::setDeadzone(int deadzone) +void AirSensor::setAnalogSensitivity(uint8_t analogSensitivity) { - this->deadzone = deadzone; - EEPROM.put(68, deadzone); -} - -void AirSensor::setAlpha(float alpha) -{ - this->alpha = alpha; - EEPROM.put(72, alpha); -} - -int AirSensor::getDeadzone() -{ - return deadzone; -} - -float AirSensor::getAlpha() -{ - return alpha; + this->analogSensitivity = analogSensitivity; } void AirSensor::recalibrate() @@ -270,5 +252,6 @@ void AirSensor::recalibrate() sensorValues[i] = 0; calibrated[i] = false; } + allCalibrated = false; } diff --git a/Firmware/OpeNITHM/AirSensor.h b/Firmware/OpeNITHM/AirSensor.h index 2aa1266..8591dd3 100644 --- a/Firmware/OpeNITHM/AirSensor.h +++ b/Firmware/OpeNITHM/AirSensor.h @@ -18,6 +18,7 @@ class AirSensor void changeLight(int light); void turnOffLight(); + uint8_t analogSensitivity; int thresholds[6]; int calibrationSamples[6]; int skippedSamples[6]; @@ -28,9 +29,7 @@ class AirSensor bool calibrated[6]; bool allCalibrated; bool digitalMode; - - uint16_t deadzone; - float alpha; + public: AirSensor(int requiredSamples, int skippedSamples); bool isCalibrated(); @@ -40,11 +39,7 @@ class AirSensor float getHandPosition(); uint8_t getSensorReadings(); bool getSensorCalibrated(int i); - - void setDeadzone(int deadzone); - void setAlpha(float alpha); - int getDeadzone(); - float getAlpha(); + void setAnalogSensitivity(uint8_t analogSensitivity); void recalibrate(); }; diff --git a/Firmware/OpeNITHM/AutoTouchboard.cpp b/Firmware/OpeNITHM/AutoTouchboard.cpp index 920a55d..de4ae05 100644 --- a/Firmware/OpeNITHM/AutoTouchboard.cpp +++ b/Firmware/OpeNITHM/AutoTouchboard.cpp @@ -143,7 +143,7 @@ void AutoTouchboard::calibrateKeys(bool forceCalibrate = false) leds[i] = CRGB::Green; FastLED.show(); - uint16_t window = (key_values[i] - baselines[i]) * 0.76; + uint16_t window = (key_values[i] - baselines[i]) * (sensitivity / 100); single_thresholds[i] = baselines[i] + window; double_thresholds[i] = baselines[i] + (2 * window); } @@ -176,6 +176,11 @@ uint16_t AutoTouchboard::getRawValue(int key) return key_values[key]; } +void AutoTouchboard::setSensitivity(uint8_t sensitivity) +{ + this->sensitivity = sensitivity; +} + AutoTouchboard::AutoTouchboard() #ifndef TEENSY sensor(CapacitiveSensor(SEND, RECEIVE_1, RECEIVE_2)), @@ -184,6 +189,7 @@ AutoTouchboard::AutoTouchboard() pinMode(MUX_0, OUTPUT); pinMode(MUX_1, OUTPUT); pinMode(MUX_2, OUTPUT); + sensitivity = 76; calibrateKeys(); } diff --git a/Firmware/OpeNITHM/AutoTouchboard.h b/Firmware/OpeNITHM/AutoTouchboard.h index 1a66803..8db4429 100644 --- a/Firmware/OpeNITHM/AutoTouchboard.h +++ b/Firmware/OpeNITHM/AutoTouchboard.h @@ -24,7 +24,7 @@ class AutoTouchboard #ifndef TEENSY CapacitiveSensor sensor; #endif - + uint8_t sensitivity; uint16_t key_values[16]; uint16_t single_thresholds[16]; uint16_t double_thresholds[16]; @@ -37,6 +37,7 @@ class AutoTouchboard KeyState update(int key); uint16_t getRawValue(int key); void calibrateKeys(bool forceCalibrate = false); + void setSensitivity(uint8_t sensitivity); }; #endif diff --git a/Firmware/OpeNITHM/OpeNITHM.ino b/Firmware/OpeNITHM/OpeNITHM.ino index a136107..ba9156e 100644 --- a/Firmware/OpeNITHM/OpeNITHM.ino +++ b/Firmware/OpeNITHM/OpeNITHM.ino @@ -8,17 +8,21 @@ #ifdef LED_SERIAL #include "SerialLeds.h" -byte serialBuffer[200]; +byte serialBuffer[100]; SerialLeds serialLeds; #endif #include "AirSensor.h" #include "AutoTouchboard.h" +#include "SerialProcessor.h" #include +SerialProcessor serialProcessor; + KeyState key_states[16]; CRGB leds[16]; bool updateLeds = false; +bool useSerialLeds = true; CRGB led_on = CRGB::Purple; CRGB led_off = CRGB::Yellow; @@ -30,8 +34,6 @@ AutoTouchboard *touchboard; AirSensor *sensor; Output *output; -char command[32]; - void parseCommand() { char input1 = Serial.read(); @@ -63,13 +65,7 @@ void parseCommand() while (!Serial.available()); input2 = Serial.read(); switch (input2) - { - case 'd': // dead zone - sensor->setDeadzone(Serial.parseInt()); - break; - case 'a': // alpha - sensor->setAlpha(Serial.parseFloat()); - break; + { case 'c': // calibrate sensor->recalibrate(); } @@ -81,10 +77,6 @@ void parseCommand() activated = true; break; case 'g': // print values - Serial.print("id \t"); - Serial.println(sensor->getDeadzone()); - Serial.print("ia \t"); - Serial.println(sensor->getAlpha()); Serial.print("lor \t"); Serial.println(led_on.r); Serial.print("log \t"); @@ -184,7 +176,7 @@ void setup() { // Display the number of air sensors that were calibrated for (CRGB& led : leds) - led = CFGB::Black; + led = CRGB::Black; for (int i = 0; i < 6; i++) { @@ -199,7 +191,7 @@ void setup() { // Set LEDs blue for "ready" for (CRGB& led : leds) - led = CFGB::Blue; + led = CRGB::Blue; FastLED.show(); @@ -212,30 +204,21 @@ void setup() { } void loop() { - -#ifndef LED_SERIAL - // Config commands can only be sent when the LEDs aren't being updated via serial - if (Serial.available()) - { - parseCommand(); - } - - updateLeds = true; -#else - // Wait until have at least one message + // Check for serial messages if (Serial.available() >= 100) { Serial.readBytes(serialBuffer, 100); - updateLeds = serialLeds.processBulk(serialBuffer, 100); + serialProcessor.processBulk(serialBuffer); } - else - updateLeds = false; -#endif // If currently paused through a config command, do not execute main loop if (!activated) return; + // If we're not using serial LEDs, just update the lights every loop + if (!useSerialLeds) + updateLeds = true; + // Scan touch keyboard and update lights touchboard->scan(); int index = 0; @@ -247,36 +230,37 @@ void loop() { #else index = 15 - i; #endif - if (lightIntensity[index] > 0.05f) - lightIntensity[index] -= 0.05f; KeyState keyState = touchboard->update(i); - // If the key is currently being held, set its color to purple - if (keyState == SINGLE_PRESS || - keyState == DOUBLE_PRESS) + // handle changing key colors for non-serial LED updates + if (!useSerialLeds) { -#ifndef LED_SERIAL - lightIntensity[index] = 1.0f; - leds[index].setRGB(min(led_on.r / 2 + led_on.r / 2 * lightIntensity[index], 255), min(led_on.g / 2 + led_on.g / 2 * lightIntensity[index], 255), min(led_on.b / 2 + led_on.b / 2 * lightIntensity[index], 255)); -#endif + if (lightIntensity[index] > 0.05f) + lightIntensity[index] -= 0.05f; + + // If the key is currently being held, set its color to the on color + if (keyState == SINGLE_PRESS || keyState == DOUBLE_PRESS) + { + lightIntensity[index] = 1.0f; + leds[index].setRGB(min(led_on.r / 2 + led_on.r / 2 * lightIntensity[index], 255), min(led_on.g / 2 + led_on.g / 2 * lightIntensity[index], 255), min(led_on.b / 2 + led_on.b / 2 * lightIntensity[index], 255)); + } + else + { + // If not, make it the off color + leds[index].setRGB(led_off.r / 2, led_off.g / 2, led_off.b / 2); + } } - else + // handle changing key colors for serial LED updates + else { -#ifndef LED_SERIAL - // If not, make it yellow and send the "key released" event if it was previously pressed - leds[index].setRGB(led_off.r / 2, led_off.g / 2, led_off.b / 2); -#endif + if (updateLeds) + { + RGBLed temp = serialLeds.getKey(i); + leds[index].setRGB(temp.r, temp.g, temp.b); + } } -#ifdef LED_SERIAL - if (updateLeds) - { - RGBLed temp = serialLeds.getKey(i); - leds[index].setRGB(temp.r, temp.g, temp.b); - } -#endif - #if !defined(SERIAL_PLOT) && defined(USB) if (key_states[i] != keyState) output->sendKeyEvent(i, keyState); @@ -329,5 +313,8 @@ void loop() { // If the air sensor is calibrated, update lights. The lights will stay red as long as the air sensor is not calibrated. if (sensor->isCalibrated() && updateLeds) + { FastLED.show(); + updateLeds = false; + } } diff --git a/Firmware/OpeNITHM/SerialLeds.cpp b/Firmware/OpeNITHM/SerialLeds.cpp index c8de0f9..4ec0eee 100644 --- a/Firmware/OpeNITHM/SerialLeds.cpp +++ b/Firmware/OpeNITHM/SerialLeds.cpp @@ -3,90 +3,15 @@ SerialLeds::SerialLeds() { } -bool SerialLeds::process(byte in) +void SerialLeds::processBulk(uint8_t *buf) { - bool readyToProcess = false; - switch (currentState) + if (memcmp(buf + 2, lastBuffer + 2, 98) != 0) { - case headerSearch: - // Search for header - if (in == 0xAA) - { - dataBuffer[0] = in; - currentState = headerConfirm; - } - break; - case headerConfirm: - // Confirm header - if (in == 0xAA) - { - dataBuffer[1] = in; - currentState = dataRead; - currentReadNum = 2; - } - else - { - currentState = headerSearch; - } - break; - case dataRead: - { - dataBuffer[currentReadNum] = in; - currentReadNum++; - if (currentReadNum == 100) - { - if (memcmp(dataBuffer, lastBuffer, 100) != 0) - readyToProcess = true; - memcpy(lastBuffer, dataBuffer, 100); - currentState = headerSearch; - } - } - break; + memcpy(ledData.raw, buf + 2, 96); + updateLeds = true; } - - return readyToProcess; -} - -bool SerialLeds::processBulk(uint8_t *buf, size_t length) -{ - bool readyToProcess = false; - currentState = headerSearch; - - for (int i = 0; i < length; i++) - { - switch (currentState) - { - case headerSearch: - if (buf[i] == 0xAA) - { - currentState = headerConfirm; - dataBuffer[0] = buf[i]; - } - break; - case headerConfirm: - if (buf[i] == 0xAA) - { - currentState = dataRead; - dataBuffer[1] = buf[i]; - } - else - { - currentState = headerSearch; - } - break; - case dataRead: - memcpy(dataBuffer + 2, buf + i, 98); - if (memcmp(dataBuffer, lastBuffer, 100) != 0) - { - memcpy(ledData.raw, dataBuffer + 2, 96); - readyToProcess = true; - } - memcpy(lastBuffer, dataBuffer, 100); - currentState = headerSearch; - return readyToProcess; - } - } - return readyToProcess; + + memcpy(lastBuffer, buf, 100); } // Left to Right diff --git a/Firmware/OpeNITHM/SerialLeds.h b/Firmware/OpeNITHM/SerialLeds.h index c97442c..9d13288 100644 --- a/Firmware/OpeNITHM/SerialLeds.h +++ b/Firmware/OpeNITHM/SerialLeds.h @@ -7,6 +7,8 @@ #include "WProgram.h" #endif +extern bool updateLeds; + typedef struct { uint8_t b; uint8_t r; @@ -16,22 +18,11 @@ typedef struct { class SerialLeds { private: - enum state - { - headerSearch, - headerConfirm, - dataRead - }; - - uint8_t dataBuffer[100]; uint8_t lastBuffer[100]; - state currentState = headerSearch; - uint8_t currentReadNum = 0; public: SerialLeds(); - bool process(uint8_t in); - bool processBulk(uint8_t *buf, size_t length); + void processBulk(uint8_t *buf); RGBLed getKey(uint8_t key); // Left to Right union { diff --git a/Firmware/OpeNITHM/SerialProcessor.cpp b/Firmware/OpeNITHM/SerialProcessor.cpp new file mode 100644 index 0000000..d878162 --- /dev/null +++ b/Firmware/OpeNITHM/SerialProcessor.cpp @@ -0,0 +1,61 @@ +#include "SerialProcessor.h" + +SerialProcessor::SerialProcessor() +{ } + +void SerialProcessor::processConfigCommand(uint8_t* buf) +{ + // figure out which command we're running + switch (buf[2]) + { + case CMD_CHANGE_LIGHT_MODE: + if (buf[3] == LIGHT_MODE_SERIAL) + { + useSerialLeds = true; + + // Set LEDs blue for "ready" + for (CRGB& led : leds) + led = CRGB::Blue; + + FastLED.show(); + } + else if (buf[3] == LIGHT_MODE_REACTIVE) + { + useSerialLeds = false; + } + + break; + case CMD_CHANGE_ON_COLOR: + led_on.r = buf[3]; + led_on.g = buf[4]; + led_on.b = buf[5]; + break; + case CMD_CHANGE_OFF_COLOR: + led_off.r = buf[3]; + led_off.g = buf[4]; + led_off.b = buf[5]; + break; + case CMD_CALIBRATE_SLIDER: + touchboard->setSensitivity(buf[3]); + touchboard->calibrateKeys(true); + break; + case CMD_CALIBRATE_AIR_SENSORS: + sensor->setAnalogSensitivity(buf[3]); + sensor->recalibrate(); + break; + default: + break; + } +} + +void SerialProcessor::processBulk(uint8_t *buf) +{ + if (buf[0] == LED_FLAG && buf[1] == LED_FLAG) + { + serialLeds.processBulk(buf); + } + else if (buf[0] == CONFIG_FLAG && buf[1] == CONFIG_FLAG) + { + processConfigCommand(buf); + } +} diff --git a/Firmware/OpeNITHM/SerialProcessor.h b/Firmware/OpeNITHM/SerialProcessor.h new file mode 100644 index 0000000..3551575 --- /dev/null +++ b/Firmware/OpeNITHM/SerialProcessor.h @@ -0,0 +1,45 @@ +// SerialProcessor.h + +#ifndef _SERIALPROCESSOR_h +#define _SERIALPROCESSOR_h + +#include "AirSensor.h" +#include "AutoTouchboard.h" +#include "Config.h" +#include "SerialLeds.h" + +#include + +// magic bytes we look for in the packet headers to identify whether it's LED updates or config commands +#define LED_FLAG 0xAA +#define CONFIG_FLAG 0xBB + +// config command opcodes +#define CMD_CHANGE_LIGHT_MODE 0x11 +#define CMD_CHANGE_ON_COLOR 0x22 +#define CMD_CHANGE_OFF_COLOR 0x33 +#define CMD_CALIBRATE_SLIDER 0x44 +#define CMD_CALIBRATE_AIR_SENSORS 0x55 + +// light modes for the change light mode command +#define LIGHT_MODE_SERIAL 0x00 +#define LIGHT_MODE_REACTIVE 0x01 + +extern AirSensor *sensor; +extern AutoTouchboard *touchboard; +extern SerialLeds serialLeds; +extern bool useSerialLeds; +extern CRGB leds[16]; +extern CRGB led_on; +extern CRGB led_off; + +class SerialProcessor +{ + private: + void processConfigCommand(uint8_t* buf); + public: + SerialProcessor(); + void processBulk(uint8_t* buf); +}; + +#endif