I am using CoreBlueTooth API to write data into a peripheral we have received from some hardware manufacturing company. As per the specs they have given us a bunch of characteristics UUIDs to write data into. Once we want to finish we need to write 0 in one of the characteristics. Now, the problem is that when I am trying to send String/Integer and converting them into NSData then its not working. I think I need to send byte stream in those writable characteristics. Can someone help me as how can I convert my NSString & NSNumber data into byte stream before sending them. Below is my conversion code I tried with:
- (void)writeCharactersticData:(NSDictionary *)iData toPeripheral:(CBPeripheral *)iPeripheral {
NSArray *charactersticsIDs = [NSArray arrayWithArray:iData.allKeys];
self.writeCharactersticsCount = charactersticsIDs.count;
for (CBUUID *uuid in charactersticsIDs) {
if (self.peripheralCharacterstics[uuid]) {
NSData *payload;
if ([iData[uuid] isKindOfClass:[NSNumber class]]) {
NSInteger data = ((NSNumber *)iData[uuid]).integerValue;
// int integerSize = sizeof(data);
//
// uint8_t bytes[integerSize];
//
//
// NSLog(#"Integer data = %d", data);
//
// int8_t tx = (int8_t)data;
// bytes[0] = tx;
// payload = [NSData dataWithBytes:bytes length:sizeof(data)];
payload = [NSData dataWithBytes:&data length:sizeof(data)];
} else if ([iData[uuid] isKindOfClass:[NSString class]]) {
int stringSize = sizeof(iData[uuid]);
uint8_t bytes[stringSize];
NSScanner *scanner = [NSScanner scannerWithString:iData[uuid]];
for (int i=0; i<stringSize; i++) {
unsigned int value;
[scanner scanHexInt:&value];
bytes[i] = (uint8_t)value;
}
payload = [NSData dataWithBytes:bytes length:stringSize];
// payload = [iData[uuid] dataUsingEncoding:NSUTF8StringEncoding];
}
[self.discoveredPeripheral writeValue:payload forCharacteristic:self.peripheralCharacterstics[uuid] type:CBCharacteristicWriteWithResponse];
}
}
}
Here is the fix, It works for me
//Integer to NSData
+(NSData *) IntToNSData:(NSInteger)data
{
Byte *byteData = (Byte*)malloc(4);
byteData[3] = data & 0xff;
byteData[2] = (data & 0xff00) >> 8;
byteData[1] = (data & 0xff0000) >> 16;
byteData[0] = (data & 0xff000000) >> 24;
NSData * result = [NSData dataWithBytes:byteData length:4];
NSLog(#"result=%#",result);
return (NSData*)result;
}
//NSData to Integer
+(NSInteger) NSDataToInt:(NSData *)data
{
unsigned char bytes[4];
[data getBytes:bytes length:4];
NSInteger n = (int)bytes[0] << 24;
n |= (int)bytes[1] << 16;
n |= (int)bytes[2] << 8;
n |= (int)bytes[3];
return n;
}
Thanks to http://coding-snippet.blogspot.in/2013/05/blog-post.html
This is not a Core Bluetooth based problem you have here.
For debugging, you could use
NSLog(#"%#", payload);
For your string to NSData conversion, your approach seems very complicated. I would suggest something simple like
NSData* payload = [iData[uuid] dataUsingEncoding:NSUTF8StringEncoding];
if (payload.length > 20)
{
// handle error. most LE peripherals don't support longer values.
}
Another error could be that you mix ASCII 0 '0' with a binary zero '\0' when writing your value.
Just use
https://github.com/LGBluetooth/LGBluetooth It will make your job 100x easier
Related
How to split byteArray in iOS
Iam getting 160 length of arrayByte data..
I need to split into 4 parts..each part contain 40 arrayByte.that data I need to copy and use for decoding..I tried to converted it but its not working..Can some one help to do this..
Finally i got solution Below is updated working code
-(NSMutableData*)decodeOpusData:(NSData*)data
{
NSMutableData *audioData = [[NSMutableData alloc] init];
for (NSUInteger i = 0; i < 4; i ++)
{
int bufferLength = 40;
if([data length]>= 40){
NSData *subData = [data subdataWithRange:NSMakeRange(i*bufferLength, bufferLength)];
Byte *byteData = (Byte*)malloc(sizeof(Byte)*bufferLength);
memcpy(byteData, [subData bytes], bufferLength);
//You can do anything here with data..........
//Below iam decoding audio data using OPUS library
short decodedBuffer[WB_FRAME_SIZE];
int nDecodedByte = sizeof(short) * [self decode:byteData length:bufferLength output:decodedBuffer];
NSData *PCMData = [NSData dataWithBytes:(Byte *)decodedBuffer length:nDecodedByte ];
[audioData appendData:PCMData];
//Decoding audio data using OPUS library
}
}
return audioData;
}
Below code is android.i want to do like this..
ArrayByte length = 160
BUFFER_LENGTH = 40
public fun opusDataDecoder(data:ByteArray){
for (i in 0..3){
val byteArray = ByteArray(BUFFER_LENGTH)
System.arraycopy(data,i * BUFFER_LENGTH,byteArray,0, BUFFER_LENGTH) //BUFFER_LENGTH = 40
val decodeBufferArray = ShortArray(byteArray.size * 8) // decodeBufferArray = 320
val size = tntOpusUtils.decode(decoderHandler, byteArray, decodeBufferArray)
if (size > 0) {
val decodeArray = ShortArray(size)
System.arraycopy(decodeBufferArray, 0, decodeArray, 0, size)
opusDecode(decodeArray)
} else {
Log.e(TAG, "opusDecode error : $size")
}
}
}
Iam getting only first 40 bytes..i want like first 0-40 bytes then 40-80 bytes,then 80-120bytes then 120-160bytes..
But here iam getting always 40 bytes...
Can some one help me how to fix this?
Finally i got solution for split byte array and send it in small packs
Below is updated working code..
-(NSMutableData*)decodeOpusData:(NSData*)data
{
NSMutableData *audioData = [[NSMutableData alloc] init];
for (NSUInteger i = 0; i < 4; i ++)
{
int bufferLength = 40;
if([data length]>= 40){
NSData *subData = [data subdataWithRange:NSMakeRange(i*bufferLength, bufferLength)];
Byte *byteData = (Byte*)malloc(sizeof(Byte)*bufferLength);
memcpy(byteData, [subData bytes], bufferLength);
//You can do anything here with data..........
//Below iam decoding audio data using OPUS library
short decodedBuffer[WB_FRAME_SIZE];
int nDecodedByte = sizeof(short) * [self decode:byteData length:bufferLength output:decodedBuffer];
NSData *PCMData = [NSData dataWithBytes:(Byte *)decodedBuffer length:nDecodedByte ];
[audioData appendData:PCMData];
//Decoding audio data using OPUS library
}
}
return audioData;
}
I am using the following code to write the 0xDE value for a Bluetooth Caracteristic (Reset Device) using the IOS Core Bluetooth :
...
NSData *bytes = [#"0xDE" dataUsingEncoding:NSUTF8StringEncoding];
[peripheral writeValue:bytes
forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
...
is there any mistake in my code because the value is not written properly?
Swift 3.0: In case anyone is wondering the format for Swift is slightly different as writeValue can get the count from the array.
let value: UInt8 = 0xDE
let data = Data(bytes: [value])
peripheral.writeValue(data, for: characteristic, type: .withResponse)
Try creating your data with an array of single byte values.
const uint8_t bytes[] = {0xDE};
NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)];
This is a useful approach for creating arbitrary constant data. For more bytes,
const uint8_t bytes[] = {0x01,0x02,0x03,0x04,0x05};
NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)];
If you want to create data to send using variables, I would recommend using NSMutableData and appending the bytes that you need. It isn't very pretty, but it is easy to read / understand, especially when you are matching a packed struct on the embedded side. Example below is from a BLE project where we were making a simple communication protocol.
NSMutableData *data = [[NSMutableData alloc] init];
//pull out each of the fields in order to correctly
//serialize into a correctly ordered byte stream
const uint8_t start = PKT_START_BYTE;
const uint8_t bitfield = (uint8_t)self.bitfield;
const uint8_t frame = (uint8_t)self.frameNumber;
const uint8_t size = (uint8_t)self.size;
//append the individual bytes to the data chunk
[data appendBytes:&start length:1];
[data appendBytes:&bitfield length:1];
[data appendBytes:&frame length:1];
[data appendBytes:&size length:1];
The answer by bensarz is almost correct. Except one thing: you shouldn't use sizeof(int) as the length for NSData. The size of int is 4 or 8 bytes (depending on the architecture). As you want to send 1 byte, use uint8_t or Byte instead:
uint8_t byteToWrite = 0xDE;
NSData *data = [[NSData alloc] initWithBytes:&byteToWrite length:sizeof(&byteToWrite)];
[peripheral writeValue:data
forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
Of courser you could also use int as the variable's type, but you have to initialize NSData with the length of 1.
This code will fix the problem :
NSData * data = [self dataWithHexString: #"DE"];
[peripheral writeValue:data forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
dataWithHexString implementation :
- (NSData *)dataWithHexString:(NSString *)hexstring
{
NSMutableData* data = [NSMutableData data];
int idx;
for (idx = 0; idx+2 <= hexstring.length; idx+=2) {
NSRange range = NSMakeRange(idx, 2);
NSString* hexStr = [hexstring substringWithRange:range];
NSScanner* scanner = [NSScanner scannerWithString:hexStr];
unsigned int intValue;
[scanner scanHexInt:&intValue];
[data appendBytes:&intValue length:1];
}
return data;
}
What you are, in fact, doing here is writing the string "0xDE" to the characteristic. If you want to use binary/octal notation, you need to stay away from strings.
int integer = 0xDE;
NSData *data = [[NSData alloc] initWithBytes:&integer length:sizeof(integer)];
[peripheral writeValue:data
forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
The first method is:
-(NSData *)stringToAddBytes:(NSString*)addString
{
int length = (int)[addString length];
if(length < 2)
{
return nil;
}
Byte buf[length / 2];
for(int i = 0 ;i < length/2 ;i++)
{
NSString *str = [addString substringWithRange:NSMakeRange(i * 2, 2)];
Byte b = [self hexStringToByte:str];
buf[i]=b;
}
NSData * myD = [[NSData alloc]initWithBytes:buf length:length/2];
return myD;
}
THe method that the first method called.
-(Byte)hexStringToByte:(NSString*)str
{
NSArray *charArray = [[NSArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",
#"A",#"B",#"C",#"D",#"E",#"F",nil];
NSString *str1 = [str substringWithRange:NSMakeRange(0, 1)];
int num1 = (int)[charArray indexOfObject:str1];
NSString *str2 = [str substringWithRange:NSMakeRange(1, 1)];
int num2 = (int)[charArray indexOfObject:str2];
Byte b = num1*16+num2;
return b;
}
Thank you for your answer.It looks change large char to small char.
hexStringToByte: wil convert string with hexadecimal number representation (example #"FF") to Byte value (in this example 255).
stringToAddBytes: uses hexStringToByte: to create NSData of bytes breaking addString into two letter peases and converting them to Byte values.
In other words, this is string serialization.
Example:
// 255 = 0xFF
// 170 = 0xAA
// 136 = 0x88
NSString* addString = #"FFAA88";
NSData* data = [self stringToAddBytes:addString];
// data will be [255, 170, 136]
Be aware that NSData is not an array, instead, it represents a raw object.
I have the following category method on NSData. I'm trying to extract the bit field at the given index and have it return as an NSNumber. I have it working perfectly for all positive but I need it to work with negative numbers as well.
My Implementation looks as follows:
#import <Foundation/Foundation.h>
#interface NSData (ExternalDevices)
#end
#implementation NSData (ExternalDevices)
- (NSNumber *)extractLittleEndianBitFieldAtIndex:(int)index forLength:(int)length
{
//This function has limitations on the "length" parameter that are not yet know/defined
//These limitations are due to the max size of "NSInteger intData" defined below
int first_byte = index/8; //Index of the first byte containing this bit field
int last_byte = (length+index-1)/8; //Index of the last byte containing this bit field
int byte_length = last_byte - first_byte + 1; //number of bytes containing this bit field
Byte *byteArray = (Byte*)malloc(byte_length);
memcpy(byteArray, [[self subdataWithRange:NSMakeRange(first_byte, byte_length)] bytes], byte_length);
NSInteger intData = *((NSInteger *)byteArray);
free(byteArray);
return [NSNumber numberWithInt:intData];
}
+ (NSData *)dataFromHexString:(NSString *)string
{
string = [string lowercaseString];
NSMutableData *data= [NSMutableData new];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i = 0;
NSUInteger length = string.length;
while (i < length-1) {
char c = [string characterAtIndex:i++];
if (c < '0' || (c > '9' && c < 'a') || c > 'f')
continue;
byte_chars[0] = c;
byte_chars[1] = [string characterAtIndex:i++];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
return data;
}
#end
#interface Testing:NSObject
#end
#implementation Testing
- (instancetype)init
{
self = [super init];
if (self)
{
{
NSData *data = [NSData dataFromHexString:#"e30b"];
NSLog(#"%# should be 3043", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"46e0"];
NSLog(#"%# should be -8122", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"f208"];
NSLog(#"%# should be 2290", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"10e6"];
NSLog(#"%# should be -6640", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"018900"];
NSLog(#"%# should be 137", [data extractLittleEndianBitFieldAtIndex:8 forLength:16]);
}
}
return self;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
[[Testing alloc] init];
}
}
The following website seems to always yield the results I want under INT16 - Little Endian (BA)
http://www.scadacore.com/field-applications/miscellaneous/online-hex-converter.html
Although it is important to note that not every number I work with will be an INT16
Your line:
NSInteger intData = *((NSInteger *)byteArray);
is your key problem for two reasons:
byteArray may be shorter (or less likely, longer) than an NSInteger and you'll end up reading garbage. E.g. if byteArray is 2 bytes as in your examples and NSInteger is 4 bytes - which it will be in 64-bit - you'll read two bytes of garbage.
If you are converting signed values you need to sign-extend the value - that is replicate the sign bit into the higher unused bits. E.g. if you are converting a signed 16-bit field into a 32-bit signed value then the upper 16-bits need to be a replication of the most significant bit of the 16-bit value, so 0x7000 -> 0x00007000 and 0x8000 -> 0xFFFF8000.
You need to come up with an algorithm that handles these issues. You may find it easier to do the conversion a byte at a time using masking (and'ing), or'ing and shifting.
HTH
I'm developing a mobile application for iOS related to voice recording.
Due to that fact, I'm developing some different sound effects to modify recorded voice but I have a problem to implement some of them.
I'm trying to create echo/delay effect and I need to transform a byte array into a short array but I have no idea how to do it in Objective-C.
Thanks.
This is my current source code to implement it, but like byte is a very short type, when I apply attenuation (what must return a float value) produce an awful noise in my audio.
- (NSURL *)echo:(NSURL *)input output:(NSURL *)output{
int delay = 50000;
float attenuation = 0.5f;
NSMutableData *audioData = [NSMutableData dataWithContentsOfURL:input];
NSUInteger dataSize = [audioData length] - 44;
NSUInteger audioLength = [audioData length];
NSUInteger newAudioLength = audioLength + delay;
// Copy bytes
Byte *byteData = (Byte*)malloc(audioLength);
memcpy(byteData, [audioData bytes], audioLength);
short *shortData = (short*)malloc(audioLength/2);
// create a new array to store new modify data
Byte *newByteData = (Byte*)malloc(newAudioLength);
newByteData = byteData;
for (int i = 44; i < audioLength - delay; i++)
{
newByteData[i + delay] += byteData[i] * attenuation;
}
// Copy bytes in a new NSMutableData
NSMutableData *newAudioData = [NSMutableData dataWithBytes:newByteData length:newAudioLength];
// Store in a file
[newAudioData writeToFile:[output path] atomically:YES];
// Set WAV size
[[AudioUtils alloc] setAudioFileSize:output];
return output;
}
Finally, I could finish my echo effect implementing these four methods. I hope they will be useful for you.
Byte to short array
- (short *) byte2short:(Byte *)bytes size:(int)size resultSize:(int)resultSize{
short *shorts = (short *)malloc(sizeof(short)*resultSize);
for (int i=0; i < size/2; i++){
shorts[i] = (bytes[i*2+1] << 8) | bytes[i*2];
}
return shorts;
}
Short to byte array
- (Byte *) short2byte:(short *)shorts size:(int)size resultSize:(int)resultSize{
Byte *bytes = (Byte *)malloc(sizeof(Byte)*resultSize);
for (int i = 0; i < size; i++)
{
bytes[i * 2] = (Byte) (shorts[i] & 0x00FF);
bytes[(i * 2) + 1] = (Byte) (shorts[i] >> 8);
shorts[i] = 0;
}
return bytes;
}
Effect
- (NSMutableData *) effect:(NSMutableData *)data delay:(int)delay attenuation:(float)attenuation{
NSUInteger audioLength = [data length];
// Copy original data in a byte array
Byte *byteData = (Byte*)malloc(sizeof(Byte)*audioLength);
memcpy(byteData, [data bytes], audioLength);
short *shortData = (short*)malloc(sizeof(short)*(audioLength/2 + delay));
shortData = [self byte2short:byteData size:(int)audioLength resultSize:(int)audioLength/2 + delay];
// Array to store shorts
short *newShortData = shortData;
for (int i = 44; i < audioLength/2; i++)
{
newShortData[i + delay] += (short)((float)shortData[i] * attenuation);
}
Byte *newByteData = [self short2byte:newShortData size:(int)(audioLength/2 + delay) resultSize:(int)(audioLength + delay*2)];
// Copy bytes to a NSMutableData in order to create new file
NSMutableData *newAudioData = [NSMutableData dataWithBytes:newByteData length:(int)(audioLength + delay*2)];
return newAudioData;
}
Echo effect
- (NSURL *)echo:(NSURL *)input output:(NSURL *)output{
NSMutableData *audioData = [NSMutableData dataWithContentsOfURL:input];
// we call effect method that returns a NSMutableData and create a new file
[[self effect:audioData delay:6000 attenuation:0.5f] writeToFile:[output path] atomically:YES];
// We set file's size (is a method I have implemented)
[[AudioUtils alloc] setAudioFileSize:output];
return output;
}
There's no predefined function that will create a short array from a byte array, but it should be fairly simple to do it with a for loop
// create a short array
short *shortData = malloc(sizeof(short)*audioLength);
for (i=0; i<bytearray.length, i++)
{
shortData[i] = byteData[i];
}
The code is not rigorously correct (meaning I didn't compile it, just wrote it here on the fly), but it should give you an idea on how to do it.
Also be aware that saving audio data with two bytes instead of one can give very different results when playing back, but I'll assume you know how to handle with audio data for your specific purposes.