Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented rate limiter for HID reports. #39

Closed
wants to merge 12 commits into from
Closed

Conversation

ChrisVeigl
Copy link
Contributor

Added new config variables for KConfig:
-) enabling rate limiter
-) set interval in microseconds
-) Either accumulate mouse movements or discard reports when sent too fast

Added new config variables for KConfig:
-) enabling rate limiter
-) set interval in microseconds
-) Either accumulate mouse movements or discard reports when sent too fast
/// discard any packets. If we get them too fast, many of them are still sent with the current values.
#if CONFIG_MODULE_USERATELIMITER
//check interval, jst to be sure use the absolute value of the subtraction.
if(abs(esp_timer_get_time() - cmdBuffer->lastTimeHIDCommandSent) > timeIntervalHIDCommand)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it guaranteed that the last (accumulated) data or button actions get sent if the rate limiter is active?
or can it happen that the last accumulated values are only sent when the next burst of mouse commands come in ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. My intention was, that even if the rate limiter is active, most (but not all) commands will be sent.
I guess it is very unlikely, that e.g., the button release report is sent as last one of too much (only in this case it would trigger the rate limiter).

If we would implement any async mechanism for sending the last movement values and mouse buttons, it would increase the complexity quite much:

  • New timer (I suggest a FreeRTOS timer)
  • Callback structure
  • Detecting if the timer should be started (how could we know in this command processor that this would be the last command?)

If we really need the proper transmission of every last movement value, I could implement it.
Nevertheless, I think if some external device hits the rate limiter, the chance of dropped values is quite high.

An easier approach:
If the rate limiter hits, just check if the button values have changed?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a button state tracker, which sends the button values if they change, even if the report is received out of the interval.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think that the rate limiter will always cause an unwanted behaviour if we do not guarantee that the most recent accumulated data is sent as soon as possible. for example: if movement data stays "in the pipeline" it will influence the next burst of mouse activities which come in, which might cause unintended deflection of the mouse cursor.
You could throw away acculumeted data if there was a pause, but IMO the only "clean" method to implement this is to trigger a timeout (a bit longer than the selected update rate) for a callback function where we check if accumulated coordinates are in the pipeline an send them if necessary.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I will implement the accumulation in a different way:

  • In uart_parse_command I will just accumulate the mouse values
  • A timer (endless loop) will check the accumulated values on a regular timebase (-> use the rate limiter value) and
    send any accumulated values.

do you think this is working :-)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good :-)
in case the accumulator is empty, the report could be sent immediately in order to avoid unnecessary delay/jitter.

if(cmdBuffer->accumValues[i] < -6000) cmdBuffer->accumValues[i] = -6000;
}
#else /* CONFIG_MODULE_RATELIMITERACCUMULATE */
ESP_LOGI(EXT_UART_TAG,"Rate limit: discard mouse report");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might create a whole lot of log messages in case of slower rate limits.
maybe limit that output, too ;-)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this output to log level verbose, which is off by default.

Comment on lines 72 to 78
config MODULE_RATELIMITERACCUMULATE
bool "Accumulate mouse HID packets if rate limiter hits (NOT RECOMMENDED)"
depends on MODULE_USERATELIMITER
default n
help
If enabled, relative mouse movements are accumulated and
sent on next occasion. If disabled, the HID report is discarded.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this feature should be disabled by default. I had many cases, that this accumulation winds up and there is no reasonable mouse movement possible....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i do not understand this comment...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L75: default n and not default y :-)

…(LED report), closing #38 :

- Added report ID for LEDs
- Added callback event type for LED
- Fixed the characteristic & added write no response (according to espressif/esp-idf#5369 THX @robinkrens for the hint!)
Added new config variables for KConfig:
-) enabling rate limiter
-) set interval in microseconds
-) Either accumulate mouse movements or discard reports when sent too fast
…umulate mode not finished!

Implemented a timer based solution for accumulating mode.
- moved to esp_hidd_prf_api.c
- changed default values for time interval in KConfig
- Implemented rate limiter callback
- Fixed signed int8 problem
- Synchronizing the accumulated values with a binary semaphore
@benjaminaigner
Copy link
Collaborator

I've moved all the rate limiter stuff to the esp_hidd_prf_api.c file, because it is more reasonable to it there.
The rate limiter is using an esp_timer in accumulate mode, discard mode is working just with a timestamp.

Every packet is sent via the timer, because the default value is set to 500us, which is half the time of the USB request (1ms).
With up to 50ms there is no noticeable delay (this setting should be sufficient for all devices, even for older ones).

@ChrisVeigl
Copy link
Contributor Author

Every packet is sent via the timer, because the default value is set to 500us, which is half the time of the USB request (1ms).
With up to 50ms there is no noticeable delay (this setting should be sufficient for all devices, even for older ones).

OK!
i think we should test the response with different rate limits and devices.
if we want to apply it in the FlipMouse, we need to remove the current rate limiter (which currently just drops everything < 20ms) in bluetooth.cpp: https://github.com/asterics/FLipMouse/blob/56b854f2b889f4f2fac8f5503f65db2b070b8f90/FLipWare/bluetooth.cpp#L19

And it we could make the rate limit adjustable via uart / a new FlipMouse AT-command (eg. AT BC to transfer bluetooth commands to the esp32 - for example "AT BC $RA 80" for setting the rate limiter to 80 ms) ?

@benjaminaigner
Copy link
Collaborator

OK!
i think we should test the response with different rate limits and devices.
if we want to apply it in the FlipMouse, we need to remove the current rate limiter (which currently just drops everything < 20ms) in bluetooth.cpp: https://github.com/asterics/FLipMouse/blob/56b854f2b889f4f2fac8f5503f65db2b070b8f90/FLipWare/bluetooth.cpp#L19

I don't know if we should remove it completely.
If we do that, we might run into the issue, that the serial buffer is full and packets are dropped within the FM?

@ChrisVeigl
Copy link
Contributor Author

I don't know if we should remove it completely.
If we do that, we might run into the issue, that the serial buffer is full and packets are dropped within the FM?

if we do not remove it, it will compromise the function of the rate limiter inside the esp32 add-on (becuaes it just drops commands), IMO the best implementation for the FlipMouse would be to limit the rate of commands as soon as possible (before USB or BT actions are created).

@benjaminaigner
Copy link
Collaborator

This PR is also ~2years old and we don't have any problems with rate limiting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants