Sending a PNG image with BLE in Swift - swift
I've been stuck with this problem for the past 2 hours and I'm about to give up. I've Googled a lot and just can't find something that works. I am using the newest version of XCode.
I want to send a PNG image through Bluetooth Low Energy, the receiver in this case is Bleno. Things I've tried include converting the image to a Base64 String and converting the image to an UInt8 array and sending each entry in the array one by one.
Nothing I do works, so the only "working" code I is for converting an image to bytes, which is this:
let testImage = UIImage(named: "smallImage")
let imageData = UIImagePNGRepresentation(testImage!)
I already have all the connection code for BLE and am able to send a simple and short string successfully to Bleno. I also know through "peripheral.maximumWriteValueLength" that the maximum amount of bytes I can send at once is 512 bytes, although I can imagine that using Low Energy lowers this maximum. I'm trying to send the data with peripheral.writeValue, which looks like this at the moment (array was the UInt8 array I tried):
peripheral.writeValue(Data(bytes:array), for: char, type: CBCharacteristicWriteType.withResponse)
The error I most often get is Error Domain=CBATTErrorDomain Code=13 "The value's length is invalid.", which I assume is because the data I try to send is more than 512 bytes. I tried sending the data in packages smaller than 512 bytes, but like I said, I just can't get it to work.
In short my question is this: How do I send a PNG image (in multiple parts) through BLE?
Edit: I'm got something to work, altough it is pretty slow, because it's not utilising the full 20 bytes per packet:
let buffer: [UInt8] = Array(UIImagePNGRepresentation(testImage!)!)
let start = "I:"+String(buffer.count)
peripheral.writeValue(start.data(using: .utf8)!, for: char, type: CBCharacteristicWriteType.withResponse)
buffer.forEach{b in
let data = NSData(bytes: [UInt8(b)], length: MemoryLayout<UInt8>.size)
peripheral.writeValue(data as Data, for: char, type: CBCharacteristicWriteType.withResponse)
}
Thanks in advance!
Related
How to view the actual bytes in a Data variable in Swift
I have a variable of type Data in Swift code, using Xcode 10.1, called data. I can see it in the debugger, but I don't know where the actual values are stored. It should contain a letter (one byte) and three Uint8 values, all 0-255, so it should be 4 bytes. The first _length is shown to be 6, so i don't know what else could be added in (one reason I want to see what is actually in there) (below). But I do not understand where the binary value is. The _rawValue does not seem to be it because it contains 4.5 bytes. Perhaps it is a pointer, as it says "RawPointer"? Where are the actual bytes stored? Edit: By setting a new variable equal to data[i], i did figure out the number of bytes is correct (I found the code was putting things in i didn't know). My string is, for example "!C 0 21 255 17", so 6 bytes. However, I would still love to find an answer to my question: Is there way during debug to view the elements without creating new variables to inspect?
Create an extension of Data as follows: extension Data { public var bytes: [UInt8] { return [UInt8](self) } } You can view the bytes of data during debugging as: po data.bytes
Just type po data as NSData in the debug console. You will see the hex bytes like <066465666768>
Performance problem with saving images locally and writing to api in Swift 4
We are transforming images to a byte array and then saving them as a string in a JSON file. This happens locally on the file system. When reading this JSON file from the file system or writing it to the API the performance is really bad. For large quantities of images you have to wait >10 sec, before it is loaded in the ViewController. Is there a better way or library (Swift4) to deal with this? We are already compressing the quality to 0.2. Someone told us to use base64, is this the way to go? We encode the image like this:ImagesBytes : [[UInt8]]? in to a JSON File with some TextField data. This is the code public var formImagesBytes : [[UInt8]]? = [] So this is in the model as a field. The model is a form with fields(strings) and an array of images which is an array of UInt8. This will be first saved locally while decoding it like this: formImagesBytes = try container.decodeIfPresent([[UInt8]].self, forKey: .formImagesBytes) Then we JSON encode all the data, including values of the form and image array of bytes. Then we add it with the Filemanager to the local storage on the device. We can open this file and it is including the formdata and an array of bytes in this case. When loading the data from the local storage in the viewcontroller we again read this local file en decode it to a form model. But processing this file is taking too much time. I hope it is clear. My question is not really about the code, but about: is there a best practice or better way to write the image to this JSON file in order to let the app perform better. Example output: {"Images":[[255,216,255,224,0,16,74,70,73,70,0,1,1,0,0,144,0,144,0,0,255,225,0,140,69,120,105,102,0,0,77,77,0,42,0,0,0,8,0,5,1,18,0,3,0,0,0,1,0,1,0,0,1,26,0,5,0,0,0,1,0,0,0,74,1,27,0,5,0,0,0,1,0,0,0,82,1,40,0,3,0,0,0,1,0,2,0,0,135,105,0,4,0,0,0,1,0,0,0,90,0,0,0,0,0,0,0,144,0,0,0,1,0,0,0,144,0,0,0,1,0,3,160,1,0,3,0,0,0,1,0,1,0,0,160,2,0,4,0,0,0,1,0,0,1,82,160,3,0,4,0,0,0,1,0,0,0,253,0,0,0,0,255,237,0,56,80,104,111,116,111,115,104,111,112,32,51,46,48,0,56,66,73,77,4,4,0,0,0,0,0,0,56,66,73,77,4,37,0,0,0,0,0,16,212,29,140,217,143,0,178,4,233,128,9,152,236,248,66,126,255,192,0,17,8,0,253,1,82,3,1,34,0,2,17,1,3,17,1,255,196,0,31,0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,255,196,0,181,16,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,125,1,2,3,0,4,17,5,18,33,49,65,6,19,81,97,7,34,113,20,50,129,145,161,8,35,66,177,193,21,82,209,240,36,51,98,114,130,9,10,22,23,24,25,26,37,38,39,40,41,42,52,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,115,116,117,118,119,120,121,122,131,132,133,134,135,136,137,138,146,147,148,149,150,151,152,153,154,162,163,164,165,166,167,168,169,170,178,179,180,181,182,183,184,185,186,194,195,196,197,198,199,200,201,202,210,211,212,213,214,215,216,217,218,225,226,227,228,229,230,231,232,233,234,241,242,243,244,245,246,247,248,249,250,255,196,0,31,1,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,255,196,0,181,17,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,119,0,1,2,3,17,4,5,33,49,6,18,65,81,7,97,113,19,34,50,129,8,20,66,145,161,177,193,9,35,51,82,240,21,98,114,209,10,22,36,52,225,37,241,23,24,25,26,38,39,40,41,42,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,115,116,117,118,119,120,121,122,130,131,132,133,134,135,136,137,138,146,147,148,149,150,151,152,153,154,162,163,164,165,166,167,168,169,170,178,179,180,181,182,183,184,185,186,194,195,196,197,198,199,200,201,202,210,211,212,213,214,215,216,217,218,226,227,228,229,230,231,232,233,234,242,243,244,245,246,247,248,249,250,255,219,0,67,0,28,28,28,28,28,28,48,28,28,48,68,48,48,48,68,92,68,68,68,68,92,116,92,92,92,92,92,116,140,116,116,116,116,116,116,140,140,140,140,140,140,140,140,168,168,168,168,168,168,196,196,196,196,196,220,220,220,220,220,220,220,220,220,220,255,219,0,67,1,34,36,36,56,52,56,96,52,52,96,230,156,128,156,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,255,221,0,4,0,22,255,218,0,12,3,1,0,2,17,3,17,0,63,0,223,162,138,40,16,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,1,255,208,223,162,138,40,16,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,1,255,209,223,162,138,40,16,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,1,255,210,223,162,138,40,16,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0]], Here is a screenshot: Output of the Image in a ByteArray
The JSON you posted explains why it is taking so long to parse. It probably takes a while to parse these huge arrays. Instead of having each image as an array of bytes it should just be one base64 encoded string for each image. Something like this: public var formImagesBase64 : [String]? Here is some Swift code that might help you: // Encoding Image let data = image.jpegData(compressionQuality: 1) let b64string = data?.base64EncodedString() // Decoding image let decodedData = Data(base64Encoded: b64string!) let decodedImage = UIImage(data: decodedData!)
sysex from AudioKit's receivedMIDISystemCommand
I am very new to Swift and struggling so please go easy. I am building a macOS project using Swift 4 in XCode 10.1 I am receiving a sysex message from a device using the following code which I got from the MIDIUtility example project supplied with AudioKit: func receivedMIDISystemCommand(_ data: [MIDIByte]) { if let command = AKMIDISystemCommand(rawValue: data[0]) { var newString = "MIDI System Command: \(command) \n" var hexArray = [UInt8]() for bit in data { hexArray.append(bit) //this requires 'public typealias MIDIByte = UInt8' newString.append(String("\(bit) ")) } updateText("\(newString) \n") updateData(hexArray) } updateText("received \(data.count) bytes of data \n\n") } The sysex I am receiving is very different than what I get when using SysEx Librarian or MIDI Monitor to receive the same sysEx. Both of these apps receive 6 packets of 32,110,110,110,110, and 110 bytes. This is what I expect to get from my MIDI device. My code gets 6 packets of 520,1300,20,1538,1294 and 1544 bytes. It looks like the data I want is in these packets, but it is spit up by a bunch of 0's. data as received in MIDI Monitor or SysEx Librarian: some of the data as received by my code Why is my code receiving so much extra data? Is there a way to filter out the unwanted data or will I need to figure out how to use something other that AudioKit for my project?
UIImage generated base64 is larger than it's referenced (local) base64 image
I am capturing an image via a user's Camera (using a library called CameraManager). The captured image is saved to the file disk (for testing purposes only), and also generates a UIImage, which is then converted to a base64 string via: cameraManager.capturePictureWithCompletion({ (image, error) -> Void in let UIImage = UIImageJPEGRepresentation(image!, 0.85)! let base64Image = UIImage.base64EncodedString(options: .lineLength64Characters) ... Now when I select the same image (which was saved to the disk) via an image-picker plugin, I am returned a base64 string (of the same image) and it is a little more than 2.5x smaller than the original captured image that it represents. What could be causing the extra overhead? Could it be something that is being embedded in the UIImage? My other thought was to just save the UIImage to a temp folder, pass the URI to the client, and then have the client worry about converting it to a base64 string to see if it makes the string smaller.
How to use MTLBlitCommandEncoder for copying interlaced video fields into a MTLBuffer
We are working with interlaced HD video. The metal color attachment I use for rendering has the dimensions of one field (1920*540 RGBA). When I try to copy two rendered fields into the same MTLBuffer that has the size of 1920*1080*4 = 8294400 bytes it works only if the destination offset is zero. let commandBuffer = commandQueue.makeCommandBuffer() let blitEncoder = commandBuffer.makeBlitCommandEncoder() blitEncoder.copy(from: attachmentTexture, sourceSlice: 0, sourceLevel: 0, sourceOrigin: MTLOriginMake(0, 0, 0), sourceSize: MTLSizeMake(attachmentTexture.width, attachmentTexture.height, 1), to: destinationBuffer, destinationOffset: 1920*4, destinationBytesPerRow: 1920*4*2, destinationBytesPerImage: destinationBuffer.length) blitEncoder.endEncoding() commandBuffer.commit() For the first field where the destination offset is zero the function works well. The destination buffer is filled for every second row. But when I want to write the second field with the same code into the same MTLBuffer object only with the destinationOffset set to 1920*4 like you see in the code above (to start with the second row in the buffer) then I get an assertion like this: -[MTLDebugBlitCommandEncoder validateCopyFromTexture:sourceSlice:sourceLevel:sourceOrigin:sourceSize:toBuffer:destinationOffset:destinationBytesPerRow:destinationBytesPerImage:options:]:677: failed assertion `totalBytesUsed(8302080) must be <= destinationBuffer length.' The totalBytesUsed are exactly the destination buffer length in bytes plus the offset. So every offset I use in this function will result in this assertion error. Can someone explain me how to use this function correctly because the other way around like creating two MTLTexture objects (odd and even fields) for an incoming video frame works well with similar parameters.
I ran into something like this recently, too. You're passing destinationBuffer.length to the destinationBytesPerImage: parameter. As you've noticed, Metal is adding together the offset and the bytes-per-image value and comparing that against the length of the to: destination buffer (destinationBuffer). It's noticing that offset plus bytes-per-image won't fit into the buffer and refusing to accept that. You may be able to simply pass 0 for destinationBytesPerImage:, since you're not working with a 3D or 2D array texture. If that doesn't work, pass destinationBuffer.length - 1920*4.