I am using the following code to make an ftms service. This service is easily visible on the mobile phone as shown below but the python script below doesn't show that and gives some unknown junk service shown in the image:
My esp32 code:
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLEAdvertising.h>
#include <BLE2902.h>
// External globals (dummy values) bool fetchingAllowed = false; int modeInt = 0; int16_t powerTarget = 0; int16_t windspeed = 0; int16_t grade = 0; uint8_t crr = 0; uint8_t crw = 0;
#define SERVICE_FITNESS_MACHINE_UUID 0x1826
#define fitnessMachineService BLEUUID((uint16_t)SERVICE_FITNESS_MACHINE_UUID)
#define CHAR_INDOOR_BIKE_DATA_UUID 0x2AD2
#define CHAR_FITNESS_MACHINE_FEATURE_UUID 0x2ACC
#define CHAR_FITNESS_MACHINE_CONTROL_POINT_UUID 0x2AD9
#define CHAR_FITNESS_MACHINE_STATUS_UUID 0x2ADA
#define CHAR_INDOOR_BIKE_DATA_SIZE 13 const uint16_t indoorBikeDataCharacteristicDef = 0b0000100001010100; uint16_t speedOut = 0; uint16_t cadenceOut = 0; int16_t powerOut = 0; uint8_t bufferIndoorBikeData[CHAR_INDOOR_BIKE_DATA_SIZE] = { (uint8_t)(indoorBikeDataCharacteristicDef & 0xff), (uint8_t)(indoorBikeDataCharacteristicDef >> 8), (uint8_t)(speedOut & 0xff), (uint8_t)(speedOut >> 8), (uint8_t)(cadenceOut & 0xff), (uint8_t)(cadenceOut >> 8), 0x0, 0x0, 0x0, (uint8_t)(powerOut & 0xff), (uint8_t)(powerOut >> 8), 0x0, 0x0, };
#define CHAR_FITNESS_MACHINE_FEATURE_SIZE 8 uint8_t bufferFitnessMachineFeature[CHAR_FITNESS_MACHINE_FEATURE_SIZE] = {0x86, 0x50, 0x00, 0x00, 0x0C, 0xE0, 0x00, 0x00};
BLECharacteristic fitnessMachineFeatureCharacteristic (BLEUUID((uint16_t)CHAR_FITNESS_MACHINE_FEATURE_UUID), BLECharacteristic::PROPERTY_READ); BLECharacteristic indoorBikeDataCharacteristic (BLEUUID((uint16_t)CHAR_INDOOR_BIKE_DATA_UUID), BLECharacteristic::PROPERTY_NOTIFY); BLECharacteristic fitnessMachineControlPointCharacteristic (BLEUUID((uint16_t)CHAR_FITNESS_MACHINE_CONTROL_POINT_UUID), BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_INDICATE); BLECharacteristic fitnessMachineStatusCharacteristic (BLEUUID((uint16_t)CHAR_FITNESS_MACHINE_STATUS_UUID), BLECharacteristic::PROPERTY_NOTIFY);
BLEAdvertisementData advertisementData = BLEAdvertisementData(); bool deviceConnected = false;
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer)
{
deviceConnected = true;
Serial.println("CONNECTED");
};
void onDisconnect(BLEServer* pServer)
{
deviceConnected = false;
Serial.println("DISCONNECTED");
} };
void advertiseInit(void) { BLEDevice::init("WH_PDC0EAD356A");
BLEServer* pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks());
// Fitness Machine Service BLEService* pFTMS = pServer->createService(fitnessMachineService); fitnessMachineFeatureCharacteristic.setValue(bufferFitnessMachineFeature, CHAR_FITNESS_MACHINE_FEATURE_SIZE); pFTMS->addCharacteristic(&fitnessMachineFeatureCharacteristic); pFTMS->addCharacteristic(&indoorBikeDataCharacteristic); pFTMS->addCharacteristic(&fitnessMachineControlPointCharacteristic); pFTMS->addCharacteristic(&fitnessMachineStatusCharacteristic); indoorBikeDataCharacteristic.addDescriptor(new BLE2902()); fitnessMachineControlPointCharacteristic.addDescriptor(new BLE2902()); fitnessMachineStatusCharacteristic.addDescriptor(new BLE2902()); pFTMS->start(); // Start the Fitness Machine Service
// Advertising BLEAdvertising *pAdvertising = pServer->getAdvertising(); advertisementData.setFlags(ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT); advertisementData.setCompleteServices(BLEUUID((uint16_t)SERVICE_FITNESS_MACHINE_UUID)); pAdvertising->setAdvertisementData(advertisementData); pAdvertising->addServiceUUID(fitnessMachineService); pAdvertising->start();
Serial.println("FTMS ready, advertising"); }
void updateFTMSData(int speed, int cadence, int power) { speedOut = speed; cadenceOut = cadence; powerOut = power;
bufferIndoorBikeData[2] = (uint8_t)(speedOut & 0xff); bufferIndoorBikeData[3] = (uint8_t)(speedOut >> 8); bufferIndoorBikeData[4] = (uint8_t)(cadenceOut & 0xff); bufferIndoorBikeData[5] = (uint8_t)(cadenceOut >> 8); bufferIndoorBikeData[9] = (uint8_t)(powerOut & 0xff); bufferIndoorBikeData[10] = (uint8_t)(powerOut >> 8);
indoorBikeDataCharacteristic.setValue(bufferIndoorBikeData, CHAR_INDOOR_BIKE_DATA_SIZE);
if (deviceConnected) {
indoorBikeDataCharacteristic.notify(); } }
void setup() { Serial.begin(115200); advertiseInit(); }
void loop() { // Add your logic to update speed, cadence, and power based on your requirements int speed = 1000; // example value int cadence = 80; // example value int power = 500; // example value
updateFTMSData(speed, cadence, power);
delay(1000); // update every second }
The Python script and the output of the script is also shown:
import asyncio
from bleak import BleakScanner, BleakClient
async def main():
target_name = "WH_PDC0EAD356A"
target_address = None
SERVICE_UUID = "00001826-0000-1000-8000-00805F9B34FB"
CHARACTERISTIC_UUID = "00002AD2-0000-1000-8000-00805F9B34FB"
devices = await BleakScanner.discover()
for d in devices:
if target_name == d.name:
target_address = d.address
print(f"Found target {target_name} Bluetooth device with address {target_address}")
break
if target_address is not None:
async with BleakClient(target_address) as client:
print(f"Connected: {client.is_connected}")
# Print services of the device
services = await client.get_services()
for service in services:
print(f"Service UUID: {service.uuid}")
for characteristic in service.characteristics:
print(f" Characteristic UUID: {characteristic.uuid}")
while True:
text = input()
if text == "quit":
break
await client.write_gatt_char(CHARACTERISTIC_UUID, bytes(text, 'UTF-8'), response=True)
try:
data = await client.read_gatt_char(CHARACTERISTIC_UUID)
data = data.decode('utf-8') # convert bytes to str
print(f"Received data: {data}")
except Exception as e:
print(f"Error reading characteristic: {str(e)}")
else:
print("Could not find target Bluetooth device nearby")
asyncio.run(main())
The image below is on the App:
And the result of python is:
I am not sure why am I not able to detect the third service as the first two are easily discovered.
get_services
will be removed in future versions of bleak. Try to use the services property instead: bleak.readthedocs.io/en/latest/api/…