Decode Ble data raw flutter - flutter

I'm developing a flutter app using the flutter_blue library to interface a BlueNRG-tile from STMicroelectronics. I'm receiving the the raw data from the desired caracteristics then i'm note able ble to convert them to string using the utf8.decode() function.
This is the received data as a list and the issue.
I/flutter (32277): Teste conversion : [121, 85, 0, 0, 209, 133, 1, 0, 5, 10, 237, 0, 0, 0]
E/flutter (32277): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: FormatException: Missing extension byte (at offset 11).
the code from the in the st board:
tBleStatus Environmental_Update(int32_t Press,int32_t Press2,uint16_t Hum, int16_t Temp,int16_t Temp2) {
uint8_t BuffPos = 0;
STORE_LE_16(buff, (getTimestamp()));
BuffPos = 2;
STORE_LE_32(buff + BuffPos, Press);
BuffPos += 4;
STORE_LE_16(buff + BuffPos, Hum);
BuffPos += 2;
STORE_LE_16(buff + BuffPos, Temp);
BuffPos += 2;
STORE_LE_16(buff + BuffPos, Temp2);
return aci_gatt_update_char_value(HWServW2STHandle, EnvironmentalCharHandle, 0, EnvironmentalCharSize, buff);
}
Environmental_Update(PressToSend,PressToSend2, HumToSend, TempToSend,TempToSend2);
Thank You.

You are not able to convert your RAW data to string because you are not sending it as string but in form of bytes.
Take your temperature for example:
You receive the temperature as int16_t, a 16-bit number storing values from –32768 to 32767. This number needs two bytes to be stored, that's why you used BuffPos += 2; and increased the position by 2 bytes.
You need to extract the values from your received array the same way, bytewise. Have a look at this example:
import 'dart:typed_data';
int fromBytesToInt16(int b1, int b0) {
final int8List = new Int8List(2)
..[1] = b1
..[0] = b0;
return ByteData.sublistView(int8List).getInt16(0);
}
void main() {
var received = [121, 85, 0, 0, 209, 133, 1, 0, 5, 10, 237, 0, 0, 0];
var temp = fromBytesToInt16(received[8], received[9]) / 100;
print('temperature: $temp');
}
The temperature was stored as a int16 at index 8 and 9 so I converted it the same way. This results in a temp value of 2565, which divided by 100 would give a pretty nice temperature of 25.65 degree

Related

How to target specific int (bits) in an int array into Uint16 or 32?

I have a list of int like the following.
List<int> data = [52, 24, 40, 0, 198, 7, 98, 0, 0, 0, 40, 223, 30, 0, 203, 244, 0, 0]
I would like to generate 8/16/32 Uint so that I can process them. For example, bytes 2 & 3 is actually a 16 bit value, so both bytes need to be added, in the right order which in this case is 00000000 00101000 .
Question: How can I target specific index to add to a specific Uint type?
eg.. Uint16 powerValue = data[2] data[3];
Presuming that your List<int> is meant to be a list of bytes, convert your List<int> into a Uint8List with Uint8List.fromList. Note that your List<int> might already be a Uint8List; if so, just cast it with as Uint8List to avoid an unnecessary copy.
Access the Uint8List.buffer getter to obtain the underlying ByteBuffer.
You then can use methods such as ByteBuffer.asUint16List, ByteBuffer.asUint32List, etc. These methods allow you to specify a starting offset and length.
Alternatively, if you need more control (for example, if you want to interpret bytes using the non-native endianness), then you can use ByteBuffer.asByteData to obtain a ByteData view that provides methods such as getUint16, getUint32, etc.
Putting it all together, for your specific example:
import 'dart:typed_data';
void main() {
List<int> data = [
52,
24,
40,
0,
198,
7,
98,
0,
0,
0,
40,
223,
30,
0,
203,
244,
0,
0
];
var bytes = Uint8List.fromList(data);
var powerValue = bytes.buffer.asByteData().getUint16(2, Endian.little);
print(value); // Prints: 40
}
Of course, if this is just something you need to do as a one-off case, you also could just do bitwise operations yourself:
var powerValue = (data[3] << 8) | data[2];

I am working with BLE in dart, where I need to send 9 bytes to the specific characteristic , where first byte is 5 and remaining is epoch

Hi I trying to send 9 bytes to specific characteristic, where first byte is 0x05 , i.e 5 ,and next 8 bytes as epoch in seconds,
I tried this,
List<int> timeDataForBLEWrite = [0x5, 0, 0, 0, 0, 0, 0, 0, 0 ]; // here 0 will be replaced by 8 bytes of epoch
to get epoch in seconds, I tried this,
int timestampEpochInSeconds = DateTime.now().millisecondsSinceEpoch ~/ 1000; // 1623331779
to convert epoch into bytes I have tried this,
List<int> bytes = utf8.encode(timestampEpochInSeconds.toString());
but here I am getting 10 bytes because timestampEpochInSeconds is 1623331779 // 10 digits
print(bytes); // [49, 54, 50, 51, 51, 51, 49, 55, 55, 57]
how can I get 8 integers from the seconds epoch so that I can send total 9 bytes to the characteristic. like below,
characteristic.write(timeDataForBLEWrite);
I am assuming that you don't want the string in bytes but the values in bytes.
Most data in Bluetooth is in Little Endian so I have made that assumption about the timestamp as bytes.
I did the following as an example on DartPad:
import 'dart:typed_data';
List<int> epoch() {
var timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
var sendValueBytes = ByteData(9);
sendValueBytes.setUint8(0, 5);
// setUint64 not implemented on some systems so use setUint32 in
// those cases. Leading zeros to pad to equal 64 bit.
// Epoch as 32-bit good until 2038 Jan 19 # 03:14:07
try {
sendValueBytes.setUint64(1, timestamp.toInt(), Endian.little);
} on UnsupportedError {
sendValueBytes.setUint32(1, timestamp.toInt(), Endian.little);
}
return sendValueBytes.buffer.asUint8List();
}
void main() {
print('Epoch Bytes (plus 0x05): ${epoch()}');
}
Which gave the following output:
Epoch Bytes (plus 0x05): [5, 167, 60, 194, 96, 0, 0, 0, 0]

The ten's digit and unit's digit of numbers

I have this code
int[,] array = new int[,]{ {34, 21, 32, 41, 25},
{14 ,42, 43, 14, 31},
{54, 45, 52, 42, 23},
{33, 15, 51, 31, 35},
{21, 52, 33, 13, 23} };
for (int i = 0; i < array.GetLength(1); i++)
{
for (int j = 0; j < array.GetLength(0); j++)
{
Console.Write(array[i, j] + " ");
}
Console.WriteLine();
}
and i need to find a specific number ( the treasure ).
For each value the ten's digit represents the row number and the unit's digit represents the column number of the cell containing the next clue.
Starting in the upper left corner (at 1,1), i have to use the clues to guide me search of the array. (The first three clues are 11, 34, 42).
The treasure is a cell whose value is the same as its coordinates.
The program should output the cells it visits during its search.
I did the simply way:
Console.WriteLine("The next clue is: {0}", array[0, 0]);
Console.WriteLine("The next clue is: {0}", array[2, 3]);
Console.WriteLine("The next clue is: {0}", array[3, 2]);
Console.WriteLine("The next clue is: {0}", array[0, 4]);
and so on, but the problem is, that if I change the array to set another route the program will output the wrong way. So the solution needs to be dynamic and find the treasure regardless of the array content.
My problem is that i don't know how to do to find the ten's digit of the numbers and the unit's digit.
Can anyone please help me with this?
To illustrate my comment: code below and Fiddle
(I've added a HashSet<int> to track which cells have already been visited and avoid ending up with an infinite loop)
int[,] array = new int[,]
{
{34, 21, 32, 41, 25},
{14 ,42, 43, 14, 31},
{54, 45, 52, 42, 23},
{33, 15, 51, 31, 35},
{21, 52, 33, 13, 23}
};
int currentCoordinates = 11;
bool treasureFound = false;
var visitedCells = new HashSet<int>();
while (!treasureFound && !visitedCells.Contains(currentCoordinates))
{
int currentRowIndex = currentCoordinates / 10;
int currentColumnIndex = currentCoordinates % 10;
int nextCoordinates = array[currentRowIndex - 1, currentColumnIndex - 1];
if (nextCoordinates == currentCoordinates)
{
treasureFound = true;
}
else
{
visitedCells.Add(currentCoordinates);
currentCoordinates = nextCoordinates;
}
}
if (treasureFound)
{
Console.WriteLine($"Treasure found in cell {currentCoordinates}");
}
else
{
Console.WriteLine("No treasure");
}

Int96Value to Date string

When reading a parquet file (using Scala) I read the timestamp field back as:
Int96Value{Binary{12 constant bytes, [0, 44, 84, 119, 54, 49, 0, 0, -62, -127, 37, 0]}}
How can I convert it to a date string?
I did some research for you. The Int96 format is quite specific a seems to be deprecated.
Here is a discussion about converting Int96 to Date.
Based on this, I created following piece of code:
def main(args: Array[String]): Unit = {
import java.util.Date
import org.apache.parquet.example.data.simple.{Int96Value, NanoTime}
import org.apache.parquet.io.api.Binary
val int96Value = new Int96Value(Binary.fromConstantByteArray(Array(0, 44, 84, 119, 54, 49, 0, 0, -62, -127, 37, 0)))
val nanoTime = NanoTime.fromInt96(int96Value)
val nanosecondsSinceUnixEpoch = (nanoTime.getJulianDay - 2440588) * (86400 * 1000 * 1000 * 1000) + nanoTime.getTimeOfDayNanos
val date = new Date(nanosecondsSinceUnixEpoch / (1000 * 1000))
println(date)
}
However, it prints Sun Sep 27 17:05:55 CEST 2093. I am not sure, if this is a date, that you expected.
Edit: using Instance as suggested:
val nanosInSecond = 1000 * 1000 * 1000;
val instant = Instant.ofEpochSecond(nanosecondsSinceUnixEpoch / nanosInSecond, nanosecondsSinceUnixEpoch % nanosInSecond)
println(instant) // prints 2093-09-27T15:05:55.933865216Z
java.time supports Julian days.
Credits to ygor for doing the research and finding out how to interpret the 12 bytes of your array.
byte[] int96Bytes = { 0, 44, 84, 119, 54, 49, 0, 0, -62, -127, 37, 0 };
// Find Julian day
int julianDay = 0;
int index = int96Bytes.length;
while (index > 8) {
index--;
julianDay <<= 8;
julianDay += int96Bytes[index] & 0xFF;
}
// Find nanos since midday (since Julian days start at midday)
long nanos = 0;
// Continue from the index we got to
while (index > 0) {
index--;
nanos <<= 8;
nanos += int96Bytes[index] & 0xFF;
}
LocalDateTime timestamp = LocalDate.MIN
.with(JulianFields.JULIAN_DAY, julianDay)
.atTime(LocalTime.NOON)
.plusNanos(nanos);
System.out.println("Timestamp: " + timestamp);
This prints:
Timestamp: 2017-10-24T03:01:50
I’m not happy about converting your byte array to an int and a long by hand, but I don’t know Parquet will enough to use the conversions that are probably available there. Use them if you can.
It doesn’t matter which LocalDate we use as starting point since we are changing it to the right Julian day anyway, so I picked LocalDate.MIN just to pick one.
The way I read the documentation, Julian days are always in the local time zone, that is, no time zone is understood, and they always start at midday (not midnight).
Link: Documentation of JulianFields in java.time

Without debug mode UnsafePointer withMemoryRebound will gives wrong value

Here I am trying to concatenate 5 bytes into the single Integer value, I am getting an issue with UnsafePointer withMemoryRebound method.
when I am debugging and checking logs it will gives the correct value. But when I try without debug, it will give the wrong value.(4 out of 5 times wrong value). I got confuses on this API. Is it correct way I am using?
case 1:
let data = [UInt8](rowData) // rowData is type of Data class
let totalKM_BitsArray = [data[8],data[7],data[6],data[5],data[4]]
self.totalKm = UnsafePointer(totalKM_BitsArray).withMemoryRebound(to:UInt64.self, capacity: 1) {$0.pointee}
case 2:
Below code will work for both Enable or Disable debug mode And gives the correct value.
let byte0 : UInt64 = UInt64(data[4])<<64
let byte1 : UInt64 = UInt64(data[5])<<32
let byte2 : UInt64 = UInt64(data[6])<<16
let byte3 : UInt64 = UInt64(data[7])<<8
let byte4 : UInt64 = UInt64(data[8])
self.totalKm = byte0 | byte1 | byte2 | byte3 | byte4
Please suggest me UnsafePointer way of using? Why will this issue come?
Addtional infomation :
let totalKm : UInt64
let data = [UInt8](rowData) // data contain [100, 200, 28, 155, 0, 0, 0, 26, 244, 0, 0, 0, 45, 69, 0, 0, 0, 4, 246]
let totalKM_BitsArray = [data[8],data[7],data[6],data[5],data[4]] // contain [ 244,26,0,0,0]
self.totalKm = UnsafePointer(totalKM_BitsArray).withMemoryRebound(to:UInt64.self, capacity: 1) {$0.pointee}
// when print log gives correct value, when run on device give wrong 3544649566089386 like this.
self.totalKm = byte0 | byte1 | byte2 | byte3 | byte4
// output is 6900 This is correct as expected
There are a few problems with this approach:
let data = [UInt8](rowData) // rowData is type of Data class
let totalKM_BitsArray = [data[8], data[7], data[6], data[5], data[4]]
self.totalKm = UnsafePointer(totalKM_BitsArray)
.withMemoryRebound(to:UInt64.self, capacity: 1) { $0.pointee }
Dereferencing UnsafePointer(totalKM_BitsArray) is undefined behaviour, as the pointer to totalKM_BitsArray's buffer is only temporarily valid for the duration of the initialiser call (hopefully at some point in the future Swift will warn on such constructs).
You're trying to bind only 5 instances of UInt8 to UInt64, so the remaining 3 instances will be garbage.
You can only withMemoryRebound(_:) between types of the same size and stride; which is not the case for UInt8 and UInt64.
It's dependant on the endianness of your platform; data[8] will be the least significant byte on a little endian platform, but the most significant byte on a big endian platform.
Your implementation with bit shifting avoids all of these problems (and is generally the safer way to go as you don't have to consider things like layout compatibility, alignment, and pointer aliasing).
However, assuming that you just wanted to pad out your data with zeroes for the most significant bytes, with rowData[4] to rowData[8] making up the rest of the less significant bytes, then you'll want your bit-shifting implementation to look like this:
let rowData = Data([
100, 200, 28, 155, 0, 0, 0, 26, 244, 0, 0, 0, 45, 69, 0, 0, 0, 4, 246
])
let byte0 = UInt64(rowData[4]) << 32
let byte1 = UInt64(rowData[5]) << 24
let byte2 = UInt64(rowData[6]) << 16
let byte3 = UInt64(rowData[7]) << 8
let byte4 = UInt64(rowData[8])
let totalKm = byte0 | byte1 | byte2 | byte3 | byte4
print(totalKm) // 6900
or, iteratively:
var totalKm: UInt64 = 0
for byte in rowData[4 ... 8] {
totalKm = (totalKm << 8) | UInt64(byte)
}
print(totalKm) // 6900
or, using reduce(_:_:):
let totalKm = rowData[4 ... 8].reduce(0 as UInt64) { accum, byte in
(accum << 8) | UInt64(byte)
}
print(totalKm) // 6900
We can even abstract this into an extension on Data in order to make it easier to load such fixed width integers:
enum Endianness {
case big, little
}
extension Data {
/// Loads the type `I` from the buffer. If there aren't enough bytes to
/// represent `I`, the most significant bits are padded with zeros.
func load<I : FixedWidthInteger>(
fromByteOffset offset: Int = 0, as type: I.Type, endianness: Endianness = .big
) -> I {
let (wholeBytes, spareBits) = I.bitWidth.quotientAndRemainder(dividingBy: 8)
let bytesToRead = Swift.min(count, spareBits == 0 ? wholeBytes : wholeBytes + 1)
let range = startIndex + offset ..< startIndex + offset + bytesToRead
let bytes: Data
switch endianness {
case .big:
bytes = self[range]
case .little:
bytes = Data(self[range].reversed())
}
return bytes.reduce(0) { accum, byte in
(accum << 8) | I(byte)
}
}
}
We're doing a bit of extra work here in order to we read the right number of bytes, as well as being able to handle both big and little endian. But now that we've written it, we can simply write:
let totalKm = rowData[4 ... 8].load(as: UInt64.self)
print(totalKm) // 6900
Note that so far I've assumed that the Data you're getting is zero-indexed. This is safe for the above examples, but isn't necessarily safe depending on where the data is coming from (as it could be a slice). You should be able to do Data(someUnknownDataValue) in order to get a zero-indexed data value that you can work with, although unfortunately I don't believe there's any documentation that guarantees this.
In order to ensure you're correctly indexing an arbitrary Data value, you can define the following extension in order to perform the correct offsetting in the case where you're dealing with a slice:
extension Data {
subscript(offset offset: Int) -> Element {
get { return self[startIndex + offset] }
set { self[startIndex + offset] = newValue }
}
subscript<R : RangeExpression>(
offset range: R
) -> SubSequence where R.Bound == Index {
get {
let concreteRange = range.relative(to: self)
return self[startIndex + concreteRange.lowerBound ..<
startIndex + concreteRange.upperBound]
}
set {
let concreteRange = range.relative(to: self)
self[startIndex + concreteRange.lowerBound ..<
startIndex + concreteRange.upperBound] = newValue
}
}
}
Which you can use then call as e.g data[offset: 4] or data[offset: 4 ... 8].load(as: UInt64.self).
Finally it's worth noting that while you could probably implement this as a re-interpretation of bits by using Data's withUnsafeBytes(_:) method:
let rowData = Data([
100, 200, 28, 155, 0, 0, 0, 26, 244, 0, 0, 0, 45, 69, 0, 0, 0, 4, 246
])
let kmData = Data([0, 0, 0] + rowData[4 ... 8])
let totalKm = kmData.withUnsafeBytes { buffer in
UInt64(bigEndian: buffer.load(as: UInt64.self))
}
print(totalKm) // 6900
This is relying on Data's buffer being 64-bit aligned, which isn't guaranteed. You'll get a runtime error for attempting to load a misaligned value, for example:
let data = Data([0x01, 0x02, 0x03])
let i = data[1...].withUnsafeBytes { buffer in
buffer.load(as: UInt16.self) // Fatal error: load from misaligned raw pointer
}
By loading individual UInt8 values instead and performing bit shifting, we can avoid such alignment issues (however if/when UnsafeMutableRawPointer supports unaligned loads, this will no longer be an issue).