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

Feature: Implement Haptic Feedback 6.05 #12

Closed
wants to merge 1 commit into from

Conversation

Relys
Copy link
Contributor

@Relys Relys commented Aug 27, 2024

All function calls should be < 6.05 safe as they explicitly check if the play tone pointers aren't null to determine if the API is available.

Features:
Duty Cycle Solid Tone: Duty Cycle threshold where solid tone is played. Tiltback will pulse at a period of 200ms and then go solid once this value is set. Set <= tiltback to always have it solid. Set >=95 to disable (just like tiltback)
Duty Cycle Strength: Zero disables. Will pulse at 200ms when duty cycle tiltback is present.
Duty Cycle Frequency:
Error Strength: Zero Disables. Will pulse at 200ms with a 400ms pause every 2 beeps for hv/lv. For temp (and bms in future) it will pulse at half rate. i.e. 100ms with 200ms pause.
Error Frequency:
Vibrate Strength: Zero disables. Plays in separate channel
Vibrate Frequency:

@Relys Relys force-pushed the haptic-feedback-6.05 branch 2 times, most recently from 80bb0c9 to 4fc90ea Compare August 27, 2024 22:46
@Relys Relys changed the base branch from dev/6.05 to main August 27, 2024 22:47
@Relys Relys force-pushed the haptic-feedback-6.05 branch 25 times, most recently from 10bb08a to 17f592a Compare September 2, 2024 07:52
float haptic_error_volt;
int haptic_error_freq;
float haptic_vibrate_volt;
int haptic_vibrate_freq;
Copy link
Owner

Choose a reason for hiding this comment

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

Can you make a struct member haptic_feedback the same way e.g. the LEDs have it's own config structs? Then you can easily make a copy of it in your HapticFeedback struct.

if (!VESC_IF->foc_play_tone) {
return;
}
if ((volt > 0) && (freq > 0.0)) {
Copy link
Owner

Choose a reason for hiding this comment

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

The braces around the > expressions are redundant (and this is a very common expression composition). Can you remove them? This repeats multiple times throughout the PR.

When in doubt, refer to C Operator Precedence

}
__attribute__((fallthrough));
case SAT_PB_LOW_VOLTAGE:
__attribute__((fallthrough));
Copy link
Owner

Choose a reason for hiding this comment

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

The attribute isn't needed if the case labels follow directly after each other.

}

if (!haptic_feedback->tone_in_progress) {
haptic_feedback_play_two_tones(haptic_feedback);
Copy link
Owner

Choose a reason for hiding this comment

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

In a clean design this call shouldn't be here. This Function should just set up the tones or the pattern, and the haptic_feedback_update() function take care of the playing by itself. I understand the logic you have relies on this, see my comment on the haptic_feedback_update() function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you don't immediately start playing a tone when called, then by the time you get to the haptic_feedback_update function timer and alt_timer will already be out of date.

Comment on lines +122 to +152
if (!(VESC_IF->foc_play_tone && VESC_IF->foc_stop_audio)) {
return;
}

if (haptic_feedback->tone_in_progress) {
if ((fabsf(haptic_feedback->timer - current_time) > haptic_feedback->note_duration) ||
(haptic_feedback->haptic_feedback_state.type == HAPTIC_FEEDBACK_BEEP &&
haptic_feedback->beep_num == 0)) {
haptic_feedback_reset(haptic_feedback, current_time);
} else if ((haptic_feedback->haptic_feedback_state.peroid > 0) &&
(fabsf(haptic_feedback->alt_timer - current_time) >
haptic_feedback->haptic_feedback_state.peroid / 1000.0)) {
haptic_feedback->alt_timer = current_time;
// update for special modes
haptic_feedback->counter++;
if (haptic_feedback->counter % 2 == 0) {
if (haptic_feedback->haptic_feedback_state.type == HAPTIC_FEEDBACK_BEEP) {
haptic_feedback_play_single_tone(
haptic_feedback->haptic_feedback_state.freq,
haptic_feedback->haptic_feedback_state.volt,
0
);
haptic_feedback->beep_num--;
} else {
haptic_feedback_play_two_tones(haptic_feedback);
}
} else {
VESC_IF->foc_stop_audio(false);
}
}
}
Copy link
Owner

Choose a reason for hiding this comment

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

This logic is very complex and opaque. You also repeatedly call the play functions all the time, which is not very clean. What I'd do instead is take a similar approach to when you implement an animation. I would store a bool that says whether you are currently playing a tone. Then, depending on the time, determine whether you're supposed to be playing a tone, and if that differs from the current state, start or stop playing the tone.

For a duty tone pattern, tone-gap-tone-gap-..., you can do e.g.:

const float tone_length = 0.2f;
const float period = tone_length  * 2; // note: according to convention I call period the whole segment that repeats
time = fmodf(time, period); // now time is your time within a single period
bool should_be_playing = time < tone_length;

For lets say three beeps and a pause:

const float tone_length = 0.2f;
const float period = tone_length  * 8; // tone-gap-tone-gap-tone-gap-gap-gap = 8 beats
time = fmodf(time, period);
int beat = floorf(time / tone_length);
bool should_be_playing = (beat % 2) == 0 && beat != 6;

I hope this makes sense to you, if not let me know and I'll try to come up with a PoC to demonstrate better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So I don't call any of the functions redundantly, only every period when alt_timer expires. Regarding the example for duty tone pattern, I'm not sure that this logic would blend smoothly with how I'm implementing creating a solid tone for when we've reached the duty cycle threshold (by continue to update alt_timer so it never reaches its next period). If you can provide an alternative that's fine, but I'm not sure I want to spend more cycles here rewriting this.

@Jared-Is-Coding
Copy link
Contributor

Jared-Is-Coding commented Sep 13, 2024

I would suggest these revisions. GitHub isn't letting me "review", so I'll just post the commit:

Jared-Is-Coding@be7611a

Will give this some actual ride testing this week.

@lukash lukash mentioned this pull request Sep 17, 2024
@Relys Relys closed this Oct 21, 2024
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.

3 participants