ANT Battery Level for LIGHTS

I don't expect anyone will know this. Kind of a corner case. But frustrating that there doesn't seem to be a solution.

I can get the battery levels for my ANT Shifters, Radar, Power Meters, Speed sensors, etc. Unfortunately, HEART RATE is still not supported by CIQ.

I can even get it for my Headlight and Taillight.

For a 24 hr ride I have coming up, I'll have two ANT headlights I can swap out. The issue is, I can't see distinctions between the two headlights to report on their individual battery levels.

I can POLL the Light Network and see I have two devices. But in the Page 0x01 onMessage(msg) where the battery status is reported, there is no device ID. And "msg.deviceNumber" is not populated.

So both headlights will send a Page 0x01. With their battery status (eg: GOOD, OK, CRITICAL, etc). But there is no way to know which headlight is reporting. Not a big deal. But if anyone has played with ANT LIGHTS and knows how to grab the device ID from the responding lights and can tie that ID to the Page 0x01 onMessage... let me know. Thanks!!

  • I'm not 100% sure about this, 'cause as I wrote I only ever tested with HR sensor and only 1 at a time, but MAYBE it depends on the deviceConfig you send to GenericChannel.setDeviceConfig() before GenericChannel.open().
    But from what I see in my code I have deviceType => 0x78 /* HR sensor */ so it looks like you're right. However the documenttion also has: DEFAULT_DEVICE_TYPE = 1, so maybe if you try that then it'll connect to any type?

  • I managed to read the battery info from the HR sensor's message:

        function parse(payload as Array<Number>) as Void {
    
    // this is the "right" way to deal with pages, though it adds some code
            var pageChangeToggleBit = payload[0] & 0x80 ? 1 : 0;
            if (!firstPage && !toggleBitHasBeenDetected) {
                if (pageChangeToggleBit != lastPageChangeToggleBit) {
                    toggleBitHasBeenDetected = true;
                }
            }
            firstPage = false;
            lastPageChangeToggleBit = pageChangeToggleBit;
    
            if (toggleBitHasBeenDetected) {
    // but probably for POC even this code is enough:
                var pageNumber = payload[0] & 0x7F;
                if (pageNumber == 7) {
                    batteryLevelPercentage = payload[1] & 0xFF;
                    // fractionalBatteryVoltage = payload[2] & 0xFF;
                    // var batteryBitField = payload[3] & 0xFF;
                    batteryStatus = payload[3] & 0x70 >> 4;
                    log("batteryStatus:" + batteryStatus + ", " + batteryLevelPercentage + '%');
                }
            }
        }
    // optionally you an use something like this to "translate" batteryStatus to human readable.
    // note that you should only index this array if batteryStatus < 6
    const BATTERY_STATUS_STR = ["", "New", "Good", "OK", "Low", "Critical"] as Array<String>;
    

  • Thanks Gavriel! I still am a little confused. Here is why. I access all the other devices via specific listeners from ANTPLUS classes. And there is no HR class. Here is my initialization and some parts from my RADAR code. I'm sure I'm missing a key concept to get data from a HR strap when no specific ANTPLUS class exists to setup the listener... Sorry for being dense.

    // this is in the main MC file
    // NOTE: there is no HEART RATE class
    
    class DeviceView extends Toybox.WatchUi.DataField {
        function initialize() {
            DataField.initialize();
            if (AntPlus has :BikeRadar) { RADAR[LISTENER] = new MyRadar(); RADAR[SENSOR] = new AntPlus.BikeRadar(RADAR[LISTENER]); }
            if (AntPlus has :BikePower) { POWERR[LISTENER] = new MyPower(); POWERR[SENSOR] = new AntPlus.BikePower(POWERR[LISTENER]); }
            if (AntPlus has :Shifting) { SHIFTINGF[LISTENER] = new MyShifting(); SHIFTINGF[SENSOR] = new AntPlus.Shifting(SHIFTINGF[LISTENER]); }
            if (AntPlus has :BikeSpeed) { SPEED[LISTENER] = new MySpeed(); SPEED[SENSOR] = new AntPlus.BikeSpeed(SPEED[LISTENER]); }
            if (AntPlus has :BikeCadence) { CADENCE[LISTENER] = new MyCadence(); CADENCE[SENSOR] = new AntPlus.BikeCadence(CADENCE[LISTENER]); }
            if (AntPlus has :BikeSpeedCadence) { SPDCAD[LISTENER] = new MySpeedCadence(); SPDCAD[SENSOR] = new AntPlus.BikeSpeedCadence(SPDCAD[LISTENER]); }
            if (AntPlus has :LightNetwork) { HEADLIGHT[LISTENER] = new MyLights(); HEADLIGHT[SENSOR] = new AntPlus.LightNetwork(HEADLIGHT[LISTENER]); }
        }
    
    
    // I have a separate MC for each device type
    // Ideally I'd have one for HR, but CIQ doesn't support HR
    // This is an example from some of my RADAR code
    // All these calls are only related to the RADAR device
    
    class MyRadar extends AntPlus.BikeRadarListener {
        function initialize() { BikeRadarListener.initialize(); }
    
        function onDeviceStateUpdate(data) { }
    
        function onBatteryStatusUpdate(data) {
    	RADAR[BATSTAT] = data.batteryStatus;
    	RADAR[VOLTAGE] = data.batteryVoltage;
    
        function onMessage(msg) {
    	var payload = msg.getPayload(); //get the data payload
            var page = payload[0] & 0x7F;
    	if (page != 0x52 || msg.deviceNumber == 0 || msg.deviceNumber == null) { return; }
    	RADAR[BATSTAT] = (payload[7] >> 4) & 0x07;
            RADAR[VOLTAGE] = (payload[7] & 0x0F) + (payload[6] / 256.0);
    

  • Wow. Thank you!!

    I use Eclipse and the Simulator's ANT controls seem broken. Any luck exercising this logic in the sim?

    Also, I use an H10 strap. I don't think if my EDGE pairs with my strap that this CIQ app can also grab data from it, right? But I think I can pair from my EDGE to the H10 via Bluetooth, and then this app should also be able to get data from the strap.

    I'll play around with it later tonight.

  • Nice! Got it working on my EDGE 1030. It searched for and found my ForeRunner Optical HR that is broadcasting HR via ANT. And is displaying my HR. So now I'll work on getting battery status. THANKS!!!

  • Hey - I'm making some updates to your code. That is REALLY helpful. Take a look at this one area, which might be an issue. onStart() does this. And both the initialization of AuxHRSensor and the open() function both instantiate data = new AuxHRData(). No big deal. Is that intentional? Anyway, great stuff!! I turned it into a regular datafield so I could display multiple lines on the screen, and got rid of the FIT writes. Next step is to try to grab battery status.

    mSensor = new AuxHRSensor(mAntID); mSensor.open();

  • It's not my code. I found this a year ago and used it also in my datafield. I added lots of new features, look, and optimization since. You can see the app here: forums.garmin.com/.../ant-hrm-heart-rate-monitor-data-field

    If you put the code I sent above to AuxHRData.parse then you'll get the battery status

  • THANKS!!!

    This will do what I want... get the current battery status of my HR strap. And with my H10 I can concurrently pair to my EDGE via BLE and to the datafield via ANT. That doesn't work for most HR straps since most don't offer both BLE and ANT.

    The nice thing about the ANTPLUS listener is that it does support getting ANT messages in CIQ while being concurrently paired with the device. I know Former Member has bumped the request to add the ANTPLUS listener for HR devices. A glaring gap in the set of devices supported.

  • For the lights that have only a few hours of battery life it probably makes more sense to get a notification during a ride that the battery is low. For the HRM probably less, because it has hundreds of hours of battery life, and you can see it here: https://connect.garmin.com/modern/ so maybe it's not that important to get a notification during a ride. If you connected your H10 either by BT or ANT to the device then it'll show up here (though strangely the battery "level" reported by BT and ANT are "different"