Hi,
I am new to the forum and I'd like to make an application that transfer a file from iOS to a microcontroller using the nano33 as a BLE-UART(AT) converter.
Now it works as a basic concept. I do the following.
For Swift:
private func sendNextFileChunkToPeripheral()
{
if ftpIsDownstreaming == false
{
return
}
guard let peripheral = getConnectedPeripheral()?.peripheral else {
return
}
/* done */
if ftpDownstreamChunks.count == 0
{
sendFtpCtlToPeripheral(what: .FileEnd)
ftpIsDownstreaming = false
return
}
// Find the target characteristic by its UUID
if let characteristic = discoveredServices[targetFtpServiceUUID]?[CBUUID(string: "F47AC10B-58CC-4372-A568-000000000002")],
let chunk = ftpDownstreamChunks.first
{
// Send the data to the peripheral
if chunk.count >= 4 {
let value = chunk.withUnsafeBytes { $0.load(as: UInt32.self) }
print("Send chunk offset: \(value)")
}
peripheral.writeValue(Data(chunk), for: characteristic, type: .withoutResponse)
}
}
func sendFileToPeripheral(offset: Int, data: Data)
{
ftpIsDownstreaming = false
guard let peripheral = getConnectedPeripheral()?.peripheral else
{
return
}
let chunkMtu = min(Int(peripheral.maximumWriteValueLength(for: .withoutResponse)), 244) - 4 // Ensure MTU ≤ 244 which should be optimal
/* pack chunks */
var chunks: [[UInt8]] = []
var done: Int = 0
while done < data.count
{
/* getting chunk */
let chunkOffset: Int = offset + done
let remaining = data.count - done
let chunkSize = min(chunkMtu, remaining)
let chunk = data.subdata(in: done ..< (done + chunkSize))
/* format chunk */
var offsetLE = UInt32(chunkOffset).littleEndian
let offsetBytes = withUnsafeBytes(of: &offsetLE) { Array($0) } // [UInt8]
chunks.append(offsetBytes + chunk)
done += chunkSize
}
DispatchQueue.main.async {
self.ftpDownstreamChunks.removeAll();
self.ftpDownstreamChunks.append(contentsOf: chunks)
self.ftpIsDownstreaming = true
self.sendNextFileChunkToPeripheral()
}
}
func handleFtpCharacteristic(_ charUUID: CBUUID, data: Data, peripheral: CBPeripheral) {
if charUUID == CBUUID(string: "F47AC10B-58CC-4372-A568-000000000001") {
/* controll */
parseFtpControl(data)
}
else if charUUID == CBUUID(string: "F47AC10B-58CC-4372-A568-000000000003")
{
let ackOffset = data.withUnsafeBytes { $0.load(as: UInt32.self) }
print("ftp acknowledged chunk, todo check number \(ackOffset)")
//ftpDownstreamChunks.removeFirst()
//sendNextFileChunkToPeripheral()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
self.ftpDownstreamChunks.removeFirst()
self.sendNextFileChunkToPeripheral()
}
}
else
{
print("Unhandled charUUID(mqtt): \(charUUID.uuidString)")
}
}
In words: I send out a chunk of the file and wait until I receive an ACK on a different characteristic and then send the next chunk. Please note the 50ms delay! Yes I know I could use .withResponse here, but bare with me.
On the Nano33 I do:
/* ftp channel handler */
void BLEwrapper::ftpChannelCharacteristicWritten(BLECharacteristic &characteristic)
{
if(serial == nullptr)
return;
if(characteristic.uuid() == ftpRetrCharacteristic.uuid())
{
const uint8_t *data = characteristic.value();
uint32_t length = characteristic.valueLength();
if(length < 4)
return;
/* separate header from payload */
const uint32_t *offset = (uint32_t *) data;
data += 4;
length -= 4;
//FTPR=offset,len,crc\n<DATA>
char hdr[32];
const uint16_t hdrLen = snprintf(hdr, sizeof(hdr), "FTPR=%lX,%lX,%X\n<", *offset, length, CRC::crc16_ccitt(data, length));
uint16_t msgSize = hdrLen + length + 1; //hdr + data + '>'
uint8_t *msg = new uint8_t[msgSize];
memcpy(msg, hdr, hdrLen);
memcpy(msg+hdrLen, data, length);
memcpy(msg+hdrLen+length, ">", 1);
if(dataMsg_enqueue(msg, msgSize) == false)
{
delete [] msg;
serial->print("+DMSG=full\n");
}
else
{
/* Ack back */
ftpRetrAckCharacteristic.writeValue(offset, 4, false);
serial->print("+DMSG=new\n");
}
return;
}
As soon as I receive something. I store it so that AT serial can collect it later on...
This works so far and I achieve around 2kb/s so far.
BUT: there is the 50ms delay which leaves a lot of questions! Why do I need it. If I omit it, the Nano33 will get stuck after 4 chunks and does not react for 30sec (even for AT commands). Thou' it does not crash, because after 30sec it will report the next chunk.
So my question is: I want to get rid of the magic 50ms delay. Does there exist a hook or something that indicated we have some space left to receive new data? Or in general: how to boost the throughput of the Nano33??
Many thanks!
Juergen