USB to ASCII using Hobbytronics USB Host board

I'm working on an application using the Hobbytronics USB Host board that translates a USB byte stream to an equivalent ASCII byte stream. So, for instance, you plug your USB keyboard into the Hobbytronics board, hit the F1 key, and the board outputs 0x1B, 0x3B on it's serial port.

For the special case of control keys, e.g. CTRL-A, the board puts out a null (0x00) followed by the appropriate byte, e.g. 0x01. So if you hit CTRL-A, the board sends 0x00, 0x01.

My question is, what does my Arduino do with the first byte, the null, when I do a Serial.read()?

It seems as though the Serial.read() simply discards the null and reads the next byte immediately, without the need for a second call. If that is the case, is there any way to alter that behavior?

Serial.read() does not discard nulls. The problem is in the code you didn't post.

A null character is a perfectly valid character to transmit and read over Serial. Why do you think Serial.read() skips over it? Please post your code that demonstrates this behavior.

Code I didn't post:

/*
  USB HOST BOARD - Keyboard Program
  HOBBYTRONICS Ltd 2012
  
  Simple Arduino sketch to show use of USB HOST board Keyboard software
  
 */
char flag_esc=0;

void setup() {
  Serial.begin(9600);
  Serial.println("Type some characters and i will send them back");
}

void loop() {

  if (Serial.available() > 0) {
    int inByte = Serial.read();  
    if(inByte == 27)
    {
       flag_esc = 1;
    }   
    else
    {
      if(flag_esc==1)
      {
        // Previous char was ESC - Decode all the escaped keys
        switch(inByte)
        {
            case 0x49:
              Serial.print("[PgUp]");
              break;
            case 0x51:
              Serial.print("[PgDn]");
              break;    
            case 0x47:
              Serial.print("[Home]");
              break;
            case 0x4F:
              Serial.print("[End]");
              break;     
            case 0x52:
              Serial.print("[Ins]");
              break;
            case 0x53:
              Serial.print("[Del]");
              break;               
            case 0x3B:
              Serial.print("[F1]");
              break;
            case 0x3C:
              Serial.print("[F2]");
              break;    
            case 0x3D:
              Serial.print("[F3]");
              break;
            case 0x3E:
              Serial.print("[F4]");
              break;     
            case 0x3F:
              Serial.print("[F5]");
              break;
            case 0x40:
              Serial.print("[F6]");
              break;          
            case 0x41:
              Serial.print("[F7]");
              break; 
            case 0x42:
              Serial.print("[F8]");
              break; 
            case 0x43:
              Serial.print("[F9]");
              break; 
            case 0x44:
              Serial.print("[F10]");
              break; 
            case 0x57:
              Serial.print("[F11]");
              break; 
            case 0x58:
              Serial.print("[F12]");
              break;     
            case 0x48:
              Serial.print("[Up]");
              break; 
            case 0x50:
              Serial.print("[Down]");
              break; 
            case 0x4B:
              Serial.print("[Left]");
              break; 
            case 0x4D:
              Serial.print("[Right]");
              break;         
            case 0x54:
              Serial.print("[Print]");
              break; 
            case 0x5B:
              Serial.print("[Windows]");
              break;   
            default:
              Serial.print("[?]");
              break;            
        }
        flag_esc=0;    
      }
      else
      {  

        if(inByte==1)
        {
           Serial.print("Control-A");
        }  
        else if(inByte==2)
        {
           Serial.print("Control-B");
        }
        else if(inByte==3)
        {
           Serial.print("Control-C");
        }
        else if(inByte==4)
        {
           Serial.print("Control-D");
        }
        else if(inByte==5)
        {
           Serial.print("Control-E");
        }
        else if(inByte==6)
        {
           Serial.print("Control-F");
        }
        else if(inByte==7)
        {
           Serial.print("Control-G");
        }
        // Dont decode 8 - backspace
        else if(inByte==9)
        {
           Serial.print("Tab");
        }
        // Dont decode 10 - Line Feed
        else if(inByte==11)
        {
           Serial.print("Control-K");
        }
        else if(inByte==12)
        {
           Serial.print("Control-L");
        }
        // Dont decode 13 - Carriage Return
        else if(inByte==14)
        {
           Serial.print("Control-N");
        }
        else if(inByte==15)
        {
           Serial.print("Control-O");
        }
        else if(inByte==16)
        {
           Serial.print("Control-P");
        }
        else if(inByte==17)
        {
           Serial.print("Control-Q");
        }
        else if(inByte==18)
        {
           Serial.print("Control-R");
        }
        else if(inByte==19)
        {
           Serial.print("Control-S");
        }
        else if(inByte==20)
        {
           Serial.print("Control-T");
        }
        else if(inByte==21)
        {
           Serial.print("Control-U");
        }
        else if(inByte==22)
        {
           Serial.print("Control-V");
        }
        else if(inByte==23)
        {
           Serial.print("Control-W");
        }
        else if(inByte==24)
        {
           Serial.print("Control-X");
        }
        else if(inByte==25)
        {
           Serial.print("Control-Y");
        }        
        else if(inByte==26)
        {
           Serial.print("Control-Z");
        }

        else   
        {
          // Its a normal key
          Serial.write(inByte);
        }  
      }
    }  
  }     
}

Perhaps I'm misinterpreting what I'm seeing on the serial monitor? If my arduino board reads the null and then writes it, what would I see on the serial monitor...nothing? So if I hit CTRL-A, I would get 0x00, 0x01, but all I would see is "Control-A" because the null was written but produced nothing visible on the monitor?

Since the nul character is 0, all your if() statements do not apply so it falls into the "normal key" clause. Writing that out to the Serial monitor will not display anything. I would add another if() statement before your Ctrl-A

//...
      else
      { 

        if(inByte==0)
        {
           Serial.print("Nul");
        } 
        if(inByte==1)
        {
           Serial.print("Control-A");
        } 
//...

It seems that my confusion may arrise from an incomplete (read "total lack of ") understanding of USB keyboard protocol. And, while it may be true, per the Hobbytronics website that:

Certain keys cannot be represented by the standard ascii codes. To represent the codes, a two-character
sequence is used. The first character is always an ASCII NUL (0). The second character and its
translations are listed in the following table. Some codes expand to multi-keystroke characters.

Control codes

Dec Hex ASCII Key
0 00 NUL (null) ctrl @
1 01 SOH (start of heading) ctrl A
2 02 STX (start of text) ctrl B
3 03 ETX (end of text) ctrl C
4 04 EOT (end of transmission) ctrl D
5 05 ENQ (enquiry) ctrl E
6 06 ACK (acknowledge) ctrl F
7 07 BEL (bell) ctrl G
8 08 BS (backspace) ctrl H
9 09 HT (horizontal tab) ctrl I
10 0A LF (line feed) ctrl J
11 0B VT (vertical tab) ctrl K
12 0C FF (form feed) ctrl L
13 0D CR (carriage return) ctrl M
14 0E SO (shift out) ctrl N
15 0F SI (shift in) ctrl O
16 10 DLE (data link escape) ctrl P
17 11 DC1 (device control 1) ctrl Q
18 12 DC2 (device control 2) ctrl R
19 13 DC3 (device control 3) ctrl S
20 14 DC4 (device control 4) ctrl T
21 15 NAK (negative acknowledge) ctrl U
22 16 SYN (synchronous idle) ctrl V
23 17 ETB (end of transmission block) ctrl W
24 18 CAN (cancel) ctrl X
25 19 EM (end of medium) ctrl Y
26 1A SUB (substitute) ctrl Z
27 1B ESC (escape) ctrl [
28 1C FS (file separator) ctrl
29 1D GS (group separator) ctrl [
30 1E RS (record separator) ctrl ^
31 1F US (unit separator) ctrl _

... this does not mean that your keyboard transmits two bytes when you hit CTRL-A.

What is, in fact, transmitted when you hit CTRL-A (ascii SOH - Start of Header) is the single byte, 0x01. The ASCII code for null (or NUL), which is 0x00, is not transmitted from your keyboard when you hit the CTRL-SHIFT-2 (or CTRL-@) keys. What gets transmitted in this case simply ignores the CTRL key and you get the single byte 0x40 (the @ sign).

So, in answer to my original question, the null character has not been lost, it was never there in the first place.