Merge pull request 'Add rotary encoder support, simplify AddMuxButton interface.' (#3) from encoders into main

Reviewed-on: https://git.annabunch.es/annabunches/arduino-joystick/pulls/3
This commit is contained in:
Anna Rose 2021-11-07 22:51:18 +00:00
commit 346e612f65
5 changed files with 106 additions and 15 deletions

View File

@ -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");
}

View File

@ -1,9 +1,10 @@
#ifndef _JOYSTICK_H_
#define _JOYSTICK_H_
#include <Mux.h>
#include <Arduino.h>
#include <Bounce2.h>
#include <Encoder.h>
#include <Mux.h>
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

8
examples/full/Makefile Normal file
View File

@ -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}

38
examples/full/full.ino Normal file
View File

@ -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 <Joystick.h>
#include <Mux.h>
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();
}

View File

@ -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();
}