Language: EN

array-puerto-serie-arduino

Send or receive an array through the serial port in Arduino

We continue with this series of entries aimed at mastering the serial port of processors such as Arduino. In the previous entry, we saw how to send and receive a sequence of bytes. We agreed to delve into generating and processing the bytes. For this, in this post we will see how to send and receive an array through the serial port directly as bytes.

When sending or receiving an array through the serial port, we are working with a sequence of data of the same type (int, float). In the next entry, we will generalize the process by seeing how to send or receive any grouping of data, in the form of an object or structure.

Despite being a particular case of the next entry, sending and receiving the array through the serial port is interesting both because it is a common need, and because it allows us to illustrate and present the conversion to bytes that we will use in the case of sending structures.

Sending the array through the serial port directly as bytes is much more efficient than sending them as text with a separator, as we saw in the entry send an array separated by commas. It sends less data, it is more efficient because it avoids having to perform conversions, and the code is simpler.

Remember what was mentioned in the previous entry regarding the differences in the number of bytes that different processors use to store different variable types.

Sending and receiving individual elements

The first method we are going to send the individual elements. In the example, we are going to send an array of int. For brevity, the array in the example is defined but empty. In a real case, logically, this array would contain the data we want to send.

The code is quite simple. The sender goes through the array, and uses a function to send the bytes that make up each of the data.

const size_t dataLength = 10;
int data[dataLength];
 
void setup()
{
   Serial.begin(9600);
  
   for(int n = 0; n < dataLength; n++)
   {
      sendBytes(data[n]);
   }
} 
 
void loop() 
{ 
}
 
void sendBytes(uint16_t value)
{
  Serial.write(highByte(value));
  Serial.write(lowByte(value));
}

On the other hand, the receiver receives the bytes, uses another function to reassemble the data from the bytes, and stores them in the array.

const size_t dataLength = 10;
int data[dataLength];
int dataIndex = 0;
 
void setup()
{
  Serial.begin(9600);
} 
 
 
void loop()
{   
  if(Serial.available() >= sizeof(data[0])
  {
    byte byte1 = Serial.read();
    byte byte2 = Serial.read();
    data[dataIndex] = byteToInt(byte1, byte2);
 
    dataIndex++;
    if(dataIndex >= dataLength)
    {
      dataIndex = 0;
    }
  } 
} 

uint16_t byteToInt(byte byte1, byte byte2)
{
   return (uint16_t)byte1 << 8 | (uint16_t)byte2;
}

The main advantage of the method is that it allows us great flexibility in the process since we control it manually. For example, we can use the values to take actions without having to wait for the entire array.

However, in general, it is not something we are going to miss. Normally we want to work with a fixed frame, that is, we do not want to take action until we have chosen the complete array.

Sending and receiving with direct cast

Now let’s see how the sending and receiving would be done working directly with bytes. To send the array, we simply cast it to byte* and send the number of bytes in the array.

const size_t dataLength = 10;
int data[dataLength];

void setup()
{
  Serial.begin(9600);
  Serial.write((byte*)data, dataLength * sizeof(data[0]));
} 

void loop()
{
}

The reception is just as simple, we simply pass the int array as a byte* to the read() function, and in the reception the array will be overwritten with the received values.

const size_t dataLength = 10;
int data[dataLength];
 
void setup()
{
  Serial.begin(9600);
} 

void loop()
{   
  if(Serial.available() >= dataLength * sizeof(data[0]))
  {
    Serial.readBytes((byte*)data, dataLength * sizeof(data[0]));
  } 
} 

The advantage of the method is evident. Faster, more comfortable, simple (and more “pro”). Disadvantages, as we have commented, we have to wait to receive all the bytes. Note that the condition has changed and the reception is not performed until all the bytes of the frame have been received.

Therefore, we have as a constraint the size of the serial port buffer, which in the case of Arduino Uno and Nano is 64 bytes. If a larger frame is needed, we can either change the value in the HardwareSerial.h library or, more recommended, divide the transmission into several packets.

On the other hand, again, keep in mind the possible differences in the size in bytes of the different types of variables.

In the next entry, we will see a generalization of the case for sending arbitrary groupings of data in the form of structures or objects.

Download the code

All the code in this post is available for download on Github. github-full