I can't access thingy53 in Mac Os Ventura to execute a neuton-nordic-thingy53-ble-remotecontrol project using Bluetooth 4.0 Orico bt-403 dongle adapter

Hi,

https://github.com/Neuton-tinyML/neuton-nordic-thingy53-ble-remotecontrol

https://www.youtube.com/watch?v=eDUTf1Zch24

I've been trying this project for 3 days. I can compile, make changes to capture data and/or return to the inference. This is on Ubuntu 22.04 or Windows 11 (always using an Orico BT-403 Bluetooth 4.0 dongle).

However, my computers are macOS Big Sur and macOS Ventura. On the first one, I was only able to connect once (Host/MacOS-Computer <--> Thingy53 with Bluetooth).

I'm writing to ask what changes I need to make to the neuton-nordic-thingy53-ble-remotecontrol project (probably in the file below) to resolve my Bluetooth connection problem on my Mac OS Ventura.

#include "ble_hid.h"

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/settings/settings.h>

//////////////////////////////////////////////////////////////////////////////

#define KEY_ARROW_LEFT (0x50)
#define KEY_ARROW_RIGHT (0x4F)
#define KEY_F5 (0x3E)
#define KEY_ESP (0x29)

#define KEY_MEDIA_VOLUME_UP ( 1 << 0 )
#define KEY_MEDIA_VOLUME_DOWN ( 1 << 1 )
#define KEY_MEDIA_MUTE ( 1 << 2 )
#define KEY_MEDIA_PLAY_PAUSE ( 1 << 3 )
#define KEY_MEDIA_PREV_TRACK ( 1 << 4 )
#define KEY_MEDIA_NEXT_TRACK ( 1 << 5 )

#if CONFIG_SAMPLE_BT_USE_AUTHENTICATION
/* Require encryption using authenticated link-key. */
#define SAMPLE_BT_PERM_READ BT_GATT_PERM_READ_AUTHEN
#define SAMPLE_BT_PERM_WRITE BT_GATT_PERM_WRITE_AUTHEN
#else
/* Require encryption. */
#define SAMPLE_BT_PERM_READ BT_GATT_PERM_READ_ENCRYPT
#define SAMPLE_BT_PERM_WRITE BT_GATT_PERM_WRITE_ENCRYPT
#endif

//////////////////////////////////////////////////////////////////////////////

enum
{
    HIDS_REMOTE_WAKE = BIT(0),
    HIDS_NORMALLY_CONNECTABLE = BIT(1),
};

struct hids_info
{
    uint16_t version; /* version number of base USB HID Specification */
    uint8_t code;     /* country HID Device hardware is localized for. */
    uint8_t flags;
} __packed;

struct hids_report
{
    uint8_t id;   /* report id */
    uint8_t type; /* report type */
} __packed;

enum
{
    HIDS_INPUT = 0x01,
    HIDS_OUTPUT = 0x02,
    HIDS_FEATURE = 0x03,
};

//////////////////////////////////////////////////////////////////////////////

static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    BT_DATA_BYTES(BT_DATA_UUID16_ALL,
                  BT_UUID_16_ENCODE(BT_UUID_HIDS_VAL),
                  BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
};

static const struct bt_data sd[] = {
    BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

static struct hids_info info = {
    .version = 0x0000,
    .code = 0x00,
    .flags = HIDS_NORMALLY_CONNECTABLE,
};

static struct hids_report input = {
    .id = 0x01,
    .type = HIDS_INPUT,
};

static struct hids_report input_consumer = {
    .id = 0x02,
    .type = HIDS_INPUT,
};

static bool ble_connected_ = false;
static ble_connection_cb_t user_conn_callback_ = NULL;

static bool ccc_enabled_ = false;
static uint8_t ctrl_point;
static uint8_t consumer_report;

static uint8_t report_map[] = {
    0x05, 0x01, // Usage Page (Generic Desktop)
    0x09, 0x06, // Usage (Keyboard)
    0xA1, 0x01, // Collection (Application)

    0x05, 0x07, //   Usage Page (Keyboard/Keypad)
    0x85, 0x01, //   Report ID (1)
    0x19, 0xE0, //   Usage Minimum (Keyboard Left Control)
    0x29, 0xE7, //   Usage Maximum (Keyboard Right GUI)
    0x15, 0x00, //   Logical Minimum (0)
    0x25, 0x01, //   Logical Maximum (1)
    0x75, 0x01, //   Report Size (1)
    0x95, 0x08, //   Report Count (8)
    0x81, 0x02, //   Input (Data, Variable, Absolute) ; Modifier keys

    0x95, 0x01, //   Report Count (1)
    0x75, 0x08, //   Report Size (8)
    0x81, 0x01, //   Input (Constant) ; Reserved byte

    0x95, 0x05, //   Report Count (5)
    0x75, 0x01, //   Report Size (1)
    0x05, 0x08, //   Usage Page (LEDs)
    0x85, 0x01, //   Report ID (1)
    0x19, 0x01, //   Usage Minimum (Num Lock)
    0x29, 0x05, //   Usage Maximum (Kana)
    0x91, 0x02, //   Output (Data, Variable, Absolute) ; LED report

    0x95, 0x01, //   Report Count (1)
    0x75, 0x03, //   Report Size (3)
    0x91, 0x03, //   Output (Constant) ; LED report padding

    0x95, 0x06, //   Report Count (6)
    0x75, 0x08, //   Report Size (8)
    0x15, 0x00, //   Logical Minimum (0)
    0x25, 0x65, //   Logical Maximum (101)
    0x05, 0x07, //   Usage Page (Keyboard/Keypad)
    0x19, 0x00, //   Usage Minimum (Reserved (no event indicated))
    0x29, 0x65, //   Usage Maximum (Keyboard Application)
    0x81, 0x00, //   Input (Data, Array) ; Key arrays (6 bytes)

    0xC0, // End Collection

    // Consumer Control Report
    0x05, 0x0C, // Usage Page (Consumer Devices)
    0x09, 0x01, // Usage (Consumer Control)
    0xA1, 0x01, // Collection (Application)

    0x85, 0x02, //   Report ID (2)
    0x05, 0x0C, //   Usage Page (Consumer Devices)
    0x15, 0x00, //   Logical Minimum (0)
    0x25, 0x01, //   Logical Maximum (1)

    0x09, 0xE9, //   Usage (Volume Up)
    0x09, 0xEA, //   Usage (Volume Down)
    0x09, 0xE2, //   Usage (Mute)
    0x09, 0xCD, //   Usage (Play/Pause)
    0x19, 0xB5, //   Usage Minimum (Scan Next Track)
    0x29, 0xB8, //   Usage Maximum (Scan Previous Track)

    0x75, 0x01, //   Report Size (1)
    0x95, 0x08, //   Report Count (8)
    0x81, 0x02, //   Input (Data, Variable, Absolute) ; Media keys

    0xC0 // End Collection
};

//////////////////////////////////////////////////////////////////////////////

static ssize_t read_info(struct bt_conn* conn,
                         const struct bt_gatt_attr* attr, void* buf,
                         uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
                             sizeof(struct hids_info));
}

//////////////////////////////////////////////////////////////////////////////

static ssize_t read_report_map(struct bt_conn* conn,
                               const struct bt_gatt_attr* attr, void* buf,
                               uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, report_map,
                             sizeof(report_map));
}

//////////////////////////////////////////////////////////////////////////////

static ssize_t read_report(struct bt_conn* conn,
                           const struct bt_gatt_attr* attr, void* buf,
                           uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
                             sizeof(struct hids_report));
}

//////////////////////////////////////////////////////////////////////////////

static void input_ccc_changed(const struct bt_gatt_attr* attr, uint16_t value)
{
    printk("Input CCCD %s\n", value == BT_GATT_CCC_NOTIFY ? "enabled" : "disabled");
    printk("Input attribute handle: %d\n", attr->handle);

    ccc_enabled_ = (value == BT_GATT_CCC_NOTIFY);
}

//////////////////////////////////////////////////////////////////////////////

static ssize_t read_input_report(struct bt_conn* conn,
                                 const struct bt_gatt_attr* attr, void* buf,
                                 uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0);
}

//////////////////////////////////////////////////////////////////////////////

static ssize_t write_ctrl_point(struct bt_conn* conn,
                                const struct bt_gatt_attr* attr,
                                const void* buf, uint16_t len, uint16_t offset,
                                uint8_t flags)
{

    uint8_t* value = attr->user_data;

    printk("write_ctrl_point\n");

    if (offset + len > sizeof(ctrl_point))
    {
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
    }

    memcpy(value + offset, buf, len);

    return len;
}

//////////////////////////////////////////////////////////////////////////////

static ssize_t read_consumer_report(struct bt_conn* conn,
                                    const struct bt_gatt_attr* attr,
                                    void* buf, uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0);
}

//////////////////////////////////////////////////////////////////////////////

static void consumer_ccc_changed(const struct bt_gatt_attr* attr, uint16_t value)
{
    printk("Consumer CCCD %s\n", value == BT_GATT_CCC_NOTIFY ? "enabled" : "disabled");
}

//////////////////////////////////////////////////////////////////////////////

BT_GATT_SERVICE_DEFINE(hog_svc,
                       BT_GATT_PRIMARY_SERVICE(BT_UUID_HIDS),
                       BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_INFO, BT_GATT_CHRC_READ,
                                              BT_GATT_PERM_READ, read_info, NULL, &info),
                       BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT_MAP, BT_GATT_CHRC_READ,
                                              BT_GATT_PERM_READ, read_report_map, NULL, NULL),

                       BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
                                              BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
                                              SAMPLE_BT_PERM_READ,
                                              read_input_report, NULL, NULL),
                       BT_GATT_CCC(input_ccc_changed,
                                   SAMPLE_BT_PERM_READ | SAMPLE_BT_PERM_WRITE),
                       BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ,
                                          read_report, NULL, &input),

                       BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
                                              BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
                                              SAMPLE_BT_PERM_READ,
                                              read_consumer_report, NULL, &consumer_report),
                       BT_GATT_CCC(consumer_ccc_changed,
                                   SAMPLE_BT_PERM_READ | SAMPLE_BT_PERM_WRITE),
                       BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ,
                                          read_report, NULL, &input_consumer),

                       BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT,
                                              BT_GATT_CHRC_WRITE_WITHOUT_RESP,
                                              BT_GATT_PERM_WRITE,
                                              NULL, write_ctrl_point, &ctrl_point), );

//////////////////////////////////////////////////////////////////////////////

static void connected(struct bt_conn* conn, uint8_t err)
{
    char addr[BT_ADDR_LE_STR_LEN];

    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    if (err)
    {
        printk("Failed to connect to %s (%u)\n", addr, err);
        return;
    }

    printk("Connected %s\n", addr);

    if (bt_conn_set_security(conn, BT_SECURITY_L2))
    {
        printk("Failed to set security\n");
    }

    ble_connected_ = true;

    if (user_conn_callback_)
        user_conn_callback_(ble_connected_);
}

//////////////////////////////////////////////////////////////////////////////

static void disconnected(struct bt_conn* conn, uint8_t reason)
{
    char addr[BT_ADDR_LE_STR_LEN];

    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    printk("Disconnected from %s (reason 0x%02x)\n", addr, reason);

    ble_connected_ = false;
    ccc_enabled_ = false;

    if (user_conn_callback_)
        user_conn_callback_(ble_connected_);
}

//////////////////////////////////////////////////////////////////////////////

static void security_changed(struct bt_conn* conn, bt_security_t level,
                             enum bt_security_err err)
{
    char addr[BT_ADDR_LE_STR_LEN];

    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    if (!err)
    {
        printk("Security changed: %s level %u\n", addr, level);
    } else
    {
        printk("Security failed: %s level %u err %d\n", addr, level,
               err);

        bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    }
}

//////////////////////////////////////////////////////////////////////////////

BT_CONN_CB_DEFINE(conn_callbacks) = {
    .connected = connected,
    .disconnected = disconnected,
    .security_changed = security_changed,
};

//////////////////////////////////////////////////////////////////////////////

static void bt_ready(int err)
{
    if (err)
    {
        printk("Bluetooth init failed (err %d)\n", err);
        return;
    }

    printk("Bluetooth initialized\n");

    if (IS_ENABLED(CONFIG_SETTINGS))
    {
        settings_load();
    }

    err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    if (err)
    {
        printk("Advertising failed to start (err %d)\n", err);
        return;
    }

    printk("Advertising successfully started\n");
}

//////////////////////////////////////////////////////////////////////////////

static void auth_passkey_display(struct bt_conn* conn, unsigned int passkey)
{
    char addr[BT_ADDR_LE_STR_LEN];

    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    printk("Passkey for %s: %06u\n", addr, passkey);
}

//////////////////////////////////////////////////////////////////////////////

static void auth_cancel(struct bt_conn* conn)
{
    char addr[BT_ADDR_LE_STR_LEN];

    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    printk("Pairing cancelled: %s\n", addr);
}

//////////////////////////////////////////////////////////////////////////////

static struct bt_conn_auth_cb auth_cb_display = {
    .passkey_display = auth_passkey_display,
    .passkey_entry = NULL,
    .cancel = auth_cancel,
};

//////////////////////////////////////////////////////////////////////////////

int ble_hid_init(ble_connection_cb_t cb)
{
    int err;
    err = bt_enable(bt_ready);

    if (err)
    {
        printk("Bluetooth init failed (err %d)\n", err);
        return err;
    }

    user_conn_callback_ = cb;

    if (IS_ENABLED(CONFIG_SAMPLE_BT_USE_AUTHENTICATION))
    {
        bt_conn_auth_cb_register(&auth_cb_display);
    }

    return err;
}

//////////////////////////////////////////////////////////////////////////////

int ble_hid_send_key(ble_hid_key_t key)
{
    int res = -1;

    if (!ble_connected_ || !ccc_enabled_)
        return -1;

    uint8_t report[8] = {0};
    size_t report_len = sizeof(report);

    int attr_index = 5;

    switch (key)
    {
        case BLE_HID_KEY_ARROW_LEFT:
            report[2] = KEY_ARROW_LEFT;
            break;
        case BLE_HID_KEY_ARROW_RIGHT:
            report[2] = KEY_ARROW_RIGHT;
            break;
        case BLE_HID_KEY_F5:
            report[2] = KEY_F5;
            break;
        case BLE_HID_KEY_ESC:
            report[2] = KEY_ESP;
            break;
        case BLE_HID_KEY_MEDIA_PREV_TRACK:
            report[0] = KEY_MEDIA_PREV_TRACK;
            report_len = 2;
            attr_index = 10;
            break;
        case BLE_HID_KEY_MEDIA_NEXT_TRACK:
            report[0] = KEY_MEDIA_NEXT_TRACK;
            report_len = 2;
            attr_index = 10;
            break;
            case BLE_HID_KEY_MEDIA_MUTE:
            report[0] = KEY_MEDIA_MUTE;
            report_len = 2;
            attr_index = 10;
            break;
        case BLE_HID_KEY_MEDIA_PLAY_PAUSE:
            report[0] = KEY_MEDIA_PLAY_PAUSE;
            report_len = 2;
            attr_index = 10;
            break;
        case BLE_HID_KEY_MEDIA_VOLUME_UP:
            report[0] = KEY_MEDIA_VOLUME_UP;
            report_len = 2;
            attr_index = 10;
            break;
        case BLE_HID_KEY_MEDIA_VOLUME_DOWN:
            report[0] = KEY_MEDIA_VOLUME_DOWN;
            report_len = 2;
            attr_index = 10;
            break;
        default:
            return res;
    }

    res = bt_gatt_notify(NULL, &hog_svc.attrs[attr_index], report, report_len);

    if (res)
    {
        printk("Failed to send key, error = %d\n", res);
        return false;
    }
    else
    {
        printk("BLE HID Key %d sent successfully\n", report[0]);
    }

    /* reset report */
    memset(report, 0, sizeof(report));

    res = bt_gatt_notify(NULL, &hog_svc.attrs[attr_index], report, report_len);
    return res;
}

Parents Reply
  • I build and flash peripheral_hids_mouse in my hackintosh/Mac-Intel/MacOS Ventura and not appear in bluetooth devices. How to use it?

    Note.: My nordic device here is thingy53 in moment. But I have nrf7002DK and nrf54l15 too

    Note 2: My bluetooth in Ventura is Ok. Look my headphone name siidoo

    Note 3.:

    Note 4.: I can access NCS HIDS mouse app in thingy53 from Windows 11 with Orico bt-403 dongle bluetooth But I can't acess NCS HIDS mouse from Ventura:

Children
  • Alright. This indicates that the problem isn't with the Neuton demo, but with the compatibility between your hackintosh machines and BLE HIDS device.

    On that note, is the machine capable of connecting to any BLE HIDS devices at all?

    I suspect it is an issue with your setup.

  • Tks

    "On that note, is the machine capable of connecting to any BLE HIDS devices at all?"

    Probably not. I'm still trying some code changes. I say this because in that issue

    devzone.nordicsemi.com/.../hid-get-report-from-a-mac-not-as-expected

     it took 3 years for someone to provide a solution.

    That being said, I have a few questions:

    https://www.youtube.com/watch?v=eDUTf1Zch24

    In the video above (which is the project I'm interested in), was macOS Sequoia used? And surely the author's notebook had built-in Bluetooth, right?

    How would I run/test this project/Neuton on Windows 11 using the YouTube Music app or the Spotify app as the music player?

  • Hello @Hieu
    Pls I need a solution for this, preferably on Mac OS Ventura, or how to do it using a music player on Windows 11 and/or Ubuntu 22.04.

  • Hi neuberfran,

    neuberfran said:

    Probably not. I'm still trying some code changes. I say this because in that issue

    devzone.nordicsemi.com/.../hid-get-report-from-a-mac-not-as-expected

     it took 3 years for someone to provide a solution.

    As you can see in that same DevZone case, the issue ended up being a MacOS issue, so you are better off seeking help from Apple support. However, considering you are using hackintosh, I think it is unlikely that would be within their responsibility to support either.

    The thing is that you are dealing with HID, Human Interface Device. That means that it serves as the input or output of the device it connects to, so that device's operating system might have special handling for it. This is likely one of those cases, and it is not possible to know whether this is a MacOS issue or a hackintosh issue.

    If you can't even connect to a regular HIDS sample, then I recommend not pursuing getting it to work with your hackintosh setup anymore. At the very least, we need to try with a normal Mac machine.

    neuberfran said:
    In the video above (which is the project I'm interested in), was macOS Sequoia used? And surely the author's notebook had built-in Bluetooth, right?

    I will have to find and ask the presenter. It is likely that the presenter is using the notebook's built-in Bluetooth, yes.

    neuberfran said:
    How would I run/test this project/Neuton on Windows 11 using the YouTube Music app or the Spotify app as the music player?

    What the demo does is it act like a keyboard, basically. If the app supports interaction from those keys, I expect the demo to just works.

    If your keyboard has the media buttons, try those with the YouTube or Spotify app first. If those don't work either, then you can't expect the Neuton demo to work.

    I notice that the demo has some simpler keys to test too, like left, right, or F5. You might want to test it with a text editor or browser.

  • Hi neuberfran,

    Sorry for the late follow up.

    Hieu said:
    neuberfran said:
    In the video above (which is the project I'm interested in), was macOS Sequoia used? And surely the author's notebook had built-in Bluetooth, right?

    I will have to find and ask the presenter. It is likely that the presenter is using the notebook's built-in Bluetooth, yes.

    The OS version is macOS 15 Sequoia.

Related