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..09c0635a4 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 }} @@ -108,6 +111,7 @@ jobs: - board: t-echo - board: pca10059_diy_eink - board: feather_diy + - board: nano-g2-ultra uses: ./.github/workflows/build_nrf52.yml with: board: ${{ matrix.board }} @@ -255,6 +259,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/.vscode/settings.json b/.vscode/settings.json index 3b489975b..03922dc72 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "editor.formatOnSave": true, - "editor.defaultFormatter": "trunk.io" + "editor.defaultFormatter": "trunk.io", + "trunk.enableWindows": true } 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..8e7500278 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit f17298c2b093ac0d2536642b508f6cf84771b172 +Subproject commit 8e7500278f32a0f8096961843aad9b431916dcb0 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/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index b6162b846..aa39244ff 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -53,7 +53,9 @@ typedef enum _meshtastic_AdminMessage_ModuleConfigType { /* TODO: REPLACE */ meshtastic_AdminMessage_ModuleConfigType_AUDIO_CONFIG = 7, /* TODO: REPLACE */ - meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG = 8 + meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG = 8, + /* TODO: REPLACE */ + meshtastic_AdminMessage_ModuleConfigType_AMBIENTLIGHTING_CONFIG = 10 } meshtastic_AdminMessage_ModuleConfigType; /* Struct definitions */ @@ -172,8 +174,8 @@ extern "C" { #define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG+1)) #define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG -#define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG -#define _meshtastic_AdminMessage_ModuleConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ModuleConfigType)(meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG+1)) +#define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_AMBIENTLIGHTING_CONFIG +#define _meshtastic_AdminMessage_ModuleConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ModuleConfigType)(meshtastic_AdminMessage_ModuleConfigType_AMBIENTLIGHTING_CONFIG+1)) #define meshtastic_AdminMessage_payload_variant_get_config_request_ENUMTYPE meshtastic_AdminMessage_ConfigType #define meshtastic_AdminMessage_payload_variant_get_module_config_request_ENUMTYPE meshtastic_AdminMessage_ModuleConfigType diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 6007265d5..7fffe9ea7 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -59,6 +59,8 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_TLORA_T3_S3 = 16, /* B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer */ meshtastic_HardwareModel_NANO_G1_EXPLORER = 17, + /* B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra */ + meshtastic_HardwareModel_NANO_G2_ULTRA = 18, /* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */ meshtastic_HardwareModel_STATION_G1 = 25, /* RAK11310 (RP2040 + SX1262) */ @@ -228,9 +230,9 @@ typedef enum _meshtastic_Routing_Error { to make sure that critical packets are sent ASAP. In the case of meshtastic that means we want to send protocol acks as soon as possible (to prevent unneeded retransmissions), we want routing messages to be sent next, - then messages marked as reliable and finally ‘background’ packets like periodic position updates. + then messages marked as reliable and finally 'background' packets like periodic position updates. So I bit the bullet and implemented a new (internal - not sent over the air) - field in MeshPacket called ‘priority’. + field in MeshPacket called 'priority'. And the transmission queue in the router object is now a priority queue. */ typedef enum _meshtastic_MeshPacket_Priority { /* Treated as Priority.DEFAULT */ 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/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index a3cac53b1..1caae69a3 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -249,7 +249,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp if (moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT || moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_SIMPLE) { - serialPrint->printf("%s", p.payload.bytes); + serialPrint->write(p.payload.bytes, p.payload.size); } else if (moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG) { meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(getFrom(&mp)); String sender = (node && node->has_user) ? node->user.short_name : "???"; diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 50198efca..a7a6b662b 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(); @@ -597,6 +607,15 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp) if (int(decoded->sats_in_view)) { msgPayload["sats_in_view"] = new JSONValue((int)decoded->sats_in_view); } + if ((int)decoded->PDOP) { + msgPayload["PDOP"] = new JSONValue((int)decoded->PDOP); + } + if ((int)decoded->HDOP) { + msgPayload["HDOP"] = new JSONValue((int)decoded->HDOP); + } + if ((int)decoded->VDOP) { + msgPayload["VDOP"] = new JSONValue((int)decoded->VDOP); + } jsonObj["payload"] = new JSONValue(msgPayload); } else { LOG_ERROR("Error decoding protobuf for position message!\n"); 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..c00681305 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 1 -build = 20 +build = 22