diff --git a/.github/ISSUE_TEMPLATE/Bug Report.yml b/.github/ISSUE_TEMPLATE/Bug Report.yml index d4e00b4ed..7fe42051c 100644 --- a/.github/ISSUE_TEMPLATE/Bug Report.yml +++ b/.github/ISSUE_TEMPLATE/Bug Report.yml @@ -37,7 +37,9 @@ body: - T-Lora v1 - T-Lora v1.3 - T-Lora v2 1.6 + - T-Deck - T-Echo + - T-Watch - Rak4631 - Rak11200 - Rak11310 @@ -45,6 +47,8 @@ body: - Heltec v2 - Heltec v2.1 - Heltec V3 + - Heltec Wireless Paper + - Heltec Wireless Tracker - Raspberry Pi Pico (W) - Relay v1 - Relay v2 diff --git a/.github/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml index 7996a9b1b..c9664152e 100644 --- a/.github/workflows/build_esp32.yml +++ b/.github/workflows/build_esp32.yml @@ -17,11 +17,11 @@ jobs: uses: ./.github/actions/setup-base - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master + uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 with: - repo: "meshtastic/web" - file: "build.tar" - target: "build.tar" + repo: meshtastic/web + file: build.tar + target: build.tar token: ${{ secrets.GITHUB_TOKEN }} - name: Unpack web ui @@ -40,11 +40,11 @@ jobs: run: bin/build-esp32.sh ${{ inputs.board }} - name: Pull OTA Firmware - uses: dsaltares/fetch-gh-release-asset@master + uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 with: - repo: "meshtastic/firmware-ota" - file: "firmware.bin" - target: "release/bleota.bin" + repo: meshtastic/firmware-ota + file: firmware.bin + target: release/bleota.bin token: ${{ secrets.GITHUB_TOKEN }} - name: Get release version string diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml index 7f556a991..9611dd5b8 100644 --- a/.github/workflows/build_esp32_s3.yml +++ b/.github/workflows/build_esp32_s3.yml @@ -17,11 +17,11 @@ jobs: uses: ./.github/actions/setup-base - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master + uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 with: - repo: "meshtastic/web" - file: "build.tar" - target: "build.tar" + repo: meshtastic/web + file: build.tar + target: build.tar token: ${{ secrets.GITHUB_TOKEN }} - name: Unpack web ui @@ -38,11 +38,11 @@ jobs: run: bin/build-esp32.sh ${{ inputs.board }} - name: Pull OTA Firmware - uses: dsaltares/fetch-gh-release-asset@master + uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 with: - repo: "meshtastic/firmware-ota" - file: "firmware-s3.bin" - target: "release/bleota-s3.bin" + repo: meshtastic/firmware-ota + file: firmware-s3.bin + target: release/bleota-s3.bin token: ${{ secrets.GITHUB_TOKEN }} - name: Get release version string diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index acc07dec9..b4a8a4739 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -1,4 +1,7 @@ name: CI +#concurrency: +# group: ${{ github.ref }} +# cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} on: # # Triggers the workflow on push but only for the master branch push: @@ -46,7 +49,7 @@ jobs: - name: Trunk Check if: ${{ github.event_name != 'workflow_dispatch' }} - uses: trunk-io/trunk-action@v1 + uses: trunk-io/trunk-action@782e83f803ca6e369f035d64c6ba2768174ba61b - name: Check ${{ matrix.board }} run: bin/check-all.sh ${{ matrix.board }} @@ -255,6 +258,7 @@ jobs: retention-days: 30 - name: Create request artifacts + continue-on-error: true # FIXME: Why are we getting 502, but things still work? if: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request' }} uses: gavv/pull-request-artifacts@v1.1.0 with: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index d9d52a2a4..da59bc0fd 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -14,6 +14,6 @@ jobs: uses: actions/checkout@v3 - name: Trunk Check - uses: trunk-io/trunk-action@v1 + uses: trunk-io/trunk-action@782e83f803ca6e369f035d64c6ba2768174ba61b with: trunk-token: ${{ secrets.TRUNK_TOKEN }} diff --git a/bin/device-install.bat b/bin/device-install.bat index 4d14193e5..c7d8a10cf 100755 --- a/bin/device-install.bat +++ b/bin/device-install.bat @@ -32,7 +32,7 @@ IF EXIST %FILENAME% IF x%FILENAME:update=%==x%FILENAME% ( %PYTHON% -m esptool --baud 115200 write_flash 0x00 %FILENAME% @REM Account for S3 board's different OTA partition - IF x%FILENAME:s3=%==x%FILENAME% IF x%FILENAME:v3=%==x%FILENAME% ( + IF x%FILENAME:s3=%==x%FILENAME% IF x%FILENAME:v3=%==x%FILENAME% IF x%FILENAME:t-deck=%==x%FILENAME% IF x%FILENAME:wireless-paper=%==x%FILENAME% IF x%FILENAME:wireless-tracker=%==x%FILENAME% ( %PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota.bin ) else ( %PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota-s3.bin diff --git a/bin/device-install.sh b/bin/device-install.sh index cd5d6ad59..35d99286d 100755 --- a/bin/device-install.sh +++ b/bin/device-install.sh @@ -50,7 +50,7 @@ if [ -f "${FILENAME}" ] && [ ! -z "${FILENAME##*"update"*}" ]; then "$PYTHON" -m esptool erase_flash "$PYTHON" -m esptool write_flash 0x00 ${FILENAME} # Account for S3 board's different OTA partition - if [ ! -z "${FILENAME##*"s3"*}" ] && [ ! -z "${FILENAME##*"-v3"*}" ]; then + if [ ! -z "${FILENAME##*"s3"*}" ] && [ ! -z "${FILENAME##*"-v3"*}" ] && [ ! -z "${FILENAME##*"t-deck"*}" ] && [ ! -z "${FILENAME##*"wireless-paper"*}" ] && [ ! -z "${FILENAME##*"wireless-tracker"*}" ]; then "$PYTHON" -m esptool write_flash 0x260000 bleota.bin else "$PYTHON" -m esptool write_flash 0x260000 bleota-s3.bin diff --git a/boards/nano-g2-ultra.json b/boards/nano-g2-ultra.json new file mode 100644 index 000000000..7afce178b --- /dev/null +++ b/boards/nano-g2-ultra.json @@ -0,0 +1,51 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A", "0x8029"], + ["0x239A", "0x0029"], + ["0x239A", "0x002A"], + ["0x239A", "0x802A"] + ], + "usb_product": "BQ nRF52840", + "mcu": "nrf52840", + "variant": "nano-g2-ultra", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": ["bluetooth"], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": ["arduino"], + "name": "BQ nRF52840", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra", + "vendor": "BQ Consulting" +} diff --git a/protobufs b/protobufs index f17298c2b..57bd75ea8 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit f17298c2b093ac0d2536642b508f6cf84771b172 +Subproject commit 57bd75ea8b3c4fe551dcaf1dcd402646878176a8 diff --git a/src/ButtonThread.h b/src/ButtonThread.h index fcbb73af0..255ab5162 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -101,23 +101,8 @@ class ButtonThread : public concurrency::OSThread #endif // if (!canSleep) LOG_DEBUG("Suppressing sleep!\n"); // else LOG_DEBUG("sleep ok\n"); -#if defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) - int x, y = 0; - screen->getTouch(&x, &y); - if (x > 0 && y > 0) { -#ifdef T_WATCH_S3 - drv.setWaveform(0, 75); - drv.setWaveform(1, 0); // end waveform - drv.go(); -#endif - LOG_DEBUG("touch %d %d\n", x, y); - powerFSM.trigger(EVENT_PRESS); - return 150; // Check for next touch every in 150ms - } -#endif - - return 5; + return 50; } private: diff --git a/src/commands.h b/src/commands.h index 7c7595143..03ede5982 100644 --- a/src/commands.h +++ b/src/commands.h @@ -15,4 +15,6 @@ enum class Cmd { PRINT, START_SHUTDOWN_SCREEN, START_REBOOT_SCREEN, + SHOW_PREV_FRAME, + SHOW_NEXT_FRAME }; \ No newline at end of file diff --git a/src/configuration.h b/src/configuration.h index 3e289ef54..aa9064251 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -98,8 +98,9 @@ along with this program. If not, see . // Define if screen should be mirrored left to right // #define SCREEN_MIRROR -// The m5stack I2C Keyboard (also RAK14004) +// I2C Keyboards (M5Stack, RAK14004, T-Deck) #define CARDKB_ADDR 0x5F +#define TDECK_KB_ADDR 0x55 // ----------------------------------------------------------------------------- // SENSOR @@ -173,6 +174,12 @@ along with this program. If not, see . #ifndef HAS_BUTTON #define HAS_BUTTON 0 #endif +#ifndef HAS_TRACKBALL +#define HAS_TRACKBALL 0 +#endif +#ifndef HAS_TOUCHSCREEN +#define HAS_TOUCHSCREEN 0 +#endif #ifndef HAS_TELEMETRY #define HAS_TELEMETRY 0 #endif diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp index 75b23f419..996bdf62a 100644 --- a/src/detect/ScanI2C.cpp +++ b/src/detect/ScanI2C.cpp @@ -30,8 +30,8 @@ ScanI2C::FoundDevice ScanI2C::firstRTC() const ScanI2C::FoundDevice ScanI2C::firstKeyboard() const { - ScanI2C::DeviceType types[] = {CARDKB, RAK14004}; - return firstOfOrNONE(2, types); + ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, RAK14004}; + return firstOfOrNONE(3, types); } ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 559cff7ec..418a6bf5e 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -16,6 +16,7 @@ class ScanI2C RTC_RV3028, RTC_PCF8563, CARDKB, + TDECKKB, RAK14004, PMU_AXP192_AXP2101, BME_680, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 66e092951..30f9e7b7c 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -212,6 +212,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port) } break; + SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard found\n"); SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found\n"); #ifdef HAS_NCP5623 SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found\n"); diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index d1cc5ad36..fbd99d252 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -31,6 +31,7 @@ along with this program. If not, see . #include "gps/GeoCoord.h" #include "gps/RTC.h" #include "graphics/images.h" +#include "input/TouchScreenImpl1.h" #include "main.h" #include "mesh-pb-constants.h" #include "mesh/Channels.h" @@ -835,7 +836,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ } static char distStr[20]; - strncpy(distStr, "? km", sizeof(distStr)); // might not have location data + if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { + strncpy(distStr, "? mi", sizeof(distStr)); // might not have location data + } else { + strncpy(distStr, "? km", sizeof(distStr)); + } meshtastic_NodeInfoLite *ourNode = nodeDB.getMeshNode(nodeDB.getNodeNum()); const char *fields[] = {username, distStr, signalStr, lastStr, NULL}; int16_t compassX = 0, compassY = 0; @@ -1040,12 +1045,18 @@ void Screen::setup() #endif serialSinceMsec = millis(); +#if HAS_TOUCHSCREEN + touchScreenImpl1 = new TouchScreenImpl1(dispdev.getWidth(), dispdev.getHeight(), dispdev.getTouch); + touchScreenImpl1->init(); +#endif + // Subscribe to status updates powerStatusObserver.observe(&powerStatus->onNewStatus); gpsStatusObserver.observe(&gpsStatus->onNewStatus); nodeStatusObserver.observe(&nodeStatus->onNewStatus); if (textMessageModule) textMessageObserver.observe(textMessageModule); + inputObserver.observe(inputBroker); // Modules can notify screen about refresh MeshModule::observeUIEvents(&uiFrameEventObserver); @@ -1123,6 +1134,12 @@ int32_t Screen::runOnce() handleOnPress(); } break; + case Cmd::SHOW_PREV_FRAME: + handleShowPrevFrame(); + break; + case Cmd::SHOW_NEXT_FRAME: + handleShowNextFrame(); + break; case Cmd::START_BLUETOOTH_PIN_SCREEN: handleStartBluetoothPinScreen(cmd.bluetooth_pin); break; @@ -1416,6 +1433,28 @@ void Screen::handleOnPress() } } +void Screen::handleShowPrevFrame() +{ + // If screen was off, just wake it, otherwise go back to previous frame + // If we are in a transition, the press must have bounced, drop it. + if (ui.getUiState()->frameState == FIXED) { + ui.previousFrame(); + lastScreenTransition = millis(); + setFastFramerate(); + } +} + +void Screen::handleShowNextFrame() +{ + // If screen was off, just wake it, otherwise advance to next frame + // If we are in a transition, the press must have bounced, drop it. + if (ui.getUiState()->frameState == FIXED) { + ui.nextFrame(); + lastScreenTransition = millis(); + setFastFramerate(); + } +} + #ifndef SCREEN_TRANSITION_FRAMERATE #define SCREEN_TRANSITION_FRAMERATE 30 // fps #endif @@ -1853,6 +1892,20 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event) return 0; } +int Screen::handleInputEvent(const InputEvent *event) +{ + if (showingNormalScreen && moduleFrames.size() == 0) { + LOG_DEBUG("Screen::handleInputEvent from %s\n", event->source); + if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) { + showPrevFrame(); + } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) { + showNextFrame(); + } + } + + return 0; +} + } // namespace graphics #else graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 9ebe1c75a..8812b7c70 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -53,6 +53,7 @@ class Screen #include "commands.h" #include "concurrency/LockGuard.h" #include "concurrency/OSThread.h" +#include "input/InputBroker.h" #include "mesh/MeshModule.h" #include "power.h" #include @@ -118,6 +119,8 @@ class Screen : public concurrency::OSThread CallbackObserver(this, &Screen::handleTextMessage); CallbackObserver uiFrameEventObserver = CallbackObserver(this, &Screen::handleUIFrameEvent); + CallbackObserver inputObserver = + CallbackObserver(this, &Screen::handleInputEvent); public: explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY); @@ -152,8 +155,10 @@ class Screen : public concurrency::OSThread void blink(); - /// Handles a button press. + /// Handle button press, trackball or swipe action) void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); } + void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); } + void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); } // Implementation to Adjust Brightness void adjustBrightness(); @@ -301,9 +306,11 @@ class Screen : public concurrency::OSThread // Use this handle to set things like battery status, user count, GPS status, etc. DebugInfo *debug_info() { return &debugInfo; } + // Handle observer events int handleStatusUpdate(const meshtastic::Status *arg); int handleTextMessage(const meshtastic_MeshPacket *arg); int handleUIFrameEvent(const UIFrameEvent *arg); + int handleInputEvent(const InputEvent *arg); /// Used to force (super slow) eink displays to draw critical frames void forceDisplay(); @@ -313,13 +320,6 @@ class Screen : public concurrency::OSThread void setWelcomeFrames(); - void getTouch(int *x, int *y) - { -#if defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) - dispdev.getTouch(x, y); -#endif - }; - protected: /// Updates the UI. // @@ -350,6 +350,8 @@ class Screen : public concurrency::OSThread // Implementations of various commands, called from doTask(). void handleSetOn(bool on); void handleOnPress(); + void handleShowNextFrame(); + void handleShowPrevFrame(); void handleStartBluetoothPinScreen(uint32_t pin); void handlePrint(const char *text); void handleStartFirmwareUpdateScreen(); diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 1f67f90c9..4c9196b2f 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -174,7 +174,7 @@ class LGFX : public lgfx::LGFX_Device auto cfg = _light_instance.config(); // Gets a structure for backlight settings. cfg.pin_bl = ST7789_BL; // Pin number to which the backlight is connected - cfg.invert = true; // true to invert the brightness of the backlight + cfg.invert = false; // true to invert the brightness of the backlight // cfg.pwm_channel = 0; _light_instance.config(cfg); @@ -196,7 +196,7 @@ class LGFX : public lgfx::LGFX_Device // cfg.freq = 2500000; // I2C - cfg.i2c_port = 1; + cfg.i2c_port = TOUCH_I2C_PORT; cfg.i2c_addr = TOUCH_SLAVE_ADDRESS; #ifdef SCREEN_TOUCH_USE_I2C1 cfg.pin_sda = I2C_SDA1; @@ -205,7 +205,7 @@ class LGFX : public lgfx::LGFX_Device cfg.pin_sda = I2C_SDA; cfg.pin_scl = I2C_SCL; #endif - cfg.freq = 400000; + // cfg.freq = 400000; _touch_instance.config(cfg); _panel_instance.setTouch(&_touch_instance); @@ -275,6 +275,9 @@ void TFTDisplay::sendCommand(uint8_t com) #endif #ifdef VTFT_CTRL digitalWrite(VTFT_CTRL, LOW); +#endif +#ifndef M5STACK + tft.setBrightness(128); #endif break; } @@ -284,6 +287,9 @@ void TFTDisplay::sendCommand(uint8_t com) #endif #ifdef VTFT_CTRL digitalWrite(VTFT_CTRL, HIGH); +#endif +#ifndef M5STACK + tft.setBrightness(0); #endif break; } @@ -294,6 +300,24 @@ void TFTDisplay::sendCommand(uint8_t com) // Drop all other commands to device (we just update the buffer) } +bool TFTDisplay::hasTouch(void) +{ +#ifndef M5STACK + return tft.touch() != nullptr; +#else + return false; +#endif +} + +bool TFTDisplay::getTouch(int16_t *x, int16_t *y) +{ +#ifndef M5STACK + return tft.getTouch(x, y); +#else + return false; +#endif +} + void TFTDisplay::setDetected(uint8_t detected) { (void)detected; @@ -322,12 +346,4 @@ bool TFTDisplay::connect() return true; } -// Get touch coords from the display -void TFTDisplay::getTouch(int *x, int *y) -{ -#ifndef M5STACK - tft.getTouch(x, y); -#endif -} - #endif \ No newline at end of file diff --git a/src/graphics/TFTDisplay.h b/src/graphics/TFTDisplay.h index 03293d6f4..325765b1f 100644 --- a/src/graphics/TFTDisplay.h +++ b/src/graphics/TFTDisplay.h @@ -22,14 +22,16 @@ class TFTDisplay : public OLEDDisplay // Write the buffer to the display memory virtual void display(void) override; + // Touch screen (static handlers) + static bool hasTouch(void); + static bool getTouch(int16_t *x, int16_t *y); + /** * shim to make the abstraction happy * */ void setDetected(uint8_t detected); - void getTouch(int *x, int *y); - protected: // the header size of the buffer used, e.g. for the SPI command header virtual int getBufferOffset(void) override { return 0; } diff --git a/src/input/TouchScreenBase.cpp b/src/input/TouchScreenBase.cpp new file mode 100644 index 000000000..dad1bb56c --- /dev/null +++ b/src/input/TouchScreenBase.cpp @@ -0,0 +1,137 @@ +#include "TouchScreenBase.h" +#include "main.h" + +#ifndef TIME_LONG_PRESS +#define TIME_LONG_PRESS 400 +#endif + +// move a minimum distance over the screen to detect a "swipe" +#ifndef TOUCH_THRESHOLD_X +#define TOUCH_THRESHOLD_X 30 +#endif + +#ifndef TOUCH_THRESHOLD_Y +#define TOUCH_THRESHOLD_Y 20 +#endif + +TouchScreenBase::TouchScreenBase(const char *name, uint16_t width, uint16_t height) + : concurrency::OSThread(name), _display_width(width), _display_height(height), _first_x(0), _last_x(0), _first_y(0), + _last_y(0), _start(0), _tapped(false), _originName(name) +{ +} + +void TouchScreenBase::init(bool hasTouch) +{ + if (hasTouch) { + LOG_INFO("TouchScreen initialized %d %d\n", TOUCH_THRESHOLD_X, TOUCH_THRESHOLD_Y); + this->setInterval(100); + } else { + disable(); + this->setInterval(UINT_MAX); + } +} + +int32_t TouchScreenBase::runOnce() +{ + TouchEvent e; + e.touchEvent = static_cast(TOUCH_ACTION_NONE); + + // process touch events + int16_t x, y; + bool touched = getTouch(x, y); + if (touched) { + hapticFeedback(); + this->setInterval(20); + _last_x = x; + _last_y = y; + } + if (touched != _touchedOld) { + if (touched) { + _state = TOUCH_EVENT_OCCURRED; + _start = millis(); + _first_x = x; + _first_y = y; + } else { + _state = TOUCH_EVENT_CLEARED; + time_t duration = millis() - _start; + x = _last_x; + y = _last_y; + this->setInterval(50); + + // compute distance + int16_t dx = x - _first_x; + int16_t dy = y - _first_y; + uint16_t adx = abs(dx); + uint16_t ady = abs(dy); + + // swipe horizontal + if (adx > ady && adx > TOUCH_THRESHOLD_X) { + if (0 > dx) { // swipe right to left + e.touchEvent = static_cast(TOUCH_ACTION_LEFT); + LOG_DEBUG("action SWIPE: right to left\n"); + } else { // swipe left to right + e.touchEvent = static_cast(TOUCH_ACTION_RIGHT); + LOG_DEBUG("action SWIPE: left to right\n"); + } + } + // swipe vertical + else if (ady > adx && ady > TOUCH_THRESHOLD_Y) { + if (0 > dy) { // swipe bottom to top + e.touchEvent = static_cast(TOUCH_ACTION_UP); + LOG_DEBUG("action SWIPE: bottom to top\n"); + } else { // swipe top to bottom + e.touchEvent = static_cast(TOUCH_ACTION_DOWN); + LOG_DEBUG("action SWIPE: top to bottom\n"); + } + } + // tap + else { + if (duration > 0 && duration < TIME_LONG_PRESS) { + if (_tapped) { + _tapped = false; + e.touchEvent = static_cast(TOUCH_ACTION_DOUBLE_TAP); + LOG_DEBUG("action DOUBLE TAP(%d/%d)\n", x, y); + } else { + _tapped = true; + } + } else { + _tapped = false; + } + } + } + } + _touchedOld = touched; + + // fire TAP event when no 2nd tap occured within time + if (_tapped && (time_t(millis()) - _start) > TIME_LONG_PRESS - 50) { + _tapped = false; + e.touchEvent = static_cast(TOUCH_ACTION_TAP); + LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y); + } + + // fire LONG_PRESS event without the need for release + if (touched && (time_t(millis()) - _start) > TIME_LONG_PRESS) { + // tricky: prevent reoccurring events and another touch event when releasing + _start = millis() + 30000; + e.touchEvent = static_cast(TOUCH_ACTION_LONG_PRESS); + LOG_DEBUG("action LONG PRESS(%d/%d)\n", _last_x, _last_y); + } + + if (e.touchEvent != TOUCH_ACTION_NONE) { + e.source = this->_originName; + e.x = _last_x; + e.y = _last_y; + onEvent(e); + } + + return interval; +} + +void TouchScreenBase::hapticFeedback() +{ +#ifdef T_WATCH_S3 + drv.setWaveform(0, 75); + drv.setWaveform(1, 0); // end waveform + drv.go(); +#endif +} \ No newline at end of file diff --git a/src/input/TouchScreenBase.h b/src/input/TouchScreenBase.h new file mode 100644 index 000000000..a68c23e99 --- /dev/null +++ b/src/input/TouchScreenBase.h @@ -0,0 +1,55 @@ +#pragma once + +#include "InputBroker.h" +#include "concurrency/OSThread.h" +#include "mesh/NodeDB.h" + +typedef struct _TouchEvent { + const char *source; + char touchEvent; + uint16_t x; + uint16_t y; +} TouchEvent; + +class TouchScreenBase : public Observable, public concurrency::OSThread +{ + public: + explicit TouchScreenBase(const char *name, uint16_t width, uint16_t height); + void init(bool hasTouch); + + protected: + enum TouchScreenBaseStateType { TOUCH_EVENT_OCCURRED, TOUCH_EVENT_CLEARED }; + + enum TouchScreenBaseEventType { + TOUCH_ACTION_NONE, + TOUCH_ACTION_UP, + TOUCH_ACTION_DOWN, + TOUCH_ACTION_LEFT, + TOUCH_ACTION_RIGHT, + TOUCH_ACTION_TAP, + TOUCH_ACTION_DOUBLE_TAP, + TOUCH_ACTION_LONG_PRESS + }; + + virtual int32_t runOnce() override; + + virtual bool getTouch(int16_t &x, int16_t &y) = 0; + virtual void onEvent(const TouchEvent &event) = 0; + + volatile TouchScreenBaseStateType _state = TOUCH_EVENT_CLEARED; + volatile TouchScreenBaseEventType _action = TOUCH_ACTION_NONE; + void hapticFeedback(); + + protected: + uint16_t _display_width; + uint16_t _display_height; + + private: + bool _touchedOld = false; // previous touch state + int16_t _first_x, _last_x; // horizontal swipe direction + int16_t _first_y, _last_y; // vertical swipe direction + time_t _start; // for LONG_PRESS + bool _tapped; // for DOUBLE_TAP + + const char *_originName; +}; diff --git a/src/input/TouchScreenImpl1.cpp b/src/input/TouchScreenImpl1.cpp new file mode 100644 index 000000000..9a7ecd4a2 --- /dev/null +++ b/src/input/TouchScreenImpl1.cpp @@ -0,0 +1,68 @@ +#include "TouchScreenImpl1.h" +#include "InputBroker.h" +#include "configuration.h" + +TouchScreenImpl1 *touchScreenImpl1; + +TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTouch)(int16_t *, int16_t *)) + : TouchScreenBase("touchscreen1", width, height), _getTouch(getTouch) +{ +} + +void TouchScreenImpl1::init() +{ +#if !HAS_TOUCHSCREEN + TouchScreenBase::init(false); + return; +#else + TouchScreenBase::init(true); + inputBroker->registerSource(this); +#endif +} + +bool TouchScreenImpl1::getTouch(int16_t &x, int16_t &y) +{ + return _getTouch(&x, &y); +} + +/** + * @brief forward touchscreen event + * + * @param event + * + * The touchscreen events are translated to input events and reversed + */ +void TouchScreenImpl1::onEvent(const TouchEvent &event) +{ + InputEvent e; + e.source = event.source; + switch (event.touchEvent) { + case TOUCH_ACTION_LEFT: { + e.inputEvent = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT); + break; + } + case TOUCH_ACTION_RIGHT: { + e.inputEvent = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT); + break; + } + case TOUCH_ACTION_UP: { + e.inputEvent = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP); + break; + } + case TOUCH_ACTION_DOWN: { + e.inputEvent = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN); + break; + } + case TOUCH_ACTION_DOUBLE_TAP: { + e.inputEvent = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT); + break; + } + case TOUCH_ACTION_LONG_PRESS: { + e.inputEvent = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL); + break; + } + default: + return; + } + this->notifyObservers(&e); +} \ No newline at end of file diff --git a/src/input/TouchScreenImpl1.h b/src/input/TouchScreenImpl1.h new file mode 100644 index 000000000..0c5338459 --- /dev/null +++ b/src/input/TouchScreenImpl1.h @@ -0,0 +1,17 @@ +#pragma once +#include "TouchScreenBase.h" + +class TouchScreenImpl1 : public TouchScreenBase +{ + public: + TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTouch)(int16_t *, int16_t *)); + void init(void); + + protected: + virtual bool getTouch(int16_t &x, int16_t &y); + virtual void onEvent(const TouchEvent &event); + + bool (*_getTouch)(int16_t *, int16_t *); +}; + +extern TouchScreenImpl1 *touchScreenImpl1; diff --git a/src/input/TrackballInterruptBase.cpp b/src/input/TrackballInterruptBase.cpp new file mode 100644 index 000000000..649e4b362 --- /dev/null +++ b/src/input/TrackballInterruptBase.cpp @@ -0,0 +1,78 @@ +#include "TrackballInterruptBase.h" +#include "configuration.h" + +TrackballInterruptBase::TrackballInterruptBase(const char *name) +{ + this->_originName = name; +} + +void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLeft, uint8_t pinRight, uint8_t pinPress, + char eventDown, char eventUp, char eventLeft, char eventRight, char eventPressed, + void (*onIntDown)(), void (*onIntUp)(), void (*onIntLeft)(), void (*onIntRight)(), + void (*onIntPress)()) +{ + this->_pinDown = pinDown; + this->_pinUp = pinUp; + this->_pinLeft = pinLeft; + this->_pinRight = pinRight; + this->_eventDown = eventDown; + this->_eventUp = eventUp; + this->_eventLeft = eventLeft; + this->_eventRight = eventRight; + this->_eventPressed = eventPressed; + + pinMode(pinPress, INPUT_PULLUP); + pinMode(this->_pinDown, INPUT_PULLUP); + pinMode(this->_pinUp, INPUT_PULLUP); + pinMode(this->_pinLeft, INPUT_PULLUP); + pinMode(this->_pinRight, INPUT_PULLUP); + + attachInterrupt(pinPress, onIntPress, RISING); + attachInterrupt(this->_pinDown, onIntDown, RISING); + attachInterrupt(this->_pinUp, onIntUp, RISING); + attachInterrupt(this->_pinLeft, onIntLeft, RISING); + attachInterrupt(this->_pinRight, onIntRight, RISING); + + LOG_DEBUG("Trackball GPIO initialized (%d, %d, %d, %d, %d)\n", this->_pinUp, this->_pinDown, this->_pinLeft, this->_pinRight, + pinPress); +} + +void TrackballInterruptBase::intPressHandler() +{ + InputEvent e; + e.source = this->_originName; + e.inputEvent = this->_eventPressed; + this->notifyObservers(&e); +} + +void TrackballInterruptBase::intDownHandler() +{ + InputEvent e; + e.source = this->_originName; + e.inputEvent = this->_eventDown; + this->notifyObservers(&e); +} + +void TrackballInterruptBase::intUpHandler() +{ + InputEvent e; + e.source = this->_originName; + e.inputEvent = this->_eventUp; + this->notifyObservers(&e); +} + +void TrackballInterruptBase::intLeftHandler() +{ + InputEvent e; + e.source = this->_originName; + e.inputEvent = this->_eventLeft; + this->notifyObservers(&e); +} + +void TrackballInterruptBase::intRightHandler() +{ + InputEvent e; + e.source = this->_originName; + e.inputEvent = this->_eventRight; + this->notifyObservers(&e); +} diff --git a/src/input/TrackballInterruptBase.h b/src/input/TrackballInterruptBase.h new file mode 100644 index 000000000..a82a20cb0 --- /dev/null +++ b/src/input/TrackballInterruptBase.h @@ -0,0 +1,30 @@ +#pragma once + +#include "InputBroker.h" +#include "mesh/NodeDB.h" + +class TrackballInterruptBase : public Observable +{ + public: + explicit TrackballInterruptBase(const char *name); + void init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLeft, uint8_t pinRight, uint8_t pinPress, char eventDown, char eventUp, + char eventLeft, char eventRight, char eventPressed, void (*onIntDown)(), void (*onIntUp)(), void (*onIntLeft)(), + void (*onIntRight)(), void (*onIntPress)()); + void intPressHandler(); + void intDownHandler(); + void intUpHandler(); + void intLeftHandler(); + void intRightHandler(); + + private: + uint8_t _pinDown = 0; + uint8_t _pinUp = 0; + uint8_t _pinLeft = 0; + uint8_t _pinRight = 0; + char _eventDown = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + char _eventUp = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + char _eventLeft = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + char _eventRight = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + char _eventPressed = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + const char *_originName; +}; diff --git a/src/input/TrackballInterruptImpl1.cpp b/src/input/TrackballInterruptImpl1.cpp new file mode 100644 index 000000000..0a73b83b6 --- /dev/null +++ b/src/input/TrackballInterruptImpl1.cpp @@ -0,0 +1,54 @@ +#include "TrackballInterruptImpl1.h" +#include "InputBroker.h" +#include "configuration.h" + +TrackballInterruptImpl1 *trackballInterruptImpl1; + +TrackballInterruptImpl1::TrackballInterruptImpl1() : TrackballInterruptBase("trackball1") {} + +void TrackballInterruptImpl1::init() +{ +#if !HAS_TRACKBALL + // Input device is disabled. + return; +#else + uint8_t pinUp = TB_UP; + uint8_t pinDown = TB_DOWN; + uint8_t pinLeft = TB_LEFT; + uint8_t pinRight = TB_RIGHT; + uint8_t pinPress = TB_PRESS; + + char eventDown = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN); + char eventUp = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP); + char eventLeft = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT); + char eventRight = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT); + char eventPressed = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT); + + TrackballInterruptBase::init(pinDown, pinUp, pinLeft, pinRight, pinPress, eventDown, eventUp, eventLeft, eventRight, + eventPressed, TrackballInterruptImpl1::handleIntDown, TrackballInterruptImpl1::handleIntUp, + TrackballInterruptImpl1::handleIntLeft, TrackballInterruptImpl1::handleIntRight, + TrackballInterruptImpl1::handleIntPressed); + inputBroker->registerSource(this); +#endif +} + +void TrackballInterruptImpl1::handleIntDown() +{ + trackballInterruptImpl1->intDownHandler(); +} +void TrackballInterruptImpl1::handleIntUp() +{ + trackballInterruptImpl1->intUpHandler(); +} +void TrackballInterruptImpl1::handleIntLeft() +{ + trackballInterruptImpl1->intLeftHandler(); +} +void TrackballInterruptImpl1::handleIntRight() +{ + trackballInterruptImpl1->intRightHandler(); +} +void TrackballInterruptImpl1::handleIntPressed() +{ + trackballInterruptImpl1->intPressHandler(); +} diff --git a/src/input/TrackballInterruptImpl1.h b/src/input/TrackballInterruptImpl1.h new file mode 100644 index 000000000..36efac6a6 --- /dev/null +++ b/src/input/TrackballInterruptImpl1.h @@ -0,0 +1,16 @@ +#pragma once +#include "TrackballInterruptBase.h" + +class TrackballInterruptImpl1 : public TrackballInterruptBase +{ + public: + TrackballInterruptImpl1(); + void init(); + static void handleIntDown(); + static void handleIntUp(); + static void handleIntLeft(); + static void handleIntRight(); + static void handleIntPressed(); +}; + +extern TrackballInterruptImpl1 *trackballInterruptImpl1; diff --git a/src/input/UpDownInterruptBase.cpp b/src/input/UpDownInterruptBase.cpp index 7c340bab0..ecc3b944a 100644 --- a/src/input/UpDownInterruptBase.cpp +++ b/src/input/UpDownInterruptBase.cpp @@ -23,7 +23,7 @@ void UpDownInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinPress, attachInterrupt(this->_pinDown, onIntDown, RISING); attachInterrupt(this->_pinUp, onIntUp, RISING); - LOG_DEBUG("GPIO initialized (%d, %d, %d)\n", this->_pinDown, this->_pinUp, pinPress); + LOG_DEBUG("Up/down/press GPIO initialized (%d, %d, %d)\n", this->_pinUp, this->_pinDown, pinPress); } void UpDownInterruptBase::intPressHandler() diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index 686f4b5a2..44db1d952 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -7,7 +7,7 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {} void CardKbI2cImpl::init() { - if (cardkb_found.address != CARDKB_ADDR) { + if (cardkb_found.address != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) { disable(); return; } diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index 6850eff51..cdffbaf7e 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -41,7 +41,7 @@ void write_to_14004(const TwoWire * i2cBus, uint8_t reg, uint8_t data) int32_t KbI2cBase::runOnce() { - if (cardkb_found.address != CARDKB_ADDR) { + if (cardkb_found.address != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) { // Input device is not detected. return INT32_MAX; } @@ -85,9 +85,9 @@ int32_t KbI2cBase::runOnce() e.kbchar = PrintDataBuf; this->notifyObservers(&e); } - } else { - // m5 cardkb - i2cBus->requestFrom(CARDKB_ADDR, 1); + } else if (kb_model == 0x00 || kb_model == 0x10) { + // m5 cardkb and T-Deck + i2cBus->requestFrom(kb_model == 0x00 ? CARDKB_ADDR : TDECK_KB_ADDR, 1); while (i2cBus->available()) { char c = i2cBus->read(); @@ -132,6 +132,8 @@ int32_t KbI2cBase::runOnce() this->notifyObservers(&e); } } + } else { + LOG_WARN("Unknown kb_model 0x%02x\n", kb_model); } - return 500; + return 300; } diff --git a/src/main.cpp b/src/main.cpp index 101ef51d8..12ee157a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -96,7 +96,7 @@ ScanI2C::DeviceAddress screen_found = ScanI2C::ADDRESS_NONE; // The I2C address of the cardkb or RAK14004 (if found) ScanI2C::DeviceAddress cardkb_found = ScanI2C::ADDRESS_NONE; -// 0x02 for RAK14004 and 0x00 for cardkb +// 0x02 for RAK14004, 0x00 for cardkb, 0x10 for T-Deck uint8_t kb_model; // The I2C address of the RTC Module (if found) @@ -300,6 +300,15 @@ void setup() #endif #endif +#ifdef T_DECK + // enable keyboard + pinMode(KB_POWERON, OUTPUT); + digitalWrite(KB_POWERON, HIGH); + // There needs to be a delay after power on, give LILYGO-KEYBOARD some startup time + // otherwise keyboard and touch screen will not work + delay(800); +#endif + // Currently only the tbeam has a PMU // PMU initialization needs to be placed before i2c scanning power = new Power(); @@ -372,8 +381,15 @@ void setup() kb_model = 0x02; break; case ScanI2C::DeviceType::CARDKB: + kb_model = 0x00; + break; + case ScanI2C::DeviceType::TDECKKB: + // assign an arbitrary value to distinguish from other models + kb_model = 0x10; + break; default: // use this as default since it's also just zero + LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00\n", kb_info.type); kb_model = 0x00; } } @@ -458,6 +474,11 @@ void setup() #ifdef ARCH_NRF52 nrf52Setup(); #endif + +#ifdef ARCH_RP2040 + rp2040Setup(); +#endif + // We do this as early as possible because this loads preferences from flash // but we need to do this after main cpu iniot (esp32setup), because we need the random seed set nodeDB.init(); diff --git a/src/main.h b/src/main.h index b513d6478..301e8a10c 100644 --- a/src/main.h +++ b/src/main.h @@ -68,7 +68,7 @@ extern uint32_t serialSinceMsec; // This will suppress the current delay and instead try to run ASAP. extern bool runASAP; -void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), clearBonds(); +void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(); meshtastic_DeviceMetadata getDeviceMetadata(); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 37e19eed1..45b987780 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -166,7 +166,7 @@ void NodeDB::installDefaultConfig() config.has_bluetooth = true; config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL; - config.lora.sx126x_rx_boosted_gain = false; + config.lora.sx126x_rx_boosted_gain = true; config.lora.tx_enabled = true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off) config.lora.override_duty_cycle = false; diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h index e4aaeeb96..089d7b59f 100644 --- a/src/mesh/generated/meshtastic/portnums.pb.h +++ b/src/mesh/generated/meshtastic/portnums.pb.h @@ -54,8 +54,6 @@ typedef enum _meshtastic_PortNum { /* Audio Payloads. Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now */ meshtastic_PortNum_AUDIO_APP = 9, - /* Payloads for clients with a network connection proxying MQTT pub/sub to the device */ - meshtastic_PortNum_MQTT_CLIENT_PROXY_APP = 10, /* Provides a 'ping' service that replies to any packet it receives. Also serves as a small example module. */ meshtastic_PortNum_REPLY_APP = 32, diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index d3a450371..b788d6a06 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -164,12 +164,21 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) || (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) { LOG_DEBUG("Canned message event (%x)\n", event->kbchar); - if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + // tweak for left/right events generated via trackball/touch with empty kbchar + if (!event->kbchar) { + if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) { + this->payload = 0xb4; + this->destSelect = true; + } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) { + this->payload = 0xb7; + this->destSelect = true; + } + } else { // pass the pressed key this->payload = event->kbchar; - this->lastTouchMillis = millis(); - validEvent = true; } + this->lastTouchMillis = millis(); + validEvent = true; } if (event->inputEvent == static_cast(ANYKEY)) { LOG_DEBUG("Canned message event any key pressed\n"); @@ -225,7 +234,7 @@ int32_t CannedMessageModule::runOnce() (this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE)) { return INT32_MAX; } - LOG_DEBUG("Check status\n"); + // LOG_DEBUG("Check status\n"); UIFrameEvent e = {false, true}; if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { // TODO: might have some feedback of sendig state @@ -300,8 +309,7 @@ int32_t CannedMessageModule::runOnce() this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; LOG_DEBUG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); } - } else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { - e.frameChanged = true; + } else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT || this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) { switch (this->payload) { case 0xb4: // left if (this->destSelect) { @@ -347,38 +355,49 @@ int32_t CannedMessageModule::runOnce() } } break; - case 0x08: // backspace - if (this->freetext.length() > 0) { - if (this->cursor == this->freetext.length()) { - this->freetext = this->freetext.substring(0, this->freetext.length() - 1); - } else { - this->freetext = this->freetext.substring(0, this->cursor - 1) + - this->freetext.substring(this->cursor, this->freetext.length()); - } - this->cursor--; - } - break; - case 0x09: // tab - if (this->destSelect) { - this->destSelect = false; - } else { - this->destSelect = true; - } - break; default: - if (this->cursor == this->freetext.length()) { - this->freetext += this->payload; - } else { - this->freetext = - this->freetext.substring(0, this->cursor) + this->payload + this->freetext.substring(this->cursor); - } - this->cursor += 1; - if (this->freetext.length() > meshtastic_Constants_DATA_PAYLOAD_LEN) { - this->cursor = meshtastic_Constants_DATA_PAYLOAD_LEN; - this->freetext = this->freetext.substring(0, meshtastic_Constants_DATA_PAYLOAD_LEN); - } break; } + if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + e.frameChanged = true; + switch (this->payload) { + case 0x08: // backspace + if (this->freetext.length() > 0) { + if (this->cursor == this->freetext.length()) { + this->freetext = this->freetext.substring(0, this->freetext.length() - 1); + } else { + this->freetext = this->freetext.substring(0, this->cursor - 1) + + this->freetext.substring(this->cursor, this->freetext.length()); + } + this->cursor--; + } + break; + case 0x09: // tab + if (this->destSelect) { + this->destSelect = false; + } else { + this->destSelect = true; + } + break; + case 0xb4: // left + case 0xb7: // right + // already handled above + break; + default: + if (this->cursor == this->freetext.length()) { + this->freetext += this->payload; + } else { + this->freetext = + this->freetext.substring(0, this->cursor) + this->payload + this->freetext.substring(this->cursor); + } + this->cursor += 1; + if (this->freetext.length() > meshtastic_Constants_DATA_PAYLOAD_LEN) { + this->cursor = meshtastic_Constants_DATA_PAYLOAD_LEN; + this->freetext = this->freetext.substring(0, meshtastic_Constants_DATA_PAYLOAD_LEN); + } + break; + } + } this->lastTouchMillis = millis(); this->notifyObservers(&e); @@ -406,6 +425,11 @@ const char *CannedMessageModule::getNextMessage() { return this->messages[this->getNextIndex()]; } +const char *CannedMessageModule::getMessageByIndex(int index) +{ + return (index >= 0 && index < this->messagesCount) ? this->messages[index] : ""; +} + const char *CannedMessageModule::getNodeName(NodeNum node) { if (node == NODENUM_BROADCAST) { @@ -482,12 +506,31 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); display->drawStringf(0 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest)); - display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL, cannedMessageModule->getPrevMessage()); - display->fillRect(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, x + display->getWidth(), y + FONT_HEIGHT_SMALL); - display->setColor(BLACK); - display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, cannedMessageModule->getCurrentMessage()); - display->setColor(WHITE); - display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 3, cannedMessageModule->getNextMessage()); + int lines = (display->getHeight() / FONT_HEIGHT_SMALL) - 1; + if (lines == 3) { + // static (old) behavior for small displays + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL, cannedMessageModule->getPrevMessage()); + display->fillRect(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, x + display->getWidth(), y + FONT_HEIGHT_SMALL); + display->setColor(BLACK); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, cannedMessageModule->getCurrentMessage()); + display->setColor(WHITE); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 3, cannedMessageModule->getNextMessage()); + } else { + // use entire display height for larger displays + int topMsg = (messagesCount > lines && currentMessageIndex >= lines - 1) ? currentMessageIndex - lines + 2 : 0; + for (int i = 0; i < std::min(messagesCount, lines); i++) { + if (i == currentMessageIndex - topMsg) { + display->fillRect(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), x + display->getWidth(), + y + FONT_HEIGHT_SMALL); + display->setColor(BLACK); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), cannedMessageModule->getCurrentMessage()); + display->setColor(WHITE); + } else { + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), + cannedMessageModule->getMessageByIndex(topMsg + i)); + } + } + } } } } diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index 5858a473c..4e9dadccf 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -30,6 +30,7 @@ class CannedMessageModule : public SinglePortModule, public Observableinit(); #endif +#if HAS_TRACKBALL + trackballInterruptImpl1 = new TrackballInterruptImpl1(); + trackballInterruptImpl1->init(); +#endif #if HAS_SCREEN cannedMessageModule = new CannedMessageModule(); #endif @@ -81,7 +86,7 @@ void setupModules() storeForwardModule = new StoreForwardModule(); #endif -#if defined(ARCH_ESP32) || defined(ARCH_NRF52) +#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) externalNotificationModule = new ExternalNotificationModule(); new RangeTestModule(); #endif @@ -92,4 +97,4 @@ void setupModules() // NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra // acks routingModule = new RoutingModule(); -} +} \ No newline at end of file diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 35457a23e..10289b837 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -52,11 +52,22 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes nodeDB.updatePosition(getFrom(&mp), p); + // Only respond to location requests on the channel where we broadcast location. + if (channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) { + ignoreRequest = false; + } else { + ignoreRequest = true; + } + return false; // Let others look at this message also if they want } meshtastic_MeshPacket *PositionModule::allocReply() { + if (ignoreRequest) { + return NULL; + } + meshtastic_NodeInfoLite *node = service.refreshLocalMeshNode(); // should guarantee there is now a position assert(node->has_position); diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 50198efca..bbdb65c87 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -164,6 +164,8 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) #endif { if (moduleConfig.mqtt.enabled) { + LOG_DEBUG("Initializing MQTT\n"); + assert(!mqtt); mqtt = this; @@ -181,6 +183,14 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) if (!moduleConfig.mqtt.proxy_to_client_enabled) pubSub.setCallback(mqttCallback); #endif + + if (moduleConfig.mqtt.proxy_to_client_enabled) { + LOG_INFO("MQTT configured to use client proxy...\n"); + enabled = true; + runASAP = true; + reconnectCount = 0; + publishStatus(); + } // preflightSleepObserver.observe(&preflightSleep); } else { disable(); diff --git a/src/platform/rp2040/main-rp2040.cpp b/src/platform/rp2040/main-rp2040.cpp index 5e24bf03c..1d7f8fe70 100644 --- a/src/platform/rp2040/main-rp2040.cpp +++ b/src/platform/rp2040/main-rp2040.cpp @@ -27,4 +27,12 @@ void getMacAddr(uint8_t *dmac) dmac[2] = src.id[4]; dmac[1] = src.id[3]; dmac[0] = src.id[2]; +} + +void rp2040Setup() +{ + /* Sets a random seed to make sure we get different random numbers on each boot. + Taken from CPU cycle counter and ROSC oscillator, so should be pretty random. + */ + randomSeed(rp2040.hwrand32()); } \ No newline at end of file diff --git a/variants/nano-g2-ultra/platformio.ini b/variants/nano-g2-ultra/platformio.ini new file mode 100644 index 000000000..c00a9e5ac --- /dev/null +++ b/variants/nano-g2-ultra/platformio.ini @@ -0,0 +1,18 @@ +; First prototype eink/nrf52840/sx1262 device +[env:nano-g2-ultra] +extends = nrf52840_base +board = nano-g2-ultra +debug_tool = jlink + +# add our variants files to the include and src paths +# define build flags for the TFT_eSPI library - NOTE: WE NOT LONGER USE TFT_eSPI, it was for an earlier version of the TTGO eink screens +# -DBUSY_PIN=3 -DRST_PIN=2 -DDC_PIN=28 -DCS_PIN=30 +# add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. +build_flags = ${nrf52840_base.build_flags} -Ivariants/nano-g2-ultra -D NANO_G2_ULTRA + -L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m4/fpv4-sp-d16-hard" +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nano-g2-ultra> +lib_deps = + ${nrf52840_base.lib_deps} + adafruit/Adafruit BusIO@^1.13.2 + lewisxhe/PCF8563_Library@^1.0.1 +;upload_protocol = fs diff --git a/variants/nano-g2-ultra/variant.cpp b/variants/nano-g2-ultra/variant.cpp new file mode 100644 index 000000000..ce5d00886 --- /dev/null +++ b/variants/nano-g2-ultra/variant.cpp @@ -0,0 +1,36 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 - pins 0 and 1 are hardwired for xtal and should never be enabled + 0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() +{ + // Nothing need to be inited for now +} \ No newline at end of file diff --git a/variants/nano-g2-ultra/variant.h b/variants/nano-g2-ultra/variant.h new file mode 100644 index 000000000..da46f311d --- /dev/null +++ b/variants/nano-g2-ultra/variant.h @@ -0,0 +1,197 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_Nano_G2_ +#define _VARIANT_Nano_G2_ + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +//#define USE_LFRC // Board uses 32khz RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (1) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED1 (-1) +#define PIN_LED2 (-1) +#define PIN_LED3 (-1) + +#define LED_RED PIN_LED3 +#define LED_BLUE PIN_LED1 +#define LED_GREEN PIN_LED2 + +#define LED_BUILTIN LED_BLUE +#define LED_CONN PIN_GREEN + +#define LED_STATE_ON 0 // State when LED is lit +//#define LED_INVERTED 1 + +/* + * Buttons + */ +#define PIN_BUTTON1 (32 + 6) + +#define EXT_NOTIFY_OUT (0 + 4) // Default pin to use for Ext Notify Module. + +/* + * Analog pins + */ +#define PIN_A4 (0 + 2) // Battery ADC + +#define BATTERY_PIN PIN_A4 + +static const uint8_t A4 = PIN_A4; + +#define ADC_RESOLUTION 14 + +/* + * Serial interfaces + */ +#define PIN_SERIAL2_RX (0 + 22) +#define PIN_SERIAL2_TX (0 + 20) + +/** + Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (0 + 17) +#define PIN_WIRE_SCL (0 + 15) + +#define PIN_RTC_INT (0 + 14) // Interrupt from the PCF8563 RTC + +/* +External serial flash W25Q16JV_IQ +*/ + +// QSPI Pins +#define PIN_QSPI_SCK (0 + 8) +#define PIN_QSPI_CS (32 + 7) +#define PIN_QSPI_IO0 (0 + 6) // MOSI if using two bit interface +#define PIN_QSPI_IO1 (0 + 26) // MISO if using two bit interface +#define PIN_QSPI_IO2 (32 + 4) // WP if using two bit interface (i.e. not used) +#define PIN_QSPI_IO3 (32 + 2) // HOLD if using two bit interface (i.e. not used) + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES W25Q16JV_IQ +#define EXTERNAL_FLASH_USE_QSPI + +/* + * Lora radio + */ + +#define USE_SX1262 +#define SX126X_CS (32 + 13) // FIXME - we really should define LORA_CS instead +#define SX126X_DIO1 (32 + 10) +// Note DIO2 is attached internally to the module to an analog switch for TX/RX switching +//#define SX1262_DIO3 \ + (0 + 21) // This is used as an *output* from the sx1262 and connected internally to power the tcxo, do not drive from the main +// CPU? +#define SX126X_BUSY (32 + 11) +#define SX126X_RESET (32 + 15) +#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 + +// #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...) + +// #undef SX126X_CS + +/* + * GPS pins + */ + +#define GPS_L76K + +#define PIN_GPS_WAKE (0 + 13) // An output to wake GPS, low means allow sleep, high means force wake +#define PIN_GPS_TX (0 + 9) // This is for bits going TOWARDS the CPU +#define PIN_GPS_RX (0 + 10) // This is for bits going TOWARDS the GPS + +//#define GPS_THREAD_INTERVAL 50 + +#define PIN_SERIAL1_RX PIN_GPS_TX +#define PIN_SERIAL1_TX PIN_GPS_RX + +// PCF8563 RTC Module +#define PCF8563_RTC 0x51 + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 1 + +// For LORA, spi 0 +#define PIN_SPI_MISO (32 + 9) +#define PIN_SPI_MOSI (0 + 11) +#define PIN_SPI_SCK (0 + 12) + +//#define PIN_PWR_EN (0 + 6) + +// To debug via the segger JLINK console rather than the CDC-ACM serial device +// #define USE_SEGGER + +// Battery +// The battery sense is hooked to pin A0 (2) +// it is defined in the anlaolgue pin section of this file +// and has 12 bit resolution +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +// Definition of milliVolt per LSB => 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096 +#define VBAT_MV_PER_LSB (0.73242188F) +// Voltage divider value => 100K + 100K voltage divider on VBAT = (100K / (100K + 100K)) +#define VBAT_DIVIDER (0.5F) +// Compensation factor for the VBAT divider +#define VBAT_DIVIDER_COMP (2.0) +// Fixed calculation of milliVolt from compensation value +#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB) +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER VBAT_DIVIDER_COMP +#define VBAT_RAW_TO_SCALED(x) (REAL_VBAT_MV_PER_LSB * x) + +#define HAS_RTC 1 + +/** + OLED Screen Model + */ +#define ARDUINO_ARCH_AVR +#define USE_SH1107_128_64 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif \ No newline at end of file diff --git a/variants/rpipico/variant.h b/variants/rpipico/variant.h index fb4b9bd75..71d1bd159 100644 --- a/variants/rpipico/variant.h +++ b/variants/rpipico/variant.h @@ -19,6 +19,7 @@ // SDA = 4 // SCL = 5 +#define EXT_NOTIFY_OUT 22 #define BUTTON_PIN 17 #define LED_PIN PIN_LED diff --git a/variants/rpipicow/variant.h b/variants/rpipicow/variant.h index 59f8d2ec2..ac393d4d3 100644 --- a/variants/rpipicow/variant.h +++ b/variants/rpipicow/variant.h @@ -19,6 +19,7 @@ // SDA = 4 // SCL = 5 +#define EXT_NOTIFY_OUT 22 #define BUTTON_PIN 17 #define BATTERY_PIN 26 diff --git a/variants/t-deck/variant.h b/variants/t-deck/variant.h index e434cd35d..04f20fa74 100644 --- a/variants/t-deck/variant.h +++ b/variants/t-deck/variant.h @@ -16,8 +16,11 @@ #define TFT_OFFSET_X 0 #define TFT_OFFSET_Y 0 #define SCREEN_ROTATE -#define SCREEN_TRANSITION_FRAMERATE 1 // fps +#define SCREEN_TRANSITION_FRAMERATE 5 + +#define HAS_TOUCHSCREEN 1 #define SCREEN_TOUCH_INT 16 +#define TOUCH_I2C_PORT 0 #define TOUCH_SLAVE_ADDRESS 0x5D // GT911 #define BUTTON_PIN 0 @@ -43,14 +46,25 @@ // keyboard #define I2C_SDA 18 // I2C pins for this board #define I2C_SCL 8 -#define BOARD_POWERON 10 // must be set to HIGH -#define KB_SLAVE_ADDRESS 0x55 -#define KB_BL_PIN 46 // INT, set to INPUT -#define KB_UP 2 -#define KB_DOWN 3 -#define KB_LEFT 1 -#define KB_RIGHT 15 +#define KB_POWERON 10 // must be set to HIGH +#define KB_SLAVE_ADDRESS TDECK_KB_ADDR // 0x55 +#define KB_BL_PIN 46 // not used for now +// trackball +#define HAS_TRACKBALL 1 +#define TB_UP 3 +#define TB_DOWN 15 +#define TB_LEFT 1 +#define TB_RIGHT 2 +#define TB_PRESS BUTTON_PIN + +// microphone +#define ES7210_SCK 47 +#define ES7210_DIN 14 +#define ES7210_LRCK 21 +#define ES7210_MCLK 48 + +// LoRa #define USE_SX1262 #define USE_SX1268 diff --git a/variants/t-watch-s3/variant.h b/variants/t-watch-s3/variant.h index 652696c3f..8c0fc9122 100644 --- a/variants/t-watch-s3/variant.h +++ b/variants/t-watch-s3/variant.h @@ -16,10 +16,13 @@ #define TFT_OFFSET_X 0 #define TFT_OFFSET_Y 0 #define SCREEN_ROTATE -#define SCREEN_TRANSITION_FRAMERATE 1 // fps +#define SCREEN_TRANSITION_FRAMERATE 5 // fps + +#define HAS_TOUCHSCREEN 1 #define SCREEN_TOUCH_INT 16 -#define SCREEN_TOUCH_USE_I2C1 1 -#define TOUCH_SLAVE_ADDRESS 0x38 // GT911 +#define SCREEN_TOUCH_USE_I2C1 +#define TOUCH_I2C_PORT 1 +#define TOUCH_SLAVE_ADDRESS 0x38 #define I2C_SDA1 39 // Used for capacitive touch #define I2C_SCL1 40 // Used for capacitive touch diff --git a/version.properties b/version.properties index 76538d46b..00ddcfc79 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 1 -build = 20 +build = 21