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.