Which mode of DLMS you are you using?
IEC 61107 or currently IEC 62056-21, was an international standard for a computer protocol to read utility meters. It is designed to operate over any media, including the Internet. A meter sends ASCII (in modes A..D) or HDLC (mode E) data to a nearby hand-held unit (HHU) using a serial port. IEC 62056
In your question you mentioned:
hex string
which means a string of hex characters using ASCII (modes A to D).
However, in the comments you mentioned:
void ReactivePower(unsigned char data[], int dataSize)
String reacP = String(data[dataSize - 5], HEX) + String(data[dataSize - 4], HEX);
which means an array of bytes obtained from a stream of bytes using HDLC (mode E).
ASCII (Modes A to D)
For ASCII (modes A to D), there is a simple transformation to get the digit value from the hex character.
ASCII
Hex -------------------- Digit
char Decimal Hexadecimal Op1 Low nibble Op2 Base 10
----- ------- ----------- ------- ---------- --- -------
'0' 48 30 & 0x0F 0 0
'1' 49 31 & 0x0F 1 1
'2' 50 32 & 0x0F 2 2
'3' 51 33 & 0x0F 3 3
'4' 52 34 & 0x0F 4 4
'5' 53 35 & 0x0F 5 5
'6' 54 36 & 0x0F 6 6
'7' 55 37 & 0x0F 7 7
'8' 56 38 & 0x0F 8 8
'9' 57 39 & 0x0F 9 9
'A' 65 41 & 0x0F 1 +9 10
'B' 66 42 & 0x0F 2 +9 11
'C' 67 43 & 0x0F 3 +9 12
'D' 68 44 & 0x0F 4 +9 13
'E' 69 45 & 0x0F 5 +9 14
'F' 70 46 & 0x0F 6 +9 15
'a' 97 61 & 0x0F 1 +9 10
'b' 98 62 & 0x0F 2 +9 11
'c' 99 63 & 0x0F 3 +9 12
'd' 100 64 & 0x0F 4 +9 13
'e' 101 65 & 0x0F 5 +9 14
'f' 102 66 & 0x0F 6 +9 15
- For characters '0' to '9', simply
AND
with 0x0F.
- For characters 'A' to 'F' and 'a' to 'f',
AND
with 0x0F and add
9.
char ch = '1';
byte b = 0;
if (ch >= '0' && ch <= '9')
{
b = ch & 0x0F;
}
else if ((ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
{
b = (ch & 0x0F) + 9;
}
It's also important to know whether the transformation was successful, e.g. the first character could be bad (either a null terminator or non hex character), so this needs to be tested for during the transformation. Also, there are four nibbles in an int16
so the transformation needs to quit when a maximum of four hex characters have been processed:
void setup()
{
pinMode(13, OUTPUT);
Serial.begin(115200);
}
void loop()
{
char string[] = "A";
//char string[] = "Waffle";
//char string[] = "ff9d";
int16_t the_int = 0;
int count = AsciiToInt16(string, the_int);
Serial.print(string);
Serial.print(", ");
if (count)
{
Serial.print("Ok, ");
Serial.print(count);
Serial.print(", ");
Serial.println(the_int);
}
else
{
Serial.println("Bad");
}
}
int AsciiToInt16(const char* ch, int16_t& value)
{
int count = 0;
byte nibble = 0;
// Stop after 4th nibble or null terminator.
for(int i = 0; i < 4 && ch[i] != 0; i++)
{
if (AsciiToNibble(ch[i], nibble))
{
value <<= 4;
value |= nibble;
count++;
}
else
{
break; // Stop after non hex digit.
}
}
return count;
}
bool AsciiToNibble(const char ch, byte& b)
{
if (ch >= '0' && ch <= '9')
{
b = ch & 0x0F;
return true;
}
else if ((ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
{
b = (ch & 0x0F) + 9;
return true;
}
return false;
}
Output
A, Ok, 1, 10
Waffle, Bad
ff9d, Ok, 4, -99
HDLC (Mode E)
For HDLC (mode E), simply do:
void ReactivePower(byte data[], int dataSize)
{
uint16_t reacP = (data[dataSize - 5] << 8) | data[dataSize - 4];
Serial.println(dataSize);
Serial.println(reacP);
}
. . .
byte data[] = { 1, 3, 5, 2, 4, 8 };
ReactivePower(data, sizeof(data));
Output
6
773