mirror of
https://github.com/skogaby/OpeNITHM.git
synced 2026-09-22 22:58:16 +03:00
Added comments. Updated air sensor code to work with mickabrig7's design.
This commit is contained in:
+96
-77
@@ -1,74 +1,75 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
#include "AirSensor.h"
|
||||
|
||||
|
||||
// Sets the output pins to switch the charlieplexed array of LEDs.
|
||||
void AirSensor::changeLight(int light)
|
||||
{
|
||||
switch (light)
|
||||
{
|
||||
case 0:
|
||||
pinMode(IR_A, OUTPUT);
|
||||
pinMode(IR_B, OUTPUT);
|
||||
pinMode(IR_C, INPUT);
|
||||
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, 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, 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, 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, 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, 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;
|
||||
digitalWrite(IR_A, HIGH);
|
||||
digitalWrite(IR_B, LOW);
|
||||
digitalWrite(IR_C, LOW);
|
||||
break;
|
||||
default:
|
||||
turnOffLight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets all output pins to high-impedance to turn off all LEDs
|
||||
void AirSensor::turnOffLight()
|
||||
{
|
||||
pinMode(IR_A, INPUT);
|
||||
@@ -78,35 +79,43 @@ void AirSensor::turnOffLight()
|
||||
|
||||
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();
|
||||
}
|
||||
// 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);
|
||||
#else
|
||||
return analogRead(ir_sensor_pins[sensor]);
|
||||
#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)
|
||||
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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
if (!allCalibrated)
|
||||
{
|
||||
for(int i = 0; i < 6; i++)
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
if(!calibrated[i])
|
||||
if (!calibrated[i])
|
||||
return false;
|
||||
}
|
||||
allCalibrated = true;
|
||||
@@ -116,23 +125,32 @@ bool AirSensor::isCalibrated()
|
||||
|
||||
bool AirSensor::getSensorState(int sensor)
|
||||
{
|
||||
// Flash the LED and read the IR sensor
|
||||
int value = getValue(sensor, true);
|
||||
turnOffLight();
|
||||
|
||||
if(allCalibrated || calibrated[sensor]){
|
||||
sensorValues[sensor] = (float) value * EMA_AIRSENSOR_ALPHA + sensorValues[sensor] * (1 - EMA_AIRSENSOR_ALPHA);
|
||||
// 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(skippedSamples[sensor] > samplesToSkip)
|
||||
// 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)
|
||||
{
|
||||
if(value < thresholds[sensor]) thresholds[sensor] = value;
|
||||
if(++calibrationSamples[sensor] > samplesToAcquire)
|
||||
// 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] -= AIR_SENSOR_THRESHOLD_SUBTRACT;
|
||||
thresholds[sensor] -= deadzone;
|
||||
};
|
||||
}
|
||||
else
|
||||
@@ -143,13 +161,14 @@ bool AirSensor::getSensorState(int sensor)
|
||||
}
|
||||
}
|
||||
|
||||
// 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++)
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
if(getSensorState(i))
|
||||
if (getSensorState(i))
|
||||
{
|
||||
sensorsTriggered++;
|
||||
total += i + 1;
|
||||
@@ -182,7 +201,7 @@ float AirSensor::getAlpha()
|
||||
|
||||
void AirSensor::recalibrate()
|
||||
{
|
||||
for(int i = 0; i < 6; i++)
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
thresholds[i] = 0;
|
||||
calibrationSamples[i] = 0;
|
||||
|
||||
+3
-2
@@ -12,8 +12,9 @@
|
||||
#include "PinConfig.h"
|
||||
#include <EEPROM.h>
|
||||
|
||||
#define AIR_SENSOR_THRESHOLD_SUBTRACT 5
|
||||
#define EMA_AIRSENSOR_ALPHA 0.4f
|
||||
#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
|
||||
{
|
||||
|
||||
+66
-64
@@ -5,6 +5,7 @@
|
||||
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
|
||||
@@ -43,13 +44,11 @@ CapacitiveSensor::CapacitiveSensor(uint8_t sendPin, uint8_t receivePin1, uint8_t
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
@@ -57,20 +56,21 @@ CapacitiveSensor::CapacitiveSensor(uint8_t sendPin, uint8_t receivePin1, uint8_t
|
||||
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
|
||||
pinMode(sendPin, OUTPUT); // sendpin to OUTPUT
|
||||
pinMode(receivePin1, INPUT); // receivePins to INPUT
|
||||
pinMode(receivePin2, INPUT);
|
||||
|
||||
// Get pin bitmask and registers
|
||||
|
||||
sBit = PIN_TO_BITMASK(sendPin);
|
||||
sReg = PIN_TO_BASEREG(sendPin);
|
||||
|
||||
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 //////////////////////////////////////////////////////////////
|
||||
@@ -81,17 +81,18 @@ unsigned int* CapacitiveSensor::sense(uint8_t samples)
|
||||
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; // variable over timeout
|
||||
if (!SenseOneCycle()) return nullptr; // Poll capacitive sensors repeatedly
|
||||
}
|
||||
interrupts();
|
||||
|
||||
retVal[0] = total1;
|
||||
retVal[1] = total2;
|
||||
|
||||
// Return the pair of values for the 2 sensors that were polled
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
// Private Methods /////////////////////////////////////////////////////////////
|
||||
@@ -99,60 +100,61 @@ unsigned int* CapacitiveSensor::sense(uint8_t samples)
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
total2 += !pin2State;
|
||||
|
||||
// Break once both pins are high
|
||||
if(pin1State && pin2State) break;
|
||||
}
|
||||
|
||||
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;
|
||||
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
|
||||
|
||||
// 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;
|
||||
|
||||
if(!(pin1State || pin2State)) break;
|
||||
}
|
||||
|
||||
if (total1 >= CS_Timeout_Millis) {
|
||||
return -2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
+20
-5
@@ -32,6 +32,8 @@ void onKeyPress(int key, bool 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();
|
||||
@@ -94,21 +96,27 @@ void setup() {
|
||||
|
||||
FastLED.addLeds<LED_TYPE, RGBPIN, LED_ORDER>(leds, 16);
|
||||
|
||||
// Set LEDs blue
|
||||
for (CRGB& led : leds)
|
||||
{
|
||||
led = 0x0000FF;
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
// Initialize and calibrate touch sensors
|
||||
touchboard = new Touchboard(onKeyPress);
|
||||
|
||||
// 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 relevant output method / USB or serial
|
||||
#ifdef USB
|
||||
output = new USBOutput();
|
||||
#else
|
||||
@@ -117,6 +125,13 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process config commands
|
||||
if (Serial.available())
|
||||
{
|
||||
parseCommand();
|
||||
}
|
||||
|
||||
// If currently paused through a config command, do not execute main loop
|
||||
if (!activated) return;
|
||||
|
||||
// Scan touch keyboard and update lights
|
||||
@@ -125,12 +140,15 @@ void loop() {
|
||||
{
|
||||
if (lightIntensity[i] > 0.05f)
|
||||
lightIntensity[i] -= 0.05f;
|
||||
|
||||
// If the key is currently being held, set its color to purple
|
||||
if (touchboard->update(i))
|
||||
{
|
||||
leds[i].setRGB(128 + 127 * lightIntensity[i], 0, 128 + 127 * lightIntensity[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not, make it yellow and send the "key released" event if it was previously pressed
|
||||
leds[i].setRGB(128, 128, 0);
|
||||
if (keyStates[i])
|
||||
{
|
||||
@@ -140,6 +158,7 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Process air sensor hand position
|
||||
const float newPosition = sensor->getHandPosition();
|
||||
if (newPosition != sensorPosition)
|
||||
{
|
||||
@@ -148,13 +167,9 @@ void loop() {
|
||||
}
|
||||
|
||||
|
||||
// If the air sensor is calibrated, begin updating lights. The air sensor will automatically calibrate as it is being polled.
|
||||
// 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();
|
||||
|
||||
if (Serial.available())
|
||||
{
|
||||
parseCommand();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define IR_SENSOR_MULTIPLEXED
|
||||
#define CALIBRATION_SAMPLES 500
|
||||
|
||||
// Multiplexer pin settings
|
||||
#define MUX_A 9
|
||||
#define MUX_B 8
|
||||
@@ -10,7 +13,16 @@
|
||||
#define IR_B 4
|
||||
#define IR_C 3
|
||||
|
||||
#define SENSOR_IN A0
|
||||
#ifdef IR_SENSOR_MULTIPLEXED
|
||||
#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
|
||||
#endif
|
||||
|
||||
// Capsense pin settings
|
||||
#define KEYBOARDPIN_1 10
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
#include "SerialOutput.h"
|
||||
|
||||
// Workaround for microcontrollers without USB functionality
|
||||
// use a serial line to talk to a UC with USB, which will then act as a USB peripheral
|
||||
// Only needed because I didn't receive my Atmega328p until late in the project
|
||||
|
||||
void SerialOutput::sendKeyEvent(int key, bool pressed, bool doublePressed)
|
||||
{
|
||||
builtPacket.data = 0;
|
||||
|
||||
+9
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
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));
|
||||
@@ -9,6 +10,8 @@ void Touchboard::scan()
|
||||
digitalWrite(MUX_C, bitRead(i, 2));
|
||||
|
||||
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];
|
||||
}
|
||||
@@ -16,6 +19,7 @@ void Touchboard::scan()
|
||||
|
||||
void Touchboard::calibrateKeys()
|
||||
{
|
||||
// Reset calibration data for all keys
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
em_averages[i] = 0;
|
||||
@@ -24,12 +28,15 @@ void Touchboard::calibrateKeys()
|
||||
keys[i] = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; 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];
|
||||
}
|
||||
@@ -49,8 +56,8 @@ bool Touchboard::update(int key)
|
||||
key_states[key] = false;
|
||||
}
|
||||
else
|
||||
//If we are outside of the deadzone:
|
||||
{
|
||||
//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.
|
||||
|
||||
Reference in New Issue
Block a user