Skip to content

Commit

Permalink
Haptic Feedback
Browse files Browse the repository at this point in the history
Feature: Implement Haptic Feedback
  • Loading branch information
lukash committed Sep 18, 2024
1 parent 889a963 commit d6b751f
Show file tree
Hide file tree
Showing 5 changed files with 397 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/conf/datatypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,19 @@ typedef struct {
CfgHwLeds leds;
} CfgHardware;

typedef struct {
uint16_t frequency;
float voltage;
} CfgHapticTone;

typedef struct {
float duty_solid_threshold;
CfgHapticTone duty;
CfgHapticTone error;
CfgHapticTone vibrate;
uint16_t tone_length;
} CfgHapticFeedback;

typedef struct {
float version;
bool disabled;
Expand Down Expand Up @@ -250,6 +263,8 @@ typedef struct {
float surge_duty_start;
float surge_angle;

CfgHapticFeedback haptic;

CfgLeds leds;
CfgHardware hardware;
} RefloatConfig;
Expand Down
200 changes: 200 additions & 0 deletions src/conf/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,182 @@ p, li { white-space: pre-wrap; }
<suffix> ERPM</suffix>
<vTx>3</vTx>
</tiltback_variable_erpm>
<haptic.duty.frequency>
<longName>Duty Cycle Frequency</longName>
<type>2</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Roboto'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Audible frequency of the Duty Cycle haptic feedback.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>CFG_DFLT_HAPTIC_DUTY_FREQUENCY</cDefine>
<editorScale>1</editorScale>
<editAsPercentage>0</editAsPercentage>
<maxInt>1000</maxInt>
<minInt>200</minInt>
<showDisplay>0</showDisplay>
<stepInt>10</stepInt>
<valInt>495</valInt>
<suffix> Hz</suffix>
<vTx>3</vTx>
</haptic.duty.frequency>
<haptic.duty.voltage>
<longName>Duty Cycle Strength</longName>
<type>1</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Roboto'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Strength of the audible Duty Cycle haptic feedback, in Volts.&lt;span style=&quot; font-style:italic;&quot;&gt;&lt;br /&gt;&lt;br /&gt;Recommended Values: 1.0 - 6.0 (Caution with higher values!)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Set to 0.0 to disable.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>CFG_DFLT_HAPTIC_DUTY_VOLTAGE</cDefine>
<editorDecimalsDouble>1</editorDecimalsDouble>
<editorScale>1</editorScale>
<editAsPercentage>0</editAsPercentage>
<maxDouble>10</maxDouble>
<minDouble>0</minDouble>
<showDisplay>0</showDisplay>
<stepDouble>0.2</stepDouble>
<valDouble>3</valDouble>
<vTxDoubleScale>10</vTxDoubleScale>
<suffix> V</suffix>
<vTx>7</vTx>
</haptic.duty.voltage>
<haptic.duty_solid_threshold>
<longName>Duty Cycle Solid Threshold</longName>
<type>1</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Roboto'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Duty Cycle threshold to trigger solid tone haptic feedback.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Set to 100% to disable.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>CFG_DFLT_HAPTIC_DUTY_SOLID_THRESHOLD</cDefine>
<editorDecimalsDouble>2</editorDecimalsDouble>
<editorScale>1</editorScale>
<editAsPercentage>1</editAsPercentage>
<maxDouble>1</maxDouble>
<minDouble>0</minDouble>
<showDisplay>0</showDisplay>
<stepDouble>0.01</stepDouble>
<valDouble>0.85</valDouble>
<vTxDoubleScale>1000</vTxDoubleScale>
<suffix></suffix>
<vTx>7</vTx>
</haptic.duty_solid_threshold>
<haptic.error.frequency>
<longName>Error Frequency</longName>
<type>2</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Roboto'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Audible frequency of the Error haptic feedback.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>CFG_DFLT_HAPTIC_ERROR_FREQUENCY</cDefine>
<editorScale>1</editorScale>
<editAsPercentage>0</editAsPercentage>
<maxInt>1000</maxInt>
<minInt>200</minInt>
<showDisplay>0</showDisplay>
<stepInt>10</stepInt>
<valInt>495</valInt>
<suffix> Hz</suffix>
<vTx>3</vTx>
</haptic.error.frequency>
<haptic.error.voltage>
<longName>Error Strength</longName>
<type>1</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Roboto'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Strength of the audible Error haptic feedback, in Volts.&lt;span style=&quot; font-style:italic;&quot;&gt;&lt;br /&gt;&lt;br /&gt;Recommended Values: 1.0 - 6.0 (Caution with higher values!)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Set to 0.0 to disable.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>CFG_DFLT_HAPTIC_ERROR_VOLTAGE</cDefine>
<editorDecimalsDouble>1</editorDecimalsDouble>
<editorScale>1</editorScale>
<editAsPercentage>0</editAsPercentage>
<maxDouble>10</maxDouble>
<minDouble>0</minDouble>
<showDisplay>0</showDisplay>
<stepDouble>0.2</stepDouble>
<valDouble>3</valDouble>
<vTxDoubleScale>10</vTxDoubleScale>
<suffix> V</suffix>
<vTx>7</vTx>
</haptic.error.voltage>
<haptic.vibrate.frequency>
<longName>Vibrate Frequency</longName>
<type>2</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Roboto'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Vibrating frequency of both Duty Cycle and Error haptic feedback.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>CFG_DFLT_HAPTIC_VIBRATE_FREQUENCY</cDefine>
<editorScale>1</editorScale>
<editAsPercentage>0</editAsPercentage>
<maxInt>200</maxInt>
<minInt>10</minInt>
<showDisplay>0</showDisplay>
<stepInt>5</stepInt>
<valInt>120</valInt>
<suffix> Hz</suffix>
<vTx>3</vTx>
</haptic.vibrate.frequency>
<haptic.vibrate.voltage>
<longName>Vibrate Strength</longName>
<type>1</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Roboto'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Strength of the vibrating haptic feedback for both Duty Cycle and Error, in Volts.&lt;span style=&quot; font-style:italic;&quot;&gt;&lt;br /&gt;&lt;br /&gt;Recommended Values: 1.0 - 6.0 (Caution with higher values!)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Set to 0.0 to disable the vibrating frequency for haptic alerts.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>CFG_DFLT_HAPTIC_VIBRATE_VOLTAGE</cDefine>
<editorDecimalsDouble>1</editorDecimalsDouble>
<editorScale>1</editorScale>
<editAsPercentage>0</editAsPercentage>
<maxDouble>10</maxDouble>
<minDouble>0</minDouble>
<showDisplay>0</showDisplay>
<stepDouble>0.2</stepDouble>
<valDouble>0</valDouble>
<vTxDoubleScale>10</vTxDoubleScale>
<suffix> V</suffix>
<vTx>7</vTx>
</haptic.vibrate.voltage>
<haptic.tone_length>
<longName>Tone Length</longName>
<type>2</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Roboto'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Length of the haptic feedback beeps in milliseconds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>CFG_DFLT_HAPTIC_TONE_LENGTH</cDefine>
<editorScale>1</editorScale>
<editAsPercentage>0</editAsPercentage>
<maxInt>2000</maxInt>
<minInt>50</minInt>
<showDisplay>0</showDisplay>
<stepInt>50</stepInt>
<valInt>200</valInt>
<suffix> ms</suffix>
<vTx>3</vTx>
</haptic.tone_length>
<noseangling_speed>
<longName>Nose Angling Speed</longName>
<type>1</type>
Expand Down Expand Up @@ -3564,6 +3740,14 @@ p, li { white-space: pre-wrap; }
<ser>dark_pitch_offset</ser>
<ser>is_beeper_enabled</ser>
<ser>disabled</ser>
<ser>haptic.duty.voltage</ser>
<ser>haptic.duty.frequency</ser>
<ser>haptic.duty_solid_threshold</ser>
<ser>haptic.error.voltage</ser>
<ser>haptic.error.frequency</ser>
<ser>haptic.vibrate.voltage</ser>
<ser>haptic.vibrate.frequency</ser>
<ser>haptic.tone_length</ser>
</SerOrder>
<Grouping>
<group>
Expand Down Expand Up @@ -3703,6 +3887,22 @@ p, li { white-space: pre-wrap; }
<param>tiltback_lv_speed</param>
</subgroupParams>
</subgroup>
<subgroup>
<subgroupName>Haptic Feedback (6.05+)</subgroupName>
<subgroupParams>
<param>::sep::Duty</param>
<param>haptic.duty.voltage</param>
<param>haptic.duty.frequency</param>
<param>haptic.duty_solid_threshold</param>
<param>::sep::Error</param>
<param>haptic.error.voltage</param>
<param>haptic.error.frequency</param>
<param>::sep::General</param>
<param>haptic.vibrate.voltage</param>
<param>haptic.vibrate.frequency</param>
<param>haptic.tone_length</param>
</subgroupParams>
</subgroup>
<subgroup>
<subgroupName>Remote</subgroupName>
<subgroupParams>
Expand Down
127 changes: 127 additions & 0 deletions src/haptic_feedback.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright 2024 Lukas Hrazky
//
// This file is part of the Refloat VESC package.
//
// Refloat VESC package is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// Refloat VESC package is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <http://www.gnu.org/licenses/>.

#include "haptic_feedback.h"

#include "vesc_c_if.h"

#include <math.h>

void haptic_feedback_init(HapticFeedback *hf) {
hf->type_playing = HAPTIC_FEEDBACK_NONE;
hf->start_time = 0.0f;
hf->is_playing = false;
}

void haptic_feedback_configure(HapticFeedback *hf, const CfgHapticFeedback *cfg) {
hf->cfg = cfg;
}

static HapticFeedbackType state_to_haptic_type(const State *state) {
if (state->state != STATE_RUNNING) {
return HAPTIC_FEEDBACK_NONE;
}

switch (state->sat) {
case SAT_PB_DUTY:
return HAPTIC_FEEDBACK_DUTY;
case SAT_PB_TEMPERATURE:
return HAPTIC_FEEDBACK_ERROR_TEMPERATURE;
case SAT_PB_LOW_VOLTAGE:
case SAT_PB_HIGH_VOLTAGE:
return HAPTIC_FEEDBACK_ERROR_VOLTAGE;
default:
return HAPTIC_FEEDBACK_NONE;
}
}

// Returns the number of "beats" per period of a given tone. Tones are played
// on even beats and if there are more than two beats, the last beat is
// skipped, giving a certain number of "beeps" followed by a pause.
static uint8_t get_beats(HapticFeedbackType type) {
switch (type) {
case HAPTIC_FEEDBACK_DUTY:
return 2;
case HAPTIC_FEEDBACK_DUTY_CONTINUOUS:
return 0;
case HAPTIC_FEEDBACK_ERROR_TEMPERATURE:
return 6;
case HAPTIC_FEEDBACK_ERROR_VOLTAGE:
return 8;
case HAPTIC_FEEDBACK_NONE:
break;
}

return 0;
}

static const CfgHapticTone *get_haptic_tone(const HapticFeedback *hf) {
switch (hf->type_playing) {
case HAPTIC_FEEDBACK_DUTY:
case HAPTIC_FEEDBACK_DUTY_CONTINUOUS:
return &hf->cfg->duty;
case HAPTIC_FEEDBACK_ERROR_TEMPERATURE:
case HAPTIC_FEEDBACK_ERROR_VOLTAGE:
return &hf->cfg->error;
case HAPTIC_FEEDBACK_NONE:
break;
}

return 0;
}

void haptic_feedback_update(
HapticFeedback *hf, const State *state, float duty_cycle, float current_time
) {
if (!VESC_IF->foc_play_tone) {
return;
}

HapticFeedbackType type_to_play = state_to_haptic_type(state);
if (type_to_play == HAPTIC_FEEDBACK_DUTY && duty_cycle > hf->cfg->duty_solid_threshold) {
type_to_play = HAPTIC_FEEDBACK_DUTY_CONTINUOUS;
}

float tone_length = hf->cfg->tone_length * 0.001f;

if (type_to_play != hf->type_playing && current_time - hf->start_time > tone_length) {
hf->type_playing = type_to_play;
hf->start_time = current_time;
}

bool should_be_playing = false;
if (hf->type_playing != HAPTIC_FEEDBACK_NONE) {
uint8_t beats = get_beats(hf->type_playing);
float period = tone_length * beats;
float time = fmodf(current_time - hf->start_time, period);
uint8_t beat = floorf(time / tone_length);
uint8_t off_beat = beats > 2 ? beats - 2 : 0;

should_be_playing = beats == 0 || (beat % 2 == 0 && (off_beat == 0 || beat != off_beat));
}

if (hf->is_playing && !should_be_playing) {
VESC_IF->foc_play_tone(0, 1, 0.0f);
VESC_IF->foc_play_tone(1, 1, 0.0f);
hf->is_playing = false;
} else if (!hf->is_playing && should_be_playing) {
const CfgHapticTone *tone = get_haptic_tone(hf);
VESC_IF->foc_play_tone(0, tone->frequency, tone->voltage);
VESC_IF->foc_play_tone(1, hf->cfg->vibrate.frequency, hf->cfg->vibrate.voltage);
hf->is_playing = true;
}
}
Loading

0 comments on commit d6b751f

Please sign in to comment.