mbl_mw_metawearboard_initialize() keeps printing the status 16 (MBL_MW_STATUS_ERROR_TIMEOUT)

edited May 18 in C++

Hi all!

I'm writing my first application for using the MMR device, and I'm attempting to initialise the MblMwMetaWearBoard using the C++ SDK (version 0.18.4):

MblMwBtleConnection btle_conn = { nullptr, write_gatt_char, read_gatt_char, enable_char_notify, on_disconnect };
MblMwMetaWearBoard* board = mbl_mw_metawearboard_create(&btle_conn);
mbl_mw_metawearboard_initialize(board, nullptr, [](void* context, MblMwMetaWearBoard* board, int32_t status)
{
    (void) context;
    (void) board;

    if (status)
    {
        std::cout << "Error initialising board: " << status << "\n";
    }
    else
    {
        std::cout << "Board initialised.\n";
    }
});

This code is directly taken from MbientLab's own cppdocs. The first thing I'd like to double-check is that the IF-condition in the lambda function above should read if (status) rather than if (!status), since status should have a value of 0 only if the board is initialised properly, i.e. there might be a typo in the lambda function in the cppdocs?


Secondly, when I run this code, my output is: Error initialising board: 16. Consequently, as expected, mbl_mw_metawearboard_is_initialized(board) will return a value of 0, and any further commands like mbl_mw_sensor_fusion_set_mode(board, MBL_MW_SENSOR_FUSION_MODE_IMU_PLUS) will throw a std::out_of_range exception.

Therefore, I also wish to check that my implementations of the various functions in btle_conn have been written correctly. I especially want to check that my understanding of enable_char_notify is correct, because I've been combing through various sample codes and I've not found that many examples of it.

The only thing that I'm certain about is that my implementation of read_gatt_char works, with the inspiration for highlow_to_uuid taken from here and with the latest version of gattlib-master being used to set up the BTLE connection with the MMR device. I'm also using this sample code as a reference:

static void read_gatt_char(void* context, const void* caller, const MblMwGattChar* characteristic, MblMwFnIntVoidPtrArray handler)
{
    (void) context;

    const uuid_t gatt_char_uuid = highlow_to_uuid(characteristic->uuid_high, characteristic->uuid_low);
    int ret;

    for (int i = 0; i < characteristics_count; ++i)
    {
        ret = gattlib_uuid_cmp(&characteristics[i].uuid, &gatt_char_uuid);
        if (ret == GATTLIB_SUCCESS)
        {
            uint8_t* buffer;
            size_t buffer_len;
            ret = gattlib_read_char_by_uuid(gatt_connection, &characteristics[i].uuid, (void**)&buffer, &buffer_len);
            if (ret != GATTLIB_SUCCESS)
            {
                std::cout << "Read operation failed.\n";
                return;
            }
            std::cout << "Read by UUID completed: ";
            for (size_t j = 0; j < buffer_len; ++j)
            {
                std::cout << (char)(buffer[j]);
            }
            std::cout << "\nRead " << buffer_len << " bytes\n";

            handler(caller, buffer, buffer_len);
            return;
        }
    }

    std::cout << "Characteristic " << std::hex << characteristic->uuid_high
          << " - " << characteristic->uuid_low << std::dec << "does not exist.\n";
}

The reason I know this works is that the output from mbl_mw_metawearboard_initialize() includes all the Gatt characteristics that have been read correctly from my MMR device:

Read by UUID completed: 1.5.0
Read 5 bytes
Read by UUID completed: 5
Read 1 bytes
Read by UUID completed: 0.4
Read 3 bytes
Read by UUID completed: MbientLab Inc
Read 13 bytes
Read by UUID completed: 047D22
Read 6 bytes

As for the rest of the functions, I'm not sure if they currently work as intended. Below is my implementation for write_gatt_char:

static void write_gatt_char(void* context, const void* caller, MblMwGattCharWriteType write_type, const MblMwGattChar* characteristic, const uint8_t* value, uint8_t length)
{
    (void) context;
    (void) caller;
    (void) write_type;

    const uuid_t gatt_char_uuid = highlow_to_uuid(characteristic->uuid_high, characteristic->uuid_low);
    int ret;

    for (int i = 0; i < characteristics_count; ++i)
    {
        ret = gattlib_uuid_cmp(&characteristics[i].uuid, &gatt_char_uuid);
        if (ret == GATTLIB_SUCCESS)
        {
            std::cout << "Write by UUID: ";
            for (size_t j = 0; j < length; ++j)
            {
                std::cout << (int)(value[j]);
            }
            std::cout << "\nWrote " << (size_t)length << " bytes\n";
            ret = gattlib_write_char_by_uuid(gatt_connection, &characteristics[i].uuid, (void*)value, length);
            // ret = gattlib_write_char_by_handle(gatt_connection, characteristics[i].value_handle, (void*)value, length);
            if (ret != GATTLIB_SUCCESS)
            {
                std::cout << "Write operation failed.\n";
            }
            return;
        }
    }

    std::cout << "Characteristic " << std::hex << characteristic->uuid_high
          << " - " << characteristic->uuid_low << std::dec << "does not exist.\n";
}

The resulting output from mbl_mw_metawearboard_initialize() includes this:

Write by UUID: 1128
Wrote 2 bytes

I wanted to ask what exactly this 1128 represents. This would help me understand what exactly needs to be written to the MMR device. A look at the source code says that the related UUID belongs to a METAWEAR_COMMAND_CHAR -- may I ask what exactly does this do?


Below is my implementation for enable_char_notify:

static void enable_char_notify(void* context, const void* caller, const MblMwGattChar* characteristic, MblMwFnIntVoidPtrArray handler, MblMwFnVoidVoidPtrInt ready)
{
    (void) context;

    const uuid_t gatt_char_uuid = highlow_to_uuid(characteristic->uuid_high, characteristic->uuid_low);
    int ret;

    for (int i = 0; i < characteristics_count; ++i)
    {
        ret = gattlib_uuid_cmp(&characteristics[i].uuid, &gatt_char_uuid);
        if (ret == GATTLIB_SUCCESS)
        {
            ret = gattlib_notification_start(gatt_connection, &characteristics[i].uuid);
            notifyHandlers.insert({ caller, handler });
            if (ret != GATTLIB_SUCCESS)
            {
                std::cout << "Enabling notification failed.\n";
                ready(caller, MBL_MW_STATUS_ERROR_ENABLE_NOTIFY);
            }
            else
            {
                ready(caller, MBL_MW_STATUS_OK);
            }
            return;
        }
    }

    std::cout << "Characteristic " << std::hex << characteristic->uuid_high
          << " - " << characteristic->uuid_low << std::dec << "does not exist.\n";
    ready(caller, MBL_MW_STATUS_ERROR_ENABLE_NOTIFY);
}

According to the cppdocs, the handler argument is run every time the specific Gatt characteristic has changed. I wanted to check if I have to call handler anywhere in enable_char_notify. When I looked through the source code of the MetaWear SDK, it seemed to me that read_gatt_char might actually be called indirectly through enable_char_notify -- please correct me if I'm wrong.


Lastly, here is my implementation of on_disconnect:

static void on_disconnect(void* context, const void* caller, MblMwFnVoidVoidPtrInt handler)
{
    // call this handler every time connection is lost, use 0 for 'value' parameter
    (void) context;
    dcHandlers.insert({ caller, handler });
}

I lifted it completely from the cppdocs; it seems trivial so I didn't change a thing -- again, please correct me if my understanding is wrong.

Thanks in advance!
Li

Comments

  • Forgot to mention:

    • PC is Ubuntu 16.04
    • gattlib was built using Linux bluetooth library version 5.54

    Cheers!

  • Hi there! Would it please be possible to get some hints about this issue? I just need to know whether my implementations of write_gatt_char and enable_char_notify functions make sense. Thanks so much in advance!! :)

  • Update: I improved my code but continued to encounter the same problem. Follow-up is here.

Sign In or Register to comment.