The message body was left empty....
I cant seem to post my whole threadstart since it says "The message body was left empty.". Am I doing some stupid noob mistake? ![]()
New posters (<100 posts) are restricted in many ways, e.g. timeouts.
This is done to counteract spamming by idiots and advertisers.
Did you read the "how to use this forum" that is on top of most pages.
Generally not a good idea to change the original post.
Leo..
Many have tried this before you.
Try "4067" in the search field on top of this page.
Or Google something like "4067 pot site:https://forum.arduino.cc".
Here's an example.
Leo..
The thing is that I need some example code for 8 multiplexers, and in an array instead of repetitive code. I also need some help with merging the signals from the multiplexers to one CC current that can be manipulated later on.
Any tips?
Imbecillen:
Any tips?
Start with one 4067 with 16 10kB (linear) pots, connected to one analogue input.
Leo..
I've got 16 pots to work with one 4067, and also sending the midi CC value to a SSD1306 oled display as a bar graph. I can map it to the computer as I want. I could probably make it work with several muxes if I make some ugly code, but I would like to make it as "simple" as possible. Or at least a small and neat code that is pretty easy to edit when new projects arrive.
I'm reading about arrays atm, hoping it will clear some stuff out..
Here's the code I used for a MIDI Controller with multiplexers:
/*
This is an example of the "ControlChange" class of the MIDI_controller library.
Connect faders or potentiometers to the inputs of analog multiplexers,
and connect the outputs of the multiplexers to analog inputs on the Arduino
(A0 and A1 in this case).
Connect the address lines of the different multiplexers together, and connect
them to digital outputs on the Arduino (pins 2, 3 and 4 in this case).
Change the number of multiplexers, the number of address lines, the
analog inputs and the address select pins to fit your hardware.
Upload the sketch, and map the faders in your DAW or DJ software.
If you are using a Teensy, make sure you have the USB type set to MIDI.
If you are using an Arduino Uno or Mega, use the HIDUINO firmware for the ATmega16U2.
Written by tttapa, 04-7-2017
https://github.com/tttapa/MIDI_controller
*/
#include <MIDI_controller.h> // include the library
const size_t analogAverage = 8; // Use the average of 8 samples to get smooth transitions and prevent noise
const uint8_t numberOfMultiplexers = 2; // Total number of multiplexers connected to the Arduino
const uint8_t addressWidth = 3; // Number of address lines of the multiplexers
const uint8_t addressLines[addressWidth] = {2, 3, 4}; // The pins connected to the address lines of the multiplexers
const uint8_t analogInputs[numberOfMultiplexers] = {A0, A1}; // The pins connected to the outputs of the multiplexers
//________________________________________________________________________________________________________________________________
const uint8_t numberOfInputsPerMultiplexer = 1 << addressWidth; // Number of inputs of each multiplexer
const size_t totalNumberOfAnalogInputs = numberOfMultiplexers * numberOfInputsPerMultiplexer; // the total number of potentiometers
//________________________________________________________________________________________________________________________________
ControlChange* potentiometers[totalNumberOfAnalogInputs]; // create an empty array of pointers to ControlChange objects
//________________________________________________________________________________________________________________________________
void setup() {
USBMidiController.blink(LED_BUILTIN); // flash the built-in LED (pin 13 on most boards) on every message
USBMidiController.setDelay(15); // wait 15 ms after each message not to flood the connection
USBMidiController.begin(); // Initialise the USB MIDI connection
delay(1000); // Wait a second...
for (unsigned int i = 0; i < totalNumberOfAnalogInputs; i++) {
potentiometers[i] = new ControlChange(i, 1); // create a new ControlChange object and store a pointer to it in the array
potentiometers[i]->average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
}
for (unsigned int i = 0; i < addressWidth; i++) {
pinMode(addressLines[i], OUTPUT); // set all address line pins as outputs
}
}
//________________________________________________________________________________________________________________________________
void loop() {
for (unsigned int i = 0; i < totalNumberOfAnalogInputs; i++) { // refresh all analog inputs
uint8_t value = analogReadMux(i) >> 3; // convert 10-bit analog value to 7-bit MIDI value (10-7 = 3)
potentiometers[i]->refresh(value);
}
}
void setMuxAddress(unsigned int address) {
for (unsigned int i = 0; i < addressWidth; i++) {
digitalWrite(addressLines[i], address & (1 << i));
}
}
int analogReadMux(unsigned int muxPin) {
int address = muxPin % numberOfInputsPerMultiplexer; // the input of the multiplexer
int analogPinIndex = muxPin / numberOfInputsPerMultiplexer; // the index of the multiplexer
setMuxAddress(address); // select the right input of the multiplexer
analogRead(analogInputs[analogPinIndex]); // Throw away first reading
return analogRead(analogInputs[analogPinIndex]); // read the output of the right multiplexer
}
The MIDI Controller library can be found here: GitHub - tttapa/MIDI_controller: This is a library for creating a MIDI controller using an Arduino or Teensy board..
It only sends new MIDI messages when the value has changed, it averages the inputs, and sends the right MIDI messages.
Pieter
Oh! That looks amazing! Pretty much exactly what I need. ![]()
How am I supposed to set the CC output in:
USBMidiController.send(CC, 1, 123, 0);
If I use a value of e.g. 123, it will be fixed right? Is there any prepared int I should use?
Imbecillen:
How am I supposed to set the CC output in:USBMidiController.send(CC, 1, 123, 0);If I use a value of e.g. 123, it will be fixed right? Is there any prepared int I should use?
I'm not sure what you mean. The channel and controller number are set in the ControlChange constructor:
potentiometers[i] = new ControlChange(i, 1); // create a new ControlChange object and store a pointer to it in the array
Where 'i' is the controller number, and the '1' is for channel 1.
You could create an array to select which controller numbers to use:
const uint8_t controllerNumbers[numberOfAnalogInputs] = {20, 21, ... };
[...]
potentiometers[i] = new ControlChange(controllerNumbers [i], 1); // create a new ControlChange object and store a pointer to it in the array
Note that 123 is not a valid controller number, 120 to 127 are reserved values.
You could use USBMidiController.send, but that kind of defeats the point of using the library, because you would have to write all logic yourself.
Pieter
Ah, now I get it and I found the manual! Was a bit tired last night
I was looking for the new ControlChange but I'm still feeling stupid.
I must use the USBMidiController.send to send midi over USB right? I can't figure out how to write it. My whole code right now:
Edit: The send is not supposed to be in my code at all I understand? The library handles it in the .h file?
/*
This is an example of the "ControlChange" class of the MIDI_controller library.
Connect faders or potentiometers to the inputs of analog multiplexers,
and connect the outputs of the multiplexers to analog inputs on the Arduino
(A0 and A1 in this case).
Connect the address lines of the different multiplexers together, and connect
them to digital outputs on the Arduino (pins 2, 3 and 4 in this case).
Change the number of multiplexers, the number of address lines, the
analog inputs and the address select pins to fit your hardware.
Upload the sketch, and map the faders in your DAW or DJ software.
If you are using a Teensy, make sure you have the USB type set to MIDI.
If you are using an Arduino Uno or Mega, use the HIDUINO firmware for the ATmega16U2.
Written by tttapa, 04-7-2017
https://github.com/tttapa/MIDI_controller
*/
#include <MIDI_controller.h> // include the library
#define DEBUG true
// #define DEBUG false
//----------------------------------------------------------------------------
// MULTIPLEXING
//----------------------------------------------------------------------------
const size_t analogAverage = 8; // Use the average of 8 samples to get smooth transitions and prevent noise
const uint8_t numberOfMultiplexers = 2; // Total number of multiplexers connected to the Arduino
const uint8_t addressWidth = 4; // Number of address lines of the multiplexers
const uint8_t addressLines[addressWidth] = {0, 1, 2, 3}; // The pins connected to the address lines of the multiplexers
const uint8_t analogInputs[numberOfMultiplexers] = {A0, A1}; // The pins connected to the outputs of the multiplexers
//________________________________________________________________________________________________________________________________
const uint8_t numberOfInputsPerMultiplexer = 2 << addressWidth; // Number of inputs of each multiplexer
const size_t totalNumberOfAnalogInputs = numberOfMultiplexers * numberOfInputsPerMultiplexer; // the total number of potentiometers
//________________________________________________________________________________________________________________________________
ControlChange* potentiometers[totalNumberOfAnalogInputs]; // create an empty array of pointers to ControlChange objects
//________________________________________________________________________________________________________________________________
void setup() {
analogReference(EXTERNAL); // Voltage reference for analog input
USBMidiController.blink(LED_BUILTIN); // flash the built-in LED (pin 13 on most boards) on every message
USBMidiController.setDelay(15); // wait 15 ms after each message not to flood the connection
USBMidiController.begin(31250, DEBUG); // Initialise the USB MIDI connection, set true to send serial instead of midi package
delay(1000); // Wait a second...
for (unsigned int i = 0; i < totalNumberOfAnalogInputs; i++) {
potentiometers[i] = new ControlChange(i, 1); // create a new ControlChange object and store a pointer to it in the array
potentiometers[i]->average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
}
for (unsigned int i = 0; i < addressWidth; i++) {
pinMode(addressLines[i], OUTPUT); // set all address line pins as outputs
}
}
//________________________________________________________________________________________________________________________________
void loop() {
for (unsigned int i = 0; i < totalNumberOfAnalogInputs; i++) { // refresh all analog inputs
uint8_t value = analogReadMux(i) >> 3; // convert 10-bit analog value to 7-bit MIDI value (10-7 = 3)
potentiometers[i]->refresh(value);
}
}
void setMuxAddress(unsigned int address) {
for (unsigned int i = 0; i < addressWidth; i++) {
digitalWrite(addressLines[i], address & (1 << i));
}
}
int analogReadMux(unsigned int muxPin) {
int address = muxPin % numberOfInputsPerMultiplexer; // the input of the multiplexer
int analogPinIndex = muxPin / numberOfInputsPerMultiplexer; // the index of the multiplexer
setMuxAddress(address); // select the right input of the multiplexer
analogRead(analogInputs[analogPinIndex]); // Throw away first reading
return analogRead(analogInputs[analogPinIndex]); // read the output of the right multiplexer
USBMidiController.send(); // Should this be something like new ControlChange(i, 1)?
Serial.println("CC value");
Serial.println();
}
And the thing about 120-127 are reserved, is it possible to make them free to use? Since it's a midi controller that will be hooked up with a DAW, I don't need set values for e.g pitch bend.
I guess it's these?
const uint8_t NOTE_OFF = 0x80;
const uint8_t NOTE_ON = 0x90;
const uint8_t KEY_PRESSURE = 0xA0;
const uint8_t CC = 0xB0;
const uint8_t PROGRAM_CHANGE = 0xC0;
const uint8_t CHANNEL_PRESSURE = 0xD0;
const uint8_t PITCH_BEND = 0xE0;
potentiometers[i]->refresh(value);
This line sends the MIDI messages automatically, no need to call USBMidiController.send().
I don't think you can use these controller numbers, but you can use different channels.
Also, debugging at 31250 baud is pretty useless, because it's not supported by the Serial Monitor.
What Arduino board are you using?
I haven't tested it with that many potentiometers. The example dynamically allocates memory for the ControlChange objects, you might run out of RAM.
Pieter
Ok, great! Yes I've understand that after reading some about it.
I'm using a Teensy LC board and will use at least 96 potentiometers and 16 buttons in toggle mode.
This seems to be a great library and well calibrated for midi CC's, but it also confuses me a lot instead of being simple to use. I tried to do a really simple code, in fact it's kind of the same as you posted, but I can't get my Teensy to send anything at all. I will give it a try later and see if it's possible to solve.
I would like to take the processed CC values and show them on a display as well. Is it even possible with this library without editing in the .h files and so on?
The basic principle is pretty simple, it's just the combination of multiplexers and arrays of pointers that make it quite complicated.
This is the most basic example:
#include <MIDI_controller.h> // include the library
const static byte Channel_Volume = 0x7; // controller number 7 is defined as Channel Volume in the MIDI implementation.
const static size_t analogAverage = 8; // Use the average of 8 samples to get smooth transitions and prevent noise
Analog fader(A0, Channel_Volume, 1); // Create a new instance of the class 'Analog, called 'fader', on pin A0, that sends MIDI messages with controller 7 (channel volume) on channel 1.
void setup(){
USBMidiController.blink(LED_BUILTIN); // flash the built-in LED (pin 13 on most boards) on every message
USBMidiController.setDelay(15); // wait 15 ms after each message not to flood the connection
USBMidiController.begin(); // Initialise the USB MIDI connection
fader.average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
}
void loop(){
fader.refresh(); // refresh the fader (check whether the input has changed since last time, if so, send it over MIDI)
}
I'm currently working on extensions to the library to make multiplexers really easy, and I've created a way to easily add many potentiometers, without using arrays of pointers.
Pieter
Thanks for all the help PieterP, but I think I will stick to my previous codes and expand them. In fact I may learn from scratch what things does. And I'm ready to have an "ugly" code for the sake of the dynamic use afterwards. It's pretty neat to be able to change the amount of potentiometers on each multiplexer for instance.
It may be some repetitive coding but at least I understand it.
Right now I'm struggle with how to merge the stream from the muxes analog pins. I would like to set a couple of CH (channel) and CC integer or something to send like this:
usbMIDI.sendControlChange(CH, CC);
The code as it is right now:
//Edited version of:
//Mux_Shield_AnalogIn_Example
//http://mayhewlabs.com/arduino-mux-shield
//Give convenient names to the control pins
#define CONTROL0 0
#define CONTROL1 1
#define CONTROL2 2
#define CONTROL3 3
//Create arrays for data from the the MUXs
//See the Arduino Array Reference: http://www.arduino.cc/en/Reference/Array
byte mux0array[16];
byte mux1array[16];
//Number of potentiometers on each multiplexer
int number_of_pots0 = 2;
int number_of_pots1 = 2;
void setup()
{
//Set MUX control pin number and make them output
pinMode(CONTROL0, OUTPUT);
pinMode(CONTROL1, OUTPUT);
pinMode(CONTROL2, OUTPUT);
pinMode(CONTROL3, OUTPUT);
//Open the serial port
Serial.begin(31250);
}
void loop()
{
//This for loop is used to scroll through and store the inputs on the FIRST multiplexer
for (int i = 0; i < number_of_pots0; i ++)
{
//The following 4 commands set the correct logic for the control pins to select the desired input
digitalWriteFast(CONTROL0, bitRead(i, 0));
digitalWriteFast(CONTROL1, bitRead(i, 1));
digitalWriteFast(CONTROL2, bitRead(i, 2));
digitalWriteFast(CONTROL3, bitRead(i, 3));
//Read and store the input value at a location in the array
mux0array[i] = analogRead(A0);
}
//This for loop is used to scroll through and store the inputs on the SECOND multiplexer
for (int i = 0; i < number_of_pots1; i ++)
{
//The following 4 commands set the correct logic for the control pins to select the desired input
digitalWriteFast(CONTROL0, bitRead(i, 0));
digitalWriteFast(CONTROL1, bitRead(i, 1));
digitalWriteFast(CONTROL2, bitRead(i, 2));
digitalWriteFast(CONTROL3, bitRead(i, 3));
//Read and store the input value at a location in the array
mux1array[i] = analogRead(A1);
}
//The following lines are for printing out results of array0
Serial.print("mux0array: ");
for (int i = 0; i < number_of_pots0; i ++)
{
Serial.print(mux0array[i]);
Serial.print("-");
}
Serial.println(); //line feed
//The following lines are for printing out results of array1
Serial.print("mux1array: ");
for (int i = 0; i < number_of_pots1; i ++)
{
Serial.print(mux1array[i]);
Serial.print("-");
}
Serial.println(); //line feed
delay (1000); //Just to see the changes in serial monitor before "only send when parameter changed" is added
}
#define CONTROL0 0
#define CONTROL1 1
Pin 0 and 1 are use by the USB<>Serial chip.
Use different pins.
Leo..
I would create a (multidimensional) array to store all previous values:
const uint8_t analogPins[] = { A0, A1, A2, A3, A4, A5, A6, A7 };
const size_t nb_analogPins = sizeof(analogPins);
const uint8_t addressPins[] = { 0, 1, 2, 3 };
const size_t nb_addressPins = sizeof(addressPins);
const size_t nb_addresses = 1 << nb_addressPins;
uint8_t previousValues[nb_analogPins][nb_addressPins] = {};
Then in the setup, set all address pins to outputs:
void setup() {
for(unsigned int i = 0; i < nb_addressPins; i++){
pinMode(addressPins[i], OUTPUT);
}
}
Next, write a function that reads the analog value of one of the multiplexed potentiometers, and a helper function to set the address:
void setMuxAddress(uint8_t address) {
for (unsigned int i = 0; i < nb_addressPins; i++) {
digitalWrite(addressPins[i], address & (1 << i));
}
}
int analogReadMux(uint8_t analogPin, uint8_t address) {
setMuxAddress(address); // select the right input of the multiplexer
analogRead(analogPin); // Throw away first reading
return analogRead(analogPin); // read the output of the right multiplexer
}
Then in the loop, iterate over all analog pins, and all multiplex addresses:
void loop() {
for (unsigned int pinIndex = 0; pinIndex < nb_analogPins; pinIndex++) {
for (uint8_t address = 0; address < nb_addresses; address++) {
int newAnalogValue = analogReadMux(analogPins[pinIndex], address);
uint8_t newMidiValue = newAnalogValue >> 3; // map from 10 bit to 7 bit
if (newMidiValue != previousValues[pinIndex][address]) { // if the value changed since last time
usbMIDI.sendControlChange(address + 0x10, newMidiValue, pinIndex + 1); // controller number, value, channel
previousValues[pinIndex][address] = newMidiValue; // the new value will be the previous value the next time the loop repeats
}
} // end_for address
} // end_for pinIndex
} // end of loop
The Mux address + 0x10 is used as the controller number (address 0 → Gen. purpose controller #1 (0x10) and address 15 → Undefined (0x1F)). The analog pin determines the channel (1 - 8).
If there's something you don't understand, don't hesitate to ask.
Wawa:
#define CONTROL0 0
#define CONTROL1 1Pin 0 and 1 are use by the USB<>Serial chip.
Use different pins.
Leo..
Not on a Teensy LC.
Pieter
Okey, thats more than amazing! It's pretty much exactly how I was thinking it was possible to do, but since I'm pretty new to this I can't barely get my head around all of your arrays and for statements. ![]()
But I think I do understand what everything does, so that's a good start!
Some questions:
The void setMuxAddress function you did there, is it basically to state an example for the void loop to follow?
Is it possible to use CC number 0-127 now, or is 120-127 still locked? I guess it has something to do with:
(address 0 → Gen. purpose controller #1 (0x10) and address 15 → Undefined (0x1F))
Could you explain that row a bit?
As I understand it there will be 16 potentiometers (one mux) on each midi channel with this code? "The analog pin determines the channel (1 - 8)."
Since many synthesizer geeks use a ton of instruments and controllers on all their channels it would be great to minimize the use of channels. One channel for 128 (112 work as well if it's the only way!) potentiometers would be great. Then it's also pretty simple to integrate the possibility to change midi channel for the whole mixer with an encoder, switch or button. But I guess this requires another approach than the pinIndex you did?
The last question: This code doesn't need the potentiometers to be announced at each multiplexer(?), what happens if I set 12 pots on one multiplexer? Is there a risk for random values since the rest of the inputs is empty? This is what I've experienced when prototyping I think, but I may be wrong on this one!
At last, a big thank you for taking your time!
Imbecillen:
The void setMuxAddress function you did there, is it basically to state an example for the void loop to follow?
I just defined a helper function that will be used in the loop function, to make it more readable.
Imbecillen:
Is it possible to use CC number 0-127 now, or is 120-127 still locked?
According to the MIDI specs, you can't use them for normal control change events. However, it's possible that the DAW doesn't need the special functions of controllers 120-127, and treats them as normal controllers. There's only one way to find out, and that's to try it.
Imbecillen:
I guess it has something to do with:
(address 0 → Gen. purpose controller #1 (0x10) and address 15 → Undefined (0x1F))
Could you explain that row a bit?
Mux 0, address 0: channel 1, controller 0x10
Mux 0, address 1: channel 1, controller 0x11
...
Mux 0, address 15: channel 1, controller 0x1F
Mux 1, address 0: channel 2, controller 0x10
...
Mux 1, address 15: channel 2, controller 0x1F
Mux 2, address 0: channel 3, controller 0x10
...
etc.
Imbecillen:
As I understand it there will be 16 potentiometers (one mux) on each midi channel with this code? "The analog pin determines the channel (1 - 8)."
Correct.
Imbecillen:
Since many synthesizer geeks use a ton of instruments and controllers on all their channels it would be great to minimize the use of channels. One channel for 128 (112 work as well if it's the only way!) potentiometers would be great. Then it's also pretty simple to integrate the possibility to change midi channel for the whole mixer with an encoder, switch or button. But I guess this requires another approach than the pinIndex you did?
Yes, you'd have to change your approach.
One way would be to use controller = pinIndex * nb_addresses + address. This would give you a value from 0 to 127. (Again, not sure if the DAW will support controllers 120-127.)
Imbecillen:
The last question: This code doesn't need the potentiometers to be announced at each multiplexer(?), what happens if I set 12 pots on one multiplexer? Is there a risk for random values since the rest of the inputs is empty? This is what I've experienced when prototyping I think, but I may be wrong on this one!
Yes, it will just read the voltage on the floating pin, which is pretty random, and it will change all the time, causing it to send CC messages all over the place.
One way to solve this would be to add a break condition in your for loop. For example, if your last multiplexer only has 12 potentiometers instead of 16:
void loop() {
for (unsigned int pinIndex = 0; pinIndex < nb_analogPins; pinIndex++) {
for (uint8_t address = 0; address < nb_addresses; address++) {
if ((pinIndex == nb_analogPins - 1) && (address >= 12)) // an address higher than 11 (12th input) on the last multiplexer
break;
int newAnalogValue = analogReadMux(analogPins[pinIndex], address);
uint8_t newMidiValue = newAnalogValue >> 3; // map from 10 bit to 7 bit
if (newMidiValue != previousValues[pinIndex][address]) { // if the value changed since last time
usbMIDI.sendControlChange(address + 0x10, newMidiValue, pinIndex + 1); // controller number, value, channel
previousValues[pinIndex][address] = newMidiValue; // the new value will be the previous value the next time the loop repeats
}
} // end_for address
} // end_for pinIndex
} // end of loop
Pieter