Serial port and pulse counter. Byte array (little-endian) to double precision

Hello.
At first I could not configure the data exchange with the pulse counter via usb-rs232.
Then I learned about the need to add a new environment variable in Windows (JAVA_TOOL_OPTIONS = -Dfile.encoding=ISO-8859-1)
The pulse counter must accept a string of hexadecimal values. After that he issues an answer.

val AskСoldChannel = ‘\u0000\u0006\u0026\u0017\u0001\u000E\u0001\u0000\u0000\u0000\u0041\u00CF\u002F\u00D6’

sendCommand(Serial_string,AskСoldChannel)

Next, I discard the insignificant part of the answer and leave only the value of the counter.
logInfo(“Demo”,Serial_string.state.toString.substring(PulsarAnswerValueStartAt,PulsarAnswerValueEndAt).bytes.toString)
The counter has a program from the manufacturer. It issues a query string and a response string. Here is the response string from this program:

[0, 0, 0, 0, 40, 68, E9, 40] - HEX
[0, 0, 0, 0, 64, 104, 233, 64] - DEC

But these values are given by this program. In Openhab, everything is different. In the Openhab log file I get the following response:
[0, 0, 0, 0, 64, 104, -23, 64] - DEC

-23??? What??? There must be 233!
-23 DEC = FFFF FFFF FFFF FFE9 in HEX ! In the end we see the desired E9! But why the value is negative?

Then I wrote down the new value in the counter. It is 1234567890
Then I read the counter value using the manufacturer’s program:
[00, 00, 80, B4, 80, 65, D2, 41] HEX

Then I read the value of the counter using serial binding:
[0, 0, -128, -76, -128, 101, -46, 65] DEC
-128? -76? -46?
Hmmmmm… -128 DEC = FFFF FFFF FFFF FF80 in HEX
-76 DEC = FFFF FFFF FFFF FFB4 in HEX
-46 DEC = FFFF FFFF FFFF FFD2 in HEX

What’s happening? Whence negative values? The counter accurately gives the correct values (checked with Arduino).

Only in 64 bit. In 16 bit, FFE9. It’s a matter of representation.

I wouldn’t worry about how the values get represented in the log, that doesn’t affect what value your Item(s) hold. What do you see there?

item.state.toString.getBytes might prove useful

logInfo(“Demo”,Serial_string.state.toString.substring(PulsarAnswerValueStartAt,PulsarAnswerValueEndAt).getBytes().toString)
Result: [0, 0, -128, -76, -128, 101, -46, 65]
I want to see [0, 0, 128, 180, 128, 101, 210, 65]

This is NOT a binding problem, it is just about handling a string of byte values (which is not particularly easy in OH)

Noo, that will never work with a byte array , its a Java array limitation

For display, try

	var bytearray = inputstring.getBytes("UTF-8")
	logInfo("test1", "rubbish " + bytearray.toString)
	val StringBuilder hexdisplay = new StringBuilder
	bytearray.forEach[b | hexdisplay.append(Integer::toHexString(b) + " ")]
	logInfo("test2", "hex " + hexdisplay)
	val StringBuilder decdisplay = new StringBuilder
	bytearray.forEach[b | decdisplay.append(Integer::toString(b) + " ")]
	logInfo("test3", "dec " + decdisplay)

Untested, but I think the decimal version works with bytes >128 ?

I ask you to excuse me, I’m just at the very beginning of studying Xtend. I received a number of errors when using your code. Here they are:

var bytearray = inputstring.getBytes(“UTF-8”)
Multiple markers at this line

  • The method getBytes(String) is undefined for the type StringItem

If you meant this:
var bytearray = Serial_string.getBytes(“UTF-8”)
Then there is such an error:
The method getBytes(String) is undefined for the type StringItem

bytearray.forEach[b | hexdisplay.append(Integer::toHexString(b) + " ")]
There is no context to infer the closure’s argument types from.
Consider typing the arguments or put the closures into a typed
context.

bytearray.forEach[b | decdisplay.append(Integer::toString(b) + " ")]
There is no context to infer the closure’s argument types from.
Consider typing the arguments or put the closures into a typed
context.

I don’t know what your string is called, so “inputstring” was meant as an obvious guess for you to substitute what you like. Its an example, not code written just for you…

Well, the code ran for me (in OH1). Designer complains about many things, most are valid but not all. Did Designer complain in similar ways about the logInfo you showed us earlier? What happens if you actually run the code?

If I run the code you submitted, I’ll get this log entry:
Rule ‘Otvet’: An error occured during the script execution: The name ’ .getBytes ()’ can not be resolved to an item or type.
For this code:
var bytearray = Serial_string.getBytes(“UTF-8”)
logInfo(“test1”, "rubbish " + bytearray.toString)
val StringBuilder hexdisplay = new StringBuilder
bytearray.forEach[b | hexdisplay.append(Integer::toHexString(b) + " ")]
logInfo(“test2”, "hex " + hexdisplay)
val StringBuilder decdisplay = new StringBuilder
bytearray.forEach[b | decdisplay.append(Integer::toString(b) + " ")]
logInfo(“test3”, "dec " + decdisplay)

Serial_string is I guess your Item of type string. It is not ‘just’ a string variable. You have to look at its .state to get at the string that it contains.

You had getBytes apparently working with it in your post number 3. Why not try out a similar method?

var bytesarray = Serial_string.state.toString.substring(PulsarAnswerValueStartAt,PulsarAnswerValueEndAt).getBytes()

I don’t know what the substring stuff does there, but presumably you do, and it yields the string part you are interested in.

rubbish [0, 0, -128, -76, -128, 101, -46, 65]
hex 0 0 ffffff80 ffffffb4 ffffff80 65 ffffffd2 41
dec 0 0 -128 -76 -128 101 -46 65
Why negative numbers exists?

I asked myself, and I will answer.
Because the type of byte in java has a range from -128 to 127.
Question. How do I modify the code so that I get values ​​greater than 127?
And I would still be very happy if someone told me how to convert this array of values into float32_t (IEEE 754).

It’s Java thing, java treats bytes as “two’s complement” numbers.

I’ve seen java tips that descrie how to recover the byte into a more conventional form by performing bitwise AND with 0xFF
I don’t know quite how to implement that in Xtend (Rules)

Does this counter of your speak Modbus or some other recognized serial protocol? You might be reinventing wheels here.

This works better, there are probably other neater ways
(again, you will have to introduce your own string)

	var testinput = "\u00a5\u0003\u0003\u0008\u0016\u0001\u00ca"
	var bytesarray = testinput.getBytes()
	logInfo("test1", "rubbish " + bytesarray.toString)
	val StringBuilder hexdisplay = new StringBuilder
	bytesarray.forEach[b |
		var int x = b.intValue.bitwiseAnd(255)
		hexdisplay.append(Integer::toHexString(x) + ", ")
		
	]
	logInfo("test2", "hex " + hexdisplay)
	val StringBuilder decdisplay = new StringBuilder
	bytesarray.forEach[b |
		var int x = b.intValue.bitwiseAnd(255)
		decdisplay.append(Integer::toString(x) + ", ")
		
	]
	logInfo("test3", "decimal " + decdisplay)

Cribbed from @Udo_Hartmann

1 Like

Many thanks! I’m very sorry for my impudence, but can I ask you to help me to convert an array of this values ​​to float32_t (IEEE 754)? ByteOrder.LITTLE_ENDIAN

I have no idea how to float a byte array in a rule. Mostly in OH this kind of activity is handled within a binding, or done by external script.
Googling throws up ideas like

Does this counter of your speak Modbus or some other recognized serial protocol? You might be reinventing wheels here.

The counter only has a proprietary protocol
And i am sorry. From manual:
…In the IEEE 754 (double64_t) format. The lowest byte forward.
I wrote to this counter 0. Read 0
wrote 1. Read F0 3F
wrote 2. Read 40
wrote 3. Read 08 40
wrote 4. Read 10 40
It is double precision format

And here I solved my question completely:
var bytesarray = Serial_string.state.toString.substring(PulsarAnswerValueStartAt,PulsarAnswerValueEndAt).getBytes()

var int order = (bytesarray.get(7).bitwiseAnd(255) << 4) +
(bytesarray.get(6).bitwiseAnd(255) >> 4)
var long mantisse = (bytesarray.get(6).bitwiseAnd(15)).longValue << 48
mantisse = (bytesarray.get(5).bitwiseAnd(255).longValue << 40) + mantisse
mantisse = (bytesarray.get(4).bitwiseAnd(255).longValue << 32) + mantisse
mantisse = (bytesarray.get(3).bitwiseAnd(255).longValue << 24) + mantisse
mantisse = (bytesarray.get(2).bitwiseAnd(255).longValue << 16) + mantisse
mantisse = (bytesarray.get(1).bitwiseAnd(255).longValue << 8) + mantisse
mantisse = bytesarray.get(0).bitwiseAnd(255).longValue + mantiss
var myresult = (1 + (mantisse / Math.pow(2,52))) * Math.pow(2,(order - 1023))
logInfo(“test1”,"" + Math.round(myresult))