From d6e0648abfd3368436775df364285b6287dc2e42 Mon Sep 17 00:00:00 2001 From: Anna Wiggins Date: Mon, 1 Nov 2021 23:12:30 +0000 Subject: [PATCH 1/3] Add support for multiplexers. This introduces a new dependency, but c'est la vie. --- Joystick.cpp | 27 ++++++++++++++++++++++++++- Joystick.h | 37 +++++++++++++++++++++++++++++++------ Readme.md | 23 +++++++++++++++++------ 3 files changed, 74 insertions(+), 13 deletions(-) diff --git a/Joystick.cpp b/Joystick.cpp index 5b4c99a..f7aee16 100644 --- a/Joystick.cpp +++ b/Joystick.cpp @@ -1,5 +1,8 @@ #include "Joystick.h" #include +#include + +using namespace admux; bool operator ==(JoyReport a, JoyReport b){ for (uint8_t i=0; i < JOYSTICK_NUM_AXES; i++) { @@ -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() { @@ -64,6 +72,20 @@ void Joystick::AddAxis(uint8_t pin) { _num_axes++; } +void Joystick::AddMux(uint8_t signal_pin, uint8_t addr_pins[], uint8_t addr_width, bool pullup=true) { + Pinset pins; + for (uint8_t i = 0; i < addr_width; i++) { + pins.pins[i] = addr_pins[i]; + } + pins.m_size = addr_width; + + uint8_t mode = INPUT_PULLUP; + if (!pullup) mode = INPUT; + Mux mux(Pin(signal_pin, mode, PinType::Digital), pins); + _mux[_num_mux] = mux; + _num_mux++; +} + void Joystick::Update() { JoyReport oldReport = _joyReport; @@ -138,6 +160,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..568bd13 100644 --- a/Joystick.h +++ b/Joystick.h @@ -3,6 +3,9 @@ #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 @@ -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,19 +57,29 @@ 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, bool mux=false, uint8_t mux_id = 0; uint8_t mux_channel = 0); + + // 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, uint8_t addr_pins[], uint8_t addr_width); - void AddButton(uint8_t pin, ButtonType type, bool pullup=true); - void AddAxis(uint8_t pin); // Axes don't actually work yet! - - // 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); @@ -72,6 +94,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..a733af4 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. It 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`. -- 2.45.1 From 5c2a2736dcb04a42edf875235a19fe7e2d226626 Mon Sep 17 00:00:00 2001 From: Anna Wiggins Date: Tue, 2 Nov 2021 19:55:35 +0000 Subject: [PATCH 2/3] Clean up examples directory. --- examples/{type_test => basic}/Makefile | 0 .../type_test.ino => basic/basic.ino} | 14 +++++- examples/multiplexer/Makefile | 8 +++ examples/multiplexer/multiplexer.ino | 49 +++++++++++++++++++ .../complex_switch_panel.ino | 0 .../switch_panel/switch_panel.ino | 0 .../switch_panel_improved.ino | 0 7 files changed, 70 insertions(+), 1 deletion(-) rename examples/{type_test => basic}/Makefile (100%) rename examples/{type_test/type_test.ino => basic/basic.ino} (52%) create mode 100644 examples/multiplexer/Makefile create mode 100644 examples/multiplexer/multiplexer.ino rename examples/{ => obsolete}/complex_switch_panel/complex_switch_panel.ino (100%) rename examples/{ => obsolete}/switch_panel/switch_panel.ino (100%) rename examples/{ => obsolete}/switch_panel_improved/switch_panel_improved.ino (100%) 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..259e3e2 --- /dev/null +++ b/examples/multiplexer/multiplexer.ino @@ -0,0 +1,49 @@ +// 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 + +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, {A2, A3, A4, A5}, 4); + + // now we can add the rest of the buttons + joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 1); // the last parameter is which channel/pin the input is attached to on the multiplexer + joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 2); + joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 3); + joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 4); + joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 5); + joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 6); + joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 7); + joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 8); + + 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 -- 2.45.1 From fbd786fe96138374529a0172004356ffa2f49b70 Mon Sep 17 00:00:00 2001 From: Anna Wiggins Date: Tue, 2 Nov 2021 23:17:25 +0000 Subject: [PATCH 3/3] Fix compile-time errors, which necessitated some refactoring of the interface. --- Joystick.cpp | 31 ++++++++++++++++++---------- Joystick.h | 13 +++++++----- Readme.md | 2 +- examples/multiplexer/multiplexer.ino | 19 +++++++++-------- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/Joystick.cpp b/Joystick.cpp index f7aee16..b7707e9 100644 --- a/Joystick.cpp +++ b/Joystick.cpp @@ -1,6 +1,6 @@ #include "Joystick.h" -#include #include +#include using namespace admux; @@ -40,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; @@ -65,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) { @@ -72,18 +85,14 @@ void Joystick::AddAxis(uint8_t pin) { _num_axes++; } -void Joystick::AddMux(uint8_t signal_pin, uint8_t addr_pins[], uint8_t addr_width, bool pullup=true) { - Pinset pins; - for (uint8_t i = 0; i < addr_width; i++) { - pins.pins[i] = addr_pins[i]; - } - pins.m_size = addr_width; - +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), pins); - _mux[_num_mux] = mux; + Mux mux(Pin(signal_pin, mode, PinType::Digital), addr_pins); + _mux[mux_id] = &mux; _num_mux++; + return mux_id; } void Joystick::Update() { @@ -161,7 +170,7 @@ 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); + _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 568bd13..24008ee 100644 --- a/Joystick.h +++ b/Joystick.h @@ -1,9 +1,9 @@ #ifndef _JOYSTICK_H_ #define _JOYSTICK_H_ +#include #include #include -#include using namespace admux; @@ -63,7 +63,8 @@ class Joystick { // 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, bool mux=false, uint8_t mux_id = 0; uint8_t mux_channel = 0); + void AddButton(uint8_t pin, ButtonType type, bool pullup=true); + 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); @@ -71,7 +72,7 @@ class Joystick { // 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, uint8_t addr_pins[], uint8_t addr_width); + uint8_t AddMux(uint8_t signal_pin, Pinset addr_pins, bool pullup=true); private: void SetAxis(uint8_t axis, int16_t value); @@ -83,7 +84,9 @@ class Joystick { 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. @@ -95,7 +98,7 @@ class Joystick { JoyReport _joyReport; bool _debug; - Mux _mux[MAX_MUX]; + Mux *_mux[MAX_MUX]; uint8_t _num_mux; }; diff --git a/Readme.md b/Readme.md index a733af4..ae8e941 100644 --- a/Readme.md +++ b/Readme.md @@ -26,4 +26,4 @@ This is a library that builds and sends USB HID Joystick reports, making it easy 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. It 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`. +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/multiplexer/multiplexer.ino b/examples/multiplexer/multiplexer.ino index 259e3e2..cdac23f 100644 --- a/examples/multiplexer/multiplexer.ino +++ b/examples/multiplexer/multiplexer.ino @@ -7,6 +7,7 @@ // 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); @@ -28,17 +29,17 @@ void setup() { joystick.AddButton(A0, BUTTON_PASSTHRU); // to get more room for our inputs, we add an 8-bit multiplexer - uint8_t mux = joystick.AddMux(A1, {A2, A3, A4, A5}, 4); + uint8_t mux = joystick.AddMux(A1, admux::Pinset(A2, A3, A4, A5), 4); // now we can add the rest of the buttons - joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 1); // the last parameter is which channel/pin the input is attached to on the multiplexer - joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 2); - joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 3); - joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 4); - joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 5); - joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 6); - joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 7); - joystick.AddButton(A1, BUTTON_PASSTHRU, true, true, mux, 8); + 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(); } -- 2.45.1