{"id":22918282,"url":"https://github.com/candera/falconpanel","last_synced_at":"2025-05-12T17:46:54.256Z","repository":{"id":26980033,"uuid":"30443706","full_name":"candera/falconpanel","owner":"candera","description":"Arduino program for turning physical switches, knobs, and buttons into a DirectX game controller.","archived":false,"fork":false,"pushed_at":"2015-08-14T22:46:20.000Z","size":616,"stargazers_count":6,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-12-09T09:51:21.061Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/candera.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-07T03:30:24.000Z","updated_at":"2022-01-24T10:45:11.000Z","dependencies_parsed_at":"2022-08-31T21:45:15.919Z","dependency_job_id":null,"html_url":"https://github.com/candera/falconpanel","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candera%2Ffalconpanel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candera%2Ffalconpanel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candera%2Ffalconpanel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candera%2Ffalconpanel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/candera","download_url":"https://codeload.github.com/candera/falconpanel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229673762,"owners_count":18105433,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-14T06:29:49.359Z","updated_at":"2024-12-14T06:29:49.932Z","avatar_url":"https://github.com/candera.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"* Falconpanel\n\nAn Arduino program for surfacing switches and knobs as a USB game\ncontroller.\n\n** Show Me\n\nHere's a video demonstrating the features of Falconpanel:\n\n[[https://www.youtube.com/watch?v=VwVLXjgCeJg]]\n\n** Status\n\nAlpha - all code subject to change. May cause tingling in the extremities.\n\n** What's with the name?\n\nI developed this in the process of building a control panel for my\nfavorite flight simulator [[http://www.bmsforum.org/forum/content.php][Falcon BMS]]. However, since this surfaces the\nArduino as a regular USB game controller, there's really no reason\nthat it has to be used just with Falcon; it should work with any\nprogram that's expecting DirectX buttons or axes.\n\n** My Hardware\n\n[[panel-800.jpg]]\n\nThe buttons and switches are at the left edge. The thing in the middle\nis a [[http://gaming.logitech.com/en-us/product/g13-advanced-gameboard][Logitech G13]] that I use for the ICP/DED, and has nothing to do\nwith Falconpanel, other than sitting on the same stand. The point of\nthe picture is to show how I was able to make a pretty simple panel by\ndrilling some holes in some 1/4\" plywood and mounting various things\nin it. Here's a shot of the back:\n\n[[panel-back-800.jpg]]\n\nThe hardware I used:\n\n- An [[http://www.adafruit.com/products/849][Arduino Leonardo]]\n- An [[http://www.adafruit.com/products/192][Adafruit Protoshield]]\n- A [[http://www.adafruit.com/products/64][tiny breadboard]]\n- [[http://www.amazon.com/gp/product/B0094GRZPE/ref%3Doh_aui_detailpage_o01_s00?ie%3DUTF8\u0026psc%3D1][Momentary switches]]\n- [[http://www.amazon.com/gp/product/B008ICKO30/ref%3Doh_aui_detailpage_o05_s00?ie%3DUTF8\u0026psc%3D1][Two-position switches]]\n- [[http://www.amazon.com/gp/product/B008ICEJM2/ref%3Doh_aui_detailpage_o07_s01?ie%3DUTF8\u0026psc%3D1][Three-position switches]]\n- [[http://www.amazon.com/gp/product/B009QFU9H4/ref%3Doh_aui_detailpage_o06_s00?ie%3DUTF8\u0026psc%3D1][10K potentiometer]]\n- [[http://www.ebay.com/itm/251785644683][A 74LS151 3-to-8 multiplexer]]\n\nBut you can use any Arduino, you don't need the protoshield nor the\nbreadboard, and you can use whatever switches, knobs, and buttons you\nlike. I used these.\n\n** The software\n*** Prerequisities\n\nIf you want to compile and build exactly what I did (unlikely), you'll\nneed to install the Arduino tools and then - as the key part of all\nthis - [[https://github.com/NicoHood/HID][The HID Project libraries]]. NicoHood has done all the hard work\nhere, and I will refer you to his documentation for installation.\n\n*** Falconpanel features\n\nThe key idea in Falconpanel is that of *Components*. These map\nphysical controls and other electronics to USB buttons and axes. You\nwill need to map these to your particular setup by modifying the code\nin Falconpanel that looks like this:\n\n#+begin_src cpp\n  // I've got a 74LS151 3-to-8 mux with its address pins connected to\n  // Arduino pins 2-4, and with its output pin connected to Arduino pin\n  // 5. We have to declare this outside the components array below\n  // because we reference it in there.\n  IC74LS151* mux1 = new IC74LS151(new DigitalOutputPin(2),\n                                  new DigitalOutputPin(3),\n                                  new DigitalOutputPin(4),\n                                  new DigitalInputPullupPin(5));\n\n  // Keep track of the button number so I don't have to keep looking at\n  // what I used.\n  int dxButton = 1;\n\n  Component* components[] =   {\n    // List the mux here so its setup gets called\n    mux1,\n    // FACK\n    new PushButton(mux1-\u003einput(0), new DxButton(dxButton++)),\n    // Laser Arm\n    new OnOffSwitch(mux1-\u003einput(2),\n                    new MomentaryButton(new DxButton(dxButton++)),\n                    new MomentaryButton(new DxButton(dxButton++))),\n    // Master Arm\n    new OnOffOnSwitch(mux1-\u003einput(3),\n                      mux1-\u003einput(4),\n                      new MomentaryButton(new DxButton(dxButton++)),\n                      new MomentaryButton(new DxButton(dxButton++)),\n                      new MomentaryButton(new DxButton(dxButton++))),\n    // HMCS\n    new SwitchingRotary(new AnalogInputPin(0),\n                        DxAxis::XRotation(),\n                        new MomentaryButton(new DxButton(dxButton++)),\n                        new MomentaryButton(new DxButton(dxButton++)),\n                        0.05)\n  };\n#+end_src\n\nFalconpanel currently supports the following types of component\n\n**** PushButton\n\nThe simplest of the controls, this maps a Arduino input pin directly\nto a DirectX button. Intended to be connect to a momentary, pushbutton\nswitch.\nConstructor:\n\n#+begin_src cpp\nPushButton(DigitalInput* in, DxButton* dxButton)\n#+end_src\n\nWatches the digital input =in= and maps it to DirectX button\n=dxButton= (DirectX buttons are numbered from 1, with a max of 32).\nThe DirectX button stays pressed for as long as the physical button\ndoes.\n\n**** OnOffSwitch\n\nMaps a two-position switch to DirectX buttons for its *up* and *down*\nstates. The DirectX button presses are momentary, even though the\nswitch is not.\n\nConstructor:\n\n#+begin_src cpp\nOnOffSwitch(DigitalInput* in, DxButton* dxButtonUp, DxButton* dxButtonDown, int duration)\n#+end_src\n\nWatches the digital input =in=, and when it changes state,\npresses DirectX button =dxButtonUp= or =dxButtonDown= (DirectX buttons\nare numbered from 1, with a max of 32) depending on whether the switch\nhas been flipped up or down. The button stays pressed for =duration=\n\"ticks\", or until the switch state is changed. A tick is currently\nabout 150ms.\n\nNote that one switch therefore generates two different DirectX button\npresses.\n\n**** OnOffOnSwitch\n\nMaps a three-position switch to DirectX buttons for its *up*,\n*middle*, and *down* states. The DirectX button presses are momentary,\neven though the switch is not.\n\nConstructor:\n\n#+begin_src cpp\n  OnOffOnSwitch(DigitalInput* inUp, DigitalInput* inDown,\n                DxButton* dxButtonUp, DxButton* dxButtonMiddle, DxButton* dxButtonDown,\n                int duration)\n#+end_src\n\nWatches the digital inputs =inUp= and =inDown=, which should be\nconnected to the up and down leads of the physical switch, and when\nthe switch changes state, presses DirectX button =dxButtonUp=,\n=dxButtonMiddle=, or =dxButtonDown= (DirectX buttons are numbered from\n1, with a max of 32) depending on which position the switch has been\nflipped to. The button stays pressed for =duration= \"ticks\", or until\nthe switch state is changed. A tick is currently about 150ms.\n\nNote that one switch therefore generates three different DirectX button\npresses.\n\n**** SwitchingRotary\n\nMaps a potentiometer to a DirectX axis and two buttons - one for\n\"switching on\" and one for \"switching off\". Note that there is no need\nto use a potentiometer with an actual switch - on/off state is tracked\nby watching whether the pot is below a configurable threshold.\n\nConstructor:\n\n#+begin_src cpp\n  SwitchingRotary(AnalogInput* in,\n                  DxAxis* dxAxis, DxButton* dxButtonOn, DxButton* dxButtonOff,\n                  int duration, float threshold)\n#+end_src\n\nWatches the analog input =in=, which should be connected to the middle\nlead of a potentiometer, ideally in the 10K Ohm range. When the pot is\nbelow =threshold=, reports the specified DirectX axis as being at its\nminimum value. When above =threshold=, reports values scaled between\nthe minimum and maximum DirectX axis values.\n\nWhen the pot passes through the threshold value in the increasing\ndirection, sends a momentary press on DirectX button =dxButtonOn=.\nWhen the pot passes through the threshold value in the decreasing\ndirection, sends a momentary press on DirectX button =dxButtonOff=.\nMomentary presses are of duration =duration= ticks, where a tick is\ncurrently about 150ms.\n\nNote that one pot therefore generates two different DirectX button\npresses and one DirectX axis.\n\n**** PulseRotary\n*DEPRECATED* If you're looking at this, it's much more likely you want\nto use =RotaryEncoder=.\n\nMaps a potentiometer to two buttons - one for motion in the direction\nof increasing input values (the \"up\" direction), and one for motion in\nthe opposite direction (the \"down\" direction).\n\nConstructor:\n\n#+begin_src cpp\n  PulseRotary(AnalogInput* in, DxButton* dxButtonUp, DxButton* dxButtonDown, int divisions);\n#+end_src\n\nWatches the analog input =in=, which should be connected to the middle\nlead of a potentiometer, ideally in the 10K Ohm range. When the pot\nmoves more than 1/divisions of its range, the up or down button is\ntriggered, depending on the direction of motion. The position the pot\nis in when this threshold is crossed is the new \"home\" position for\ndetermining the next transition, so =divisions= does not result in a\nstrict division of the pot range.\n\nThis control does take into account the possibility of the pot\n\"wrapping around\", as can happen with a pot that was made or modified\nto have full 360 degree rotation, and will correctly calculate the\ndirection.\n\nThe up or down button is pressed each time the threshold is crossed in\nthat direction, at which time the opposite button is released. It\nmaintains its own internal buffer of contiguous presses in one\ndirection, so it probably does not make sense to use this with buttons\nwrapped in a =MomentaryButton=.\n\n**** RotaryEncoder\n\nMaps a rotary encoder onto two buttons: one for one direction, one for\nthe other.\n\nConstructor:\n\n#+begin_src cpp\n  RotaryEncoder(DigitalInput* in1, DigitalInput in2,\n                Button* buttonForward, Button* buttonBackward,\n                int queueLimit);\n#+end_src\n\nWatches digital inputs =in1= and =in2=, which should be connected to\nthe non-ground leads of a [[https://en.wikipedia.org/wiki/Rotary_encoder][rotary encoder]]. When the encoder is rotated\nin each direction, a DirectX button press/release for \"forward\" or\n\"backward\" is sent for each \"click\" of the encoder. Presses are\nenqueued (up to =queueLimit=), so if rotation of the physical control\ncan get ahead of the DirectX presses. =queueLimit= should never be set\nlower than one.\n\nIf your notion of forward and backward is the opposite of the DirectX\nevents you're seeing, just reverse the order of the digital inputs.\n\nIt probably doesn't make any sense to use this with a\n=MomentaryButton=, as =RotaryEncoder= is already inherently momentary.\n\n**** IC74LS151\n\nRepresents a 74LS151 3-to-8 mulitplexer (mux). These can be used to\nmultiplex three input pins and one output pin on the Arduino to 8\ninput pins on the mux, effectively doubling the number of input wires\nyou can have connected to a single Arduino.\n\n=IC74LS151= is a component mainly so it can be listed in the\ncomponents array and have its setup function called; the primary use\nof it is via its =input= method, which is an adapter that bridges from\nan IC54LS151 mux instance to anything that's expecting a digital\ninput, like the =PushButton= class.\n\nConstructor:\n\n#+begin_src cpp\nIC74LS151(DigitalOutput* dout0, DigitalOutput* dout1, DigitalOutput* dout2, DigitalInput* din)\n#+end_src\n\nSets up a 74LS151 multiplexer with its address lines driven by =dout0=\n(LSB), =dout1=, and =dout2= (MSB). Input will arrive on =din=. Use the\n=input= method to connect the input pins of the mux to other controls,\nas in this example:\n\n#+begin_src cpp\n  // I've got a 74LS151 3-to-8 mux with its address pins connected to\n  // Arduino pins 2-4, and with its output pin connected to Arduino pin\n  // 5. We have to declare this outside the components array below\n  // because we reference it in there.\n  IC74LS151* mux1 = new IC74LS151(new DigitalOutputPin(2),\n                                  new DigitalOutputPin(3),\n                                  new DigitalOutputPin(4),\n                                  new DigitalInputPullupPin(5));\n\n  // A simple example with only one control connect to the mux.\n  // Ordinarily you would connect several, since saving Arduino pins is\n  // the point of the mux.\n  Component* components[] =  {\n    // List the mux here so its setup gets called\n    mux1,\n    // Connect an On/Off switch to the mux input 3 (D3 on the data sheet)\n    new OnOffSwitch(mux1-\u003einput(3),\n                    new DxButton(1),\n                    new DxButton(2),\n                    3)\n  };\n#+end_src\n\nTo learn more about the mux, read the [[http://pdf.datasheetcatalog.com/datasheet2/2/05zhla9si2dxjf61z5qx4spz7uyy.pdf][datasheet]].\n\n\nEach of these components can be hooked up to either *Buttons*, *Axes*\nor both, depending on the component. Currently, an axis is always a\ndirect connect to a DirectX axis. Axis values are represented as\nfloating point numbers in the range 0.0 to 1.0, inclusive.\n\nButtons outputs of components, however, can either be a direct connect\nto a DirectX button on the virtual gamepad, or can go through a\n=MomentaryButton= adapter. =MomentaryButton= turns a button press into\na press-and-release, where the release happens automatically a\nconfigurable number of *ticks* later. A tick is currently about 150ms,\nand the default delay is three ticks. This approach is useful in\nhaving the button presses coming out of a component like an\n=OnOffSwitch= indicate changes in state rather than switch position.\nThis can help with mapping in a game, where holding buttons down may\ncause problems.\n\n** Feedback\n\nFeel free to drop an issue here on the project or contact me at\ncandera@wangdera.com if you have questions or feature requests. Hope\nyou find it useful!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcandera%2Ffalconpanel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcandera%2Ffalconpanel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcandera%2Ffalconpanel/lists"}