diff options
-rw-r--r-- | ButtonHandler/ButtonHandler.cpp | 125 | ||||
-rw-r--r-- | ButtonHandler/ButtonHandler.h | 25 | ||||
-rw-r--r-- | Layout/Layout.cpp | 61 | ||||
-rw-r--r-- | Layout/Layout.h | 11 | ||||
-rw-r--r-- | Layout/LayoutConfig.cpp | 23 | ||||
-rw-r--r-- | Layout/LayoutConfig.h | 20 | ||||
-rw-r--r-- | Layout/LayoutDemoHelp.cpp | 28 | ||||
-rw-r--r-- | Layout/LayoutDemoHelp.h | 22 | ||||
-rw-r--r-- | Layout/LayoutJoin.cpp | 104 | ||||
-rw-r--r-- | Layout/LayoutJoin.h | 46 | ||||
-rw-r--r-- | Layout/LayoutScrollSelect.cpp | 244 | ||||
-rw-r--r-- | Layout/LayoutScrollSelect.h | 40 | ||||
-rw-r--r-- | Layout/LayoutSingleHelp.cpp | 26 | ||||
-rw-r--r-- | Layout/LayoutSingleHelp.h | 21 | ||||
-rw-r--r-- | Layout/LayoutStartup.cpp | 14 | ||||
-rw-r--r-- | Layout/LayoutSweepHelp.cpp | 26 | ||||
-rw-r--r-- | Layout/LayoutSweepHelp.h | 21 | ||||
-rw-r--r-- | LoRaHandler/LoRaHandler.cpp | 160 | ||||
-rw-r--r-- | LoRaHandler/LoRaHandler.h | 50 | ||||
-rw-r--r-- | Mode/Mode.cpp | 19 | ||||
-rw-r--r-- | Mode/Mode.h | 47 | ||||
-rw-r--r-- | Mode/ModeJoin.cpp | 86 | ||||
-rw-r--r-- | Mode/ModeJoin.h | 30 | ||||
-rw-r--r-- | main.cpp | 242 |
24 files changed, 1427 insertions, 64 deletions
diff --git a/ButtonHandler/ButtonHandler.cpp b/ButtonHandler/ButtonHandler.cpp index 23bcb02..edc9ea1 100644 --- a/ButtonHandler/ButtonHandler.cpp +++ b/ButtonHandler/ButtonHandler.cpp @@ -1,45 +1,136 @@ #include "ButtonHandler.h" +#define signal (int32_t)0xA0 + +typedef enum { + b_none = 0, + b_sw1_fall, + b_sw1_rise, + b_sw2_fall, + b_sw2_rise +} InternalButtonEvent; + +InternalButtonEvent event = b_none; +bool check_sw1 = false; + +void b_worker(void const* argument) { + ButtonHandler* b = (ButtonHandler*)argument; + osEvent e; + + while (true) { + e = Thread::signal_wait(signal, 250); + if (e.status == osEventSignal) { + switch (event) { + case b_sw1_fall: + if (! b->_sw1_running) { + check_sw1 = true; + b->_sw1_running = true; + b->_sw1_timer.reset(); + b->_sw1_timer.start(); + } + break; + + case b_sw1_rise: + if (b->_sw1_running) { + check_sw1 = false; + b->_sw1_running = false; + b->_sw1_timer.stop(); + b->_sw1_time = b->_sw1_timer.read_ms(); + + if (b->_sw1_time > b->_debounce_time) { + b->_event = ButtonHandler::sw1_press; + osSignalSet(b->_main, buttonSignal); + } + } + break; + + case b_sw2_fall: + if (! b->_sw2_running) { + b->_sw2_running = true; + b->_sw2_timer.reset(); + b->_sw2_timer.start(); + } + break; + + case b_sw2_rise: + if (b->_sw2_running) { + b->_sw2_running = false; + b->_sw2_timer.stop(); + b->_sw2_time = b->_sw2_timer.read_ms(); + + if (b->_sw2_time > b->_debounce_time) { + b->_event = ButtonHandler::sw2_press; + osSignalSet(b->_main, buttonSignal); + } + } + break; + + default: + break; + } + } + + if (check_sw1) { + if (b->_sw1_timer.read_ms() > b->_hold_threshold) { + check_sw1 = false; + b->_sw1_running = false; + b->_sw1_timer.stop(); + b->_event = ButtonHandler::sw1_hold; + osSignalSet(b->_main, buttonSignal); + } + } + } +} + ButtonHandler::ButtonHandler(osThreadId main) : _main(main), + _thread(b_worker, (void*)this), _sw1(PA_12), _sw2(PA_11), _sw1_time(0), - _event(none) + _sw2_time(0), + _event(none), + _debounce_time(20), + _hold_threshold(500) { - // gpio goes low when push button is pressed - // fall handler will be the press, rise handler will be the release + // fall handler called on press, rise handler called on release _sw1.fall(this, &ButtonHandler::sw1_fall); _sw1.rise(this, &ButtonHandler::sw1_rise); // need to set mode to PullUp after attaching handlers - won't work otherwise _sw1.mode(PullUp); _sw2.fall(this, &ButtonHandler::sw2_fall); + _sw2.rise(this, &ButtonHandler::sw2_rise); _sw2.mode(PullUp); } -ButtonEvent ButtonHandler::getButtonEvent() { +ButtonHandler::ButtonEvent ButtonHandler::getButtonEvent() { ButtonEvent event = _event; _event = none; return event; } -void ButtonHandler::sw1_rise() { - _sw1_timer.stop(); - _sw1_time = _sw1_timer.read_ms(); - - if (_sw1_time > 10) { - _event = (_sw1_time > 500) ? sw1_hold : sw1_press; - osSignalSet(_main, buttonSignal); - } +void ButtonHandler::sw1_fall() { + event = b_sw1_fall; + _thread.signal_set(signal); + _thread.signal_clr(signal); } -void ButtonHandler::sw1_fall() { - _sw1_timer.reset(); - _sw1_timer.start(); +void ButtonHandler::sw1_rise() { + event = b_sw1_rise; + _thread.signal_set(signal); + _thread.signal_clr(signal); } void ButtonHandler::sw2_fall() { - _event = sw2_press; - osSignalSet(_main, buttonSignal); + event = b_sw2_fall; + _thread.signal_set(signal); + _thread.signal_clr(signal); } + +void ButtonHandler::sw2_rise() { + event = b_sw2_rise; + _thread.signal_set(signal); + _thread.signal_clr(signal); +} + diff --git a/ButtonHandler/ButtonHandler.h b/ButtonHandler/ButtonHandler.h index 3eb8298..e99c767 100644 --- a/ButtonHandler/ButtonHandler.h +++ b/ButtonHandler/ButtonHandler.h @@ -4,33 +4,40 @@ #include "mbed.h" #include "rtos.h" -#define buttonSignal (uint32_t)0x01 - -typedef enum { - none = 0, - sw1_press, - sw1_hold, - sw2_press -} ButtonEvent; +#define buttonSignal (int32_t)0x01 class ButtonHandler { public: + typedef enum { + none = 0, + sw1_press, + sw1_hold, + sw2_press + } ButtonEvent; + ButtonHandler(osThreadId main); ~ButtonHandler(); ButtonEvent getButtonEvent(); - private: void sw1_fall(); void sw1_rise(); void sw2_fall(); + void sw2_rise(); osThreadId _main; + Thread _thread; InterruptIn _sw1; InterruptIn _sw2; Timer _sw1_timer; + Timer _sw2_timer; time_t _sw1_time; + time_t _sw2_time; + bool _sw1_running; + bool _sw2_running; ButtonEvent _event; + time_t _debounce_time; + time_t _hold_threshold; }; #endif diff --git a/Layout/Layout.cpp b/Layout/Layout.cpp index b2ca942..7b0c375 100644 --- a/Layout/Layout.cpp +++ b/Layout/Layout.cpp @@ -35,15 +35,22 @@ void Layout::endUpdate() { _lcd->endUpdate(); } -bool Layout::writeField(uint8_t col, uint8_t row, std::string field, bool apply) { - return writeField(col, row, field.c_str(), field.size(), apply); +bool Layout::writeLabel(const Label& label) { + return writeText(label._col, label._row, label._value.c_str(), label._value.size()); } -bool Layout::writeField(uint8_t col, uint8_t row, const char* field, size_t size, bool apply) { +bool Layout::writeField(const Field& field, const std::string& value, bool apply) { + bool ret; + std::string v = value; + if (apply) startUpdate(); - _lcd->writeText(col*6, row, font_6x8, field, size); + // fill the whole length with blank space in case the previous value was longer than this one + while (v.size() < field._maxSize) + v += " "; + + ret = writeText(field._col, field._row, v.c_str(), field._maxSize); if (apply) endUpdate(); @@ -51,11 +58,20 @@ bool Layout::writeField(uint8_t col, uint8_t row, const char* field, size_t size return true; } -bool Layout::writeImage(uint8_t col, uint8_t row, const uint8_t* bmp, bool apply) { +bool Layout::writeField(const Field& field, const char* value, size_t size, bool apply) { + bool ret; + char buf[32]; + size_t s = (field._maxSize > size) ? size : field._maxSize; + + // fill the whole length with blank space in case the previous value was longer than this one + memset(buf, 0x20, sizeof(buf)); + if (apply) startUpdate(); - _lcd->writeBitmap(col, row, bmp); + snprintf(buf, s, "%s", value); + + ret = writeText(field._col, field._row, value, field._maxSize); if (apply) endUpdate(); @@ -63,3 +79,36 @@ bool Layout::writeImage(uint8_t col, uint8_t row, const uint8_t* bmp, bool apply return true; } +bool Layout::writeImage(const Image& image, bool apply) { + bool ret; + + if (apply) + startUpdate(); + + ret = writeBmp(image._row, image._col, image._bmp); + + if (apply) + endUpdate(); + + return ret; +} + +void Layout::removeField(const Field& field) { + startUpdate(); + std::string s(' ', field._maxSize); + writeText(field._row, field._col, s.c_str(), s.size()); + endUpdate(); +} + +bool Layout::writeText(uint8_t col, uint8_t row, const char* value, size_t size) { + _lcd->writeText(col*6, row, font_6x8, value, size); + + return true; +} + +bool Layout::writeBmp(uint8_t col, uint8_t row, const uint8_t* bmp) { + _lcd->writeBitmap(col*6, row, bmp); + + return true; +} + diff --git a/Layout/Layout.h b/Layout/Layout.h index 5b25458..29d59ff 100644 --- a/Layout/Layout.h +++ b/Layout/Layout.h @@ -42,11 +42,16 @@ class Layout { void clear(); void startUpdate(); void endUpdate(); - bool writeField(uint8_t col, uint8_t row, std::string field, bool apply = false); - bool writeField(uint8_t col, uint8_t row, const char* field, size_t size, bool apply = false); - bool writeImage(uint8_t col, uint8_t row, const uint8_t* bmp, bool apply = false); + bool writeLabel(const Label& label); + bool writeField(const Field& field, const std::string& value, bool apply = false); + bool writeField(const Field& field, const char* value, size_t size, bool apply = false); + bool writeImage(const Image& image, bool apply = false); + void removeField(const Field& field); private: + bool writeText(uint8_t col, uint8_t row, const char* value, size_t size); + bool writeBmp(uint8_t col, uint8_t row, const uint8_t* bmp); + DOGS102* _lcd; }; diff --git a/Layout/LayoutConfig.cpp b/Layout/LayoutConfig.cpp new file mode 100644 index 0000000..dc28738 --- /dev/null +++ b/Layout/LayoutConfig.cpp @@ -0,0 +1,23 @@ +#include "LayoutConfig.h" + +LayoutConfig::LayoutConfig(DOGS102* lcd) + : Layout(lcd), + _lMode(0, 0, "Configuration"), + _lHelp1(0, 2, "Connect USB debug"), + _lHelp2(0, 3, "to PC at 115200"), + _lHelp3(0, 4, "baud to configure") +{} + +LayoutConfig::~LayoutConfig() {} + +void LayoutConfig::display() { + clear(); + startUpdate(); + + writeLabel(_lMode); + writeLabel(_lHelp1); + writeLabel(_lHelp2); + writeLabel(_lHelp3); + + endUpdate(); +} diff --git a/Layout/LayoutConfig.h b/Layout/LayoutConfig.h new file mode 100644 index 0000000..0fdba1c --- /dev/null +++ b/Layout/LayoutConfig.h @@ -0,0 +1,20 @@ +#ifndef __LAYOUTCONFIG_H__ +#define __LAYOUTCONFIG_H__ + +#include "Layout.h" + +class LayoutConfig : public Layout { + public: + LayoutConfig(DOGS102* lcd); + ~LayoutConfig(); + + void display(); + + private: + Label _lMode; + Label _lHelp1; + Label _lHelp2; + Label _lHelp3; +}; + +#endif diff --git a/Layout/LayoutDemoHelp.cpp b/Layout/LayoutDemoHelp.cpp new file mode 100644 index 0000000..dab907e --- /dev/null +++ b/Layout/LayoutDemoHelp.cpp @@ -0,0 +1,28 @@ +#include "LayoutDemoHelp.h" + +LayoutDemoHelp::LayoutDemoHelp(DOGS102* lcd) + : Layout(lcd), + _lMode(0, 0, "LoRa Demo"), + _lDesc(0, 1, "Select TX Method"), + _lIns1(0, 4, "Hold SW1 any time"), + _lIns2(0, 5, "for Main Menu"), + _lSw1(10, 7, "Trigger"), + _lSw2(0, 7, "Interval") +{} + +LayoutDemoHelp::~LayoutDemoHelp() {} + +void LayoutDemoHelp::display() { + clear(); + startUpdate(); + + writeLabel(_lMode); + writeLabel(_lDesc); + writeLabel(_lIns1); + writeLabel(_lIns2); + writeLabel(_lSw1); + writeLabel(_lSw2); + + endUpdate(); +} + diff --git a/Layout/LayoutDemoHelp.h b/Layout/LayoutDemoHelp.h new file mode 100644 index 0000000..3e20df9 --- /dev/null +++ b/Layout/LayoutDemoHelp.h @@ -0,0 +1,22 @@ +#ifndef __LAYOUTDEMOHELP_H__ +#define __LAYOUTDEMOHELP_H__ + +#include "Layout.h" + +class LayoutDemoHelp : public Layout { + public: + LayoutDemoHelp(DOGS102* lcd); + ~LayoutDemoHelp(); + + void display(); + + private: + Label _lMode; + Label _lDesc; + Label _lIns1; + Label _lIns2; + Label _lSw1; + Label _lSw2; +}; + +#endif diff --git a/Layout/LayoutJoin.cpp b/Layout/LayoutJoin.cpp new file mode 100644 index 0000000..3b67c28 --- /dev/null +++ b/Layout/LayoutJoin.cpp @@ -0,0 +1,104 @@ +#include "LayoutJoin.h" + +LayoutJoin::LayoutJoin(DOGS102* lcd, uint8_t band) + : Layout(lcd), + _lId(0, 1, "NI="), + _lKey(0, 2, "NK="), + _lFsb(0, 3, "FSB="), + _lRate(0, 5, "DR="), + _lPower(6, 5, "P="), + _lAttempt(11, 5, "A="), + _fStatus(0, 0, 17), + _fId(3, 1, 14), + _fKey(3, 2, 14), + _fFsb(4, 3, 2), + _fRate(3, 5, 2), + _fPower(8, 5, 2), + _fAttempt(13, 5, 4), + _fCountdown(0, 7, 9), + _fCountdownLabel(0, 6, 17), + _fCancel(11, 7, 6), + _band(band) +{} + +LayoutJoin::~LayoutJoin() {} + +void LayoutJoin::display() { + clear(); + startUpdate(); + + writeLabel(_lId); + writeLabel(_lKey); + if (_band == mDot::FB_915) { + writeLabel(_lFsb); + } + writeLabel(_lRate); + writeLabel(_lPower); + writeLabel(_lAttempt); + + displayCancel(); + + endUpdate(); +} + +void LayoutJoin::updateId(std::string id) { + writeField(_fId, id, true); +} + +void LayoutJoin::updateKey(std::string key) { + writeField(_fKey, key, true); +} + +void LayoutJoin::updateFsb(uint8_t band) { + char buf[8]; + size_t size; + + size = snprintf(buf, sizeof(buf), "%u", band); + writeField(_fFsb, buf, size, true); +} + +void LayoutJoin::updateRate(std::string rate) { + writeField(_fRate, rate); +} + +void LayoutJoin::updatePower(uint32_t power) { + char buf[16]; + size_t size; + + size = snprintf(buf, sizeof(buf), "%lu", power); + writeField(_fPower, buf, size, true); +} + +void LayoutJoin::updateAttempt(uint32_t attempt) { + char buf[16]; + size_t size; + + size = snprintf(buf, sizeof(buf), "%lu", attempt); + writeField(_fAttempt, buf, size, true); +} + +void LayoutJoin::updateStatus(std::string status) { + writeField(_fStatus, status, true); +} + +void LayoutJoin::updateCountdown(uint32_t seconds) { + char buf[16]; + size_t size; + + writeField(_fCountdownLabel, "No Free Channel"); + size = snprintf(buf, sizeof(buf), "%lu", seconds); + writeField(_fPower, buf, size, true); +} + +void LayoutJoin::removeCountdown() { + removeField(_fCountdownLabel); + removeField(_fCountdown); +} + +void LayoutJoin::displayCancel(bool display) { + if (display) + writeField(_fCancel, "Cancel", true); + else + removeField(_fCancel); +} + diff --git a/Layout/LayoutJoin.h b/Layout/LayoutJoin.h new file mode 100644 index 0000000..2f71aec --- /dev/null +++ b/Layout/LayoutJoin.h @@ -0,0 +1,46 @@ +#ifndef __LAYOUTJOIN_H__ +#define __LAYOUTJOIN_H__ + +#include "Layout.h" +#include "mDot.h" + +class LayoutJoin : public Layout { + public: + LayoutJoin(DOGS102* lcd, uint8_t band); + ~LayoutJoin(); + + void display(); + + void updateId(std::string id); + void updateKey(std::string key); + void updateFsb(uint8_t band); + void updateRate(std::string rate); + void updatePower(uint32_t power); + void updateAttempt(uint32_t attempt); + void updateStatus(std::string status); + void updateCountdown(uint32_t seconds); + void removeCountdown(); + void displayCancel(bool display = true); + + private: + Label _lId; + Label _lKey; + Label _lFsb; + Label _lRate; + Label _lPower; + Label _lAttempt; + + Field _fStatus; + Field _fId; + Field _fKey; + Field _fFsb; + Field _fRate; + Field _fPower; + Field _fAttempt; + Field _fCountdown; + Field _fCountdownLabel; + Field _fCancel; + uint8_t _band; +}; + +#endif diff --git a/Layout/LayoutScrollSelect.cpp b/Layout/LayoutScrollSelect.cpp new file mode 100644 index 0000000..5c7072e --- /dev/null +++ b/Layout/LayoutScrollSelect.cpp @@ -0,0 +1,244 @@ +#include "LayoutScrollSelect.h" +#include "MTSLog.h" + +LayoutScrollSelect::LayoutScrollSelect(DOGS102* lcd, Items items, std::string info1, std::string info2) + : Layout(lcd), + _lSw1(0, 7, "Scroll"), + _lSw2(11, 7, "Select"), + _lInfo1(0, 0, info1), + _lInfo2(0, 1, info2), + _lCursor(0, 4, "=>"), + _fItem1(3, 2, 14), + _fItem2(3, 3, 14), + _fItem3(3, 4, 14), + _fItem4(3, 5, 14), + _fItem5(3, 6, 14), + _items(items) +{ + _size = _items.size(); + _selected = 0; +} + +LayoutScrollSelect::~LayoutScrollSelect() {} + +void LayoutScrollSelect::display() { + clear(); + startUpdate(); + + writeLabel(_lSw1); + writeLabel(_lSw2); + writeLabel(_lInfo1); + writeLabel(_lInfo2); + writeLabel(_lCursor); + + switch (_size) { + case 0: + // special case - no items + // (empty) + // (empty) + // => (empty) + // (empty) + // (empty) + break; + + case 1: + // special case - 1 item + // (empty) + // (empty) + // => item1 + // (empty) + // (empty) + writeField(_fItem3, _items[0]); + break; + + case 2: + // special case - 2 items + // (empty) + // (empty) + // => item1 + // item2 + // (empty) + writeField(_fItem3, _items[0]); + writeField(_fItem4, _items[1]); + break; + + case 3: + // special case - 3 items + // (empty) + // item3 + // => item1 + // item2 + // (empty) + writeField(_fItem2, _items[2]); + writeField(_fItem3, _items[0]); + writeField(_fItem4, _items[1]); + break; + + case 4: + // special case - 4 items + // item3 + // item4 + // => item1 + // item2 + // (empty) + writeField(_fItem1, _items[2]); + writeField(_fItem2, _items[3]); + writeField(_fItem3, _items[0]); + writeField(_fItem4, _items[1]); + break; + + default: + // this is the generic case - should handle lists of 5+ items correctly + // item4 item6 item9 + // item5 item7 item10 + // => item1 => item1 => item1 + // item2 item2 item2 + // item3 item3 item3 + writeField(_fItem1, _items[_size - 2]); + writeField(_fItem2, _items[_size - 1]); + writeField(_fItem3, _items[0]); + writeField(_fItem4, _items[1]); + writeField(_fItem5, _items[2]); + break; + } + + endUpdate(); +} + +void LayoutScrollSelect::scroll() { + size_t index; + + switch (_size) { + case 0: + case 1: + // nothing to scroll + break; + + case 2: + // special case - 2 items + // (empty) -> (empty) + // (empty) -> (empty) + // => item1 -> => item2 + // item2 -> item1 + // (empty) -> (empty) + + index = _selected; + // keep selected item up to date + increment(_selected); + + startUpdate(); + // previously selected item moves down to field4 + writeField(_fItem4, _items[index]); + increment(index); + // other item moves up to field3 + writeField(_fItem3, _items[index]); + endUpdate(); + break; + + case 3: + // special case - 3 items + // (empty) -> (empty) + // item3 -> item1 + // => item1 -> => item2 + // item2 -> item3 + // (empty) -> (empty) + + index = _selected; + // keep selected item up to date + increment(_selected); + + startUpdate(); + // previously selected item moves up to field2 + writeField(_fItem2, _items[index]); + increment(index); + // new selected item moves up to field3 + writeField(_fItem3, _items[index]); + increment(index); + // item from field2 moves down to field4 + writeField(_fItem4, _items[index]); + endUpdate(); + break; + + case 4: + // special case - 4 items + // item3 -> item4 + // item4 -> item1 + // => item1 -> => item2 + // item2 -> item3 + // (empty) -> (empty) + + index = _selected; + // keep selected item up to date + increment(_selected); + + startUpdate(); + // previously selected item moves up to field2 + writeField(_fItem2, _items[index]); + increment(index); + // new selected item moves up to field3 + writeField(_fItem3, _items[index]); + increment(index); + // item from field1 moves down to field4 + writeField(_fItem4, _items[index]); + increment(index); + // item from field2 moves up to field1 + writeField(_fItem1, _items[index]); + endUpdate(); + break; + + default: + // this is the generic case - should handle lists of 5+ items correctly + // item4 -> item5 item6 -> item7 + // item5 -> item1 item7 -> item1 + // => item1 -> => item2 => item1 -> => item2 + // item2 -> item3 item2 -> item3 + // item3 -> item4 item3 -> item4 + + index = _selected; + // keep selected item up to date + increment(_selected); + + startUpdate(); + decrement(index); + // item from field2 moves up to field1 + writeField(_fItem1, _items[index]); + increment(index); + // previously selected item moves up to field2 + writeField(_fItem2, _items[index]); + increment(index); + // new selected item moves up to field3 + writeField(_fItem3, _items[index]); + increment(index); + // item from field5 moves up to field4 + writeField(_fItem4, _items[index]); + increment(index); + // next item (from field1 or off screen) moves up to field5 + writeField(_fItem5, _items[index]); + endUpdate(); + break; + } +} + +std::string LayoutScrollSelect::select() { + std::string selected = ""; + if (_size > 0) + selected = _items[_selected]; + + return selected; +} + +void LayoutScrollSelect::increment(size_t& index) { + if (_size > 1) { + index++; + index %= _size; + } +} + +void LayoutScrollSelect::decrement(size_t& index) { + if (_size > 1) { + if (index == 0) + index = _size - 1; + else + index--; + } +} diff --git a/Layout/LayoutScrollSelect.h b/Layout/LayoutScrollSelect.h new file mode 100644 index 0000000..4811ae5 --- /dev/null +++ b/Layout/LayoutScrollSelect.h @@ -0,0 +1,40 @@ +#ifndef __LAYOUTSCROLLSELECT_H__ +#define __LAYOUTSCROLLSELECT_H__ + +#include "Layout.h" +#include <vector> + +typedef std::vector<std::string> Items; + +class LayoutScrollSelect : public Layout { + public: + LayoutScrollSelect(DOGS102* lcd, Items items, std::string info1 = "", std::string info2 = ""); + ~LayoutScrollSelect(); + + void display(); + + void scroll(); + std::string select(); + + private: + void increment(size_t& index); + void decrement(size_t& index); + + Label _lSw1; + Label _lSw2; + Label _lInfo1; + Label _lInfo2; + Label _lCursor; + + Field _fItem1; + Field _fItem2; + Field _fItem3; + Field _fItem4; + Field _fItem5; + + Items _items; + size_t _selected; + size_t _size; +}; + +#endif diff --git a/Layout/LayoutSingleHelp.cpp b/Layout/LayoutSingleHelp.cpp new file mode 100644 index 0000000..82c4270 --- /dev/null +++ b/Layout/LayoutSingleHelp.cpp @@ -0,0 +1,26 @@ +#include "LayoutSingleHelp.h" + +LayoutSingleHelp::LayoutSingleHelp(DOGS102* lcd) + : Layout(lcd), + _lMode(0, 0, "Survey Single"), + _lIns1(0, 4, "Hold SW1 any time"), + _lIns2(0, 5, "for Main Menu"), + _lSw1(11, 7, "DR/PWR"), + _lSw2(0, 7, "Survey") +{} + +LayoutSingleHelp::~LayoutSingleHelp() {} + +void LayoutSingleHelp::display() { + clear(); + startUpdate(); + + writeLabel(_lMode); + writeLabel(_lIns1); + writeLabel(_lIns2); + writeLabel(_lSw1); + writeLabel(_lSw2); + + endUpdate(); +} + diff --git a/Layout/LayoutSingleHelp.h b/Layout/LayoutSingleHelp.h new file mode 100644 index 0000000..421bc1c --- /dev/null +++ b/Layout/LayoutSingleHelp.h @@ -0,0 +1,21 @@ +#ifndef __LAYOUTSINGLEHELP_H__ +#define __LAYOUTSINGLEHELP_H__ + +#include "Layout.h" + +class LayoutSingleHelp : public Layout { + public: + LayoutSingleHelp(DOGS102* lcd); + ~LayoutSingleHelp(); + + void display(); + + private: + Label _lMode; + Label _lIns1; + Label _lIns2; + Label _lSw1; + Label _lSw2; +}; + +#endif diff --git a/Layout/LayoutStartup.cpp b/Layout/LayoutStartup.cpp index c005b2f..850ea50 100644 --- a/Layout/LayoutStartup.cpp +++ b/Layout/LayoutStartup.cpp @@ -11,15 +11,19 @@ LayoutStartup::LayoutStartup(DOGS102* lcd) _iLogo(0, 0, MultiTech_Logo) {} +LayoutStartup::~LayoutStartup() {} + void LayoutStartup::display() { + std::string version = MTDOT_BOX_VERSION; + clear(); startUpdate(); - writeImage(_iLogo._col, _iLogo._row, _iLogo._bmp); - writeField(_lName._col, _lName._row, _lName._value); - writeField(_lInfo._col, _lInfo._row, _lInfo._value); - writeField(_lVersion._col, _lVersion._row, _lVersion._value); - writeField(_fVersion._col, _fVersion._row, MTDOT_BOX_VERSION, sizeof(MTDOT_BOX_VERSION)); + writeImage(_iLogo); + writeLabel(_lName); + writeLabel(_lInfo); + writeLabel(_lVersion); + writeField(_fVersion, version); endUpdate(); } diff --git a/Layout/LayoutSweepHelp.cpp b/Layout/LayoutSweepHelp.cpp new file mode 100644 index 0000000..eb40013 --- /dev/null +++ b/Layout/LayoutSweepHelp.cpp @@ -0,0 +1,26 @@ +#include "LayoutSweepHelp.h" + +LayoutSweepHelp::LayoutSweepHelp(DOGS102* lcd) + : Layout(lcd), + _lMode(0, 0, "Survey Sweep"), + _lIns1(0, 4, "Hold SW1 any time"), + _lIns2(0, 5, "for Main Menu"), + _lSw1(11, 7, "Cancel"), + _lSw2(0, 7, "Sweep") +{} + +LayoutSweepHelp::~LayoutSweepHelp() {} + +void LayoutSweepHelp::display() { + clear(); + startUpdate(); + + writeLabel(_lMode); + writeLabel(_lIns1); + writeLabel(_lIns2); + writeLabel(_lSw1); + writeLabel(_lSw2); + + endUpdate(); +} + diff --git a/Layout/LayoutSweepHelp.h b/Layout/LayoutSweepHelp.h new file mode 100644 index 0000000..0ec0e45 --- /dev/null +++ b/Layout/LayoutSweepHelp.h @@ -0,0 +1,21 @@ +#ifndef __LAYOUTSWEEPHELP_H__ +#define __LAYOUTSWEEPHELP_H__ + +#include "Layout.h" + +class LayoutSweepHelp : public Layout { + public: + LayoutSweepHelp(DOGS102* lcd); + ~LayoutSweepHelp(); + + void display(); + + private: + Label _lMode; + Label _lIns1; + Label _lIns2; + Label _lSw1; + Label _lSw2; +}; + +#endif diff --git a/LoRaHandler/LoRaHandler.cpp b/LoRaHandler/LoRaHandler.cpp new file mode 100644 index 0000000..27eb266 --- /dev/null +++ b/LoRaHandler/LoRaHandler.cpp @@ -0,0 +1,160 @@ +#include "LoRaHandler.h" + +#define signal (int32_t)0xA0 + +typedef enum { + l_none = 0, + l_ping, + l_send, + l_join +} InternalLoRa; + +uint8_t cmd = l_none; +std::vector<uint8_t> send_data; + +void l_worker(void const* argument) { + LoRaHandler* l = (LoRaHandler*)argument; + osEvent e; + + l->_dot = mDot::getInstance(); + int32_t ret; + mDot::ping_response pr; + mDot::rssi_stats rs; + mDot::snr_stats ss; + + while (true) { + e = Thread::signal_wait(signal); + if (e.status == osEventSignal) { + l->_status = LoRaHandler::busy; + switch (cmd) { + case l_ping: + l->_mutex.lock(); + pr = l->_dot->ping(); + l->_mutex.unlock(); + if (pr.status == mDot::MDOT_OK) { + l->_ping.up = pr; + l->_mutex.lock(); + rs = l->_dot->getRssiStats(); + ss = l->_dot->getSnrStats(); + l->_mutex.unlock(); + l->_ping.down.rssi = rs.last; + l->_ping.down.snr = ss.last; + l->_status = LoRaHandler::ping_success; + } else { + l->_status = LoRaHandler::ping_failure; + } + osSignalSet(l->_main, loraSignal); + break; + + case l_send: + l->_mutex.lock(); + ret = l->_dot->send(send_data); + l->_mutex.unlock(); + if (ret == mDot::MDOT_OK) + l->_status = LoRaHandler::send_success; + else + l->_status = LoRaHandler::send_failure; + osSignalSet(l->_main, loraSignal); + break; + + case l_join: + l->_mutex.lock(); + ret = l->_dot->joinNetworkOnce(); + l->_mutex.unlock(); + if (ret == mDot::MDOT_OK) { + l->_status = LoRaHandler::join_success; + } else { + l->_status = LoRaHandler::join_failure; + } + osSignalSet(l->_main, loraSignal); + break; + + default: + l->_status = LoRaHandler::none; + break; + } + } + } +} + +LoRaHandler::LoRaHandler(osThreadId main) + : _main(main), + _thread(l_worker, (void*)this), + _status(none) +{ + _ping.status = false; +} + +bool LoRaHandler::setDataRate(uint8_t rate) { + int32_t res; + _mutex.lock(); + res = _dot->setTxDataRate(rate); + _mutex.unlock(); + if (res == mDot::MDOT_OK) + return true; + + return false; +} + +bool LoRaHandler::setPower(uint32_t power) { + int32_t res; + _mutex.lock(); + res = _dot->setTxPower(power); + _mutex.unlock(); + if (res == mDot::MDOT_OK) + return true; + + return false; +} + +bool LoRaHandler::ping() { + return action(l_ping); +} + +bool LoRaHandler::send(std::vector<uint8_t> data) { + send_data = data; + return action(l_send); +} + +bool LoRaHandler::join() { + return action(l_join); +} + +bool LoRaHandler::action(uint8_t c) { + if (_status != busy) { + cmd = c; + _thread.signal_set(signal); + _thread.signal_clr(signal); + return true; + } + + return false; +} + +LoRaHandler::LoRaStatus LoRaHandler::getStatus() { + LoRaStatus status; + _mutex.lock(); + status = _status; + _mutex.unlock(); + + return status; +} + +LoRaHandler::LoRaPing LoRaHandler::getPingResults() { + LoRaPing ping; + _mutex.lock(); + ping = _ping; + _mutex.unlock(); + + return ping; +} + +uint32_t LoRaHandler::getNextTx() { + uint32_t ms; + _mutex.lock(); + ms = _dot->getNextTxMs(); + _mutex.unlock(); + + return ms; +} + diff --git a/LoRaHandler/LoRaHandler.h b/LoRaHandler/LoRaHandler.h new file mode 100644 index 0000000..20f9d6f --- /dev/null +++ b/LoRaHandler/LoRaHandler.h @@ -0,0 +1,50 @@ +#ifndef __LORAHANDLER_H__ +#define __LORAHANDLER_H__ + +#include "mDot.h" + +#define loraSignal (int32_t)0x02 + +class LoRaHandler { + public: + typedef enum { + none = 0, + busy, + ping_success, + send_success, + join_success, + ping_failure, + send_failure, + join_failure + } LoRaStatus; + + typedef struct { + bool status; + mDot::ping_response up; + mDot::ping_response down; + } LoRaPing; + + LoRaHandler(osThreadId main); + ~LoRaHandler(); + + bool setDataRate(uint8_t rate); + bool setPower(uint32_t power); + bool ping(); + bool send(std::vector<uint8_t> data); + bool join(); + bool action(uint8_t cmd); + LoRaStatus getStatus(); + LoRaPing getPingResults(); + uint32_t getNextTx(); + + + osThreadId _main; + Thread _thread; + LoRaStatus _status; + LoRaPing _ping; + mDot* _dot; + Mutex _mutex; +}; + +#endif + diff --git a/Mode/Mode.cpp b/Mode/Mode.cpp new file mode 100644 index 0000000..eea6d3c --- /dev/null +++ b/Mode/Mode.cpp @@ -0,0 +1,19 @@ +#include "Mode.h" + +Mode::Mode(DOGS102* lcd, ButtonHandler* buttons) + : _lcd(lcd), + _buttons(buttons), + _index(0), + _main_id(Thread::gettid()) +{} + +Mode::~Mode() {} + +bool Mode::deleteDataFile() { + return true; +} + +bool Mode::appendDataFile(const DataItem& data) { + return true; +} + diff --git a/Mode/Mode.h b/Mode/Mode.h new file mode 100644 index 0000000..1254a9e --- /dev/null +++ b/Mode/Mode.h @@ -0,0 +1,47 @@ +#ifndef __MODE_H__ +#define __MODE_H__ + +#include "DOGS102.h" +#include "ButtonHandler.h" +#include "GPSPARSER.h" + +class Mode { + public: + typedef enum { + single = 0, + sweep + } DataType; + + typedef struct { + DataType type; + int32_t index; + bool status; + int32_t lock; + GPSPARSER::longitude gps_longitude; + GPSPARSER::latitude gps_latitude; + int16_t gps_altitude; + struct tm gps_time; + int16_t rssi_up; + int16_t snr_up; + int16_t rssi_down; + int16_t snr_down; + uint8_t data_rate; + uint8_t power; + } DataItem; + + Mode(DOGS102* lcd, ButtonHandler* buttons); + ~Mode(); + + virtual bool start() = 0; + + protected: + bool deleteDataFile(); + bool appendDataFile(const DataItem& data); + + DOGS102* _lcd; + ButtonHandler* _buttons; + uint32_t _index; + osThreadId _main_id; +}; + +#endif diff --git a/Mode/ModeJoin.cpp b/Mode/ModeJoin.cpp new file mode 100644 index 0000000..f1f0e9a --- /dev/null +++ b/Mode/ModeJoin.cpp @@ -0,0 +1,86 @@ +#include "ModeJoin.h" +#include "MTSLog.h" +#include "MTSText.h" + +ModeJoin::ModeJoin(DOGS102* lcd, ButtonHandler* buttons, mDot* dot, LoRaHandler* lora, uint8_t band) + : Mode(lcd, buttons), + _lj(lcd, band), + _dot(dot), + _lora(lora), + _band(band), + _data_rate(band == mDot::FB_915 ? mDot::SF_10 : mDot::SF_12), + _power(20), + _next_tx(0), + _joined(false) +{} + +ModeJoin::~ModeJoin() {} + +bool ModeJoin::start() { + // clear any stale signals + osSignalClear(_main_id, buttonSignal | loraSignal); + + _lj.display(); + _lj.updateStatus("Joining..."); + if (_dot->getJoinMode() == mDot::MANUAL) { + _lj.updateId(mts::Text::bin2hexString(_dot->getNetworkId())); + _lj.updateKey(mts::Text::bin2hexString(_dot->getNetworkKey())); + } else { + _lj.updateId(_dot->getNetworkName()); + _lj.updateKey(_dot->getNetworkPassphrase()); + } + if (_band == mDot::FB_915) { + _sub_band = _dot->getFrequencySubBand(); + _lj.updateFsb(_sub_band); + } + // mDot::DataRateStr returns format SF_XX - we only want to display the XX part + _lj.updateRate(_dot->DataRateStr(_data_rate).substr(3)); + _lj.updatePower(_power); + + _lora->setDataRate(_data_rate); + _lora->setPower(_power); + + while (! _joined) { + _next_tx = _lora->getNextTx(); + if (_next_tx) { + _lj.updateCountdown(_next_tx * 1000); + } else { + _lj.updateAttempt(++_index); + _lj.updateStatus("Joining..."); + _lora->join(); + } + + osEvent e = Thread::signal_wait(0); + if (e.status == osEventSignal) { + if (e.value.signals & buttonSignal) { + _be = _buttons->getButtonEvent(); + switch (_be) { + case ButtonHandler::sw1_press: + return false; + case ButtonHandler::sw2_press: + break; + case ButtonHandler::sw1_hold: + return false; + } + } + if (e.value.signals & loraSignal) { + _ls = _lora->getStatus(); + switch (_ls) { + case LoRaHandler::join_success: + _lj.updateStatus("Join Success!"); + _lj.displayCancel(false); + logInfo("joined"); + _joined = true; + osDelay(2000); + return true; + + case LoRaHandler::join_failure: + logInfo("failed to join"); + break; + } + } + } + } + + return false; +} diff --git a/Mode/ModeJoin.h b/Mode/ModeJoin.h new file mode 100644 index 0000000..5b88e25 --- /dev/null +++ b/Mode/ModeJoin.h @@ -0,0 +1,30 @@ +#ifndef __MODEJOIN_H__ +#define __MODEJOIN_H__ + +#include "Mode.h" +#include "LayoutJoin.h" +#include "mDot.h" +#include "LoRaHandler.h" + +class ModeJoin : public Mode { + public: + ModeJoin(DOGS102* lcd, ButtonHandler* buttons, mDot* dot, LoRaHandler* lora, uint8_t band); + ~ModeJoin(); + + bool start(); + + private: + LayoutJoin _lj; + mDot* _dot; + LoRaHandler* _lora; + uint8_t _band; + uint8_t _sub_band; + uint8_t _data_rate; + uint8_t _power; + uint32_t _next_tx; + bool _joined; + ButtonHandler::ButtonEvent _be; + LoRaHandler::LoRaStatus _ls; +}; + +#endif @@ -4,7 +4,7 @@ // MTS headers #include "mDot.h" #include "MTSLog.h" -#include "CommandTerminal.h" +#include "MTSText.h" // sensor headers #include "ISL29011.h" #include "MMA845x.h" @@ -14,14 +14,20 @@ #include "DOGS102.h" #include "NCP5623B.h" #include "LayoutStartup.h" +#include "LayoutScrollSelect.h" +#include "LayoutConfig.h" +#include "LayoutDemoHelp.h" +#include "LayoutSingleHelp.h" +#include "LayoutSweepHelp.h" // button header #include "ButtonHandler.h" +// LoRa header +#include "LoRaHandler.h" +// mode objects +#include "ModeJoin.h" // misc heders #include <string> -// only here for button handling example code in main -#include "font_6x8.h" - // LCD and backlight controllers SPI lcd_spi(SPI1_MOSI, SPI1_MISO, SPI1_SCK); I2C backlight_i2c(I2C_SDA, I2C_SCL); @@ -30,29 +36,93 @@ DigitalOut lcd_cd(XBEE_ON_SLEEP, 1); DOGS102* lcd; NCP5623B* lcd_backlight; +// Thread informaiton +osThreadId main_id; + // Button controller ButtonHandler* buttons; +// LoRa controller +LoRaHandler* lora; +mDot* dot; + +// Modes +ModeJoin* modeJoin; + // Serial debug port Serial debug(USBTX, USBRX); mts::MTSSerial serial(USBTX, USBRX, 512, 512); +// Prototypes +void mainMenu(); +void join(); +void configuration(); +void loraDemo(); +void surveySingle(); +void surveySweep(); + int main() { debug.baud(115200); - MTSLog::setLogLevel(MTSLog::TRACE_LEVEL); lcd = new DOGS102(lcd_spi, lcd_spi_cs, lcd_cd); lcd_backlight = new NCP5623B(backlight_i2c); + main_id = Thread::gettid(); + buttons = new ButtonHandler(main_id); + dot = mDot::getInstance(); + lora = new LoRaHandler(main_id); + + modeJoin = new ModeJoin(lcd, buttons, dot, lora, dot->getFrequencyBand()); + // display startup screen for 3 seconds LayoutStartup ls(lcd); ls.display(); osDelay(3000); - osThreadId main_id = Thread::gettid(); - buttons = new ButtonHandler(main_id); + // start of temporary stuff! + if (dot->getFrequencyBand() == mDot::FB_915) + dot->setFrequencySubBand(mDot::FSB_7); + dot->setJoinMode(mDot::OTA); + dot->setNetworkName("mikes_lora_network"); + dot->setNetworkPassphrase("password_123"); + dot->setAck(1); + // end of temporary stuff! + + //MTSLog::setLogLevel(MTSLog::TRACE_LEVEL); + MTSLog::setLogLevel(MTSLog::INFO_LEVEL); + logInfo("displaying main menu"); + mainMenu(); + + return 0; +} + +void mainMenu() { + bool mode_selected = false; + std::string selected; + + typedef enum { + demo = 2, + config, + single, + sweep + } menu_items; + + std::string menu_strings[] = { + "MultiTech EVB", + "Select Mode", + "LoRa Demo", + "Configuration", + "Survey Single", + "Survey Sweep" + }; + + std::vector<std::string> items; + items.push_back(menu_strings[demo]); + items.push_back(menu_strings[config]); + items.push_back(menu_strings[single]); + items.push_back(menu_strings[sweep]); @@ -69,33 +139,157 @@ int main() { while (true) { - char buf[16]; - size_t size; + LayoutScrollSelect menu(lcd, items, menu_strings[0], menu_strings[1]); + menu.display(); + + while (! mode_selected) { + osEvent e = Thread::signal_wait(buttonSignal); + if (e.status == osEventSignal) { + ButtonHandler::ButtonEvent ev = buttons->getButtonEvent(); + switch (ev) { + case ButtonHandler::sw1_press: + selected = menu.select(); + mode_selected = true; + break; + case ButtonHandler::sw2_press: + menu.scroll(); + break; + case ButtonHandler::sw1_hold: + break; + default: + break; + } + } + } + + if (selected == menu_strings[demo]) { + if (modeJoin->start()) + loraDemo(); + } else if (selected == menu_strings[config]) { + configuration(); + } else if (selected == menu_strings[single]) { + if (modeJoin->start()) + surveySingle(); + } else if (selected == menu_strings[sweep]) { + if (modeJoin->start()) + surveySweep(); + } + + mode_selected = false; + } +} + +void configuration() { + LayoutConfig lc(lcd); + + // clear any stale signals + osSignalClear(main_id, buttonSignal | loraSignal); + + lc.display(); + logInfo("config mode"); + + while (true) { + osEvent e = Thread::signal_wait(buttonSignal); + if (e.status == osEventSignal) { + ButtonHandler::ButtonEvent ev = buttons->getButtonEvent(); + switch (ev) { + case ButtonHandler::sw1_press: + break; + case ButtonHandler::sw2_press: + break; + case ButtonHandler::sw1_hold: + return; + default: + break; + } + } + } +} + +void loraDemo() { + LayoutDemoHelp ldh(lcd); + + // clear any stale signals + osSignalClear(main_id, buttonSignal | loraSignal); + ldh.display(); + logInfo("demo mode"); + + while (true) { osEvent e = Thread::signal_wait(buttonSignal); if (e.status == osEventSignal) { - ButtonEvent ev = buttons->getButtonEvent(); + ButtonHandler::ButtonEvent ev = buttons->getButtonEvent(); switch (ev) { - case sw1_press: - logInfo("SW1 press"); - size = snprintf(buf, sizeof(buf), "SW1 press"); + case ButtonHandler::sw1_press: + logInfo("trigger TX mode"); break; - case sw1_hold: - logInfo("SW1 hold"); - size = snprintf(buf, sizeof(buf), "SW1 hold"); + case ButtonHandler::sw2_press: + logInfo("interval TX mode"); break; - case sw2_press: - logInfo("SW2 press"); - size = snprintf(buf, sizeof(buf), "SW2 press"); + case ButtonHandler::sw1_hold: + return; + default: break; } + } + } +} - lcd->clearBuffer(); - lcd->startUpdate(); - lcd->writeText(0, 0, font_6x8, buf, size); - lcd->endUpdate(); +void surveySingle() { + LayoutSingleHelp lsh(lcd); + + // clear any stale signals + osSignalClear(main_id, buttonSignal | loraSignal); + + lsh.display(); + logInfo("survey single mode"); + + while (true) { + osEvent e = Thread::signal_wait(buttonSignal); + if (e.status == osEventSignal) { + ButtonHandler::ButtonEvent ev = buttons->getButtonEvent(); + switch (ev) { + case ButtonHandler::sw1_press: + logInfo("datarate/power"); + break; + case ButtonHandler::sw2_press: + logInfo("start survey"); + break; + case ButtonHandler::sw1_hold: + return; + default: + break; + } } } +} - return 0; +void surveySweep() { + LayoutSweepHelp lsh(lcd); + + // clear any stale signals + osSignalClear(main_id, buttonSignal | loraSignal); + + lsh.display(); + logInfo("survey sweep mode"); + + while (true) { + osEvent e = Thread::signal_wait(buttonSignal); + if (e.status == osEventSignal) { + ButtonHandler::ButtonEvent ev = buttons->getButtonEvent(); + switch (ev) { + case ButtonHandler::sw1_press: + logInfo("cancel"); + break; + case ButtonHandler::sw2_press: + logInfo("start sweep"); + break; + case ButtonHandler::sw1_hold: + return; + default: + break; + } + } + } } + |