Sensor list

I'm writing a start screen for my first app and I'm trying to display on connectivity overview which would include GPS status, phone connection status, HR, power meter and Di2 device list with battery statuses, where applicable. AntPlus.Shifting will probably cover the Di2 and AntPlus BikePower the power meter but where I don't see any subclass for the HR. What is that under? FitnessEquiment?

And can you use the AntPlus.DeviceListener onDeviceStateUpdate for detecting when any AntPlus device changes status? 

Thanks in advance,

  Nik

  • I have a similar problem DaveBrillhart, but with a Garmin SpeedSensor.  I can see batt status in my Edge530 device settings, but not in my app (reports null, like you).  I do, however, see battery status for my Power Meter in my app now.  I'm using the "polling method" - similar code but without the listener.  Is your cadence sensor a garmin variety?

  • Hi. Yes a Garmin sensor. I’ll update my code to scan my other devices and see if I can get battery status. Power, Speed and Cadence. Will let you know.

  • Did you ever figure out how to capture the battery status of your Polar H10 strap from CIQ code? If so, how? I'll discard my Wahoo and Garmin HR straps if I can get this working.

  • Did you ever figure out how to capture the battery status of your Polar H10 strap from CIQ code? If so, how?

    Yes I did. Since it is not accessible via AntPlus API at all, I used two different methods:

    1. Using Ant API. This is bit more complex than AntPlus API, but not horribly. Need to configure the channel properly, and handle the pairing, but after that it is more or less parsing data from data pages in onMessage()

    2. Using BLE. I connect to the sensor via BLE API, and then read out the battery status info from there. Problem with BLE is that older, or lower end, devices do not support it via CIQ. Also to me BLE was causing crashes, which are now fixed in CIQ 3.2.0, but also rollout of that to all devices is slow.

    Good thing about AntPlus API is that you can get data from devices which are connected to your Garmin device via normal way.

    Both of my methods require that it is NOT connected. This is why dual-HR sensor like H10 is good, because if I connect it via Ant+ I can use BLE to get the data, and if connected via BLE I use Ant+.

    This mean if I have Ant+ only HR sensor connected and in use, there is no way to get the battery status. Same for BLE only.

  • Great to know! Since I use my HR strap for HR data, I'd need a dual-channel strap. My Garmin HR strap is both ANT+ and BT. The HR sensor does report battery status to my Garmin Edge computer even while connected and providing HR data. So it must be a limitation of the CIQ framework that doesn't allow us access to those in-band messages. I'd love to see the ANT API channel setup code. Sounds like once I get that setup (paring to a hardcoded sensor, since I can't connect/pair it from the Edge device itself?), then my onMessage logic would pretty much handle parsing the payload? Thanks for all the great tips and examples you provide to this forum!

  • I'd love to see the ANT API channel setup code. Sounds like once I get that setup (paring to a hardcoded sensor, since I can't connect/pair it from the Edge device itself?), then my onMessage logic would pretty much handle parsing the payload?

    Here it comes Slight smile

    using Toybox.System as Sys;
    using Toybox.Ant;
    
    class MyAntListener extends Ant.GenericChannel {
        var myView = null;
        var deviceCfg;
        var searching = false;
        var channelInitDone = false;
    
        const PAGE_MANUFACTURER_INFO = 0x02;
        const PAGE_PRODUCT_INFO      = 0x03;
        const PAGE_BATTERY_STATUS    = 0x07;
    
        function initialize(view) {
            Sys.println("AntListener::initialize");
            myView = view;
    
            var chanAssign = new Ant.ChannelAssignment(Ant.CHANNEL_TYPE_RX_NOT_TX, Ant.NETWORK_PLUS);
    
            try {
                GenericChannel.initialize(method(:onMessage), chanAssign);
                channelInitDone = true;
            } catch (e instanceof Ant.UnableToAcquireChannelException) {
                Sys.println(e.getErrorMessage());
                channelInitDone = false;
            }
        }
    
        function start(antId) {
            if (channelInitDone == false) { return; }
    
            Sys.println("AntListener::start: antId=" + antId);
    
            // Set the configuration
            deviceCfg = new Ant.DeviceConfig( {
                :deviceNumber => antId,
                :deviceType => 120,
                :transmissionType => 0,
                :messagePeriod => 8070,
                :radioFrequency => 57,
                :searchTimeoutLowPriority => 10,
                :searchTimeoutHighPriority => 0,
                :searchThreshold => 0} );
    
            GenericChannel.setDeviceConfig(deviceCfg);
            open();
        }
    
        function open() {
            Sys.println("AntListener::open");
            searching = true;
            GenericChannel.open();
        }
    
        function onMessage(msg) {
            var payload = msg.getPayload();
    
            if (Ant.MSG_ID_BROADCAST_DATA == msg.messageId) {
                if (searching == true) {
                    deviceCfg = GenericChannel.getDeviceConfig();
                    searching = false;
                }
                
                var page = payload[0] & 0x7F;
    
                if (PAGE_MANUFACTURER_INFO == page) {
                    Sys.println("Ant: [RX] " + payload + " [" + msg.deviceNumber + "], PAGE_MANUFACTURER_INFO");
                }
                else if (PAGE_PRODUCT_INFO == page) {
                    Sys.println("Ant: [RX] " + payload + " [" + msg.deviceNumber + "], PAGE_PRODUCT_INFO");
                }
                else if (PAGE_BATTERY_STATUS == page) {
                    Sys.println("Ant: [RX] " + payload + " [" + msg.deviceNumber + "], PAGE_BATTERY_STATUS");
    
                    var percentage = null;
                    if (payload[1] != 0xFF) {
                        percentage = payload[1];
                    }
    
                    var state = (payload[3] >> 4) & 0x7;
    
                    var voltage = (payload[3] & 0xF);
                    if (voltage != 0xF) {
                        voltage = voltage + (payload[2] / 256.0);
                    } else {
                        voltage = 0.00;
                    }
    
                    Sys.println("Ant: Battery Status: " + percentage + "%, state: " + state + ", voltage: " + voltage);
                }
            }
        }
    }

    So you instantiate MyAntListener, and call start(AntId). You can give your sensor's AntId, or 0 which will connect to first HR sensor type (type 120) it finds. 

    Other things to note, with HR sensors the Battery State page is 7 (not 4), and it may contain additionally a percentage value. You can also implement the common page 82 handling as previously if you want (which will not have percentage though).

    Have fun Slight smile

  • THANK YOU!! Great stuff.

  • THANK YOU!

    Welcome :) 

    That is not quite the full story, in addition you should be able to handle device search timeouts, and disconnects etc. to reconnect properly. Take a look at MO2Display, GenericChannelBurst, MoxyField SDK code samples for more info.

    And then you can start implementing your own page request function, if you want data faster Slight smile