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 Answer 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:
The first statement,
byte dehex(char c);
is a function declaration for thedehex()
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 followsSendCTRL()
, adehex()
caller that needs to knowdehex()
'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.In this edit I added
const
beforechar *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.ccc_str()
webpage doesn't specify the return type ofc_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? Twochar *hin =input.c_str()
is giving me an invalid conversion error from const char* to char* how should I fix that?ATE-ENGE– ATE-ENGE2017年05月15日 12:25:03 +00:00Commented May 15, 2017 at 12:25 -
1@ATE-ENGE, see edit 2James Waldby - jwpat7– James Waldby - jwpat72017年05月15日 15:08:10 +00:00Commented May 15, 2017 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円\E400円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 aboveATE-ENGE– ATE-ENGE2017年05月15日 16:13:16 +00:00Commented May 15, 2017 at 16:13 -
1How 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– James Waldby - jwpat72017年05月15日 18:03:33 +00:00Commented May 15, 2017 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 :DATE-ENGE– ATE-ENGE2017年05月15日 18:31:59 +00:00Commented May 15, 2017 at 18:31
Explore related questions
See similar questions with these tags.