How to get data from an object returned from a Bluetooth device using BLE

I have managed to write a program that connects to my power meter via Bluetooth, I have set up a profile for the power data and retrieved it via BLE. When I try and decode the output I get the following error:

"Details: Could not find symbol 'decodeNumber'"

I imagine this means that the value I am getting in my power characteristic is not a ByteArray but an Object, I am unsure how to get the data I need from this if that is the case. Here is my profile manager code:


import Toybox.BluetoothLowEnergy;

class ProfileManager {

public const POWER_METER_SERVICE = BluetoothLowEnergy.longToUuid(0x6E400001B5A3F393L, 0xE0A9E50E24DCCA9EL);

public const CYCLING_POWER_SERVICE = BluetoothLowEnergy.longToUuid(0x0000181800001000L, 0x800000805F9B34FBL);
public const CYCLING_POWER_MEASURMENT_CHARACTERISTIC = BluetoothLowEnergy.longToUuid(0x00002A6300001000L, 0x800000805F9B34FBL);
public const CYCLING_POWER_FEATURE_CHARACTERISTIC = BluetoothLowEnergy.longToUuid(0x00002A6500001000L, 0x800000805F9B34FBL);
public const CYCLING_POWER_CP_CHARACTERISTIC = BluetoothLowEnergy.longToUuid(0x00002A6600001000L, 0x800000805F9B34FBL);

private const _cyclingPowerDef = {
:uuid => CYCLING_POWER_SERVICE,
:characteristics => [{
:uuid => CYCLING_POWER_MEASURMENT_CHARACTERISTIC
}, {
:uuid => CYCLING_POWER_FEATURE_CHARACTERISTIC
}, {
:uuid => CYCLING_POWER_CP_CHARACTERISTIC
}]
};

//! Register the bluetooth profile
public function registerProfiles() as Void {
BluetoothLowEnergy.registerProfile(_cyclingPowerDef);
}

And my startRead code in device manager:

private function startRead() as Void {
System.println("Start Read");
var device = _device;
if (device != null) {
_cycleService = device.getService(_profileManager.CYCLING_POWER_SERVICE);
var cycleService = _cycleService;
if (cycleService != null) {
_feature = cycleService.getCharacteristic(_profileManager.CYCLING_POWER_FEATURE_CHARACTERISTIC);
_powerMeterData = cycleService.getCharacteristic(_profileManager.CYCLING_POWER_MEASURMENT_CHARACTERISTIC);
}
if (_powerMeterData instanceof Toybox.Lang.ByteArray){
System.println("yup");
}


value.decodeNumber(Lang.NUMBER_FORMAT_UINT8, {});
var pd = _powerMeterData.decodeNumber(Lang.NUMBER_FORMAT_UINT16, {});
System.println(pd);

System.println(_powerMeterData.toString());
}

Any help would be greatly appreciated

  • Ah okay I'll look into that, cheers

  • If you look at the source for the Pi I posted, you'll see CommQueue.mc and PiBLEDelegate.mc.  The basic queue, where I add to the queue in the view.

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

  • I'll look that over thanks for sharing

  • So I've tried to make a basic queue based off the EnviromentModel found in the NordicThingy52 sample this is the code:

    import Toybox.BluetoothLowEnergy;
    import Toybox.Lang;
    import Toybox.WatchUi;

    class PowerMeterBLEModel {

    private var _service as Service?;
    private var _profileManager as ProfileManager;
    private var _pendingNotifies as Array<Characteristic>;

    private var _power as Numeric?;

    //! Constructor
    //! @param delegate The BLE delegate for the model
    //! @param profileManager The profile manager for this model
    //! @param device The current device
    public function initialize(delegate as PowerMeterBLEDelegate, profileManager as ProfileManager, device as Device) {
    _profileManager = profileManager;

    _service = device.getService(profileManager.CYCLING_POWER_SERVICE);

    _pendingNotifies = [] as Array<Characteristic>;

    var characteristic = _service.getCharacteristic(profileManager.CYCLING_POWER_MEASURMENT_CHARACTERISTIC);
    if (null != characteristic) {
    _pendingNotifies = _pendingNotifies.add(characteristic);
    }

    // activateNextNotification();
    }

    //! Handle a characteristic being changed
    //! @param char The characteristic that changed
    //! @param value The updated value of the characteristic
    public function onCharacteristicChanged(char as Characteristic, value as ByteArray) as Void {
    //if(char.getUuid() == _profileManager.CYCLING_POWER_MEASURMENT_CHARACTERISTIC) {
    _profileManager.CYCLING_POWER_MEASURMENT_CHARACTERISTIC;
    processPowerValue(value);
    System.println("Changed called in Model");
    //}
    }


    public function onCharacteristicRead(char as Characteristic, value as ByteArray) as Void {
    //if(char.getUuid() == _profileManager.CYCLING_POWER_MEASURMENT_CHARACTERISTIC) {
    System.println("read called in model");
    _profileManager.CYCLING_POWER_MEASURMENT_CHARACTERISTIC;
    processPowerValue(value);

    //}
    }

    //
    //
    //! Get the temperature
    //! @return The temperature
    public function getPower() as Numeric? {
    return _power;
    }

    //
    //! Write the next notification to the descriptor
    private function activateNextNotification() as Void {
    if (_pendingNotifies.size() == 0) {
    return;
    }

    var char = _pendingNotifies[0];
    var cccd = char.getDescriptor(BluetoothLowEnergy.cccdUuid());
    cccd.requestWrite([0x01, 0x00]b);
    }

    private function processPowerValue(value as ByteArray) as Void {
    _power = value.decodeNumber(Lang.NUMBER_FORMAT_SINT16, {});
    System.println(_power);
    }
    }

    It is instantiated in the onCharacteristicRead function within the Delegate

    public function setUpModel(device as Device) as Void{
    var dataModel = new $.PowerMeterBLEModel(self, _profileManager, device);
    }

    public function onCharacteristicRead(char as BluetoothLowEnergy.Characteristic,status as BluetoothLowEnergy.Status,
    value as Lang.ByteArray) as Void {

     setUpModel();
    if ((null != _onCharRead) && _onCharRead.stillAlive()) {
    (_onCharRead.get() as PowerMeterBLEModel).onCharacteristicRead(char, value);
    }
    }

    However, when I run it I get the following error:

    Error: Symbol Not Found Error
    Details: Could not find symbol 'onCharacteristicRead'
    Stack:
    - onCharacteristicRead() at C:\Users\PC-User\eclipse-workspace\PowerMeterBLE\source\PowerMeterBLEDelegate.mc:75 0x1000035c

    There definitely is an onCharacteristicRead() in PowerMeterEnviromentModel and it is instantiated so I don't really know what's going on.

  • Where are you defining the class for your BLE delegate?

    i.e.

    class MyBLEDelegate extends Ble.BleDelegate {
    I see 
    class PowerMeterBLEModel {
    but that's not the same
  • Its a different class defined as below:

    import Toybox.BluetoothLowEnergy;
    import Toybox.Lang;
    import Toybox.System;

    class PowerMeterBLEDelegate extends BluetoothLowEnergy.BleDelegate {
    private var _profileManager as ProfileManager;
    private var dataModel as PowerMeterBLEModel;
    private var _onScanResult as WeakReference?;
    private var _onConnection as WeakReference?;
    private var _onCharRead as WeakReference?;

    private var _onCharChanged as WeakReference?;
    private var _onCharWrite as WeakReference?;

    //! Constructor
    //! @param profileManager The profile manager
    public function initialize(profileManager as ProfileManager) {
    BleDelegate.initialize();
    _profileManager = profileManager;
    }

    public function onConnectedStateChanged(device as Device, state as ConnectionState) as Void {
    var onConnection = _onConnection;
    if (onConnection != null) {
    if (onConnection.stillAlive()) {
    (onConnection.get() as DeviceManager).procConnection(device);
    }
    }
    }

    public function setUpModel(device as Device) as Void{
    var dataModel = new $.PowerMeterBLEModel(self, _profileManager, device);
    }

    //! Handle new Scan Results being received
    //! @param scanResults An iterator of new scan result objects
    public function onScanResults(scanResults as Iterator) as Void {
    //System.println("Hello");
    for (var result = scanResults.next(); result != null; result = scanResults.next()) {
    if (result instanceof ScanResult) {
    //System.println(_profileManager.POWER_METER_SERVICE);
    if (contains(result.getServiceUuids(), _profileManager.POWER_METER_SERVICE)) {
    System.println("HELLLLLOOOOO");
    broadcastScanResult(result);
    }
    }
    }
    }

    // //! Handle pairing and connecting to a device
    // //! @param device The device state that was changed
    // //! @param state The state of the connection

    // public function onCharacteristicChanged(char as Characteristic, value as ByteArray) as Void {
    // System.println("changed called in delegate");
    // if ((null != _onCharChanged) && _onCharChanged.stillAlive()) {
    // (_onCharChanged.get() as PowerMeterBLEModel).onCharacteristicChanged(char, value);
    // }
    // }

    public function onCharacteristicRead(char as BluetoothLowEnergy.Characteristic,status as BluetoothLowEnergy.Status,
    value as Lang.ByteArray) as Void {
    System.println("not alive");
    System.println(_onCharRead);
    setUpModel();
    if ((null != _onCharRead) && _onCharRead.stillAlive()) {
    System.println("Read called in delegate");
    //dataModel.processPowerValue(value);
    (_onCharRead.get() as PowerMeterBLEModel).onCharacteristicRead(char, value);

    }
    }
    //! Handle the completion of a write operation on a characteristic
    //! @param characteristic The characteristic that was written
    //! @param status The BluetoothLowEnergy status indicating the result of the operation
    // public function onCharacteristicWrite(characteristic as Characteristic, status as Status) as Void {
    // var onCharWrite = _onCharWrite;
    // if (onCharWrite != null) {
    // if (onCharWrite.stillAlive()) {
    // (onCharWrite.get() as DeviceManager).procCharWrite(characteristic, status);
    // }
    // }
    // }


    // public function onCharacteristicRead(characteristic as BluetoothLowEnergy.Characteristic,status as BluetoothLowEnergy.Status,
    // value as Lang.ByteArray) as Void {
    //
    // var onCharRead = _onCharRead;
    // if(value instanceof Lang.ByteArray){
    // System.println("Its a ByteArray in read");
    // }
    // //System.println("read");
    // //(onCharRead.get() as DeviceManager).procCharWrite(value);
    // System.println(" state " + status );
    // if ((null != onCharRead) && onCharRead.stillAlive()) {
    // (onCharRead.get() as DeviceManager).procCharWrite(value);
    // System.println("read");
    //
    // }
    // }

    // public function onCharacteristicChanged(characteristic as BluetoothLowEnergy.Characteristic,value as Lang.ByteArray) as Void {
    // System.println(value);
    // var onCharChanged = _onCharChanged;
    // if(value instanceof Lang.ByteArray){
    // System.println("Its a ByteArray in changed");
    // }
    // //System.println("read");
    // //(onCharRead.get() as DeviceManager).procCharWrite(value);
    // if ((null != onCharChanged) && onCharChanged.stillAlive()) {
    // (onCharChanged.get() as DeviceManager).procCharWrite(value);
    // System.println("read in changed");
    //
    // }
    // }

    // public function notifyCharacteristicChanged(model as DeviceManager) as Void {
    // _onCharChanged = model.weak();
    // System.println("Notify Char Changed");
    // }

    //
    // private function processValue(value as ByteArray) as Void {
    // var _power = value.decodeNumber(Lang.NUMBER_FORMAT_SINT16, {});
    // WatchUi.requestUpdate();
    // }
    //

    //! Store a new manager to manage scan results
    //! @param manager The manager of the scan results
    public function notifyScanResult(manager as DeviceManager) as Void {
    _onScanResult = manager.weak();
    }

    //! Store a new manager to manage device connections
    //! @param manager The manager for devices
    public function notifyConnection(manager as DeviceManager) as Void {
    _onConnection = manager.weak();
    }

    //! Store a new manager to handle characteristic writes
    //! @param manager The manager for characteristics
    public function notifyCharWrite(manager as DeviceManager) as Void {
    _onCharWrite = manager.weak();
    }
    public function notifyCharRead(manager as DeviceManager) as Void {
    System.println("Notify Char Read");
    _onCharRead = manager.weak();
    }

    // public function notifyCharChanged(manager as DeviceManager) as Void {
    // System.println("CharChanged");
    // _onCharChanged = manager.weak();
    // }

    //! Broadcast a new scan result
    //! @param scanResult The new scan result
    private function broadcastScanResult(scanResult as ScanResult) as Void {
    System.println("broadcast");
    var onScanResult = _onScanResult;
    if (onScanResult != null) {
    if (onScanResult.stillAlive()) {
    (onScanResult.get() as DeviceManager).procScanResult(scanResult);
    }
    }
    }

    //! Get whether the iterator contains a specific uuid
    //! @param iter Iterator of uuid objects
    //! @param obj Uuid to search for
    //! @return true if object found, false otherwise
    private function contains(iter as Iterator, obj as Uuid) as Boolean {
    for (var uuid = iter.next(); uuid != null; uuid = iter.next()) {
    if (uuid.equals(obj)) {
    return true;
    }
    }

    return false;
    }
    }

  • Hi, Jim, I  wanna ask an unrelated question. If I want to send a pack to a BLE device with this

    _instantData.requestWrite(DataEncoding.packDisconnectByteArray(), {
    :writeType => BluetoothLowEnergy.WRITE_TYPE_WITH_RESPONSE,
    });
    and the device will send me a byte array as a reply. How to process the byte array?

    onCharacteristicWrite(characteristic as BluetoothLowEnergy.Characteristic, status as BluetoothLowEnergy.Status) as Void

    After requesting a write operation on a characteristic using Characteristic.requestWrite() this function will be called when the operation is completed.

    Parameters:

    Since:

    API Level 3.1.0

    This API has only 2 params.

    OR do I have to call this API for the response, which looks async but sync

    onCharacteristicRead(characteristic as BluetoothLowEnergy.Characteristic, status as BluetoothLowEnergy.Status, value as Lang.ByteArray) as Void

  • Yo won't see any data coming back in onCharacteristicWrite -it's just it won't be called until the sensor has processed it if you ask for a response

    In onCharacteristicWrite(), you can do a readRequest for the data.  Recall, with CIQ BLE, you need to have your own queueing.  You can't just do

    write

    read

    You need to do

    write

    (wait for onCharacterisicWrite)

    read

    Depending on the sensor and the characteristic, you can turn on Notify, so if something changes, you see that in onCharacteristicChanged() without doing a readRequest.  Depending on the sensor, the notify might happen right away or could be done on a time basis, where the notify happens say every 30 seconds (for example), if it's needed or not.