0

I'm writing code that I discussed in a previous post. I now have a setup where I send my Arduino an ASCII-encoded Hex String that represents the bytes I want to write to my serial device. I think I've got most the code figured out other than the way to best convert the hex string for sending

This Code is working

void SendCTRL (String &input) 
{
  const char *hin = input.c_str();    // Get character array
  int clen = input.length()/2;
  unsigned char cmd[clen+1]; // Leave a byte for null terminator
  for (int i=0; i < 2*clen; i+=2)
  {
    cmd[i/2] = dehex(hin[i])<<4 | dehex(hin[i+1]);
  }
  cmd[clen] = 0;                     // Null-byte terminator
  digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit   
  for (int i=0; i< clen; i++)
  {
  RS485Serial.write(cmd[i]);   // Send string someplace
  }
  delay(clen/4);
  digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit
  Serial.println();

}

byte dehex(char c) 
{
  // Get nibble value 0...15 from character c
  // Treat digit if c<'A', else letter
  return c<'A'? c & 0xF : 9 + (c & 0xF);
}

From what I understand something along the lines of char ISS = SubS.GetBytes(), but guidance would be appreciated

1

What your code is aiming at isn't particularly clear to me, but I will assume that for some obscure reason you want to accept an input String containing a series of ASCII-encoded hex digits, and create an array of half that many bytes with reconstituted byte values.

First, here are several criticisms of the code shown.

It's likely length(input) won't compile but that input.length() will.

The expression input.substring((2*i),(2*i)+1)) is a String of one character. If you want two characters, use input.substring((2*i),(2*i)+2)). And use i += 2 instead of i++ in the for loop.

I don't understand String CMD = CMD+ISS. I won't say it's invalid, as I don't know without more research. But it looks like it's declaring variable CMD and using CMD in the initialization of CMD. [In the example code I give below, I assume CMD should begin as an empty string and be filled up with reconstituted bytes.]

RS485Serial.write(input); // Send byte to Remote Arduino looks mis-commented; presumably it will write out all of the input, not just a byte.

Using a timed delay for holding RS485 Transmit open, as in delay(10); digitalWrite(SSerialTxControl, RS485Receive); // Disable RS485 Transmit
may cause problems. On the one hand, it may usually waste five to ten milliseconds of time; on the other, it might occasionally disable RS485 transmit before transmission finishes. Instead, look for a flag to test for transmission complete, or look for a buffer-fill count, or compute how long to wait, etc.

The next paragraph describes some reasons that Strings may perform badly in this sketch. For additional discussion of Arduino String objects, see arduino.cc's String page, and see Majenko's The Evils of Arduino Strings page.

Each of the String-object operations in the code creates a new String. New String objects SubS and CMD are created and destroyed in each pass of the for loop. The object headers (with type data and member variables, such as a buffer pointer and buffer size) can be stack-allocated, which is harmless (does not leak or fragment memory) but takes some time. Each String object's character-buffer is heap-allocated: see code in cores/arduino/ file WString.cpp. Heap fragmentation can and will occur. ... In brief: In this application where using String objects is clunky and in the way, you might as well use ordinary C strings instead of String objects.


Ok, with criticisms out of the way, here's some example code that converts ASCII-encoded hex digits into a command string. Note, as I don't know the specs for RS485Serial.write() or how it determines how much to send, for demonstration purposes I've supposed it can handle a C string.

// Function declaration for dehex()
byte dehex(char c); // Get nibble value 0...15 from character c

void SendCTRL (String input) {
  const char *hin = input.c_str();  // Get character array
  int clen = input.length/2;
  // Next line invalid in C++, ok in C99.  Probably need to
  // instead declare a fixed-length array, cmd[MAXCMDLEN], etc
  char cmd[clen+1]; // Leave a byte for null terminator
  for (int i=0; i < 2*clen; i+=2) {
    cmd[i/2] = dehex(hin[i])<<4 | dehex(hin[i+1]);
  }
  cmd[clen] = 0;                     // Null-byte terminator
  digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit   
  RS485Serial.write(cmd);   // Send string someplace
  delay(clen/4);        // Wait for write to complete? What rate?
  digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit
}

byte dehex(char c) { // Get nibble value 0...15 from character c
  // Treat digit if c<'A', else letter
  return c<'A'? c & 0xF : 9 + (c & 0xF);
  // Above assumes that c is a 'hex digit' in 0...9, A or a ... F or f.
  // It would make more sense to just use 16 consecutive characters,
  // like eg 0123456789:;<=>? or @ABCDEFGHIJKLMNO so the above
  // could just say `return c & 0xF;`
}

Edit 2:

  1. The first statement, byte dehex(char c); is a function declaration for the dehex() function. It tells the C/C++ compiler the datatypes that the function accepts and returns. Note, instead of a function declaration at that point, one could instead put the function definition itself (which in the above follows SendCTRL(), a dehex() caller that needs to know dehex()'s datatypes). Some versions of the Arduino IDE appear to work ok without function declarations ahead of use, but almost all other C/C++ environments will not.

  2. In this edit I added const before char *hin = input.c_str(); because ATE-ENGE reports “char *hin =input.c_str() is giving me an invalid conversion error from const char* to char*”. That error indicates the left-hand-side variable has a datatype different from that of the right-hand-side expression and an automatic conversion isn't available. Note, unfortunately the arduino.cc c_str() webpage doesn't specify the return type of c_str().

Edit 3: Changed for (int i=0; i < clen; i+=2) to for (int i=0; i < 2*clen; i+=2) per notes in another question's answer.

Edit 4: Changed cmd[i/2] = dehex(hin[i])<<4 + dehex(hin[i+1]) to cmd[i/2] = dehex(hin[i])<<4 | dehex(hin[i+1]) to avoid an operator precedence problem. The precedence of +, addition, is higher than that of <<, bitwise shift, which in turn is higher than that of |, bitwise OR. The unparenthesized expression with + adds 4 to dehex(hin[i+1]) and uses the total as a shift count; not at all as desired. Note, one can of course use parentheses to control the problem, as in the expression (dehex(hin[i])<<4) + dehex(hin[i+1])

  • This looks like what I want, thanks a lot! I have two questions. One, What does calling byte dehex(char c); at the beginning do? Two char *hin =input.c_str() is giving me an invalid conversion error from const char* to char* how should I fix that? – ATE-ENGE May 15 '17 at 12:25
  • 1
    @ATE-ENGE, see edit 2 – James Waldby - jwpat7 May 15 '17 at 15:08
  • Ok! that all seems to be working now. I seem to not be getting the same input and output though. If you could give me your thoughts on what might be going wrong. As an Example, the input 010307E40001C549 should return me (as ASCII \code) \01\03\07\E4\00\01\C5I or (as U8) [1,3,7,228,0,1,197,73] Instead I'm getting (as ASCII) \100~@\EE or (as U8) [16,48,126,64,238] Updated code is posted above – ATE-ENGE May 15 '17 at 16:13
  • 1
    How do you know the input the Arduino gets is 010307E40001C549, and the output the Arduino sends [1,3,7,228,0,1,197,73]? Or is that what you sent to the Arduino, and received from it somewhere? Do you have a way for the Arduino to display debugging output? (All of that is info I'd need as basis for what to do next) – James Waldby - jwpat7 May 15 '17 at 18:03
  • Got it working, the counting was off in two different places. I have working code posted above now. If you see any improvements though, I'd like to know :D – ATE-ENGE May 15 '17 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.