├── .gitignore
├── LICENSE.md
├── README.md
├── dcs-bios
├── blank-template.svg
├── vrbox-p51d.png
└── vrbox.lua
├── firmware
└── vrtogglebox.ino
├── hardware
├── Base.stl
├── TopPlate.stl
└── VR_Toggle_Box_Schematic.pdf
└── images
├── back.jpg
├── front-small.jpg
└── front.jpg
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | firmware/vrtogglebox/
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 | Copyright (c) 2020, Matt Dwyer
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | * Neither the name of [project] nor the names of its
16 | contributors may be used to endorse or promote products derived from
17 | this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VR Toggle Box
2 |
3 |
4 |
5 | # Overview
6 |
7 | The VR Toggle Box provides a VR friendly control surface with simultaneous joystick emulation and an optional DCS-BIOS interface. Unlike some other projects, this is a diode matrix and so it supports simultaneous button presses. The device can be used to control switches and dials on any aircraft in Digital Combat Simulator via joystick controls or DCS-BIOS. It can also be used with any game that has a joystick interface that can support up to 120 different joystick buttons.
8 |
9 | A mode switch to the left of the rotary encoders is selectable between 3 positions. Each position changes the mapping of the switches into different sets of joystick buttons or DCS-BIOS control messages. This provides up to 120 different control inputs. A typical setup might be to map the controls on mode 1 for aircraft startup and lights, 2 for nav/radios and 3 for weapons.
10 |
11 | The project includes:
12 |
13 | * STL files for 3D printing the enclosure
14 | * A circuit schematic for wiring the panel
15 | * Code for programming an Arduino Pro Micro
16 | * Customizable LUA scripts for DCS-BIOS to control any aircraft (coming soon)
17 |
18 | # Build Parts List
19 | * QTY 7 - 2 Position Miniature Toggle Switch [Amazon](https://www.amazon.com/gp/product/B013DZB6CO/ref=ppx_yo_dt_b_asin_title_o01_s01?ie=UTF8&psc=1)
20 | * QTY 6 - 3 Position Miniature Toggle Switch [Amazon](https://www.amazon.com/gp/product/B07RNX57ZM/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1)
21 | * QTY 2 - 2 Pin SPST Momentary Mini Push Button Switch [Amazon](https://www.amazon.com/gp/product/B07SJ7RQL8/ref=ppx_yo_dt_b_asin_title_o01_s01?ie=UTF8&psc=1)
22 | * QTY 4 - 5 pin 360 Degree Rotary Encoder with push button [Amazon](https://www.amazon.com/gp/product/B07DM2YMT4/ref=ppx_yo_dt_b_asin_title_o01_s01?ie=UTF8&psc=1)
23 | * QTY 1 - Arduino Pro Micro board [Amazon](https://www.amazon.com/gp/product/B012FOV17O/ref=ppx_yo_dt_b_asin_title_o01_s01?ie=UTF8&psc=1)
24 | * QTY 19 - 1N4004 diode (or most any diode) [Amazon](https://www.amazon.com/Poilee-1N4004-Rectifier-Electronic-Silicon/dp/B07CDCTZ8R/ref=sr_1_1_sspa?keywords=1N4004+diode&qid=1580868878&s=electronics&sr=1-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyWVVQQlJJQ1FHTlYxJmVuY3J5cHRlZElkPUEwMTcwOTk0MzNZWThIRDNEUEFQSCZlbmNyeXB0ZWRBZElkPUEwMDA4OTYwMlc1S0pWNk9MTU8zUiZ3aWRnZXROYW1lPXNwX2F0ZiZhY3Rpb249Y2xpY2tSZWRpcmVjdCZkb05vdExvZ0NsaWNrPXRydWU=)
25 |
26 | # Enclosure and Wiring Build Notes
27 |
28 | * The 3D model provides a top panel with 6mm holes. You may need to drill these slightly larger if you use different size switches.
29 | * The top panel is a snug fit into the base enclosure. You could optionally use an adhesive, but I haven't bothered.
30 | * The direction of the diodes must match the schematic
31 |
32 | # Building the Firmware
33 |
34 | Required Arduino Libraries
35 |
36 | * Adafruit Keypad [Github](https://github.com/adafruit/Adafruit_Keypad)
37 | * Joystick Library [Github](https://github.com/MHeironimus/ArduinoJoystickLibrary)
38 | * Simply Rotary [Github](https://github.com/mprograms/SimpleRotary)
39 | * Arduino DCS-BIOS [Github](https://github.com/dcs-bios/dcs-bios-arduino-library)
40 |
41 | # Mapping controls in DCS
42 |
43 | Simple. VR Toggle Box will appear as a joystick controller so you can select a control and toggle a switch like with any joystick.
44 |
45 | # Installing DCS-BIOS scripts
46 |
47 | Still under development, but there are example files in the dcs-bios folder.
48 | After installing DCS-BIOS, you can connect to the VR-BOX from the DCS-BIOS Dashboard.
49 | Then, add vrbox.lua as a hub script. Currently, the P51-D is modeled fairly well along with a kneeboard file. There is also some start on the Viggen, F16 and F14. It should be straightforward how to edit vrbox.lua to map functions to different models.
50 |
51 | ** If you develop a mapping file, please share it! I would love to include it in this project.
52 |
53 |
54 |
--------------------------------------------------------------------------------
/dcs-bios/vrbox-p51d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdlogic/VR_Toggle_Box/4adc683c77d60d3992c2ad25b358c0f67d723889/dcs-bios/vrbox-p51d.png
--------------------------------------------------------------------------------
/dcs-bios/vrbox.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | hub.registerInputCallback(function(cmd, arg)
6 | local acftName = hub.getSimString("MetadataStart/_ACFT_NAME")
7 |
8 | if cmd == "3_SW1" and arg == "0" then mode=0 end
9 | if cmd == "3_SW1" and arg == "1" then mode=1 end
10 | if cmd == "3_SW1" and arg == "2" then mode=2 end
11 |
12 | if acftName=="AJS37" then AJS37(cmd,arg,mode) end
13 | if acftName=="P-51D" then P51D(cmd,arg,mode) end
14 | if acftName=="F-16C_50" then F16C50(cmd,arg,mode) end
15 | if acftName=="F-14B" then F14B(cmd,arg,mode) end
16 |
17 | end)
18 |
19 | function F14B(cmd,arg,mode)
20 |
21 | -- Global Switch Functions
22 | -- Define switch cmds here that are the same in all 3 modes
23 |
24 | if cmd == "4_SW4" then
25 | hub.sendSimCommand("PLT_GEAR_LEVER", arg)
26 | return true
27 | end
28 |
29 | -- Mode 1 Switch Functions
30 |
31 | if mode==0 then
32 |
33 | if cmd == "1_SW1" then
34 | if arg=="0" then
35 | hub.sendSimCommand("PLT_OXY_ON", "DEC") else
36 | hub.sendSimCommand("PLT_OXY_ON", "INC")
37 | end
38 | end
39 |
40 | if cmd == "1_SW2" then
41 |
42 | hub.sendSimCommand("PLT_EJECT_SEAT_SAFE", arg)
43 | return true
44 | end
45 |
46 | end
47 |
48 |
49 | end
50 |
51 | function F16C50(cmd,arg,mode)
52 |
53 | if mode==0 then
54 |
55 | if cmd == "2_SW1" then
56 | hub.sendSimCommand("MAIN_PWR_SW", arg)
57 | return true
58 | end
59 |
60 | if cmd == "2_SW2" then
61 | hub.sendSimCommand("CANOPY_SW", arg)
62 | return true
63 | end
64 |
65 | if cmd == "2_SW3" then
66 | hub.sendSimCommand("CANOPY_HANDLE", arg)
67 | return true
68 |
69 | end
70 |
71 | if cmd == "2_SW4" then
72 | hub.sendSimCommand("JFS_SW", arg)
73 | return true
74 | end
75 | end
76 |
77 |
78 | end
79 |
80 | function P51D(cmd,arg,mode)
81 |
82 | if mode==0 then
83 |
84 | if cmd == "1_SW1" then
85 | hub.sendSimCommand("BAT", arg)
86 | return true
87 | end
88 |
89 | if cmd == "1_SW2" then
90 | hub.sendSimCommand("GEN", arg)
91 | return true
92 | end
93 |
94 | if cmd == "1_SW3" then
95 | hub.sendSimCommand("FUEL_SHUT_OFF_VALVE", arg)
96 | return true
97 | end
98 |
99 | if cmd == "1_SW4" then
100 | hub.sendSimCommand("FUEL_BOOSTER", arg)
101 | return true
102 | end
103 |
104 | if cmd == "1_SW5" then
105 | if arg=="0" then hub.sendSimCommand("CANOPY_HAND_CRANK", "0") end
106 | if arg=="1" then hub.sendSimCommand("CANOPY_HAND_CRANK", "65536") end
107 | return true
108 | end
109 |
110 |
111 |
112 |
113 | if cmd == "4_SW1" then
114 | hub.sendSimCommand("PRIMER", arg)
115 | return true
116 | end
117 |
118 | if cmd == "4_SW2" then
119 | hub.sendSimCommand("STARTER", arg)
120 | return true
121 | end
122 | if cmd == "ROT1-CW" then
123 |
124 | hub.sendSimCommand("IGNITION", "INC")
125 | return true
126 | end
127 |
128 | if cmd == "ROT1-CCW" then
129 |
130 | hub.sendSimCommand("IGNITION", "DEC")
131 | return true
132 | end
133 | if cmd == "ROT2-CW" then
134 |
135 | hub.sendSimCommand("FUEL_SELECTOR_VALVE", "INC")
136 | return true
137 | end
138 |
139 | if cmd == "ROT2-CCW" then
140 |
141 | hub.sendSimCommand("FUEL_SELECTOR_VALVE", "DEC")
142 | return true
143 | end
144 |
145 | if cmd == "ROT3-CW" then
146 |
147 | hub.sendSimCommand("COCKPIT_LIGHTS", "+3200")
148 | return true
149 | end
150 |
151 | if cmd == "ROT3-CCW" then
152 |
153 | hub.sendSimCommand("COCKPIT_LIGHTS", "-3200")
154 | return true
155 | end
156 |
157 | if cmd == "ROT4-CW" then
158 |
159 | hub.sendSimCommand("LEFT_FLUORESCENT_LIGHT", "+3200")
160 | hub.sendSimCommand("RT_FLRES_LT", "+3200")
161 | return true
162 | end
163 |
164 | if cmd == "ROT4-CCW" then
165 |
166 | hub.sendSimCommand("LEFT_FLUORESCENT_LIGHT", "-3200")
167 | hub.sendSimCommand("RT_FLRES_LT", "-3200")
168 | return true
169 | end
170 |
171 | if cmd == "2_SW1" then
172 |
173 | hub.sendSimCommand("MIXTURE_CONTROL", arg)
174 | return true
175 | end
176 |
177 | if cmd == "2_SW2" then
178 |
179 | if arg=="0" then hub.sendSimCommand("TAIL_LTS", "1") end
180 | if arg=="1" then hub.sendSimCommand("TAIL_LTS", "0") end
181 | if arg=="2" then hub.sendSimCommand("TAIL_LTS", "2") end
182 | return true
183 | end
184 | if cmd == "2_SW3" then
185 |
186 | if arg=="0" then hub.sendSimCommand("WING_LTS", "1") end
187 | if arg=="1" then hub.sendSimCommand("WING_LTS", "0") end
188 | if arg=="2" then hub.sendSimCommand("WING_LTS", "2") end
189 | return true
190 | end
191 |
192 | if cmd == "4_SW3" then
193 | hub.sendSimCommand("LANDING_LIGHT", arg)
194 | return true
195 | end
196 |
197 | if cmd == "4_SW4" then
198 | hub.sendSimCommand("LANDING_GEAR_CONTR0L_HANDLE", arg)
199 | return true
200 | end
201 | end
202 |
203 | if mode==1 then
204 |
205 | if cmd == "4_SW3" then
206 | hub.sendSimCommand("LANDING_LIGHT", arg)
207 | return true
208 | end
209 |
210 | if cmd == "4_SW4" then
211 | hub.sendSimCommand("LANDING_GEAR_CONTR0L_HANDLE", arg)
212 | return true
213 | end
214 |
215 | if cmd == "3_SW2" then
216 |
217 | hub.sendSimCommand("CAGE_ROTARY", 0)
218 | return true
219 | end
220 |
221 | if cmd == "ROT1-CW" then
222 |
223 | hub.sendSimCommand("PITCH_ADJUST", "INC")
224 | return true
225 | end
226 |
227 | if cmd == "ROT1-CCW" then
228 |
229 | hub.sendSimCommand("PITCH_ADJUST", "DEC")
230 | return true
231 | end
232 | end
233 |
234 |
235 | if mode==2 then
236 |
237 |
238 | if cmd == "1_SW1" then
239 | hub.sendSimCommand("RKT_DEL_SWITCH", arg)
240 | return true
241 | end
242 |
243 | if cmd == "4_SW3" then
244 | hub.sendSimCommand("LANDING_LIGHT", arg)
245 | return true
246 | end
247 |
248 | if cmd == "4_SW4" then
249 | hub.sendSimCommand("LANDING_GEAR_CONTR0L_HANDLE", arg)
250 | return true
251 | end
252 |
253 | if cmd == "2_SW1" then
254 | hub.sendSimCommand("GUN_CONTROL", arg)
255 | return true
256 | end
257 | if cmd == "2_SW2" then
258 | hub.sendSimCommand("ROCKETS_BOMBS_MODES", arg)
259 | return true
260 | end
261 |
262 | if cmd == "2_SW3" then
263 | hub.sendSimCommand("RKT_REL_MODE", arg)
264 | return true
265 | end
266 |
267 | if cmd == "2_SW4" then
268 | hub.sendSimCommand("LEFT_BOMB_ARM_CHEM", arg)
269 | return true
270 | end
271 |
272 | if cmd == "2_SW5" then
273 | hub.sendSimCommand("RIGHT_BOMB_ARM_CHEM", arg)
274 | return true
275 | end
276 | if cmd == "ROT1-CW" then
277 |
278 | hub.sendSimCommand("GUNSIGHT_FIXED_GYRO", "DEC")
279 | return true
280 | end
281 |
282 | if cmd == "ROT1-CCW" then
283 |
284 | hub.sendSimCommand("GUNSIGHT_FIXED_GYRO", "INC")
285 | return true
286 | end
287 |
288 | if cmd == "3_SW2" then
289 | hub.sendSimCommand("GUNSIGHT_ON_OFF", arg)
290 | return true
291 | end
292 |
293 |
294 | end
295 | end
296 |
297 | function AJS37(cmd,arg,mode)
298 | local navbutton=0
299 | if mode==0 then
300 |
301 | if cmd == "1_SW1" then
302 | hub.sendSimCommand("MAIN_ELECTRIC_POWER", arg)
303 | return true
304 | end
305 |
306 | if cmd == "1_SW2" then
307 | hub.sendSimCommand("LOW_PRES_FUEL_VALVE", arg)
308 | return true
309 | end
310 |
311 | if cmd == "1_SW3" then
312 | hub.sendSimCommand("HIGH_PRES_FUEL_VALVE", arg)
313 | return true
314 | end
315 |
316 | if cmd == "1_SW4" then
317 | hub.sendSimCommand("START_SYSTEM", arg)
318 | return true
319 | end
320 |
321 | if cmd == "1_SW5" then
322 | hub.sendSimCommand("GENERATOR", arg)
323 | return true
324 | end
325 |
326 | if cmd == "2_SW1" then
327 |
328 | hub.sendSimCommand("OXYGEN_LEVER", arg)
329 | return true
330 | end
331 |
332 | if cmd == "2_SW2" then
333 | hub.sendSimCommand("CANOPY_OPEN_CLOSE", arg)
334 | return true
335 | end
336 |
337 | if cmd == "2_SW3" then
338 | hub.sendSimCommand("EJECTION_SEAT_ARM", arg)
339 | return true
340 | end
341 |
342 | if cmd == "2_SW5" then
343 | if arg =="0" then
344 | hub.sendSimCommand("NAVIGATION_LIGHTS", 1)
345 | hub.sendSimCommand("TAXI_LANDING_LIGHTS", 0)
346 | hub.sendSimCommand("FORMATION_LIGHTS", 0)
347 | hub.sendSimCommand("POSITION_LIGHTS", 0)
348 |
349 | end
350 | if arg =="1" then
351 | hub.sendSimCommand("NAVIGATION_LIGHTS", 0)
352 | hub.sendSimCommand("TAXI_LANDING_LIGHTS", 1)
353 | hub.sendSimCommand("FORMATION_LIGHTS", 0)
354 | hub.sendSimCommand("POSITION_LIGHTS", 0)
355 |
356 |
357 |
358 | end
359 | if arg =="2" then
360 | hub.sendSimCommand("NAVIGATION_LIGHTS", 2)
361 | hub.sendSimCommand("TAXI_LANDING_LIGHTS", 1)
362 | hub.sendSimCommand("FORMATION_LIGHTS", 1)
363 | hub.sendSimCommand("POSITION_LIGHTS", 1)
364 |
365 | end
366 | return true
367 | end
368 |
369 | if cmd == "4_SW3" then
370 |
371 | hub.sendSimCommand("REVERSAL", arg)
372 | return true
373 | end
374 |
375 | if cmd == "4_SW4" then
376 | if arg=="1" then hub.sendSimCommand("GEAR_HANDLE", 0) end
377 | if arg=="0" then hub.sendSimCommand("GEAR_HANDLE", 1) end
378 | return true
379 | end
380 |
381 |
382 |
383 |
384 | if cmd == "4_SW1" then
385 | hub.sendSimCommand("MASTER_CAUTION_RESET", arg)
386 | return true
387 | end
388 |
389 | if cmd == "4_SW2" then
390 | hub.sendSimCommand("WARNING_PANEL_TEST", arg)
391 | return true
392 | end
393 |
394 |
395 | if cmd == "ROT1-CW" then
396 |
397 | hub.sendSimCommand("MASTER_MODE_SELECT", "INC")
398 | return true
399 | end
400 |
401 | if cmd == "ROT1-CCW" then
402 |
403 | hub.sendSimCommand("MASTER_MODE_SELECT", "DEC")
404 | return true
405 | end
406 |
407 | if cmd == "ROT2-CW" then
408 |
409 | hub.sendSimCommand("FLOOD_LIGHTS", "+2500")
410 | return true
411 | end
412 |
413 | if cmd == "ROT2-CCW" then
414 |
415 | hub.sendSimCommand("FLOOD_LIGHTS", "-2500")
416 | return true
417 | end
418 |
419 | if cmd == "ROT3-CW" then
420 |
421 | hub.sendSimCommand("PANEL_LIGHTS", "+2500")
422 | return true
423 | end
424 |
425 | if cmd == "ROT3-CCW" then
426 |
427 | hub.sendSimCommand("PANEL_LIGHTS", "-2500")
428 | return true
429 | end
430 |
431 | if cmd == "ROT4-CW" then
432 |
433 | hub.sendSimCommand("INSTRUMENT_LIGHTS", "+2500")
434 | return true
435 | end
436 |
437 | if cmd == "ROT4-CCW" then
438 |
439 | hub.sendSimCommand("INSTRUMENT_LIGHTS", "-2500")
440 | return true
441 | end
442 |
443 |
444 | end
445 |
446 | if mode==1 then
447 |
448 | if cmd == "4_SW1" and arg=="1" then
449 | hub.sendSimCommand("ATTITUDE_HOLD", "TOGGLE")
450 | return true
451 | end
452 |
453 | if cmd == "4_SW2" and arg=="1" then
454 | hub.sendSimCommand("ALTITUDE_HOLD", "TOGGLE")
455 | return true
456 | end
457 |
458 | if cmd == "4_SW3" then
459 |
460 | hub.sendSimCommand("REVERSAL", arg)
461 | return true
462 | end
463 |
464 | if cmd == "4_SW4" then
465 | if arg=="1" then hub.sendSimCommand("GEAR_HANDLE", 0) end
466 | if arg=="0" then hub.sendSimCommand("GEAR_HANDLE", 1) end
467 | return true
468 | end
469 |
470 | if cmd == "ROT1-CW" then
471 |
472 | hub.sendSimCommand("MASTER_MODE_SELECT", "INC")
473 | return true
474 | end
475 |
476 | if cmd == "ROT1-CCW" then
477 |
478 | hub.sendSimCommand("MASTER_MODE_SELECT", "DEC")
479 | return true
480 | end
481 |
482 | if cmd == "ROT2-CW" then
483 |
484 | if navbutton < 12 then navbutton=navbutton+1 end
485 | if navbutton == 1 then hub.sendSimCommand("NAV_SELECT_BTN_LS", 1) hub.sendSimCommand("NAV_SELECT_BTN_LS", 0) end
486 | if navbutton == 2 then hub.sendSimCommand("NAV_SELECT_BTN_B1", 1) hub.sendSimCommand("NAV_SELECT_BTN_B1", 0) end
487 | if navbutton == 3 then hub.sendSimCommand("NAV_SELECT_BTN_B2", 1) hub.sendSimCommand("NAV_SELECT_BTN_B2", 0) end
488 | if navbutton == 4 then hub.sendSimCommand("NAV_SELECT_BTN_B3", 1) hub.sendSimCommand("NAV_SELECT_BTN_B3", 0) end
489 | if navbutton == 5 then hub.sendSimCommand("NAV_SELECT_BTN_B4", 1) hub.sendSimCommand("NAV_SELECT_BTN_B4", 0) end
490 | if navbutton == 6 then hub.sendSimCommand("NAV_SELECT_BTN_B5", 1) hub.sendSimCommand("NAV_SELECT_BTN_B5", 0) end
491 | if navbutton == 7 then hub.sendSimCommand("NAV_SELECT_BTN_B6", 1) hub.sendSimCommand("NAV_SELECT_BTN_B6", 0) end
492 | if navbutton == 8 then hub.sendSimCommand("NAV_SELECT_BTN_B7", 1) hub.sendSimCommand("NAV_SELECT_BTN_B7", 0) end
493 | if navbutton == 9 then hub.sendSimCommand("NAV_SELECT_BTN_B8", 1) hub.sendSimCommand("NAV_SELECT_BTN_B8", 0) end
494 | if navbutton == 10 then hub.sendSimCommand("NAV_SELECT_BTN_B9", 1) hub.sendSimCommand("NAV_SELECT_BTN_B9", 0) end
495 | if navbutton == 11 then hub.sendSimCommand("NAV_SELECT_BTN_BX", 1) hub.sendSimCommand("NAV_SELECT_BTN_BX", 0) end
496 | if navbutton == 12 then hub.sendSimCommand("NAV_SELECT_BTN_L_MAL", 1) hub.sendSimCommand("NAV_SELECT_BTN_L_MAL", 0) end
497 | return true
498 | end
499 |
500 | if cmd == "ROT2-CCW" then
501 |
502 | if navbutton > 0 then navbutton=navbutton-1 end
503 | if navbutton == 1 then hub.sendSimCommand("NAV_SELECT_BTN_LS", 1) hub.sendSimCommand("NAV_SELECT_BTN_LS", 0) end
504 | if navbutton == 2 then hub.sendSimCommand("NAV_SELECT_BTN_B1", 1) hub.sendSimCommand("NAV_SELECT_BTN_B1", 0)end
505 | if navbutton == 3 then hub.sendSimCommand("NAV_SELECT_BTN_B2", 1) hub.sendSimCommand("NAV_SELECT_BTN_B2", 0) end
506 | if navbutton == 4 then hub.sendSimCommand("NAV_SELECT_BTN_B3", 1) hub.sendSimCommand("NAV_SELECT_BTN_B3", 0) end
507 | if navbutton == 5 then hub.sendSimCommand("NAV_SELECT_BTN_B4", 1) hub.sendSimCommand("NAV_SELECT_BTN_B4", 0) end
508 | if navbutton == 6 then hub.sendSimCommand("NAV_SELECT_BTN_B5", 1) hub.sendSimCommand("NAV_SELECT_BTN_B5", 0) end
509 | if navbutton == 7 then hub.sendSimCommand("NAV_SELECT_BTN_B6", 1) hub.sendSimCommand("NAV_SELECT_BTN_B6", 0) end
510 | if navbutton == 8 then hub.sendSimCommand("NAV_SELECT_BTN_B7", 1) hub.sendSimCommand("NAV_SELECT_BTN_B7", 0) end
511 | if navbutton == 9 then hub.sendSimCommand("NAV_SELECT_BTN_B8", 1) hub.sendSimCommand("NAV_SELECT_BTN_B8", 0) end
512 | if navbutton == 10 then hub.sendSimCommand("NAV_SELECT_BTN_B9", 1) hub.sendSimCommand("NAV_SELECT_BTN_B9", 0) end
513 | if navbutton == 11 then hub.sendSimCommand("NAV_SELECT_BTN_BX", 1) hub.sendSimCommand("NAV_SELECT_BTN_BX", 0) end
514 | if navbutton == 12 then hub.sendSimCommand("NAV_SELECT_BTN_L_MAL", 1) hub.sendSimCommand("NAV_SELECT_BTN_L_MAL", 0) end
515 | return true
516 | end
517 |
518 | if cmd == "ROT3-CW" then
519 |
520 | hub.sendSimCommand("TILS_CHANNEL_SELECT", "INC")
521 | return true
522 | end
523 |
524 | if cmd == "ROT3-CCW" then
525 |
526 | hub.sendSimCommand("TILS_CHANNEL_SELECT", "DEC")
527 | return true
528 | end
529 |
530 | end
531 |
532 | if mode==2 then
533 |
534 | if cmd == "1_SW1" then
535 | if arg=="1" then hub.sendSimCommand("WEAPON_REL_MODE", 0) end
536 | if arg=="0" then hub.sendSimCommand("WEAPON_REL_MODE", 1) end
537 |
538 | return true
539 | end
540 |
541 | if cmd == "1_SW2" then
542 | if arg=="1" then hub.sendSimCommand("RB_BK_REL_MODE", 0) end
543 | if arg=="0" then hub.sendSimCommand("RB_BK_REL_MODE", 1) end
544 |
545 | return true
546 | end
547 |
548 | if cmd == "1_SW3" then
549 |
550 | hub.sendSimCommand("HUD_GLASS_POSITION", "TOGGLE")
551 | return true
552 | end
553 |
554 | if cmd == "4_SW3" then
555 |
556 | hub.sendSimCommand("REVERSAL", arg)
557 | return true
558 | end
559 |
560 | if cmd == "4_SW4" then
561 | if arg=="1" then hub.sendSimCommand("GEAR_HANDLE", 0) end
562 | if arg=="0" then hub.sendSimCommand("GEAR_HANDLE", 1) end
563 | return true
564 | end
565 |
566 | if cmd == "4_SW1" and arg=="1" then
567 | hub.sendSimCommand("ATTITUDE_HOLD", "TOGGLE")
568 | return true
569 | end
570 |
571 | if cmd == "4_SW2" and arg=="1" then
572 | hub.sendSimCommand("ALTITUDE_HOLD", "TOGGLE")
573 | return true
574 | end
575 |
576 | if cmd == "4_SW3" then
577 |
578 | hub.sendSimCommand("REVERSAL", arg)
579 | return true
580 | end
581 |
582 | if cmd == "4_SW4" then
583 | if arg=="1" then hub.sendSimCommand("GEAR_HANDLE", 0) end
584 | if arg=="0" then hub.sendSimCommand("GEAR_HANDLE", 1) end
585 | return true
586 | end
587 |
588 | if cmd == "ROT1-CW" then
589 |
590 | hub.sendSimCommand("MASTER_MODE_SELECT", "INC")
591 | return true
592 | end
593 |
594 | if cmd == "ROT1-CCW" then
595 |
596 | hub.sendSimCommand("MASTER_MODE_SELECT", "DEC")
597 | return true
598 | end
599 |
600 | if cmd == "ROT2-CW" then
601 |
602 | hub.sendSimCommand("WEAPON_SELECT", "DEC")
603 | return true
604 | end
605 |
606 | if cmd == "ROT2-CCW" then
607 |
608 | hub.sendSimCommand("WEAPON_SELECT", "INC")
609 | return true
610 | end
611 |
612 | if cmd == "ROT3-CW" then
613 |
614 | hub.sendSimCommand("WEAPON_INTERVAL", "INC")
615 | return true
616 | end
617 |
618 | if cmd == "ROT3-CCW" then
619 |
620 | hub.sendSimCommand("WEAPON_INTERVAL", "DEC")
621 | return true
622 | end
623 |
624 | end
625 |
626 | end
627 |
--------------------------------------------------------------------------------
/firmware/vrtogglebox.ino:
--------------------------------------------------------------------------------
1 | /*
2 | VR Toggle Box
3 | 2/2020 M.Dwyer
4 | */
5 | #define DCSBIOS_DEFAULT_SERIAL
6 |
7 | #include "DcsBios.h"
8 | #include "Adafruit_Keypad.h"
9 | #include
10 | #include
11 |
12 | // Define Rows and Cols for switch matrix
13 | const byte ROWS = 5; // rows
14 | const byte COLS = 5; // columns
15 |
16 | byte mult = 0;
17 |
18 | // Sequence of the buttons on the keypad matrix
19 |
20 | char keys[ROWS][COLS] = {
21 | {4, 3, 2, 9, 10},
22 | {14, 13, 8, 7, 1},
23 | {20, 19, 18, 16, 15},
24 | {12, 11, 17, 6, 5},
25 | {24, 23, 22, 21, 0},
26 |
27 | };
28 |
29 | // Assign pins to matrix
30 | byte rowPins[5] = {21, 20, 19, 18, 15};
31 | byte colPins[5] = {14, 16, 10, 9, 8};
32 |
33 | // Assign pins to rotary encoders
34 | SimpleRotary rotary1(7, 6, 0);
35 | SimpleRotary rotary2(5, 4, 0);
36 | SimpleRotary rotary3(3, 2, 0);
37 | SimpleRotary rotary4(0, 1, 0);
38 |
39 | // Lookup tables used to specify which joystick buttons to toggle based on which button (1-25) is pressed.
40 | // There are 3 lookup tables, each for one of the 3 modes selected by the mode selector switch
41 |
42 | byte lut[3][25][2] = { { {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12},
43 | {13, 14}, {15, 14}, {16, 17}, {18, 17}, {19, 20}, {21, 20}, {22, 23}, {24, 23}, {25, 26}, {27, 26}, {0, 1}, {2, 1},
44 | {28, 255}, {29, 255}, {30, 255}, {31, 255}, {32, 255}, {33, 255}, {34, 35}, {36, 37}
45 | },
46 |
47 | { {44, 45}, {46, 47}, {48, 49}, {50, 51}, {52, 53},
48 | {54, 55}, {56, 55}, {57, 58}, {59, 58}, {60, 61}, {62, 61}, {63, 64}, {65, 64}, {66, 67}, {68, 67}, {0, 1}, {2, 1},
49 | {69, 255}, {70, 255}, {71, 255}, {72, 255}, {73, 255}, {74, 255}, {75, 76}, {77, 78}
50 | },
51 |
52 | { {85, 86}, {87, 88}, {89, 90}, {91, 92}, {93, 94},
53 | {95, 96}, {97, 96}, {98, 99}, {100, 99}, {101, 102}, {103, 102}, {104, 105}, {106, 105}, {107, 108}, {109, 108}, {0, 1}, {2, 1},
54 | {110, 255}, {111, 255}, {112, 255}, {113, 255}, {114, 255}, {115, 255}, {116, 117}, {118, 119}
55 | }
56 |
57 | };
58 |
59 | // Lookup tables for the rotary switches to map to joystick button. One for each mode. Rotary1 uses a hat switch, so the first 2 values in each of the 3 tables are in degrees
60 | byte r_lut[3][8] = {
61 | {0, 45, 38, 39, 40, 41, 42, 43},
62 | {90, 135, 79, 80, 81, 82, 83, 84},
63 | {180, 225, 120, 121, 122, 123, 124, 125},
64 | };
65 |
66 | // Define names of switches for DCS bios and the value that is sent for on and off positions
67 | const char *rots[] =
68 | {
69 | "ROT1-CCW", "ROT1-CW",
70 | "ROT2-CCW", "ROT2-CW",
71 | "ROT3-CCW", "ROT3-CW",
72 | "ROT4-CCW", "ROT4-CW",
73 | };
74 |
75 | const char *cmds[][3] =
76 | {
77 | { "1_SW1", "1", "0" }, { "1_SW2", "1", "0" }, { "1_SW3", "1", "0" }, { "1_SW4", "1", "0" }, { "1_SW5", "1", "0" },
78 |
79 | { "2_SW1", "2", "1" }, { "2_SW1", "0", "1" }, { "2_SW2", "2", "1" }, { "2_SW2", "0", "1" },
80 | { "2_SW3", "2", "1" }, { "2_SW3", "0", "1" }, { "2_SW4", "2", "1" }, { "2_SW4", "0", "1" },
81 | { "2_SW5", "2", "1" }, { "2_SW5", "0", "1" }, { "3_SW1", "2", "1" }, { "3_SW1", "0", "1" },
82 |
83 | { "3_SW2", "1", "0" }, { "3_SW3", "1", "0" }, { "3_SW4", "1", "0" }, { "3_SW5", "1", "0" },
84 |
85 | { "4_SW1", "1", "0" }, { "4_SW2", "1", "0" }, { "4_SW3", "1", "0" }, { "4_SW4", "1", "0" },
86 | };
87 |
88 |
89 |
90 | //initialize an instance of class NewKeypad
91 | Adafruit_Keypad customKeypad = Adafruit_Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);
92 |
93 | // Initialize instance of Joystick
94 | Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
95 | JOYSTICK_TYPE_JOYSTICK, 128, 2,
96 | false, false, false, false, false, false,
97 | false, false, false, false, false);
98 |
99 | void setup() {
100 |
101 | DcsBios::setup();
102 | customKeypad.begin();
103 | Joystick.begin();
104 | }
105 |
106 | void loop() {
107 |
108 | DcsBios::loop();
109 | customKeypad.tick();
110 |
111 | switch (rotary1.rotate())
112 | {
113 | case 1:
114 | sendDcsBiosMessage(rots[0], "1");
115 | Joystick.setHatSwitch(0, r_lut[mult][0]); delay(50); Joystick.setHatSwitch(2, -1);
116 | break;
117 | case 2: sendDcsBiosMessage(rots[1], "1");
118 | Joystick.setHatSwitch(0, r_lut[mult][1]); delay(50); Joystick.setHatSwitch(0, -1);
119 | break;
120 | }
121 |
122 | switch (rotary2.rotate())
123 | {
124 | case 1:
125 | sendDcsBiosMessage(rots[2], "1");
126 | Joystick.setButton(r_lut[mult][2], 1); delay(50); Joystick.setButton(r_lut[mult][2], 0);
127 | break;
128 | case 2:
129 | sendDcsBiosMessage(rots[3], "1");
130 | Joystick.setButton(r_lut[mult][3], 1); delay(50); Joystick.setButton(r_lut[mult][3], 0);
131 | break;
132 | }
133 |
134 | switch (rotary3.rotate())
135 | {
136 | case 1:
137 | sendDcsBiosMessage(rots[4], "1");
138 | Joystick.setButton(r_lut[mult][4], 1); delay(50); Joystick.setButton(r_lut[mult][4], 0);
139 | break;
140 | case 2:
141 | sendDcsBiosMessage(rots[5], "1");
142 | Joystick.setButton(r_lut[mult][5], 1); delay(50); Joystick.setButton(r_lut[mult][5], 0);
143 | break;
144 | }
145 |
146 | switch (rotary4.rotate())
147 | {
148 | case 1: sendDcsBiosMessage(rots[6], "1");
149 | Joystick.setButton(r_lut[mult][6], 1); delay(50); Joystick.setButton(r_lut[mult][6], 0);
150 | break;
151 | case 2: sendDcsBiosMessage(rots[7], "1");
152 | Joystick.setButton(r_lut[mult][7], 1); delay(50); Joystick.setButton(r_lut[mult][7], 0);
153 | break;
154 | }
155 |
156 | while (customKeypad.available()) {
157 |
158 | keypadEvent e = customKeypad.read();
159 |
160 | switch (e.bit.EVENT)
161 | {
162 | case KEY_JUST_PRESSED:
163 | sendDcsBiosMessage(cmds[e.bit.KEY][0], cmds[e.bit.KEY][1]);
164 | Joystick.setButton(lut[mult][e.bit.KEY][0], 1);
165 | Joystick.setButton(lut[mult][e.bit.KEY][1], 0);
166 | break;
167 | case KEY_JUST_RELEASED:
168 | sendDcsBiosMessage(cmds[e.bit.KEY][0], cmds[e.bit.KEY][2]);
169 | Joystick.setButton(lut[mult][e.bit.KEY][0], 0);
170 | Joystick.setButton(lut[mult][e.bit.KEY][1], 1);
171 | break;
172 | }
173 |
174 | if (e.bit.KEY == 16 && e.bit.EVENT == KEY_JUST_PRESSED) mult = 0;
175 | if (e.bit.KEY == 15 && e.bit.EVENT == KEY_JUST_PRESSED) mult = 2;
176 | if ((e.bit.KEY == 15 || e.bit.KEY == 16) && e.bit.EVENT == KEY_JUST_RELEASED) mult = 1;
177 |
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/hardware/Base.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdlogic/VR_Toggle_Box/4adc683c77d60d3992c2ad25b358c0f67d723889/hardware/Base.stl
--------------------------------------------------------------------------------
/hardware/TopPlate.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdlogic/VR_Toggle_Box/4adc683c77d60d3992c2ad25b358c0f67d723889/hardware/TopPlate.stl
--------------------------------------------------------------------------------
/hardware/VR_Toggle_Box_Schematic.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdlogic/VR_Toggle_Box/4adc683c77d60d3992c2ad25b358c0f67d723889/hardware/VR_Toggle_Box_Schematic.pdf
--------------------------------------------------------------------------------
/images/back.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdlogic/VR_Toggle_Box/4adc683c77d60d3992c2ad25b358c0f67d723889/images/back.jpg
--------------------------------------------------------------------------------
/images/front-small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdlogic/VR_Toggle_Box/4adc683c77d60d3992c2ad25b358c0f67d723889/images/front-small.jpg
--------------------------------------------------------------------------------
/images/front.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdlogic/VR_Toggle_Box/4adc683c77d60d3992c2ad25b358c0f67d723889/images/front.jpg
--------------------------------------------------------------------------------