How to convert relative cursor movement to absolute position for BLE HID using Zephyr?

Hey @MCU, MPU & Firmware @Helper Guys, i have a slight logic problem, So i built a BLE HID device using zephyr which tags input from a console and send BLE HID packets to the connected device All well and good...for now The problem arises here, where the HID report is based of the relative position of the cursor, but my client requires a way to give it through Absolute position of a device(Main eg: phone) Could anyone help how we can give absolute movement from relative movement(Even external(PC based) code implementation is Okay) My HID report is 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) / 0x09, 0x02, / Usage (Mouse) / 0xA1, 0x01, / Collection (Application) / 0x85, 0x01, / Report Id (1) / 0x09, 0x01, / Usage (Pointer) / 0xA1, 0x00, / Collection (Physical) / 0x05, 0x09, / Usage Page (Button) / 0x19, 0x01, / Usage Minimum (0x01) / 0x29, 0x03, / Usage Maximum (0x03) / 0x15, 0x00, / Logical Minimum (0) / 0x25, 0x01, / Logical Maximum (1) / 0x95, 0x03, / Report Count (3) / 0x75, 0x01, / Report Size (1) / 0x81, 0x02, / Input (Data,Var,Abs,No Wrap,Linear,...) / 0x95, 0x01, / Report Count (1) / 0x75, 0x05, / Report Size (5) / 0x81, 0x03, / Input (Const,Var,Abs,No Wrap,Linear,...) / 0x05, 0x01, / Usage Page (Generic Desktop Ctrls) / 0x09, 0x30, / Usage (X) / 0x09, 0x31, / Usage (Y) / 0x15, 0x81, / Logical Minimum (129) / 0x25, 0x7F, / Logical Maximum (127) / 0x75, 0x08, / Report Size (8) / 0x95, 0x02, / Report Count (2) / 0x81, 0x06, / Input (Data,Var,Rel,No Wrap,Linear,...) / {I have tried 0x81, 0x02 for Abs but it didnt register in my phone as a HID device for some reason} 0xC0, / End Collection / 0xC0, / End Collection */
14 Replies
Nayel
Nayel4mo ago
which software are you using
melta101
melta1014mo ago
The RTOS i am using is Zephyr
melta101
melta1014mo ago
Any help and clarifications needed are welcome!
Nayel
Nayel4mo ago
To convert relative movement to absolute positioning for your BLE HID device, you'll need to make a few changes to both your HID report descriptor and your firmware implementation. Here's how you can approach this: Modify the HID report descriptor: Change the X and Y input fields to absolute positioning by modifying these lines:
Nayel
Nayel4mo ago
This changes the X and Y fields to 16-bit absolute values with a range of 0-32767. Update your firmware implementation: You'll need to maintain the current cursor position in your device's memory. When you receive input from the console, instead of sending relative movement, you'll send the new absolute position. Here's a basic example of how you might implement this in your firmware:
Nayel
Nayel4mo ago
#include <zephyr/types.h>
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>

#define SCREEN_WIDTH 32768
#define SCREEN_HEIGHT 32768

struct hid_report {
uint8_t buttons;
uint16_t x;
uint16_t y;
} __packed;

static struct hid_report report = {0};

void update_cursor_position(int16_t dx, int16_t dy) {
// Update absolute position based on relative movement
report.x = MIN(MAX(report.x + dx, 0), SCREEN_WIDTH - 1);
report.y = MIN(MAX(report.y + dy, 0), SCREEN_HEIGHT - 1);

// Send the updated report
bt_gatt_notify(NULL, &hid_svc.attrs[2], &report, sizeof(report));
}

void set_cursor_position(uint16_t x, uint16_t y) {
report.x = MIN(x, SCREEN_WIDTH - 1);
report.y = MIN(y, SCREEN_HEIGHT - 1);

// Send the updated report
bt_gatt_notify(NULL, &hid_svc.attrs[2], &report, sizeof(report));
}
#include <zephyr/types.h>
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>

#define SCREEN_WIDTH 32768
#define SCREEN_HEIGHT 32768

struct hid_report {
uint8_t buttons;
uint16_t x;
uint16_t y;
} __packed;

static struct hid_report report = {0};

void update_cursor_position(int16_t dx, int16_t dy) {
// Update absolute position based on relative movement
report.x = MIN(MAX(report.x + dx, 0), SCREEN_WIDTH - 1);
report.y = MIN(MAX(report.y + dy, 0), SCREEN_HEIGHT - 1);

// Send the updated report
bt_gatt_notify(NULL, &hid_svc.attrs[2], &report, sizeof(report));
}

void set_cursor_position(uint16_t x, uint16_t y) {
report.x = MIN(x, SCREEN_WIDTH - 1);
report.y = MIN(y, SCREEN_HEIGHT - 1);

// Send the updated report
bt_gatt_notify(NULL, &hid_svc.attrs[2], &report, sizeof(report));
}
In this example, update_cursor_position takes relative movement as input and updates the absolute position, while set_cursor_position allows you to set the absolute position directly. Handling phone compatibility: If your phone isn't recognizing the device with absolute positioning, you might need to add a physical (x, y) min/max to your descriptor:
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x7F, // Physical Maximum (32767)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x7F, // Physical Maximum (32767)
Add these lines just before the Logical Minimum and Maximum in your descriptor. Calibration: You'll need to implement a calibration process to map the phone's screen dimensions to your 0-32767 range. This could involve having the user touch specific points on the screen and adjusting your coordinates accordingly.
Nayel
Nayel4mo ago
I'm really not an expert but it should help
melta101
melta1014mo ago
Interesting! Lemme implement this n let yk furthur Thanks for your time mate!
melta101
melta1014mo ago
I have a few doubts if we are setting this HID report to abs, why should we implement the below functions, as they seems to be converting relative to abs BTW, I have tried HID format change but after getting connected, it doesnt seem to move the cursor
Nayel
Nayel4mo ago
Absolute vs. Relative Positioning: If you've set up your HID report for absolute positioning, you're correct that you shouldn't need to convert from relative to absolute movements. The functions I provided earlier were indeed more suited for a hybrid approach, which isn't necessary if you're fully committed to absolute positioning.
Nayel
Nayel4mo ago
for the cursor : you have to ensure your HID report is being sent correctly with the new format. and then Verify that the absolute values you're sending are within the range you specified in the descriptor (0 to 32767 in the example). note that Some devices might expect different ranges or additional information in the HID report for absolute positioning.
Nayel
Nayel4mo ago
struct hid_report {
uint8_t buttons;
uint16_t x;
uint16_t y;
} __packed;

static struct hid_report report = {0};

void set_cursor_position(uint16_t x, uint16_t y) {
report.x = x;
report.y = y;

// Send the updated report
bt_gatt_notify(NULL, &hid_svc.attrs[2], &report, sizeof(report));
}
struct hid_report {
uint8_t buttons;
uint16_t x;
uint16_t y;
} __packed;

static struct hid_report report = {0};

void set_cursor_position(uint16_t x, uint16_t y) {
report.x = x;
report.y = y;

// Send the updated report
bt_gatt_notify(NULL, &hid_svc.attrs[2], &report, sizeof(report));
}
melta101
melta1014mo ago
Hmm, Still the same Connects but doesnt send the mouse commands
Nayel
Nayel4mo ago
Hmmm... weird
Want results from more Discord servers?
Add your server