
Force sensor you could build yourself. Plans are to scale it up and template the cutting of the layers for a commercially viable poly-touch interface of some kind.
But you can make this DIY version yourself, see how in our post. Code below.
#include <Adafruit_NeoPixel.h>
#include <MIDIUSB.h>
#include <Keyboard.h>
#include <Mouse.h>
#define channel 12 // really that means it's ch 13
#define space 30
int br = 255;
#define MIDI_CC MIDI_CC_GENERAL1
#define distfilter 15
#define tfilter 3
int distchange;
int distchange2;
int distchange3;
int dist;
int dist2;
int dist3;
int ln1; // previous note
int ln2;
int ln3;
int tcheck1;
int tcheck2;
int touch1;
int touch2;
int cc1;
int cc2 = 5;
int cc3 = 7;
int ct1 = 6;
int ct2 = 4;
bool sa;
bool sb;
bool b1;
bool b4 = true;
bool b5;
bool b6;
bool mmode;
bool smode;
bool tmode;
bool pup; //full octave pitch up;
int mx;
int my;
bool off1;
bool off2;
bool off3;
#define NUM_AI 4
#define ANALOGUE_PIN_ORDER A0, A6, A7, A8
#define MIDI_CC MIDI_CC_GENERAL1
#define FILTER_AMOUNT 12 // for sliders n knobs
#define ANALOGUE_INPUT_CHANGE_TIMEOUT 200000
byte analogueInputMapping[4] = {ANALOGUE_PIN_ORDER};
byte analogueInputs[4];
byte tempAnalogueInput;
byte digitalOffset = 0;
byte analogueDiff = 0;
boolean analogueInputChanging[4];
unsigned long analogueInputTimer[4];
Adafruit_NeoPixel strip = Adafruit_NeoPixel(29, 5, NEO_GRB + NEO_KHZ800);
void setup() {
Keyboard.begin();
Mouse.begin();
strip.begin();
strip.setBrightness(br);
strip.show();
pinMode(A1, INPUT); // sensor
pinMode(A2, INPUT); // sensor
pinMode(A3, INPUT); // sensor
pinMode(A0, INPUT); // slider
pinMode(A6, INPUT); // slider
pinMode(A7, INPUT); // slider
pinMode(A8, INPUT); // slider
pinMode(A9, INPUT_PULLUP); // knob
pinMode(A10, INPUT_PULLUP); // knob
pinMode(14, INPUT); // switch
pinMode(15, INPUT); // switch
pinMode(5, OUTPUT); // LEDs
pinMode(2, INPUT_PULLUP); // button
pinMode(3, INPUT_PULLUP); // button
pinMode(7, INPUT_PULLUP); // button
pinMode(16, INPUT_PULLUP); // button
pinMode(0, INPUT_PULLUP); // button
pinMode(1, INPUT_PULLUP); // button
Mouse.release();
Keyboard.releaseAll();
}
void controlChange(byte thechannel, byte control, byte value) {
midiEventPacket_t event = {0x0B, 0xB0 | thechannel, control, value};
MidiUSB.sendMIDI(event);
MidiUSB.flush();
}
void pitchBendChange(byte thechannel, int value) {
byte lowValue = value & 0x7F;
byte highValue = value >> 7;
midiEventPacket_t event = {0x0E, 0xE0 | thechannel, lowValue, highValue};
MidiUSB.sendMIDI(event);
MidiUSB.flush();
}
void noteOn(byte thechannel, byte pitch, byte velocity) {
midiEventPacket_t noteOn = {0x09, 0x90 | thechannel, pitch, velocity};
MidiUSB.sendMIDI(noteOn);
}
void noteOff(byte thechannel, byte pitch, byte velocity) {
midiEventPacket_t noteOff = {0x08, 0x80 | thechannel, pitch, velocity};
MidiUSB.sendMIDI(noteOff);
}
void loop() {
sensor();
touch();
sliders();
swcheck();
}
void swcheck() {
if (digitalRead(14) != sa || digitalRead(15) != sb) {
sa = digitalRead(14);
sb = digitalRead(15);
stripn();
if (mmode) {
if (sa) {
Mouse.press();
} else {
Mouse.release();
}
} else {
controlChange(channel, space + cc1, 0);
delay(50);
controlChange(channel, space + cc2, 0);
delay(50);
controlChange(channel, space + cc3, 0);
delay(50);
if (!sa && !sb) {
cc3 = 7; // fuzz drum
cc2 = 5; // fuzz bass
cc1 = 0; // filter wah
ct2 = 4;
ct1 = 6;
}
if (sa && !sb) {
cc3 = 14; // crumble
cc2 = 4; // glitch bass
cc1 = 6; // glitch drum
ct2 = 5;
ct1 = 0;
}
if (!sa && sb) {
cc3 = 13; // radiate
cc2 = 11; // flange
cc1 = 12; // chorus
ct2 = 62;
ct1 = 61;
}
if (sa && sb) {
cc3 = 29; // new
cc2 = 28; // new
cc1 = 27; // new
ct2 = 64;
ct1 = 63;
}
}
}
if (!digitalRead(0)) { //back button L
if (mmode) {
Mouse.click(MOUSE_RIGHT);
} else if (tmode) {
if (b1) {
b1 = false;
strip.setPixelColor(1, strip.Color(0, 3, 0));
strip.setPixelColor(27, strip.Color(0, 3, 0));
strip.show();
controlChange(channel, space + 81, 0);
} else {
b1 = true;
strip.setPixelColor(1, strip.Color(0, 0, 3));
strip.setPixelColor(27, strip.Color(0, 0, 3));
strip.show();
controlChange(channel, space + 81, 127);
}
} else {
Keyboard.press(0x87);
Keyboard.press(0x86);
Keyboard.press(0x70);
Keyboard.release(0x70);
Keyboard.release(0x86);
Keyboard.release(0x87);
}
delay(300);
while (!digitalRead(0)) {}
}
if (!digitalRead(1)) { //back button R
if (tmode) {
tmode = false;
for (int panic = 0; panic < 24; panic++) {
for (int pchan = 0; pchan < 2; pchan++) {
noteOff(pchan, panic + 68, 64);
}
}
MidiUSB.flush();
}
if (mmode) {
if (smode) {
smode = false;
} else {
smode = true;
}
stripn();
delay(300);
} else {
b4 = false;
mmode = true;
strip.setPixelColor(0, strip.Color(0, 0, 0));
strip.setPixelColor(28, strip.Color(0, 0, 0));
strip.setPixelColor(1, strip.Color(0, 0, 0));
strip.setPixelColor(27, strip.Color(0, 0, 0));
stripn();
controlChange(channel, space + cc1, 0);
delay(50);
controlChange(channel, space + cc2, 0);
delay(50);
controlChange(channel, space + cc3, 0);
delay(200);
}
while (!digitalRead(1)) {}
}
if (!digitalRead(7)) { //next
if (!tmode) {
b5 = false;
b6 = false;
controlChange(channel, space + 74, 0);
controlChange(channel, space + 34, 0);
controlChange(channel, space + 75, 0);
controlChange(channel, space + 35, 0);
}
if (tmode && !pup) {
pup = true;
stripn();
delay(300);
} else {
tmode = true;
mmode = false;
b4 = false;
pup = false;
strip.setPixelColor(9, strip.Color(0, 0, 0));
strip.setPixelColor(19, strip.Color(0, 0, 0));
stripn();
controlChange(channel, space + cc1, 0);
delay(50);
controlChange(channel, space + cc2, 0);
delay(50);
controlChange(channel, space + cc3, 0);
delay(200);
}
while (!digitalRead(7)) {}
}
if (!digitalRead(3)) { // set
if (tmode) {
tmode = false;
b4 = true;
b5 = false;
b6 = false;
for (int panic = 0; panic < 24; panic++) {
for (int pchan = 0; pchan < 2; pchan++) {
noteOff(pchan, panic + 68, 64);
}
}
controlChange(channel, space + 74, 0);
controlChange(channel, space + 34, 0);
controlChange(channel, space + 75, 0);
controlChange(channel, space + 35, 0);
MidiUSB.flush();
}
if (!b4 && !mmode) {
b4 = true;
stripn();
controlChange(channel, space + cc1, 0);
delay(50);
controlChange(channel, space + cc2, 0);
delay(50);
controlChange(channel, space + cc3, 0);
delay(200);
distchange = 0;
distchange2 = 0;
distchange3 = 0;
} else {
b4 = false;
if (mmode) {
mmode = false;
Mouse.release();
Keyboard.releaseAll();
}
strip.setPixelColor(0, strip.Color(0, 0, 0));
strip.setPixelColor(28, strip.Color(0, 0, 0));
strip.setPixelColor(1, strip.Color(0, 0, 0));
strip.setPixelColor(27, strip.Color(0, 0, 0));
stripn();
delay(300);
}
while (!digitalRead(3)) {}
}
if (!digitalRead(16)) { //plus
if (mmode) {
Keyboard.press(0x87);
Keyboard.press(0x86);
Keyboard.press(0x3D);
Keyboard.release(0x3D);
delay(10);
Keyboard.press(0x3D);
Keyboard.release(0x3D);
Keyboard.release(0x86);
Keyboard.release(0x87);
delay(100);
} else {
if (!b5) {
if (b6) {
b6 = false;
if (tmode) {
controlChange(channel, space + 75, 0);
} else {
controlChange(channel, space + 35, 0);
}
for (int bw6 = 1; bw6 < 9; bw6++) {
strip.setPixelColor(bw6 + 19, strip.Color(0, 3, 0));
strip.setPixelColor(bw6, strip.Color(0, 3, 0));
}
} else {
b5 = true;
if (tmode) {
controlChange(channel, space + 74, 127);
} else {
controlChange(channel, space + 34, 127);
}
for (int bw5 = 1; bw5 < 9; bw5++) {
strip.setPixelColor(bw5 + 19, strip.Color(3, 3, 3));
strip.setPixelColor(bw5, strip.Color(0, 3, 0));
}
}
strip.show();
delay(400);
} else {
b5 = false;
if (tmode) {
controlChange(channel, space + 74, 0);
} else {
controlChange(channel, space + 34, 0);
}
for (int bv5 = 1; bv5 < 9; bv5++) {
strip.setPixelColor(bv5 + 19, strip.Color(0, 3, 0));
strip.setPixelColor(bv5, strip.Color(0, 3, 0));
}
strip.show();
delay(400);
}
}
while (!digitalRead(16)) {}
}
if (!digitalRead(2)) { //minus
if (mmode) {
Keyboard.press(0x87);
Keyboard.press(0x86);
Keyboard.press(0x38);
Keyboard.release(0x38);
Keyboard.release(0x86);
Keyboard.release(0x87);
} else {
if (!b6) {
if (b5) {
b5 = false;
if (tmode) {
controlChange(channel, space + 74, 0);
} else {
controlChange(channel, space + 34, 0);
}
for (int bw6 = 1; bw6 < 9; bw6++) {
strip.setPixelColor(bw6 + 19, strip.Color(0, 3, 0));
strip.setPixelColor(bw6, strip.Color(0, 3, 0));
}
} else {
b6 = true;
if (tmode) {
controlChange(channel, space + 75, 127);
} else {
controlChange(channel, space + 35, 127);
}
for (int bw6 = 1; bw6 < 9; bw6++) {
strip.setPixelColor(bw6 + 19, strip.Color(0, 3, 0));
strip.setPixelColor(bw6, strip.Color(3, 3, 3));
}
}
strip.show();
} else {
b6 = false;
if (tmode) {
controlChange(channel, space + 75, 0);
} else {
controlChange(channel, space + 35, 0);
}
for (int bv6 = 1; bv6 < 9; bv6++) {
strip.setPixelColor(bv6 + 19, strip.Color(0, 3, 0));
strip.setPixelColor(bv6, strip.Color(0, 3, 0));
}
strip.show();
}
}
delay(400);
while (!digitalRead(16)) {}
}
} // end switch check
void sensor() {
if (!b4) {
mx = 0;
my = 0;
int d1a = analogRead(A1);
int d2a = analogRead(A1);
int d3a = analogRead(A1);
int distcheck = (d1a + d2a + d3a) / 3;
if (distcheck < 150) {
distcheck = 0;
if (!off1) {
off1 = true;
noteOff(0, 70, 64);
MidiUSB.flush();
}
}
if ((distcheck < distchange - distfilter) || (distcheck > distchange + distfilter)) {
off1 = false;
if (distcheck > 1000) {
distcheck = 0;
}
dist = ((distcheck - 135) / 4.5);
if (dist > 127) {
dist = 127;
}
if (dist < 0) {
dist = 0;
}
if (mmode) {
if ((dist > 20) && (dist < 64)) {
mx = -((dist - 20) / 4);
}
if (dist > 63) {
mx = -((127 - dist) / 4);
}
if (smode) {
if ((dist > 20) && (dist < 108)) {
Keyboard.press(0x81);
Mouse.move(0, 0, -1 - sb);
Keyboard.release(0x81);
delay(100);
}
} else {
Mouse.move(mx * (sb + 1), 0, 0);
}
} else if (tmode) {
delay(48);
noteOff(0, ln1, 64);
MidiUSB.flush();
ln1=dist / 8 + 70;
noteOn(0, ln1, 64);
MidiUSB.flush();
} else {
controlChange(channel, space + cc1, dist);
}
distchange = distcheck;
stripn();
}
int d1b = analogRead(A2);
int d2b = analogRead(A2);
int d3b = analogRead(A2);
int distcheck2 = (d1b + d2b + d3b) / 3;
if (distcheck2 < 150) {
distcheck2 = 0;
if (!off2) {
off2 = true;
noteOff(1, 70, 64);
MidiUSB.flush();
}
}
if ((distcheck2 < distchange2 - distfilter) || (distcheck2 > distchange2 + distfilter)) {
off2 = false;
if (distcheck2 > 1000) {
distcheck2 = 0;
}
dist2 = ((distcheck2 - 135) / 4.5);
if (dist2 > 127) {
dist2 = 127;
}
if (dist2 < 0) {
dist2 = 0;
}
if (mmode) {
if ((dist2 > 20) && (dist2 < 53)) {
my = -((53 - dist2) / 4);
}
if ((dist2 > 73) && (dist2 < 108)) {
my = ((dist2 - 72) / 4);
}
if (smode) {
if ((dist2 > 20) && (dist2 < 53)) {
Mouse.move(0, 0, -1 - sb);
delay(100);
}
if ((dist2 > 73) && (dist2 < 108)) {
Mouse.move(0, 0, 1 + sb);
delay(100);
}
} else {
Mouse.move(0, my * ((sb * 2) + 1), 0);
}
} else if (tmode) {
if (sa) {
controlChange(channel, space + 76, dist2);
} else {
delay(48);
noteOff(1, ln2, 64);
MidiUSB.flush();
ln2=dist2 / 8 + 70;
noteOn(1, ln2, 64);
MidiUSB.flush();
}
} else {
controlChange(channel, space + cc2, dist2);
}
distchange2 = distcheck2;
stripn();
}
int d1c = analogRead(A3);
int d2c = analogRead(A3);
int d3c = analogRead(A3);
int distcheck3 = (d1c + d2c + d3c) / 3;
if (distcheck3 < 150) {
distcheck3 = 0;
if (!off3) {
off3 = true;
noteOff(2, 70, 64);
MidiUSB.flush();
}
}
if ((distcheck3 < distchange3 - distfilter) || (distcheck3 > distchange3 + distfilter)) {
off3 = false;
if (distcheck3 > 1000) {
distcheck3 = 0;
}
dist3 = ((distcheck3 - 135) / 4.5);
if (dist3 > 127) {
dist3 = 127;
}
if (dist3 < 0) {
dist3 = 0;
}
if (mmode) {
if ((dist3 > 20) && (dist3 < 64)) {
mx = ((dist3 - 20) / 4);
}
if (dist3 > 63) {
mx = ((127 - dist3) / 4);
}
if (smode) {
if ((dist3 > 20) && (dist3 < 108)) {
Keyboard.press(0x81);
Mouse.move(0, 0, 1 + sb);
Keyboard.release(0x81);
delay(100);
}
} else {
Mouse.move(mx * (sb + 1), 0, 0);
}
} else if (tmode) {
if (sb) {
controlChange(channel, space + 70, dist3);
} else {
delay(48);
noteOff(2, ln3, 64);
MidiUSB.flush();
ln3=dist3 / 8 + 70;
noteOn(2, ln3, 64);
MidiUSB.flush();
}
} else {
controlChange(channel, space + cc3, dist3);
}
distchange3 = distcheck3;
stripn();
}
}
}
void sliders() { // now the sliders
byte i = 0;
for (i = 0; i < 4; i++)
{
tempAnalogueInput = analogRead(analogueInputMapping[i]) / 4;
analogueDiff = abs(tempAnalogueInput - analogueInputs[i]);
if ((analogueDiff > 0 && analogueInputChanging[i] == true) || analogueDiff >= FILTER_AMOUNT)
{
if (analogueInputChanging[i] == false || analogueDiff >= FILTER_AMOUNT)
{
analogueInputTimer[i] = micros();
analogueInputChanging[i] = true;
}
else if (micros() - analogueInputTimer[i] > ANALOGUE_INPUT_CHANGE_TIMEOUT)
{
analogueInputChanging[i] = false;
}
if (analogueInputChanging[i] == true)
{
analogueInputs[i] = tempAnalogueInput;
if (i == 0) {
controlChange(channel, space + 23, tempAnalogueInput / 2); // C vol
}
if (i == 1) {
if (tmode) {
controlChange(channel, space + 70, tempAnalogueInput / 2); // theremin vol cc
} else {
controlChange(channel, space + 20, tempAnalogueInput / 2); // noise select
}
}
if (i == 2) {
if (tmode) {
controlChange(channel, space + 71, tempAnalogueInput / 2); // theremin select
} else {
controlChange(channel, space + 22, tempAnalogueInput / 2); // drums select
}
}
if (i == 3) {
if (tmode) {
controlChange(channel, space + 72, tempAnalogueInput / 2); // theremin verb
} else {
controlChange(channel, space + 24, tempAnalogueInput / 2); // bass select
}
}
for (int p = 0; p < 29; p++) {
if ((p > round(tempAnalogueInput / 9 - 1)) && (p < round(tempAnalogueInput / 9 + 1))) {
if (b4) {
strip.setPixelColor(p, strip.Color(6, 3, 0));
} else {
strip.setPixelColor(p, strip.Color(64, 0, 0));
}
} else if (p == 9 - (tmode * 9) || p == 19 + (tmode * 9)) {
if (!mmode) {
strip.setPixelColor(p, strip.Color(3, 2 * digitalRead(14), 2 * digitalRead(15)));
}
} else if ((b4) && (p == 4 || p == 14 || p == 24)) {
strip.setPixelColor(p, strip.Color(0, 3, 3));
} else {
strip.setPixelColor(p, strip.Color(0, 0, 0));
}
}
if (tmode && pup) {
strip.setPixelColor(1, strip.Color(3, 0, 0));
strip.setPixelColor(27, strip.Color(3, 0, 0));
}
if (mmode) {
if (smode) {
strip.setPixelColor(9 + (mx / 2) + (my / 2), strip.Color(0, 0, 3));
strip.setPixelColor(19 + (mx / 2) - (my / 2), strip.Color(0, 0, 3));
} else {
strip.setPixelColor(9 + (mx / 2) + (my / 2), strip.Color(0, 3, 0));
strip.setPixelColor(19 + (mx / 2) - (my / 2), strip.Color(0, 3, 0));
}
}
strip.show();
}
}
}
}
void touch() {
int tt1 = analogRead(A9);
int tt2 = analogRead(A9);
int tt3 = analogRead(A9);
int t1 = (1023 - ((tt1 + tt2 + tt3) / 3)) - 566;
if (t1 < 0) {
t1 = 0;
}
touch1 = (t1 / 2);
if ((touch1 < tcheck1 - tfilter) || (touch1 > tcheck1 + tfilter)) {
if (touch1 > 127) {
touch1 = 127;
}
if (mmode) {
my = 0;
if ((touch1 < 53) && (touch1 > 20)) {
my = -((53 - touch1) / 4);
}
if (touch1 > 73) {
my = ((touch1 - 72) / 4);
}
if (smode) {
if ((touch1 < 53) && (touch1 > 20)) {
Mouse.move(0, 0, -1 - (sb * 4));
delay(100);
}
if (touch1 > 73) {
Mouse.move(0, 0, 1 + (sb * 2));
delay(100);
}
} else {
Mouse.move(0, my * ((sb * 2) + 1), 0);
}
} else if (tmode) {
controlChange(channel, space + 70, touch1);
} else {
controlChange(channel, space + ct1, touch1);
}
tcheck1 = touch1;
stripn();
}
int tu1 = analogRead(A10);
int tu2 = analogRead(A10);
int tu3 = analogRead(A10);
int t2 = (1023 - ((tu1 + tu2 + tu3) / 3)) - 566;
if (t2 < 0) {
t2 = 0;
}
touch2 = (t2 / 2);
if ((touch2 < tcheck2 - tfilter) || (touch2 > tcheck2 + tfilter)) {
if (touch2 > 127) {
touch2 = 127;
}
if (mmode) {
mx = 0;
if ((touch2 < 53) && (touch2 > 20)) {
mx = -((53 - touch2) / 5);
}
if (touch2 > 73) {
mx = ((touch2 - 72) / 5);
}
if (smode) {
if ((touch2 < 53) && (touch2 > 20)) {
Keyboard.press(0x81);
Mouse.move(0, 0, -1 - (sb * 4));
Keyboard.release(0x81);
delay(100);
}
if (touch2 > 73) {
Keyboard.press(0x81);
Mouse.move(0, 0, 1 + (sb * 2));
Keyboard.release(0x81);
delay(100);
}
} else {
Mouse.move(mx * ((sb * 3) + 1), 0, 0);
}
} else if (tmode) {
if (pup) {
pitchBendChange(channel, map(touch2, 0, 127, 8192, 16383));
} else {
pitchBendChange(channel, map(touch2, 0, 127, 8192, 9524));
}
} else {
controlChange(channel, space + ct2, touch2);
}
tcheck2 = touch2;
stripn();
}
}
void stripn() {
strip.clear();
if (mmode) {
if (smode) {
strip.setPixelColor(9 + (mx / 2) + (my / 2), strip.Color(0, 0, 3));
strip.setPixelColor(19 + (mx / 2) - (my / 2), strip.Color(0, 0, 3));
} else {
strip.setPixelColor(9 + (mx / 2) + (my / 2), strip.Color(0, 3, 0));
strip.setPixelColor(19 + (mx / 2) - (my / 2), strip.Color(0, 3, 0));
}
} else {
int d1;
int d2;
int d3;
for (int p = 0; p < 9; p++) {
if (p < 5) {
d1 = (distchange / 3) - (200 - 50 * p);
d2 = (distchange2 / 3) - (200 - 50 * p);
d3 = (distchange3 / 3) - (200 - 50 * p);
} else {
d1 = (distchange / 3) - (50 * (p - 4));
d2 = (distchange2 / 3) - (50 * (p - 4));
d3 = (distchange3 / 3) - (50 * (p - 4));
}
if (d1 > 255) d1 = 255;
if (d2 > 255) d2 = 255;
if (d3 > 255) d3 = 255;
if (d1 < 0) d1 = 0;
if (d2 < 0) d2 = 0;
if (d3 < 0) d3 = 0;
strip.setPixelColor(p, strip.Color(random(d1), random(d1), random(d1)));
strip.setPixelColor(p + 10, strip.Color(random(d2), random(d2), random(d2)));
strip.setPixelColor(p + 20, strip.Color(random(d3), random(d3), random(d3)));
}
strip.setPixelColor(9 - (tmode * 9), strip.Color(3, 2 * digitalRead(14), 2 * digitalRead(15)));
strip.setPixelColor(19 + (tmode * 9), strip.Color(3, 2 * digitalRead(14), 2 * digitalRead(15)));
if (tcheck1 != 0) {
strip.setPixelColor(29 - tcheck1 / 4, strip.Color(0, 64, 0));
}
if (tcheck2 != 0) {
strip.setPixelColor(29 - tcheck2 / 4, strip.Color(0, 64, 0));
}
}
if (b4) {
strip.setPixelColor(4, strip.Color(0, 3, 3));
strip.setPixelColor(14, strip.Color(0, 3, 3));
strip.setPixelColor(24, strip.Color(0, 3, 3));
}
if (tmode && pup) {
strip.setPixelColor(1, strip.Color(3, 0, 0));
strip.setPixelColor(27, strip.Color(3, 0, 0));
}
strip.show();
}
Code for Pro Micro or Arduino Leonardo. This device also has the sharp distance sensors and several other pots and buttons.
more like this at woz.lol