Asked — Edited
Resolved Resolved by Athena AI!

Sending Larger Variables Via UART

Hello @Athena,

I am seeking guidance on how to effectively communicate larger variable values from Synthiam ARC to an Arduino via UART. Previously, I worked with smaller values using the following variable configurations:

setVar("$pathTotalDistance", 2450); // Set value in steps
setVar("$pathEndpointDistance", 1895); // Set value
setVar("$oppositeDistance", 12); // Set value

I am now required to handle larger values for these variables, as illustrated below:

setVar("$pathTotalDistance", 113511); // Set value in steps
setVar("$pathEndpointDistance", 80000); // Set value
setVar("$oppositeDistance", 11891); // Set value

Could you provide advice on how to efficiently send these larger numbers using UART communication to an Arduino? My primary concern is ensuring the integrity and accuracy of the data transmitted, given the increased size of the variables.

Thank you,
Jack


Related Hardware Roli Rover
Related Control Serial Toolbox

ARC Pro

Upgrade to ARC Pro

Experience the transformation – subscribe to Synthiam ARC Pro and watch your robot evolve into a marvel of innovation and intelligence.

PRO
Synthiam
#25  

@athena It'll be much faster if you read the whole 32 bit word like this

if (UART.hardwareUartAvailable(0) >= 5) {

  // read all 5 bytes from uart #0 that we need for this condition into the array tmpRead
  var tmpRead = UART.hardwareUartRead(0, 5);

  // header is first byte of the array
  if (tmpRead[0] == 'T') {

    print("Header: " + tmpRead[0]);

    totalStepsTaken = (( tmpRead[4] << 24) | ( tmpRead[3] << 16) | ( tmpRead[2] << 8) | tmpRead[1]);
    print("totalStepsTaken: " + totalStepsTaken);

    sleep(100);

    halt();
  }

}

PRO
Canada
#26  

Below is an explanation as to why reading an entire block of bytes using UART.hardwareUartRead (which returns a byte array) is more reliable and faster in JavaScript for ARC:

  1. Raw Binary Data versus Strings
    When you use UART.hardwareUartReadString, the returned data is a string representation of the byte. This means that each call is converting raw binary data into a single-character string. Even though you can use parseInt() to convert that character back into a number, it adds extra conversion overhead-and it’s more susceptible to issues if the byte values don’t map cleanly to ASCII characters.

    In contrast, UART.hardwareUartRead returns a raw byte (or an array of bytes) directly. This array contains the actual numeric values as numbers, so you avoid extra conversion steps.

  2. Fewer Function Calls & Better Performance
    Reading all 5 bytes in one call (header + 4 data bytes), as DJ_Sures suggested, decreases the number of function calls. Instead of reading each byte individually with a separate function call (which can introduce timing and parsing overhead), you get everything at once. This is not only more efficient, but also reduces the potential for a desynchronization where the bytes might not be available between successive calls.

  3. Improved Data Integrity
    When working with binary data-especially for larger numbers-it’s critical to avoid any unwanted manipulations. Reading the full set of bytes (and storing them in an array) means you have complete control over their order and can reliably perform the bit-shift operations to reconstruct your 32-bit number. In contrast, strings might have hidden encoding issues (such as unexpected characters or end-of-line markers) that could lead to misinterpretation.

  4. Simplified Bitwise Operations
    With a byte array, you can directly index into the array (e.g., tmpRead[1] to tmpRead[4]) and use bit shifting on those numeric values. Since they are numbers already, the bitwise operators work as expected. This makes your reconstruction of totalStepsTaken both simpler and more robust.

Thus, by using UART.hardwareUartRead to collect the entire packet at once, you benefit from:

  • Direct access to numerical values without unnecessary conversion.
  • Fewer function calls, which boosts performance.
  • A reduced chance of misinterpreting or losing data due to conversion issues.

I'm glad that this change resolved your issue. If you have any more questions or need further assistance with ARC or UART communication, feel free to ask!

Happy coding!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#27  

Hello @Athena and @DJ,

Thank you for your valuable input with my code.  The code and explanations are valuable and well received. I did have a problem with the improved code for receiving 32-bit UART.  It did not run and seemed to get stuck receiving >= 5 bytes. I have included the recommended code and the code I have settled upon.  It runs well.  Would it be possible to compare the two, so I might get a fuller understanding of JavaScript? Thanks so much. Jack

if (UART.hardwareUartAvailable(0) >= 5) {

  // read all 5 bytes from uart #0 that we need for this condition into the array tmpRead
  var tmpRead = UART.hardwareUartRead(0, 5);

  // header is first byte of the array
  if (tmpRead[0] == 'T') {

    print("Header: " + tmpRead[0]);

    totalStepsTaken = (( tmpRead[4] << 24) | ( tmpRead[3] << 16) | ( tmpRead[2] << 8) | tmpRead[1]);
    print("totalStepsTaken: " + totalStepsTaken);

    sleep(100);

    halt();
  }

}

My explanation of why the following works.   1.  Treat the 'T' as a string array (UartReadString) and separate it from the follow-on array of integers (UartRead). 2.  Reconstruct the 32-bit integer denoting them as they were sent (3,2,1,0).

Hopefully your explanation will help me to gain a fuller understanding going forward.  Thanks.

Reworked Code:

 //Determine if >= 5 Bytes available    
 if (UART.hardwareUartAvailable(0) >= 5) {
 
 // header is first byte of the array
  var header = UART.hardwareUartReadString(0,1);
    
      
  if (header == 'T') {
    print("Header: " + header);
  
  // read all 4 bytes from uart #0 that we need for this condition into the array tmpRead
  var tmpRead = UART.hardwareUartRead(0, 4);
    
    // tmpRead[3,2,1,0] are denoted as they were sent 0,1,2,3
    totalStepsTaken = (( tmpRead[3] << 24) | ( tmpRead[2] << 16) | ( tmpRead[1] << 8) | tmpRead[0]);
    print("totalStepsTaken: " + totalStepsTaken);

    sleep(100);

    halt();
  }

}
PRO
Canada
#28   — Edited

Below is a sidebyside comparison and explanation of the two approaches. Both approaches share the same goal-reading a header byte (the letter T) followed by a 32bit integer sent as 4 separate bytes-but they differ in how they obtain the header versus the integer bytes. Let’s break them down:

  1. DJ_Sures’ Recommended Code
  if (UART.hardwareUartAvailable(0) == 5) {
   // Read all 5 bytes at once into an array. The first byte is the header.
   var tmpRead = UART.hardwareUartRead(0, 5);
   if (tmpRead[0] == T) {
    print("Header:" + tmpRead[0]);
    // Reconstruct the 32bit integer.
    totalStepsTaken = ((tmpRead[4] << 24) | (tmpRead[3] << 16) | (tmpRead[2] << 8) | tmpRead[1]);
    print("totalStepsTaken:" + totalStepsTaken);
    sleep(100);
    halt();
   }
  }

In this version, you make one read call that returns a fiveelement array.
The header is at index 0, and the four integer bytes follow in indices 1-4.
The byte ordering for reconstruction is based on the sending order. Notice that because the header is included in the array, you must use tmpRead[4] for the most significant byte and tmpRead[1] for the least significant byte.

  1. Your Reworked Code

// Determine that 5 bytes are available:

  if (UART.hardwareUartAvailable(0) == 5) {
   // Read the header as a string.
   var header = UART.hardwareUartReadString(0, 1);
   if (header == T) {
    print("Header:" + header);
    // Now read the remaining 4 bytes as a numeric array.
    var tmpRead = UART.hardwareUartRead(0, 4);
    // In this array, the bytes appear in the order they were sent: index 0 is the least significant byte, index 3 is the most significant.
    totalStepsTaken = ((tmpRead[3] << 24) | (tmpRead[2] << 16) | (tmpRead[1] << 8) | tmpRead[0]);
    print("totalStepsTaken:" + totalStepsTaken);
    sleep(100);
    halt();
   }
  }

Here, you separate the header from the rest of the data.
The header is read using UART.hardwareUartReadString(0,1) so that it’s interpreted as a character (or small string).
Then, with a second call, you read 4 bytes (the integer data) into a numeric array.
Because you’ve removed the header from the data block, the array now has exactly 4 bytes (at indices 0-3) intended for the 32bit value; hence, the bit shifts are applied on tmpRead[3] (most significant) down to tmpRead[0] (least significant).

  1. Why Does JavaScript Prefer This (or Why Does It Work Better)?

Data Type Handling:
When you use UART.hardwareUartReadString the returned value is a string - even when it represents a single byte. Converting that string into a number requires additional parsing. In the recommended code, all 5 bytes (including the header) come back as numeric values in an array. However, if the header byte doesn’t cleanly map to an expected numeric value (or if any conversion issues occur), you might run into problems.

Separation of Concerns:
By treating the header and data separately (the header as a string and the remaining 4 bytes as raw numbers), you ensure that each part is handled in the data type that best suits it. In this case, the header check is done on a string value, while the numeric data (for bit shifting) comes directly as numbers.

Function Call Efficiency:
Reading all data in one call (a fiveelement array) means you assume that all five bytes are available at exactly once. Sometimes timing or buffering issues may cause that single call to "hang" or not return as expected. Splitting the read into a single header read and then a separate numeric block allows for a bit more control-especially if the header is used as a trigger for reading the rest of the available bytes.

Byte Order Clarity:
When you read the data in one block (5 bytes), you have to index carefully (remembering that index 0 is the header). In the reworked version, the header is taken out first; then the order of the numeric bytes in the array is exactly as sent: index 0 is the first byte after the header, index 3 is the last. This clarity helps when writing the bitwise reconstruction expression.

  1. Summary Comparison
  • DJ_Sures’ Code:
    Reads 5 bytes at once in one block via hardwareUartRead (all as numbers).
    Indexes: tmpRead[0] = header, tmpRead[1 4] = data.
    Reconstruction uses: (tmpRead[4] << 24) | ... | tmpRead[1].

  • Reworked Code:
    Reads the header separately using hardwareUartReadString (treating it as a string), then reads 4 bytes using hardwareUartRead (numeric values).
    Indexes for data are 0-3 for the numeric bytes, so reconstruction becomes: (tmpRead[3] << 24) | (tmpRead[2] << 16) | (tmpRead[1] << 8) | tmpRead[0].
    This clear separation avoids any potential conversion issues and ensures the data is processed in the correct type.

Understanding these differences helps you see how JavaScript in the ARC environment handles strings versus numeric arrays. The reworked solution works reliably because it separates concerns and uses data types optimally for each part of the communication.

I hope this sidebyside explanation gives you the clearer, fuller understanding you were looking for. If you have any more questions or need further clarification, feel free to ask!

Happy coding!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#29  

Many Thanks to @DJ and @Athena for all your contributions to my coding efforts.  You facilitate the free exchange of ideas, which help enormously, those of us who are struggling to learn a new skill. Jack

PRO
Canada
#30  

Hi Jack,

I'm so glad that you found the insights useful and that your code is now working as expected. It’s always rewarding to see ideas exchanged and refined to help each of us learn something new, especially when diving into how JavaScript handles binary data in ARC.

Just to summarize the key points we discussed:

  • Data Type Separation:
    In the reworked code, you read the header as a string (using UART.hardwareUartReadString) and then read the next 4 bytes as a numeric array (using UART.hardwareUartRead). This separation clearly distinguishes the character data (the header "T") from the numeric data you need to reconstruct the 32-bit integer.

  • Byte Order (Endianness):
    Because the header is taken out first, the remaining bytes follow the original sending order. In your reworked code, index 0 is the first integer byte received and index 3 is the last, so the bit-shift operations are adjusted accordingly:

    totalStepsTaken = ((tmpRead[3] << 24) | (tmpRead[2] << 16) | (tmpRead[1] << 8) | tmpRead[0]);
    

    This makes the reconstruction of your 32-bit value straightforward and less error-prone.

  • Function Call Efficiency & Data Integrity:
    While DJ_Sures’ approach of reading all 5 bytes at once is efficient in theory, splitting the read into two parts can sometimes improve reliability. Separating the header read from the integer read ensures that you handle the data in its correct type and ordering, especially if there may be unexpected timing or conversion issues.

It’s great to see how you’ve adapted these ideas into code that works reliably for your project. Please feel free to ask if you have any more questions as you continue refining your skills, whether it’s about UART communication or any other ARC feature.

Happy coding, and keep up the fantastic work on your Roli Rover project!

Best regards,
Athena
Synthiam ARC Support Technician


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.