From fcc47f9c899560e7892c7720c34b056fa8d8e49e Mon Sep 17 00:00:00 2001 From: Jonathan Montineri Date: Tue, 22 Jan 2019 14:22:00 -0500 Subject: [PATCH] Initial commit to public repo --- AirSensor.cpp | 194 ++++++++++++++++++++++++++++++++++++ AirSensor.h | 52 ++++++++++ CapacitiveSensor.cpp | 158 +++++++++++++++++++++++++++++ CapacitiveSensor.h | 232 +++++++++++++++++++++++++++++++++++++++++++ OpeNITHM.ino | 161 ++++++++++++++++++++++++++++++ Output.h | 20 ++++ PinConfig.h | 23 +++++ SerialOutput.cpp | 25 +++++ SerialOutput.h | 42 ++++++++ Touchboard.cpp | 117 ++++++++++++++++++++++ Touchboard.h | 49 +++++++++ USBOutput.cpp | 13 +++ USBOutput.h | 21 ++++ 13 files changed, 1107 insertions(+) create mode 100644 AirSensor.cpp create mode 100644 AirSensor.h create mode 100644 CapacitiveSensor.cpp create mode 100644 CapacitiveSensor.h create mode 100644 OpeNITHM.ino create mode 100644 Output.h create mode 100644 PinConfig.h create mode 100644 SerialOutput.cpp create mode 100644 SerialOutput.h create mode 100644 Touchboard.cpp create mode 100644 Touchboard.h create mode 100644 USBOutput.cpp create mode 100644 USBOutput.h diff --git a/AirSensor.cpp b/AirSensor.cpp new file mode 100644 index 0000000..0eb742f --- /dev/null +++ b/AirSensor.cpp @@ -0,0 +1,194 @@ +// +// +// + +#include "AirSensor.h" + + +void AirSensor::changeLight(int light) +{ + switch (light) + { + case 0: + pinMode(IR_A, OUTPUT); + pinMode(IR_B, OUTPUT); + pinMode(IR_C, 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(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(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(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(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(IR_A, HIGH); + digitalWrite(IR_B, LOW); + digitalWrite(IR_C, LOW); + break; + default: + turnOffLight(); + break; + } +} + +void AirSensor::turnOffLight() +{ + pinMode(IR_A, INPUT); + pinMode(IR_B, INPUT); + pinMode(IR_C, INPUT); +} + +int AirSensor::getValue(int sensor, bool light) +{ + digitalWrite(MUX_A, bitRead(sensor, 0)); + digitalWrite(MUX_B, bitRead(sensor, 1)); + digitalWrite(MUX_C, bitRead(sensor, 2)); + + if(light) + { + changeLight(sensor); + } + else + { + turnOffLight(); + } + return analogRead(SENSOR_IN); +} + + +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) +{ + EEPROM.get(12, deadzone); + EEPROM.get(16, alpha); +} + +bool AirSensor::isCalibrated() +{ + if(!allCalibrated) + { + for(int i = 0; i < 6; i++) + { + if(!calibrated[i]) + return false; + } + allCalibrated = true; + } + return allCalibrated; +} + +bool AirSensor::getSensorState(int sensor) +{ + int value = getValue(sensor, true); + turnOffLight(); + + if(allCalibrated || calibrated[sensor]){ + sensorValues[sensor] = (float) value * EMA_AIRSENSOR_ALPHA + sensorValues[sensor] * (1 - EMA_AIRSENSOR_ALPHA); + return sensorValues[sensor] < thresholds[sensor]; + } + else + { + if(skippedSamples[sensor] > samplesToSkip) + { + if(value < thresholds[sensor]) thresholds[sensor] = value; + if(++calibrationSamples[sensor] > samplesToAcquire) + { + sensorValues[sensor] = value; + calibrated[sensor] = true; + thresholds[sensor] -= AIR_SENSOR_THRESHOLD_SUBTRACT; + }; + } + else + { + skippedSamples[sensor]++; + } + return false; + } +} + +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; +} + +void AirSensor::setDeadzone(int deadzone) +{ + this->deadzone = deadzone; + EEPROM.put(12, deadzone); +} + +void AirSensor::setAlpha(float alpha) +{ + this->alpha = alpha; + EEPROM.put(16, alpha); +} + +int AirSensor::getDeadzone() +{ + return deadzone; +} + +float AirSensor::getAlpha() +{ + 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; +} diff --git a/AirSensor.h b/AirSensor.h new file mode 100644 index 0000000..4692043 --- /dev/null +++ b/AirSensor.h @@ -0,0 +1,52 @@ +// AirSensor.h + +#ifndef _AIRSENSOR_h +#define _AIRSENSOR_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "arduino.h" +#else + #include "WProgram.h" +#endif + +#include "PinConfig.h" +#include + +#define AIR_SENSOR_THRESHOLD_SUBTRACT 5 +#define EMA_AIRSENSOR_ALPHA 0.4f + +class AirSensor +{ +private: + void changeLight(int light); + void turnOffLight(); + + int thresholds[6]; + int calibrationSamples[6]; + int skippedSamples[6]; + float sensorValues[6]; + int samplesToAcquire; + int samplesToSkip; + + 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(); + + void setDeadzone(int deadzone); + void setAlpha(float alpha); + int getDeadzone(); + float getAlpha(); + void recalibrate(); +}; + + +#endif + diff --git a/CapacitiveSensor.cpp b/CapacitiveSensor.cpp new file mode 100644 index 0000000..dddd227 --- /dev/null +++ b/CapacitiveSensor.cpp @@ -0,0 +1,158 @@ +/* + 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 + 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: + + 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. +*/ + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#include "pins_arduino.h" +#include "WConstants.h" +#endif + +#include "CapacitiveSensor.h" + +// Constructor ///////////////////////////////////////////////////////////////// +// Function that handles the creation and setup of instances + +CapacitiveSensor::CapacitiveSensor(uint8_t sendPin, uint8_t receivePin1, uint8_t receivePin2) +{ + // initialize this instance's variables + error = 1; + loopTimingFactor = 310; // determined empirically - a hack + + CS_Timeout_Millis = (200 * (float)loopTimingFactor * (float)F_CPU) / 16000000; + + // Serial.print("timwOut = "); + // Serial.println(CS_Timeout_Millis); + + // 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; +#endif + pinMode(sendPin, OUTPUT); // sendpin to OUTPUT + pinMode(receivePin1, INPUT); // receivePin to INPUT + pinMode(receivePin2, INPUT); // receivePin to INPUT + + sBit = PIN_TO_BITMASK(sendPin); // get send pin's ports and bitmask + sReg = PIN_TO_BASEREG(sendPin); // get pointer to output register + + r1Bit = PIN_TO_BITMASK(receivePin1); + r1Reg = PIN_TO_BASEREG(receivePin1); + + r2Bit = PIN_TO_BITMASK(receivePin2); + r2Reg = PIN_TO_BASEREG(receivePin2); + + retVal = new unsigned int[2]; +} + +// Public Methods ////////////////////////////////////////////////////////////// +// Functions available in Wiring sketches, this library, and other libraries + +unsigned int* CapacitiveSensor::sense(uint8_t samples) +{ + total1 = 0; + total2 = 0; + + noInterrupts(); + for (uint8_t i = 0; i < samples; i++) { + if (!SenseOneCycle()) return nullptr; // variable over timeout + } + interrupts(); + + retVal[0] = total1; + retVal[1] = total2; + + return retVal; + +} + +// Private Methods ///////////////////////////////////////////////////////////// +// Functions only available to other functions in this library + +int CapacitiveSensor::SenseOneCycle(void) +{ + + DIRECT_WRITE_LOW(sReg, sBit); // sendPin Register low + DIRECT_MODE_INPUT(r1Reg, r1Bit); // r1eceivePin to input (pullups ar1e off) + DIRECT_MODE_OUTPUT(r1Reg, r1Bit); // r1eceivePin to OUTPUT + DIRECT_WRITE_LOW(r1Reg, r1Bit); // pin is now LOW AND OUTPUT + delayMicroseconds(10); + DIRECT_MODE_INPUT(r1Reg, r1Bit); // r1eceivePin to input (pullups ar1e off) + + DIRECT_MODE_INPUT(r2Reg, r2Bit); // r2eceivePin to input (pullups ar2e off) + DIRECT_MODE_OUTPUT(r2Reg, r2Bit); // r2eceivePin to OUTPUT + DIRECT_WRITE_LOW(r2Reg, r2Bit); // pin is now LOW AND OUTPUT + delayMicroseconds(10); + DIRECT_MODE_INPUT(r2Reg, r2Bit); // r2eceivePin to input (pullups ar2e off) + DIRECT_WRITE_HIGH(sReg, sBit); // sendPin High + + while ((total1 < CS_Timeout_Millis)) { // while total is positive value + pin1State = DIRECT_READ(r1Reg, r1Bit); + pin2State = DIRECT_READ(r2Reg, r2Bit); + + total1 += !pin1State; + total2 += !pin2State; + + if(pin1State && pin2State) break; + } + //Serial.print("SenseOneCycle(1): "); + //Serial.println(total); + + if (total1 > CS_Timeout_Millis) { + return -2; // total variable over timeout + } + // 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 + + while ( (total1 < CS_Timeout_Millis) ) { // while receive pin is HIGH AND total is less than timeout + pin1State = DIRECT_READ(r1Reg, r1Bit); + pin2State = DIRECT_READ(r2Reg, r2Bit); + + total1 += pin1State; + total2 += pin2State; + + if(!(pin1State || pin2State)) break; + } + //Serial.print("SenseOneCycle(2): "); + //Serial.println(total); + if (total1 >= CS_Timeout_Millis) { + return -2; // total variable over timeout + } else { + return 1; + } +} diff --git a/CapacitiveSensor.h b/CapacitiveSensor.h new file mode 100644 index 0000000..8112244 --- /dev/null +++ b/CapacitiveSensor.h @@ -0,0 +1,232 @@ +/* + CapacitiveSense.h v.04 - 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) 2008 Paul Bagder All rights reserved. + Version 05 by Paul Stoffregen - Support non-AVR board: Teensy 3.x, Arduino Due + Version 04 by Paul Stoffregen - Arduino 1.0 compatibility, issue 146 fix + vim: set ts=4: +*/ + +// ensure this library description is only included once +#ifndef CapacitiveSensor_h +#define CapacitiveSensor_h + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +// Direct I/O through registers and bitmask (from OneWire library) + +#if defined(__AVR__) +#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint8_t +#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask), (*((base)+2)) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask)) + +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) +#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) +#define PIN_TO_BITMASK(pin) (1) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (*((base)+512)) +#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0) +#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1) +#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1) +#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1) + +#elif defined(__MKL26Z64__) +#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask)) + +#elif defined(__SAM3X8E__) +#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask)) + +#elif defined(__PIC32MX__) +#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10 +#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08 +#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04 +#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24 +#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28 + +#elif defined(ARDUINO_ARCH_ESP8266) +#define PIN_TO_BASEREG(pin) (portOutputRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*(base+6)) & (mask)) ? 1 : 0) //GPIO_IN_ADDRESS +#define DIRECT_MODE_INPUT(base, mask) ((*(base+5)) = (mask)) //GPIO_ENABLE_W1TC_ADDRESS +#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+4)) = (mask)) //GPIO_ENABLE_W1TS_ADDRESS +#define DIRECT_WRITE_LOW(base, mask) ((*(base+2)) = (mask)) //GPIO_OUT_W1TC_ADDRESS +#define DIRECT_WRITE_HIGH(base, mask) ((*(base+1)) = (mask)) //GPIO_OUT_W1TS_ADDRESS + +#elif defined(__SAMD21G18A__) +// runs extremely slow/unreliable on Arduino Zero - help wanted.... +#define PIN_TO_BASEREG(pin) portModeRegister(digitalPinToPort(pin)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*((base)+8)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) = (mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+2)) = (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+5)) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+6)) = (mask)) + +#elif defined(RBL_NRF51822) +#define PIN_TO_BASEREG(pin) (0) +#define PIN_TO_BITMASK(pin) (pin) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, pin) nrf_gpio_pin_read(pin) +#define DIRECT_WRITE_LOW(base, pin) nrf_gpio_pin_clear(pin) +#define DIRECT_WRITE_HIGH(base, pin) nrf_gpio_pin_set(pin) +#define DIRECT_MODE_INPUT(base, pin) nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL) +#define DIRECT_MODE_OUTPUT(base, pin) nrf_gpio_cfg_output(pin) + +#elif defined(__arc__) + +#include "scss_registers.h" +#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 + +/* 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 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); +} + +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)); + } +} + +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)); + } +} + +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)); + } +} + +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)); + } +} + +#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 + +// some 3.3V chips with 5V tolerant pins need this workaround +// +#if defined(__MK20DX256__) +#define FIVE_VOLT_TOLERANCE_WORKAROUND +#endif + + +// library interface description +class CapacitiveSensor +{ + // 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 + 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; + + 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/OpeNITHM.ino b/OpeNITHM.ino new file mode 100644 index 0000000..a98f726 --- /dev/null +++ b/OpeNITHM.ino @@ -0,0 +1,161 @@ +#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32u4__) +#include "USBOutput.h" +#define USB +#endif + +#ifdef USB +#include "USBOutput.h" +#else +#include "SerialOutput.h" +#endif +#include "AirSensor.h" +#include "Touchboard.h" +#include +#include + +CRGB leds[16]; + +float lightIntensity[16]; +bool keyStates[16]; +bool activated = true; +float sensorPosition = -1; + +Touchboard *touchboard; +AirSensor *sensor; +Output *output; + +char *command; + +void onKeyPress(int key, bool wasHeld) +{ + lightIntensity[key] = 1.0f; + output->sendKeyEvent(key, true, wasHeld); + keyStates[key] = true; +} + +void parseCommand() +{ + char input1 = Serial.read(); + char input2 = Serial.read(); + switch (input1) + { + case 't': + switch (input2) + { + case 't': + touchboard->setThreshold(Serial.parseInt()); + break; + case 'd': + touchboard->setDeadzone(Serial.parseInt()); + break; + case 'a': + touchboard->setAlpha(Serial.parseFloat()); + break; + case 'c': + touchboard->calibrateKeys(); + } + break; + case 'i': + switch (input2) + { + case 'd': + sensor->setDeadzone(Serial.parseInt()); + break; + case 'a': + sensor->setAlpha(Serial.parseFloat()); + break; + case 'c': + sensor->recalibrate(); + } + break; + case 'p': + activated = false; + break; + case 'r': + activated = true; + break; + } + + 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(";"); +} + +void setup() { + Serial.begin(112500); + command = malloc(32); + + FastLED.addLeds(leds, 16); + + for (CRGB& led : leds) + { + led = 0x0000FF; + FastLED.show(); + } + + touchboard = new Touchboard(onKeyPress); + + for (CRGB& led : leds) + { + led = 0xFF0000; + FastLED.show(); + } + + sensor = new AirSensor(500, 50); + #ifdef USB + output = new USBOutput(); + #else + output = new SerialOutput(); + #endif +} + +void loop() { + if (!activated) return; + + // 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 (touchboard->update(i)) + { + leds[i].setRGB(128 + 127 * lightIntensity[i], 0, 128 + 127 * lightIntensity[i]); + } + else + { + leds[i].setRGB(128, 128, 0); + if (keyStates[i]) + { + output->sendKeyEvent(i, false, false); + keyStates[i] = false; + } + } + } + + const float newPosition = sensor->getHandPosition(); + if (newPosition != sensorPosition) + { + output->sendSensorEvent(newPosition); + sensorPosition = newPosition; + } + + + // If the air sensor is calibrated, begin updating lights. The air sensor will automatically calibrate as it is being polled. + if (sensor->isCalibrated()) + FastLED.show(); + + if (Serial.available()) + { + parseCommand(); + } +} + diff --git a/Output.h b/Output.h new file mode 100644 index 0000000..76cd6a5 --- /dev/null +++ b/Output.h @@ -0,0 +1,20 @@ +// Output.h + +#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); +}; + +#endif + diff --git a/PinConfig.h b/PinConfig.h new file mode 100644 index 0000000..004580b --- /dev/null +++ b/PinConfig.h @@ -0,0 +1,23 @@ +#pragma once + +// Multiplexer pin settings +#define MUX_A 9 +#define MUX_B 8 +#define MUX_C 7 + +// Sensor pin settings +#define IR_A 5 +#define IR_B 4 +#define IR_C 3 + +#define SENSOR_IN A0 + +// Capsense pin settings +#define KEYBOARDPIN_1 10 +#define KEYBOARDPIN_2 12 +#define KEYBOARDPIN_COM 11 + +// Lighting pin settings +#define LED_TYPE WS2812B +#define LED_ORDER GRB +#define RGBPIN 2 diff --git a/SerialOutput.cpp b/SerialOutput.cpp new file mode 100644 index 0000000..111d398 --- /dev/null +++ b/SerialOutput.cpp @@ -0,0 +1,25 @@ +// +// +// + +#include "SerialOutput.h" + +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; + + Serial.write(builtPacket.data); +} + +void SerialOutput::sendSensorEvent(float position) +{ + builtPacket.data = 0; + builtPacket.sensorEvent.isKeyboard = false; + builtPacket.sensorEvent.position = position * 127; + + Serial.write(builtPacket.data); +} diff --git a/SerialOutput.h b/SerialOutput.h new file mode 100644 index 0000000..cdac7b7 --- /dev/null +++ b/SerialOutput.h @@ -0,0 +1,42 @@ +// SerialOutput.h + +#ifndef _SERIALOUTPUT_h +#define _SERIALOUTPUT_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "arduino.h" +#else + #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; +}; + + +class SerialOutput : public Output +{ +private: + Packet builtPacket = Packet(); +public: + void sendKeyEvent(int key, bool pressed, bool doublePressed) override; + void sendSensorEvent(float position) override; +}; + +#endif + diff --git a/Touchboard.cpp b/Touchboard.cpp new file mode 100644 index 0000000..2d49217 --- /dev/null +++ b/Touchboard.cpp @@ -0,0 +1,117 @@ +#include "Touchboard.h" + +void Touchboard::scan() +{ + 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)); + + unsigned int* values = sensor.sense(3); + keys[i] = values[0]; + keys[i + 8] = values[1]; + } +} + +void Touchboard::calibrateKeys() +{ + 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 < 100; i++) { + scan(); + for (int j = 0; j < 16; j++) { + if (keys[j] > neutral_values[j]) neutral_values[j] = keys[j]; + } + } + 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]; +} + +void Touchboard::setThreshold(uint16_t threshold) +{ + this->threshold = threshold; + EEPROM.put(0, threshold); +} + +void Touchboard::setDeadzone(uint16_t deadzone) +{ + this->deadzone = deadzone; + EEPROM.put(4, deadzone); +} + +void Touchboard::setAlpha(float alpha) +{ + this->alpha = alpha; + EEPROM.put(8, alpha); +} + +int Touchboard::getThreshold() +{ + return threshold; +} + +int Touchboard::getDeadzone() +{ + return deadzone; +} + +float Touchboard::getAlpha() +{ + return alpha; +} + +Touchboard::Touchboard(void(*keyPressCallback)(int, bool)) : + sensor(CapacitiveSensor(KEYBOARDPIN_COM, KEYBOARDPIN_1, KEYBOARDPIN_2)), + onKeyPress(keyPressCallback) +{ + EEPROM.get(0, threshold); + EEPROM.get(4, deadzone); + EEPROM.get(8, alpha); + + pinMode(MUX_A, OUTPUT); + pinMode(MUX_B, OUTPUT); + pinMode(MUX_C, OUTPUT); + + calibrateKeys(); +} + diff --git a/Touchboard.h b/Touchboard.h new file mode 100644 index 0000000..f2a5fff --- /dev/null +++ b/Touchboard.h @@ -0,0 +1,49 @@ +// Touchboard.h + +#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 +#define EMA_TOUCHDETECT_ALPHA 0.1f + +class Touchboard +{ +private: + CapacitiveSensor sensor; + + + uint16_t threshold; + uint16_t deadzone; + float alpha; + + float em_averages[16]; + unsigned int keys[16]; + int neutral_values[16]; + void (*onKeyPress)(int, bool); + + +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(); +}; + +#endif + diff --git a/USBOutput.cpp b/USBOutput.cpp new file mode 100644 index 0000000..3e45730 --- /dev/null +++ b/USBOutput.cpp @@ -0,0 +1,13 @@ +// +// +// + +#include "USBOutput.h" + +void USBOutput::sendKeyEvent(int key, bool pressed, bool doublePressed) +{ +} + +void USBOutput::sendSensorEvent(float position) +{ +} diff --git a/USBOutput.h b/USBOutput.h new file mode 100644 index 0000000..0bf76ed --- /dev/null +++ b/USBOutput.h @@ -0,0 +1,21 @@ +// 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 +{ +public: + void sendKeyEvent(int key, bool pressed, bool doublePressed) override; + void sendSensorEvent(float position) override; +}; + +#endif +