EDITED & SOVLED (below)
I'm using Java for Android trying to send the byte 255 (0xFF in WriteSingleCoil function) to a ModBUS server device.
Device is not runnig, I don't know if because of not able to interpretate the signed byte -1 or because of I'm wrong calculating the CRC.
I don't know how to calculate CRC for negative bytes.
Summarizing: I don't know how to send function 05 Write Single Coil with 0xFF value for switch on the coil for from Java to ModBUS server.
Can anyone help me please?
SOLUTION:
" iIndex = ucCRCLo ^ b: operations like this must be written as iIndex
= (ucCRCLo ^ b)&0xff because the & will cast ucCRCLo, b and the result to int, which is 32 bits while short is 16 so you will have a lot of
extra bits set to 1 "
This answer helped me. Thanks a lot to TheDayOfcondor
But also my huge problem was the usual problem in Java with signed bytes. My CRC calculating function is doing it right for unsigned bytes, but it give errors if I pass inside signed bytes.
The trick for work with bytes for ModBUS comunication is work in the whole App with shorts as bytes, for have the range 0-255, even calculating trames and CRC. And only in the last step, when sending trame to ModBUS sever, cast them to bytes again. This is running.
Hope it will helps to someone in future.
EXPLAINING PROBLEM:
I'm trying to set ON a coil to ModBUS with function 05, this is explaining of function:
Request
I'm tryiing to set ON the coil on address 1:
This hex: 0A 05 00 01 ff 00 DC 81
This byte array: 10 5 0 1 255 0 220 129
10: The Slave Address (10 = 0A hex)
05: The Function Code (Force Single Coil)
0001: The Data Address of the coil. (coil# 1 = 01 hex)
FF00: The status to write ( FF00 = ON, 0000 = OFF )
DC81: The CRC (cyclic redundancy check) for error checking.
The thing is that Java is using signed bytes, so I can't put 255 on my byte array.
I understand I should put -1, but then I can't calculate CRC correctly, because of I have a couple of precalculated array of bytes for get the CRC but the function send a negative index.
So: I don't know if I'm doing right trying to send -1, if I have an alternative for sending 255, neither how to calculate CRC for -1.
This is function for calculate CRC:
public short[] GenerateCRC (byte[] pMsg) {
short ucCRCHi = 0xFF;
short ucCRCLo = 0xFF;
int iIndex;
for (byte b : pMsg)
{
iIndex = ucCRCLo ^ b;
try {
ucCRCLo = (short)(ucCRCHi ^ aucCRCHi[ ( iIndex ) ]);
ucCRCHi = aucCRCLo[ ( iIndex ) ];
} catch (Exception e) {
Log.e(LOGTAG, "GenerateCRC: " + e.toString(), e);
e.printStackTrace();
}
}
short[]result= new short[2];
result0]= ucCRCHi;
result1]= ucCRCLo;
return result;
}
The question is not very clear - however, the most common problem dealing with bytes is the fact Java does not have unsigned bytes, and boolean operation are always between int
The best way to deal with bytes is to use integers, and-ing every operation with 0xff. Also use >>> for the shift right (it is the unsigned version)
Example:
byte b= (byte)(255 & 0xff) // gives you the "unsigned byte"
byte b= (byte) ((b<<2)0xff ) // shift left must be truncated
If you post your code to calculate the CRC I can have a look into it
The best way to define a byte array without using negative numbers is like this:
byte[]={ (byte)0xff, (byte)0xff, (byte)0xff };
Related
I'm coding a personal project in Java right now and have recently been using bit operations for the first time. I was trying to convert two bytes into a short, with one byte being the upper 8 bits and the other being the lower 8 bits.
I ran into an error when running the first line of code below.
Incorrect Results
short regPair = (short) ( (byte1 << 8) + (byte2) );
Correct Results
short regPair = (short) ( (byte1 << 8) + (byte2 & 0xFF) );
The expected results were: AAAAAAAABBBBBBBB, where A represents bits from byte1 and B represents bits from byte2.
Using the 1st line of code I would get the typical addition between a bit-shifted byte1 with byte 2 added to it.
Example of incorrect results
byte1 = 11, byte2 = -72
result = 2816 -72
= 2744
When using the line of code which produces the expected results I can get the proper answer of 3000. I am curious as to why the bit-masking is needed for byte2. My thoughts are that it converts byte2 into binary before the addition and then performs binary addition with both bytes.
In the incorrect case, byte2 is promoted to an int because of the + operator. This doesn't just mean adding some zeros to the start of the binary representation of byte2. Since integer types are represented in two's complement in Java, 1s will be added. After the promotion, byte2 becomes:
1111 1111 1111 1111 1111 1111 1011 1000
By doing & 0xFF, you force the promotion to int first, then you keep the least significant 8 bits: 1011 1000 and make everything else 0.
Print the intermediate value directly to see what is going on. Like,
System.out.printf("%d %s%n", ((byte) -72) & 0xFF, Integer.toBinaryString(((byte) -72) & 0xFF));
I get
184 10111000
So the correct code is actually adding 184 (not subtracting 72).
So I totally forgot that byte is singed in Java, therefore when performing math with a variable of this data type it will take the signed interpretation and not the direct value of the bits. By performing byte2 & 0xFF, Java converts the signed byte value into an unsigned int with all but the first 8 bits set as 0's. Therefore you can perform binary addition correctly.
signed byte value 0x11111111 = -1
unsigned byte value 0x11111111 = 255
In both cases byte values are promoted to int in the expression when
it is evaluated.
byte byte1 = 11, byte2 = -72;
short regPair = (short) ( (byte1 << 8) + (byte2) );
(2816) + (-72) = 2744
And even in below expression byte is promoted to int
short regPair = (short) ( (byte1 << 8) + (byte2 & 0xFF) );
2816 + 184 = 3000
Here in this expression there is no concatenation of two bytes like it has been expressed in the above question- AAAAAAAABBBBBBBB, where A represents bits from byte1 and B represents bits from byte2.
Actually -7 & 255 gives 184 which is added to 2816 to give the output 3000.
I'm implementing variable lenght encoding and reading wikipedia about it. Here is what I found:
0x00000080 0x81 0x00
It mean 0x80 int is encoded as 0x81 0x00 2 bytes. That what I cannot understand. Okay, following the algorithm listed there we have.
Binary 0x80: 00000000 00000000 00000000 10000000
We move the sign bit to the next octet so we have and set to 1 (indicating that we have more octets):
00000000 00000000 00000001 10000000 which is not equals to 0x81 0x00. I tried to write a program for that:
byte[] ba = new byte[]{(byte) 0x81, (byte) 0x00};
int first = (ba[0] & 0xFF) & 0x7F;
int second = ((ba[1] & 0xFF) & 0x7F) << 7;
int result = first | second;
System.out.println(result); //prints 1, not 0x80
ideone
What did I miss?
Let's review the algorithm from the Wikipedia page:
Take the binary representation of the integer
Split it into groups of 7 bits, the group with the highest value will have less
Take these seven bits as a byte, setting the MSB (most significant bit) to 1 for all but the last; leave it 0 for the last one
We can implement the algorithm like this:
public static byte[] variableLengthInteger(int input) {
// first find out how many bytes we need to represent the integer
int numBytes = ((32 - Integer.numberOfLeadingZeros(input)) + 6) / 7;
// if the integer is 0, we still need 1 byte
numBytes = numBytes > 0 ? numBytes : 1;
byte[] output = new byte[numBytes];
// for each byte of output ...
for(int i = 0; i < numBytes; i++) {
// ... take the least significant 7 bits of input and set the MSB to 1 ...
output[i] = (byte) ((input & 0b1111111) | 0b10000000);
// ... shift the input right by 7 places, discarding the 7 bits we just used
input >>= 7;
}
// finally reset the MSB on the last byte
output[0] &= 0b01111111;
return output;
}
You can see it working for the examples from the Wikipedia page here, you can also plug in your own values and try it online.
Another Variable length encoding of integers exists and are widely used. For example ASN.1 from 1984 does define "length" field as:
The encoding of length can take two forms: short or long. The short
form is a single byte, between 0 and 127.
The long form is at least two bytes long, and has bit 8 of the first
byte set to 1. Bits 7-1 of the first byte indicate how many more bytes
are in the length field itself. Then the remaining bytes specify the
length itself, as a multi-byte integer.
This encoding is used for example in DLMS COSEM protocol or https certificates. For simple code, you can have a look at ASN.1 java library.
I'm reading a file into a byte array in chunks and sending it over the network via a POST request to a webserver. It's not anything complicated, I've done it before using this exact same code. This time, I noticed that my images are looking really odd when they get to the server, so I decided to look at the byte array being sent and the one being received just to make sure it was the same. It's not. On the java sending side the byte array contains negative numbers. On the C# receiving side, there are no negative numbers.
The first 15 bytes on the receiving side (C#)
137
80
78
71
13
10
26
10
0
0
0
13
73
72
68
Those same bytes but on the sending side (java)
-119
80
78
71
13
10
26
10
0
0
0
13
73
72
68
All of the non-negative numbers are the same, and the -119 isn't the only negative number, they are all over. I did notice that -119 and 137 are 256 apart and wondered if that has something to do with it.
The code I'm using to read the image (java)
public static byte[] readPart(String fileName, long offset, int length) throws FileNotFoundException, Exception
{
byte[] data = new byte[length];
File file = new File(fileName);
InputStream is = new FileInputStream(file);
is.skip(offset);
is.read(data,0,data.length);
is.close();
return data;
}
The code I'm using to write the data (c#)
private void writeFile(string fileName, Stream contents)
{
using (FileStream fs = new FileStream(fileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
{
int bufferLen = 65000;
byte[] buffer = new byte[bufferLen];
int count = 0;
while ((count = contents.Read(buffer, 0, bufferLen)) > 0)
{
fs.Write(buffer, 0, count);
}
fs.Close();
}
contents.Close();
}
I don't know if that is something that always happens and I just never noticed it before or if it is something that decided to go horribly wrong. What I do know is that this code worked before for something very similar and that it's not working now.
If anyone has any suggestions or an explanation I would really appreciate it.
EDIT:
The reason my images were looking odd is how I was calling the readPart method.
byte[] data = FileUtilities.readPart(fileName,counter,maxFileSize);//counter is the current chunk number
How I should have been calling it
byte[] data = FileUtilities.readPart(fileName,counter*maxFileSize,maxFileSize);//the current chunk * cuhnksize for the offset...
Thanks everyone, I'm significantly less confused now :)
In Java, byte is a signed value (using two's complement to encode negative values), so what you see it correct if unexpected by most people.
To convert a byte to an unsigned int value, use b & 0xff
Java doesn't have unsigned bytes; all bytes are treated as signed. That's all.
All that really matters is how you think of the bytes, since you rarely ever actually need to do comparisons on bytes. The only significant difference is that they print out as signed, as you've discovered.
If you like, you can use e.g. Guava's UnsignedBytes utilities to view Java bytes as unsigned, but there's really not much practical difference.
As a further explanation, assume you have 137 as an unsigned byte. That is represented as:
1000 1001
This binary value, when expressed as a signed two's complement number, turns out to be -119. (-128 + 9)
Any unsigned byte values over 128 will be affected by the difference since the left-most bit is used in this way by the two's complement scheme.
Maybe it has something to do with the fact that Java's byte is signed (range -128 to 127) while C#'s is unsigned (0 to 255) :). The information is the same in binary, it's just interpreted differently.
The range of byte is from -128 to 127, so if you try to assign a byte 128, it will cycle around and the result will be -128.
System.out.println("Max val = " + Byte.MAX_VALUE); //prints: Max val = 127
System.out.println("Min val = " + Byte.MIN_VALUE); //prints: Min val = -128
System.out.println("(byte)137 = " + (byte)137); //prints: (byte)137 = -119
System.out.println("(byte)128 = " + (byte)128); //prints: (byte)128 = -128
System.out.println("(byte)-129 = " + (byte)-129); //prints: (byte)-129 = 127
This is so far what I've done to convert the 8 bytes I received to UInt64:
+ (UInt64)convertByteArrayToUInt64:(unsigned char *)bytes ofLength:(NSInteger)length
{
UInt64 data = 0;
for (int i = 0; i < length; i++)
{
data = data | ((UInt64) (bytes[i] & 0xff) << (24 - i * 8));
}
return data;
}
The sender that converts the data to 8 bytes data did it this way:
for(int i=1;i<9;i++)
{
statusdata[i] = (time >> 8*i & 0xff);
}
The 8 bytes data data that I received is:
01 00 00 00 00 00 00 3b
The output of my method is:
16777216
I tried to convert this "16777216" to bytes using calculator and I got:
01 00 00 00 00 00 00
which means the 3b was not included in conversion.
But, I tried this code under java and it's working fine. I don't know where the problem is.
Please help. Thanks in advance!
A UInt64 is 8 bytes, so if you have an 8 byte buffer all you need to do is make a UInt64 pointer to it and dereference it (as long as the data is in little-endian format on x86 architectures, but I'll get to that in a sec),
So:
unsigned char foo[8] = {0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
UInt64 *n = (UInt64 *) foo; // pointer to value 0x0102030405060708
UInt64 nValue = *n;
Be aware though the individual byte values on x86 hardware are little-endian, so the least-significant byte goes first in the buffer. Thus if you think about the buffer above as individual base-16 digits, the number will be:
0x0102030405060708 (most significant byte is last in the buffer).
The trick you're looking for though is to simply cast your 8-byte buffer to a UInt64* and dereference it. If you've never seen big-vs.-little endian storage/byte-order before I recommend you go read about it.
p.s. - The sender code is wrong btw, the array index (i) needs to run from 0 to 7, so (i=0;i<8;++i), not 1-8 or the value of time will not be copied correctly. Incidentally, the sender is attempting to copy time into statusdata in little-endian order (least-significant byte first in the buffer), but again, it's being done wrong.
Also, if the sender code is on Java you need to be careful that the value of time isn't actually supposed to be negative. Java doesn't have unsigned integer values so if time is negative and you reconstitute it to a UInt64 it will be a large positive value, which isn't what you'll want.
For the sake of completeness, I'll show you how to reconstitute the data byte-by-byte from the little-endian buffer, but remember, the sender code is wrong and needs to be rewritten to index from zero (and a typecast as shown above would get you there as well):
data = 0
for (i=0; i < 8; ++i) {
data = (data << 8) | bytes[i]; // bytes is already UInt8 * so no need to mask it
}
I have a byte array sent via UDP from x-plane. The bytes (4) are all floats or integers…
I tried to cast them to floats but no luck so far…
Example array:
byte data[41] = {-66,30,73,0};
How do I convert 4 bytes into int or float and doesn't float use 8 bytes?
Note: I recommend #Ophidian's ByteBuffer approach below, it's much cleaner than this. However this answer can be helpful in understanding the bit arithmetic going on.
I don't know the endianness of your data. You basically need to get the bytes into an int type depending on the order of the bytes, e.g.:
int asInt = (bytes[0] & 0xFF)
| ((bytes[1] & 0xFF) << 8)
| ((bytes[2] & 0xFF) << 16)
| ((bytes[3] & 0xFF) << 24);
Then you can transform to a float using this:
float asFloat = Float.intBitsToFloat(asInt);
This is basically what DataInputStream does under the covers, but it assumes your bytes are in a certain order.
Edit - On Bitwise OR
The OP asked for clarification on what bitwise OR does in this case. While this is a larger topic that might be better researched independently, I'll give a quick brief. Or (|) is a bitwise operator whose result is the set of bits by individually or-ing each bit from the two operands.
E.g. (in binary)
10100000
| 10001100
-----------
10101100
When I suggest using it above, it involves shifting each byte into a unique position in the int. So if you had the bytes {0x01, 0x02, 0x03, 0x04}, which in binary is {00000001, 00000010, 00000011, 00000100}, you have this:
0000 0001 (1)
0000 0010 (2 << 8)
0000 0011 (3 << 16)
| 0000 0100 (4 << 24)
--------------------------------------------------------
0000 0100 0000 0011 0000 0010 0000 0001 (67 305 985)
When you OR two numbers together and you know that no two corresponding bits are set in both (as is the case here), bitwise OR is the same as addition.
See Also
Wikipedia: Bitwise OR
You probably want to make use of java.nio.ByteBuffer. It has a lot of handy methods for pulling different types out of a byte array and should also handle most issues of endianness for you (including switching the byte order if necessary).
byte[] data = new byte[36];
//... populate byte array...
ByteBuffer buffer = ByteBuffer.wrap(data);
int first = buffer.getInt();
float second = buffer.getFloat();
It also has fancy features for converting your byte array to an int array (via an IntBuffer from the asIntBuffer() method) or float array (via a FloatBuffer from the asFloatBuffer() method) if you know that the input is really all of one type.
Use a DataInputStream as follows:
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));
float f = dis.readFloat();
//or if it's an int:
int i = dis.readInt();
You cannot just cast them into a float/int. You have to convert the bytes into an int or float.
Here is one simple way to do it:
byte [] data = new byte[] {1,2,3,4};
ByteBuffer b = ByteBuffer.wrap(data);
System.out.println(b.getInt());
System.out.println(b.getFloat());
There is a reasonable discussion here:
http://www.velocityreviews.com/forums/t129791-convert-a-byte-array-to-a-float.html
In my opinion the ByteBuffer approach is better, you can specify where the data is wrapped (from which byte index, here 0) and also the endianness (here BIG_ENDIAN).
try {
float result = ByteBuffer.wrap(data, 0, 4).order(BIG_ENDIAN).getFloat();
} catch (IndexOutOfBoundsException exception) {
// TODO: handle exception
}
Functions similar to getFloat() exist for Int, Short, Long...