Dart Convert two Uint8 to Uint16 that are little endian - flutter

I am new to dart but I need to take two Uint8 (part of a bluetooth response) and convert them to a single Uint16. They are also in little endian (LSB) so the second value will need to shift 8 bytes. I am struggling on how to do this in Dart.
I have tried something like this but it isn't coming close as the values are too high.
var list = new Uint8List(2);
list[0] = 56;
list[1] = 55;
int intValue = list[0] + (list[1] << 8);
Uint16 int16Value = Uint16(intValue);
print(int16Value);
Thank you very much.

You can get the ByteData from the list and ByteData has all sorts of functions for changing endianess and getting different integer types, more here.
var data = list.buffer.asByteData();
print(data.getUint16(0, Endian.little));

Related

How to convert Uint8List to Float32List to List<double> in Flutter?

I'm using Dart's typed_data library together with flutter_bluetooth_serial. One of the latter's limitation is it can only read data in Uint8List format. However, I need to send a bunch of single-precision floating-point numbers (specifically from another microcontroller). I can send the data from the microcontroller to Flutter but the latter can't just decode the Uint8List well. I'm not sure if it is a bug in the library or I'm missing something in my code. Shown below is the snippet of the code which aims to convert intBytes to List<double>
Uint8List intBytes = Uint8List.fromList([63, 158, 184, 82,]);
List<double> floatList = intBytes.buffer.asFloat32List().toList();
The intBytes variable represents a four-byte single-precision number (1.24...) which is declared with an array of 8-bit integers. However floatBytes can't seem to get the correct value from the said array even after viewing the buffer as a list of float.
If I try to view the type of value from the first element with the following code
print(doubleList[0].runtimeType);
It reads it as an int data type but I was expecting a double
From your comment:
the first element's value will be 396464455680; an integer value very far from the floating point value of 1.24...
396464455680 is a correct interpretation for a little-endian representation for a byte sequence 63, 158, 184, 82. You apparently assume that it will be interpreted as a big-endian single-precision floating point value, but IEEE-754 does not specify endianness. If you reverse the bytes, you will get 1.2400000095367432 on a little-endian system:
Uint8List intBytes = Uint8List.fromList([63, 158, 184, 82].reversed.toList());
List<double> floatList = intBytes.buffer.asFloat32List();
print(floatList);
(Incidentally, Float32List derives from List<double>, so you do not need the extra .toList() call.)
Alternatively, you can use ByteData.getFloat32 to parse bytes with a specified endianness:
Uint8List intBytes = Uint8List.fromList([63, 158, 184, 82]);
ByteData byteData = intBytes.buffer.asByteData();
List<double> floatList = [
for (var offset = 0; offset < intBytes.length; offset += 4)
byteData.getFloat32(offset, Endian.big),
];
print(floatList);
Note that using ByteData.getFloat32 would be more robust than reordering bytes yourself since it'd make your code agnostic to the endianness of the platform you're running on.
Regarding:
print(doubleList[0].runtimeType);
It reads it as an int data type but I was expecting a double
Presuming that you mean floatList (doubleList is not defined in your example), if you run that code in the Dart VM, double should be printed.
If, however, you run that code in a web browser (e.g. with DartPad or with Flutter for the Web), int will be printed. As a simpler example, running:
double d = 1.0;
print(d.runtimeType);
in DartPad also will print int. On the web, Dart transpiles to JavaScript, and for efficiency, Dart directly uses some JavaScript primitives. JavaScript does not distinguish between integer and floating-point types; all numbers are double-precision floating point values. It therefore isn't surprising that type information gets lost in the roundtrip.

How to read and convert Bluetooth characteristic from byte data to proper values(Bluetooth for flutter)

I have to read and write some values to a Bike Smart trainer with BLE (Bluetooth Low Energy) used with Flutter. When I try to read the values from the GATT characteristic org.bluetooth.characteristic.supported_power_range (found on bluetooth.org site https://www.bluetooth.com/specifications/gatt/characteristics/ ) I get the return value of an Int List [0,0,200,0,1,0].
The GATT characteristic sais that there are 3 sint16 fields for Min., Max. and step size Watts (Power).
The Byte transmission order also sais that the least significant octet is transmitted first.
My guessings are, that the 3 parameters are returned in an Int array with 8bit value each. But I can't interpret the 200 for maybe the maximum Power setting. Because the smart trainer should provide max. 2300W Watts resistance (ELITE Drivo https://www.elite-it.com/de/produkte/home-trainer/rollentrainer-interaktive/drivo)
The Output results from this code snippet:
device.readCharacteristic(savedCharacteristics[Characteristics.SUPPORTED_POWER_RANGE]).then((List<int> result) {
result.forEach((i) {
print(i.toString());
});
});
// result: [0,0,200,0,1,0]
Maybe some one of u knows how to interpret the binary/hex/dec values of the flutter_blue characteristic output.
Or some hints would be great
Edit
For future readers, I got the solution. I'm a bit asheamed because I read the wrong characteristic.
The return value [0,0,200,0,1,0] was for supported resistance level. (which is 20% and the 200 shows the 20% with a resolution of 0.1 like described in the GATT spec)
I also got a return value for the supported power level which was [0,0,160,15,1,0]. Now the solution how to read the 2 Bytes of max powre level: you get the 160,15 the spec sais LSO (least significant octet first, don't confuse it with LSB least significant bit first). In fact of that you have to read it like 15,160. now do the math with the first Byte 15*256 + 160 = 4000 and thats the correct maximum supported power of the trainer like in the datasheet.
I hope I help someone with that. Thanks for the two replys they are also correct and helped me to find my mistake.
I had the same problem connecting to a Polar H10 to recover HR and RR intervals. It might not be 100% the same, but I think my case can guide you to solve yours.
I am receiving the same list as you like these two examples:
[0,60]
[16,61,524,2]
Looking at the specs of the GATT Bluetooth Heart Rate Service I figured that each element of the list retrieved matches 1 byte of the data transmitted by the characteristic you are subscripted to. For this service, the first byte, i.e., the first element of the list, has some flags to point out if there is an RR value after the HR value (16) or not (0). This is just two cases among the many different ones that can ocur depending on the flags values, but I think it shows how important this first byte can be.
After that, the HR value is coded as an unsigned integer with 8 bits (UINT8), that is, the HR values match the second element of the lists shown before. However, the RR interval is coded as an unsigned integer eith 16bits (UINT16), so it complicates the translation of those two last elements of the list #2 [16,61,524,2], because we should use 16 bits to get this value and the bytes are not in the correct order.
This is when we import the library dart:typed_data
import 'dart:typed_data';
...
_parseHr(List<int> value) {
// First sort the values in the list to interpret correctly the bytes
List<int> valueSorted = [];
valueSorted.insert(0, value[0]);
valueSorted.insert(1, value[1]);
for (var i=0; i< (value.length-3); i++) {
valueSorted.insert(i+2, value[i+3]);
valueSorted.insert(i+3, value[i+2]);
}
// Get flags directly from list
var flags = valueSorted[0];
// Get the ByteBuffer view of the data to recode it later
var buffer = new Uint8List.fromList(valueSorted).buffer; // Buffer bytes from list
if (flags == 0) {
// HR
var hrBuffer = new ByteData.view(buffer, 1, 1); // Get second byte
var hr = hrBuffer.getUint8(0); // Recode as UINT8
print(hr);
}
if (flags == 16) {
// HR
var hrBuffer = new ByteData.view(buffer, 1, 1); // Get second byte
var hr = hrBuffer.getUint8(0); // Recode as UINT8
// RR (more than one can be retrieved in the list)
var nRr = (valueSorted.length-2)/2; // Remove flags and hr from byte count; then split in two since RR is coded as UINT16
List<int> rrs = [];
for (var i = 0; i < nRr; i++) {
var rrBuffer = new ByteData.view(buffer, 2+(i*2), 2); // Get pairs of bytes counting since the 3rd byte
var rr = rrBuffer.getUint16(0); // Recode as UINT16
rrs.insert(i,rr);
}
print(rrs);
}
Hope it helps, the key is to get the buffer view of the sorted list, get the bytes that you need, and recode them as the standard points out.
I used print(new String.fromCharCodes(value)); and that worked for me.
value is your return from List<int> value = await characteristic.read();
I thank ukBaz for his answer to this question. Write data to BLE device and read its response flutter?
You can use my package byte_data_wrapper to transform this data to a decimal value which you can understand:
Get the buffer:
import 'dart:typed_data';
final buffer = Uint16List.fromList(result).buffer;
Create the byteDataCreator:
// Don't forget to add it to your pubspec.yaml
//dependencies:
// byte_data_wrapper:
// git: git://github.com/Taym95/byte_data_wrapper.git
import 'byte_data_wrapper/byte_data_wrapper.dart';
final byteDataCreator = ByteDataCreator.view(buffer);
Get your data :
// You can use getUint8() if valeu is Uint8
final min = byteDataCreator.getUint16();
final max = byteDataCreator.getUint16();
final stepSize = byteDataCreator.getUint16();
I know its too late to answer this but if there is anyone still having a trouble, just convert it manually to be an integer. Because I think you are receiving a type of ByteArray (correct me if I'm wrong).
num bytesToInteger(List<int> bytes) {
/// Given
/// 232 3 0 0
/// Endian.little representation:
/// To binary
/// 00000000 00000000 00000011 11101000
/// Combine
/// 00000000000000000000001111101000
/// Equivalent : 1000
num value = 0;
//Forcing to be Endian.little (I think most devices nowadays uses this type)
if (Endian.host == Endian.big) {
bytes = List.from(bytes.reversed);
}
for (var i = 0, length = bytes.length; i < length; i++) {
value += bytes[i] * pow(256, i);
}
return value;
}
and vice versa when you try to write over 255
Uint8List integerToBytes(int value) {
const arrayLength = 4;
return Uint8List(arrayLength)..buffer.asByteData().setInt32(0, value, Endian.little);
}
Hope this helps.
P.S. I also posted the similar problem here.

Difference between Int and Uint8 swift

What are the differences between the data types Int & UInt8 in swift.
Looks like UInt8 is used for binary data, i need to convert UInt8 to Int is this possible.
That U in UInt stands for unsigned int.
It is not just using for binary data. Uint is used for positive numbers only, like natural numbers.
I recommend you to get to know how negative numbers are understood from a computer.
Int8 is an Integer type which can store positive and negative values.
UInt8 is an unsigned integer which can store only positive values.
You can easily convert UInt8 to Int8 but if you want to convert Int8 to UInt8 then make sure value should be positive.
UInt8 is an 8bit store, while Int not hardly defined or defined by the compiler:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
Int could be 32 or 64 bits
Updated for swift:
Operation Output Range Bytes per Element
uint8 0 to 255 1
Int - 9223372036854775808 to 9223372036854775807 2 or 4
If you want to find the max and min range of Int or UInt8:
let maxIntValue = Int.max
let maxUInt8Value = UInt8.max
let minIntValue = Int.min
let minUInt8Value = UInt8.min
If you want to convert UInt8 to Int, used below simple code:
func convertToInt(unsigned: UInt) -> Int {
let signed = (unsigned <= UInt(Int.max)) ?
Int(unsigned) :
Int(unsigned - UInt(Int.max) - 1) + Int.min
return signed
}

How to convert ASCII array (image) to a single string

My metadata is stored in a 8 bit unsigned dataset in a HDF5 file. After importing to DM, it become a 2D image of 1*length dimension. Each "pixel" stores the ASCII value of the corresponding value to the character.
For further processing, I have to convert the ASCII array to a single string, and further to TagGroup. Here is the stupid method (pixel by pixel) I currently do:
String Img2Str (image img){
Number dim1, dim2
img.getsize(dim1,dim2)
string out = ""
for (number i=0; i<dim1*dim2; i++)
out += img.getpixel(0,i).chr()
Return out
}
This pixel-wise operation is really quite slow! Is there any other faster method to do this work?
Yes, there is a better way. You really want to look into the chapter of raw-data streaming:
If you hold raw data in a "stream" object, you can read and write it in any form you like. So the solution to your problem is to
Create a stream
Add the "image" to the stream (writing binary data)
Reset the steam position to the start
Read out the binary data a string
This is the code:
{
number sx = 10
number sy = 10
image textImg := IntegerImage( "Text", 1, 0 , sx, sy )
textImg = 97 + random()*26
textImg.showimage()
object stream = NewStreamFromBuffer( 0 )
ImageWriteImageDataToStream( textImg, stream, 0 )
stream.StreamSetPos(0,0)
string asString = StreamReadAsText( stream, 0, sx*sy )
Result("\n as string:\n\t"+asString)
}
Note that you could create a stream linked to file on disc and, provided you know the starting position in bytes, read from the file directly as well.

Obj-C Read Little Endian From Binary File? (xcode)

I'm looking for a way to get an int value from a binary file. So lets say i have this file "myfile.dat" where from my PC i stored a lot of stuff...
Now i need to read that file from my IPhone and show the data...
on the "myfile.dat" i have this (binary and all ints are little endian):
0-12: A signature string
13-16: An int number (note that length = 4)
So using NSData i know i can read from pos 13 to pos 16 and get those bytes... i can get the first 0-12 string correctly, but i cannot read pos 13-16 and convert it to an int value in Obj-C.... ;(
I have something like:
unsigned char bytes[length];
[_data getBytes:bytes range:NSMakeRange(offset, length)];
int32_t elem = OSReadLittleInt32(bytes, 0);
Now, im a newbie when it comes to Obj-C and C/C++... all my life i have been working with C# (sad)...
Can anyone help? THANKS IN ADVANCE!
Give this a try:
unsigned long bytes;
[_data getBytes: &bytes length: sizeof(unsigned long)];
NSLog(#"%i", NSSwapLittleLongToHost(bytes));