diff --git a/Joystick.cpp b/Joystick.cpp index 5b4c99a..b7707e9 100644 --- a/Joystick.cpp +++ b/Joystick.cpp @@ -1,6 +1,9 @@ #include "Joystick.h" +#include #include +using namespace admux; + bool operator ==(JoyReport a, JoyReport b){ for (uint8_t i=0; i < JOYSTICK_NUM_AXES; i++) { if (a.axis[i] != b.axis[i]) return false; @@ -19,11 +22,16 @@ Joystick::Joystick(bool debug) { _debug = debug; _virtual_buttons = 0; _num_axes = 0; + _num_mux = 0; + _num_buttons = 0; _have_pulsed_button = false; - + for (uint8_t i=0; i < JOYSTICK_NUM_AXES; i++) { _joyReport.axis[i] = 0; } + for (uint8_t i=0; i < JOYSTICK_NUM_BYTES; i++) { + _joyReport.button[i] = 0; + } } void Joystick::Init() { @@ -32,6 +40,17 @@ void Joystick::Init() { } void Joystick::AddButton(uint8_t pin, ButtonType type, bool pullup) { + _BuildButton(pin, type, pullup); +} + +void Joystick::AddMuxButton(uint8_t pin, uint8_t mux_id, uint8_t mux_channel, ButtonType type, bool pullup) { + Button *button = _BuildButton(pin, type, pullup); + button->mux = true; + button->mux_id = mux_id; + button->mux_channel = mux_channel; +} + +Button* Joystick::_BuildButton(uint8_t pin, ButtonType type, bool pullup) { uint8_t mode; if (pullup) mode = INPUT_PULLUP; else mode = INPUT; @@ -57,6 +76,8 @@ void Joystick::AddButton(uint8_t pin, ButtonType type, bool pullup) { _virtual_buttons += increment; if (type & _BUTTON_PULSED_TYPES) _have_pulsed_button = true; + + return &button; } void Joystick::AddAxis(uint8_t pin) { @@ -64,6 +85,16 @@ void Joystick::AddAxis(uint8_t pin) { _num_axes++; } +uint8_t Joystick::AddMux(uint8_t signal_pin, Pinset addr_pins, bool pullup) { + uint8_t mux_id = _num_mux; + uint8_t mode = INPUT_PULLUP; + if (!pullup) mode = INPUT; + Mux mux(Pin(signal_pin, mode, PinType::Digital), addr_pins); + _mux[mux_id] = &mux; + _num_mux++; + return mux_id; +} + void Joystick::Update() { JoyReport oldReport = _joyReport; @@ -138,6 +169,9 @@ void Joystick::Write() { void Joystick::_UpdateButton(uint8_t button_num) { Button *button = &_buttons[button_num]; + if (button->mux) { + _mux[button->mux_id]->channel(button->mux_channel); + } bool changed = button->bouncer.update(); if (!changed) return; diff --git a/Joystick.h b/Joystick.h index 9802925..24008ee 100644 --- a/Joystick.h +++ b/Joystick.h @@ -1,9 +1,12 @@ #ifndef _JOYSTICK_H_ #define _JOYSTICK_H_ +#include #include #include +using namespace admux; + // If you're using the arduino-big-joystick firmware, these numbers can't be // changed. If you're writing your own Report Descriptor, tune these to match by // defining them *before* you include Joystick.h. @@ -15,6 +18,10 @@ #endif #define JOYSTICK_NUM_BYTES (JOYSTICK_NUM_BUTTONS+7)/8 +#ifndef MAX_MUX +#define MAX_MUX 3 +#endif + enum ButtonType { BUTTON_PASSTHRU = 0x1, // always use the (debounced) absolute state of the input BUTTON_PULSED = 0x2, // on button press, send an on signal followed immediately by an off signal. @@ -35,6 +42,11 @@ struct Button { uint8_t vbutton1; // only used by BUTTON_PULSED_DOUBLE_ACTION_SPLIT bool pressed = false; // only used by BUTTON_LATCHED_MOMENTARY bool inverted = false; // if true, send button press on release and vice versa. + + // todo: this should probably be abstracted out from this struct... + bool mux; + uint8_t mux_id; + uint8_t mux_channel; }; bool operator ==(JoyReport a, JoyReport b); @@ -45,23 +57,36 @@ class Joystick { Joystick(bool debug=false); void Init(); void Update(); - + + // Add a button to the joystick. + // Button types are documented in the ButtonType enum. + // If `pullup` is true, your button should connect the pin to ground. (also be sure that your board supports INPUT_PULLUP on that pin) + // If `pullup` is false, your button should connect the pin to VCC. + // Mux parameters are ignored unless `mux` is true. void AddButton(uint8_t pin, ButtonType type, bool pullup=true); - void AddAxis(uint8_t pin); // Axes don't actually work yet! + void AddMuxButton(uint8_t pin, uint8_t mux_id, uint8_t mux_channel, ButtonType type, bool pullup=true); + + // Add an analog axis to the joystick. THIS METHOD IS NOT CURRENTLY TESTED OR SUPPORTED. It might work, but probably not. + void AddAxis(uint8_t pin); + + // Add control for a multiplexer. To add a button that's connected via the multiplexer, + // pass the mux's signal pin as the pin parameter to AddButton(), along with the mux* parameters. + // mux_id is the array index of the mux (in the order you added them via AddMux()) + uint8_t AddMux(uint8_t signal_pin, Pinset addr_pins, bool pullup=true); - // Public access to these functions is deprecated and they may become private in a future - // version. Prefer the above API instead. + private: void SetAxis(uint8_t axis, int16_t value); void PressButton(uint8_t button); void ReleaseButton(uint8_t button); void ReleaseAllButtons(); void Write(); - private: void _ReleasePulsedButtons(); void _UpdateButton(uint8_t index); void _UpdateAxis(uint8_t index); - + + Button* _BuildButton(uint8_t pin, ButtonType type, bool pullup); + Button _buttons[JOYSTICK_NUM_BUTTONS]; uint8_t _num_buttons; uint8_t _virtual_buttons; // a single user-defined button can have multiple virtual buttons. @@ -72,6 +97,9 @@ class Joystick { JoyReport _joyReport; bool _debug; + + Mux *_mux[MAX_MUX]; + uint8_t _num_mux; }; // Internal use only. diff --git a/Readme.md b/Readme.md index 3d12ea6..ae8e941 100644 --- a/Readme.md +++ b/Readme.md @@ -2,17 +2,28 @@ This is a library that builds and sends USB HID Joystick reports, making it easy to build USB Joysticks with Arduino. ## Dependencies -* Your Arduino's USB communication chip must be programmed with the arduino-big-joystick firmware (or similar). See for more info. +* After uploading your sketch, your Arduino's USB communication chip will need to be programmed with the arduino-big-joystick firmware (or similar). See for more info. * The Bounce2 library, available at . +* The Analog-Digital Multiplexers library, available at . ## Installation -1. Put the arduino-joystick directory into your Arduino libraries directory. -2. Make sure Bounce2 is installed! +1. Install dependencies as above, or if using `arduino-cli`, with: + arduino-cli lib install Bounce2 + arduino-cli lib install "Analog-Digital Multiplexers" +2. Put the arduino-joystick directory into your Arduino libraries directory. ## Usage 1. In your arduino sketch, add the includes: - #include #include 2. Create a Joystick object: - Joystick joystick; -3. Add buttons and axes in setup() with Joystick::AddButton(), and call Joystick::Update() in loop(). That's it! + Joystick joystick(true); +3. Add buttons and axes in setup() with Joystick::AddButton(), and call Joystick::Update() in loop(). +4. Upload the sketch, connect to the serial monitor (at 115200 baud) and test the buttons. +5. Set the joystick's `debug` parameter to `false` and re-upload the sketch. The arduino will NOT work in joystick mode with debug set to `true`! +6. Flash the `arduino-big-joystick` firmware onto the USB Controller. + +### Advanced usage: multiplexers + +If you need more buttons than your board has pins, multiplexers (such as [this one](https://www.sparkfun.com/products/13906) and [this one](https://www.sparkfun.com/products/9056)) are a popular solution. This library supports multiplexers! To use them, you need to do some extra work. + +Call the `AddMux()` method and pass it the pins the multiplexer is connected to. You'll need to `#include ` to access `admux::Pinset`. `AddMux()` will return a `mux_id` for subsequently passing to `AddButton()`. The `pin` parameter for all multiplexed buttons should be the same as the multiplexer's `signal_pin`. diff --git a/examples/type_test/Makefile b/examples/basic/Makefile similarity index 100% rename from examples/type_test/Makefile rename to examples/basic/Makefile diff --git a/examples/type_test/type_test.ino b/examples/basic/basic.ino similarity index 52% rename from examples/type_test/type_test.ino rename to examples/basic/basic.ino index 03f6e35..eacc04e 100644 --- a/examples/type_test/type_test.ino +++ b/examples/basic/basic.ino @@ -2,21 +2,33 @@ // In this example, we have 3 toggle switches and 2 momentary pushbuttons. // Each button is configured in a different one of the available behaviors. -#include #include bool debug = false; Joystick joystick(debug); void setup() { + // momentary pushbutton #1 joystick.AddButton(9, BUTTON_PASSTHRU); + + // momentary pushbutton #2 - this one will toggle on or off each time it is pushed joystick.AddButton(10, BUTTON_LATCHED_MOMENTARY); + + // a toggle switch that acts like a momentary button - every time it's toggled 'on' it briefly sends + // a keypresss joystick.AddButton(11, BUTTON_PULSED); + + // like the above, but it sends its button press when toggled on *and* when toggled off joystick.AddButton(12, BUTTON_PULSED_DOUBLE_ACTION); + + // again, similar to the above, but it sends two *different* button presses - 'on' will be one button, 'off' another. joystick.AddButton(13, BUTTON_PULSED_DOUBLE_ACTION_SPLIT); + + // start up serial communication joystick.Init(); } void loop() { + // check all the button states and send any changes joystick.Update(); } diff --git a/examples/multiplexer/Makefile b/examples/multiplexer/Makefile new file mode 100644 index 0000000..d1058d4 --- /dev/null +++ b/examples/multiplexer/Makefile @@ -0,0 +1,8 @@ +TARGET_BOARD=arduino:avr:uno +COM_PORT=/dev/ttyACM0 + +build: + arduino-cli compile -b ${TARGET_BOARD} + +upload: + arduino-cli upload -b ${TARGET_BOARD} -p ${COM_PORT} diff --git a/examples/multiplexer/multiplexer.ino b/examples/multiplexer/multiplexer.ino new file mode 100644 index 0000000..cdac23f --- /dev/null +++ b/examples/multiplexer/multiplexer.ino @@ -0,0 +1,50 @@ +// An example sketch using the joystick library, demonstrating multiplexer support +// In this example, we have 21 buttons we want to attach. +// Unfortunately, the Arduino Uno only has 17 pins we can use! +// So we've added an 8-channel multiplexer, connected to pins A1-A5. +// +// Note: this may not be the best approach for this many simple buttons. Using an input matrix +// would require as few as 10 inputs in this scenario, for example. This is just a demo :) + +#include +#include + +bool debug = false; +Joystick joystick(debug); + +void setup() { + // All of our digital pins and A0 are taken up with pushbuttons + joystick.AddButton(2, BUTTON_PASSTHRU); + joystick.AddButton(3, BUTTON_PASSTHRU); + joystick.AddButton(4, BUTTON_PASSTHRU); + joystick.AddButton(5, BUTTON_PASSTHRU); + joystick.AddButton(6, BUTTON_PASSTHRU); + joystick.AddButton(7, BUTTON_PASSTHRU); + joystick.AddButton(8, BUTTON_PASSTHRU); + joystick.AddButton(9, BUTTON_PASSTHRU); + joystick.AddButton(10, BUTTON_PASSTHRU); + joystick.AddButton(11, BUTTON_PASSTHRU); + joystick.AddButton(12, BUTTON_PASSTHRU); + joystick.AddButton(13, BUTTON_PASSTHRU); + joystick.AddButton(A0, BUTTON_PASSTHRU); + + // to get more room for our inputs, we add an 8-bit multiplexer + uint8_t mux = joystick.AddMux(A1, admux::Pinset(A2, A3, A4, A5), 4); + + // now we can add the rest of the buttons + joystick.AddMuxButton(A1, mux, 0, BUTTON_PASSTHRU); + joystick.AddMuxButton(A1, mux, 1, BUTTON_PASSTHRU); + joystick.AddMuxButton(A1, mux, 2, BUTTON_PASSTHRU); + joystick.AddMuxButton(A1, mux, 3, BUTTON_PASSTHRU); + joystick.AddMuxButton(A1, mux, 4, BUTTON_PASSTHRU); + joystick.AddMuxButton(A1, mux, 5, BUTTON_PASSTHRU); + joystick.AddMuxButton(A1, mux, 6, BUTTON_PASSTHRU); + joystick.AddMuxButton(A1, mux, 7, BUTTON_PASSTHRU); + + joystick.Init(); +} + +void loop() { + // check all the button states and send any changes + joystick.Update(); +} diff --git a/examples/complex_switch_panel/complex_switch_panel.ino b/examples/obsolete/complex_switch_panel/complex_switch_panel.ino similarity index 100% rename from examples/complex_switch_panel/complex_switch_panel.ino rename to examples/obsolete/complex_switch_panel/complex_switch_panel.ino diff --git a/examples/switch_panel/switch_panel.ino b/examples/obsolete/switch_panel/switch_panel.ino similarity index 100% rename from examples/switch_panel/switch_panel.ino rename to examples/obsolete/switch_panel/switch_panel.ino diff --git a/examples/switch_panel_improved/switch_panel_improved.ino b/examples/obsolete/switch_panel_improved/switch_panel_improved.ino similarity index 100% rename from examples/switch_panel_improved/switch_panel_improved.ino rename to examples/obsolete/switch_panel_improved/switch_panel_improved.ino