I've tried with two different MIDI pedal board/processors. When you
change preset on the pedal board, it is supposed to send MIDI program
change messages, if enabled. I've confirmed MIDI channel is set to 1.
The values are supposed to correspond with the numbering scheme of the pedal board,
so bank 1 preset 1 should send a command byte of 192 for channel 1 program change,
and the next byte would be 0 for preset 1.
However, when I use the serial monitor in the Arduino IDE, instead I get a command byte of 128
which is channel 1 Note Off. Also program numbers are not in order.
Bank 1 presets 1, 2, 3 and 4 send values of 0,1,6 and 7 respectively.
The manual for the pedal boards (Vox Tonelab LE and Line 6 POD HD500) do not mention
being able to edit or map these values.
Here are the important bits from the sketch:
I declared two variables...
byte cmdByte; // 1st byte is command or "status" byte
byte progNum; // 2nd byte is program change number
In setup, I've included...
//start serial with midi baudrate 31250 or 38400 for debugging
Serial.begin(38400);
In the main loop, I have...
if (Serial.available() > 0) {
// read the incoming bytes:
cmdByte = Serial.read();
progNum = Serial.read();
According to my understanding of I what I've read, Serial.available() returns the number of bytes available to read. There are 2 bytes in a MIDI program change message, and the above method is giving me two different numbers in the serial monitor that change as I send different program changes.
The first Serial.read() returns the first byte. I thought you could call Serial.read() again to retrieve the next byte.
Or does it have to be nested in a while Serial.available() loop?
The code is executing way faster than the midi rate.
You need to do something to differentiate one byte from another. You can read when you got 2 bytes, store them in an array, and read pos 0 and pos 1 of the array.
Or you could use switch, if the command byte arrived(byte == 192(PC status byte if i remember correctly)) go to next case, if the new byte is between 0 - 127, that's your program change value
ArtieJ:
According to my understanding of I what I've read, Serial.available() returns the number of bytes available to read.
Yes, it does, but Serial.available() does not return the bytes themselves; but only how many bytes there are available.
See the comments in the code. Assume you receive two bytes, with values of 14 and 20.
if (Serial.available() > 0) {
// There is at least one byte available now. You don't know how many because
// you throw away the return from Serial.available
// read the incoming bytes: // Incoming byte, not bytes.
cmdByte = Serial.read();
// you now have the first bte the first byte
progNum = Serial.read();
// you put something into progNum, but it likely isn't the second byte, because
// you sre zipping through this routine atthe rate of 62.5 nanoseconds per insctuction,
// and your thing that's sending the data is still busy sending out the first bit of the second byte
// it will arrive eventually, just not yet.
The first Serial.read() returns the first byte. I thought you could call Serial.read() again to retrieve the next byte.
You can, and you do. BUt it will, with the above code, be placed in cmdByte, and again you'll put junk into progNum.
You can wait until Serial.available() returns 2, or you can collect bytes until you have the right number, or you can grab each byte, one at a time, and put them in different variables, but you'll need to know which byte it is.
OK, thanks for some clarification.
The 1st byte value I'm getting is consistently 128, not 192. I've checked with a Windows utility to capture the data and it is what it is supposed to be, 192. So I'm not even getting the first byte right!
So, you're saying the function "if (Serial.available() > 0)" evaluates TRUE when there is a complete byte. But the byte from Serial.read() is incorrect. Any thought why that might be? It's called immediately, shouldn't it be the first complete byte?
I'm also going to try a different hardware interface to see if that helps.
Ah, re-read you're info, cmdByte value is going to change with the 2nd Serial.read().
OK, closer to getting by brain around it, haha. I guess I need to figure out how to make a while loop
create an array of the bytes?
RE-read again. Look at the switch pseudo example I wrote.
The good thing is that you know what and how many bytes you'll receive. That simplifies things. The first byte will be above or equal to 192, the second data byte will be under 128. So when you receive the first byte, store it in a variable, then go to the next case, and if the new byte is below 128, store it in another variable corresponding to the byte function. Then when you received that 2nd byte, execute your programchange function.
Here's a nice tutorial for serial.read and etc http://hacking.majenko.co.uk/reading-serial-on-the-arduino
144 was a late hour brain fart. I had changed it to 128 just to see the relays work. Actually, it worked reliably after dozens of MIDI messages sent.
But my question stands, is there anything procedurally wrong with the method? It waits until there are two bytes, stuffs them in an array.
One more important thing, I changed the hardware interface. The original circuit was based on a 4N28 opto, used by others with success. I built another one based on the 6N138. I'm getting different values now, 0 for cmdByte. So, hardware appears to be mangling bits. I'll find a way to capture MIDI messages and inspect the waveform before continuing with code work.
To answer questions, I have used 3 different MIDI processors that send standard MIDI program changes. This is interfaced with the circuit I built. I've been in electronics for over 35 years, I'm confident of the hardware build.
Also, what text am I sending back? The only MIDI connection is an input.
Thanks all, I'll report back when I know the MIDI bits are correct.
It's working. All I had to do was be smarter than the code. Which is a challenge for me.
I forgot to change the baud rate back to 31250 for hardware test. Scrambled bits I guess.
Anyway, from the original code when I started, if I had just made 2 changes, the baud rate and
changing the 1st if() statement from if(Serial.available() > 0) to if(Serial.available() > 1) or
if(Serial.available() ==2) then all would have been fine. I tested both of those repeatedly and had not one glitch.
So my final questions would be is if(Serial.available() ==2) the best test? Also it didn't matter if I used an array or just did Serial.read() twice consecutively. I've tested both repeatedly, and again, not a glitch. But would one method be preferable?
As an aside, I found the 4N28 worked more reliably and uses less external components.
Thanks again, all, I hang my head in shame!
changing the 1st if() statement from if(Serial.available() > 0) to if(Serial.available() > 1) or
if(Serial.available() ==2) then all would have been fine.
Until there was some hiccup, and a third byte arrived before you checked. Then, Serial.available() == 2 would never have been true again. >= 2 is the proper test.
ok, just for reference since this is a topic i found, when i searched for an arduino midi in implementation (other then via the midi-library).
grumpy mike's example at: MIDI Shield is only partially "right", it handles only 3 byte messages (and running status) correctly. however channel pressure and program change are two byte messages that the example fails to read in. here is an updated code snippet, that reads those messages as well and safely ignores sysex... i don't know if it is the best way to do it, but it works very well so i thought i post it here:
//some random cc numbers:
#define SAVE_CC 120
#define CHANNELMODE_CC 119
#define FULLLEGATO_CC 118
#define OCTAVE_CC 117
#define STICK_CC 116
//midi in variables
byte state = 0;
byte statusByte;
byte dataByte1;
byte dataByte2;
void setup() {
//start serial with MIDI baud rate 31250 or another standard speed for debugging
Serial.begin(31250);
}
void loop() {
// put your main code here, to run repeatedly:
MidiIn();
}
void MidiIn(){
if (Serial.available() > 0) {
// read the incoming byte:
byte incomingByte = Serial.read();
switch (state){
case 0:
if (incomingByte > 0x7F && incomingByte < 0xF0 ){ // this reads all statusbytes, realtime stuff is ignored
statusByte = incomingByte;
state=1;
}
if (incomingByte == 0xF0) state=3;
case 1:
// get first databyte
if(incomingByte < 0x80) {
dataByte1 = incomingByte;
//check for two byte messages...
if ((statusByte & 0xF0) == 0xC0 | (statusByte & 0xF0) == 0xD0) {
midiParser();
state = 0; //reset state machine
} else state = 2;
} else{
state = 0; // reset state machine as this should be a note number
}
break;
case 2:
// get second databyte
if(incomingByte < 128) {
dataByte2 = incomingByte;
}
midiParser();
state = 0; // reset state machine to start
break;
//stay here for sysex, wait for it to end...
case 3:
if (incomingByte == 0xF7) state=0;
if(incomingByte > 0x7F && incomingByte < 0xF0) {
statusByte = incomingByte;
state=1;
}
break;
}
}
}
void midiParser(){
switch (statusByte){
case 0xC0: //program change
//do something when a program change arrives, prg nr is dataByte1
break;
case 0xB0: //cc messages
switch (dataByte1) {
case SAVE_CC:
//do something here
break;
case CHANNELMODE_CC:
//do something else
case STICK_CC:
//again something on this cc
break;
case FULLLEGATO_CC:
//you get the idea
break;
case OCTAVE_CC:
//octaves!!!
break;
}
}
}
please don't use the other code (not grumpy mikes) posted in this thread, it does not work correctly, or only for very special use cases