diff --git a/Button.cpp b/Button.cpp index 5591517..ab4047b 100644 --- a/Button.cpp +++ b/Button.cpp @@ -32,9 +32,9 @@ LatchedButton::LatchedButton(uint8_t vbutton, Reader* reader) : Button(vbutton, } bool LatchedButton::Update(Joystick* js) { - if (!BouncerUpdate()) return false; + if (!reader->Update()) return false; - if (On()) { + if (reader->On()) { if (!pressed) { js->PressButton(vbutton); pressed = true; @@ -48,7 +48,7 @@ bool LatchedButton::Update(Joystick* js) { } -PulsedButton::PulsedButton(uint8_t pin, uint8_t vbutton, bool double_action, bool split, bool pullup, Mux* mux, bool analog_only) : SwitchButton(pin, vbutton, pullup, mux, analog_only) { +PulsedButton::PulsedButton(uint8_t vbutton, Reader* reader, bool double_action, bool split) : Button(vbutton, reader) { this->release_time1 = 0; this->release_time2 = 0; if (double_action) { @@ -74,18 +74,18 @@ bool PulsedButton::Update(Joystick* js) { release_time2 = 0; } - if (!BouncerUpdate()) return false; + if (!reader->Update()) return false; switch(type) { case BUTTON_PULSED: - if (On()) js->PressButton(vbutton); + if (reader->On()) js->PressButton(vbutton); break; case BUTTON_PULSED_DOUBLE_ACTION: js->PressButton(vbutton); release_time1 = millis() + 250; break; case BUTTON_PULSED_DOUBLE_ACTION_SPLIT: - if (On()) { + if (reader->On()) { js->PressButton(vbutton); release_time1 = millis() + 250; } @@ -100,7 +100,7 @@ bool PulsedButton::Update(Joystick* js) { } -EncoderButton::EncoderButton(uint8_t pin1, uint8_t pin2, uint8_t vbutton) : Button(vbutton) { +EncoderButton::EncoderButton(uint8_t pin1, uint8_t pin2, uint8_t vbutton) : Button(vbutton, NULL) { this->type = ENCODER_PULSED_SPLIT; this->vbutton2 = vbutton + 1; this->encoder = new Encoder(pin1, pin2); diff --git a/Joystick.cpp b/Joystick.cpp index a5eaaf1..5b63eba 100644 --- a/Joystick.cpp +++ b/Joystick.cpp @@ -48,8 +48,8 @@ void Joystick::AddMuxButton(uint8_t channel, Mux* mux, ButtonType type, bool pul _addButton(type, new MuxReader(channel, mux, pullup)); } -void Joystick::AddMatrixButton(uint8_t row, uint8_t col, ButtonType type, bool pullup) { - _addButton(type, new MatrixReader(row, col, pullup)); +void Joystick::AddMatrixButton(uint8_t row, uint8_t col, Matrix* matrix, ButtonType type, bool pullup) { + _addButton(type, new MatrixReader(row, col, matrix, pullup)); } void Joystick::AddEncoder(uint8_t pin1, uint8_t pin2, ButtonType type) { @@ -147,6 +147,7 @@ void Joystick::_UpdateAxis(uint8_t index) { } void Joystick::_addButton(ButtonType type, Reader* reader) { + Button* button; switch (type) { case BUTTON_PASSTHRU: button = new PassthruButton(_virtual_buttons, reader); diff --git a/Joystick.h b/Joystick.h index 0712375..93cb8e7 100644 --- a/Joystick.h +++ b/Joystick.h @@ -2,8 +2,8 @@ #define _JOYSTICK_H_ #include "Button.h" +#include "Matrix.h" #include -#include #include using namespace admux; @@ -41,7 +41,7 @@ class Joystick { // on the Arduino Nano. void AddButton(uint8_t pin, ButtonType type, bool pullup=true); void AddMuxButton(uint8_t channel, Mux* mux, ButtonType type, bool pullup=true); - void AddMatrixButton(uint8_t row, uint8_t col, ButtonType type, bool pullup=true); + void AddMatrixButton(uint8_t row, uint8_t col, Matrix* matrix, 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 pin1, uint8_t pin2, ButtonType type); diff --git a/Matrix.cpp b/Matrix.cpp new file mode 100644 index 0000000..31f572a --- /dev/null +++ b/Matrix.cpp @@ -0,0 +1,39 @@ +#include "Matrix.h" + +Matrix::Matrix(uint8_t* columns, bool inverted) { + this->columns = columns; + this->num_columns = sizeof(columns); + this->active_pin = num_columns; + this->inverted = inverted; + + for (uint8_t i = 0; i < num_columns; i++) { + pinMode(columns[i], OUTPUT); + if (inverted) digitalWrite(columns[i], HIGH); + else digitalWrite(columns[i], LOW); + } +} + +void Matrix::Activate(uint8_t column) { + if (column > num_columns) return; // TODO: throw an error here? + + uint8_t pin = columns[column]; + if (pin == active_pin) return; + + if (active_pin != num_columns) { + _disable(active_pin); + } + + _enable(pin); + active_pin = pin; + delayMicroseconds(10); // allow ample time for signal to propagate +} + +void Matrix::_enable(uint8_t pin) { + if (inverted) digitalWrite(pin, LOW); + else digitalWrite(pin, HIGH); +} + +void Matrix::_disable(uint8_t pin) { + if (inverted) digitalWrite(pin, HIGH); + else digitalWrite(pin, LOW); +} diff --git a/Matrix.h b/Matrix.h new file mode 100644 index 0000000..d306b47 --- /dev/null +++ b/Matrix.h @@ -0,0 +1,24 @@ +// A state-keeping class used with MatrixReader. Keeps track of which column is active, +// switches between them. + +#ifndef _MATRIX_H_ +#define _MATRIX_H_ + +#include + +class Matrix { + public: + Matrix(uint8_t *columns, bool inverted=true); + void Activate(uint8_t column); + + private: + void _enable(uint8_t pin); + void _disable(uint8_t pin); + + uint8_t* columns; + uint8_t active_pin; + uint8_t num_columns; + bool inverted; +}; + +#endif diff --git a/Reader.cpp b/Reader.cpp index d6f1c95..9bebeba 100644 --- a/Reader.cpp +++ b/Reader.cpp @@ -1,8 +1,5 @@ #include "Reader.h" -#include -#include - Reader::Reader(bool inverted) { this->inverted = inverted; @@ -22,7 +19,7 @@ uint8_t Reader::getMode(bool pullup) { DirectReader::DirectReader(uint8_t pin, bool inverted) : Reader(inverted) { uint8_t mode = getMode(inverted); - this->bouncer.attach(pin) + this->bouncer.attach(pin); } bool DirectReader::Update() { @@ -42,20 +39,16 @@ bool MuxReader::Update() { } -MatrixReader::MatrixReader(uint8_t row, uint8_t col, bool inverted) : Reader(inverted) { +MatrixReader::MatrixReader(uint8_t row, uint8_t col, Matrix* matrix, bool inverted) : Reader(inverted) { uint8_t mode = getMode(inverted); this->bouncer.attach(row, mode); this->col = col; + this->matrix = matrix; } bool MatrixReader::Update() { - digitalWrite(col, LOW); - delayMicroseconds(10); - + matrix->Activate(col); bool changed = bouncer.update(); - digitalWrite(col, HIGH); - delayMicroseconds(10); - return changed; } diff --git a/Reader.h b/Reader.h index f8b9349..37830af 100644 --- a/Reader.h +++ b/Reader.h @@ -4,9 +4,13 @@ #ifndef _READER_H_ #define _READER_H_ +#include "Matrix.h" +#include #include #include +using namespace admux; + // The abstract base class - a very simple interface class Reader { public: @@ -24,7 +28,7 @@ class Reader { // A standard reader; the button is connected directly to a switch -class DirectReader { +class DirectReader : public Reader { public: DirectReader(uint8_t pin, bool inverted = true); bool Update(); @@ -33,27 +37,29 @@ class DirectReader { // The button is connected to a Multiplexer channel -class MuxReader { +class MuxReader : public Reader { public: MuxReader(uint8_t channel, Mux* mux, bool inverted = true); bool Update(); bool On(); protected: + uint8_t channel; Mux * mux; }; // The button is connected via a scan matrix // "row" should always be the input, with "col" the selecter. -class MatrixReader { +class MatrixReader : public Reader { public: - MatrixReader(uint8_t row, uint8_t col, bool inverted = true); + MatrixReader(uint8_t row, uint8_t col, Matrix* matrix, bool inverted = true); bool Update(); bool On(); protected: uint8_t col; + Matrix* matrix; }; #endif diff --git a/examples/basic/Makefile b/example/Makefile similarity index 100% rename from examples/basic/Makefile rename to example/Makefile diff --git a/example/example.ino b/example/example.ino new file mode 100644 index 0000000..05713f9 --- /dev/null +++ b/example/example.ino @@ -0,0 +1,97 @@ +// An example sketch using the joystick library, demonstrating recommended usage +// of all features. + +#include +#include +#include + +using namespace admux; + +// Always use defines to match your pins to their logical wiring + +// pins 2 and 3 are attached to a rotary encoder +#define ENCODER 2,3 + +// these buttons are attached directly to Arduino input pins +#define BTN1 4 +#define BTN2 5 +#define BTN3 6 +#define BTN4 7 + +// pins 8-13 are attached to a 3x3 scanning input matrix. Pins 8-10 are the row pins, 11-13 are the column pins +// However, note that the column pins are 0-indexed; the actual pins are defined in the Matrix object later. +#define MATRIX1 8,0 +#define MATRIX2 9,0 +#define MATRIX3 10,0 +#define MATRIX4 8,1 +#define MATRIX5 9,1 +#define MATRIX6 10,1 +#define MATRIX7 8,2 +#define MATRIX8 9,2 +#define MATRIX9 10,2 + +// A multiplexer is attached to pins A1-A5 +#define MUX_SIG A1 // the multiplexer's signal pin is on Analog 1 +#define MUX_ADDR A2,A3,A4,A5 // the multiplexer's address pins are A2-A5, in that order + +// These are 3 buttons attached to the multiplexer channels +#define MUX_BTN1 0 +#define MUX_BTN2 1 +#define MUX_BTN3 2 + + +bool debug = false; +Joystick js(debug); + +void setup() { + js.AddEncoder(ENCODER, ENCODER_PULSED_SPLIT); + + // Different types of button programming are available. BUTTON_PASSTHRU simply passes on the button's + // real state, and will be the most common, but you can also change how the button works in software like so... + + // With BUTTON_PULSED, no matter how long the button stays held down, it will only send a button press for 250ms + // then deactivate until pressed again. + js.AddButton(BTN1, BUTTON_PULSED); + + // BUTTON_PULSED_DOUBLE_ACTION does the same thing, but sends a *second* 250ms press when the button is deactivated + // This is great for an on-off toggle switch that should send an "event" in both directions. + js.AddButton(BTN2, BUTTON_PULSED_DOUBLE_ACTION); + + // BUTTON_PULSED_DOUBLE_ACTION_SPLIT is just like the above, but it sends *different* button presses for the "on" + // and "off" states. + js.AddButton(BTN3, BUTTON_PULSED_DOUBLE_ACTION_SPLIT); + + // BUTTON_LATCHED_MOMENTARY lets you turn a momentary pushbutton into a toggle switch. + // One press will "press and hold" the button. The next press releases the button. + js.AddButton(BTN4, BUTTON_LATCHED_MOMENTARY); + + // One way to get more room for inputs is to use a scan matrix. + // See http://blog.komar.be/how-to-make-a-keyboard-the-matrix/ for a very detailed discussion of how these work. + // This is a 3x3 example, so we can get 9 buttons out of 6 inputs. + // For faster read results, always add buttons so that buttons with the same *column* are grouped together. + uint8_t cols[] = {11, 12, 13}; + Matrix* matrix = new Matrix(cols); + js.AddMatrixButton(MATRIX1, matrix, BUTTON_PASSTHRU); + js.AddMatrixButton(MATRIX2, matrix, BUTTON_PASSTHRU); + js.AddMatrixButton(MATRIX3, matrix, BUTTON_PASSTHRU); + js.AddMatrixButton(MATRIX4, matrix, BUTTON_PASSTHRU); + js.AddMatrixButton(MATRIX5, matrix, BUTTON_PASSTHRU); + js.AddMatrixButton(MATRIX6, matrix, BUTTON_PASSTHRU); + js.AddMatrixButton(MATRIX7, matrix, BUTTON_PASSTHRU); + js.AddMatrixButton(MATRIX8, matrix, BUTTON_PASSTHRU); + js.AddMatrixButton(MATRIX9, matrix, BUTTON_PASSTHRU); + + // another way to get more room for our inputs, this code adds a 16-channel multiplexer. + // It only attaches 3 buttons, but could attach up to channel 15. + Mux* mux = new Mux(Pin(MUX_SIG, INPUT_PULLUP, PinType::Digital), Pinset(MUX_ADDR)); + js.AddMuxButton(MUX_BTN1, mux, BUTTON_PASSTHRU); + js.AddMuxButton(MUX_BTN2, mux, BUTTON_PASSTHRU); + js.AddMuxButton(MUX_BTN3, mux, BUTTON_PASSTHRU); + + js.Init(); +} + +void loop() { + // check all the button states and send any changes + js.Update(); +} diff --git a/examples/full/Makefile b/example/obsolete/basic/Makefile similarity index 100% rename from examples/full/Makefile rename to example/obsolete/basic/Makefile diff --git a/examples/basic/basic.ino b/example/obsolete/basic/basic.ino similarity index 100% rename from examples/basic/basic.ino rename to example/obsolete/basic/basic.ino diff --git a/examples/obsolete/complex_switch_panel/complex_switch_panel.ino b/example/obsolete/complex_switch_panel/complex_switch_panel.ino similarity index 100% rename from examples/obsolete/complex_switch_panel/complex_switch_panel.ino rename to example/obsolete/complex_switch_panel/complex_switch_panel.ino diff --git a/examples/multiplexer/Makefile b/example/obsolete/full/Makefile similarity index 100% rename from examples/multiplexer/Makefile rename to example/obsolete/full/Makefile diff --git a/examples/full/full.ino b/example/obsolete/full/full.ino similarity index 100% rename from examples/full/full.ino rename to example/obsolete/full/full.ino diff --git a/example/obsolete/multiplexer/Makefile b/example/obsolete/multiplexer/Makefile new file mode 100644 index 0000000..d1058d4 --- /dev/null +++ b/example/obsolete/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/example/obsolete/multiplexer/multiplexer.ino similarity index 100% rename from examples/multiplexer/multiplexer.ino rename to example/obsolete/multiplexer/multiplexer.ino diff --git a/examples/obsolete/switch_panel/switch_panel.ino b/example/obsolete/switch_panel/switch_panel.ino similarity index 100% rename from examples/obsolete/switch_panel/switch_panel.ino rename to example/obsolete/switch_panel/switch_panel.ino diff --git a/examples/obsolete/switch_panel_improved/switch_panel_improved.ino b/example/obsolete/switch_panel_improved/switch_panel_improved.ino similarity index 100% rename from examples/obsolete/switch_panel_improved/switch_panel_improved.ino rename to example/obsolete/switch_panel_improved/switch_panel_improved.ino