|
Post by MikMo on Mar 4, 2023 8:45:56 GMT
For a while i have been toying with the idea of making a "tactile box" with several controls built in. I have a leftover 2x12 AE case that i made custom aluminium front panels for. It will house the project, which will include a DIY ribbon controller, the DIY dual joystick module i made a while back, and some other stuff. Preliminary layout of ribbon controller and joysticks. Joysticks will of course be mounted inside the case, not on top :-)  See individual posts about details.
|
|
|
Post by MikMo on Mar 4, 2023 8:50:53 GMT
The ribbon controllerA very long time ago (like 10 years+) i got some free samples of Spectrasymbol touch related products. One of the items was a ca. 20 cm soft pot / ribbon controller. So i decided to use it as a ribbon controller for AE. First i tried to just hook it up as any potentiometer, like a voltage dvider between 5V and ground. This worked, but i got a rather limited range, brecause the soft pot's resistance does not go down to zero when not touched. As i also wanted the ribbon controller to put out a gate and trigger signal whenever it was touched, i chose to go the microcontroller way. There was also som problems with linearity near one end of the soft pot, that was easier to deal with this way. So by now the setup consists of the soft pot, and Aduino Nano microcontroller and a small MCP4725 12 bit DAC. The softpot is read by the Arduino which in turn controls the DAC to put out the CV. This way i can remap the values read from the softpot to the full voltage range of the DAC (and AE). I have also included a "range" toggle switch, so i can scale the CV output to either 0-2V or 0-5V. This is how everything is hooked up:   Everything is mostly working by now, i just have a few arduino code changes to make. Arduino code ataced below. The softpot i am using has a very linear response over most of it's length, except the last 15mm or so. So in the Arduino code, i simply exclude values read from the last bit of the ribbon. Since this is not going into a standard AE module format, i am not so concerned about space / size. This could be made smaller by using one of the tiny Arduino Leonardo Beetle boards, like this: www.aliexpress.com/item/1005005163297459.html?spm=a2g0o.productlist.main.63.24052ee3cRATYF&algo_pvid=62385952-a8db-401c-8edc-13f6c2590ebc&algo_exp_id=62385952-a8db-401c-8edc-13f6c2590ebc-31&pdp_ext_f=%7B%22sku_id%22%3A%2212000031932710682%22%7D&pdp_npi=3%40dis%21DKK%2150.75%2145.7%21%21%21%21%21%4021227d8316779200553546097d0705%2112000031932710682%21sea%21DK%21127995896&curPageLogUid=WWTL7npYrtJGThese boards does not have all the Arduino I/O pins broken out, but sufficient pins are available for this and other projects. They also have 500 bytes more SRAM than Arduino Nano. Or even smaller by using discrete components instead of the "ready made" modules i am using. Resources:
Sparkfun article about using softpot's / ribbon controllers with Arduino: learn.sparkfun.com/tutorials/softpot-hookup-guide/allPossible source for softpots (no afiliation) www.sparkfun.com/categories/527Adafruit how to guide for using the MPC4725 DAC with Arduino: learn.adafruit.com/mcp4725-12-bit-dac-tutorialDAC module from Adafruit (no afiliation, just love and gratitude :-)): www.adafruit.com/product/935DAC module i used: ( When i bought them the better Adafruit ones was out of stock, consider supporting Adafruit. They put a lot of time into makng high quality products and libraries for the Arduino eco system, and their products are better quality than the chineese knock offs) www.aliexpress.com/item/1005003301834508.html?spm=a2g0o.productlist.main.1.3ee43d82tydf8L&algo_pvid=ad7059f0-0c07-4084-a000-2a7de0192391&algo_exp_id=ad7059f0-0c07-4084-a000-2a7de0192391-0&pdp_ext_f=%7B%22sku_id%22%3A%2212000025099039286%22%7D&pdp_npi=3%40dis%21USD%211.94%211.84%21%21%21%21%21%40211bd4cd16779169162763533d0745%2112000025099039286%21sea%21DK%21127995896&curPageLogUid=zMxbMRdr7kTWArduino code
/*****************************************************************************************************************************************************'
Ribbon controller for AE synth
Version 1.0
Mikael Mørup 05-03-2023
******************************************************************************************************************************************************/
#include <Wire.h>
#include <Adafruit_MCP4725.h>
Adafruit_MCP4725 dac;
const int ribbonPin = A1;
const int triggerPin = 3;
const int gatePin = 4;
const int rangeSwitchPin = 5; //switch for 0-2V or 0-5V output
const byte stateIdle = 0;
const byte stateTouchBegin = 1;
const byte stateTouched = 2;
const byte triggerDuration = 20; // trigger duration 20 mS
const byte range2V = 0;
const byte range5V = 1;
byte currentState = 0;
byte currentRange = 0;
int ribbonBaseVoltageValue = 0; //the value read from the ribbon when not touched.
int currentRibbonVoltageValue = 0;
long triggerStartTime = 0;
long rangeChangeCheckInterval = 1000; //only check for range switch changes once every second
long lastRangeCheckTime = 0;
// Variables for debouncing the ribbon potentiometer. It will have switchbounce just like a mechanical switch.
int ribbonState;
int lastRibbonState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 20;
int reading = LOW;
void setup()
{
pinMode(triggerPin, OUTPUT);
pinMode(gatePin, OUTPUT);
pinMode(rangeSwitchPin, INPUT_PULLUP);
currentRange = GetRange();
ribbonBaseVoltageValue = analogRead(ribbonPin);
currentState = stateIdle;
dac.begin(0x60);
dac.setVoltage(0, false);
}
void loop()
{
switch (currentState)
{
case stateIdle:
if (millis() > lastRangeCheckTime + rangeChangeCheckInterval)
currentRange = GetRange();
ReadRibbonVoltage();
if (currentRibbonVoltageValue <= ribbonBaseVoltageValue + 3)
reading = LOW;
else
{
reading = HIGH;
if (reading != lastRibbonState)
{
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay)
{
if (reading != ribbonState)
{
ribbonState = reading;
if (ribbonState == HIGH)
{
currentState = stateTouchBegin;
}
}
}
}
lastRibbonState = reading;
break;
case stateTouchBegin:
digitalWrite(triggerPin, HIGH);
triggerStartTime = millis();
digitalWrite(gatePin, HIGH);
currentState = stateTouched;
break;
case stateTouched:
if (triggerStartTime > 0)
CheckTriggerTimeElapsed();
ReadRibbonVoltage();
if (currentRibbonVoltageValue <= ribbonBaseVoltageValue + 3) // If the ribbon is no longer touched
{
digitalWrite(gatePin, LOW);
dac.setVoltage(0, false);
ribbonState = LOW;
reading = LOW;
lastRibbonState = LOW;
currentState = stateIdle;
}
else
SetDacValue(currentRibbonVoltageValue);
break;
}
}
void ReadRibbonVoltage()
{
currentRibbonVoltageValue = analogRead(ribbonPin);
}
void CheckTriggerTimeElapsed()
{
if (millis() >= triggerStartTime + triggerDuration)
{
digitalWrite(triggerPin, LOW);
triggerStartTime = 0;
}
}
byte GetRange()
{
if (digitalRead(rangeSwitchPin) == LOW)
{
lastRangeCheckTime = millis();
return (range2V);
}
else
{
lastRangeCheckTime = millis();
return (range5V);
}
}
void SetDacValue(int value)
{
if (value < 500) // Values below 500 read from the ribbon are rounded up to 500, due to non linearity in the response of the last bit of the ribbon.
value = 500;
int mV = 0;
if (currentRange == range2V)
mV = map (value, 500, 1023, 0, 1640); // map value to 0-2V output
else
mV = map(value, 500, 1023, 0, 4095); // map value to 0-5V output
dac.setVoltage(mV, false);
}
|
|