DIY controllers

Here is the finished schematic right now. https://drive.google.com/open?id=1df7c6NgvyiyRq3a6nFFZ_QWMlsYzsjJn
Instagram post about my first fully complete protoboard. https://www.instagram.com/p/B_WESoFh5NI/

The only thing I didn’t have on the protoboard are the diode protection and the filtering IC caps. I bread boarded it and they should work if you add them. Planning on ordering some PCBs later on as practice circuit board.

2 Likes

would defiantly be interested in the pcb’s . I have horrible luck with bread board . A joy stick controller for the modular stuff would be awesome.

1 Like

I don’t see anything at that link, is it just me?

1 Like

Doesn’t work for me either. I seem to have bad luck with web sites right now, though, so that may not mean much.

2 Likes

It was on my end it seems. Imgur was having problems allowing me too post. Sorry about that gotta find a better way too share documents. Here is The schematic PDF https://drive.google.com/open?id=1df7c6NgvyiyRq3a6nFFZ_QWMlsYzsjJn

2 Likes

Been dabbling with the “Control Surface” Lib for Arduino. Wow its easy to use. Ive only scratched the surface, but yeah its really good. Way better than the default midi lib.

https://tttapa.github.io/Control-Surface-doc/Doxygen/d5/d7d/md_pages_Getting-Started.html#first-input

Some demo serial out messages from the USBDebugMIDI_Interface:

01:15:02.777 -> Control Change  	Channel: 2	Data 1: 0x07	Data 2: 0x40	Cable: 0
01:15:02.812 -> Control Change  	Channel: 2	Data 1: 0x07	Data 2: 0x42	Cable: 0
01:15:02.812 -> Control Change  	Channel: 2	Data 1: 0x07	Data 2: 0x43	Cable: 0
01:15:02.847 -> Control Change  	Channel: 2	Data 1: 0x07	Data 2: 0x44	Cable: 0
01:15:03.092 -> Control Change  	Channel: 1	Data 1: 0x07	Data 2: 0x01	Cable: 0
01:15:03.127 -> Control Change  	Channel: 1	Data 1: 0x07	Data 2: 0x03	Cable: 0
01:15:03.127 -> Control Change  	Channel: 1	Data 1: 0x07	Data 2: 0x05	Cable: 0
01:15:03.127 -> Control Change  	Channel: 1	Data 1: 0x07	Data 2: 0x06	Cable: 0
01:15:03.162 -> Control Change  	Channel: 1	Data 1: 0x07	Data 2: 0x07	Cable: 0
01:15:03.301 -> Control Change  	Channel: 1	Data 1: 0x07	Data 2: 0x08	Cable: 0
01:15:11.537 -> Control Change  	Channel: 5	Data 1: 0x07	Data 2: 0x2d	Cable: 0

I have tested with the 5 pin Midi din interface this has and its really as simple as swapping the interface init.

There is support for midi in/out and a lot of support for mux and shift registers. Also, many capabilities for visualizations with led and even OLED displays. Im just using the default lib for the LCD screen for output.

Im using the getValue() function of the CCPotentiometer object to pull back the value before display to the screen. This is just nuts how much of an abstraction layer this presents.

I also have a for loop within the loop() so to offset the screen draw. I wanted the screen to update less frequently so it doesnt dim/flicker as much, and i didnt want to have the midi interface updates be delayed as well.

//===============================================================================
//  Header Files
//===============================================================================
#include <Control_Surface.h> // Include the Control Surface library
#include <LiquidCrystal.h>


// Initialize the LCD library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 5, en = 6, d4 = 7, d5 = 8, d6 = 9, d7 = 10;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);


// Instantiate a Serial MIDI interface at the default MIDI baud rate.
USBDebugMIDI_Interface midi = {115200};

AnalogMultiplex<4> mux = {
  A0,      // Analog input pin
  {2,3,4}  // Address pins S0, S1, S2
};


using namespace MIDI_Notes;

// Create an array of potentiometers that send out
// MIDI Control Change messages when you turn the
// potentiometers connected to the eight input pins of
// the multiplexer plus 2 other analog pins
CCPotentiometer volumePotentiometers[] = {
  {mux.pin(0), {MIDI_CC::Channel_Volume, CHANNEL_5}},
  {mux.pin(1), {MIDI_CC::Channel_Volume, CHANNEL_4}},
  {mux.pin(2), {MIDI_CC::Channel_Volume, CHANNEL_3}},
  {mux.pin(3), {MIDI_CC::Channel_Volume, CHANNEL_6}},
  {mux.pin(4), {MIDI_CC::Channel_Volume, CHANNEL_1}},
  {mux.pin(5), {MIDI_CC::Channel_Volume, CHANNEL_8}},
  {mux.pin(6), {MIDI_CC::Channel_Volume, CHANNEL_2}},
  {mux.pin(7), {MIDI_CC::Channel_Volume, CHANNEL_7}},
  {A1,         {MIDI_CC::Channel_Volume, CHANNEL_9}},
  {A2,         {MIDI_CC::Channel_Volume, CHANNEL_10}},
};

void setup()
{
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  
  Control_Surface.begin(); // Initialize the Control Surface
}

void loop()
{
  // For loop will have the control interface update i times for every screen refresh
  for (int i = 0; i<=10; i++) {
    Control_Surface.loop();
  };

  lcd.setCursor(0,0);
  lcd.print("                    ");
  lcd.setCursor(0,1);
  lcd.print("                    ");
  
  int pot_5_value = volumePotentiometers[0].getValue();
  int pot_4_value = volumePotentiometers[1].getValue();
  int pot_3_value = volumePotentiometers[2].getValue();
  int pot_6_value = volumePotentiometers[3].getValue();
  int pot_1_value = volumePotentiometers[4].getValue();
  int pot_8_value = volumePotentiometers[5].getValue();
  int pot_2_value = volumePotentiometers[6].getValue();
  int pot_7_value = volumePotentiometers[7].getValue();
  int pot_9_value = volumePotentiometers[8].getValue();
  int pot_10_value = volumePotentiometers[9].getValue();

  // Print the values of the pots in hex to the LCD screen.
  lcd.setCursor(0,0);
  lcd.print((char)124);
  lcd.setCursor(1,0);
  lcd.print(pot_1_value, HEX);
  lcd.setCursor(3,0);
  lcd.print((char)124);
  lcd.setCursor(4,0);
  lcd.print(pot_2_value, HEX);
  lcd.setCursor(6,0);
  lcd.print((char)124);
  lcd.setCursor(7,0);
  lcd.print(pot_3_value, HEX);
  lcd.setCursor(9,0);
  lcd.print((char)124);
  lcd.setCursor(10,0);
  lcd.print(pot_4_value, HEX);
  lcd.setCursor(12,0);
  lcd.print((char)124);
  lcd.setCursor(13,0);
  lcd.print(pot_5_value, HEX);
  lcd.setCursor(15,0);
  lcd.print((char)124);
  lcd.setCursor(0,1);
  lcd.print((char)124);
  lcd.setCursor(1,1);
  lcd.print(pot_6_value, HEX);
  lcd.setCursor(3,1);
  lcd.print((char)124);
  lcd.setCursor(4,1);
  lcd.print(pot_7_value, HEX);
  lcd.setCursor(6,1);
  lcd.print((char)124);
  lcd.setCursor(7,1);
  lcd.print(pot_8_value, HEX);
  lcd.setCursor(9,1);
  lcd.print((char)124);
  lcd.setCursor(10,1);
  lcd.print(pot_9_value, HEX);
  lcd.setCursor(12,1);
  lcd.print((char)124);
  lcd.setCursor(13,1);
  lcd.print(pot_10_value, HEX);
  lcd.setCursor(15,1);
  lcd.print((char)124);
  
}




Oh thats a 74HC4051 8 channel multiplexer btw:
https://www.taydaelectronics.com/cd74hc4051-74hc4051-744051-ic-8-channel-analog-multiplexer.html

4 Likes

Oh look at this. A layout for the controller to be.

Designed this to work with the arturia suite of software synths. I might get into making specific controllers for my favorite VSTs.

Throwing into an old cigar box i got for free.

This thing is going to be an arduino nano, a couple of mux, and the interface bits. Its really wild how much easier this process can be now than when i looked into this years ago.

4 Likes

As you have the space why not add a second MIDI out. Always handy on a controller. My Roland A33 was a star at gigs simply because I could send to A,B and AB to two module chains.

1 Like

Thats not a bad idea yo. I might have less space that you think though, not 100% sure where everything’s going yet, so ill def take you up on that if i can. as you point out, why not?

3 Likes

Built a couple of MIDI splitters for a friend. Used tiny tin boxes and a grommet with 5 cables out to 5 female din connectors. A later effort used a single female din to a special cable with two outs. It all depends how you wire that single din socket. It’s your controller so adding a trrs socket for MIDI outputs or a centronics printer type socket for multiples is an option you have that manufacturing companies lack. Stuff that thang!

It seems to me that in the main loop you can simplify this long enumeration of variables :

int pot_5_value = volumePotentiometers[0].getValue();
int pot_4_value = volumePotentiometers[1].getValue();

etc etc

int pot_10_value = volumePotentiometers[9].getValue();

into something short like this:

#define NR 10
int pot_value[NR];
for (int i = 0; i < NR; i++) {
  pot_value[i]] = volumePotentiometers[i].getValue();
}

and similarly reduce the long list of commands:

  lcd.setCursor(0,0);
  lcd.print((char)124);
  lcd.setCursor(1,0);
  lcd.print(pot_1_value, HEX);

etc etc

  lcd.print((char)124);
  lcd.setCursor(13,1);
  lcd.print(pot_10_value, HEX);
  lcd.setCursor(15,1);
  lcd.print((char)124);

to something like:

for (int i = 0; i < NR / 2; i++){
  lcd.setCursor(i, 0);
  lcd.print((char) 124);
  lcd.setCursor(i + 1, 0);
  lcd.print(pot_value[i], HEX);
}
for (int i = NR / 2; i < NR; i++){
  lcd.print((char)124);
  lcd.setCursor(i,1);
  lcd.print(pot_10_value, HEX);
  lcd.setCursor(i + 1, 1);
  lcd.print((char)124);
}

Which makes this code more readable.

Just a suggestion.

Note: because I can not check this using hardware you have to check the bounds of the last 2 for loops and whether the positioning of the values is what you want them to have.

2 Likes

Or he might have been carefully unrolling the loops to avoid spending cycles on loop index maintenace and address calculations and you just rolled them up again :grinning:. Tradeoffs, tradeoffs…

3 Likes

Basically this. At the end of the day it gets compiled anyways and is for my personal use. If it works, its good enough.

@Jos I appreciate the suggestions, however i should note that either way can be looked at as acceptable.

My personal philosophy is always to get it working first, then refactor if it needs it. Otherwise I am just sitting there optimizing code that may not really need it.

-edit-
I haven’t even finished building this thing, much less finish the code :slight_smile:. But please don’t feel like those efforts of code refactoring were not appreciated. My cookies arnt out of the oven yet as it were.

3 Likes

I remember once being taught the two rules of optimization:

  1. Don’t optimize
  2. Don’t optimize yet

In other words, optimization often causes so much trouble that you shouldn’t do it unless it’s really needed, and if it’s really needed, you shouldn’t do it until it’s really needed.

4 Likes

Premature optimization – whether for source code style & neatness or for speed or for size (will this fit in this ATtiny?) or for some other artificial constraint – can be a lot of fun, though :smiley: I mean, we’re all hobbyists, and are free to pick our own rules, even if they wouldn’t make sense for our daytime jobs (e.g. I will admit that I’ve written more AVR assembler than might be good for you. Who needs a Teensy when you can count cycles? :upside_down_face:)

3 Likes

I said I learned those rules. I didn’t say I practice them!

5 Likes

Well, I meant these suggestions more for the sake of readability. When I think of optimization I normally look at memory usage (of data and code) and speed. And those matters are often best left to the compiler.

1 Like

Coming along. Only really need to fit it in the box, and drill out hole to fit the bits.

Oh, and i need to find some good cc ranges. :slight_smile:

4 Likes

From your description I gather that you want to produce midi data with the controller. What do you use to read the analog values of the sliders and potentiometers? Have you experienced any numeric noise in the values read from the analog inputs?

2 Likes

Using the control surface library I linked above, or did you mean more specifically?

No noise, butter smooth.

2 Likes