diff --git a/CAD/Fusion 360/Frame v16.f3d b/CAD/Fusion 360/Frame v16.f3d new file mode 100644 index 0000000..7d599e1 Binary files /dev/null and b/CAD/Fusion 360/Frame v16.f3d differ diff --git a/CAD/Fusion 360/Key v6.f3d b/CAD/Fusion 360/Key v6.f3d new file mode 100644 index 0000000..5777fbb Binary files /dev/null and b/CAD/Fusion 360/Key v6.f3d differ diff --git a/CAD/Fusion 360/Key-US_Cust v3.f3d b/CAD/Fusion 360/Key-US_Cust v3.f3d new file mode 100644 index 0000000..74ddbb7 Binary files /dev/null and b/CAD/Fusion 360/Key-US_Cust v3.f3d differ diff --git a/CAD/Fusion 360/OpeNITHM PCB v3.f3d b/CAD/Fusion 360/OpeNITHM PCB v3.f3d new file mode 100644 index 0000000..e05a110 Binary files /dev/null and b/CAD/Fusion 360/OpeNITHM PCB v3.f3d differ diff --git a/CAD/Fusion 360/Sensor Stand Left v9.f3d b/CAD/Fusion 360/Sensor Stand Left v9.f3d new file mode 100644 index 0000000..9265c5b Binary files /dev/null and b/CAD/Fusion 360/Sensor Stand Left v9.f3d differ diff --git a/CAD/STL/Center Frame.stl b/CAD/STL/Center Frame.stl new file mode 100644 index 0000000..27acecc Binary files /dev/null and b/CAD/STL/Center Frame.stl differ diff --git a/CAD/STL/Key-US_Cust.stl b/CAD/STL/Key-US_Cust.stl new file mode 100644 index 0000000..dab4c95 Binary files /dev/null and b/CAD/STL/Key-US_Cust.stl differ diff --git a/CAD/STL/Key.stl b/CAD/STL/Key.stl new file mode 100644 index 0000000..79012e1 Binary files /dev/null and b/CAD/STL/Key.stl differ diff --git a/CAD/STL/Left Frame.stl b/CAD/STL/Left Frame.stl new file mode 100644 index 0000000..78711a3 Binary files /dev/null and b/CAD/STL/Left Frame.stl differ diff --git a/CAD/STL/Right Frame.stl b/CAD/STL/Right Frame.stl new file mode 100644 index 0000000..44484f8 Binary files /dev/null and b/CAD/STL/Right Frame.stl differ diff --git a/CAD/STL/Sensor Base Left.stl b/CAD/STL/Sensor Base Left.stl new file mode 100644 index 0000000..334d796 Binary files /dev/null and b/CAD/STL/Sensor Base Left.stl differ diff --git a/CAD/STL/Sensor Cover Left.stl b/CAD/STL/Sensor Cover Left.stl new file mode 100644 index 0000000..9153a6d Binary files /dev/null and b/CAD/STL/Sensor Cover Left.stl differ diff --git a/CAD/STL/Sensor Rail.stl b/CAD/STL/Sensor Rail.stl new file mode 100644 index 0000000..4d1131c Binary files /dev/null and b/CAD/STL/Sensor Rail.stl differ diff --git a/CAD/STL/Sensor Spacer Left.stl b/CAD/STL/Sensor Spacer Left.stl new file mode 100644 index 0000000..e38a2f6 Binary files /dev/null and b/CAD/STL/Sensor Spacer Left.stl differ diff --git a/Firmware/AirSensor.cpp b/Firmware/AirSensor.cpp index 881d949..083cf2f 100644 --- a/Firmware/AirSensor.cpp +++ b/Firmware/AirSensor.cpp @@ -1,213 +1,238 @@ -// -// -// +// +// +// #include "AirSensor.h" +#ifndef IR_SENSOR_MULTIPLEXED +int ir_sensor_pins[6] = {AIR_SENSOR_0_PIN, AIR_SENSOR_1_PIN, AIR_SENSOR_2_PIN, AIR_SENSOR_3_PIN, AIR_SENSOR_4_PIN, AIR_SENSOR_5_PIN}; +#endif // Sets the output pins to switch the charlieplexed array of LEDs. +// 0 is the bottom-most LED and 5 is the top-most void AirSensor::changeLight(int light) { - switch (light) - { - case 0: - pinMode(IR_A, OUTPUT); - pinMode(IR_B, OUTPUT); - pinMode(IR_C, INPUT); + switch (light) + { + case 0: + pinMode(LED_0, OUTPUT); + pinMode(LED_1, OUTPUT); + pinMode(LED_2, INPUT); - digitalWrite(IR_A, LOW); - digitalWrite(IR_B, HIGH); - digitalWrite(IR_C, LOW); - break; - case 1: - pinMode(IR_A, OUTPUT); - pinMode(IR_B, OUTPUT); - pinMode(IR_C, INPUT); + digitalWrite(LED_0, HIGH); + digitalWrite(LED_1, LOW); + digitalWrite(LED_2, LOW); + break; + case 1: + pinMode(LED_0, OUTPUT); + pinMode(LED_1, OUTPUT); + pinMode(LED_2, INPUT); - digitalWrite(IR_A, HIGH); - digitalWrite(IR_B, LOW); - digitalWrite(IR_C, LOW); - break; - case 2: - pinMode(IR_A, INPUT); - pinMode(IR_B, OUTPUT); - pinMode(IR_C, OUTPUT); + digitalWrite(LED_0, LOW); + digitalWrite(LED_1, HIGH); + digitalWrite(LED_2, LOW); + break; + case 2: + pinMode(LED_0, INPUT); + pinMode(LED_1, OUTPUT); + pinMode(LED_2, OUTPUT); - digitalWrite(IR_A, LOW); - digitalWrite(IR_B, LOW); - digitalWrite(IR_C, HIGH); - break; - case 3: - pinMode(IR_A, INPUT); - pinMode(IR_B, OUTPUT); - pinMode(IR_C, OUTPUT); + digitalWrite(LED_0, LOW); + digitalWrite(LED_1, HIGH); + digitalWrite(LED_2, LOW); + break; + case 3: + pinMode(LED_0, INPUT); + pinMode(LED_1, OUTPUT); + pinMode(LED_2, OUTPUT); - digitalWrite(IR_A, LOW); - digitalWrite(IR_B, HIGH); - digitalWrite(IR_C, LOW); - break; - case 4: - pinMode(IR_A, OUTPUT); - pinMode(IR_B, INPUT); - pinMode(IR_C, OUTPUT); + digitalWrite(LED_0, LOW); + digitalWrite(LED_1, LOW); + digitalWrite(LED_2, HIGH); + break; + case 4: + pinMode(LED_0, OUTPUT); + pinMode(LED_1, INPUT); + pinMode(LED_2, OUTPUT); - digitalWrite(IR_A, LOW); - digitalWrite(IR_B, LOW); - digitalWrite(IR_C, HIGH); - break; - case 5: - pinMode(IR_A, OUTPUT); - pinMode(IR_B, INPUT); - pinMode(IR_C, OUTPUT); + digitalWrite(LED_0, HIGH); + digitalWrite(LED_1, LOW); + digitalWrite(LED_2, LOW); + break; + case 5: + pinMode(LED_0, OUTPUT); + pinMode(LED_1, INPUT); + pinMode(LED_2, OUTPUT); - digitalWrite(IR_A, HIGH); - digitalWrite(IR_B, LOW); - digitalWrite(IR_C, LOW); - break; - default: - turnOffLight(); - break; - } + digitalWrite(LED_0, LOW); + digitalWrite(LED_1, LOW); + digitalWrite(LED_2, HIGH); + break; + default: + turnOffLight(); + break; + } } // Sets all output pins to high-impedance to turn off all LEDs void AirSensor::turnOffLight() { - pinMode(IR_A, INPUT); - pinMode(IR_B, INPUT); - pinMode(IR_C, INPUT); + pinMode(LED_0, INPUT); + pinMode(LED_1, INPUT); + pinMode(LED_2, INPUT); } int AirSensor::getValue(int sensor, bool light) { - // Turn on light corresponding to read sensor - if (light) - { - changeLight(sensor); - } - else - { - turnOffLight(); - } + // Turn on light corresponding to read sensor + if (light) + { + changeLight(sensor); + } + else + { + turnOffLight(); + } + #ifdef IR_SENSOR_MULTIPLEXED - // Set multiplexer to corresponding sensor - digitalWrite(MUX_A, bitRead(sensor, 0)); - digitalWrite(MUX_B, bitRead(sensor, 1)); - digitalWrite(MUX_C, bitRead(sensor, 2)); - // Return sensor value - return analogRead(SENSOR_IN); + // Set multiplexer to corresponding sensor + digitalWrite(MUX_A, bitRead(sensor, 0)); + digitalWrite(MUX_B, bitRead(sensor, 1)); + digitalWrite(MUX_C, bitRead(sensor, 2)); + // Return sensor value +#ifdef IR_SENSOR_ANALOG + return analogRead(SENSOR_IN); #else - return analogRead(ir_sensor_pins[sensor]); + delay(1); + return digitalRead(SENSOR_IN); +#endif +#else +#ifdef IR_SENSOR_ANALOG + return analogRead(ir_sensor_pins[sensor]); +#else + delay(1); + return digitalRead(ir_sensor_pins[sensor]); +#endif #endif } 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(12, deadzone); - EEPROM.get(16, alpha); + // Load config values + EEPROM.get(12, deadzone); + EEPROM.get(16, alpha); + +#ifndef IR_SENSOR_ANALOG + // No calibration required in digital mode + for (int i = 0; i < 6; i++) + calibrated[i] = true; + allCalibrated = true; +#endif } // Check if all IR sensors are calibrated. If they are, set a flag to not need to re-check it bool AirSensor::isCalibrated() { - if (!allCalibrated) - { - for (int i = 0; i < 6; i++) - { - if (!calibrated[i]) - return false; - } - allCalibrated = true; - } - return allCalibrated; + if (!allCalibrated) + { + for (int i = 0; i < 6; i++) + { + if (!calibrated[i]) + return false; + } + allCalibrated = true; + } + return allCalibrated; } bool AirSensor::getSensorState(int sensor) { - // Flash the LED and read the IR sensor - int value = getValue(sensor, true); - turnOffLight(); + // Flash the LED and read the IR sensor + int value = getValue(sensor, true); + turnOffLight(); - // 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 - { - // 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) - { - // Keep the minimum value seen by the sensor - if (value < thresholds[sensor]) thresholds[sensor] = value; - // If we have enough samples: - if (++calibrationSamples[sensor] > samplesToAcquire) - { - // Consider the sensor calibrated. Finalize calibration for this sensor. - sensorValues[sensor] = value; - calibrated[sensor] = true; - thresholds[sensor] -= deadzone; - }; - } - else - { - skippedSamples[sensor]++; - } - return false; - } +#ifdef IR_SENSOR_ANALOG + // 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 + { + // 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) + { + // Keep the minimum value seen by the sensor + if (value < thresholds[sensor]) thresholds[sensor] = value; + // If we have enough samples: + if (++calibrationSamples[sensor] > samplesToAcquire) + { + // Consider the sensor calibrated. Finalize calibration for this sensor. + sensorValues[sensor] = value; + calibrated[sensor] = true; + thresholds[sensor] -= deadzone; + }; + } + else + { + skippedSamples[sensor]++; + } + return false; + } +#else + return value == LOW ? true : false; +#endif } // Using data from air sensors, compute the height of the player's hand, from 0 (not present) to 1 (highest possible position). float AirSensor::getHandPosition() { - float total = 0; - float sensorsTriggered = 0; - for (int i = 0; i < 6; i++) - { - if (getSensorState(i)) - { - sensorsTriggered++; - total += i + 1; - } - } - return sensorsTriggered == 0 ? 0 : (total / (sensorsTriggered)) / 6; + int highestTriggered = -1; + for (int i = 0; i < 6; i++) + { + if (getSensorState(i)) + { + if ((i + 1) > highestTriggered) + highestTriggered = i + 1; + } + } + return highestTriggered == -1 ? 0 : ((float)highestTriggered / 6.0f); } void AirSensor::setDeadzone(int deadzone) { - this->deadzone = deadzone; - EEPROM.put(12, deadzone); + this->deadzone = deadzone; + EEPROM.put(12, deadzone); } void AirSensor::setAlpha(float alpha) { - this->alpha = alpha; - EEPROM.put(16, alpha); + this->alpha = alpha; + EEPROM.put(16, alpha); } int AirSensor::getDeadzone() { - return deadzone; + return deadzone; } float AirSensor::getAlpha() { - return alpha; + return alpha; } void AirSensor::recalibrate() { - for (int i = 0; i < 6; i++) - { - thresholds[i] = 0; - calibrationSamples[i] = 0; - skippedSamples[i] = 0; - sensorValues[i] = 0; - calibrated[i] = false; - } - allCalibrated = false; + for (int i = 0; i < 6; i++) + { + thresholds[i] = 0; + calibrationSamples[i] = 0; + skippedSamples[i] = 0; + sensorValues[i] = 0; + calibrated[i] = false; + } + allCalibrated = false; } diff --git a/Firmware/AirSensor.h b/Firmware/AirSensor.h index 8caf847..555f93b 100644 --- a/Firmware/AirSensor.h +++ b/Firmware/AirSensor.h @@ -4,50 +4,47 @@ #define _AIRSENSOR_h #if defined(ARDUINO) && ARDUINO >= 100 - #include "arduino.h" +#include "arduino.h" #else - #include "WProgram.h" +#include "WProgram.h" #endif #include "PinConfig.h" #include -#ifndef IR_SENSOR_MULTIPLEXED - int ir_sensor_pins[6] = {AIR_SENSOR_0_PIN, AIR_SENSOR_1_PIN, AIR_SENSOR_2_PIN, AIR_SENSOR_3_PIN, AIR_SENSOR_4_PIN, AIR_SENSOR_5_PIN}; -#endif + class AirSensor { -private: - void changeLight(int light); - void turnOffLight(); + private: + void changeLight(int light); + void turnOffLight(); - int thresholds[6]; - int calibrationSamples[6]; - int skippedSamples[6]; - float sensorValues[6]; - int samplesToAcquire; - int samplesToSkip; + int thresholds[6]; + int calibrationSamples[6]; + int skippedSamples[6]; + float sensorValues[6]; + int samplesToAcquire; + int samplesToSkip; - bool calibrated[6]; - bool allCalibrated; + bool calibrated[6]; + bool allCalibrated; - uint16_t deadzone; - float alpha; -public: - AirSensor(int requiredSamples, int skippedSamples); - bool isCalibrated(); - bool getSensorState(int sensor); - int getValue(int sensor, bool light); - float getHandPosition(); + uint16_t deadzone; + float alpha; + public: + AirSensor(int requiredSamples, int skippedSamples); + bool isCalibrated(); + bool getSensorState(int sensor); + int getValue(int sensor, bool light); + float getHandPosition(); - void setDeadzone(int deadzone); - void setAlpha(float alpha); - int getDeadzone(); - float getAlpha(); - void recalibrate(); + void setDeadzone(int deadzone); + void setAlpha(float alpha); + int getDeadzone(); + float getAlpha(); + void recalibrate(); }; #endif - diff --git a/Firmware/CapacitiveSensor.cpp b/Firmware/CapacitiveSensor.cpp index 3f7fb55..050b340 100644 --- a/Firmware/CapacitiveSensor.cpp +++ b/Firmware/CapacitiveSensor.cpp @@ -1,30 +1,30 @@ /* - CapacitiveSense.h - Capacitive Sensing Library for 'duino / Wiring - https://github.com/PaulStoffregen/CapacitiveSensor - http://www.pjrc.com/teensy/td_libs_CapacitiveSensor.html - http://playground.arduino.cc/Main/CapacitiveSensor - Copyright (c) 2009 Paul Bagder - Updates for other hardare by Paul Stoffregen, 2010-2016 - Several modifications to better suit OpeNITHM by Jonathan Montineri, 2019 - vim: set ts=4: + CapacitiveSense.h - Capacitive Sensing Library for 'duino / Wiring + https://github.com/PaulStoffregen/CapacitiveSensor + http://www.pjrc.com/teensy/td_libs_CapacitiveSensor.html + http://playground.arduino.cc/Main/CapacitiveSensor + Copyright (c) 2009 Paul Bagder + Updates for other hardare by Paul Stoffregen, 2010-2016 + Several modifications to better suit OpeNITHM by Jonathan Montineri, 2019 + vim: set ts=4: - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. */ #if ARDUINO >= 100 @@ -42,35 +42,35 @@ CapacitiveSensor::CapacitiveSensor(uint8_t sendPin, uint8_t receivePin1, uint8_t receivePin2) { - // initialize this instance's variables - error = 1; - retVal = new unsigned int[2]; + // initialize this instance's variables + error = 1; + retVal = new unsigned int[2]; - loopTimingFactor = 310; // determined empirically - a hack - CS_Timeout_Millis = (200 * (float)loopTimingFactor * (float)F_CPU) / 16000000; + loopTimingFactor = 310; // determined empirically - a hack + CS_Timeout_Millis = (200 * (float)loopTimingFactor * (float)F_CPU) / 16000000; - // get pin mapping and port for send Pin - from PinMode function in core + // get pin mapping and port for send Pin - from PinMode function in core #ifdef NUM_DIGITAL_PINS - if (sendPin >= NUM_DIGITAL_PINS) error = -1; - if (receivePin1 >= NUM_DIGITAL_PINS) error = -1; - if (receivePin2 >= NUM_DIGITAL_PINS) error = -1; + if (sendPin >= NUM_DIGITAL_PINS) error = -1; + if (receivePin1 >= NUM_DIGITAL_PINS) error = -1; + if (receivePin2 >= NUM_DIGITAL_PINS) error = -1; #endif - pinMode(sendPin, OUTPUT); // sendpin to OUTPUT - pinMode(receivePin1, INPUT); // receivePins to INPUT - pinMode(receivePin2, INPUT); + pinMode(sendPin, OUTPUT); // sendpin to OUTPUT + pinMode(receivePin1, INPUT); // receivePins to INPUT + pinMode(receivePin2, INPUT); - // Get pin bitmask and registers + // Get pin bitmask and registers - sBit = PIN_TO_BITMASK(sendPin); - sReg = PIN_TO_BASEREG(sendPin); + sBit = PIN_TO_BITMASK(sendPin); + sReg = PIN_TO_BASEREG(sendPin); - r1Bit = PIN_TO_BITMASK(receivePin1); - r1Reg = PIN_TO_BASEREG(receivePin1); + r1Bit = PIN_TO_BITMASK(receivePin1); + r1Reg = PIN_TO_BASEREG(receivePin1); - r2Bit = PIN_TO_BITMASK(receivePin2); - r2Reg = PIN_TO_BASEREG(receivePin2); + r2Bit = PIN_TO_BITMASK(receivePin2); + r2Reg = PIN_TO_BASEREG(receivePin2); } // Public Methods ////////////////////////////////////////////////////////////// @@ -78,21 +78,21 @@ CapacitiveSensor::CapacitiveSensor(uint8_t sendPin, uint8_t receivePin1, uint8_t unsigned int* CapacitiveSensor::sense(uint8_t samples) { - total1 = 0; - total2 = 0; + total1 = 0; + total2 = 0; - // This code has very strict timing - disable interrupts - noInterrupts(); - for (uint8_t i = 0; i < samples; i++) { - if (!SenseOneCycle()) return nullptr; // Poll capacitive sensors repeatedly - } - interrupts(); + // This code has very strict timing - disable interrupts + //noInterrupts(); + for (uint8_t i = 0; i < samples; i++) { + if (!SenseOneCycle()) return nullptr; // Poll capacitive sensors repeatedly + } + //interrupts(); - retVal[0] = total1; - retVal[1] = total2; + retVal[0] = total1; + retVal[1] = total2; - // Return the pair of values for the 2 sensors that were polled - return retVal; + // Return the pair of values for the 2 sensors that were polled + return retVal; } // Private Methods ///////////////////////////////////////////////////////////// @@ -101,60 +101,60 @@ unsigned int* CapacitiveSensor::sense(uint8_t samples) int CapacitiveSensor::SenseOneCycle(void) { - DIRECT_WRITE_LOW(sReg, sBit); // sendPin Register low - DIRECT_MODE_INPUT(r1Reg, r1Bit); // receivePin to input (pullups ar1e off) - DIRECT_MODE_OUTPUT(r1Reg, r1Bit); // receivePin to OUTPUT - DIRECT_WRITE_LOW(r1Reg, r1Bit); // pin is now LOW AND OUTPUT - delayMicroseconds(10); - DIRECT_MODE_INPUT(r1Reg, r1Bit); // receivePin to input (pullups ar1e off) + DIRECT_WRITE_LOW(sReg, sBit); // sendPin Register low + DIRECT_MODE_INPUT(r1Reg, r1Bit); // receivePin to input (pullups ar1e off) + DIRECT_MODE_OUTPUT(r1Reg, r1Bit); // receivePin to OUTPUT + DIRECT_WRITE_LOW(r1Reg, r1Bit); // pin is now LOW AND OUTPUT + delayMicroseconds(10); + DIRECT_MODE_INPUT(r1Reg, r1Bit); // receivePin to input (pullups ar1e off) - DIRECT_MODE_INPUT(r2Reg, r2Bit); // receivePin to input (pullups ar2e off) - DIRECT_MODE_OUTPUT(r2Reg, r2Bit); // receivePin to OUTPUT - DIRECT_WRITE_LOW(r2Reg, r2Bit); // pin is now LOW AND OUTPUT - delayMicroseconds(10); - DIRECT_MODE_INPUT(r2Reg, r2Bit); // receivePin to input (pullups ar2e off) - DIRECT_WRITE_HIGH(sReg, sBit); // sendPin High + DIRECT_MODE_INPUT(r2Reg, r2Bit); // receivePin to input (pullups ar2e off) + DIRECT_MODE_OUTPUT(r2Reg, r2Bit); // receivePin to OUTPUT + DIRECT_WRITE_LOW(r2Reg, r2Bit); // pin is now LOW AND OUTPUT + delayMicroseconds(10); + DIRECT_MODE_INPUT(r2Reg, r2Bit); // receivePin to input (pullups ar2e off) + DIRECT_WRITE_HIGH(sReg, sBit); // sendPin High - while (total1 < CS_Timeout_Millis) { // while total is positive value - // Poll both pins at once - pin1State = DIRECT_READ(r1Reg, r1Bit); - pin2State = DIRECT_READ(r2Reg, r2Bit); + while (total1 < CS_Timeout_Millis) { // while total is positive value + // Poll both pins at once + pin1State = DIRECT_READ(r1Reg, r1Bit); + pin2State = DIRECT_READ(r2Reg, r2Bit); - total1 += !pin1State; - total2 += !pin2State; + total1 += !pin1State; + total2 += !pin2State; - // Break once both pins are high - if(pin1State && pin2State) break; - } + // Break once both pins are high + if (pin1State && pin2State) break; + } - if (total1 > CS_Timeout_Millis) { - return -2; // We timed out - should never happen with this implementation - } + if (total1 > CS_Timeout_Millis) { + return -2; // We timed out - should never happen with this implementation + } - // set receive pin HIGH briefly to charge up fully - because the while loop above will exit when pin is ~ 2.5V - DIRECT_WRITE_HIGH(r1Reg, r1Bit); - DIRECT_MODE_OUTPUT(r1Reg, r1Bit); // receivePin to OUTPUT - pin is now HIGH AND OUTPUT - DIRECT_MODE_OUTPUT(r2Reg, r2Bit); // receivePin to OUTPUT - pin is now HIGH AND OUTPUT - DIRECT_WRITE_HIGH(r1Reg, r1Bit); - DIRECT_WRITE_HIGH(r2Reg, r2Bit); - DIRECT_MODE_INPUT(r1Reg, r1Bit); // receivePin to INPUT (pullup is off) - DIRECT_MODE_INPUT(r2Reg, r2Bit); // receivePin to INPUT (pullup is off) - DIRECT_WRITE_LOW(sReg, sBit); // sendPin LOW + // set receive pin HIGH briefly to charge up fully - because the while loop above will exit when pin is ~ 2.5V + DIRECT_WRITE_HIGH(r1Reg, r1Bit); + DIRECT_MODE_OUTPUT(r1Reg, r1Bit); // receivePin to OUTPUT - pin is now HIGH AND OUTPUT + DIRECT_MODE_OUTPUT(r2Reg, r2Bit); // receivePin to OUTPUT - pin is now HIGH AND OUTPUT + DIRECT_WRITE_HIGH(r1Reg, r1Bit); + DIRECT_WRITE_HIGH(r2Reg, r2Bit); + DIRECT_MODE_INPUT(r1Reg, r1Bit); // receivePin to INPUT (pullup is off) + DIRECT_MODE_INPUT(r2Reg, r2Bit); // receivePin to INPUT (pullup is off) + DIRECT_WRITE_LOW(sReg, sBit); // sendPin LOW - // Same loop as above, but measuring capacitor discharge time instead - while ( (total1 < CS_Timeout_Millis) ) { - pin1State = DIRECT_READ(r1Reg, r1Bit); - pin2State = DIRECT_READ(r2Reg, r2Bit); + // Same loop as above, but measuring capacitor discharge time instead + while ( (total1 < CS_Timeout_Millis) ) { + pin1State = DIRECT_READ(r1Reg, r1Bit); + pin2State = DIRECT_READ(r2Reg, r2Bit); - total1 += pin1State; - total2 += pin2State; + total1 += pin1State; + total2 += pin2State; - if(!(pin1State || pin2State)) break; - } + if (!(pin1State || pin2State)) break; + } - if (total1 >= CS_Timeout_Millis) { - return -2; - } else { - return 1; - } + if (total1 >= CS_Timeout_Millis) { + return -2; + } else { + return 1; + } } diff --git a/Firmware/CapacitiveSensor.h b/Firmware/CapacitiveSensor.h index 8112244..03455ae 100644 --- a/Firmware/CapacitiveSensor.h +++ b/Firmware/CapacitiveSensor.h @@ -115,79 +115,79 @@ #include "portable.h" #include "avr/pgmspace.h" -#define GPIO_ID(pin) (g_APinDescription[pin].ulGPIOId) -#define GPIO_TYPE(pin) (g_APinDescription[pin].ulGPIOType) -#define GPIO_BASE(pin) (g_APinDescription[pin].ulGPIOBase) -#define DIR_OFFSET_SS 0x01 -#define DIR_OFFSET_SOC 0x04 -#define EXT_PORT_OFFSET_SS 0x0A -#define EXT_PORT_OFFSET_SOC 0x50 +#define GPIO_ID(pin) (g_APinDescription[pin].ulGPIOId) +#define GPIO_TYPE(pin) (g_APinDescription[pin].ulGPIOType) +#define GPIO_BASE(pin) (g_APinDescription[pin].ulGPIOBase) +#define DIR_OFFSET_SS 0x01 +#define DIR_OFFSET_SOC 0x04 +#define EXT_PORT_OFFSET_SS 0x0A +#define EXT_PORT_OFFSET_SOC 0x50 /* GPIO registers base address */ -#define PIN_TO_BASEREG(pin) ((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase) -#define PIN_TO_BITMASK(pin) pin -#define IO_REG_TYPE uint32_t +#define PIN_TO_BASEREG(pin) ((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase) +#define PIN_TO_BITMASK(pin) pin +#define IO_REG_TYPE uint32_t #define IO_REG_ASM static inline __attribute__((always_inline)) IO_REG_TYPE directRead(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) { - IO_REG_TYPE ret; - if (SS_GPIO == GPIO_TYPE(pin)) { - ret = READ_ARC_REG(((IO_REG_TYPE)base + EXT_PORT_OFFSET_SS)); - } else { - ret = MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, EXT_PORT_OFFSET_SOC); - } - return ((ret >> GPIO_ID(pin)) & 0x01); + IO_REG_TYPE ret; + if (SS_GPIO == GPIO_TYPE(pin)) { + ret = READ_ARC_REG(((IO_REG_TYPE)base + EXT_PORT_OFFSET_SS)); + } else { + ret = MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, EXT_PORT_OFFSET_SOC); + } + return ((ret >> GPIO_ID(pin)) & 0x01); } static inline __attribute__((always_inline)) void directModeInput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) { - if (SS_GPIO == GPIO_TYPE(pin)) { - WRITE_ARC_REG(READ_ARC_REG((((IO_REG_TYPE)base) + DIR_OFFSET_SS)) & ~(0x01 << GPIO_ID(pin)), - ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); - } else { - MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) &= ~(0x01 << GPIO_ID(pin)); - } + if (SS_GPIO == GPIO_TYPE(pin)) { + WRITE_ARC_REG(READ_ARC_REG((((IO_REG_TYPE)base) + DIR_OFFSET_SS)) & ~(0x01 << GPIO_ID(pin)), + ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); + } else { + MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) &= ~(0x01 << GPIO_ID(pin)); + } } static inline __attribute__((always_inline)) void directModeOutput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) { - if (SS_GPIO == GPIO_TYPE(pin)) { - WRITE_ARC_REG(READ_ARC_REG(((IO_REG_TYPE)(base) + DIR_OFFSET_SS)) | (0x01 << GPIO_ID(pin)), - ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); - } else { - MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) |= (0x01 << GPIO_ID(pin)); - } + if (SS_GPIO == GPIO_TYPE(pin)) { + WRITE_ARC_REG(READ_ARC_REG(((IO_REG_TYPE)(base) + DIR_OFFSET_SS)) | (0x01 << GPIO_ID(pin)), + ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); + } else { + MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) |= (0x01 << GPIO_ID(pin)); + } } static inline __attribute__((always_inline)) void directWriteLow(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) { - if (SS_GPIO == GPIO_TYPE(pin)) { - WRITE_ARC_REG(READ_ARC_REG(base) & ~(0x01 << GPIO_ID(pin)), base); - } else { - MMIO_REG_VAL(base) &= ~(0x01 << GPIO_ID(pin)); - } + if (SS_GPIO == GPIO_TYPE(pin)) { + WRITE_ARC_REG(READ_ARC_REG(base) & ~(0x01 << GPIO_ID(pin)), base); + } else { + MMIO_REG_VAL(base) &= ~(0x01 << GPIO_ID(pin)); + } } static inline __attribute__((always_inline)) void directWriteHigh(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) { - if (SS_GPIO == GPIO_TYPE(pin)) { - WRITE_ARC_REG(READ_ARC_REG(base) | (0x01 << GPIO_ID(pin)), base); - } else { - MMIO_REG_VAL(base) |= (0x01 << GPIO_ID(pin)); - } + if (SS_GPIO == GPIO_TYPE(pin)) { + WRITE_ARC_REG(READ_ARC_REG(base) | (0x01 << GPIO_ID(pin)), base); + } else { + MMIO_REG_VAL(base) |= (0x01 << GPIO_ID(pin)); + } } -#define DIRECT_READ(base, pin) directRead(base, pin) -#define DIRECT_MODE_INPUT(base, pin) directModeInput(base, pin) -#define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(base, pin) -#define DIRECT_WRITE_LOW(base, pin) directWriteLow(base, pin) -#define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(base, pin) +#define DIRECT_READ(base, pin) directRead(base, pin) +#define DIRECT_MODE_INPUT(base, pin) directModeInput(base, pin) +#define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(base, pin) +#define DIRECT_WRITE_LOW(base, pin) directWriteLow(base, pin) +#define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(base, pin) #endif @@ -201,32 +201,32 @@ void directWriteHigh(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) // library interface description class CapacitiveSensor { - // user-accessible "public" interface + // user-accessible "public" interface public: - // methods - CapacitiveSensor(uint8_t sendPin, uint8_t receivePin1, uint8_t receivePin2); - unsigned int* sense(uint8_t samples); - // library-accessible "private" interface + // methods + CapacitiveSensor(uint8_t sendPin, uint8_t receivePin1, uint8_t receivePin2); + unsigned int* sense(uint8_t samples); + // library-accessible "private" interface private: - // variables - int error; - unsigned long leastTotal; - unsigned int loopTimingFactor; - unsigned long CS_Timeout_Millis; - unsigned int total1; - unsigned int total2; - unsigned int *retVal; - bool pin1State; - bool pin2State; + // variables + int error; + unsigned long leastTotal; + unsigned int loopTimingFactor; + unsigned long CS_Timeout_Millis; + unsigned int total1; + unsigned int total2; + unsigned int *retVal; + bool pin1State; + bool pin2State; - IO_REG_TYPE sBit; // send pin's ports and bitmask - volatile IO_REG_TYPE *sReg; - IO_REG_TYPE r1Bit; // receive 1 pin's ports and bitmask - volatile IO_REG_TYPE *r1Reg; - IO_REG_TYPE r2Bit; // receive 2 pin's ports and bitmask - volatile IO_REG_TYPE *r2Reg; - // methods - int SenseOneCycle(void); + IO_REG_TYPE sBit; // send pin's ports and bitmask + volatile IO_REG_TYPE *sReg; + IO_REG_TYPE r1Bit; // receive 1 pin's ports and bitmask + volatile IO_REG_TYPE *r1Reg; + IO_REG_TYPE r2Bit; // receive 2 pin's ports and bitmask + volatile IO_REG_TYPE *r2Reg; + // methods + int SenseOneCycle(void); }; #endif diff --git a/Firmware/OpeNITHM.ino b/Firmware/OpeNITHM.ino index 8328569..50a23ca 100644 --- a/Firmware/OpeNITHM.ino +++ b/Firmware/OpeNITHM.ino @@ -1,13 +1,11 @@ -#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32u4__) -#include "USBOutput.h" -#define USB -#endif +#include "PinConfig.h" #ifdef USB #include "USBOutput.h" #else #include "SerialOutput.h" #endif + #include "AirSensor.h" #include "Touchboard.h" #include @@ -26,216 +24,213 @@ Touchboard *touchboard; AirSensor *sensor; Output *output; -char *command; +char command[32]; +// Triggered when Touchboard determines a key was pressed void onKeyPress(int key, bool wasHeld) { - lightIntensity[key] = 1.0f; - output->sendKeyEvent(key, true, wasHeld); - keyStates[key] = true; + lightIntensity[key] = 1.0f; + output->sendKeyEvent(key, true, wasHeld); + keyStates[key] = true; } // Parse configuration command. For now, a serial terminal is required (like the monitor in Arduino IDE) // Eventually I will make a config tool void parseCommand() { - char input1 = Serial.read(); - char input2; - switch (input1) - { - case 't': // touchboard - while(!Serial.available()); - input2 = Serial.read(); - while(!Serial.available()); - switch (input2) - { - case 't': // threshold - touchboard->setThreshold(Serial.parseInt()); - break; - case 'd': // dead zone - touchboard->setDeadzone(Serial.parseInt()); - break; - case 'a': // alpha - touchboard->setAlpha(Serial.parseFloat()); - break; - case 'c': // calibrate - touchboard->calibrateKeys(); - } - break; - case 'i': // ir sensors - while(!Serial.available()); - input2 = Serial.read(); - while(!Serial.available()); - switch (input2) - { - case 'd': // dead zone - sensor->setDeadzone(Serial.parseInt()); - break; - case 'a': // alpha - sensor->setAlpha(Serial.parseFloat()); - break; - case 'c': // calibrate - sensor->recalibrate(); - } - break; - case 'p': // pause - activated = false; - break; - case 'r': // resume - activated = true; - break; - case 'g': // print values - Serial.print("tt \t"); - Serial.println(touchboard->getThreshold()); - Serial.print("td \t"); - Serial.println(touchboard->getDeadzone()); - Serial.print("ta \t"); - Serial.println(touchboard->getAlpha()); - 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"); - Serial.println(led_on.g); - Serial.print("lob \t"); - Serial.println(led_on.b); - Serial.print("lfr \t"); - Serial.println(led_off.r); - Serial.print("lfg \t"); - Serial.println(led_off.g); - Serial.print("lfb \t"); - Serial.println(led_off.b); - Serial.print(";"); - break; - case 'a': // check if activated - Serial.println(activated); - Serial.print(";"); - break; - case 'l': // change led color - while(!Serial.available()); - input2 = Serial.read(); - while(!Serial.available()); - switch (input2) - { - case 'o': // on - while(!Serial.available()); - switch ((char)Serial.read()) - { - case 'r': // red - led_on.r = Serial.parseInt(); // for now this has to be expressed in decimal - break; - case 'g': // green - led_on.g = Serial.parseInt(); - break; - case 'b': // blue - led_on.b = Serial.parseInt(); - break; - case 'e': // everything - led_on = Serial.parseInt(); - } - break; - case 'f': // off - while(!Serial.available()); - switch ((char)Serial.read()) - { - case 'r': // red - led_off.r = Serial.parseInt(); // for now this has to be expressed in decimal - break; - case 'g': // green - led_off.g = Serial.parseInt(); - break; - case 'b': // blue - led_off.b = Serial.parseInt(); - break; - case 'e': // everything - led_off = Serial.parseInt(); - } - break; - } - break; - } + char input1 = Serial.read(); + char input2; + switch (input1) + { + case 't': // touchboard + while (!Serial.available()); + input2 = Serial.read(); + switch (input2) + { + case 't': // threshold + touchboard->setThreshold(Serial.parseInt()); + break; + case 'd': // dead zone + touchboard->setDeadzone(Serial.parseInt()); + break; + case 'a': // alpha + touchboard->setAlpha(Serial.parseFloat()); + break; + case 'c': // calibrate + touchboard->calibrateKeys(); + } + break; + case 'i': // ir sensors + 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(); + } + break; + case 'p': // pause + activated = false; + break; + case 'r': // resume + activated = true; + break; + case 'g': // print values + Serial.print("tt \t"); + Serial.println(touchboard->getThreshold()); + Serial.print("td \t"); + Serial.println(touchboard->getDeadzone()); + Serial.print("ta \t"); + Serial.println(touchboard->getAlpha()); + 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"); + Serial.println(led_on.g); + Serial.print("lob \t"); + Serial.println(led_on.b); + Serial.print("lfr \t"); + Serial.println(led_off.r); + Serial.print("lfg \t"); + Serial.println(led_off.g); + Serial.print("lfb \t"); + Serial.println(led_off.b); + Serial.print(";"); + break; + case 'a': // check if activated + Serial.println(activated); + Serial.print(";"); + break; + case 'l': // change led color + while (!Serial.available()); + input2 = Serial.read(); + switch (input2) + { + case 'o': // on + while (!Serial.available()); + switch ((char)Serial.read()) + { + case 'r': // red + led_on.r = Serial.parseInt(); // for now this has to be expressed in decimal + break; + case 'g': // green + led_on.g = Serial.parseInt(); + break; + case 'b': // blue + led_on.b = Serial.parseInt(); + break; + case 'e': // everything + led_on = Serial.parseInt(); + } + break; + case 'f': // off + while (!Serial.available()); + switch ((char)Serial.read()) + { + case 'r': // red + led_off.r = Serial.parseInt(); // for now this has to be expressed in decimal + break; + case 'g': // green + led_off.g = Serial.parseInt(); + break; + case 'b': // blue + led_off.b = Serial.parseInt(); + break; + case 'e': // everything + led_off = Serial.parseInt(); + } + break; + } + break; + } } void setup() { - Serial.begin(115200); - command = malloc(32); + Serial.begin(115200); + FastLED.addLeds(leds, 16); - FastLED.addLeds(leds, 16); + // Set LEDs blue + for (CRGB& led : leds) + { + led = 0x0000FF; + FastLED.show(); + } - // Set LEDs blue - for (CRGB& led : leds) - { - led = 0x0000FF; - FastLED.show(); - } + // Initialize and calibrate touch sensors + touchboard = new Touchboard(onKeyPress); - // Initialize and calibrate touch sensors - touchboard = new Touchboard(onKeyPress); + // Set LEDs red + for (CRGB& led : leds) + { + led = 0xFF0000; + FastLED.show(); + } - // Set LEDs red - for (CRGB& led : leds) - { - led = 0xFF0000; - FastLED.show(); - } + // Initialize air sensor - will automatically calibrate as it starts being read + sensor = new AirSensor(500, 50); - // Initialize air sensor - will automatically calibrate as it starts being read - sensor = new AirSensor(500, 50); - - // Initialize relevant output method / USB or serial - #ifdef USB - output = new USBOutput(); - #else - output = new SerialOutput(); - #endif + // Initialize relevant output method / USB or serial +#ifdef USB + output = new USBOutput(); +#else + output = new SerialOutput(); +#endif } void loop() { - // Process config commands - if (Serial.available()) - { - parseCommand(); - } - // If currently paused through a config command, do not execute main loop - if (!activated) return; + // Process config commands + if (Serial.available()) + { + parseCommand(); + } - // Scan touch keyboard and update lights - touchboard->scan(); - for (int i = 0; i < 16; i++) - { - if (lightIntensity[i] > 0.05f) - lightIntensity[i] -= 0.05f; + // If currently paused through a config command, do not execute main loop + if (!activated) return; - // If the key is currently being held, set its color to purple - if (touchboard->update(i)) - { - leds[i].setRGB(min(led_on.r / 2 + led_on.r / 2 * lightIntensity[i], 255), min(led_on.g / 2 + led_on.g / 2 * lightIntensity[i], 255), min(led_on.b / 2 + led_on.b / 2 * lightIntensity[i], 255)); - } - else - { - // If not, make it yellow and send the "key released" event if it was previously pressed - leds[i].setRGB(led_off.r/2, led_off.g/2, led_off.b/2); - if (keyStates[i]) - { - output->sendKeyEvent(i, false, false); - keyStates[i] = false; - } - } - } + // Scan touch keyboard and update lights + touchboard->scan(); + for (int i = 0; i < 16; i++) + { + if (lightIntensity[i] > 0.05f) + lightIntensity[i] -= 0.05f; - // Process air sensor hand position - const float newPosition = sensor->getHandPosition(); - if (newPosition != sensorPosition) - { - output->sendSensorEvent(newPosition); - sensorPosition = newPosition; - } + // If the key is currently being held, set its color to purple + if (touchboard->update(i)) + { + leds[i].setRGB(min(led_on.r / 2 + led_on.r / 2 * lightIntensity[i], 255), min(led_on.g / 2 + led_on.g / 2 * lightIntensity[i], 255), min(led_on.b / 2 + led_on.b / 2 * lightIntensity[i], 255)); + } + else + { + // If not, make it yellow and send the "key released" event if it was previously pressed + leds[i].setRGB(led_off.r / 2, led_off.g / 2, led_off.b / 2); + if (keyStates[i]) + { + output->sendKeyEvent(i, false, false); + keyStates[i] = false; + } + } + } + // Process air sensor hand position + const float newPosition = sensor->getHandPosition(); + if (newPosition != sensorPosition) + { + output->sendSensorEvent(newPosition); + sensorPosition = newPosition; + } - // 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()) - FastLED.show(); -} \ No newline at end of file + // 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()) + FastLED.show(); + +} diff --git a/Firmware/Output.h b/Firmware/Output.h index 76cd6a5..a96e145 100644 --- a/Firmware/Output.h +++ b/Firmware/Output.h @@ -3,18 +3,11 @@ #ifndef _OUTPUT_h #define _OUTPUT_h -#if defined(ARDUINO) && ARDUINO >= 100 - #include "arduino.h" -#else - #include "WProgram.h" -#endif - class Output { - public: - virtual void sendKeyEvent(int key, bool pressed, bool doublePressed); - virtual void sendSensorEvent(float position); + public: + virtual void sendKeyEvent(int key, bool pressed, bool doublePressed); + virtual void sendSensorEvent(float position); }; #endif - diff --git a/Firmware/PinConfig.h b/Firmware/PinConfig.h index e6ac858..1dc5737 100644 --- a/Firmware/PinConfig.h +++ b/Firmware/PinConfig.h @@ -1,35 +1,43 @@ -#pragma once +#ifndef _PINCONFIG_h +#define _PINCONFIG_h -#define IR_SENSOR_MULTIPLEXED +#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32u4__) +#define USB +#endif + +//#define IR_SENSOR_MULTIPLEXED +//#define IR_SENSOR_ANALOG // Uncomment if IR sensors are hooked up to analog pins #define CALIBRATION_SAMPLES 500 // Multiplexer pin settings -#define MUX_A 9 -#define MUX_B 8 -#define MUX_C 7 +#define MUX_0 20 +#define MUX_1 19 +#define MUX_2 18 // Sensor pin settings -#define IR_A 5 -#define IR_B 4 -#define IR_C 3 +#define LED_0 0 +#define LED_1 2 +#define LED_2 3 #ifdef IR_SENSOR_MULTIPLEXED - #define SENSOR_IN A0 +#define SENSOR_IN A0 #else - #define AIR_SENSOR_0_PIN 4 - #define AIR_SENSOR_1_PIN 5 - #define AIR_SENSOR_2_PIN 6 - #define AIR_SENSOR_3_PIN 7 - #define AIR_SENSOR_4_PIN 8 - #define AIR_SENSOR_5_PIN 9 +#define AIR_SENSOR_0_PIN 4 +#define AIR_SENSOR_1_PIN 5 +#define AIR_SENSOR_2_PIN 6 +#define AIR_SENSOR_3_PIN 7 +#define AIR_SENSOR_4_PIN 8 +#define AIR_SENSOR_5_PIN 9 #endif // Capsense pin settings -#define KEYBOARDPIN_1 10 -#define KEYBOARDPIN_2 12 -#define KEYBOARDPIN_COM 11 +#define RECEIVE_1 21 +#define RECEIVE_2 1 +#define SEND 10 // Lighting pin settings #define LED_TYPE WS2812B #define LED_ORDER GRB -#define RGBPIN 2 +#define RGBPIN 16 + +#endif diff --git a/Firmware/SerialOutput.cpp b/Firmware/SerialOutput.cpp index a1606f4..3bbbffc 100644 --- a/Firmware/SerialOutput.cpp +++ b/Firmware/SerialOutput.cpp @@ -1,6 +1,6 @@ -// -// -// +// +// +// #include "SerialOutput.h" @@ -10,20 +10,20 @@ void SerialOutput::sendKeyEvent(int key, bool pressed, bool doublePressed) { - builtPacket.data = 0; - builtPacket.keyEvent.isKeyboard = true; - builtPacket.keyEvent.isPressed = pressed; - builtPacket.keyEvent.isDoublePressed = doublePressed; - builtPacket.keyEvent.key = key; + builtPacket.data = 0; + builtPacket.keyEvent.isKeyboard = true; + builtPacket.keyEvent.isPressed = pressed; + builtPacket.keyEvent.isDoublePressed = doublePressed; + builtPacket.keyEvent.key = key; - Serial.write(builtPacket.data); + Serial.write(builtPacket.data); } void SerialOutput::sendSensorEvent(float position) { - builtPacket.data = 0; - builtPacket.sensorEvent.isKeyboard = false; - builtPacket.sensorEvent.position = position * 127; + builtPacket.data = 0; + builtPacket.sensorEvent.isKeyboard = false; + builtPacket.sensorEvent.position = position * 127; - Serial.write(builtPacket.data); + Serial.write(builtPacket.data); } diff --git a/Firmware/SerialOutput.h b/Firmware/SerialOutput.h index cdac7b7..b152aba 100644 --- a/Firmware/SerialOutput.h +++ b/Firmware/SerialOutput.h @@ -4,39 +4,38 @@ #define _SERIALOUTPUT_h #if defined(ARDUINO) && ARDUINO >= 100 - #include "arduino.h" +#include "arduino.h" #else - #include "WProgram.h" +#include "WProgram.h" #endif #include "Output.h" //All events can fit within one byte to save time sending over serial union Packet { - uint8_t data; - struct - { - bool isKeyboard : 1; - bool isPressed : 1; - bool isDoublePressed : 1; - unsigned int key : 5; - } keyEvent; - struct - { - bool isKeyboard : 1; - int position : 7; - } sensorEvent; + uint8_t data; + struct + { + bool isKeyboard : 1; + bool isPressed : 1; + bool isDoublePressed : 1; + unsigned int key : 5; + } keyEvent; + struct + { + bool isKeyboard : 1; + int position : 7; + } sensorEvent; }; class SerialOutput : public Output { -private: - Packet builtPacket = Packet(); -public: - void sendKeyEvent(int key, bool pressed, bool doublePressed) override; - void sendSensorEvent(float position) override; + private: + Packet builtPacket = Packet(); + public: + void sendKeyEvent(int key, bool pressed, bool doublePressed) override; + void sendSensorEvent(float position) override; }; #endif - diff --git a/Firmware/Touchboard.cpp b/Firmware/Touchboard.cpp index efdd212..5aaff08 100644 --- a/Firmware/Touchboard.cpp +++ b/Firmware/Touchboard.cpp @@ -2,123 +2,127 @@ void Touchboard::scan() { - // For each key, set multiplexers and poll both capacitive sensors simultaneously - for (int i = 0; i < 8; i++) - { - digitalWrite(MUX_A, bitRead(i, 0)); - digitalWrite(MUX_B, bitRead(i, 1)); - digitalWrite(MUX_C, bitRead(i, 2)); + // For each key, set multiplexers and poll both capacitive sensors simultaneously + for (int i = 0; i < 8; i++) + { + digitalWrite(MUX_0, bitRead(i, 0)); + digitalWrite(MUX_1, bitRead(i, 1)); + digitalWrite(MUX_2, bitRead(i, 2)); - unsigned int* values = sensor.sense(3); + unsigned int* values = sensor.sense(3); - // Store the values received from the sensor poll into their respective positions - keys[i] = values[0]; - keys[i + 8] = values[1]; - } + // Store the values received from the sensor poll into their respective positions + keys[i] = values[0]; + keys[i + 8] = values[1]; + } } void Touchboard::calibrateKeys() { - // Reset calibration data for all keys - for (int i = 0; i < 16; i++) - { - em_averages[i] = 0; - neutral_values[i] = 0; - key_states[i] = false; - keys[i] = false; - } + // Reset calibration data for all keys + for (int i = 0; i < 16; i++) + { + em_averages[i] = 0; + neutral_values[i] = 0; + key_states[i] = false; + keys[i] = false; + } - for (int i = 0; i < CALIBRATION_SAMPLES; i++) { - // Repeatedly scan all keys - scan(); - // Store the lowest read value as our baseline - for (int j = 0; j < 16; j++) { - if (keys[j] > neutral_values[j]) neutral_values[j] = keys[j]; - } - } - // After calibration is complete, initialize the averages to the baseline values established previously - for (int i = 0; i < 16; i++) { - em_averages[i] = neutral_values[i]; - } + for (int i = 0; i < CALIBRATION_SAMPLES; i++) { + // Repeatedly scan all keys + scan(); + // Store the lowest read value as our baseline + for (int j = 0; j < 16; j++) { + if (keys[j] > neutral_values[j]) neutral_values[j] = keys[j]; + } + } + // After calibration is complete, initialize the averages to the baseline values established previously + for (int i = 0; i < 16; i++) { + em_averages[i] = neutral_values[i]; + } } bool Touchboard::update(int key) { - int read_value = keys[key]; - float new_average = alpha * read_value + (1 - alpha) * em_averages[key]; - if (read_value - neutral_values[key] < deadzone) - { - // If we are in the deadzone, ignore any touch events, set the key to be untouched and update the moving average. - if (key_states[key]) - em_averages[key] = neutral_values[key]; - else - em_averages[key] = new_average; - key_states[key] = false; - } - else - { - //If we are outside of the deadzone: - if (read_value > em_averages[key] + threshold) - { - // If we just detected a touch, make the key touched, discard the previous moving average and trigger the callback. - em_averages[key] = read_value; - onKeyPress(key, key_states[key]); - key_states[key] = true; - } - else - { - // Otherwise, just update the average. - em_averages[key] = new_average; - } - } - return key_states[key]; + int read_value = keys[key]; + float new_average = alpha * read_value + (1 - alpha) * em_averages[key]; + if (read_value - neutral_values[key] < deadzone) + { + // If we are in the deadzone, ignore any touch events, set the key to be untouched and update the moving average. + if (key_states[key]) + em_averages[key] = neutral_values[key]; + else + em_averages[key] = new_average; + key_states[key] = false; + } + else + { + //If we are outside of the deadzone: + if (read_value > em_averages[key] + threshold) + { + // If we just detected a touch, make the key touched, discard the previous moving average and trigger the callback. + em_averages[key] = read_value; + onKeyPress(key, key_states[key]); + key_states[key] = true; + } + else + { + // Otherwise, just update the average. + em_averages[key] = new_average; + } + } + return key_states[key]; } void Touchboard::setThreshold(uint16_t threshold) { - this->threshold = threshold; - EEPROM.put(0, threshold); + this->threshold = threshold; + EEPROM.put(0, threshold); } void Touchboard::setDeadzone(uint16_t deadzone) { - this->deadzone = deadzone; - EEPROM.put(4, deadzone); + this->deadzone = deadzone; + EEPROM.put(4, deadzone); } void Touchboard::setAlpha(float alpha) { - this->alpha = alpha; - EEPROM.put(8, alpha); + this->alpha = alpha; + EEPROM.put(8, alpha); } int Touchboard::getThreshold() { - return threshold; + return threshold; } int Touchboard::getDeadzone() { - return deadzone; + return deadzone; } float Touchboard::getAlpha() { - return alpha; + return alpha; +} + +float Touchboard::getEmAverages(int key) +{ + return em_averages[key]; } Touchboard::Touchboard(void(*keyPressCallback)(int, bool)) : - sensor(CapacitiveSensor(KEYBOARDPIN_COM, KEYBOARDPIN_1, KEYBOARDPIN_2)), - onKeyPress(keyPressCallback) + sensor(CapacitiveSensor(SEND, RECEIVE_1, RECEIVE_2)), + onKeyPress(keyPressCallback) { - EEPROM.get(0, threshold); - EEPROM.get(4, deadzone); - EEPROM.get(8, alpha); + EEPROM.get(0, threshold); + EEPROM.get(4, deadzone); + EEPROM.get(8, alpha); - pinMode(MUX_A, OUTPUT); - pinMode(MUX_B, OUTPUT); - pinMode(MUX_C, OUTPUT); + pinMode(MUX_0, OUTPUT); + pinMode(MUX_1, OUTPUT); + pinMode(MUX_2, OUTPUT); - calibrateKeys(); + calibrateKeys(); } - diff --git a/Firmware/Touchboard.h b/Firmware/Touchboard.h index f2a5fff..e1e8c67 100644 --- a/Firmware/Touchboard.h +++ b/Firmware/Touchboard.h @@ -3,12 +3,6 @@ #ifndef _TOUCHBOARD_h #define _TOUCHBOARD_h -#if defined(ARDUINO) && ARDUINO >= 100 - #include "arduino.h" -#else - #include "WProgram.h" -#endif - #include "PinConfig.h" #include "CapacitiveSensor.h" #include @@ -16,34 +10,32 @@ class Touchboard { -private: - CapacitiveSensor sensor; + private: + CapacitiveSensor sensor; + uint16_t threshold; + uint16_t deadzone; + float alpha; - uint16_t threshold; - uint16_t deadzone; - float alpha; + float em_averages[16]; + unsigned int keys[16]; + int neutral_values[16]; + void (*onKeyPress)(int, bool); - float em_averages[16]; - unsigned int keys[16]; - int neutral_values[16]; - void (*onKeyPress)(int, bool); + public: + boolean key_states[16]; - -public: - boolean key_states[16]; - - Touchboard(void(*keyPressCallback)(int, bool)); - void scan(); - bool update(int key); - void setThreshold(uint16_t threshold); - void setDeadzone(uint16_t deadzone); - void setAlpha(float alpha); - int getThreshold(); - int getDeadzone(); - float getAlpha(); - void calibrateKeys(); + Touchboard(void(*keyPressCallback)(int, bool)); + void scan(); + bool update(int key); + void setThreshold(uint16_t threshold); + void setDeadzone(uint16_t deadzone); + void setAlpha(float alpha); + int getThreshold(); + int getDeadzone(); + float getAlpha(); + float getEmAverages(int key); + void calibrateKeys(); }; #endif - diff --git a/Firmware/USBOutput.cpp b/Firmware/USBOutput.cpp index 572c423..cccb52f 100644 --- a/Firmware/USBOutput.cpp +++ b/Firmware/USBOutput.cpp @@ -1,51 +1,52 @@ -#ifdef USB - -#include "USBOutput.h" - -char bottomRow[] = {'a', 'z', 's', 'x', 'd', 'c', 'f', 'v', 'g', 'b', 'h', 'n', 'j', 'm', 'k', ','}; -char topRow[] = {'1', 'q', '2', 'w', '3', 'e', '4', 'r', '5', 't', '6', 'y', '7', 'u', '8', 'i'}; - -void USBOutput::sendKeyEvent(int key, bool pressed, bool doublePressed) -{ - if(pressed){ - if(doublePressed){ - NKROKeyboard.write(topRow[key]); - } - else{ - NKROKeyboard.press(bottomRow[key]); - } - } - else{ - NKROKeyboard.release(bottomRow[key]); - } -} - -void USBOutput::sendSensorEvent(float position) -{ - // Send hand up / hand down key - if(position > lastPosition){ - NKROKeyboard.write(KEY_PAGE_UP); - } - if(position < lastPosition){ - NKROKeyboard.write(KEY_PAGE_DOWN); - } - - // Send hand seen / unseen key - if(position > 0.05f){ - NKROKeyboard.press(KEY_HOME); - } - else{ - NKROKeyboard.release(KEY_HOME); - } - - // Send hand moved key - NKROKeyboard.write(KEY_END); - -} - -USBOutput::USBOutput(){ - NKROKeyboard.begin(); - lastPosition = 0; -} +#include "PinConfig.h" + +#ifdef USB +#include "USBOutput.h" + +char bottomRow[] = {'a', 'z', 's', 'x', 'd', 'c', 'f', 'v', 'g', 'b', 'h', 'n', 'j', 'm', 'k', ','}; +char topRow[] = {'1', 'q', '2', 'w', '3', 'e', '4', 'r', '5', 't', '6', 'y', '7', 'u', '8', 'i'}; + +void USBOutput::sendKeyEvent(int key, bool pressed, bool doublePressed) +{ + if (pressed) { + if (doublePressed) { + NKROKeyboard.write(topRow[key]); + } + else { + NKROKeyboard.press(bottomRow[key]); + } + } + else { + NKROKeyboard.release(bottomRow[key]); + } +} + +void USBOutput::sendSensorEvent(float position) +{ + // Send hand up / hand down key + if (position > lastPosition) { + NKROKeyboard.write(KEY_PAGE_UP); + } + if (position < lastPosition) { + NKROKeyboard.write(KEY_PAGE_DOWN); + } + + // Send hand seen / unseen key + if (position > 0.05f) { + NKROKeyboard.press(KEY_HOME); + } + else { + NKROKeyboard.release(KEY_HOME); + } + + // Send hand moved key + NKROKeyboard.write(KEY_END); + +} + +USBOutput::USBOutput() { + NKROKeyboard.begin(); + lastPosition = 0; +} #endif diff --git a/Firmware/USBOutput.h b/Firmware/USBOutput.h index 1388954..582f7ef 100644 --- a/Firmware/USBOutput.h +++ b/Firmware/USBOutput.h @@ -1,28 +1,26 @@ -#define USBCON -#include - -// USBOutput.h - -#ifndef _USBOUTPUT_h -#define _USBOUTPUT_h - -#if defined(ARDUINO) && ARDUINO >= 100 - #include "arduino.h" -#else - #include "WProgram.h" -#endif -#include "Output.h" - - -class USBOutput : public Output -{ -private: - float lastPosition; -public: - void sendKeyEvent(int key, bool pressed, bool doublePressed) override; - void sendSensorEvent(float position) override; - USBOutput(); -}; - -#endif +// USBOutput.h +#ifndef _USBOUTPUT_h +#define _USBOUTPUT_h + +#include "Output.h" + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "arduino.h" +#else +#include "WProgram.h" +#endif + +#include + +class USBOutput : public Output +{ + private: + float lastPosition; + public: + void sendKeyEvent(int key, bool pressed, bool doublePressed) override; + void sendSensorEvent(float position) override; + USBOutput(); +}; + +#endif