From 09043919dee4630fc371fc6ae110764f5c56c2c9 Mon Sep 17 00:00:00 2001 From: Anna Wiggins Date: Sun, 7 Nov 2021 22:49:14 +0000 Subject: [PATCH] Add rotary encoder support, simplify AddMuxButton interface. --- Joystick.cpp | 40 +++++++++++++++++++++++++--- Joystick.h | 19 ++++++++++--- examples/full/Makefile | 8 ++++++ examples/full/full.ino | 38 ++++++++++++++++++++++++++ examples/multiplexer/multiplexer.ino | 16 +++++------ 5 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 examples/full/Makefile create mode 100644 examples/full/full.ino diff --git a/Joystick.cpp b/Joystick.cpp index b7707e9..3ecf363 100644 --- a/Joystick.cpp +++ b/Joystick.cpp @@ -43,7 +43,28 @@ 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) { +void Joystick::AddEncoder(uint8_t pin0, uint8_t pin1, ButtonType type, bool pullup) { + switch (type) { + case ENCODER_PULSED_SPLIT: + // add an encoder button. _BuildButton() doesn't do everything we need, however... + Button *button = _BuildButton(pin0, type, pullup); + + // ... so we set up the encoder stuff here. button->pin and button->bouncer will be + // ignored + button->encoder_button = true; + button->encoder = new Encoder(pin0, pin1); + long last_enc = button->encoder->read(); + break; + default: + if (_debug) { + Serial.print("DEBUG: Unhandled encoder type: "); + Serial.println(type); + } + } +} + +void Joystick::AddMuxButton(uint8_t mux_id, uint8_t mux_channel, ButtonType type, bool pullup) { + uint8_t pin = _mux[mux_id]->signalPin().pin; Button *button = _BuildButton(pin, type, pullup); button->mux = true; button->mux_id = mux_id; @@ -62,7 +83,7 @@ Button* Joystick::_BuildButton(uint8_t pin, ButtonType type, bool pullup) { uint8_t increment = 1; button.vbutton0 = _virtual_buttons; - if (type == BUTTON_PULSED_DOUBLE_ACTION_SPLIT) { + if (type & (BUTTON_PULSED_DOUBLE_ACTION_SPLIT | ENCODER_PULSED_SPLIT)) { increment = 2; button.vbutton1 = _virtual_buttons + 1; } @@ -99,7 +120,11 @@ void Joystick::Update() { JoyReport oldReport = _joyReport; for (uint8_t i = 0; i < _num_buttons; i++) { - _UpdateButton(i); + if (_buttons[i].type == ENCODER_PULSED_SPLIT) { // todo: make this check for any encoder type + _UpdateEncoder(i); + } else { + _UpdateButton(i); + } } for (uint8_t i = 0; i < _num_axes; i++) { @@ -167,6 +192,7 @@ void Joystick::Write() { delay(250); } +// todo: bite the bullet and use inheritance here, this is getting out of hand void Joystick::_UpdateButton(uint8_t button_num) { Button *button = &_buttons[button_num]; if (button->mux) { @@ -212,6 +238,14 @@ void Joystick::_UpdateButton(uint8_t button_num) { } } +void Joystick::_UpdateEncoder(uint8_t index) { + Button *button = &_buttons[index]; + long new_value = button->encoder->read(); + if (new_value > button->last_enc) PressButton(button->vbutton0); + else if (new_value < button->last_enc) PressButton(button->vbutton1); + button->last_enc = new_value; +} + void Joystick::_UpdateAxis(uint8_t index) { if (_debug) Serial.println("STUB: Joystick::_UpdateAxis"); } diff --git a/Joystick.h b/Joystick.h index 24008ee..39fd0e3 100644 --- a/Joystick.h +++ b/Joystick.h @@ -1,9 +1,10 @@ #ifndef _JOYSTICK_H_ #define _JOYSTICK_H_ -#include #include #include +#include +#include using namespace admux; @@ -27,7 +28,8 @@ enum ButtonType { BUTTON_PULSED = 0x2, // on button press, send an on signal followed immediately by an off signal. BUTTON_PULSED_DOUBLE_ACTION = 0x4, // Send a button press twice - once for press and once for release. BUTTON_PULSED_DOUBLE_ACTION_SPLIT = 0x8, // Send two separate button presses - one button on press, another on release. - BUTTON_LATCHED_MOMENTARY = 0x10 + BUTTON_LATCHED_MOMENTARY = 0x10, + ENCODER_PULSED_SPLIT = 0x20 // A rotary encoder that should be treated as two different pulsed/momentary buttons, one for each direction }; struct JoyReport { @@ -43,10 +45,16 @@ struct Button { bool pressed = false; // only used by BUTTON_LATCHED_MOMENTARY bool inverted = false; // if true, send button press on release and vice versa. + // multiplexer button settings // todo: this should probably be abstracted out from this struct... bool mux; uint8_t mux_id; uint8_t mux_channel; + + // encoder button settings + bool encoder_button; + Encoder* encoder; + long last_enc; }; bool operator ==(JoyReport a, JoyReport b); @@ -64,7 +72,9 @@ class Joystick { // 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 AddMuxButton(uint8_t pin, uint8_t mux_id, uint8_t mux_channel, ButtonType type, bool pullup=true); + // Add a rotary encoder. ENCODER button types allow you to treat an encoder as a momentary button or an axis (TODO) + void AddEncoder(uint8_t pin0, uint8_t pin1, ButtonType type, bool pullup=true); + void AddMuxButton(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); @@ -83,6 +93,7 @@ class Joystick { void _ReleasePulsedButtons(); void _UpdateButton(uint8_t index); + void _UpdateEncoder(uint8_t index); void _UpdateAxis(uint8_t index); Button* _BuildButton(uint8_t pin, ButtonType type, bool pullup); @@ -103,6 +114,6 @@ class Joystick { }; // Internal use only. -#define _BUTTON_PULSED_TYPES (BUTTON_PULSED | BUTTON_PULSED_DOUBLE_ACTION | BUTTON_PULSED_DOUBLE_ACTION_SPLIT) +#define _BUTTON_PULSED_TYPES (BUTTON_PULSED | BUTTON_PULSED_DOUBLE_ACTION | BUTTON_PULSED_DOUBLE_ACTION_SPLIT | ENCODER_PULSED_SPLIT) #endif diff --git a/examples/full/Makefile b/examples/full/Makefile new file mode 100644 index 0000000..d1058d4 --- /dev/null +++ b/examples/full/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/full/full.ino b/examples/full/full.ino new file mode 100644 index 0000000..9f8e592 --- /dev/null +++ b/examples/full/full.ino @@ -0,0 +1,38 @@ +// An example sketch using the joystick library. +// This example includes every possible feature of the library. +// It assumes an Arduino Nano and a 16-channel multiplexer. + +#include +#include + +using admux::Pinset; + +bool debug = false; +Joystick joystick(debug); + +void setup() { + joystick.AddButton(2, BUTTON_PASSTHRU); // this button will always transfer its current literal state to the computer + + joystick.AddButton(3, BUTTON_LATCHED_MOMENTARY); // this button will start sending a buttonpress on one push, and keep it "held down" until it is pushed again. + + joystick.AddButton(4, BUTTON_PULSED); // this button will send a buttonpress very briefly every time it is activated. Good for a toggle switch that shouldn't be "latched" in practice. + + joystick.AddButton(5, BUTTON_PULSED_DOUBLE_ACTION); // like the above, but it sends its button press when toggled on *and* when toggled off + + joystick.AddButton(6, BUTTON_PULSED_DOUBLE_ACTION_SPLIT); // again, similar to the above, but it sends two *different* button presses - 'on' will be one button, 'off' another. + + joystick.AddEncoder(7, 8, ENCODER_PULSED_SPLIT); // a rotary encoder that will send 2 different buttonpresses - one when the encoder is turned clockwise, another when it is turned counterclockwise. + + Pinset addr_pins = Pinset(10, 11, 12, 13); + uint8_t mux_id = joystick.AddMux(9, addr_pins); + + joystick.AddMuxButton(mux_id, 0, BUTTON_PASSTHRU); + + // start up serial communication + joystick.Init(); +} + +void loop() { + // check all the button states and send any changes + joystick.Update(); +} diff --git a/examples/multiplexer/multiplexer.ino b/examples/multiplexer/multiplexer.ino index cdac23f..5817fd4 100644 --- a/examples/multiplexer/multiplexer.ino +++ b/examples/multiplexer/multiplexer.ino @@ -32,14 +32,14 @@ void setup() { 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.AddMuxButton(mux, 0, BUTTON_PASSTHRU); + joystick.AddMuxButton(mux, 1, BUTTON_PASSTHRU); + joystick.AddMuxButton(mux, 2, BUTTON_PASSTHRU); + joystick.AddMuxButton(mux, 3, BUTTON_PASSTHRU); + joystick.AddMuxButton(mux, 4, BUTTON_PASSTHRU); + joystick.AddMuxButton(mux, 5, BUTTON_PASSTHRU); + joystick.AddMuxButton(mux, 6, BUTTON_PASSTHRU); + joystick.AddMuxButton(mux, 7, BUTTON_PASSTHRU); joystick.Init(); } -- 2.45.1