BLE: Long Writes are not supported. Is it possible to send data more than 20 bytes?

I have a peripheral device to which I need to send command in the next format:

It is 51 byte in total. But accordingly to the documentation I cannot send on requestWrite more than 20 bytes. What to do in such situation?

Is there any other way to handle it? Or would it be a solution to speak with peripheral through the mobile app using Garmin Mobile SDK for iOS?

Top Replies

All Replies

  • You could try using a companion app as there you wouldn't be using CIQ BLE.  Unless there is a change in System7, the 20 bytes is the max with CIQ BLE.

  • Ok, got it. After some research, I understood that I can split the long bytes array for smaller up to 20 bytes and send it in a queue. 

    But the next question I have is how should I send requests – using Descriptor or Characteristic? I'm a bit confused about the purposes of them.

    When I'm trying to do:

    char.requestWrite([0xff, 0x09, 0x00]b, {
    :writeType => BluetoothLowEnergy.WRITE_TYPE_WITH_RESPONSE,
    });
    I always receive on onCharacteristicWrite status 0 and no response in onCharacteristicChanged. (The peripheral I use should send back a response on that command).
    But when I'm sending message using:
    var cccd = char.getDescriptor(BluetoothLowEnergy.cccdUuid());
    try {
      cccd.requestWrite([0xff, 0x09, 0x00]b);
    } catch (e instanceof Lang.Exception) {
      System.println("e.getErrorMessage()");
    }
    It always returns status 14 (STATUS_WRITE_FAIL) on onDescriptorWrite. 
    Where I could be wrong or what is the difference when to use requestWrite for Descriptor or Characteristic?
  • The only time I do a write to the descriptor is when I turn on Notify.  If you don't do that, you won't see onCharacteristicChanged called in your delegate.  In all other cases, I write to the characteristic.

    Then, if you have Notify on, you should see onCharacteristicChanged if your sensor handles Notify for that characteristic if it notifies for any change.  Based on the sensor, when you write the characteristic, you may not see the notify right away, as I've seen cases where the notify is sent based on a time  so, for example, you only see it once a minute, but you see it every minute, even if nothing changed

    What I typically do, is when an app first connects to a sensor, I write the descriptor to turn on Notify and also read the characteristic as the same time, so I always have the current data.  And don't forget, you need to have a method to queue requests , as you can't do the 2nd request until the first completes.  See my sample for a Raspberry Pi.  "CommQueue.mc) handles the queue, and a request completes, in the BLE Delegate, I handle the next request in the queue.

    https://forums.garmin.com/developer/connect-iq/b/news-announcements/posts/would-you-like-some-raspberry-pi-with-your-connect-iq

  • Thank you for the clarification!

    I did a write to the Descriptor to turn on Notify and after that I made a write to the Characteristic and it returned me a result on onCharacteristicChanged. So, it works now. Thank you!

  • After some time and investigation, I ran into another problem. 

    The peripheral that I use to send and receive data with Garmin watch is expecting that I would send the commands with a delay no more than 100ms.

    I wrote my own function writeLongData to send long data of more than 20 bytes by splitting it to smaller packages up to 20 bytes.

    And the problem is that from time to time commands are sent with a delay greater than 100ms.

    I think that is because I have to wait until the onCharacteristicWrite callback is returned and only after send the next command. Otherwise there will be an error on requestWrite  "Operation already in Progress". So how to handle this problem?

    Here is the code:

    function writeLongData(cccd as Characteristic?, data as ByteArray) as Void {

      var chunkSize = 20;
      var index = 0;
      while (index < data.size()) {
        var chunk = data.slice(index, min(index + chunkSize, data.size()));
        _commQueue.add(cccd, chunk);

        index += chunkSize;
      }
    }
    // _commQueue file
    public function add(char as Characteristic?, data as ByteArray) as Void {
      if (data.size() > 0) {
        queue.add([char, data]);
        if (!isRunning) {
          run();
        }
      }
    }

    public function run() as Void {
      if (queue.size() == 0) {
        isRunning = false;
        return;
      }
      isRunning = true;
      var char = queue[0][0];
      char.requestWrite(queue[0][1], {
         :writeType => BluetoothLowEnergy.WRITE_TYPE_DEFAULT,
      });

      if (queue.size() > 0) {
        queue = queue.slice(1, queue.size());
      }
    }
    // And on onCharacteristicWrite in BluetoothLowEnergy.BleDelegate
    public function onCharacteristicWrite(characteristic as Characteristicstatus as Status) as Void {
      _commQueue.run();
    }