Integrate QN-Scale with Bluetooth (BLE) Help Wanted

Hi all,
I’m trying to integrate my QN-Scale (From Kamtron CS20M, similar models are FitIndex ES-26M / Elektra Scale / RENPHO ES-CS20M / RENPHO ES-30M / Kamtron CS20M) via Bluetooth.

I’ve made some code changes (similar to this: Add Meater BBQ sensor by 1technophile · Pull Request #1000 · 1technophile/OpenMQTTGateway · GitHub)

The Scale is supported by the openScale App (Driver here: openScale/BluetoothQNScale.java at master · oliexdev/openScale · GitHub).

The OMG does find the Scale, I’m reading the Monitor. It does

N: BLE Connect begin
T: Model to connect found: A4:C1:38:F8:E3:94
N: Failed to find service UUID: 0000ffe0-0000-1000-8000-00805f9b34fb
N: Failed registering notification

When the Scale Sleeps:

N: BLE Connect begin
T: Model to connect found: A4:C1:38:F8:E3:94
E NimBLEClient: "Connection failed; status=13 "
E: Connect to: a4:c1:38:f8:e3:94 failed
N: Failed registering notification

ZGatewayBLEconnect.h:

class QNSCALE_connect : public zBLEConnect {
  std::vector<uint8_t> m_data;
  void notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify);

public:
  QNSCALE_connect(NimBLEAddress& addr) : zBLEConnect(addr) {}
  void publishData() override;
};

/*-----------------------QN-Scale EXPERIMENTAL-----------------------*/
/* This is experimental and should be used for testing only.
 * At this time no testing has been done so this is only an example of extendability.
 */
void QNSCALE_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
  if (!ProcessLock) {
    Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str());
    if (pData[0] == 1) {
      Log.trace(F("Device identified creating BLE buffer" CR));
      JsonObject& BLEdata = getBTJsonObject();
      String mac_adress = m_pClient->getPeerAddress().toString().c_str();
      mac_adress.toUpperCase();

      BLEdata.set("model", "QN-Scale");
      BLEdata.set("id", (char*)mac_adress.c_str());
      Log.trace(F("Device identified in CB: %s" CR), (char*)mac_adress.c_str());
      BLEdata.set("batt", (int)pData[1]);
      BLEdata.set("version", (float)pData[2] / 10.0);
      BLEdata.set("num_timers", (int)pData[8]);
      BLEdata.set("mode", (pData[9] & 0x08) ? "switch" : "press");
      BLEdata.set("inverted", (bool)(pData[9] & 0x01));
      BLEdata.set("hold_secs", (int)pData[10]);

      pubBT(BLEdata);
    } else {
      Log.notice(F("Device not identified" CR));
    }
  } else {
    Log.trace(F("Callback process canceled by processLock" CR));
  }
  xTaskNotifyGive(m_taskHandle);
}

void QNSCALE_connect::publishData() {
  NimBLEUUID serviceUUID("0000ffe0-0000-1000-8000-00805f9b34fb");
  NimBLEUUID charUUID("0000ffe1-0000-1000-8000-00805f9b34fb");
  NimBLERemoteCharacteristic* pChar = getCharacteristic(serviceUUID, charUUID);

  if (pChar && pChar->canNotify()) {
    Log.trace(F("Registering notification" CR));
    pChar->subscribe(true, std::bind(&QNSCALE_connect::notifyCB, this,
                                     std::placeholders::_1, std::placeholders::_2,
                                     std::placeholders::_3, std::placeholders::_4));
    m_taskHandle = xTaskGetCurrentTaskHandle();
    ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT));
  } else {
    Log.notice(F("Failed registering notification" CR));
  }
}

Here are the screenshots from nRF Connect:

Any hints how to be more succsessful?

One more screenshot:

You may use a callback instead of a direct read so as to leverage the NOTIFY function of the characteristic. The LYWSD03MMC use a callback you can use it as an example.

I’ve made some progress and was able to connect and read a characteristic. Now I’m struggling a bit to set the code up as in the driver from the openScale App to send the right info and receive the weight. I’ll report back when I find time to figure it out!

1 Like

@ChiefGlider I can help you with some of this.

Instead of using the 128 bit uuid try using the 16 bit value
i.e. change these:

  NimBLEUUID serviceUUID("0000ffe0-0000-1000-8000-00805f9b34fb");
  NimBLEUUID charUUID("0000ffe1-0000-1000-8000-00805f9b34fb");

to:

  NimBLEUUID serviceUUID("ffe0");
  NimBLEUUID charUUID("ffe");

There may be a comparison flaw with the NimBLE library that does not resolve the 128 bit version correctly, I’d appreciate feedback on this so I may correct it.

E NimBLEClient: "Connection failed; status=13 "

This is a connection timeout, the device may no longer be listening for connections when the attempt is made. try shortening the scan time.

if (pChar && pChar->canNotify()) {
    Log.trace(F("Registering notification" CR));
    pChar->subscribe(true, std::bind(&QNSCALE_connect::notifyCB, this,
                                     std::placeholders::_1, std::placeholders::_2,
                                     std::placeholders::_3, std::placeholders::_4));
    m_taskHandle = xTaskGetCurrentTaskHandle();
    ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT));

This may result in the gateway hanging on the ulTaskNotifyTake. I suggest changing this to:

  if (pChar) && pChar->canNotify())  {
      Log.trace(F("Registering notification" CR));
      if (pChar->subscribe(true, std::bind(&QNSCALE_connect::notifyCB, this,
                                     std::placeholders::_1, std::placeholders::_2,
                                     std::placeholders::_3, std::placeholders::_4));
        m_taskHandle = xTaskGetCurrentTaskHandle();
        ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT)); 
      }

Please post back with your progress.

Hello! Do you have any progress on the integration of the qn-scale? I can see it with theengs on a esp32, but I do not get any MQTT messages.

Welcome @lowChecker

From the above it looks as if the properties of the QN-Scale can only be retrieved by connection, while the current BLE decoding for most other sensors’ properties is being decoded from the BLE advertising data.

This means an integrated decoder is not possible at this stage, but you can retrieve the data from the scale by using the READ functionality of OpenMQTTGateway, as described above.

Thank you!

Do I understand your answer/link right. I have to setup some program to listen for the Mac address of the scale and than send the specific packages via MQTT to subscribe to the notification?

I have to admit that I don’t know if the current development version of OpenMQTTGateway allows for notifications, but yes, the above mentioned service/characteristic combo ffe0/ffe1 seems to hold the relevant weighing properties.