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 - flutter

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]

Related

How to collect values every n seconds with a max count in each interval from a signal in ReactiveSwift?

It seems like a combination of collect(every:on:skipEmpty:discardWhenCompleted:) and collect(count:) in ReactiveSwift.
The resulting signal would send an event every n seconds if the count of accumulated values doesn't reach max count during each time interval. But if in a specific time interval, the count of values has reached max count, it will send immediately.
For example, timeInterval = 2s, maxCount = 2
interval 1: received two values [1, 2], forward them at end of interval 1
interval 2: received one value [3], forward them at end of interval 2
interval 3: received three values [5, 6, 7] ( 3 values > maxCount), forward [5, 6] immediately when 7 is received and 7 is regarded as received value in interval 4 (interval 3 stopped early)
Question is hard to grasp, but I will try.
If you want to batch emitted values by count and time, you can use bufferTimeout method. See documentation here https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html#bufferTimeout-int-java.time.Duration-
Some example:
void bufferTimeoutTry() throws InterruptedException {
Flux.interval(Duration.ofMillis(157))
.filter(time -> time > 20 && time < 38 || time % 5 == 0 || time % 17 == 0)
.bufferTimeout(5, Duration.ofSeconds(1))
.doOnNext(list -> {
// we will get list of items buffered in 1-second period of time, or at most 5 items.
})
.subscribe(System.out::println);
Thread.sleep(30000);
}
Output will be list of items. Flux.interval is generating sequential long number (i.e. 1, 2, 3, 4, 5, ...), it is filtered on second line of method (to get some non interval behavior) and than buffer-ed. After buffer, there is no long on stream, but it has changed to list of longs.
[0, 5]
[10, 15]
[17, 20, 21, 22, 23]
[24, 25, 26, 27, 28]
[29, 30, 31, 32, 33]
[34, 35, 36, 37, 40]
[45, 50, 51]
[55, 60]
[65, 68, 70]
[75, 80]
[85, 90]
[95, 100]
[102, 105]
[110, 115]
[119, 120, 125]
[130, 135, 136]
[140, 145]
[150, 153, 155]
[160, 165]
[170, 175]
[180, 185]
Is this what you want?

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];

Decode Ble data raw 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

Swift: BLE 16 bytes to Int

I'm getting a byte array like this one:
[60, 2, 0, 0, 0]
In the documentation there is written this:
uint16 -> heartBeatNum;
uint8 -> rawDataFilesNum;
uint8 -> alertNum
uint8 -> fallsNum
I will explain a little about the device so that you understand and then I ask my question.
The bluetooth device sends an object every minute that is called heartbeat. If this is the first time the object is to use the array looks like this:
After first minute:
[1, 0, 0, 0, 0]
After two minute:
[2, 0, 0, 0, 0]
After three minute:
[3, 0, 0, 0, 0]
After for minute:
[4, 0, 0, 0, 0]
...
Now there are more than 12 that have passed and the array is:
[60, 2, 0, 0, 0]
So I try to understand from the documentation the heartbeat count is the first 16 bytes. I can not figure out how to collect the 60's and the 2's to have the exact heartbeat number.
How does this function?
According to my calculation if I do 60 * 12 = 720
So I should have about 700
Can someone enlighten me how to gather the 16 bytes in int?

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