Sending an 8-bit Unsigned Char in Java using the Processing Language

In a Processing (which is basically Java) application that I am writing, I need to send a packet of data over the serial port that consists of unsigned bytes that are 8 bits long. In C programming, it would just be a matter of using unsigned char data types. Java or Processing does not have an unsigned char data type.

The requirements for the application are to send a packet of data from an array over the serial port. The first two bytes in the packet are header bytes, followed by various data and command bytes with a checksum at the end. The code already runs on an Arduino with the data packet stored in an array as follows:

byte tx_cmd[12] = { 0x55, 0xAA,             // header
                    0x01, 0x00,             // device ID
                    0x01, 0x00, 0x00, 0x00, // input parameter
                    0x01, 0x00,             // command code
                    0x02, 0x01              // checksum
                  };

In this Arduino code the byte type is an unsigned 8-bit byte  — equivalent of an unsigned char in C, as Arduino programming is basically C / C++.

Processing / Java Data Types Equivalent to C Unsigned Char

Two obvious candidates that look like they should be the equivalent of a C unsigned char are the Java char and Java byte data types. These two data types have the following problems:

Java Char

The Java char data type is 16 bits long. Trying to put the data packet to send over the serial port into a char array would send two 8-bit bytes for every char variable sent. The Java char is also intended for characters, so trying to pack two bytes into a char would be bad programming practice.

Processing char reference.

Java Byte

The Java byte data type is 8 bits long, however it is a signed data type, not unsigned. The range of the byte is from 127 to -128. This differs from the Arduino byte data type which is 8 bits long and unsigned with a range from 0 to 255 (same as a C unsigned char).

Processing byte reference.

Attempting to Use the Byte Data Type

The following Processing code shows the problem when using a data packet array of the byte data type.

void setup() {
  byte[] tx_cmd = new byte[12];
  tx_cmd[0] = 0x55;
  tx_cmd[1] = 0xAA;
  tx_cmd[2] = 0x01;
  tx_cmd[3] = 0x00;
  tx_cmd[4] = 0x01;
  tx_cmd[5] = 0x00;
  tx_cmd[6] = 0x00;
  tx_cmd[7] = 0x00;
  tx_cmd[8] = 0x12;
  tx_cmd[9] = 0x00;
  tx_cmd[10] = 0x13;
  tx_cmd[11] = 0x01;
  
  for (int i = 0; i < 12; i++) {
    println(hex(tx_cmd[i]));
  }
}

When trying to run the code in the Processing IDE, it gives the following error:

cannot convert from int to byte

Which refers to the code in the line:

tx_cmd[1] = 0xAA;

The problem is that the the hexadecimal value of AA which is the decimal value of 170 is too big for the positive range of the byte data type which can have a maximum value of 127. None of the other numbers assigned to the array elements give an error because they are all less than 127 decimal.

 Solution to Sending an Unsigned Byte Using Processing / Java

Although the byte data type is signed, it still stores binary values from 0000 0000b to 1111 1111b (or 0x00 to 0xFF hexadecimal) with the most significant bit (MSB) being the sign bit which is set when the number is negative. The code only treats the byte as negative because the data type of the language being used says that it is a signed number.

When data is sent over the serial port, it is sent as bytes. The device receiving the data bytes on the other side can choose whether to treat the bytes as signed or unsigned, so the solution to sending the bytes is to convert the data bytes with values above 127 to their negative equivalents.

This is actually done very simply by casting the input integer to the byte data type, which cuts off the top bytes of the integer and puts the least significant byte of the integer into the byte variable.

void setup() {
  byte[] tx_cmd = new byte[12];
  tx_cmd[0] = (byte)0x55;
  tx_cmd[1] = (byte)0xAA;
  tx_cmd[2] = (byte)0x01;
  tx_cmd[3] = (byte)0x00;
  tx_cmd[4] = (byte)0x01;
  tx_cmd[5] = (byte)0x00;
  tx_cmd[6] = (byte)0x00;
  tx_cmd[7] = (byte)0x00;
  tx_cmd[8] = (byte)0x12;
  tx_cmd[9] = (byte)0x00;
  tx_cmd[10] = (byte)0x13;
  tx_cmd[11] = (byte)0x01;
  
  // print the array in hexadecimal and decimal
  for (int i = 0; i < 12; i++) {
    print("0x");
    print(hex(tx_cmd[i]));
    print(" = ");
    println(tx_cmd[i]);
    
  }
  
}

The output from the above code:

0x55 = 85
0xAA = -86
0x01 = 1
0x00 = 0
0x01 = 1
0x00 = 0
0x00 = 0
0x00 = 0
0x12 = 18
0x00 = 0
0x13 = 19
0x01 = 1

The number stored in the byte array will be displayed as the desired value when printed as a hexadecimal number. When printed as an integer, it will be displayed as a negative value if it contains a number greater than 127.

As the code shows, it is much easier and more efficient to cast the input integer to a byte and truncate the integer, than to try and calculate the equivalent negative value for the input byte.