Use bluetooth HFP profile to get battery level from headset

2.3k views Asked by At

I am trying to get the battery level of my headset device with bluetooth 4.1 with AT commands from Windows using the Handsfree Profile (HFP).

I have extracted the bluetooth log from my Android phone (which is able to get the battery level) and the headset is using HFP to send AT command with the battery information (AT+IPHONEACCEV), this is one of the packets extracted from the log (with wireshark):

Frame 1122: 38 bytes on wire (304 bits), 38 bytes captured (304 bits)
Encapsulation type: Bluetooth H4 with linux header (99)
Arrival Time: Jul 16, 2020 12:18:16.449232000 Romance Daylight Time
[Time shift for this packet: 0.000000000 seconds]
Epoch Time: 1594894696.449232000 seconds
[Time delta from previous captured frame: 0.001885000 seconds]
[Time delta from previous displayed frame: 0.018263000 seconds]
[Time since reference or first frame: 21.363811000 seconds]
Frame Number: 1122
Frame Length: 38 bytes (304 bits)
Capture Length: 38 bytes (304 bits)
[Frame is marked: False]
[Frame is ignored: False]
Point-to-Point Direction: Received (1)
[Protocols in frame: bluetooth:hci_h4:bthci_acl:btl2cap:btrfcomm:bthfp]
Bluetooth
    [Source: GNAudio_xxxxx (AA:BB:CC:DD:EE:FF)]
    [Destination: SamsungE_xxxxx (FF:EE:DD:CC:BB:AA)]
Bluetooth HCI H4
    [Direction: Rcvd (0x01)]
    HCI Packet Type: ACL Data (0x02)
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Bluetooth RFCOMM Protocol
    Address: E/A flag: 1, C/R flag: 0, Direction: 0, Channel: 2
    Control: Frame type: Unnumbered Information with Header check (UIH) (0xef), P/F flag: 0
    Payload length: 25
    Frame Check Sequence: 0xbf
Bluetooth HFP Profile
    [Role: HS - Headset (2)]
    AT Stream: AT+IPHONEACCEV=2,1,5,2,0\r
    Command 0: +IPHONEACCEV
        Command Line Prefix: AT
        Command: +IPHONEACCEV (Apple Bluetooth Headset Battery Level Indication)
        Type: Action Command (0x003d)
        Parameters
            Count: 2
            Key: Battery Level (1)
            Value: 5
            Key: Dock State (2)
            Value: 0

Now, I am trying to do get this AT commands from the headset in my Windows C# application using the 32feet (InTheHand.Net.Bluetooth) library. To do so I try to connect to the headset using BluetoothService.Handsfree:

BluetoothDevicePicker picker = new BluetoothDevicePicker();
BluetoothDeviceInfo device = await picker.PickSingleDeviceAsync();
BluetoothClient cli = new BluetoothClient();

//device.SetServiceState(BluetoothService.Handsfree, true);

cli.Connect(device.DeviceAddress, BluetoothService.Handsfree);
if (cli.Connected) MessageBox.Show("OK");

NetworkStream stream;

stream = cli.GetStream();
if (stream.CanRead)
{
    byte[] buff = new byte[1024];
    int n = 0;
    StringBuilder str = new StringBuilder();
    while (stream.DataAvailable)
    {
        n = await stream.ReadAsync(buff, 0, buff.Length);
        str.AppendFormat("{0}", Encoding.ASCII.GetString(buff, 0, n));
    }
    MessageBox.Show(str.ToString());
}

When executing the code it gives the following error in cli.Connect(device.DeviceAddress, BluetoothService.Handsfree);:

System.Net.Sockets.SocketException: 'A socket operation failed because the destination host was down'

With cli.Connect(device.DeviceAddress, BluetoothService.SerialPort);it connects (so the host it is not down) but i do not get any reply from the stream.ReadAsync call.

How can i do this? Thanks!

1

There are 1 answers

0
Rast On

I've found another attempt to implement this: https://github.com/SpartanX1/bluetooth_classic_battery_windows

In the README it says that device should be paired but disconnected. This way when you run the code, socket is able to connect and send/read AT reports. And device is switched to connected state automatically. This works but is absolutely insane.

If device is already connected, socket returns same error (destination host was down).