diff --git a/.github/workflows/build_raspbian.yml b/.github/workflows/build_raspbian.yml index cef61bb21..697d08727 100644 --- a/.github/workflows/build_raspbian.yml +++ b/.github/workflows/build_raspbian.yml @@ -10,6 +10,11 @@ jobs: build-raspbian: runs-on: [self-hosted, linux, ARM64] steps: + - name: Install libbluetooth + shell: bash + run: | + apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev + - name: Checkout code uses: actions/checkout@v4 with: diff --git a/.github/workflows/build_raspbian_armv7l.yml b/.github/workflows/build_raspbian_armv7l.yml new file mode 100644 index 000000000..ee5eb66eb --- /dev/null +++ b/.github/workflows/build_raspbian_armv7l.yml @@ -0,0 +1,51 @@ +name: Build Raspbian Arm + +on: workflow_call + +permissions: + contents: write + packages: write + +jobs: + build-raspbian-armv7l: + runs-on: [self-hosted, linux, ARM] + steps: + - name: Install libbluetooth + shell: bash + run: | + apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Upgrade python tools + shell: bash + run: | + python -m pip install --upgrade pip + pip install -U platformio adafruit-nrfutil + pip install -U meshtastic --pre + + - name: Upgrade platformio + shell: bash + run: | + pio upgrade + + - name: Build Raspbian + run: bin/build-native.sh + + - name: Get release version string + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-raspbian-armv7l-${{ steps.version.outputs.version }}.zip + overwrite: true + path: | + release/meshtasticd_linux_armv7l + bin/config-dist.yaml diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index d329e693a..a768e5fd9 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -103,15 +103,12 @@ jobs: with: board: ${{ matrix.board }} - build-raspbian: - strategy: - fail-fast: false - max-parallel: 1 - uses: ./.github/workflows/build_raspbian.yml - package-raspbian: uses: ./.github/workflows/package_raspbian.yml + package-raspbian-armv7l: + uses: ./.github/workflows/package_raspbian_armv7l.yml + build-native: runs-on: ubuntu-latest steps: @@ -195,10 +192,10 @@ jobs: build-esp32-s3, build-esp32-c3, build-nrf52, - build-raspbian, build-native, build-rpi2040, package-raspbian, + package-raspbian-armv7l, ] steps: - name: Checkout code @@ -220,7 +217,7 @@ jobs: id: version - name: Move files up - run: mv -b -t ./ ./release/meshtasticd_linux_aarch64 ./bin/config-dist.yaml + run: mv -b -t ./ ./release/meshtasticd_linux_aarch64 ./release/meshtasticd_linux_armv7l ./bin/config-dist.yaml - name: Repackage in single firmware zip uses: actions/upload-artifact@v4 @@ -233,7 +230,7 @@ jobs: ./firmware-*-ota.zip ./device-*.sh ./device-*.bat - ./meshtasticd_linux_*64 + ./meshtasticd_linux_* ./config-dist.yaml ./littlefs-*.bin ./bleota*bin @@ -303,8 +300,9 @@ jobs: - uses: actions/download-artifact@v4 with: + pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb merge-multiple: true - name: artifact-deb + path: ./output - name: Display structure of downloaded files run: ls -R @@ -363,16 +361,26 @@ jobs: asset_name: debug-elfs-${{ steps.version.outputs.version }}.zip asset_content_type: application/zip - - name: Add raspbian .deb + - name: Add raspbian aarch64 .deb uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ github.token }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./meshtasticd_${{ steps.version.outputs.version }}_arm64.deb + asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_arm64.deb asset_name: meshtasticd_${{ steps.version.outputs.version }}_arm64.deb asset_content_type: application/vnd.debian.binary-package + - name: Add raspbian armv7l .deb + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_armhf.deb + asset_name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb + asset_content_type: application/vnd.debian.binary-package + - name: Bump version.properties run: >- bin/bump_version.py diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index f6e40052e..5471332c5 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -72,7 +72,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: artifact-deb + name: meshtasticd_${{ steps.version.outputs.version }}_arm64.deb overwrite: true path: | ./*.deb diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml new file mode 100644 index 000000000..5b9c9aa71 --- /dev/null +++ b/.github/workflows/package_raspbian_armv7l.yml @@ -0,0 +1,78 @@ +name: Package Raspbian + +on: + workflow_call: + workflow_dispatch: + +permissions: + contents: write + packages: write + +jobs: + build-raspbian_armv7l: + uses: ./.github/workflows/build_raspbian_armv7l.yml + + package-raspbian_armv7l: + runs-on: ubuntu-latest + needs: build-raspbian_armv7l + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Pull web ui + uses: dsaltares/fetch-gh-release-asset@master + with: + repo: meshtastic/web + file: build.tar + target: build.tar + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Get release version string + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: firmware-raspbian-armv7l-${{ steps.version.outputs.version }}.zip + merge-multiple: true + + - name: Display structure of downloaded files + run: ls -R + + - name: build .debpkg + run: | + mkdir -p .debpkg/DEBIAN + mkdir -p .debpkg/usr/share/doc/meshtasticd/web + mkdir -p .debpkg/usr/sbin + mkdir -p .debpkg/etc/meshtasticd + mkdir -p .debpkg/usr/lib/systemd/system/ + tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web + gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz + cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd + cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml + chmod +x .debpkg/usr/sbin/meshtasticd + cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service + echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles + chmod +x .debpkg/DEBIAN/conffiles + + - uses: jiro4989/build-deb-action@v3 + with: + package: meshtasticd + package_root: .debpkg + maintainer: Jonathan Bennett + version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.* + arch: armhf + depends: libyaml-cpp0.7, openssl, libulfius2.7 + desc: Native Linux Meshtastic binary. + + - uses: actions/upload-artifact@v4 + with: + name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb + overwrite: true + path: | + ./*.deb diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 39935b849..7e55f0934 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -1,7 +1,7 @@ ; Common settings for ESP targes, mixin with extends = esp32_base [esp32_base] extends = arduino_base -platform = platformio/espressif32@6.3.2 # This is a temporary fix to the S3-based devices bluetooth issues until we can determine what within ESP-IDF changed and can develop a suitable patch. +platform = platformio/espressif32@6.7.0 build_src_filter = ${arduino_base.build_src_filter} - - - - - @@ -15,8 +15,10 @@ board_build.filesystem = littlefs # Remove -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL for low level BLE logging. # See library directory for BLE logging possible values: .pio/libdeps/tbeam/NimBLE-Arduino/src/log_common/log_common.h # This overrides the BLE logging default of LOG_LEVEL_INFO (1) from: .pio/libdeps/tbeam/NimBLE-Arduino/src/esp_nimble_cfg.h +build_unflags = -fno-lto build_flags = ${arduino_base.build_flags} + -flto -Wall -Wextra -Isrc/platform/esp32 diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index 651677af2..3382ff891 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -1,3 +1,5 @@ +# trunk-ignore-all(ruff/F821) +# trunk-ignore-all(flake8/F821): For SConstruct imports import sys from os.path import join @@ -60,6 +62,7 @@ if platform.name == "espressif32": import esptool env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) + env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"]) Import("projenv") diff --git a/boards/tbeam-s3-core.json b/boards/tbeam-s3-core.json index 8d2c3eed6..7bda2e5a0 100644 --- a/boards/tbeam-s3-core.json +++ b/boards/tbeam-s3-core.json @@ -8,7 +8,7 @@ "-DBOARD_HAS_PSRAM", "-DLILYGO_TBEAM_S3_CORE", "-DARDUINO_USB_CDC_ON_BOOT=1", - "-DARDUINO_USB_MODE=1", + "-DARDUINO_USB_MODE=0", "-DARDUINO_RUNNING_CORE=1", "-DARDUINO_EVENT_RUNNING_CORE=1" ], diff --git a/protobufs b/protobufs index 5cfadd148..b5dc871a1 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 5cfadd14890b7723a1fe6e7683f711911154b010 +Subproject commit b5dc871a1bfa2cc932126a4f490d9ef078476e4c diff --git a/src/Power.cpp b/src/Power.cpp index 64e310b68..b80d8a0d5 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -50,7 +50,7 @@ RTC_NOINIT_ATTR uint64_t RTC_reg_b; esp_adc_cal_characteristics_t *adc_characs = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t)); #ifndef ADC_ATTENUATION -static const adc_atten_t atten = ADC_ATTEN_DB_11; +static const adc_atten_t atten = ADC_ATTEN_DB_12; #else static const adc_atten_t atten = ADC_ATTENUATION; #endif @@ -555,14 +555,24 @@ void Power::readPowerStatus() #ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect // changes. + static nrfx_power_usb_state_t prev_nrf_usb_state = (nrfx_power_usb_state_t)-1; // -1 so that state detected at boot nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get(); - if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) { - powerFSM.trigger(EVENT_POWER_DISCONNECTED); - NRF_USB = OptFalse; - } else { - powerFSM.trigger(EVENT_POWER_CONNECTED); - NRF_USB = OptTrue; + // If state changed + if (nrf_usb_state != prev_nrf_usb_state) { + // If changed to DISCONNECTED + if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) { + powerFSM.trigger(EVENT_POWER_DISCONNECTED); + NRF_USB = OptFalse; + } + // If changed to CONNECTED / READY + else { + powerFSM.trigger(EVENT_POWER_CONNECTED); + NRF_USB = OptTrue; + } + + // Cache the current state + prev_nrf_usb_state = nrf_usb_state; } #endif // Notify any status instances that are observing us diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 4f42b36b5..a7bc18f1a 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -348,12 +348,18 @@ void PowerFSM_setup() powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone"); - powerFSM.add_timed_transition(&stateON, &stateDARK, - Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL, - "Screen-on timeout"); - powerFSM.add_timed_transition(&statePOWER, &stateDARK, - Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL, - "Screen-on timeout"); +#ifdef USE_EINK + // Allow E-Ink devices to suppress the screensaver, if screen timeout set to 0 + if (config.display.screen_on_secs > 0) +#endif + { + powerFSM.add_timed_transition(&stateON, &stateDARK, + Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), + NULL, "Screen-on timeout"); + powerFSM.add_timed_transition(&statePOWER, &stateDARK, + Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), + NULL, "Screen-on timeout"); + } // We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally) #ifdef ARCH_ESP32 diff --git a/src/graphics/PointStruct.h b/src/graphics/PointStruct.h new file mode 100644 index 000000000..218731978 --- /dev/null +++ b/src/graphics/PointStruct.h @@ -0,0 +1,4 @@ +struct PointStruct { + int x; + int y; +}; \ No newline at end of file diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 0899335e6..9758e97fd 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -419,6 +419,536 @@ static bool shouldDrawMessage(const meshtastic_MeshPacket *packet) return packet->from != 0 && !moduleConfig.store_forward.enabled; } +// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage. +static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus) +{ + static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD}; + static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85}; + // Clear the bar area on the battery image + for (int i = 1; i < 14; i++) { + imgBuffer[i] = 0x81; + } + // If charging, draw a charging indicator + if (powerStatus->getIsCharging()) { + memcpy(imgBuffer + 3, lightning, 8); + // If not charging, Draw power bars + } else { + for (int i = 0; i < 4; i++) { + if (powerStatus->getBatteryChargePercent() >= 25 * i) + memcpy(imgBuffer + 1 + (i * 3), powerBar, 3); + } + } + display->drawFastImage(x, y, 16, 8, imgBuffer); +} + +#ifdef T_WATCH_S3 + +void Screen::drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode, float scale) +{ + uint16_t segmentWidth = SEGMENT_WIDTH * scale; + uint16_t segmentHeight = SEGMENT_HEIGHT * scale; + + if (digitalMode) { + uint16_t radius = (segmentWidth + (segmentHeight * 2) + 4) / 2; + uint16_t centerX = (x + segmentHeight + 2) + (radius / 2); + uint16_t centerY = (y + segmentHeight + 2) + (radius / 2); + + display->drawCircle(centerX, centerY, radius); + display->drawCircle(centerX, centerY, radius + 1); + display->drawLine(centerX, centerY, centerX, centerY - radius + 3); + display->drawLine(centerX, centerY, centerX + radius - 3, centerY); + } else { + uint16_t segmentOneX = x + segmentHeight + 2; + uint16_t segmentOneY = y; + + uint16_t segmentTwoX = segmentOneX + segmentWidth + 2; + uint16_t segmentTwoY = segmentOneY + segmentHeight + 2; + + uint16_t segmentThreeX = segmentOneX; + uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2; + + uint16_t segmentFourX = x; + uint16_t segmentFourY = y + segmentHeight + 2; + + drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight); + drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight); + drawHorizontalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight); + drawVerticalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight); + } +} + +// Draw a digital clock +void Screen::drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setTextAlignment(TEXT_ALIGN_LEFT); + + drawBattery(display, x, y + 7, imgBattery, powerStatus); + + if (powerStatus->getHasBattery()) { + String batteryPercent = String(powerStatus->getBatteryChargePercent()) + "%"; + + display->setFont(FONT_SMALL); + + display->drawString(x + 20, y + 2, batteryPercent); + } + + if (nimbleBluetooth->isConnected()) { + drawBluetoothConnectedIcon(display, display->getWidth() - 18, y + 2); + } + + drawWatchFaceToggleButton(display, display->getWidth() - 36, display->getHeight() - 36, screen->digitalWatchFace, 1); + + display->setColor(OLEDDISPLAY_COLOR::WHITE); + + uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone + if (rtc_sec > 0) { + long hms = rtc_sec % SEC_PER_DAY; + hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; + + int hour = hms / SEC_PER_HOUR; + int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN + + hour = hour > 12 ? hour - 12 : hour; + + if (hour == 0) { + hour = 12; + } + + // hours string + String hourString = String(hour); + + // minutes string + String minuteString = minute < 10 ? "0" + String(minute) : String(minute); + + String timeString = hourString + ":" + minuteString; + + // seconds string + String secondString = second < 10 ? "0" + String(second) : String(second); + + float scale = 1.5; + + uint16_t segmentWidth = SEGMENT_WIDTH * scale; + uint16_t segmentHeight = SEGMENT_HEIGHT * scale; + + // calculate hours:minutes string width + uint16_t timeStringWidth = timeString.length() * 5; + + for (uint8_t i = 0; i < timeString.length(); i++) { + String character = String(timeString[i]); + + if (character == ":") { + timeStringWidth += segmentHeight; + } else { + timeStringWidth += segmentWidth + (segmentHeight * 2) + 4; + } + } + + // calculate seconds string width + uint16_t secondStringWidth = (secondString.length() * 12) + 4; + + // sum these to get total string width + uint16_t totalWidth = timeStringWidth + secondStringWidth; + + uint16_t hourMinuteTextX = (display->getWidth() / 2) - (totalWidth / 2); + + uint16_t startingHourMinuteTextX = hourMinuteTextX; + + uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2); + + // iterate over characters in hours:minutes string and draw segmented characters + for (uint8_t i = 0; i < timeString.length(); i++) { + String character = String(timeString[i]); + + if (character == ":") { + drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale); + + hourMinuteTextX += segmentHeight + 6; + } else { + drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character.toInt(), scale); + + hourMinuteTextX += segmentWidth + (segmentHeight * 2) + 4; + } + + hourMinuteTextX += 5; + } + + // draw seconds string + display->setFont(FONT_MEDIUM); + display->drawString(startingHourMinuteTextX + timeStringWidth + 4, + (display->getHeight() - hourMinuteTextY) - FONT_HEIGHT_MEDIUM + 6, secondString); + } +} + +void Screen::drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale) +{ + uint16_t segmentWidth = SEGMENT_WIDTH * scale; + uint16_t segmentHeight = SEGMENT_HEIGHT * scale; + + uint16_t cellHeight = (segmentWidth * 2) + (segmentHeight * 3) + 8; + + uint16_t topAndBottomX = x + (4 * scale); + + uint16_t quarterCellHeight = cellHeight / 4; + + uint16_t topY = y + quarterCellHeight; + uint16_t bottomY = y + (quarterCellHeight * 3); + + display->fillRect(topAndBottomX, topY, segmentHeight, segmentHeight); + display->fillRect(topAndBottomX, bottomY, segmentHeight, segmentHeight); +} + +void Screen::drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale) +{ + // the numbers 0-9, each expressed as an array of seven boolean (0|1) values encoding the on/off state of + // segment {innerIndex + 1} + // e.g., to display the numeral '0', segments 1-6 are on, and segment 7 is off. + uint8_t numbers[10][7] = { + {1, 1, 1, 1, 1, 1, 0}, // 0 Display segment key + {0, 1, 1, 0, 0, 0, 0}, // 1 1 + {1, 1, 0, 1, 1, 0, 1}, // 2 ___ + {1, 1, 1, 1, 0, 0, 1}, // 3 6 | | 2 + {0, 1, 1, 0, 0, 1, 1}, // 4 |_7̲_| + {1, 0, 1, 1, 0, 1, 1}, // 5 5 | | 3 + {1, 0, 1, 1, 1, 1, 1}, // 6 |___| + {1, 1, 1, 0, 0, 1, 0}, // 7 + {1, 1, 1, 1, 1, 1, 1}, // 8 4 + {1, 1, 1, 1, 0, 1, 1}, // 9 + }; + + // the width and height of each segment's central rectangle: + // _____________________ + // ⋰| (only this part, |⋱ + // ⋰ | not including | ⋱ + // ⋱ | the triangles | ⋰ + // ⋱| on the ends) |⋰ + // ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + + uint16_t segmentWidth = SEGMENT_WIDTH * scale; + uint16_t segmentHeight = SEGMENT_HEIGHT * scale; + + // segment x and y coordinates + uint16_t segmentOneX = x + segmentHeight + 2; + uint16_t segmentOneY = y; + + uint16_t segmentTwoX = segmentOneX + segmentWidth + 2; + uint16_t segmentTwoY = segmentOneY + segmentHeight + 2; + + uint16_t segmentThreeX = segmentTwoX; + uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2 + segmentHeight + 2; + + uint16_t segmentFourX = segmentOneX; + uint16_t segmentFourY = segmentThreeY + segmentWidth + 2; + + uint16_t segmentFiveX = x; + uint16_t segmentFiveY = segmentThreeY; + + uint16_t segmentSixX = x; + uint16_t segmentSixY = segmentTwoY; + + uint16_t segmentSevenX = segmentOneX; + uint16_t segmentSevenY = segmentTwoY + segmentWidth + 2; + + if (numbers[number][0]) { + drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight); + } + + if (numbers[number][1]) { + drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight); + } + + if (numbers[number][2]) { + drawVerticalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight); + } + + if (numbers[number][3]) { + drawHorizontalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight); + } + + if (numbers[number][4]) { + drawVerticalSegment(display, segmentFiveX, segmentFiveY, segmentWidth, segmentHeight); + } + + if (numbers[number][5]) { + drawVerticalSegment(display, segmentSixX, segmentSixY, segmentWidth, segmentHeight); + } + + if (numbers[number][6]) { + drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight); + } +} + +void Screen::drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height) +{ + int halfHeight = height / 2; + + // draw central rectangle + display->fillRect(x, y, width, height); + + // draw end triangles + display->fillTriangle(x, y, x, y + height - 1, x - halfHeight, y + halfHeight); + + display->fillTriangle(x + width, y, x + width + halfHeight, y + halfHeight, x + width, y + height - 1); +} + +void Screen::drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height) +{ + int halfHeight = height / 2; + + // draw central rectangle + display->fillRect(x, y, height, width); + + // draw end triangles + display->fillTriangle(x + halfHeight, y - halfHeight, x + height - 1, y, x, y); + + display->fillTriangle(x, y + width, x + height - 1, y + width, x + halfHeight, y + width + halfHeight); +} + +void Screen::drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y) +{ + display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon); +} + +// Draw an analog clock +void Screen::drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setTextAlignment(TEXT_ALIGN_LEFT); + + drawBattery(display, x, y + 7, imgBattery, powerStatus); + + if (powerStatus->getHasBattery()) { + String batteryPercent = String(powerStatus->getBatteryChargePercent()) + "%"; + + display->setFont(FONT_SMALL); + + display->drawString(x + 20, y + 2, batteryPercent); + } + + if (nimbleBluetooth->isConnected()) { + drawBluetoothConnectedIcon(display, display->getWidth() - 18, y + 2); + } + + drawWatchFaceToggleButton(display, display->getWidth() - 36, display->getHeight() - 36, screen->digitalWatchFace, 1); + + // clock face center coordinates + int16_t centerX = display->getWidth() / 2; + int16_t centerY = display->getHeight() / 2; + + // clock face radius + int16_t radius = (display->getWidth() / 2) * 0.8; + + // noon (0 deg) coordinates (outermost circle) + int16_t noonX = centerX; + int16_t noonY = centerY - radius; + + // second hand radius and y coordinate (outermost circle) + int16_t secondHandNoonY = noonY + 1; + + // tick mark outer y coordinate; (first nested circle) + int16_t tickMarkOuterNoonY = secondHandNoonY; + + // seconds tick mark inner y coordinate; (second nested circle) + double secondsTickMarkInnerNoonY = (double)noonY + 8; + + // hours tick mark inner y coordinate; (third nested circle) + double hoursTickMarkInnerNoonY = (double)noonY + 16; + + // minute hand y coordinate + int16_t minuteHandNoonY = secondsTickMarkInnerNoonY + 4; + + // hour string y coordinate + int16_t hourStringNoonY = minuteHandNoonY + 18; + + // hour hand radius and y coordinate + int16_t hourHandRadius = radius * 0.55; + int16_t hourHandNoonY = centerY - hourHandRadius; + + display->setColor(OLEDDISPLAY_COLOR::WHITE); + display->drawCircle(centerX, centerY, radius); + + uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone + if (rtc_sec > 0) { + long hms = rtc_sec % SEC_PER_DAY; + hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; + + // Tear apart hms into h:m:s + int hour = hms / SEC_PER_HOUR; + int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN + + hour = hour > 12 ? hour - 12 : hour; + + int16_t degreesPerHour = 30; + int16_t degreesPerMinuteOrSecond = 6; + + double hourBaseAngle = hour * degreesPerHour; + double hourAngleOffset = ((double)minute / 60) * degreesPerHour; + double hourAngle = radians(hourBaseAngle + hourAngleOffset); + + double minuteBaseAngle = minute * degreesPerMinuteOrSecond; + double minuteAngleOffset = ((double)second / 60) * degreesPerMinuteOrSecond; + double minuteAngle = radians(minuteBaseAngle + minuteAngleOffset); + + double secondAngle = radians(second * degreesPerMinuteOrSecond); + + double hourX = sin(-hourAngle) * (hourHandNoonY - centerY) + noonX; + double hourY = cos(-hourAngle) * (hourHandNoonY - centerY) + centerY; + + double minuteX = sin(-minuteAngle) * (minuteHandNoonY - centerY) + noonX; + double minuteY = cos(-minuteAngle) * (minuteHandNoonY - centerY) + centerY; + + double secondX = sin(-secondAngle) * (secondHandNoonY - centerY) + noonX; + double secondY = cos(-secondAngle) * (secondHandNoonY - centerY) + centerY; + + display->setFont(FONT_MEDIUM); + + // draw minute and hour tick marks and hour numbers + for (uint16_t angle = 0; angle < 360; angle += 6) { + double angleInRadians = radians(angle); + + double sineAngleInRadians = sin(-angleInRadians); + double cosineAngleInRadians = cos(-angleInRadians); + + double endX = sineAngleInRadians * (tickMarkOuterNoonY - centerY) + noonX; + double endY = cosineAngleInRadians * (tickMarkOuterNoonY - centerY) + centerY; + + if (angle % degreesPerHour == 0) { + double startX = sineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + noonX; + double startY = cosineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + centerY; + + // draw hour tick mark + display->drawLine(startX, startY, endX, endY); + + static char buffer[2]; + + uint8_t hourInt = (angle / 30); + + if (hourInt == 0) { + hourInt = 12; + } + + // hour number x offset needs to be adjusted for some cases + int8_t hourStringXOffset; + int8_t hourStringYOffset = 13; + + switch (hourInt) { + case 3: + hourStringXOffset = 5; + break; + case 9: + hourStringXOffset = 7; + break; + case 10: + case 11: + hourStringXOffset = 8; + break; + case 12: + hourStringXOffset = 13; + break; + default: + hourStringXOffset = 6; + break; + } + + double hourStringX = (sineAngleInRadians * (hourStringNoonY - centerY) + noonX) - hourStringXOffset; + double hourStringY = (cosineAngleInRadians * (hourStringNoonY - centerY) + centerY) - hourStringYOffset; + + // draw hour number + display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt); + } + + if (angle % degreesPerMinuteOrSecond == 0) { + double startX = sineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + noonX; + double startY = cosineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + centerY; + + // draw minute tick mark + display->drawLine(startX, startY, endX, endY); + } + } + + // draw hour hand + display->drawLine(centerX, centerY, hourX, hourY); + + // draw minute hand + display->drawLine(centerX, centerY, minuteX, minuteY); + + // draw second hand + display->drawLine(centerX, centerY, secondX, secondY); + } +} + +#endif + +// Get an absolute time from "seconds ago" info. Returns false if no valid timestamp possible +bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo) +{ + // Cache the result - avoid frequent recalculation + static uint8_t hoursCached = 0, minutesCached = 0; + static uint32_t daysAgoCached = 0; + static uint32_t secondsAgoCached = 0; + static bool validCached = false; + + // Abort: if timezone not set + if (strlen(config.device.tzdef) == 0) { + validCached = false; + return validCached; + } + + // Abort: if invalid pointers passed + if (hours == nullptr || minutes == nullptr || daysAgo == nullptr) { + validCached = false; + return validCached; + } + + // Abort: if time seems invalid.. (> 6 months ago, probably seen before RTC set) + if (secondsAgo > SEC_PER_DAY * 30UL * 6) { + validCached = false; + return validCached; + } + + // If repeated request, don't bother recalculating + if (secondsAgo - secondsAgoCached < 60 && secondsAgoCached != 0) { + if (validCached) { + *hours = hoursCached; + *minutes = minutesCached; + *daysAgo = daysAgoCached; + } + return validCached; + } + + // Get local time + uint32_t secondsRTC = getValidTime(RTCQuality::RTCQualityDevice, true); // Get local time + + // Abort: if RTC not set + if (!secondsRTC) { + validCached = false; + return validCached; + } + + // Get absolute time when last seen + uint32_t secondsSeenAt = secondsRTC - secondsAgo; + + // Calculate daysAgo + *daysAgo = (secondsRTC / SEC_PER_DAY) - (secondsSeenAt / SEC_PER_DAY); // How many "midnights" have passed + + // Get seconds since midnight + uint32_t hms = (secondsRTC - secondsAgo) % SEC_PER_DAY; + hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; + + // Tear apart hms into hours and minutes + *hours = hms / SEC_PER_HOUR; + *minutes = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + + // Cache the result + daysAgoCached = *daysAgo; + hoursCached = *hours; + minutesCached = *minutes; + secondsAgoCached = secondsAgo; + + validCached = true; + return validCached; +} + /// Draw the last text message we received static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { @@ -440,22 +970,98 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state display->setColor(BLACK); } + // For time delta uint32_t seconds = sinceReceived(&mp); uint32_t minutes = seconds / 60; uint32_t hours = minutes / 60; uint32_t days = hours / 24; - if (config.display.heading_bold) { - display->drawStringf(1 + x, 0 + y, tempBuf, "%s ago from %s", - screen->drawTimeDelta(days, hours, minutes, seconds).c_str(), - (node && node->has_user) ? node->user.short_name : "???"); + // For timestamp + uint8_t timestampHours, timestampMinutes; + int32_t daysAgo; + bool useTimestamp = deltaToTimestamp(seconds, ×tampHours, ×tampMinutes, &daysAgo); + + // If bold, draw twice, shifting right by one pixel + for (uint8_t xOff = 0; xOff <= (config.display.heading_bold ? 1 : 0); xOff++) { + // Show a timestamp if received today, but longer than 15 minutes ago + if (useTimestamp && minutes >= 15 && daysAgo == 0) { + display->drawStringf(xOff + x, 0 + y, tempBuf, "At %02hu:%02hu from %s", timestampHours, timestampMinutes, + (node && node->has_user) ? node->user.short_name : "???"); + } + // Timestamp yesterday (if display is wide enough) + else if (useTimestamp && daysAgo == 1 && display->width() >= 200) { + display->drawStringf(xOff + x, 0 + y, tempBuf, "Yesterday %02hu:%02hu from %s", timestampHours, timestampMinutes, + (node && node->has_user) ? node->user.short_name : "???"); + } + // Otherwise, show a time delta + else { + display->drawStringf(xOff + x, 0 + y, tempBuf, "%s ago from %s", + screen->drawTimeDelta(days, hours, minutes, seconds).c_str(), + (node && node->has_user) ? node->user.short_name : "???"); + } } - display->drawStringf(0 + x, 0 + y, tempBuf, "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(), - (node && node->has_user) ? node->user.short_name : "???"); display->setColor(WHITE); +#ifndef EXCLUDE_EMOJI + if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44D") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height, + thumbup); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44E") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height, + thumbdown); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"❓") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - question_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - question_height) / 2 + 2 + 5, question_width, question_height, + question); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"‼️") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - bang_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - bang_height) / 2 + 2 + 5, + bang_width, bang_height, bang); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F4A9") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - poo_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - poo_height) / 2 + 2 + 5, + poo_width, poo_height, poo); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xf0\x9f\xa4\xa3") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - haha_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - haha_height) / 2 + 2 + 5, + haha_width, haha_height, haha); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44B") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - wave_icon_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - wave_icon_height) / 2 + 2 + 5, wave_icon_width, + wave_icon_height, wave_icon); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F920") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - cowboy_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cowboy_height) / 2 + 2 + 5, cowboy_width, cowboy_height, + cowboy); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F42D") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - deadmau5_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - deadmau5_height) / 2 + 2 + 5, deadmau5_width, deadmau5_height, + deadmau5); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\xE2\x98\x80\xEF\xB8\x8F") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - sun_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - sun_height) / 2 + 2 + 5, + sun_width, sun_height, sun); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\u2614") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - rain_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - rain_height) / 2 + 2 + 10, + rain_width, rain_height, rain); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"☁️") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - cloud_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cloud_height) / 2 + 2 + 5, cloud_width, cloud_height, cloud); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"🌫️") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - fog_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - fog_height) / 2 + 2 + 5, + fog_width, fog_height, fog); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\xf0\x9f\x98\x88") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - devil_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - devil_height) / 2 + 2 + 5, devil_width, devil_height, devil); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"♥️") == 0) { + display->drawXbm(x + (SCREEN_WIDTH - heart_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - heart_height) / 2 + 2 + 5, heart_width, heart_height, heart); + } else { + snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes); + display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf); + } +#else snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes); display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf); +#endif } /// Draw the last waypoint we received @@ -518,28 +1124,6 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char * } } -// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage. -static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus) -{ - static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD}; - static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85}; - // Clear the bar area on the battery image - for (int i = 1; i < 14; i++) { - imgBuffer[i] = 0x81; - } - // If charging, draw a charging indicator - if (powerStatus->getIsCharging()) { - memcpy(imgBuffer + 3, lightning, 8); - // If not charging, Draw power bars - } else { - for (int i = 0; i < 4; i++) { - if (powerStatus->getBatteryChargePercent() >= 25 * i) - memcpy(imgBuffer + 1 + (i * 3), powerBar, 3); - } - } - display->drawFastImage(x, y, 16, 8, imgBuffer); -} - // Draw nodes status static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStatus *nodeStatus) { @@ -875,23 +1459,47 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ const char *username = node->has_user ? node->user.long_name : "Unknown Name"; static char signalStr[20]; - snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100)); + + //section here to choose whether to display hops away rather than signal strength if more than 0 hops away. + if(node->hops_away>0) + { + snprintf(signalStr, sizeof(signalStr), "Hops Away: %d", node->hops_away); + } + else + { + snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100)); + } + + uint32_t agoSecs = sinceLastSeen(node); static char lastStr[20]; + + // Use an absolute timestamp in some cases. + // Particularly useful with E-Ink displays. Static UI, fewer refreshes. + uint8_t timestampHours, timestampMinutes; + int32_t daysAgo; + bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo); + if (agoSecs < 120) // last 2 mins? snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs); + // -- if suitable for timestamp -- + else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes + snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / SECONDS_IN_MINUTE); + else if (useTimestamp && daysAgo == 0) // Today + snprintf(lastStr, sizeof(lastStr), "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes); + else if (useTimestamp && daysAgo == 1) // Yesterday + snprintf(lastStr, sizeof(lastStr), "Seen yesterday"); + else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method) + snprintf(lastStr, sizeof(lastStr), "%li days ago", (long)daysAgo); + // -- if using time delta instead -- else if (agoSecs < 120 * 60) // last 2 hrs snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60); - else { - // Only show hours ago if it's been less than 6 months. Otherwise, we may have bad - // data. - if ((agoSecs / 60 / 60) < (hours_in_month * 6)) { - snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60); - } else { - snprintf(lastStr, sizeof(lastStr), "unknown age"); - } - } + // Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data. + else if ((agoSecs / 60 / 60) < (hours_in_month * 6)) + snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60); + else + snprintf(lastStr, sizeof(lastStr), "unknown age"); static char distStr[20]; if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { @@ -900,7 +1508,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ strncpy(distStr, "? km", sizeof(distStr)); } meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); - const char *fields[] = {username, distStr, signalStr, lastStr, NULL}; + const char *fields[] = {username, lastStr, signalStr, distStr, NULL}; int16_t compassX = 0, compassY = 0; // coordinates for the center of the compass/circle @@ -1218,6 +1826,10 @@ int32_t Screen::runOnce() return RUN_SAME; } + if (displayHeight == 0) { + displayHeight = dispdev->getHeight(); + } + // Show boot screen for first logo_timeout seconds, then switch to normal operation. // serialSinceMsec adjusts for additional serial wait time during nRF52 bootup static bool showingBootScreen = true; @@ -1448,6 +2060,15 @@ void Screen::setFrames() LOG_DEBUG("showing standard frames\n"); showingNormalScreen = true; +#ifdef USE_EINK + // If user has disabled the screensaver, warn them after boot + static bool warnedScreensaverDisabled = false; + if (config.display.screen_on_secs == 0 && !warnedScreensaverDisabled) { + screen->print("Screensaver disabled\n"); + warnedScreensaverDisabled = true; + } +#endif + moduleFrames = MeshModule::GetMeshModulesWithUIFrames(); LOG_DEBUG("Showing %d module frames\n", moduleFrames.size()); #ifdef DEBUG_PORT @@ -1487,6 +2108,10 @@ void Screen::setFrames() normalFrames[numframes++] = drawWaypointFrame; } +#ifdef T_WATCH_S3 + normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame; +#endif + // then all the nodes // We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens size_t numToShow = min(numMeshNodes, 4U); @@ -2058,6 +2683,19 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event) int Screen::handleInputEvent(const InputEvent *event) { + +#ifdef T_WATCH_S3 + // For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button + if (this->ui->getUiState()->currentFrame == 0 && event->touchX >= 204 && event->touchX <= 240 && event->touchY >= 204 && + event->touchY <= 240) { + screen->digitalWatchFace = !screen->digitalWatchFace; + + setFrames(); + + return 0; + } +#endif + if (showingNormalScreen && moduleFrames.size() == 0) { // LOG_DEBUG("Screen::handleInputEvent from %s\n", event->source); if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) { @@ -2073,4 +2711,4 @@ int Screen::handleInputEvent(const InputEvent *event) } // namespace graphics #else graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} -#endif // HAS_SCREEN \ No newline at end of file +#endif // HAS_SCREEN diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index cfb08c0f4..8c4650271 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -48,6 +48,7 @@ class Screen #include "EInkDisplay2.h" #include "EInkDynamicDisplay.h" +#include "PointStruct.h" #include "TFTDisplay.h" #include "TypedQueue.h" #include "commands.h" @@ -77,6 +78,10 @@ class Screen #define EINK_BLACK OLEDDISPLAY_COLOR::WHITE #define EINK_WHITE OLEDDISPLAY_COLOR::BLACK +// Base segment dimensions for T-Watch segmented display +#define SEGMENT_WIDTH 16 +#define SEGMENT_HEIGHT 4 + namespace graphics { @@ -389,6 +394,27 @@ class Screen : public concurrency::OSThread static void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); +#ifdef T_WATCH_S3 + static void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); + + static void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); + + static void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale = 1); + + static void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height); + + static void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height); + + static void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale = 1); + + static void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1); + + static void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y); + + // Whether we are showing the digital watch face or the analog one + bool digitalWatchFace = true; +#endif + /// Queue of commands to execute in doTask. TypedQueue cmdQueue; /// Whether we are using a display diff --git a/src/graphics/images.h b/src/graphics/images.h index 5c6fb4275..d4c738610 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -14,6 +14,12 @@ const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3 const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF}; const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF}; +#ifdef T_WATCH_S3 +const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0xe3, 0x1f, + 0xf3, 0x3f, 0x33, 0x30, 0x33, 0x33, 0x33, 0x33, 0x03, 0x33, 0xff, 0x33, + 0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f}; +#endif + #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS) || \ ARCH_PORTDUINO) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) @@ -31,4 +37,171 @@ const uint8_t imgQuestion[] PROGMEM = {0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1, const uint8_t imgSF[] PROGMEM = {0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5}; #endif +#ifndef EXCLUDE_EMOJI +#define thumbs_height 25 +#define thumbs_width 25 +static unsigned char thumbup[] PROGMEM = { + 0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00, + 0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, + 0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01, + 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00, + 0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00, +}; + +static unsigned char thumbdown[] PROGMEM = { + 0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00, + 0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, + 0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00, + 0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, + 0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, +}; + +#define question_height 25 +#define question_width 25 +static unsigned char question[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00, + 0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00, + 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define bang_height 30 +#define bang_width 30 +static unsigned char bang[] PROGMEM = { + 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, + 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, + 0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, + 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F, + 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07, +}; + +#define haha_height 30 +#define haha_width 30 +static unsigned char haha[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, + 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x1F, 0x3E, 0x00, 0x80, 0x03, 0x70, 0x00, 0xC0, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0xC2, 0x00, + 0x60, 0x00, 0x03, 0x00, 0x60, 0x00, 0xC1, 0x1F, 0x60, 0x80, 0x8F, 0x31, 0x30, 0x0E, 0x80, 0x31, 0x30, 0x10, 0x30, 0x1F, + 0x30, 0x08, 0x58, 0x00, 0x30, 0x04, 0x6C, 0x03, 0x60, 0x00, 0xF3, 0x01, 0x60, 0xC0, 0xFC, 0x01, 0x80, 0x38, 0xBF, 0x01, + 0xE0, 0xC5, 0xDF, 0x00, 0xB0, 0xF9, 0xEF, 0x00, 0x30, 0xF1, 0x73, 0x00, 0xB0, 0x1D, 0x3E, 0x00, 0xF0, 0xFD, 0x0F, 0x00, + 0xE0, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define wave_icon_height 30 +#define wave_icon_width 30 +static unsigned char wave_icon[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x0C, 0x9C, 0x01, 0x80, 0x17, 0x20, 0x01, 0x80, 0x26, 0x46, 0x02, 0x80, 0x44, 0x88, 0x02, 0xC0, 0x89, 0x8A, 0x02, + 0x40, 0x93, 0x8B, 0x02, 0x40, 0x26, 0x13, 0x00, 0x80, 0x44, 0x16, 0x00, 0xC0, 0x89, 0x24, 0x00, 0x40, 0x93, 0x60, 0x00, + 0x40, 0x26, 0x40, 0x00, 0x80, 0x0C, 0x80, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x40, 0x06, 0x80, 0x00, + 0x50, 0x0C, 0x80, 0x00, 0x50, 0x08, 0x40, 0x00, 0x90, 0x10, 0x20, 0x00, 0xB0, 0x21, 0x10, 0x00, 0x20, 0x47, 0x18, 0x00, + 0x40, 0x80, 0x0F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define cowboy_height 30 +#define cowboy_width 30 +static unsigned char cowboy[] PROGMEM = { + 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x3C, 0xFE, 0x1F, 0x0F, + 0xFE, 0xFE, 0xDF, 0x1F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, + 0x3E, 0xC0, 0x00, 0x1F, 0x1E, 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x0E, 0x1C, 0x04, 0x00, 0x0E, 0x1C, 0x00, + 0x04, 0x0E, 0x1C, 0x08, 0x04, 0x0E, 0x1C, 0x08, 0x04, 0x04, 0x08, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x08, + 0x8C, 0x07, 0x70, 0x0C, 0x88, 0xFC, 0x4F, 0x04, 0x88, 0x01, 0x40, 0x04, 0x90, 0xFF, 0x7F, 0x02, 0x30, 0x03, 0x30, 0x03, + 0x60, 0x0E, 0x9C, 0x01, 0xC0, 0xF8, 0xC7, 0x00, 0x80, 0x01, 0x60, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0xF8, 0x07, 0x00, +}; + +#define deadmau5_height 30 +#define deadmau5_width 60 +static unsigned char deadmau5[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00, + 0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00, + 0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07, + 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC, + 0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00, + 0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF, + 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define sun_width 30 +#define sun_height 30 +static unsigned char sun[] PROGMEM = { + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03, + 0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00, + 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E, + 0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, + 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03, + 0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, +}; + +#define rain_width 30 +#define rain_height 30 +static unsigned char rain[] PROGMEM = { + 0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00, + 0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20, + 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00, + 0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00, + 0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C, + 0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00, +}; + +#define cloud_height 30 +#define cloud_width 30 +static unsigned char cloud[] PROGMEM = { + 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00, + 0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01, + 0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, + 0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, + 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10, + 0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, +}; + +#define fog_height 25 +#define fog_width 25 +static unsigned char fog[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, + 0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00, + 0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define devil_height 30 +#define devil_width 30 +static unsigned char devil[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x03, 0xC0, 0x01, 0x38, 0x07, 0x7C, 0x0F, 0x38, 0x1F, 0x03, 0x30, 0x1E, + 0xFE, 0x01, 0xE0, 0x1F, 0x7E, 0x00, 0x80, 0x1F, 0x3C, 0x00, 0x00, 0x0F, 0x1C, 0x00, 0x00, 0x0E, 0x18, 0x00, 0x00, 0x06, + 0x08, 0x00, 0x00, 0x04, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x0E, 0x1C, 0x0C, + 0x0C, 0x18, 0x06, 0x0C, 0x0C, 0x1C, 0x06, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x0C, 0x06, 0x0C, + 0x08, 0x00, 0x00, 0x06, 0x18, 0x02, 0x10, 0x06, 0x10, 0x0C, 0x0C, 0x03, 0x30, 0xF8, 0x07, 0x03, 0x60, 0xE0, 0x80, 0x01, + 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x01, 0x70, 0x00, 0x00, 0x06, 0x1C, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define heart_height 30 +#define heart_width 30 +static unsigned char heart[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18, + 0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37, + 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F, + 0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03, + 0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00, + 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, +}; + +#define poo_width 30 +#define poo_height 30 +static unsigned char poo[] PROGMEM = { + 0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x8C, 0x07, 0x00, 0x00, 0x0C, 0x06, 0x00, + 0x00, 0x24, 0x0C, 0x00, 0x00, 0x34, 0x08, 0x00, 0x00, 0x1F, 0x08, 0x00, 0xC0, 0x0F, 0x08, 0x00, 0xC0, 0x00, 0x3C, 0x00, + 0x60, 0x00, 0x7C, 0x00, 0x60, 0x00, 0xC6, 0x00, 0x20, 0x00, 0xCB, 0x00, 0xA0, 0xC7, 0xFF, 0x00, 0xE0, 0x7F, 0xF7, 0x00, + 0xF0, 0x18, 0xE3, 0x03, 0x78, 0x18, 0x41, 0x03, 0x6C, 0x9B, 0x5D, 0x06, 0x64, 0x9B, 0x5D, 0x04, 0x44, 0x1A, 0x41, 0x04, + 0x4C, 0xD8, 0x63, 0x06, 0xF8, 0xFC, 0x36, 0x06, 0xFE, 0x0F, 0x9C, 0x1F, 0x07, 0x03, 0xC0, 0x30, 0x03, 0x00, 0x78, 0x20, + 0x01, 0x00, 0x1F, 0x20, 0x03, 0xE0, 0x03, 0x20, 0x07, 0x7E, 0x04, 0x30, 0xFE, 0x0F, 0xFC, 0x1F, 0xF0, 0x00, 0xF0, 0x0F, +}; +#endif + #include "img/icon.xbm" diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h index d73e6657a..57c25af4b 100644 --- a/src/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -8,6 +8,8 @@ typedef struct _InputEvent { const char *source; char inputEvent; char kbchar; + uint16_t touchX; + uint16_t touchY; } InputEvent; class InputBroker : public Observable { diff --git a/src/input/LinuxInput.cpp b/src/input/LinuxInput.cpp index 1ace2044c..6194195ed 100644 --- a/src/input/LinuxInput.cpp +++ b/src/input/LinuxInput.cpp @@ -155,6 +155,9 @@ int32_t LinuxInput::runOnce() case KEY_ENTER: // Enter e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; break; + case KEY_POWER: + system("poweroff"); + break; default: // all other keys if (keymap[code]) { e.inputEvent = ANYKEY; diff --git a/src/input/TouchScreenImpl1.cpp b/src/input/TouchScreenImpl1.cpp index c863ead69..20196278d 100644 --- a/src/input/TouchScreenImpl1.cpp +++ b/src/input/TouchScreenImpl1.cpp @@ -49,6 +49,10 @@ void TouchScreenImpl1::onEvent(const TouchEvent &event) { InputEvent e; e.source = event.source; + + e.touchX = event.x; + e.touchY = event.y; + switch (event.touchEvent) { case TOUCH_ACTION_LEFT: { e.inputEvent = static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT); diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index d692a3f30..2a209ad0a 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -75,7 +75,7 @@ typedef struct _meshtastic_HamParameters { Ensure your radio is capable of operating of the selected frequency before setting this. */ float frequency; /* Optional short name of user */ - char short_name[6]; + char short_name[5]; } meshtastic_HamParameters; /* Response envelope for node_remote_hardware_pins */ @@ -342,7 +342,7 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size #define meshtastic_AdminMessage_size 500 -#define meshtastic_HamParameters_size 32 +#define meshtastic_HamParameters_size 31 #define meshtastic_NodeRemoteHardwarePinsResponse_size 496 #ifdef __cplusplus diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 0f17c268b..9b993ae5a 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -47,6 +47,12 @@ CannedMessageModule::CannedMessageModule() disable(); } else { LOG_INFO("CannedMessageModule is enabled\n"); + + // T-Watch interface currently has no way to select destination type, so default to 'node' +#ifdef T_WATCH_S3 + this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE; +#endif + this->inputObserver.observe(inputBroker); } } else { @@ -67,8 +73,16 @@ int CannedMessageModule::splitConfiguredMessages() int messageIndex = 0; int i = 0; + String messages = cannedMessageModuleConfig.messages; + +#ifdef T_WATCH_S3 + String separator = messages.length() ? "|" : ""; + + messages = "[---- Free Text ----]" + separator + messages; +#endif + // collect all the message parts - strncpy(this->messageStore, cannedMessageModuleConfig.messages, sizeof(this->messageStore)); + strncpy(this->messageStore, messages.c_str(), sizeof(this->messageStore)); // The first message points to the beginning of the store. this->messages[messageIndex++] = this->messageStore; @@ -78,7 +92,6 @@ int CannedMessageModule::splitConfiguredMessages() if (this->messageStore[i] == '|') { // Message ending found, replace it with string-end character. this->messageStore[i] = '\0'; - LOG_DEBUG("CannedMessage %d is: '%s'\n", messageIndex - 1, this->messages[messageIndex - 1]); // hit our max messages, bail if (messageIndex >= CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT) { @@ -119,20 +132,30 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) bool validEvent = false; if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP)) { if (this->messagesCount > 0) { - // LOG_DEBUG("Canned message event UP\n"); this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_UP; validEvent = true; } } if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN)) { if (this->messagesCount > 0) { - // LOG_DEBUG("Canned message event DOWN\n"); this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_DOWN; validEvent = true; } } if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT)) { - LOG_DEBUG("Canned message event Select\n"); + +#ifdef T_WATCH_S3 + if (this->currentMessageIndex == 0) { + this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; + + UIFrameEvent e = {false, true}; + e.frameChanged = true; + this->notifyObservers(&e); + + return 0; + } +#endif + // when inactive, call the onebutton shortpress instead. Activate Module only on up/down if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) { powerFSM.trigger(EVENT_PRESS); @@ -143,38 +166,47 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) } } if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) { - LOG_DEBUG("Canned message event Cancel\n"); UIFrameEvent e = {false, true}; e.frameChanged = true; this->currentMessageIndex = -1; + +#ifndef T_WATCH_S3 this->freetext = ""; // clear freetext this->cursor = 0; this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; +#endif + this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->notifyObservers(&e); } if ((event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK)) || (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); + +#ifdef T_WATCH_S3 + if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) { + this->payload = 0xb4; + } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) { + this->payload = 0xb7; + } +#else // 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 = CANNED_MESSAGE_DESTINATION_TYPE_NODE; } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) { this->payload = 0xb7; - // this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE; } } else { // pass the pressed key this->payload = event->kbchar; } +#endif + this->lastTouchMillis = millis(); validEvent = true; } if (event->inputEvent == static_cast(ANYKEY)) { - LOG_DEBUG("Canned message event any key pressed\n"); // when inactive, this will switch to the freetext mode if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) { @@ -250,8 +282,68 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) screen->removeFunctionSymbal("Fn"); // remove modifier (function) symbal } } + +#ifdef T_WATCH_S3 + if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + String keyTapped = keyForCoordinates(event->touchX, event->touchY); + + if (keyTapped == "⇧") { + this->highlight = -1; + + this->payload = 0x00; + + validEvent = true; + + this->shift = !this->shift; + } else if (keyTapped == "⌫") { + this->highlight = keyTapped[0]; + + this->payload = 0x08; + + validEvent = true; + + this->shift = false; + } else if (keyTapped == "123" || keyTapped == "ABC") { + this->highlight = -1; + + this->payload = 0x00; + + this->charSet = this->charSet == 0 ? 1 : 0; + + validEvent = true; + } else if (keyTapped == " ") { + this->highlight = keyTapped[0]; + + this->payload = keyTapped[0]; + + validEvent = true; + + this->shift = false; + } else if (keyTapped == "↵") { + this->highlight = 0x00; + + this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT; + + this->payload = CANNED_MESSAGE_RUN_STATE_FREETEXT; + + this->currentMessageIndex = event->kbchar - 1; + + validEvent = true; + + this->shift = false; + } else if (keyTapped != "") { + this->highlight = keyTapped[0]; + + this->payload = this->shift ? keyTapped[0] : std::tolower(keyTapped[0]); + + validEvent = true; + + this->shift = false; + } + } +#endif + if (event->inputEvent == static_cast(MATRIXKEY)) { - LOG_DEBUG("Canned message event Matrix key pressed\n"); // this will send the text immediately on matrix press this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT; this->payload = MATRIXKEY; @@ -311,17 +403,24 @@ int32_t CannedMessageModule::runOnce() this->currentMessageIndex = -1; this->freetext = ""; // clear freetext this->cursor = 0; + +#ifndef T_WATCH_S3 this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; +#endif + this->notifyObservers(&e); } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) { // Reset module - LOG_DEBUG("Reset due to lack of activity.\n"); e.frameChanged = true; this->currentMessageIndex = -1; this->freetext = ""; // clear freetext this->cursor = 0; + +#ifndef T_WATCH_S3 this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; +#endif + this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->notifyObservers(&e); } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) { @@ -330,7 +429,6 @@ int32_t CannedMessageModule::runOnce() sendText(this->dest, indexChannels[this->channel], this->freetext.c_str(), true); this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; } else { - LOG_DEBUG("Reset message is empty.\n"); this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; } } else { @@ -339,11 +437,15 @@ int32_t CannedMessageModule::runOnce() powerFSM.trigger(EVENT_PRESS); return INT32_MAX; } else { +#ifdef T_WATCH_S3 + sendText(this->dest, indexChannels[this->channel], this->messages[this->currentMessageIndex], true); +#else sendText(NODENUM_BROADCAST, channels.getPrimaryIndex(), this->messages[this->currentMessageIndex], true); +#endif } this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; } else { - LOG_DEBUG("Reset message is empty.\n"); + // LOG_DEBUG("Reset message is empty.\n"); this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; } } @@ -351,7 +453,11 @@ int32_t CannedMessageModule::runOnce() this->currentMessageIndex = -1; this->freetext = ""; // clear freetext this->cursor = 0; + +#ifndef T_WATCH_S3 this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; +#endif + this->notifyObservers(&e); return 2000; } else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) { @@ -364,7 +470,11 @@ int32_t CannedMessageModule::runOnce() this->currentMessageIndex = getPrevIndex(); this->freetext = ""; // clear freetext this->cursor = 0; + +#ifndef T_WATCH_S3 this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; +#endif + this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; LOG_DEBUG("MOVE UP (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); } @@ -373,7 +483,11 @@ int32_t CannedMessageModule::runOnce() this->currentMessageIndex = this->getNextIndex(); this->freetext = ""; // clear freetext this->cursor = 0; + +#ifndef T_WATCH_S3 this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; +#endif + this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; LOG_DEBUG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); } @@ -457,7 +571,7 @@ int32_t CannedMessageModule::runOnce() switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the // display back to the default window case 0x08: // backspace - if (this->freetext.length() > 0) { + if (this->freetext.length() > 0 && this->highlight == 0x00) { if (this->cursor == this->freetext.length()) { this->freetext = this->freetext.substring(0, this->freetext.length() - 1); } else { @@ -495,13 +609,19 @@ int32_t CannedMessageModule::runOnce() runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; break; default: + if (this->highlight != 0x00) { + break; + } + 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; + uint16_t maxChars = meshtastic_Constants_DATA_PAYLOAD_LEN - (moduleConfig.canned_message.send_bell ? 1 : 0); if (this->freetext.length() > maxChars) { this->cursor = maxChars; @@ -594,6 +714,201 @@ void CannedMessageModule::showTemporaryMessage(const String &message) setIntervalFromNow(2000); } +#ifdef T_WATCH_S3 + +String CannedMessageModule::keyForCoordinates(uint x, uint y) +{ + int outerSize = *(&this->keyboard[this->charSet] + 1) - this->keyboard[this->charSet]; + + for (int8_t outerIndex = 0; outerIndex < outerSize; outerIndex++) { + int innerSize = *(&this->keyboard[this->charSet][outerIndex] + 1) - this->keyboard[this->charSet][outerIndex]; + + for (int8_t innerIndex = 0; innerIndex < innerSize; innerIndex++) { + Letter letter = this->keyboard[this->charSet][outerIndex][innerIndex]; + + if (x > letter.rectX && x < (letter.rectX + letter.rectWidth) && y > letter.rectY && + y < (letter.rectY + letter.rectHeight)) { + return letter.character; + } + } + } + + return ""; +} + +void CannedMessageModule::drawKeyboard(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + int outerSize = *(&this->keyboard[this->charSet] + 1) - this->keyboard[this->charSet]; + + int xOffset = 0; + + int yOffset = 56; + + display->setTextAlignment(TEXT_ALIGN_LEFT); + + display->setFont(FONT_SMALL); + + display->setColor(OLEDDISPLAY_COLOR::WHITE); + + display->drawStringMaxWidth(0, 0, display->getWidth(), + cannedMessageModule->drawWithCursor(cannedMessageModule->freetext, cannedMessageModule->cursor)); + + display->setFont(FONT_MEDIUM); + + int cellHeight = round((display->height() - 64) / outerSize); + + int yCorrection = 8; + + for (int8_t outerIndex = 0; outerIndex < outerSize; outerIndex++) { + yOffset += outerIndex > 0 ? cellHeight : 0; + + int innerSizeBound = *(&this->keyboard[this->charSet][outerIndex] + 1) - this->keyboard[this->charSet][outerIndex]; + + int innerSize = 0; + + for (int8_t innerIndex = 0; innerIndex < innerSizeBound; innerIndex++) { + if (this->keyboard[this->charSet][outerIndex][innerIndex].character != "") { + innerSize++; + } + } + + int cellWidth = display->width() / innerSize; + + for (int8_t innerIndex = 0; innerIndex < innerSize; innerIndex++) { + xOffset += innerIndex > 0 ? cellWidth : 0; + + Letter letter = this->keyboard[this->charSet][outerIndex][innerIndex]; + + Letter updatedLetter = {letter.character, letter.width, xOffset, yOffset, cellWidth, cellHeight}; + + this->keyboard[this->charSet][outerIndex][innerIndex] = updatedLetter; + + float characterOffset = ((cellWidth / 2) - (letter.width / 2)); + + if (letter.character == "⇧") { + if (this->shift) { + display->fillRect(xOffset, yOffset, cellWidth, cellHeight); + + display->setColor(OLEDDISPLAY_COLOR::BLACK); + + drawShiftIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.2); + + display->setColor(OLEDDISPLAY_COLOR::WHITE); + } else { + display->drawRect(xOffset, yOffset, cellWidth, cellHeight); + + drawShiftIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.2); + } + } else if (letter.character == "⌫") { + if (this->highlight == letter.character[0]) { + display->fillRect(xOffset, yOffset, cellWidth, cellHeight); + + display->setColor(OLEDDISPLAY_COLOR::BLACK); + + drawBackspaceIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.2); + + display->setColor(OLEDDISPLAY_COLOR::WHITE); + + setIntervalFromNow(0); + } else { + display->drawRect(xOffset, yOffset, cellWidth, cellHeight); + + drawBackspaceIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.2); + } + } else if (letter.character == "↵") { + display->drawRect(xOffset, yOffset, cellWidth, cellHeight); + + drawEnterIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.7); + } else { + if (this->highlight == letter.character[0]) { + display->fillRect(xOffset, yOffset, cellWidth, cellHeight); + + display->setColor(OLEDDISPLAY_COLOR::BLACK); + + display->drawString(xOffset + characterOffset, yOffset + yCorrection, + letter.character == " " ? "space" : letter.character); + + display->setColor(OLEDDISPLAY_COLOR::WHITE); + + setIntervalFromNow(0); + } else { + display->drawRect(xOffset, yOffset, cellWidth, cellHeight); + + display->drawString(xOffset + characterOffset, yOffset + yCorrection, + letter.character == " " ? "space" : letter.character); + } + } + } + + xOffset = 0; + } + + this->highlight = 0x00; +} + +void CannedMessageModule::drawShiftIcon(OLEDDisplay *display, int x, int y, float scale) +{ + PointStruct shiftIcon[10] = {{8, 0}, {15, 7}, {15, 8}, {12, 8}, {12, 12}, {4, 12}, {4, 8}, {1, 8}, {1, 7}, {8, 0}}; + + int size = 10; + + for (int i = 0; i < size - 1; i++) { + int x0 = x + (shiftIcon[i].x * scale); + int y0 = y + (shiftIcon[i].y * scale); + int x1 = x + (shiftIcon[i + 1].x * scale); + int y1 = y + (shiftIcon[i + 1].y * scale); + + display->drawLine(x0, y0, x1, y1); + } +} + +void CannedMessageModule::drawBackspaceIcon(OLEDDisplay *display, int x, int y, float scale) +{ + PointStruct backspaceIcon[6] = {{0, 7}, {5, 2}, {15, 2}, {15, 12}, {5, 12}, {0, 7}}; + + int size = 6; + + for (int i = 0; i < size - 1; i++) { + int x0 = x + (backspaceIcon[i].x * scale); + int y0 = y + (backspaceIcon[i].y * scale); + int x1 = x + (backspaceIcon[i + 1].x * scale); + int y1 = y + (backspaceIcon[i + 1].y * scale); + + display->drawLine(x0, y0, x1, y1); + } + + PointStruct backspaceIconX[4] = {{7, 4}, {13, 10}, {7, 10}, {13, 4}}; + + size = 4; + + for (int i = 0; i < size - 1; i++) { + int x0 = x + (backspaceIconX[i].x * scale); + int y0 = y + (backspaceIconX[i].y * scale); + int x1 = x + (backspaceIconX[i + 1].x * scale); + int y1 = y + (backspaceIconX[i + 1].y * scale); + + display->drawLine(x0, y0, x1, y1); + } +} + +void CannedMessageModule::drawEnterIcon(OLEDDisplay *display, int x, int y, float scale) +{ + PointStruct enterIcon[6] = {{0, 7}, {4, 3}, {4, 11}, {0, 7}, {15, 7}, {15, 0}}; + + int size = 6; + + for (int i = 0; i < size - 1; i++) { + int x0 = x + (enterIcon[i].x * scale); + int y0 = y + (enterIcon[i].y * scale); + int x1 = x + (enterIcon[i + 1].x * scale); + int y1 = y + (enterIcon[i + 1].y * scale); + + display->drawLine(x0, y0, x1, y1); + } +} + +#endif + void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { char buffer[50]; @@ -614,6 +929,16 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st } display->drawStringf(display->getWidth() / 2 + x, 0 + y + 12, buffer, displayString, cannedMessageModule->getNodeName(this->incoming)); + + display->setFont(FONT_SMALL); + + String snrString = "Last Rx SNR: %f"; + String rssiString = "Last Rx RSSI: %d"; + + if (this->ack) { + display->drawStringf(display->getWidth() / 2 + x, y + 100, buffer, snrString, this->lastRxSnr); + display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi); + } } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { display->setTextAlignment(TEXT_ALIGN_CENTER); display->setFont(FONT_MEDIUM); @@ -623,6 +948,11 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->setFont(FONT_SMALL); display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled."); } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + +#ifdef T_WATCH_S3 + drawKeyboard(display, state, 0, 0); +#else + display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); if (this->destSelect != CANNED_MESSAGE_DESTINATION_TYPE_NONE) { @@ -663,6 +993,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->drawStringMaxWidth( 0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), cannedMessageModule->drawWithCursor(cannedMessageModule->freetext, cannedMessageModule->cursor)); +#endif } else { if (this->messagesCount > 0) { display->setTextAlignment(TEXT_ALIGN_LEFT); diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index faf1d80f3..43897e782 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -22,6 +22,17 @@ enum cannedMessageDestinationType { CANNED_MESSAGE_DESTINATION_TYPE_CHANNEL }; +enum CannedMessageModuleIconType { shift, backspace, space, enter }; + +struct Letter { + String character; + float width; + int rectX; + int rectY; + int rectWidth; + int rectHeight; +}; + #define CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT 50 /** * Sum of CannedMessageModuleConfig part sizes. @@ -61,6 +72,14 @@ class CannedMessageModule : public SinglePortModule, public Observablerx_rssi != 0) { + this->lastRxRssi = p->rx_rssi; + } + + if (p->rx_snr > 0) { + this->lastRxSnr = p->rx_snr; + } + switch (p->decoded.portnum) { case meshtastic_PortNum_TEXT_MESSAGE_APP: case meshtastic_PortNum_ROUTING_APP: @@ -79,6 +98,18 @@ class CannedMessageModule : public SinglePortModule, public ObservableshouldDraw(); } virtual Observable *getUIFrameObservable() override { return this; } @@ -110,12 +141,84 @@ class CannedMessageModule : public SinglePortModule, public Observable -#define EXTERNAL_NUM_INTERRUPTS 22 -#define NUM_DIGITAL_PINS 22 -#define NUM_ANALOG_INPUTS 6 - -#define analogInputToDigitalPin(p) (((p) < NUM_ANALOG_INPUTS) ? (esp32_adc2gpio[(p)]) : -1) -#define digitalPinToInterrupt(p) (((p) < NUM_DIGITAL_PINS) ? (p) : -1) -#define digitalPinHasPWM(p) (p < EXTERNAL_NUM_INTERRUPTS) - static const uint8_t TX = 21; static const uint8_t RX = 20; diff --git a/variants/heltec_wireless_paper/pins_arduino.h b/variants/heltec_wireless_paper/pins_arduino.h index 66d091691..9e1d8a9a0 100644 --- a/variants/heltec_wireless_paper/pins_arduino.h +++ b/variants/heltec_wireless_paper/pins_arduino.h @@ -7,14 +7,6 @@ #define DISPLAY_HEIGHT 64 #define DISPLAY_WIDTH 128 -#define EXTERNAL_NUM_INTERRUPTS 16 -#define NUM_DIGITAL_PINS 40 -#define NUM_ANALOG_INPUTS 16 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 34) - static const uint8_t LED_BUILTIN = 35; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN diff --git a/variants/heltec_wireless_paper_v1/pins_arduino.h b/variants/heltec_wireless_paper_v1/pins_arduino.h index 66d091691..9e1d8a9a0 100644 --- a/variants/heltec_wireless_paper_v1/pins_arduino.h +++ b/variants/heltec_wireless_paper_v1/pins_arduino.h @@ -7,14 +7,6 @@ #define DISPLAY_HEIGHT 64 #define DISPLAY_WIDTH 128 -#define EXTERNAL_NUM_INTERRUPTS 16 -#define NUM_DIGITAL_PINS 40 -#define NUM_ANALOG_INPUTS 16 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 34) - static const uint8_t LED_BUILTIN = 35; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN diff --git a/variants/heltec_wireless_tracker/pins_arduino.h b/variants/heltec_wireless_tracker/pins_arduino.h index 5c0b529b0..1052af961 100644 --- a/variants/heltec_wireless_tracker/pins_arduino.h +++ b/variants/heltec_wireless_tracker/pins_arduino.h @@ -11,18 +11,10 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - static const uint8_t LED_BUILTIN = 18; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/heltec_wireless_tracker_V1_0/pins_arduino.h b/variants/heltec_wireless_tracker_V1_0/pins_arduino.h index f72c7661a..28b982012 100644 --- a/variants/heltec_wireless_tracker_V1_0/pins_arduino.h +++ b/variants/heltec_wireless_tracker_V1_0/pins_arduino.h @@ -11,18 +11,10 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - static const uint8_t LED_BUILTIN = 18; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/m5stack-stamp-c3/pins_arduino.h b/variants/m5stack-stamp-c3/pins_arduino.h index 38ef9934e..22d2af51d 100644 --- a/variants/m5stack-stamp-c3/pins_arduino.h +++ b/variants/m5stack-stamp-c3/pins_arduino.h @@ -3,14 +3,6 @@ #include -#define EXTERNAL_NUM_INTERRUPTS 22 -#define NUM_DIGITAL_PINS 22 -#define NUM_ANALOG_INPUTS 6 - -#define analogInputToDigitalPin(p) (((p) < NUM_ANALOG_INPUTS) ? (esp32_adc2gpio[(p)]) : -1) -#define digitalPinToInterrupt(p) (((p) < NUM_DIGITAL_PINS) ? (p) : -1) -#define digitalPinHasPWM(p) (p < EXTERNAL_NUM_INTERRUPTS) - static const uint8_t TX = -1; // 21; static const uint8_t RX = -1; // 20; diff --git a/variants/m5stack_core/pins_arduino.h b/variants/m5stack_core/pins_arduino.h index 8f2a0041e..cf807aab4 100644 --- a/variants/m5stack_core/pins_arduino.h +++ b/variants/m5stack_core/pins_arduino.h @@ -3,14 +3,6 @@ #include -#define EXTERNAL_NUM_INTERRUPTS 16 -#define NUM_DIGITAL_PINS 20 -#define NUM_ANALOG_INPUTS 16 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 34) - static const uint8_t TX = 1; static const uint8_t RX = 3; diff --git a/variants/m5stack_coreink/pins_arduino.h b/variants/m5stack_coreink/pins_arduino.h index 7f9a14785..c75283ab2 100644 --- a/variants/m5stack_coreink/pins_arduino.h +++ b/variants/m5stack_coreink/pins_arduino.h @@ -3,14 +3,6 @@ #include -#define EXTERNAL_NUM_INTERRUPTS 16 -#define NUM_DIGITAL_PINS 40 -#define NUM_ANALOG_INPUTS 16 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (esp32_adc2gpio[(p)]) : -1) -#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 34) - #define TX2 -1 #define RX2 -1 diff --git a/variants/my_esp32s3_diy_eink/pins_arduino.h b/variants/my_esp32s3_diy_eink/pins_arduino.h index 39e316624..b37a258c3 100644 --- a/variants/my_esp32s3_diy_eink/pins_arduino.h +++ b/variants/my_esp32s3_diy_eink/pins_arduino.h @@ -6,14 +6,6 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - // The default Wire will be mapped to PMU and RTC static const uint8_t SDA = 18; static const uint8_t SCL = 17; diff --git a/variants/my_esp32s3_diy_oled/pins_arduino.h b/variants/my_esp32s3_diy_oled/pins_arduino.h index 39e316624..b37a258c3 100644 --- a/variants/my_esp32s3_diy_oled/pins_arduino.h +++ b/variants/my_esp32s3_diy_oled/pins_arduino.h @@ -6,14 +6,6 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - // The default Wire will be mapped to PMU and RTC static const uint8_t SDA = 18; static const uint8_t SCL = 17; diff --git a/variants/picomputer-s3/pins_arduino.h b/variants/picomputer-s3/pins_arduino.h index c84601b1e..a3d40018c 100644 --- a/variants/picomputer-s3/pins_arduino.h +++ b/variants/picomputer-s3/pins_arduino.h @@ -6,14 +6,6 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/rak11200/pins_arduino.h b/variants/rak11200/pins_arduino.h index 2dfe02614..f383d54a7 100644 --- a/variants/rak11200/pins_arduino.h +++ b/variants/rak11200/pins_arduino.h @@ -3,14 +3,6 @@ #include -#define EXTERNAL_NUM_INTERRUPTS 16 -#define NUM_DIGITAL_PINS 40 -#define NUM_ANALOG_INPUTS 16 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (esp32_adc2gpio[(p)]) : -1) -#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 34) - #define LED_GREEN 12 #define LED_BLUE 2 diff --git a/variants/rak11200/variant.h b/variants/rak11200/variant.h index 3399594e5..3cd601254 100644 --- a/variants/rak11200/variant.h +++ b/variants/rak11200/variant.h @@ -3,14 +3,6 @@ #include -#define EXTERNAL_NUM_INTERRUPTS 16 -#define NUM_DIGITAL_PINS 40 -#define NUM_ANALOG_INPUTS 16 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (esp32_adc2gpio[(p)]) : -1) -#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 34) - #define LED_GREEN 12 #define LED_BLUE 2 diff --git a/variants/station-g2/pins_arduino.h b/variants/station-g2/pins_arduino.h index 98cbd46d3..6a803008d 100755 --- a/variants/station-g2/pins_arduino.h +++ b/variants/station-g2/pins_arduino.h @@ -6,14 +6,6 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) <= 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - // GPIO48 Reference: https://github.com/espressif/arduino-esp32/pull/8600 // The default Wire will be mapped to Screen and Sensors diff --git a/variants/station-g2/platformio.ini b/variants/station-g2/platformio.ini index e96c0ab88..b674c8bae 100755 --- a/variants/station-g2/platformio.ini +++ b/variants/station-g2/platformio.ini @@ -8,7 +8,9 @@ upload_protocol = esptool upload_speed = 921600 lib_deps = ${esp32s3_base.lib_deps} -build_unflags = -DARDUINO_USB_MODE=1 +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=1 build_flags = ${esp32s3_base.build_flags} -D STATION_G2 -I variants/station-g2 -DBOARD_HAS_PSRAM diff --git a/variants/t-deck/pins_arduino.h b/variants/t-deck/pins_arduino.h index 0150935ed..cb429d776 100644 --- a/variants/t-deck/pins_arduino.h +++ b/variants/t-deck/pins_arduino.h @@ -6,14 +6,6 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < NUM_ANALOG_INPUTS) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < NUM_DIGITAL_PINS) ? (p) : -1) -#define digitalPinHasPWM(p) (p < EXTERNAL_NUM_INTERRUPTS) - // static const uint8_t LED_BUILTIN = -1; static const uint8_t TX = 43; diff --git a/variants/t-watch-s3/pins_arduino.h b/variants/t-watch-s3/pins_arduino.h index d3dde6856..35f0e933e 100644 --- a/variants/t-watch-s3/pins_arduino.h +++ b/variants/t-watch-s3/pins_arduino.h @@ -3,14 +3,6 @@ #include -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < NUM_ANALOG_INPUTS) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < NUM_DIGITAL_PINS) ? (p) : -1) -#define digitalPinHasPWM(p) (p < EXTERNAL_NUM_INTERRUPTS) - // static const uint8_t LED_BUILTIN = -1; // static const uint8_t TX = 43; diff --git a/variants/tbeam-s3-core/pins_arduino.h b/variants/tbeam-s3-core/pins_arduino.h index 22ed814ff..24edb7d9f 100644 --- a/variants/tbeam-s3-core/pins_arduino.h +++ b/variants/tbeam-s3-core/pins_arduino.h @@ -6,6 +6,14 @@ #define USB_VID 0x303a #define USB_PID 0x1001 +#define EXTERNAL_NUM_INTERRUPTS 46 +#define NUM_DIGITAL_PINS 48 +#define NUM_ANALOG_INPUTS 20 + +#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) +#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) +#define digitalPinHasPWM(p) (p < 46) + static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/tbeam-s3-core/platformio.ini b/variants/tbeam-s3-core/platformio.ini index c71802255..e50d506b9 100644 --- a/variants/tbeam-s3-core/platformio.ini +++ b/variants/tbeam-s3-core/platformio.ini @@ -4,8 +4,6 @@ extends = esp32s3_base board = tbeam-s3-core board_check = true -platform = platformio/espressif32@6.7.0 - lib_deps = ${esp32s3_base.lib_deps} lewisxhe/PCF8563_Library@1.0.1 diff --git a/variants/tlora_t3s3_v1/pins_arduino.h b/variants/tlora_t3s3_v1/pins_arduino.h index 627dad19d..4ced1b446 100644 --- a/variants/tlora_t3s3_v1/pins_arduino.h +++ b/variants/tlora_t3s3_v1/pins_arduino.h @@ -6,14 +6,6 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - // The default Wire will be mapped to PMU and RTC static const uint8_t SDA = 18; static const uint8_t SCL = 17; diff --git a/variants/tracksenger/internal/pins_arduino.h b/variants/tracksenger/internal/pins_arduino.h index 5c0b529b0..1052af961 100644 --- a/variants/tracksenger/internal/pins_arduino.h +++ b/variants/tracksenger/internal/pins_arduino.h @@ -11,18 +11,10 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - static const uint8_t LED_BUILTIN = 18; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/tracksenger/lcd/pins_arduino.h b/variants/tracksenger/lcd/pins_arduino.h index 5c0b529b0..1052af961 100644 --- a/variants/tracksenger/lcd/pins_arduino.h +++ b/variants/tracksenger/lcd/pins_arduino.h @@ -11,18 +11,10 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - static const uint8_t LED_BUILTIN = 18; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/tracksenger/oled/pins_arduino.h b/variants/tracksenger/oled/pins_arduino.h index 5c0b529b0..1052af961 100644 --- a/variants/tracksenger/oled/pins_arduino.h +++ b/variants/tracksenger/oled/pins_arduino.h @@ -11,18 +11,10 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - static const uint8_t LED_BUILTIN = 18; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/unphone/platformio.ini b/variants/unphone/platformio.ini index f66b5db49..dbfa0599d 100644 --- a/variants/unphone/platformio.ini +++ b/variants/unphone/platformio.ini @@ -9,6 +9,7 @@ monitor_speed = 115200 monitor_filters = esp32_exception_decoder build_unflags = + ${esp32s3_base.build_unflags} -D ARDUINO_USB_MODE build_flags = ${esp32_base.build_flags} diff --git a/variants/wiphone/pins_arduino.h b/variants/wiphone/pins_arduino.h index bca9c1173..3759219d1 100644 --- a/variants/wiphone/pins_arduino.h +++ b/variants/wiphone/pins_arduino.h @@ -3,14 +3,6 @@ #include -#define EXTERNAL_NUM_INTERRUPTS 16 -#define NUM_DIGITAL_PINS 20 -#define NUM_ANALOG_INPUTS 16 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (esp32_adc2gpio[(p)]) : -1) -#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 34) - static const uint8_t TX = 1; static const uint8_t RX = 3; diff --git a/version.properties b/version.properties index 69761c0ac..aa4d2c207 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 3 -build = 10 +build = 11