├── .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 --------------------------------------------------------------------------------