From e27c15d56afd908b1fdf4f6d4bb5db28a49f9de3 Mon Sep 17 00:00:00 2001 From: Rob French Date: Sat, 6 Jun 2020 00:06:45 -0500 Subject: [PATCH] Multiple updates. Support for full 16-char status line to be sent to the Raduino. Added rotary encoder support. Everything above has been tested and works. Added rudimentary top-level menu. Compiles, but not tested yet. --- iopcomm/iopcomm.cpp | 22 +++++++ iopcomm/iopcomm.h | 2 + ubitx_iop/RigMode.h | 61 ++++++++++++++++--- ubitx_iop/audio.h | 2 +- ubitx_iop/menu.h | 78 ++++++++++++++++++++++++ ubitx_iop/menu.ino | 131 +--------------------------------------- ubitx_iop/rig.h | 47 +++++++++++++- ubitx_iop/ubitx_iop.h | 5 +- ubitx_iop/ubitx_iop.ino | 61 +++++++++++++++++++ 9 files changed, 266 insertions(+), 143 deletions(-) diff --git a/iopcomm/iopcomm.cpp b/iopcomm/iopcomm.cpp index 80ab602..98ab316 100644 --- a/iopcomm/iopcomm.cpp +++ b/iopcomm/iopcomm.cpp @@ -151,6 +151,28 @@ void sendIOPTestStatus() sendIOPMessage(m); } +//====================================================================== +// TEST STATUS MESSAGE +//====================================================================== + +void sendIOPMenuDisplay(const char* text, int8_t secs) +{ + IOPMessage m; + int l = strlen(text); + m.id = IOP_MENU_DISPLAY_MSG; + m.len = 17; + m.data[0] = uint8_t(secs); + for (int i = 0; i < 16; i++) { + if (i < l) { + m.data[i+1] = text[i]; + } else { + m.data[i+1] = ' '; + } + } + m.data[17] = '\0'; + sendIOPMessage(m); +} + //====================================================================== /* diff --git a/iopcomm/iopcomm.h b/iopcomm/iopcomm.h index 3721230..d57d811 100644 --- a/iopcomm/iopcomm.h +++ b/iopcomm/iopcomm.h @@ -74,6 +74,7 @@ enum MessageID { IOP_DGT_STATUS_MSG, IOP_CW_STATUS_MSG, IOP_TEST_STATUS_MSG, + IOP_MENU_DISPLAY_MSG, // add any new elements here NUM_MESSAGE_IDS @@ -220,6 +221,7 @@ void sendIOPSSBStatus(SSBConfig const&); void sendIOPDGTStatus(DGTConfig const&); void sendIOPCWStatus(CWConfig const&); void sendIOPTestStatus(); +void sendIOPMenuDisplay(const char*, int8_t); //====================================================================== // TRANSLATOR diff --git a/ubitx_iop/RigMode.h b/ubitx_iop/RigMode.h index 19cc28b..7a4c3d2 100644 --- a/ubitx_iop/RigMode.h +++ b/ubitx_iop/RigMode.h @@ -37,6 +37,9 @@ class IMode { inline IFilter& filter() { return _filter; } + virtual const char* rxInfo() = 0; + virtual const char* txInfo() = 0; + // Called upon mode entry. Should assume that the rig's state is // "clean", i.e. that it still needs to enable anything it needs for // use in the mode. @@ -102,11 +105,11 @@ class IMode { inline bool isTx() const { return _transmitting; } inline bool isRx() const { return !_transmitting; } - virtual void setWideRxFilter()= 0; + virtual void setWideRxFilter() = 0; - virtual void setMediumRxFilter()= 0; + virtual void setMediumRxFilter() = 0; - virtual void setNarrowRxFilter()= 0; + virtual void setNarrowRxFilter() = 0; private: @@ -129,7 +132,10 @@ class SSBMode : public IMode IMode(c, a, f), _use_mic(default_mic) { } - + + virtual const char* rxInfo() { return _rx_info; }; + virtual const char* txInfo() { return _tx_info; }; + virtual void onEntry() { setWideRxFilter(); @@ -184,19 +190,22 @@ class SSBMode : public IMode USBDEBUG("SSB mode - Line In set"); } - virtual void setWideRxFilter(){ + virtual void setWideRxFilter() { + _rx_info = "R:2.8k"; ((BPFilter&)filter()).setBand(300.0, 3100.0); filter().enable(); USBDEBUG("set wide RX SSB filter"); } - virtual void setMediumRxFilter(){ + virtual void setMediumRxFilter() { + _rx_info = "R:2.4k"; ((BPFilter&)filter()).setBand(500.0, 2900.0); filter().enable(); USBDEBUG("set medium RX SSB filter"); } - virtual void setNarrowRxFilter(){ + virtual void setNarrowRxFilter() { + _rx_info = "R:1.8k"; ((BPFilter&)filter()).setBand(700.0, 2500.0); filter().enable(); USBDEBUG("set narrow RX SSB filter"); @@ -205,6 +214,8 @@ class SSBMode : public IMode private: bool _use_mic; + const char* _rx_info; + const char* _tx_info; }; //====================================================================== @@ -219,7 +230,10 @@ class DGTMode : public IMode IMode(c, a, f) { } - + + virtual const char* rxInfo() { return _rx_info; }; + virtual const char* txInfo() { return _tx_info; }; + virtual void onEntry() { setWideRxFilter(); @@ -249,22 +263,29 @@ class DGTMode : public IMode } virtual void setWideRxFilter(){ + _rx_info = "R:2.8k"; ((BPFilter&)filter()).setBand(300.0, 3100.0); filter().enable(); USBDEBUG("set wide RX DGT filter"); } virtual void setMediumRxFilter(){ + _rx_info = "R:2.4k"; ((BPFilter&)filter()).setBand(500.0, 2900.0); filter().enable(); USBDEBUG("set medium RX DGT filter"); } virtual void setNarrowRxFilter(){ + _rx_info = "R:500 "; ((BPFilter&)filter()).setCenterAndWidth(1500.0, 500.0); filter().enable(); USBDEBUG("set narrow RX DGT filter"); } + + private: + const char* _rx_info; + const char* _tx_info; }; @@ -280,7 +301,10 @@ class CWMode : public IMode IMode(c, a, f) { } - + + virtual const char* rxInfo() { return _rx_info; }; + virtual const char* txInfo() { return _tx_info; }; + virtual void onEntry() { setWideRxFilter(); @@ -317,6 +341,7 @@ class CWMode : public IMode } ((BPFilter&)filter()).setBand(low, low + width); filter().enable(); + _rx_info = "R:1.0k"; USBDEBUG("set wide RX CW filter"); } @@ -329,6 +354,7 @@ class CWMode : public IMode } ((BPFilter&)filter()).setBand(low, low + width); filter().enable(); + _rx_info = "R:500 "; USBDEBUG("set medium RX CW filter"); } @@ -341,8 +367,13 @@ class CWMode : public IMode } ((BPFilter&)filter()).setBand(low, low + width); filter().enable(); + _rx_info = "R:250 "; USBDEBUG("set narrow RX CW filter"); } + + private: + const char* _rx_info; + const char* _tx_info; }; //====================================================================== @@ -357,7 +388,10 @@ class TTMode : public IMode IMode(c, a, f) { } - + + virtual const char* rxInfo() { return _rx_info; }; + virtual const char* txInfo() { return _tx_info; }; + virtual void onEntry() { setWideRxFilter(); @@ -387,22 +421,29 @@ class TTMode : public IMode } virtual void setWideRxFilter(){ + _rx_info = "R:2.8k"; ((BPFilter&)filter()).setBand(300.0, 3100.0); filter().enable(); USBDEBUG("set wide RX TT filter"); } virtual void setMediumRxFilter(){ + _rx_info = "R:2.4k"; ((BPFilter&)filter()).setBand(500.0, 2900.0); filter().enable(); USBDEBUG("set medium RX TT filter"); } virtual void setNarrowRxFilter(){ + _rx_info = "R:1.8k"; ((BPFilter&)filter()).setBand(700.0, 2500.0); filter().enable(); USBDEBUG("set narrow RX TT filter"); } + + private: + const char* _rx_info; + const char* _tx_info; }; #endif diff --git a/ubitx_iop/audio.h b/ubitx_iop/audio.h index dadb7d3..0829159 100644 --- a/ubitx_iop/audio.h +++ b/ubitx_iop/audio.h @@ -53,7 +53,7 @@ class IFilter { public: virtual ~IFilter() {} virtual void enable() = 0; - virtual void disable()= 0; + virtual void disable() = 0; }; class BPFilter : public IFilter { diff --git a/ubitx_iop/menu.h b/ubitx_iop/menu.h index cc409cb..e1a6d8e 100644 --- a/ubitx_iop/menu.h +++ b/ubitx_iop/menu.h @@ -5,9 +5,87 @@ #ifndef __menu_h__ #define __menu_h__ +#include "rig.h" + // 16 characters on display #define MAX_TEXT_LEN 16 #define MENU_SELECTED_CHAR '>' + +class MenuItem { + public: + virtual ~MenuItem() {} + virtual void update() = 0; + virtual MenuItem* select() = 0; + virtual MenuItem* altSelect() = 0; + virtual MenuItem* exit() = 0; + virtual MenuItem* prev() = 0; + virtual MenuItem* next() = 0; +}; + +class TopMenu : public MenuItem { + public: + TopMenu(Rig& rig): _rig(rig), _visible(false), _adjust_tx(false) {} + + virtual void update() { + strcpy(_buffer, _rig.modeText()); + if (_adjust_tx) { + _buffer[2] = MENU_SELECTED_CHAR; + } else { + _buffer[9] = MENU_SELECTED_CHAR; + } + if (_visible) { + sendIOPMenuDisplay(_buffer, 0); + } else { + sendIOPMenuDisplay(_buffer, -1); + } + } + + virtual MenuItem* select() { + if (!_visible) { + _visible = true; + } else { + _adjust_tx = !_adjust_tx; + } + return this; + } + + virtual MenuItem* altSelect() { + if (_visible) { + _visible = false; + } + return this; + } + + virtual MenuItem* exit() { + if (_visible) { + _visible = false; + } + return this; + } + + virtual MenuItem* prev() { + if (_adjust_tx) { + } else { + _rig.switchRxFilter(true); + } + return this; + } + + virtual MenuItem* next() { + if (_adjust_tx) { + } else { + _rig.switchRxFilter(); + } + return this; + } + + private: + Rig& _rig; + bool _visible; + bool _adjust_tx; + char _buffer[MAX_TEXT_LEN+1]; +}; + /* public class MenuItem { public: diff --git a/ubitx_iop/menu.ino b/ubitx_iop/menu.ino index 8b7ad15..fd8c4f2 100644 --- a/ubitx_iop/menu.ino +++ b/ubitx_iop/menu.ino @@ -2,138 +2,9 @@ // menu.ino //====================================================================== +#include #include "menu.h" -/* - CW mode: - WPM (although we can get this from Raduino) - WPM: # (L/R, select/abort) - FILT - WIDE NORM NARR - - SSB mode: - TX LVL - COMP - FILT - WIDE NORM NARR - - Digi mode: - TX LVL -*/ - -class MenuItem { - public: - MenuItem() {} - virtual MenuItem* next() = 0; - virtual MenuItem* prev() = 0; - char text[MAX_TEXT_LEN + 1]; -}; - -class TextMenu : public MenuItem { - public: - TextMenu(int num_entries, int entry_len): MenuItem(), _num_entries(num_entries), _entry_len(entry_len), _selected(0), _leftmost(0) { - text[0] = '\0'; - _entries = new MenuItem*[_num_entries]; - _labels = new char*[_num_entries]; - for (int i = 0; i < _num_entries; i++) { - _labels[i] = NULL; - } - } - - ~TextMenu() { - for (int i = 0; i < _num_entries; i++) { - if (_labels[i] != NULL) { - delete _labels[i]; - } - } - delete _labels; - delete _entries; - } - - virtual MenuItem* next() { - bool dirty = false; - - if (++_selected == _num_entries) { - _selected = 0; - } - - if (_selected < _leftmost) { - _leftmost = _selected; - dirty = true; - } else { - while ((_selected - _leftmost) * _entry_len >= MAX_TEXT_LEN) { - _leftmost++; - dirty = true; - } - } - - if (dirty) { - for (int i = _leftmost; i < _num_entries; i++) { - strncpy(&text[i - _leftmost], _labels[i], _entry_len); - } - } - text[_selected - _leftmost] = MENU_SELECTED_CHAR; - return this; - } - - virtual MenuItem* prev() { - bool dirty = false; - - if (_selected-- == 0) { - _selected += _num_entries; - } - - if (_selected < _leftmost) { - _leftmost = _selected; - dirty = true; - } else { - while ((_selected - _leftmost) * _entry_len >= MAX_TEXT_LEN) { - _leftmost++; - dirty = true; - } - } - - if (dirty) { - updateText(); - } else { - updateCursor(); - } - return this; - } - - void addEntry(int i, const char* label, MenuItem* entry) { - if (i < _num_entries) { - _labels[i] = new char[strlen(label) + 1]; // I need to learn to do strings the C++ way. - strcpy(_labels[i], label); // Ditto. - _entries[i] = entry; - } - } - - void updateText() { - for (int i = _leftmost; i < _num_entries; i++) { - strncpy(&text[i - _leftmost], _labels[i], _entry_len); - } - text[_selected - _leftmost] = MENU_SELECTED_CHAR; - } - - void updateCursor() { - text[_selected - _leftmost] = MENU_SELECTED_CHAR; - } - - /*MenuItem* operator[](int i) { - return this->entries[i]; - }*/ - - private: - int _num_entries; - int _entry_len; - int _selected; - int _leftmost; - char** _labels; - MenuItem** _entries; -}; - - //====================================================================== // EOF //====================================================================== diff --git a/ubitx_iop/rig.h b/ubitx_iop/rig.h index f9662d8..99e7162 100644 --- a/ubitx_iop/rig.h +++ b/ubitx_iop/rig.h @@ -8,11 +8,14 @@ #include #include "audio.h" #include "RigMode.h" +//#include "menu.h" //====================================================================== // Rig class //====================================================================== +const char * const modeID[] = {"s", "S", "c", "C", "d", "D", "t", "T"}; + class Rig { public: @@ -20,7 +23,7 @@ class Rig { // Constructor/destructor. //-------------------------------------------------------------------- - Rig(RigConfig& c, RigAudio& a): _config(c), _audio(a) + Rig(RigConfig& c, RigAudio& a): _config(c), _audio(a), _filter(RX_FILTER_WIDE) { _modes = new IMode*[c.numModes]; _modesLen = c.numModes; @@ -51,10 +54,21 @@ class Rig { delete _modes; } + void init() { + switchRxFilter(_filter); + } + //-------------------------------------------------------------------- // Mode control. //-------------------------------------------------------------------- + const char* modeText() { + strcpy(&_modeText[0], modeID[_modesIndex]); + strcpy(&_modeText[3], mode()->rxInfo()); + strcpy(&_modeText[10], mode()->txInfo()); + return _modeText; + } + inline bool isSSBMode() const { return (_modesIndex == RIG_MODE_USB || _modesIndex == RIG_MODE_LSB); } inline bool isUSBMode() const { return (_modesIndex == RIG_MODE_USB); } inline bool isLSBMode() const { return (_modesIndex == RIG_MODE_LSB); } @@ -156,16 +170,43 @@ class Rig { //-------------------------------------------------------------------- inline void setWideRxFilter() { + _filter = RX_FILTER_WIDE; mode()->setWideRxFilter(); } inline void setMediumRxFilter() { + _filter = RX_FILTER_MEDIUM; mode()->setMediumRxFilter(); } inline void setNarrowRxFilter() { + _filter = RX_FILTER_NARROW; mode()->setNarrowRxFilter(); } + + void switchRxFilter(RxFilter i) { + switch(i) { + case RX_FILTER_WIDE: + setWideRxFilter(); + break; + + case RX_FILTER_MEDIUM: + setMediumRxFilter(); + break; + + case RX_FILTER_NARROW: + setNarrowRxFilter(); + break; + } + } + + void switchRxFilter(bool prev=false) { + if (prev) { + _filter = RxFilter((_filter - 1) % NUM_RX_FILTERS); + } else { + _filter = RxFilter((_filter + 1) % NUM_RX_FILTERS); + } + } /* // Returns a pointer to the current RX filter. @@ -273,6 +314,10 @@ class Rig { RigMode _modesLen; RigMode _modesIndex; + RxFilter _filter; + + char _modeText[17]; + /* IFilter** _rxFilters; uint8_t _rxFiltersLen; diff --git a/ubitx_iop/ubitx_iop.h b/ubitx_iop/ubitx_iop.h index 4b02f11..1d9ff8d 100644 --- a/ubitx_iop/ubitx_iop.h +++ b/ubitx_iop/ubitx_iop.h @@ -12,9 +12,12 @@ #include "keyer.h" #define PTT_KEY_OUT_PIN 2 +#define ENCODER_A_PIN 5 +#define ENCODER_B_PIN 4 +#define ENCODER_SW_PIN 3 // comment this out to disable debugging code -//#define DEBUG +#define DEBUG #if defined(DEBUG) #define USBDEBUG(x) USBSERIAL.print("IOP: "); USBSERIAL.println(x); diff --git a/ubitx_iop/ubitx_iop.ino b/ubitx_iop/ubitx_iop.ino index 49d323a..7fbe6a3 100644 --- a/ubitx_iop/ubitx_iop.ino +++ b/ubitx_iop/ubitx_iop.ino @@ -2,11 +2,13 @@ // ubitx_iop.ino //====================================================================== +#include #include #include "audio.h" #include "config.h" #include "ubitx_iop.h" #include "keyer.h" +#include "menu.h" #include "rig.h" #include "TxSwitch.h" @@ -33,6 +35,15 @@ GPIOSwitch micPTT(true, MIC_PTT_PIN); //LineSwitch linePTTHelper; GPIOSwitch linePTT(false, LINE_PTT_PIN); +Encoder knob(ENCODER_A_PIN, ENCODER_B_PIN); +long knobPos = 0; + +Bounce btn; +elapsedMillis btnMillis; + +TopMenu topMenu(rig); +MenuItem* menu = &topMenu; + elapsedMillis frameMillis; unsigned frameTime; unsigned frameCounter; @@ -67,6 +78,13 @@ void setup() { frameCounter = 0; frameMillis = 0; + knob.write(0); + + btn.attach(ENCODER_SW_PIN, INPUT_PULLUP); + btn.interval(25); + + rig.init(); + USBDEBUG("setup completed"); // audioInit(); /* @@ -84,6 +102,7 @@ void loop() { static char frame_status[100]; static bool paddle_loop = false; +// long oldPos = knobPos; RigMode oldRigMode; @@ -191,6 +210,48 @@ void loop() //frame10Hz = 0; } + btn.update(); + if (btn.fell()) { + //sendIOPMenuDisplay("PRESSED ", 2); + btnMillis = 0; + USBDEBUG("button pressed"); + } else if (btn.rose()) { + if (btnMillis > 1000) { + // long press - exit + //sendIOPMenuDisplay("REL-LONG ", 2); + menu->exit(); + USBDEBUG("button released - long"); + } if (btnMillis > 500) { + // medium press - altSelect + menu->altSelect(); + } else { + // short press - select + //sendIOPMenuDisplay("REL-SHORT ", 2); + menu->select(); + USBDEBUG("button released - short"); + } + menu->update(); + } + + knobPos = knob.read(); + // wait til we turn greater than 3 pos in either direction, for "debouncing" + + if (knobPos > 3) { + // left + //sendIOPMenuDisplay("LEFT ", 5); + menu->prev(); + menu->update(); + knob.write(0); + USBDEBUG("knob turned left"); + } else if (knobPos < -3) { + // right + //sendIOPMenuDisplay("RIGHT ", 5); + menu->next(); + menu->update(); + knob.write(0); + USBDEBUG("knob turned right"); + } + if (frameMillis > 5000) { #if defined(DEBUG) frameTime = frameMillis;