Open FFBoard
Open source force feedback firmware
Loading...
Searching...
No Matches
HidFFB.cpp
Go to the documentation of this file.
1/*
2 * HidFFB.cpp
3 *
4 * Created on: 12.02.2020
5 * Author: Yannick
6 */
7
8#include <assert.h>
9#include "HidFFB.h"
10#include "flash_helpers.h"
11#include "hid_device.h"
12#include "cppmain.h"
13#include <math.h>
14
15HidFFB::HidFFB(std::shared_ptr<EffectsCalculator> ec,uint8_t axisCount) : effects_calc(ec), effects(ec->effects),axisCount(axisCount)
16{
17 directionEnableMask = 1 << axisCount; // Direction enable bit is last bit after axis enable bits
18 // Initialize reports
19 blockLoad_report.effectBlockIndex = 1;
20 blockLoad_report.ramPoolAvailable = (effects.size()-used_effects)*sizeof(FFB_Effect);
21 blockLoad_report.loadStatus = 1;
22
23 pool_report.ramPoolSize = effects.size()*sizeof(FFB_Effect);
24 pool_report.maxSimultaneousEffects = effects.size();
25 pool_report.memoryManagement = 1;
26
27
28 this->registerHidCallback();
29}
30
33
34
39 this->directionEnableMask = mask;
40}
41
42void HidFFB::updateSamplerate(float newSamplerate){
43 effects_calc->updateSamplerate(newSamplerate);
44}
45
46
48 return this->ffb_active;
49}
50
51bool HidFFB::HID_SendReport(uint8_t *report,uint16_t len){
52 return tud_hid_report(0, report, len); // ID 0 skips ID field
53}
54
55
59void HidFFB::sendStatusReport(uint8_t effect){
60// if(effect != 0){
61// this->reportFFBStatus.effectBlockIndex = effect;
62// }
63 this->reportFFBStatus.status = HID_ACTUATOR_POWER;
64 if(this->ffb_active){
65 this->reportFFBStatus.status |= HID_ENABLE_ACTUATORS;
66 this->reportFFBStatus.status |= HID_EFFECT_PLAYING;
67 }else{
68 this->reportFFBStatus.status |= HID_EFFECT_PAUSE;
69 }
70// if(effect > 0 && effects[effect-1].state == 1)
71// this->reportFFBStatus.status |= HID_EFFECT_PLAYING;
72 //printf("Status: %d\n",reportFFBStatus.status);
73 HID_SendReport(reinterpret_cast<uint8_t*>(&this->reportFFBStatus), sizeof(reportFFB_status_t));
74}
75
76
80void HidFFB::hidOut(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize){
81 fxUpdateEvent(); // use uint16_t for timer overflow handling if micros timer is used
82
83 // FFB Output Message
84 const uint8_t* report = buffer;
85 uint8_t event_idx = report_id - FFB_ID_OFFSET;
86
87 // -------- Out Reports --------
88 switch(event_idx)
89 {
90
91 case HID_ID_NEWEFREP: //add Effect Report. Feature
92 new_effect((FFB_CreateNewEffect_Feature_Data_t*)(report));
93 break;
94 case HID_ID_EFFREP: // Set Effect
95 {
96 FFB_SetEffect_t setEffectRepBuf;
97 memcpy(&setEffectRepBuf,report,std::min<uint16_t>(sizeof(FFB_SetEffect_t),bufsize)); // Copy report to buffer. only valid range if less axes are used
98 set_effect(&setEffectRepBuf);
99 break;
100 }
101 case HID_ID_CTRLREP: // Control report. 1=Enable Actuators, 2=Disable Actuators, 4=Stop All Effects, 8=Reset, 16=Pause, 32=Continue
102 ffb_control(report[1]);
103 //sendStatusReport(0);
104 break;
105 case HID_ID_GAINREP: // Set global gain
106 set_gain(report[1]);
107 break;
108 case HID_ID_ENVREP: // Envelope
109 set_envelope((FFB_SetEnvelope_Data_t *)report);
110 break;
111 case HID_ID_CONDREP: // Spring, Damper, Friction, Inertia
112 set_condition((FFB_SetCondition_Data_t*)report);
113 break;
114 case HID_ID_PRIDREP: // Periodic
115 set_periodic((FFB_SetPeriodic_Data_t*)report);
116 break;
117 case HID_ID_CONSTREP: // Constant
118 set_constant_effect((FFB_SetConstantForce_Data_t*)report);
119 break;
120 case HID_ID_RAMPREP: // Ramp
121 set_ramp((FFB_SetRamp_Data_t *)report);
122 break;
123 case HID_ID_CSTMREP: // Custom. pretty much never used
124 //printf("Customrep");
125 break;
126 case HID_ID_SMPLREP: // Download sample
127 //printf("Sampledl");
128 break;
129 case HID_ID_EFOPREP: //Effect operation
130 {
131 set_effect_operation((FFB_EffOp_Data_t*)report);
132 break;
133 }
134 case HID_ID_BLKFRREP: // Free a block
135 {
136 effects_calc->free_effect(report[1]-1);
137 break;
138 }
139
140 default:
141 {
142 break;
143 }
144 }
145
146}
147
148
155uint16_t HidFFB::hidGet(uint8_t report_id, hid_report_type_t report_type,uint8_t* buffer, uint16_t reqlen){
156 // Feature gets go here
157
158 uint8_t id = report_id - FFB_ID_OFFSET;
159
160 switch(id){
161 case HID_ID_BLKLDREP:
162 //printf("Get Block Report\n");
163 // Notice: first byte ID is not present in the reply buffer because it is handled by tinyusb internally!
164 memcpy(buffer,&this->blockLoad_report,sizeof(FFB_BlockLoad_Feature_Data_t));
165 return sizeof(FFB_BlockLoad_Feature_Data_t);
166 break;
167 case HID_ID_POOLREP:
168 //printf("Get Pool Report\n");
169 memcpy(buffer,&this->pool_report,sizeof(FFB_PIDPool_Feature_Data_t));
170 return sizeof(FFB_PIDPool_Feature_Data_t);
171 break;
172 default:
173 break;
174 }
175 return 0;
176}
177
179#ifdef DEBUGLOG
181#endif
182 this->set_FFB(true);
183}
184
186#ifdef DEBUGLOG
188#endif
189 this->set_FFB(false);
190}
191
193{
194 assert(effects_calc != nullptr);
195 this->ffb_active = state;
196 effects_calc->setActive(state);
197}
198
199void HidFFB::set_gain(uint8_t gain){
200 assert(effects_calc != nullptr);
201 effects_calc->setGain(gain);
202}
203
205 assert(effects_calc != nullptr);
206 effects_calc->setFilters(effect);
207}
208
209void HidFFB::ffb_control(uint8_t cmd){
210
211 if(cmd & 0x01){ //enable
212 start_FFB();
213 }if(cmd & 0x02){ //disable
214 stop_FFB();
215 }if(cmd & 0x04){ //stop
216 stop_FFB();
217 //start_FFB();
218 }if(cmd & 0x08){ //reset
219 //ffb_active = true;
220 stop_FFB();
221 reset_ffb();
222 // reset effects
223 }if(cmd & 0x10){ //pause
224 stop_FFB();
225 }if(cmd & 0x20){ //continue
226 start_FFB();
227 }
228}
229
230
231void HidFFB::set_constant_effect(FFB_SetConstantForce_Data_t* data){
232 if(data->effectBlockIndex == 0 || data->effectBlockIndex > effects.size()){
233 return;
234 }
236 FFB_Effect& effect_p = effects[data->effectBlockIndex-1];
237
238 effect_p.magnitude = data->magnitude;
239// if(effect_p.state == 0){
240// effect_p.state = 1; // Force start effect
241// }
242}
243
244void HidFFB::new_effect(FFB_CreateNewEffect_Feature_Data_t* effect){
245 // Allocates a new effect
246
247 int32_t index = effects_calc->find_free_effect(effect->effectType); // next effect
248 if(index == -1){
249 blockLoad_report.loadStatus = 2;
250#ifdef DEBUGLOG
251 CommandHandler::logSerialDebug("Can't allocate a new effect");
252#endif
253 return;
254 }
256 new_effect.type = effect->effectType;
257
258 this->effects_calc->logEffectType(effect->effectType,false);
259#ifdef DEBUGLOG
260 CommandHandler::logSerialDebug("New effect type:" + std::to_string(effect->effectType) + " idx: " + std::to_string(index-1));
261#endif
262
264
265 effects[index] = std::move(new_effect);
266 // Set block load report
267 //reportFFBStatus.effectBlockIndex = index;
268 blockLoad_report.effectBlockIndex = index+1;
269 used_effects++;
270 blockLoad_report.ramPoolAvailable = (effects.size()-used_effects)*sizeof(FFB_Effect);
271 blockLoad_report.loadStatus = 1;
272 sendStatusReport(index+1);
273
274
275}
276
281void HidFFB::set_effect(FFB_SetEffect_t* effect){
282 uint8_t index = effect->effectBlockIndex;
283 if(index > effects.size() || index == 0)
284 return;
285
286 FFB_Effect* effect_p = &effects[index-1];
287
288 if (effect_p->type != effect->effectType){
289 effect_p->startTime = 0;
290 set_filters(effect_p);
291 }
292
293 effect_p->gain = effect->gain;
294 effect_p->type = effect->effectType;
295 effect_p->samplePeriod = effect->samplePeriod;
296
297 bool directionEnable = (effect->enableAxis & this->directionEnableMask);
298 bool overridesCondition = false;
299
300// if(effect_p->useSingleCondition){ // Only allow turning single condition off in case it was overridden by sending multiple conditions previously
301// effect_p->useSingleCondition = directionEnable; // If direction is used only a single parameter block is allowed. Somehow this is still set while 2 conditions are sent...
302// }
303 // Conditional effects usually do not use directions
304 if(!effect_p->useSingleCondition && (effect->effectType == FFB_EFFECT_SPRING || effect->effectType == FFB_EFFECT_DAMPER || effect->effectType == FFB_EFFECT_INERTIA || effect->effectType == FFB_EFFECT_FRICTION))
305 {
306 if(effect_p->conditions[0].isActive()){
307 effect_p->axisMagnitudes[0] = 1.0f;
308 overridesCondition = true;
309 }
310 if(effect_p->conditions[1].isActive() || effect_p->useSingleCondition){
311 effect_p->axisMagnitudes[1] = 1.0f;
312 overridesCondition = true;
313 }
314 }
315
316
317 if(!overridesCondition){
318 float phaseX = M_PI*2.0 * (effect->directionX/36000.0f);
319
320 effect_p->axisMagnitudes[0] = directionEnable ? sin(phaseX) : (effect->enableAxis & X_AXIS_ENABLE ? (effect->directionX - 18000.0f) / 18000.0f : 0); // Angular vector if dirEnable used otherwise linear or 0 if axis enabled
321 effect_p->axisMagnitudes[1] = directionEnable ? -cos(phaseX) : (effect->enableAxis & Y_AXIS_ENABLE ? -(effect->directionY - 18000.0f) / 18000.0f : 0);
322 }
323
324#if MAX_AXIS == 3
325 float phaseY = M_PI*2.0 * (effect->directionY/36000.0);
326 effect_p->axisMagnitudes[3] = directionEnable ? sin(phaseY) : (effect->enableAxis & Z_AXIS_ENABLE ? (effect->directionZ - 18000.0f) / 18000.0f : 0);
327#endif
328 if(effect->duration == 0){ // Fix for games assuming 0 is infinite
329 effect_p->duration = FFB_EFFECT_DURATION_INFINITE;
330 }else{
331 effect_p->duration = effect->duration;
332 }
333 effect_p->startDelay = effect->startDelay;
334 if(!ffb_active)
335 start_FFB();
336
337 sendStatusReport(effect->effectBlockIndex);
338 //CommandHandler::logSerialDebug("Setting Effect: " + std::to_string(effect->effectType) + " at " + std::to_string(index) + "\n");
339}
340
354void HidFFB::set_condition(FFB_SetCondition_Data_t *cond){
355 if(cond->effectBlockIndex == 0 || cond->effectBlockIndex > effects.size()){
356 return;
357 }
358 uint8_t axis = std::min(axisCount,cond->parameterBlockOffset);
359
360 FFB_Effect *effect = &effects[cond->effectBlockIndex - 1];
361 effect->conditions[axis].cpOffset = cond->cpOffset;
362 effect->conditions[axis].negativeCoefficient = cond->negativeCoefficient;
363 effect->conditions[axis].positiveCoefficient = cond->positiveCoefficient;
364 effect->conditions[axis].negativeSaturation = cond->negativeSaturation;
365 effect->conditions[axis].positiveSaturation = cond->positiveSaturation;
366 effect->conditions[axis].deadBand = cond->deadBand;
367
368// if(effect->conditions[axis].positiveSaturation == 0){
369// effect->conditions[axis].positiveSaturation = 0x7FFF;
370// }
371// if(effect->conditions[axis].negativeSaturation == 0){
372// effect->conditions[axis].negativeSaturation = 0x7FFF;
373// }
374
375 if(axis>0 && axis < MAX_AXIS && effect->conditions[axis].isActive()){ // Workaround when direction enable is set but multiple conditions are defined... Resets direction and uses conditions again
376 effect->useSingleCondition = false;
377 }
378 if((effect->conditions[axis].isActive() || (axis > 0 && effect->useSingleCondition)) && effect->axisMagnitudes[axis] == 0){
379 effect->axisMagnitudes[axis] = 1.0;
380 }
381
382// for(uint8_t i = 0;i<MAX_AXIS;i++){
383// effect->axisMagnitudes[i] = 1.0;
384// }
385}
386
387void HidFFB::set_effect_operation(FFB_EffOp_Data_t* report){
388 if(report->effectBlockIndex == 0 || report->effectBlockIndex > effects.size()){
389 return; // Invalid ID
390 }
391 // Start or stop effect
392 uint8_t id = report->effectBlockIndex-1;
393 if(report->state == 3){
394 effects[id].state = 0; //Stop
395#ifdef DEBUGLOG
396 CommandHandler::logSerialDebug("Stop effect: " + std::to_string(id));
397#endif
398
399 }else{
400
401 // 1 = start, 2 = start solo
402 if(report->state == 2){
403#ifdef DEBUGLOG
404 CommandHandler::logSerialDebug("Start solo: " + std::to_string(id));
405#endif
406 for(FFB_Effect& effect : effects){
407 effect.state = 0; // Stop all other effects
408 }
409 }
410 if(effects[id].state != 1){
411 set_filters(&effects[id]);
412 }
413#ifdef DEBUGLOG
414 CommandHandler::logSerialDebug("Start effect: " + std::to_string(id));
415#endif
416 effects[id].startTime = HAL_GetTick() + effects[id].startDelay; // + effects[id].startDelay;
417 effects[id].state = 1; //Start
418
419
420 }
421 //sendStatusReport(report[1]);
422 this->effects_calc->logEffectState(effects[id].type,effects[id].state);
423}
424
425
426void HidFFB::set_envelope(FFB_SetEnvelope_Data_t *report){
427 if(report->effectBlockIndex == 0 || report->effectBlockIndex > effects.size()){
428 return;
429 }
430 FFB_Effect *effect = &effects[report->effectBlockIndex - 1];
431
432 effect->attackLevel = report->attackLevel;
433 effect->attackTime = report->attackTime;
434 effect->fadeLevel = report->fadeLevel;
435 effect->fadeTime = report->fadeTime;
436 effect->useEnvelope = true;
437}
438
439void HidFFB::set_ramp(FFB_SetRamp_Data_t *report){
440 if(report->effectBlockIndex == 0 || report->effectBlockIndex > effects.size()){
441 return;
442 }
443 FFB_Effect *effect = &effects[report->effectBlockIndex - 1];
444 effect->magnitude = 0x7fff; // Full magnitude for envelope calculation. This effect does not have a periodic report
445 effect->startLevel = report->startLevel;
446 effect->endLevel = report->endLevel;
447}
448
449void HidFFB::set_periodic(FFB_SetPeriodic_Data_t* report){
450 if(report->effectBlockIndex == 0 || report->effectBlockIndex > effects.size()){
451 return;
452 }
453 FFB_Effect* effect = &effects[report->effectBlockIndex-1];
454
455 effect->period = clip<uint32_t,uint32_t>(report->period,1,0x7fff); // Period is never 0
456 effect->magnitude = report->magnitude;
457 effect->offset = report->offset;
458 effect->phase = report->phase;
459 //effect->counter = 0;
460}
461
462
464 for(uint8_t i=0;i<effects.size();i++){
465 effects_calc->free_effect(i);
466 }
467 //this->reportFFBStatus.effectBlockIndex = 1;
468 this->reportFFBStatus.status = (HID_ACTUATOR_POWER) | (HID_ENABLE_ACTUATORS);
469 used_effects = 1;
470}
static void logSerialDebug(std::string string)
Send a log formatted sequence if debug is on.
virtual void fxUpdateEvent()
virtual void cfUpdateEvent()
std::array< FFB_Effect, EffectsCalculator::max_effects > & effects
Definition HidFFB.h:47
reportFFB_status_t reportFFBStatus
Definition HidFFB.h:68
void set_effect_operation(FFB_EffOp_Data_t *report)
Definition HidFFB.cpp:387
void set_FFB(bool state)
Definition HidFFB.cpp:192
void set_constant_effect(FFB_SetConstantForce_Data_t *effect)
Definition HidFFB.cpp:231
void start_FFB()
Definition HidFFB.cpp:178
uint16_t hidGet(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override
Definition HidFFB.cpp:155
void updateSamplerate(float newSamplerate)
Definition HidFFB.cpp:42
FFB_BlockLoad_Feature_Data_t blockLoad_report
Definition HidFFB.h:65
void setDirectionEnableMask(uint8_t mask)
Definition HidFFB.cpp:38
uint16_t used_effects
Definition HidFFB.h:63
std::shared_ptr< EffectsCalculator > effects_calc
Definition HidFFB.h:46
uint8_t directionEnableMask
Definition HidFFB.h:62
void set_gain(uint8_t gain)
Definition HidFFB.cpp:199
void sendStatusReport(uint8_t effect)
Definition HidFFB.cpp:59
virtual ~HidFFB()
Definition HidFFB.cpp:31
void set_condition(FFB_SetCondition_Data_t *cond)
Definition HidFFB.cpp:354
FFB_PIDPool_Feature_Data_t pool_report
Definition HidFFB.h:66
void set_envelope(FFB_SetEnvelope_Data_t *report)
Definition HidFFB.cpp:426
bool getFfbActive()
Definition HidFFB.cpp:47
void ffb_control(uint8_t cmd)
Definition HidFFB.cpp:209
static bool HID_SendReport(uint8_t *report, uint16_t len)
Definition HidFFB.cpp:51
void new_effect(FFB_CreateNewEffect_Feature_Data_t *effect)
Definition HidFFB.cpp:244
void reset_ffb()
Definition HidFFB.cpp:463
bool ffb_active
Definition HidFFB.h:64
void hidOut(uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, uint16_t bufsize) override
Definition HidFFB.cpp:80
HidFFB(std::shared_ptr< EffectsCalculator > ec, uint8_t axisCount)
Definition HidFFB.cpp:15
void set_ramp(FFB_SetRamp_Data_t *report)
Definition HidFFB.cpp:439
void stop_FFB()
Definition HidFFB.cpp:185
void set_effect(FFB_SetEffect_t *effect)
Definition HidFFB.cpp:281
uint8_t axisCount
Definition HidFFB.h:70
void set_filters(FFB_Effect *effect)
Definition HidFFB.cpp:204
void set_periodic(FFB_SetPeriodic_Data_t *report)
Definition HidFFB.cpp:449
void registerHidCallback()
T clip(T v, C l, C h)
Definition cppmain.h:58
static struct @024127060247016123033304002117326322243354210111 data
hid_report_type_t
HID Request Report Type.
Definition hid.h:85
static TU_ATTR_ALWAYS_INLINE bool tud_hid_report(uint8_t report_id, void const *report, uint16_t len)
Definition hid_device.h:97
uint8_t const * buffer
uint32_t bufsize
Definition midi_device.h:95
static void * memcpy(void *dst, const void *src, size_t n)
Definition ringbuffer.c:8
uint16_t negativeSaturation
Definition ffb_defs.h:295
int16_t negativeCoefficient
Definition ffb_defs.h:293
int16_t positiveCoefficient
Definition ffb_defs.h:292
uint16_t positiveSaturation
Definition ffb_defs.h:294
uint16_t period
Definition ffb_defs.h:325
bool useEnvelope
Definition ffb_defs.h:334
uint16_t startDelay
Definition ffb_defs.h:331
int16_t startLevel
Definition ffb_defs.h:319
uint8_t type
Definition ffb_defs.h:315
bool useSingleCondition
Definition ffb_defs.h:335
uint8_t gain
Definition ffb_defs.h:317
uint32_t duration
Definition ffb_defs.h:326
int16_t magnitude
Definition ffb_defs.h:318
uint32_t attackTime
Definition ffb_defs.h:328
float axisMagnitudes[MAX_AXIS]
Definition ffb_defs.h:321
FFB_Effect_Condition conditions[MAX_AXIS]
Definition ffb_defs.h:323
uint16_t attackLevel
Definition ffb_defs.h:327
int16_t endLevel
Definition ffb_defs.h:320
uint32_t startTime
Definition ffb_defs.h:332
uint32_t fadeTime
Definition ffb_defs.h:328
uint16_t samplePeriod
Definition ffb_defs.h:333
int16_t offset
Definition ffb_defs.h:316
uint16_t fadeLevel
Definition ffb_defs.h:327
int16_t phase
Definition ffb_defs.h:324