Issue with writing to serial ports

First off, here's my code (at least the relevant parts):

http://codepad.org/neoUct7J

I'm using the hardware UART on an Uno (software 0022) in order to read in MIDI note on/off messages and convert them into a different form for another MIDI device. This mostly works, but there's one bug I can't seem to find.

If I send in the following MIDI message:

0x94 0x28 0x7F (MIDI note on, channel 4, note is 0x28, velocity is 0x7F)

I end up spitting out the following two messages after conversion:

0xF0 0x08 0x40 0x0A 0x01 0x06 0x07 0xF0 0x08 0x40 0x0A 0x05 0x06 0x07

The seventh byte (between the 0x06 and 0xF7) is missing (should be 0x28 in the first message and 0x7F in the second). Here's what I've tried to narrow the problem down:

  • Swapped the "channel" and "note/velocity" bytes in the array before I call Serial.write. No difference in the output (meaning the problem isn't with the position of the vars)

  • Set up the serial writes to send individual bytes instead of the whole array. No change.

  • Hardcoded the note/velocity. This works fine, but I obviously need different values in there.

Any ideas where to go from here?

Thanks in advance.

if (Serial.available() > 0)
  {
    recvBuffer[0] = Serial.read();

    if (recvBuffer[0] >= 0x90 && recvBuffer[0] < 0x96) // Note on
    {
      recvBuffer[1] = Serial.read();
      recvBuffer[2] = Serial.read();

If there is at least one character available, read all 3. Makes sense to me, I guess. Not!

I get all three bytes at the same time from the device I'm reading the data from. Reading has been flawless since day 1, it's the output that's not right.

In any event, I modified that portion to actually spin the processor until I get data:

recvBuffer[1] = blockSerialRead(); recvBuffer[2] = blockSerialRead();

byte blockSerialRead() { byte data = -1;

while (data == -1) data = Serial.read(); return data; }

with no change in the output.

On another note, if I wasn't getting the correct data in, I'd be sending bad data out. I'm not seeing that byte at all in the output.

Reading has been flawless since day 1, it's the output that's not right.

The output depends on the input. If the output is not right, one must assume that there is either a problem with the input or with the mapping of the input to the output. Since there is clearly a problem with collecting the input, and you don't seem inclined to properly fix it, I don't seem inclined to look at the mapping. Sorry about that.

I did fix it (see my edit above).

The proper fix is to do nothing until there are 3 values to read:

if(Serial.available() >= 3)
{
   // read the data
}

I have to read other messages, too, including a couple that are single bytes. If I get a 0x90-0x9F byte, I know that the next two immediately following it are the two that I'm looking for.

Again, this doesn't answer the question why writing 8 bytes (whether they're valid data or not) to a serial port results in only 7 bytes coming out the other side.

I know that the next two immediately following it are the two that I’m looking for.

But you don’t know that the serial hardware has actually received the next two characters and placed them into the receive buffer ready for your next two serial reads. You are making an assumption that based on timing may or may not be true. Hardware and software serial transactions on an Arduino are a byte by byte proposition. So either test that three characters are waiting (not the best method IMHO) or test for 1 or more available right before you do each serial read statement. To not heed that message is either folly or stubbornness on your part.

Lefty

The change I posted upthread fixes that problem, as it blocks until there's another byte to read and returns it.

Why does Serial.write(sendBuffer, 8) only send seven bytes?

Why does Serial.write(sendBuffer, 8) only send seven bytes?

Try something like this:

for(int i=0; i<8; i++)
{
   Serial.print("<");
   Serial.print(sendBuffer[i], HEX);
   Serial.println(">");
}

Is one of the bytes 0x00 (correctly or incorrectly)?

I changed the bitrate to 9600 and manually fed the system the bytes I’m receiving, as I only have the one serial port and can’t use SoftwareSerial without a MAX232 chip or equivalent (can I?). Here’s how I’m calling it:

//—

void loop()
{
int channel = 0;

sendNoteOn(0x06, 0x28, 0x7F);
// everything else, since the serial port is physically disconnected and I’m not sending anything, it’ll just be skipped)

//—

And here’s the output:

<8> <40> <1> <6> <28> <8> <40> <5> <6> <7F>

This all looks right. Any ideas how I might be able to debug this further?

and can't use SoftwareSerial without a MAX232 chip or equivalent (can I?).

Yes, you can. The same rules about voltage levels, etc. apply to software serial pins as apply to hardware serial pins. If your device can send data safely to the hardware port, it can send it safely to a software port. Use NewSoftSerial, though. SoftwareSerial is obsolete (until 0023, when SoftwareSerial will be replaced by NewSoftSerial).

In order to connect a softserial port to a PC, though, I'd need the level converter, right? I have plenty of experience with programming for serial ports, but still not quite up to par with the electrical part of it.

I'd prefer to use the UART for data transmission, but if I have to use the softserial for that and reserve the UART for debugging until I get this problem figured out, that's not a big deal.

Okay. I just wrote a quick program for a Netduino I had lying around to read in bytes and display them on the Debug output (so I can see what's going on).

When I plug the input device directly into the ND to see what the input is, I'm getting this:

Note on: 0x94 0x28 0x7F

Note on: 0x94 0x28 0x00

This is what I expect. When I look at the output of the Arduino, though, here's what it's reading in:

Note on: 0x94 0xFF 0xFF

Note on: 0x94 0xFF 0xFF

So it looks like the system is actually returning -1 for "no data available." That shouldn't be possible, though, since I'm using this to pull the data:

      recvBuffer[1] = blockSerialRead();
      recvBuffer[2] = blockSerialRead();

byte blockSerialRead()
{
  byte data = -1;

  while (data == -1) data = Serial.read();
  return data; 
}

Am I being an idiot and missing something obvious?

 byte data = -1;
   while (data == -1) data = Serial.read();

"byte" is defined as an unsigned char, IIRC. It's possible that it NEVER is equal to "-1" Try changing the -1 to 0xFF...

-1 as an unsigned char should underflow to 0xFF, though.

I ended up rewriting the function to this:

byte blockSerialRead()
{
  byte data = -1;
 
  while (Serial.available() < 1) {}
  return Serial.read();
}

Not optimal (I hate empty {} clauses), but at least it works. I’ll have to refactor it later.

Thanks for the help, everyone. This had been driving me nuts. :stuck_out_tongue:

The data type returned by Serial.read() is int, to allow it to return a non-valid value, when there is no data to read. In your previous, you could change the type of data to int, while still having the function return a byte. After getting a valid value from Serial.read(), return data, which will cause only the low byte to be returned, which is the byte that contains the bits of interest.

-1 as an unsigned char should underflow to 0xFF, though.

Yes, but an unsigned char doesn't cast to -1 when promoted. Since C specifies promotion to integers in expressions, if "foo" is an unsigned char with value 0xFF, then for

if (foo == -1)

foo promotes to 0x00FF and -1 is 0xFFFF, and they're NOT equal.

At least, that's my theory.