Open FFBoard
Open source force feedback firmware
CanInputMain.cpp
Go to the documentation of this file.
1/*
2 * CanInputMain.cpp
3 *
4 * Created on: Apr 28, 2025
5 * Author: Yannick
6 */
7
8
9#include "CanInputMain.h"
10#include "usb_hid_ffb_desc.h"
11#ifdef CANINPUTMAIN
12// Unique identifier for listing
14 .name = "CAN remote Digital/Analog" ,
15 .id=CLSID_MAIN_CANINPUT,
16 };
17
19 return info;
20}
21
22extern CANPort& canport; // Must be defined in target constants
24{
25
26}
28 SelectableInputs(ButtonSource::all_buttonsources,AnalogSource::all_analogsources),
29 Thread("FFBMAIN", 256, 30),
30 can(can)
31{
32 this->restoreFlash();
33 this->registerCommands();
34 analogBuffer.reserve(8);
35 this->Start();
36}
37
39
40}
41
42
43
46 usbdev->registerUsb();
47}
48
49
50
55
56 Flash_Read(ADR_FFBWHEEL_BUTTONCONF, &this->btnsources);
58
59 Flash_Read(ADR_FFBWHEEL_ANALOGCONF, &this->ainsources);
61
62 uint16_t conf1;
63 if(Flash_Read(ADR_CANREMOTE_CONF1,&conf1)){
64 buttons_id = conf1 & 0x7ff; // 11b
65 uint8_t rateidx = (conf1 >> 11) & 0x7;
66 setReportRate(rateidx);
67 }
68 if(Flash_Read(ADR_CANREMOTE_CONF2,&conf1)){
69 analog_id = conf1 & 0x7ff; // 11b
70 }
71
72}
77
78 Flash_Write(ADR_FFBWHEEL_BUTTONCONF,this->btnsources);
79 Flash_Write(ADR_FFBWHEEL_ANALOGCONF,this->ainsources);
80
81 uint16_t conf;
82 conf = buttons_id & 0x7ff;
83 conf |= (rate_idx & 0x7) << 11;
84 Flash_Write(ADR_CANREMOTE_CONF1,conf);
85 conf = analog_id & 0x7ff;
86 Flash_Write(ADR_CANREMOTE_CONF2,conf);
87}
88
89
90
92
93 while(true){
94 Delay(1);
96 }
97}
98
99
105 report_rate_cnt = 0;
106
107 sendReport();
108 }
109}
110
112 uint64_t prevDigital = digitalBuffer;
113 digitalBuffer = 0; // Clear buffer
115 if(prevDigital != digitalBuffer){ // Only send when data changed
116 CAN_tx_msg msg;
117 msg.header.id = buttons_id;
118 msg.header.length = 8; // 64 buttons
119 for(uint8_t i = 0;i<8;i++){
120 msg.data[i] = ((digitalBuffer >> (i*8)) & 0xff);
121 }
122 can.sendMessage(msg);
123 }
124
125 std::vector<int32_t>* analogValues = SelectableInputs::getAnalogValues();
126 if(analogValues->size() != analogBuffer.size()){
127 analogBuffer.assign(analogValues->size(), 0); // Initialize vector if size changes
128 }
129
130 CAN_tx_msg msg;
131 const uint8_t valuesPerMsg = 4;
132 for(uint8_t i = 0;i<analogValues->size();i+=valuesPerMsg){
133 bool changed = false;
134 msg.header.id = analog_id + (i/valuesPerMsg);
135 msg.header.length = 8;
136 for(uint8_t v = 0;v<valuesPerMsg;v++){
137 uint8_t idx = i+v;
138 if(idx >= analogValues->size()){
139 break;
140 }
141 int32_t val = analogValues->at(idx);
142 if(val != analogBuffer[idx]){
143 changed = true;
144 }
145 msg.data[v*2] = val & 0xff;
146 msg.data[(v*2) +1] = (val >> 8) & 0xff;
147 }
148 if(changed){
149 can.sendMessage(msg);
150 msg = CAN_tx_msg(); // Clear
151 }
152 }
153 analogBuffer = *analogValues; // Copy values
154}
155
159void CANInputMain::setReportRate(uint8_t rateidx){
160 rateidx = clip<uint8_t,uint8_t>(rateidx, 0,report_rates.size());
161 rate_idx = rateidx;
162 report_rate = report_rates[rateidx];
163}
164
169 std::string s = "";
170 for(uint8_t i = 0 ; i < report_rates.size();i++){
171 s += std::to_string(1000/(report_rates[i])) + "Hz:"+std::to_string(i);
172 if(i < sizeof(report_rates)-1)
173 s += ",";
174 }
175 return s;
176}
177
178
180 // CAN speed controlled by CAN port commands directly
181 registerCommand("canidbtn", CANInput_commands::caniddigital, "Button output CAN ID",CMDFLAG_GET|CMDFLAG_SET);
182 registerCommand("canidain", CANInput_commands::canidanalog, "Analog output start CAN ID",CMDFLAG_GET|CMDFLAG_SET);
183
184 registerCommand("btntypes", CANInput_commands::btntypes, "Enabled button sources",CMDFLAG_GET|CMDFLAG_SET);
185 registerCommand("addbtn", CANInput_commands::addbtn, "Enable button source",CMDFLAG_SET);
186 registerCommand("lsbtn", CANInput_commands::lsbtn, "Get available button sources",CMDFLAG_GET|CMDFLAG_STR_ONLY);
187
188 registerCommand("aintypes", CANInput_commands::aintypes, "Enabled analog sources",CMDFLAG_GET|CMDFLAG_SET);
189 registerCommand("lsain", CANInput_commands::lsain, "Get available analog sources",CMDFLAG_GET|CMDFLAG_STR_ONLY);
190 registerCommand("addain", CANInput_commands::addain, "Enable analog source",CMDFLAG_SET);
191
192 registerCommand("rate", CANInput_commands::rate, "CAN interval rate",CMDFLAG_GET|CMDFLAG_SET|CMDFLAG_INFOSTRING);
193
194 registerCommand("dvals", CANInput_commands::dvals, "Current digital outputs",CMDFLAG_GET);
195 registerCommand("avals", CANInput_commands::avals, "Current analog outputs",CMDFLAG_GET);
196}
197
198CommandStatus CANInputMain::command(const ParsedCommand& cmd,std::vector<CommandReply>& replies){
199 switch(static_cast<CANInput_commands>(cmd.cmdId)){
201 handleGetSet(cmd, replies, buttons_id);
202 break;
204 handleGetSet(cmd, replies, analog_id);
205 break;
206 // Source management
208 if(cmd.type == CMDtype::get){
209 replies.emplace_back(btnsources);
210 }else if(cmd.type == CMDtype::set){
211 setBtnTypes(cmd.val);
212 }
213 break;
215 btn_chooser.replyAvailableClasses(replies);
216 break;
218 if(cmd.type == CMDtype::set){
219 this->addBtnType(cmd.val);
220 }
221 break;
223 if(cmd.type == CMDtype::get){
224 replies.emplace_back(ainsources);
225 }else if(cmd.type == CMDtype::set){
226 setAinTypes(cmd.val);
227 }
228 break;
230 analog_chooser.replyAvailableClasses(replies);
231 break;
233 if(cmd.type == CMDtype::set){
234 this->addAinType(cmd.val);
235 }
236 break;
237
239 if(cmd.type == CMDtype::get){
240 replies.emplace_back(rate_idx);
241 }else if(cmd.type == CMDtype::set){
242 setReportRate(cmd.val);
243 }else if(cmd.type == CMDtype::info){
244 replies.emplace_back(report_rates_names());
245 }
246 break;
247
249 if(cmd.type == CMDtype::get){
250 replies.emplace_back(digitalBuffer,0);
251 }
252 break;
253
255 if(cmd.type == CMDtype::get){
256 for(uint8_t i = 0;i<analogBuffer.size();i++){
257 replies.emplace_back(analogBuffer[i],i);
258 }
259 }
260 break;
261
262
263 default:
265 }
266 return CommandStatus::OK;
267}
268
269
270#endif
CANPort & canport
CommandStatus
uint32_t report_rate_cnt
Definition: CanInputMain.h:55
void restoreFlash()
virtual ~CANInputMain()
static ClassIdentifier info
Definition: CanInputMain.h:30
uint32_t report_rate
Definition: CanInputMain.h:56
void setReportRate(uint8_t rateidx)
CommandStatus command(const ParsedCommand &cmd, std::vector< CommandReply > &replies)
void registerCommands()
const std::array< uint8_t, 7 > report_rates
Definition: CanInputMain.h:67
std::vector< int32_t > analogBuffer
Definition: CanInputMain.h:58
void updateControl()
uint32_t buttons_id
Definition: CanInputMain.h:63
uint8_t rate_idx
Definition: CanInputMain.h:66
void saveFlash()
void usbInit() override
uint64_t digitalBuffer
Definition: CanInputMain.h:59
const ClassIdentifier getInfo()
std::string report_rates_names()
CANPort & can
Definition: CanInputMain.h:61
uint32_t analog_id
Definition: CanInputMain.h:64
Definition: CAN.h:119
virtual bool sendMessage(CAN_tx_msg &msg)=0
void registerCommand(const char *cmd, const ID cmdid, const char *help=nullptr, uint32_t flags=0)
static CommandStatus handleGetSet(const ParsedCommand &cmd, std::vector< CommandReply > &replies, TVal &value)
std::unique_ptr< USBdevice > usbdev
Definition: FFBoardMain.h:66
virtual void setAinTypes(uint16_t aintypes)
virtual void setBtnTypes(uint16_t btntypes)
virtual void addAinType(uint16_t id)
virtual std::vector< int32_t > * getAnalogValues()
virtual void addBtnType(uint16_t id)
ClassChooser< AnalogSource > analog_chooser
ClassChooser< ButtonSource > btn_chooser
virtual uint8_t getButtonValues(uint64_t &values)
void Delay(const TickType_t Delay)
Definition: thread.hpp:352
bool Flash_Write(uint16_t adr, uint16_t dat)
bool Flash_Read(uint16_t adr, uint16_t *buf, bool checkempty=true)
uint32_t id
Definition: CAN.h:60
uint32_t length
Definition: CAN.h:61
Definition: CAN.h:89
CAN_msg_header_tx header
Definition: CAN.h:92
uint8_t data[CAN_MSGBUFSIZE]
Definition: CAN.h:90
const char * name
uint32_t cmdId
const uint8_t usb_cdc_conf[]
const tusb_desc_device_t usb_devdesc_ffboard_composite
const usb_string_desc_t usb_ffboard_strings_default