I am using an Arduino Mega and got my device up and running with a MIDI shield, 16 potentiometers and a 16x2 LCD display.
I managed to print text in the two rows, so the display is working well.
Now I want that each time I move one or two of the 16 potis to show the value changes on the display.
I did some research, but I am helpless in how to start the coding for this.
I gonna add all 3 code files. main code, controller.cpp and controller.h
Would be nice, if someone could get me started with maybe 2 potis.
I would like to show the two potis in the second LCD row (one to the left and one to the right)
It would look like this:
CONSTELLATION
p01=127 p02=127
Right now the second row shows "Cosmic Octave" and it would be great, if this would show until a poti is moved and after some time of no poti activity the Cosmic Octave will show again in that row.
Here is the code I have:
#include <MIDI.h>
#include "Controller.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x3F, 16, 2);
/*************************************************************
MIDI CONTROLLER
by Notes and Volts
www.notesandvolts.com
*************************************************************/
MIDI_CREATE_DEFAULT_INSTANCE();
//************************************************************
//***SET THE NUMBER OF CONTROLS USED**************************
//************************************************************
//---How many buttons are connected directly to pins?---------
byte NUMBER_BUTTONS = 0;
//---How many potentiometers are connected directly to pins?--
byte NUMBER_POTS = 16;
//---How many buttons are connected to a multiplexer?---------
byte NUMBER_MUX_BUTTONS = 0;
//---How many potentiometers are connected to a multiplexer?--
byte NUMBER_MUX_POTS = 0;
//************************************************************
//***ANY MULTIPLEXERS? (74HC4067)************************************
//MUX address pins must be connected to Arduino UNO pins 2,3,4,5
//A0 = PIN2, A1 = PIN3, A2 = PIN4, A3 = PIN5
//*******************************************************************
//Mux NAME (OUTPUT PIN, , How Many Mux Pins?(8 or 16) , Is It Analog?);
//Mux M1(10, 16, false); //Digital multiplexer on Arduino pin 10
//Mux M2(A0, 16, true); //Analog multiplexer on Arduino analog pin A0
//*******************************************************************
//***DEFINE DIRECTLY CONNECTED POTENTIOMETERS************************
//Pot (Pin Number, Command, CC Control, Channel Number)
//**Command parameter is for future use**
Pot PO1(A0, 0, 102, 1);
Pot PO2(A1, 0, 103, 1);
Pot PO3(A2, 0, 104, 1);
Pot PO4(A3, 0, 105, 1);
Pot PO5(A4, 0, 106, 1);
Pot PO6(A5, 0, 107, 1);
Pot PO7(A6, 0, 108, 1);
Pot PO8(A7, 0, 109, 1);
Pot PO9(A8, 0, 110, 1);
Pot PO10(A9, 0, 111, 1);
Pot PO11(A10, 0, 112, 1);
Pot PO12(A11, 0, 113, 1);
Pot PO13(A12, 0, 114, 1);
Pot PO14(A13, 0, 115, 1);
Pot PO15(A14, 0, 116, 1);
Pot PO16(A15, 0, 117, 1);
//*******************************************************************
//Add pots used to array below like this-> Pot *POTS[] {&PO1, &PO2, &PO3, &PO4, &PO5, &PO6};
Pot *POTS[] {&PO1, &PO2, &PO3, &PO4, &PO5, &PO6, &PO7, &PO8, &PO9, &PO10, &PO11, &PO12, &PO13, &PO14, &PO15, &PO16};
//*******************************************************************
//***DEFINE DIRECTLY CONNECTED BUTTONS*******************************
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
//** Command parameter 0=NOTE 1=CC 2=Toggle CC **
//Button BU1(2, 0, 60, 1, 5 );
//Button BU2(3, 0, 61, 1, 5 );
//*******************************************************************
//Add buttons used to array below like this-> Button *BUTTONS[] {&BU1, &BU2, &BU3, &BU4, &BU5, &BU6, &BU7, &BU8};
Button *BUTTONS[] {};
//*******************************************************************
//***DEFINE BUTTONS CONNECTED TO MULTIPLEXER*************************
//Button::Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce)
//** Command parameter 0=NOTE 1=CC 5=Toggle CC **
//Button MBU4(M1, 3, 4, 32, 1, 5); //2" yes
//Button MBU3(M1, 2, 3, 32, 1, 5); //4" yes
//*******************************************************************
////Add multiplexed buttons used to array below like this-> Button *MUXBUTTONS[] {&MBU1, &MBU2, &MBU3, &MBU4, &MBU5, &MBU6.....};
Button *MUXBUTTONS[] {};
//*******************************************************************
//***DEFINE POTENTIOMETERS CONNECTED TO MULTIPLEXER*******************
//Pot::Pot(Mux mux, byte muxpin, byte command, byte control, byte channel)
//**Command parameter is for future use**
//MULTIPLEXER 2 (1-15)
//LFO
//Pot MPO1(M2, 3, 0, 25, 1); //Shape 1 (0-7) yes
//Pot MPO2(M2, 2, 0, 24, 1); //Speed 1 yes
//*******************************************************************
//Add multiplexed pots used to array below like this-> Pot *MUXPOTS[] {&MPO1, &MPO2, &MPO3, &MPO4, &MPO5, &MPO6.....};
Pot *MUXPOTS[] {};
//*******************************************************************
void setup() {
Serial.begin(9600); //9600 bits per second serial data output
MIDI.begin(MIDI_CHANNEL_OMNI);
// initialize the LCD
lcd.begin();
// Turn on the blacklight and print a message.
lcd.backlight();
lcd.setCursor(0, 0); // set the cursor to first column, second row
lcd.print("CONSTELLATION");
lcd.setCursor(0, 1); // set the cursor to first column, second row
lcd.print("Cosmic Octave");
}
void loop() {
MIDI.read();
if (NUMBER_BUTTONS != 0) updateButtons();
if (NUMBER_POTS != 0) updatePots();
if (NUMBER_MUX_BUTTONS != 0) updateMuxButtons();
if (NUMBER_MUX_POTS != 0) updateMuxPots();
}
//*****************************************************************
void updateButtons() {
// Cycle through Button array
for (int i = 0; i < NUMBER_BUTTONS; i = i + 1) {
byte message = BUTTONS[i]->getValue();
// Button is pressed
if (message == 0) {
switch (BUTTONS[i]->Bcommand) {
case 0: //Note
MIDI.sendNoteOn(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
break;
case 1: //CC
MIDI.sendControlChange(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
break;
case 2: //Toggle
if (BUTTONS[i]->Btoggle == 0) {
MIDI.sendControlChange(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
BUTTONS[i]->Btoggle = 1;
}
else if (BUTTONS[i]->Btoggle == 1) {
MIDI.sendControlChange(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
BUTTONS[i]->Btoggle = 0;
}
break;
}
}
// Button is not pressed
if (message == 1) {
switch (BUTTONS[i]->Bcommand) {
case 0:
MIDI.sendNoteOff(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
break;
case 1:
MIDI.sendControlChange(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
break;
}
}
}
}
//*******************************************************************
void updateMuxButtons() {
// Cycle through Mux Button array
for (int i = 0; i < NUMBER_MUX_BUTTONS; i = i + 1) {
MUXBUTTONS[i]->muxUpdate();
byte message = MUXBUTTONS[i]->getValue();
// Button is pressed
if (message == 0) {
switch (MUXBUTTONS[i]->Bcommand) {
case 0: //Note
MIDI.sendNoteOn(MUXBUTTONS[i]->Bvalue, 127, MUXBUTTONS[i]->Bchannel);
break;
case 6: //CC 16"
MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 36, MUXBUTTONS[i]->Bchannel);
break;
case 2: //CC 8"
MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 48, MUXBUTTONS[i]->Bchannel);
break;
case 3: //CC 4"
MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 60, MUXBUTTONS[i]->Bchannel);
break;
case 4: //CC 2"
MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 72, MUXBUTTONS[i]->Bchannel);
break;
case 5: //Toggle
if (MUXBUTTONS[i]->Btoggle == 0) {
MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 127, MUXBUTTONS[i]->Bchannel);
MUXBUTTONS[i]->Btoggle = 1;
}
else if (MUXBUTTONS[i]->Btoggle == 1) {
MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 0, MUXBUTTONS[i]->Bchannel);
MUXBUTTONS[i]->Btoggle = 0;
}
break;
}
}
// Button is not pressed
if (message == 1) {
switch (MUXBUTTONS[i]->Bcommand) {
case 0:
MIDI.sendNoteOff(MUXBUTTONS[i]->Bvalue, 0, MUXBUTTONS[i]->Bchannel);
break;
case 1:
MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 0, MUXBUTTONS[i]->Bchannel);
break;
}
}
}
}
//***********************************************************************
void updatePots() {
for (int i = 0; i < NUMBER_POTS; i = i + 1) {
byte potmessage = POTS[i]->getValue();
if (potmessage != 255) MIDI.sendControlChange(POTS[i]->Pcontrol, potmessage, POTS[i]->Pchannel);
}
}
//***********************************************************************
void updateMuxPots() {
for (int i = 0; i < NUMBER_MUX_POTS; i = i + 1) {
MUXPOTS[i]->muxUpdate();
byte potmessage = MUXPOTS[i]->getValue();
if (potmessage != 255) MIDI.sendControlChange(MUXPOTS[i]->Pcontrol, potmessage, MUXPOTS[i]->Pchannel);
}
}
Controller.cpp
#include "Controller.h"
//****************************************************************************************
Mux::Mux(byte outpin_, byte numPins_, bool analog_)
{
outpin = outpin_;
//enablepin = enablepin_;
numPins = numPins_;
analog = analog_;
if (analog == false) pinMode(outpin, INPUT_PULLUP);
//pinMode(enablepin, OUTPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
if (numPins > 8) pinMode(5, OUTPUT);
}
//****************************************************************************************
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
Button::Button(byte pin, byte command, byte value, byte channel, byte debounce)
{
_pin = pin;
pinMode(_pin, INPUT_PULLUP);
_value = value;
_command = command;
_debounce = debounce;
_time = 0;
_busy = false;
_status = 0b00000010;
_last = 1;
Bcommand = command;
Bvalue = value;
Bchannel = channel;
Btoggle = 0;
}
Button::Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce)
{
_pin = mux.outpin;
_numMuxPins = mux.numPins;
_muxpin = muxpin;
_value = value;
_command = command;
_debounce = debounce;
_time = 0;
_busy = false;
_status = 0b00000010;
_last = 1;
Bcommand = command;
Bvalue = value;
Bchannel = channel;
Btoggle = 0;
}
void Button::muxUpdate()
{
byte temp = _muxpin;
temp = temp << 2;
if (_numMuxPins > 8) PORTD = PORTD & B11000011;
else PORTD = PORTD & B11100011;
PORTD = PORTD | temp;
}
byte Button::getValue()
{
// If BUSY bit not set - read button
if (bitRead(_status, 0) == false) { // If busy false
if (digitalRead(_pin) == _last) return 2; // If same as last state - exit
}
// If NEW Bit set - Key just pressed, record time
if (bitRead(_status, 1) == true) { // If new is true
bitSet(_status, 0); // Set busy TRUE
bitClear(_status, 1); // Set New FALSE
_time = millis();
return 255;
}
// Check if debounce time has passed - If no, exit
if (millis() - _time < _debounce) return 255;
// Debounce time has passed. Read pin to see if still set the same
// If it has changed back - assume false alarm
if (digitalRead(_pin) == _last) {
bitClear(_status, 0); // Set busy false
bitSet(_status, 1); // Set new true
return 255;
}
// If this point is reached, event is valid. return event type
else {
bitClear(_status, 0); // Set busy false
bitSet(_status, 1); // Set new true
_last = ((~_last) & 0b00000001); // invert _last
return _last;
}
}
void Button::newValue(byte command, byte value, byte channel)
{
Bvalue = value;
Bcommand = command;
Bchannel = channel;
}
//********************************************************************
Pot::Pot(byte pin, byte command, byte control, byte channel)
{
_pin = pin;
_control = control;
_value = analogRead(_pin);
_value = _value >> 3;
_oldValue = _value << 3;
_value = _value << 3;
Pcommand = command;
Pcontrol = control;
Pchannel = channel;
}
void Pot::muxUpdate()
{
byte temp = _muxpin;
temp = temp << 2;
if (_numMuxPins > 8) PORTD = PORTD & B11000011;
else PORTD = PORTD & B11100011;
//PORTD = PORTD & B11000011;
PORTD = PORTD | temp;
}
Pot::Pot(Mux mux, byte muxpin, byte command, byte control, byte channel)
{
_pin = mux.outpin;
_numMuxPins = mux.numPins;
_muxpin = muxpin;
_control = control;
muxUpdate();
_value = analogRead(_pin);
_value = _value >> 3;
_oldValue = _value << 3;
_value = _value << 3;
Pcommand = command;
Pcontrol = control;
Pchannel = channel;
}
byte Pot::getValue()
{
_value = analogRead(_pin);
int tmp = (_oldValue - _value);
if (tmp >= 8 || tmp <= -8) {
_oldValue = _value >> 3;
_oldValue = _oldValue << 3;
return _value >> 3;
}
return 255;
}
void Pot::newValue(byte command, byte value, byte channel) {
Pcommand = command;
Pcontrol = value;
Pchannel = channel;
}
Controller.h
#ifndef Controller_h
#define Controller_h
#include <Arduino.h>
//***********************************************************************
class Mux
{
public:
Mux(byte outpin_, byte numPins_, bool analog_);
byte outpin;
byte numPins;
bool analog;
};
//************************************************************************
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
class Button
{
public:
Button(byte pin, byte command, byte value, byte channel, byte debounce);
Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce);
byte getValue();
void muxUpdate();
void newValue(byte command, byte value, byte channel);
byte Bcommand;
byte Bvalue;
byte Bchannel;
byte Btoggle;
private:
byte _previous;
byte _current;
unsigned long _time;
int _debounce;
byte _pin;
byte _muxpin;
byte _numMuxPins;
byte _value;
byte _command;
bool _busy;
byte _status;
byte _last;
byte _enablepin;
};
//*************************************************************************
class Pot
{
public:
Pot(byte pin, byte command, byte control, byte channel);
Pot(Mux mux, byte muxpin ,byte command, byte control, byte channel);
void muxUpdate();
void newValue(byte command, byte value, byte channel);
byte getValue();
byte Pcommand;
byte Pcontrol;
byte Pchannel;
private:
byte _pin;
byte _muxpin;
byte _numMuxPins;
byte _control;
int _value;
int _oldValue;
bool _changed;
byte _enablepin;
};
//*************************************************************************
#endif